我不算是特别爱喝酒,本科的时候很少喝。都说一名合格的研究生的两项标配就是撸铁和饮酒。我发现特别有趣的是,现在的年轻人,特别喜欢喝鸡尾酒,一种基酒和各种软软(饮料等)勾兑的饮品,当然我也喜欢。

我为什么会开始自己调酒?

我喜欢喝咖啡,有一段时间我就在想用古典的方式自己来手冲咖啡,当我有这种想法的时候,潜意识经常会强化这一需求。后来我真的遇到了,在朋友圈看到有人在宿舍手冲咖啡,我那一刻被击中了,像这样的瞬间,生活中总是会存在。这就好像你以前在听一首歌,里面的歌词是“这座城市被大雨倾倒,我会给你怀抱”,你可能觉得这词平平无奇,这个时候你不会感知任何情绪。但当有一天,你下班路过一片水滩,看到了城市的倒影,那一刻你瞬间明白了“城市被大雨倾倒”的浪漫,从此那片水滩、那首歌,也不再是一个普通的水滩、一首普通的歌。

“我们为什么喜欢哪些看起来荒诞的事?因为他们经历着我们想经历的,做着我们想做的,过着我们未曾想到过的生活。”


经典鸡尾酒

经典鸡尾酒已经走过两百多年的历史,不同的国家和地区由于其独特的地理环境和生活条件形成了具有鲜明地域特色的基酒以及衍生的各类鸡尾酒。如今,人们常提到的六大基酒包括威士忌,伏特加,龙舌兰,朗姆,金酒,白兰地。除此之外,各类软软,如汤力水,气泡水,苏打水;力娇酒(君度、蓝橙);糖浆(红石榴);柠檬汁等。通过酸甜来调和酒的烈性。

玛格丽特

玛格丽特被称作“鸡尾酒之后”,它是除马天尼以外世界上知名度最高的传统鸡尾酒之一。它曾经是1949年全美鸡尾酒大赛的冠军。1926年,Jean Durasa去墨西哥,与Margarita相恋,墨西哥成了他们的浪漫之地。然而,有一次当两人去野外打猎时,玛格丽特中了流弹,最后倒在恋人Jean Durasa的怀中,永远离开了。于是,Jean Durasa就用墨西哥的国酒Tequila(龙舌兰)为鸡尾酒的基酒,用柠檬汁的酸味代表心中的酸楚,用盐霜意喻怀念的泪水。

原材料: - 玛格丽特杯 - 粗盐(细盐比较闲,不做盐边也没问题) - 45ml龙舌兰 - 25ml君度力娇酒 - 10-15ml柠檬汁 - 15ml蓝橙力娇酒(非必须,蓝色玛格丽特)

图1: 蓝色玛格丽特

龙舌兰日出

特基拉日出,又称龙舌兰日出,此款鸡尾酒是星座鸡尾酒中代表射手座(11月23日-12月22日)的一款鸡尾酒。当年滚石乐队在1972年的美洲巡回演出中饮用了这款鸡尾酒,使得这款鸡尾酒在美国盛行开来。寓意在生长着星星点点仙人掌,但又荒凉到极点的墨西哥平原上,正升起鲜红的太阳,阳光把墨西哥平原照耀得一片灿烂。龙舌兰日出中浓烈的龙舌兰香味容易使人想起墨西哥的朝霞。

原材料: - 柯林杯 - 45ml龙舌兰 - 15ml柠檬汁 - 橙汁 - 红石榴糖浆

图2: 龙舌兰日出

长岛冰茶

长岛冰茶,也被称为失身酒,此款鸡尾酒是星座鸡尾酒中代表水瓶座(1月20日至2月19日)的一款鸡尾酒。它的起源说法众多。 起源一 起源于冰岛,在二十世纪二十年代美国禁酒令期间的纽约州长岛推广开来,而后在日本登陆并迅速流行开来。 起源二 在1972年,由长岛橡树滩客栈(Oak Beach Inn)的酒保发明了这种以四种基酒混制出来的饮料。 调和此酒时所使用的酒基本上都是40°以上的烈酒。 起源三 1970年由名为蔷薇(Rosebud)的人发明的(真正的名字是罗伯特·巴特Robert Butt),他在位于长岛南海岸的海滩橡木旅馆(Oak beach Inn)工作。与调制所有的古典鸡尾酒规则不同,这款鸡尾酒包括除利口酒外的另外四种含酒精饮料。

原材料: - 15ml金酒 - 15ml伏特加 - 15ml白朗姆 - 15ml龙舌兰 - 15ml君度 - 20ml柠檬汁 - 糖浆(非必须) - 可乐

图3: 长岛冰茶

自由古巴

自由古巴,起源于1900年,是用郎姆酒为基酒并兑上适量的可乐而成。自由古巴(Cuba Libre)是古巴从西班牙手中独立时,用当时市民口中常用的词来命名的酒。1902年,古巴人民进行了反对西班牙的独立战争,在这场战争中他们使用“Cuba libre”(即自由的古巴万岁)作为纲领性口号,于是便有了这款名为“自由古巴”的鸡尾酒。加入可乐后这款鸡尾酒口感轻柔,很适合在海滩酒吧饮用。

原材料: - 50ml白朗姆 - 10ml柠檬 - 可乐

。。。(未完待续)

在列车上,想着先写点什么。腊月十三,距离过年还有16天,今年没有腊月三十。环境不太好,下次回家还是买个高铁票,回家对我来说还是一件很有仪式感的事,尽管北京和大同离得很近,但我几乎都是在每次放寒暑假才会回家。

今年基本上都把时间和精力放在了科研上,2024年初的时候,还是一个小白,看一些复杂的代码和框架有些吃力,那时候会有很多非科研进展的问题要和师兄讨论,改一个代码也需要花费几天。上半年每周开两次组会,我个人体验下来,其实做自己真正感兴趣的事的时候,是特别愿意和别人讨论的,不同灵感的碰撞是快乐的,和师兄开组会,没什么压力,讨论起来就像是和朋友在交流思想,这一刻我好像有些理解了,“有辩论的地方,权威会暂时终止”,我太喜欢这样的学术氛围了。

上半年前段,这是今年我觉得遇到的最重要的人,如果故事在这里结尾了,其实我还是蛮遗憾的,我很喜欢她。这会不会是今年最大的礼物又是最大的失望呢?

5、6月的时候,科研遇阻,被算力限制,总感觉要做不下去了。很多时候,我又是幸运的,学校新建的AI平台,管理平台老师的倾心解答,没有他们的帮助,我想9月份第一个科研任务都会完不成。但还好,运气到来的时候,我紧紧攥住,大四毕业的一个假期我都没有干别的事情,每周都和师兄,讨论,做实验,最终9月份写好论文并投了出去。

研一开学,新的环境,新的朋友,我这一次选择了和大学不同的方式,积极社交,不再把学分绩看的很重,我加入研究生会,竞选团支书,为学校出力,为班级建设,同时继续开展下一个课题。本来是准备11月份的CVPR的,那段时间每天工作真的有12个小时,脑子都是晕乎乎的,遗憾的是最后还是结果不太好。本来10月1号开始,国庆直接加班做,但是11月初的时候师兄推倒之前的改动退回以前的版本重新做,结果最后尽管赶出一版论文,但是中的概率太低,决定撤稿补充实验重投,目前这个工作已经做完了,但可能还是缺一些实验,师兄还在完善,不知道什么时候投出这篇。

2024年12月底,开始了新的课题,目前还在着手跑实验中,现在的科研进度应该比以前快很多了,我们更多地去讨论思路而不是纠结于实现的细节。与此同时,大老板还让我并行开展一个课题,关于电力方向的,有一点小困难,但是也在尽力推进,不知道寒假能不能做出来。


距离农历新年的最后一天,家里收拾的差不多了,只待新年的到来。农历乙巳蛇年,作为01年的蛇宝宝,我买了红色的衣服和鞋子,本命年总还是讲究一些。

先聊近期的科研进展,回家之后花了几天的时间,这次成功解决了风电场景生成的任务,并且取得了不错的效果,准备年后着手写论文。和师兄做的之前的课题,近期准备投到ICML,最近补充了一些实验,马上要提交正文,不知道来不来的及加进去。

回来见了几个玩的好的高中同学,从德国回来的BIT精工阿政,德国巧克力确实好吃。还有我的几个发小,已经10多年的友谊了,以前听到那些说人生有一两知己实乃幸事我不以为然,等到了长大发现大家越来越忙,可真的这几个好兄弟可以随叫随到,不需要讲究,口无遮拦。


ICML又撤稿了,不过实验确实不够完善。哎,科研这么难吗?电网新能源出力场景的交叉研究已经做好了,论文已经写好。正月十五已过,新的工作又要开始了。这篇就写到这里,奔赴2025。


一年又一年,乙巳蛇年。祝愿所有蛇宝宝们在新的一年里灵蛇纳福,巳巳如意。

(回忆是一条没有归程的路)

我与hexo的一周年

时间回到2023年12月,那个时候我开始和长林师兄合作人工智能的项目。偶然一次阅读知乎贴子,被一位大佬的博客所吸引,我很早就想拥有一个个人博客,但是通过服务器直接构建个人网站成本太高,一直没有合适的方案。我点进这个博客中,发现了他用了一个叫github pages和hexo的东西,看着非常有意思,当我继续深入了解时,竟然发现这种构建网站的方式真是优雅,除了时间零成本,无需支付一分钱,我连连叫好,开始入坑,在计算机人最熟练的技巧ctrl cctrl v上我练得炉火纯青,不懂原理的我通过分析大佬的代码库,以及爬取网页源代码,一点点把别人的东西变成自己的东西,怎么感觉有点像国内某些xxx公司会干的事。那时候,我就是通过这种方式搭起我的第一个博客,时隔一年,我对hexo有了新的理解,也从原来喜欢花哨的主题到现在爱上简约风格,大概是祛魅了。我从fluid转换到next,希望更注重博客本身的质量,我不是一个善于有逻辑地表达的人,但我一直在努力。如果有人想要通过努力写高质量博客让自己的博客可以被浏览器索引,那么我的目标就是先清晰地表达我心中所思、脑中所想。

保证您的电脑拥有Git、Node.js且您熟悉git的基础命令。

从入门到放弃(精通)

在详细讲解构建操作之前,先列出构建流程: 1. 安装Hexo并初始化 2. 在Hexo初始化的根目录下安装喜欢的风格主题 3. 配置_config.yml和_config..yml文件 4. 使用github pages部署博客

安装Hexo并初始化

很高兴,我已经拥有了Git和Node.js,那么接下来就是安装Hexo了,打开我的终端,输入以下命令:

1
npm install -g hexo-cli
安装完成后,初始化我的博客,输入以下命令:
1
2
3
hexo init <my blog folder>
cd <my blog folder>
npm install
很棒的一步,这时我的目录下应该有如下结构:
1
2
3
4
5
6
7
8
9
.
├── _config.yml
├── node_modules
├── package.json
├── scaffolds
├── source
| ├── _posts
| ├── _drafts
└── themes
_config.yml是Hexo的配置文件,source/_posts是我的博客文章存放目录,source目录下还会存放我可能自定义的其他菜单。 我首先修改最重要的配置首先保证我的博客在部署后能在浏览器中正常访问,例如url: https://<your_github_name>.github.io\<repo>

在Hexo初始化的根目录下安装喜欢的风格主题

一切准备就绪,开始选一个我喜爱的主题,现在我使用了Next主题,我的终端应该保持在目录下,输入以下命令:

1
npm install hexo-theme-next
主题也有相应的配置文件,我先把它复制到我的根目录下:
1
cp themes/next/_config.yml _config.next.yml

配置_config.yml和_config.topic.yml文件

好了,页面和我总要有一个能跑起来,来看看我对_config.yml的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Site
title: blog title
subtitle: 'Blog'
description: 'your love sentence.'
keywords:
author: #Your Name
language: #en
timezone: #'Asia/Shanghai'

# URL
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
url: https://<your_github_name>.github.io\<repo>
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
type: git
repo: https://<your_github_name>.github.io\<repo>
# example, https://github.com/hexojs/hexojs.github.io
branch: gh-pages
# 配置`deploy`是为了后面的一键部署
_config.next.yml,这个是个性化配置,不会影响我博客的正常运行,稍后修改。

使用github pages部署博客

准备工作完成,如果我想要把所有代码放到仓库里,我需要在github上创建一个新的repo,然后再执行一些git命令,把我博客的源码推送到github上(但这不是必要的):

1
2
3
4
5
git init
git remote add origin <my_blog_repo>
git add .
git commit -m "init"
git push -u origin master


接下来,先来安装一键部署的插件:

1
2
3
4
npm install hexo-deployer-git --save
# 注意到我在前面的配置文件中配置了`deploy`,这是为了后面的一键部署,现在我只需要在终端输入:
hexo clean && hexo deploy
它会自动帮我部署到github pages上,现在查看我创建的仓库,如果在之前上传了主分支,那么现在会多出一个新的分支gh-pages,构建的静态网页就在这个分支上。

然后,要在 Github Pages 上发布我的 Hexo 网站,我需要创建一个 GitHub 存储库并将我的本地存储库推送到该存储库。访问我的 GitHub 存储库,从主菜单中选择“设置”>“页面”。然后我会看到如下选项:

选择分支上建构,这是因为我们在上面已经配置了部署时的分支:gh-pages,如果你的仓库中没有,请先执行:

1
2
hexo clean && hexo deploy
# hexo deploy 会自动执行部署

更多的细节,在repo中找到Settings > Pages,选择SourceDeploy from a branch, 选择gh-pages分支,点击Save,这时候我就可以通过https://<your_github_name>.github.io/<repo>访问我的博客了。作为新手的我可能会去仓库的Action面板中查看部署是否成功。

一切ok,我长舒一口气。

最后,创建一篇博客的指令如下:

1
hexo new page -p <路径>/xxx.md

如果你的自学能力很强,有良好的阅读文档习惯,请查阅官方文档并仔细阅读,那会帮助你进一步掌握hexo。 保证您了解github pages并且已成功配置,并且已经拥有.github.io的repo。 您可以在阅读下面的内容时并实践时,将repo名称直接替换为.github.io,https://.github.io<repo>的形式将被替换为https://.github.io来直接进行访问。 如果您不了解github pages以及为何要命名为.github.io,请先查看github pages官方文档 更多请查看我的github仓库以及主页的形式,这将帮助您理解我的构建方式。 更个性化的定制请查看Hexo官方文档以及其中的相关主题文档,这对于深入学习已经进行自己的个性化定制是必要的。

话外

github pages还支持个性化域名,你可以购买域名地址来解析<github_name>.github.io。当你拥有了<github_name>.github.io存储库之后,就可以像我一样创建多个仓库,然后通过<github_name>.github.io/<repo>来访问不同的仓库,而这些仓库将由你决定它们的内容。我想之后不会有特别的补充,也许你会在这一过程中和我一样遇到很多问题,我相信你会解决它们,祝你好运!

还有一些要说的,如果你认为自己的文笔足够出色,可以通过配置站点地图来让Google、Bing、Baidu等搜索引擎检索到你的文章。比较坑的是,Baidu需要备案,且不支持子域名(比如我上面用的这种“子仓库”),必须要顶级域名,我觉得很麻烦,随止。

Concept Overview

[结构化剪枝] 是指剪枝整个神经元,甚至是整个层。这种方式无需特殊的硬件或软件支持,方便地获得更小的模型以应用在通用软硬件环境中。

[细粒度剪枝] 是指对参数矩阵的行和列进行剪枝(修剪神经元),一般情况下,相当于减少模型的宽度而不改变深度。

[粗粒度剪枝] 是指直接删除某些网络的全部参数,即删除层。

[特征蒸馏] 是指学生模型的每一层输出的特征图尽量对齐教师模型的输出特征图,在 LAPTOP-Diff 中,是指SD模型的每一个块(DownBlocks,MiddleBlock,UPBlocks)的输出对齐教师模型。

Motivation

SDM 的出色性能伴随着相当大的内存消耗和延迟,使其在个人电脑甚至移动设备上的部署受到极大限制。 此外,SDM 系列的最新版本,例如 SDXL,参数趋于增大,导致内存消耗和延迟更大。很多工作都致力于减少SDM的推理成本,例如去噪步骤的减少,高效的架构设计,结构化剪枝,量化,硬件感知优化,蒸馏等。

在这些方法中,高效的架构设计和结构剪枝研究不足。一方面,之前的高效架构设计方法通常经过大量的实证研究来识别 SDM 的 U-Net 中不重要的层,然后将其删除,以实现更小更快的网络。这种手工设计的方式通常不能达到最佳性能,并且缺乏可扩展性和泛化性。我们注意到,这些手工设计的层删除方法可以被自动化的层剪枝方法取代,以获得更好的可扩展性和性能。另一方面,之前关于 SDM 的结构剪枝方法侧重于细粒度剪枝,即剪枝参数矩阵的行和列。然而有研究表明,细粒度剪枝在降低模型延迟方面通常不如粗增益层剪枝有效,而且有趣的是,层剪枝可以达到与细增益结构剪枝相同甚至更好的效果。

在移除或修剪层之后,SDM 通常无法直接生成清晰的图像。以前的方法利用知识蒸馏来重新训练修剪后的网络以恢复其性能。以前的方法通常利用三种类型的目标,即常规训练目标、logit 蒸馏(输出蒸馏)目标和特征蒸馏目标。在这三个部分中,特征蒸馏是关键部分。然而,我们进一步的检查发现了以前基于蒸馏的再训练方法中的一个不平衡问题,即在整个再训练过程中,一些特征损失项比其他特征损失项占主导地位,导致性能下降。

基于以上两点,LAPTOP-Diff 提出了压缩扩散模型的层修剪和规范化蒸馏(LAPTOP-Diff)。

Contribution

LAPTOP-Diff 站在组合优化问题的高角度定义了层修剪问题,证明了一次性层修剪标准的有效性来自于其良好的可加性。NP-hard问题转化为01背包问题后,使用动态规划选择要剪枝的网络层。

针对基于蒸馏的再训练方法中的不平衡问题,即在整个再训练过程中,一些特征损失项比其他项占主导地位,LAPTOP-Diff 通过提出的规范化特征蒸馏缓解了这个问题。

Layer Pruning

层剪枝该剪枝哪些层呢?在U-Net网络的训练过程中,通常的目标是预测噪声。所以我们很自然地想到对比剪枝前和剪枝后模型预测噪声的能力作为评价标准。于是,设\(\epsilon_ori\)表示原始模型的输出,\(\epsilon(r_1,r_2,...,r_m)\)表示去掉\(r_1,r_2,...,r_m\)层之后的网络的输出,于是优化问题可以写成:

\[\begin{equation} \begin{aligned} & \min_{r_1, \ldots, r_m} \quad \mathbb{E} \left\| \epsilon(r_1, \ldots, r_m) - \epsilon_{\text{ori}} \right\|_2^2 \\ \text{s.t.}~~~~ & \{r_1, \ldots, r_m\} \subseteq L^n, \sum_{i=1}^m \text{params}(r_i) \geq P, \end{aligned} \end{equation}\]

其中,\(param(r_i)\)表示\(r_i\)层的参数数量,P表示根据规定的剪枝率计算得到的要剪枝的参数数量。直接求解上式是一个NP-hard问题,于是作者开始对该式进行近似。

首先,通过不等式变换:

\[\begin{equation} \begin{aligned} & \mathbb{E} \left\| \epsilon(r_1, \ldots, r_m) - \epsilon_{\text{ori}} \right\|_2^2 \\ & \leq \mathbb{E} \left\| \epsilon(r_1) - \epsilon_{\text{ori}} \right\|_2^2 \\ & ~~~~ + \mathbb{E} \left\| \epsilon(r_1, r_2) - \epsilon(r_1) \right\|_2^2 \\ & ~~~~ + \cdots \\ & ~~~~ + \mathbb{E} \left\| \epsilon(r_1, \ldots, r_m) - \epsilon(r_1, \ldots, r_{m-1}) \right\|_2^2. \end{aligned} \end{equation}\]

于是,优化目标近似为如下形式:

\[\begin{equation} \begin{aligned} \min_{r_1, \ldots, r_m} & \mathbb{E} \left\| \epsilon(r_1) - \epsilon_{\text{ori}} \right\|_2^2 + \mathbb{E} \left\| \epsilon(r_1, r_2) - \epsilon(r_1) \right\|_2^2 + \cdots \\ & + \mathbb{E} \left\| \epsilon(r_1, \ldots, r_m) - \epsilon(r_1, \ldots, r_{m-1}) \right\|_2^2, \\ \text{s.t.} & ~~~~ \{r_1, \ldots, r_m\} \subseteq L^n, \ \sum_{i=1}^m \text{params}(r_i) \geq P. \end{aligned} \end{equation}\]

但这仍然是个NP-hard问题,还要再进行化简。在这个式子中,每一项都是原始网络(剪枝或未剪枝)与删除一个新的额外层的网络之间的均方误差损失。作者提出一个假设,即移除同一层\(r_i\)所导致的均方误差损失在不同网络中是相似的。公式假设如下:

\[\begin{equation} \begin{aligned} & \mathbb{E} \left\| \epsilon(r_1, \ldots, r_{i-1}, r_i) - \epsilon(r_1, \ldots, r_{i-1}) \right\|_2^2 \\ & ~~~~ \approx \mathbb{E} \left\| \epsilon(r_1, \ldots, r_{i-2}, r_i) - \epsilon(r_1, \ldots, r_{i-2}) \right\|_2^2 \\ & ~~~~ \approx \cdots \\ & ~~~~ \approx \mathbb{E} \left\| \epsilon(r_1, r_i) - \epsilon(r_1) \right\|_2^2 \\ & ~~~~ \approx \mathbb{E} \left\| \epsilon(r_i) - \epsilon_{\text{ori}} \right\|_2^2. \\ \end{aligned} \end{equation}\]

这样,我们就可以重新整理优化目标函数了,最终的形式如下:

\[\begin{equation} \begin{aligned} & \min_{r_1, \ldots, r_m} \sum_{i=1}^m \mathbb{E} \left\| \epsilon(r_i) - \epsilon_{\text{ori}} \right\|_2^2 \\ \text{s.t.} & \ \{r_1, \ldots, r_m\} \subseteq L^n, \ \sum_{i=1}^m \text{params}(r_i) \geq P. \end{aligned} \end{equation}\]

使用该式替代最开始的优化目标意味着一个有趣的性质,称为可加性,这被解释为由许多扰动引起的网络输出失真近似等于由每个单个扰动引起的输出失真的总和。作者在论文的实验部分验证了可加性以及最终目标是对原始目标的极好近似。

经过上面的推导,优化问题变成了时间复杂度仅为O(n)的0-1背包问题,通常这种问题使用动态规划或者贪婪搜索算法来解决。算法流程如图1所示。

图1: 一次性层剪枝算法

Normalized Feature Distillation

在模型蒸馏中,先前的方法对修剪后的SDM进行重新训练时,利用下面三个目标,分别是任务损失、输出蒸馏损失和特征蒸馏损失。任务损失是指使用剪枝后的模型重新预测噪声与真实噪声的差距,输出损失是指剪枝后的模型(学生,Student)预测噪声与剪枝前的模型(教师,Teacher)预测噪声之间的差距,特征损失是指学生模型每一层输出的特征图与教师模型每一层输出的特征图之间的差距(LAPTOP-Diff中,对照论文图表,这里应该指每一个块的输出)。

\[\begin{equation} \begin{aligned} \mathcal{L}_{\text{Task}} & = \mathbb{E}_{z, y, t, \epsilon} \left\| \epsilon - \epsilon_S(z_t, y, t) \right\|_2^2, \\ \mathcal{L}_{\text{OutKD}} & = \mathbb{E}_{z, y, t} \left\| \epsilon_T(z_t, y, t) - \epsilon_S(z_t, y, t) \right\|_2^2, \\ \mathcal{L}_{\text{FeatKD}} & = \sum_i \mathbb{E}_{z, y, t} \left\| f_T^i(z_t, y, t) - f_S^i(z_t, y, t) \right\|_2^2, \\ \end{aligned} \end{equation}\]

在实践中,作者发现\(L_{FeatKD}\)在retraining中扮演了至关重要的角色。然而,在进一步的检查中,作者发现再训练时先前的特征蒸馏方法存在不平衡问题。如图2所示,在整个蒸馏过程中,最高特征损失项比最低特征损失项大约大 10000 倍,并产生大约 1000 倍的梯度,稀释了数值上不重要的特征损失项的梯度。L2 范数越大的特征图自然会产生越大的特征损失项,不同特征损失项之间显著的量级差异可以归因于两个因素,即在不同阶段结束时,教师和学生特征图的固有差异(1)以及特征图的 L2 范数(2)会有所不同。

图2: SDXL 教师每个模块输出特征图的 L2 范数以及再训练第 15K 次迭代的特征损失项

很明显,问题(2)可以通过认为设计来缓解,作者提出第二个因素在很大程度上加剧了不平衡问题。于是乎,采用一种加权手段来平衡特征图的 L2 范数带来的影响:

\[\begin{equation} \begin{aligned} \mathcal{L}_{\text{FeatKD-normed}} &= \sum_{i \in V} \mathbb{E}_{z, y, t} \alpha^i \left\| f_T^i(z_t, y, t) - f_S^i(z_t, y, t) \right\|_2^2, \\ \alpha^i &= \frac{\sum_{j \in V} \left\| f_T^j(z_t, y, t) \right\|_2}{|V| \left\| f_T^i(z_t, y, t) \right\|_2}, \end{aligned} \end{equation}\]

其中,𝑉是修剪后各个层集合(每个块),|𝑉|是集合𝑉的大小。当每个块输出的特征图的L2范数相同时,系数变为1。

经过变换,最终如图3所示,通过应用归一化特征蒸馏,作者观察到一些模块的特征损失显着下降,而在其他模块的损失则略有增加,这证明了一些特征损失项相对于其他特征损失项的主导地位得到了缓解。

图3: 在再训练的第 15K 次迭代中,使用原始特征蒸馏或标准化特征蒸馏的每个模块的非标准化特征损失项

越来越多的大型视觉模型在图像和视频生成领域展现了强大的能力,与此同时,高昂的训练成本和推理成本阻碍了这些基础模型的应用,如何有效的压缩基础模型,使其在资源受限的场景(例如,自动驾驶、手机等能源和带宽限制的设备,需要快速响应和紧凑的内存占用。)中实现高效的模型推理,是目前重要的研究课题。这篇文章我想聊一聊模型压缩技术之一——剪枝。

我起初在调研视觉模型的推理加速方案,尤其针对扩散模型。目前技术分为以下几种:

  • 设计更高效的去噪流程,如将高分辨率图像压缩为低分辨率的潜在表示(我的理解,类似于VAE先做压缩,图像VAE和视觉3D VAE)。

  • 设计高效的采样算法改进采样,如DDIM、DPM,IDDPM等(这个方向侧重于微积分,需要很强的数学能力)。

  • 从预训练的扩散模型中蒸馏提取更快的生成模型(知识蒸馏,我觉得和剪枝也可以算是同源,都是在做模型压缩)。

  • 分布式并行单次图像采样,如DistriFusion(以上观点也出自这篇论文,这是软硬件的视角来加速模型推理)。

原文链接:A Survey on Deep Neural Network Pruning: Taxonomy, Comparison, Analysis, and Recommendations,论文已被TPAMI接收。


剪枝

目前已知的问题是在某些具体场景中,计算资源有限、能源和带宽受限,基础模型或通用模型一般参数量巨大,计算复杂性和内存占用使其无法应用在边缘设备上。此外,包含冗余特征的大型模型可能受到攻击,削弱网络的能力。

目前压缩模型的方案有以下几种,分别是神经网络修剪(neural network pruning)。权重矩阵的低秩分解(low-rank factorizations of the weight matrices),量化(quantization),知识蒸馏(knowledge distillation)。神经网络搜索(neural architecture search)等。

而剪枝是各种模型压缩方法中极为重要的一种。我从以下几个方面归类剪枝的方法,基于论文提出的三个问题引申的分类标准。

(1)修剪神经网络,实现通用加速还是特定加速?特定加速需要专门的硬件或者软件支持,而通用加速可以随意迁移。基于这个问题,剪枝方法被分为结构化剪枝(Structured Pruning),非结构化剪枝(Unstructured Pruning)和半结构化剪枝(Semi-structured Pruning)。

(2)何时修剪神经网络?从这个问题出发,剪枝又可以分为训练前剪枝(Pruning Before Training,PBT),训练期间剪枝(Pruning During Training,PDT),训练后剪枝(Pruning After Training,PAT)。

(3)是否根据特定标准(specific metrics)进行剪枝,还是学习剪枝(learn to prune,我更喜欢称之为自适应或可学习的剪枝)?在这个问题上,标准依赖于具体的评分公式,而可学习剪枝通常通过训练在过程中实现动态的剪枝。

特定或通用加速

非结构化剪枝

Unstructure Pruning
图1: 非结构化剪枝

如图1所示,非结构化剪枝并不是修剪神经元或通道数,而这两者影响了输出的维度和通道数。具体来说,每一层的输入和输出的维度都不会发生改变,在深度神经网络(Deep Neutral Network,DNN)规模不大时,常见的做法是为每个神经元或卷积核添加掩码,掩码决定单个神经元或卷积核的哪些参数为0。而在常规的软硬件环境中,将某些参数设置为0并不会加速模型的推理,所以,这种剪枝方式会特定于硬件和软件的支撑。在大规模的DNN中,一般不会采用掩码增加额外的开销,而是将参数直接设置为0。

结构化剪枝

Structure Pruning
图2: 结构化剪枝

如图2所示,结构化剪枝正好与非结构化剪枝相反,它直接剪枝整个神经元或卷积核,甚至是整个层。这种方式无需特殊的硬件或软件支持,方便地获得更小的模型以应用在通用软硬件环境中。

何时剪枝

Structure Pruning
图3: 何时剪枝?

训练前剪枝

顾名思义,一些研究从初始化的模型权重中寻找能够是模型更快收敛的子网络结构,主要目的是为了降低预训练成本。目前PBT主要用于CNN的修剪。网络大小和适当的逐层剪枝率是从头开始训练初始化时随机剪枝的网络以匹配密集模型性能的两个重要因素。然而,后续更多研究发现,在相同的剪枝率下,在训练期间或训练后通过剪枝得到的子网络比在初始化时剪枝的子网络具有更高的有效参数数量和更强的表达能力。

训练期间剪枝

PDT会在初始化模型权重的基础上加入权重掩码,并在训练过程中动态的学习掩码来应用剪枝。一部分方法在剪枝之后会进行微调来恢复模型的性能。如何学习合适的掩码又分出了不同的方法,一些研究基于(1)稀疏正则化进行剪枝,基于稀疏正则化的方法一般会设置可学习掩码,并在损失函数上重新构造来实现训练过程中动态地剪枝过程,一些研究关注重新构造损失中L0、L1或L2正则化项来学习与目标架构匹配的剪枝掩码;还有一些研究提出(2)初始化为稀疏网络,并动态反复增加和修剪权重来获得最优的子网络进而实现剪枝,称为动态稀疏训练;还有一部分研究(3)基于分数,利用某些评分标准来修剪不重要的权重,这些研究直接将权重置0而无需使用掩码;还有一部分研究(4)提出可微分剪枝。

训练后剪枝

PAT是目前最流行的剪枝处理流程,人们普遍认为预训练模型对于获得高效的子网络至关重要,特别是大语言模型和大型视觉模型(如扩散模型)。

这类剪枝方法的一般性流程是(1)预训练大型模型使其收敛,(2)剪枝对性能影响最小的权重,(3)对剪枝后的模型进行微调来恢复模型性能或适应新的下游任务。其中(2)、(3)可以重复多次进行来实现渐进剪枝。

最近的研究指出大型模型如LLM即使修剪之后微调成本仍然高昂,为了解决这个问题,提出了预训练+直接剪枝的策略,无需重新训练,通常通过使用补偿机制来减轻性能下降,从而实现可忽略不计的准确度损失。

彩票假设(Lottery Ticket Hypothesis,LTH)

LTH提出了一个有趣的观点:一个训练好的神经网络中,只有少部分神经元(中奖神经元)对最终模型性能至关重要,而大部分神经元和连接是冗余的。中奖神经元类似于彩票,它们的存在承担了模型的大部分功能。后续研究进一步拓展LTH,如证明中奖神经元的可迁移性(迁移学习)。

除此之外,一些研究分析了LTH成功的原因,给出理论上的证明。与此同时,也有研究挑战LTH的观点,他们发现中奖彩票很大程度上取决训练设置,例如,较小的学习率和训练次数不足的算法更有可能找到中奖彩票。

基于分数的方法(score based)

在这个方向上,有基于权重范数评估,有基于有无权重时的损失变化评估,有使用一阶泰勒展开式估计损失变化,还有设置新的指标来评估的方法。

基于稀疏正则化(sparsity based)

与PDT所提到的稀疏正则化略有区别,一些研究尽管也使用稀疏正则化来剪枝,但其是在预训练模型的基础上对推理进行评估和剪枝。

早期训练中的修剪(pruning in early training)

这种方法旨在训练的早期阶段只训练网络少数迭代或周期之后,对网络进行剪枝,早期阶段的成本较低,且经过少量训练可以更有效地找到中奖彩票,然后再后续迭代中训练子网络达到初始网络的性能。

运行时剪枝(run-time pruning)

对于给定的任务,产生准确输出的难度会有所不同,这意味着不同输入所需的模型容量是不同的(特指训练后的模型执行推理时)。这类方法旨在根据输入选择合适的子网络,例如运行时路由,通道显著性与阈值比较进而跳过贡献较小的通道。

基于标准与动态可学习剪枝

基于分数

基于分数的方法是一种更简单直观的评估策略。这是我比较关注的一个方向。

基于幅度(magnitude based)

一个直观的假设是,如果权重的绝对值很小,一般来说对于网络的输出更不重要,基于此,可以设计阈值来剪枝。

\[ \begin{equation} m_i = \begin{cases} 1 : if\parallel w_i \parallel_1 \geqslant a \\ 0 : if\parallel w_i \parallel_1 < a \end{cases} \end{equation} \]

其中,m是掩码,a是阈值。

由于输出受到激活函数的影响,如Relu激活函数在输入(即网络输出)为负数时,直接输出0,基于此,有研究提出权重与激活函数的结合。

\[ \begin{equation} s_{ij} = |w_{ij}\dot \parallel x_j \parallel_2 \end{equation} \]

\(l_p\) 正则

\(l_p\)范数的通式如下:

\[ \begin{equation} \| x \|_p := \left( \sum_{i=1}^n |x_i|^p \right)^{\frac{1}{p}} \end{equation} \]

具有较小\(l_p\)范数的权重比具有较高\(l_p\)范数的权重更有可能被剪枝。

敏感度与显著性

一些研究利用敏感度或显著性来评估权重(过滤器、神经元等)的重要性。

损失

基于损失的评估通常使用泰勒公式展开来近似。

一阶泰勒展开是测量损失变化最常用的方法。在 w 处有小扰动的损失变化定义如下

\[ \begin{equation} \Delta \mathcal{L} = \mathcal{L}(\mathbf{w} + \Delta \mathbf{w}) - \mathcal{L}(\mathbf{w}) = \nabla_{\mathbf{w}} \mathcal{L} \cdot \Delta \mathbf{w}. \end{equation} \]

损失函数的二阶泰勒展开式包括一阶(梯度)项、二阶(Hessian)项,并且忽略高阶项。不失一般性,损失变化的近似可得出

\[ \begin{equation} \mathcal{L}(\mathbf{w} + \Delta \mathbf{w}) - \mathcal{L}(\mathbf{w}) = \nabla_{\mathbf{w}} \mathcal{L} \cdot \Delta \mathbf{w} + \frac{1}{2} \Delta \mathbf{w}^T H \Delta \mathbf{w}, \end{equation} \]

where \(\text{where } H = \nabla_{\mathbf{w}}^2 \mathcal{L}(\mathbf{w})\)

小结

通过剪枝技术来实现压缩模型进而降低训练成本并提高推理效率是非常重要的手段,在大型视觉模型领域,目前已有文献直接探索这类模型的剪枝,例如U-Net主干网的SD家族以及Transformer主干网的DiT家族。Structural Pruning for Diffusion Models是第一篇在扩散模型上探索结构化剪枝的文章,LAPTOP-Diff: Layer Pruning and Normalized Distillation for Compressing Diffusion Models探讨了在粗粒度水平下,SD上做层剪枝的可能。

NeurIPS2017

Transformer
图1: Transofrmer结构

从2017年开始,transformer就成为在深度学习领域的主导架构,时至今日,你可见的所有人工智能的底层实现,几乎都以其为基础,可以说,transformer开辟了一个时代。从最后的自然语言处理,到计算机视觉,再到如今的多模态应用,transformer不断拓展边界,某种程度上讲,真正意义上实现了“大一统帝国”。

输入

在进入到编码器(Encoder)和解码器(Decoder)之前,输入需要被调整为合适的形状,只有搞清楚输入的含义,才能正确地将这种架构运用到目标领域的实践中。 标准的输入(Inputs)的维度应该为(B,S),其中B指批次(Batch Size),S指序列长度(Sequence)。在自然语言中,一个样本(句子)会被预处理进行分词,经过分词等其他处理之后的句子长度应该为S,每个元素都是一个token。

嵌入层

接下来,我们希望对每个token进行编码,用向量而不是标量表示的好处是,线性空间对于语义相似的单词区分能力有限,例如,悲伤、痛苦这两个词尽管都是消极情绪,但它们在语义上仍有很多差别。用高维的向量空间将更好地区分和聚类相似的语义以及它们的区别。 图1中嵌入层(Input Embedding)会将每一个输入的文本对应的分词映射为一个向量表示,具体来说,输入(B,S)将会经过Embedding层变为(B,S,H),H表示嵌入维度。

1
2
3
4
5
6
7
class Embedding(nn.Module):
def __init__(self, num_embeddings, embed_dim):
super(Embedding, self).__init__()
self.embed = nn.Embedding(num_embeddings, embed_dim)

def forward(self, x):
return self.embed(x)

位置编码

之后,在进入Encoder或Decoder之前,还要加上位置编码(Position Embedding)。顾名思义,位置编码用来告诉模型序列每个token之间的位置关系,对于文本来说,位置关系是至关重要的,“I love you”和“you love I(me)”表示了完全不同的意思,当然这个例子并不恰当,但是其阐明了单词在句子中的位置对句子的语义所造成的影响。

最新的位置编码RoPE展示了强大的能力,除此之外,位置编码的变体仍然有很多。但是下面我介绍这篇论文里提出的一种位置编码——正余弦位置编码。

\[ \begin{equation} \text{PE}_{i, 2k} = \sin\left( \frac{i}{10000^{2k/d}} \right) \end{equation} \]

\[ \begin{equation} \text{PE}_{i, 2k+1} = \cos\left( \frac{i}{10000^{(2k+1)/d}} \right) \end{equation} \]

其中,i 是词在序列中的位置(从0开始),k 是编码的维度索引(从0开始),d 是位置编码的总维度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_1d_sincos_pos_embed(self, embed_dim, seq_len):
# Compute the positional encodings once in log space.
pe = torch.zeros(seq_len, embed_dim).float()
pe.require_grad = False

position = torch.arange(0, seq_len).float().unsqueeze(1)
div_term = (torch.arange(0, embed_dim, 2).float()
* -(math.log(10000.0) / embed_dim)).exp()

pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)

pe = pe.unsqueeze(0)
return pe

将位置编码(postion embedding)和经过嵌入层(Embedding Layer)的输入相加,以融合序列的位置信息。

编码器与解码器

编码器(Encoder)与解码器(Decoder)最大的区别在于解码器中多了一个交叉注意力(Cross Attention)的模块,编码器-解码器架构在后续的研究中,更多地被合并在一起或者称之为只使用编码器,下面对其中的模块进行分析,来看看它们到底是什么。

自注意力or交叉注意力? | 注意力计算

自注意力(Self-Attention)和交叉注意力(Cross Attention),在图一中被表示为多头注意力(Multi-Attention)。如何从本质上理解这两个概念?其实,对比这两个概念,我们看到,在输入注意力模块前,输入被分成三份,这也是论文中提到的查询(query),键(key),值(value)。注意到图1中,当查询来自解码器,而键和值来自编码器时,我们将这时的注意力称为交叉注意力。交叉注意力有助于查询向量从键、值中提取相关的信息(在翻译任务中,是指译文注意到原文中的信息,指导译文的合理生成)。

所以,多头注意力其实是注意力内部的实现方式,它可以应用在自注意力或交叉注意力中。在文中,计算注意力的方式又被称之为缩放点积注意力。

缩放点积注意力与多头注意力

Transformer
图2: 缩放点积注意力与多头注意力

计算缩放点积注意力和多头注意力的流程如图2所示,图2(左)中,查询q先和键k进行矩阵乘法,之后进行缩放,通过掩码mask来指导查询应该关注的内容,在编码器Encoder中,主要屏蔽掉无意义的token(如padding),之后经过softmax,最后和值v做矩阵乘法。用公式表示如下:

\[ \begin{equation} Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V \end{equation} \]

图2(右)可以看到,多头注意力就是多个缩放点积注意力的结果进行拼接concat,下面给出了实现代码可供参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

def scaled_dot_attention(q, k, v, mask=None):
# Calculate the attention scores
dk = q.size(-1)
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(dk)

# Apply the mask
if mask is not None:
scores = scores.masked_fill(mask == 0, -1e9)

# Softmax
attention = F.softmax(scores, dim=-1)

# Multiply by the value
output = torch.matmul(attention, v)

return output, attention

class MultiHeadAttention(nn.Module):

def __init__(self, d_model, n_heads, dropout):
super(MultiHeadAttention, self).__init__()

self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads

self.W_Q = nn.Linear(d_model, d_model)
self.W_K = nn.Linear(d_model, d_model)
self.W_V = nn.Linear(d_model, d_model)

self.W_o = nn.Linear(d_model, d_model)

self.dropout = nn.Dropout(dropout)

def forward(self, q, k, v, mask=None):
batch_size = q.size(0)

# Linear layers
Q = self.W_Q(q)
K = self.W_K(k)
V = self.W_V(v)

# Split the d_model into n_heads*d_k
Q = Q.view(batch_size, -1, self.n_heads, self.d_k)
K = K.view(batch_size, -1, self.n_heads, self.d_k)
V = V.view(batch_size, -1, self.n_heads, self.d_k)

# Transpose to get dimensions batch_size, n_heads, sequence_length, d_k
Q = Q.transpose(1, 2)
K = K.transpose(1, 2)
V = V.transpose(1, 2)

# Scaled dot-attention
scaled_dot, attention = scaled_dot_attention(Q, K, V, mask)

# Concat
scaled_dot = scaled_dot.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)

# Linear layer
output = self.W_o(scaled_dot)

return output, attention

无论是自注意力还是交叉注意力,它们计算注意力的方式都是一致的,仅仅是输入的q、k、v和mask不同。

更重要地,应该时刻关注哪一维度代表了token,注意力本质上是token之间的相互学习。例如,在视觉领域,通过patch之后的序列我们把其视为tokens,这些token之间相互注意代表了图像在不同patch之间的关注。

前馈网络

前馈网络(Feedforward Network)并不是什么很高级的东西,先展示代码,带你直观的理解前馈网络。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Mlp(nn.Module):
def __init__(self, d_model, d_ff, dropout):
super(Mlp, self).__init__()

self.fc1 = nn.Linear(d_model, d_ff)
self.fc2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(dropout)

def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.dropout(x)
x = self.fc2(x)
return x

本质上,前馈网络就是全连接层的组合,用来进一步提取序列中的信息。

残差连接与层归一化

在注意力模块以及前馈网络中中,包含了残差连接和层归一化。残差连接最早由He.等提出,成功解决了深度模型梯度消失和梯度爆炸的瓶颈,短路路径将输入直接加到输出,确保了即使是深层网络也能更好地保持信息流,这使得更大规模的网络构建成为可能。层归一化最初是为了解决深度神经网络训练中产生的“协变量偏移”问题,在最近几年不断改进,自适应层归一化被认为是一种更有效的方式来动态调整归一化参数,但与此同时也引入了较大的开销。

输出

最终,经过解码器之后的输出,在通过一个全连接层,得到的输出用softmax函数转化为概率形式。在不同的任务中,头部的网络会有所调整来满足任务的需求。

未来? | 现在!

注意力机制通过直接建立输入序列中任意两点之间的关联,解决了以往循环神经网络(Recurrent Neural Network,RNN)及其变种如长短期忆网络(LongShort-Term Memory,LSTM)的长距离依赖问题、串行计算的限制以及上下文信息的整合能力。在各种任务中,采用注意力机制的模型已经显示出比传统RNN模型更加优越的性能,现代神经网络架构已经越来越多地采用注意力机制。“近年来,注意力机制已被扩展到其他领域。例如,在计算机视觉中,VisionTransformer(ViT)通过将图像划分为多个区域块(patch,类似于文本的 Token)的方式,成功将注意力机制应用到图像识别任务,大量的实验和工作表明ViT方法的性能能够达到甚至超越最先进的卷积神经网络(CNN)方法。在多模态学习领域,StabilityAI及其合作伙伴提出的稳定扩散模型采用了卷积神经网络(以 U-Net 为核心架构)并融合了注意力机制,在文本到图像的生成中取得了显著的效果。同时,OpenAI的研究员以及Sora的联合领袖 William Peebles 等人开发的 Diffusion Transformer(DiT),将传统的 U-Net 主干网络替换为 Transformer架构,在图像生成领域展现了与稳定扩散模型相媲美的性能。这一突破性的研究不仅证明了 Transformer在处理图像生成任务时的有效性,也推动了后续将 Transformer 作为主干网络应用于文本到图像生成任务的深入探索。

看着屏幕左上角的发送按钮,我激动的按下,在朋友圈官宣我和我的博客,时间定格在2023年12月31日。


高考结束,和大多数人一样,我对大学充满了期待,我想象着未来四年的大学生活。


看着屏幕上的printf("Hello World!\n"),我点击运行键,黑窗弹出,命运的齿轮开始转动。


高考发挥一般,华北电力大学是我的第一志愿,尽管我达到了学校的最低分数线,但是已经没有选专业的余地了,被第六志愿专业机械工程录取,那时候C语言这门课在大一下半年才上,我第一次接触编程是在大一寒假,在此之前,我一度以为编程和黑客、制作游戏画等号,那些编程很厉害的肯定可以随便盗取别人的账号。

翻了两页《C Primer Plus》,房间里暖色的灯光让我昏昏欲睡,那时候我还不知道从入门到放弃(精通)这个词,但无论知道与否,新手小白都不应该从这本书看起。我放弃了,去MOOC上听顶顶大名的浙大翁恺老师主讲的《C语言程序设计》。如果让我列举在我以往的人生中所听过的最让人醍醐灌顶的课程,那一定有翁恺老师的一个位置。如果冠之以“最”,我觉得也没有什么不合适的,往后至今,是《C语言程序设计》这门课程真正启蒙了我这个小白,让我深深的爱上了编程。有必要炫耀一下,新学期开设的C语言课程,我没有听过一节,最后拿到了99分的成绩,这也很大程度上让我做出转专业的决定。


我看着推免名单上自己的名字,交织的回忆涌上心头。


两年的时间,不足以让我学到特别有用的东西,实话实说,我们学校CS学科建设也真的一般般,2024年才拥有博士点。大三下学期3月份开学,我陷入了困境,以我的能力,弃保并从现在开始备考,溺死在考研的汪洋大海中算是一个极小概率事件。但我还是犹豫了,我知道我其实对自己也不算自信,作为小镇做题家跻身到北京这座大城市里,我见识了太多惊才艳艳的同龄人。我没有底气,我去问我的父母,他们希望我稳定,对他们而言211高校已经可以找到很好的工作了。在大多数家长的眼里,似乎每个孩子的人生路线已经被他们规划,这条路平坦、安全、没有意外;这条路普通、平凡、安稳;甚至这条路就像流水线一样一眼望得到头。我没有得到支持,我不知道如果我考不上会怎样,我好像除去大一,这一年半的时间也没有学习到特别多足以支撑我找到一份满意的工作的技能。我没能说服自己,也没能下定决心,最终我仍然选择留在华电,攻读硕士学位。


“如果没有疫情,如果我再多学一点,我高考会不会发挥的更出色,我的人生会变得不太一样?”...

“如果报考志愿时,我第一志愿填了苏州大学呢(那年我也可以被苏大录取)?”...

“如果我没有转专业,在我原先的机械专业也挺好吧,专业第一,各种奖励也是优先我拿。”...

“如果我放弃保研,我现在是为上岸欢呼,还是在为一战失败而落泪。”...


我时常觉得自己的人生是幸运的,在人生大事上总会有奇妙的相遇与邂逅。


其实大学前三年,我好像并没有发现自己真正的兴趣,我因为喜欢编程转来了计算机专业,但这里似乎和我想象的世界并不一样。枯燥的课程,老旧的知识,老师的手里拿着十年前工业出版社的书,讲着已经被社会所淘汰的技术。讲台下的同学们睁着清澈的双眼,渴望学习到他们未来能够生计的技术。教堂的教父从来不会告诉台下的信徒,那些誓言和祷告不过是虚假的自欺欺人。同样,老师也不会告诉学生,今天你们学习的东西,称之为“历史”。在这巨大的草台班子上,每个人都在逢场作戏,我是戏中的观众,负责鼓掌和喝彩。

在网页上确定了学校的推免offer后,我有一瞬间的快乐的,但是那种快乐片刻便烟消云散,我告诉自己即便还在这里读研,但我依然可以通过找实习来锻炼自己,我一定可以的。

一封、两封、三封,简历都石沉大海。尽管我用了一假期的时间掌握深度学习技术,试图在OpenAI带火的人工智能领域分一杯羹,但这看起来就是一个笑话(实际上,现在的我再去看这件事,确实是一个笑话),初生牛犊,撞得头破血流,才吸取到一点宝贵的人生经验。

我的人生完了,我最害怕的事情要发生了,泯于众人,成为无数籍籍无名其中之一。


“hi,你可以叫我师兄,你学过pytorch吗?...”


2023年10月16日,我的导师让我和正在国外做博士后的长林师兄合作,和另一位学弟组建了一个AI课题组,看着师兄的简历,ReLER Lab、顶会、顶刊,中科大本科毕业,悉尼科技大学直博4年毕业,博士后...我无法抑制心中的激动,在混沌的地方呆了太久,忘记了世界的缤纷。我差一点沉沦,却又因机缘巧合被命运拉了一把。

小说里主角跳下了悬崖,但幸运地掉进了神秘的洞穴里,主角捡起了地上的武林秘籍,此后,便静心修炼,与日月争辉。

现在的我依然没什么拿的出手的成就,但我相信我能做到。或许以往的人生,成功充满了悲观主义色,但现在,我就是在一点点地做,少想一些,多做一些。我一直以为“学习是一件痛苦的事”一定是至理名言,是永恒不变的真理,但现在,对于我来说,深耕于计算机视觉,阅读和领悟别人的工作,是一件倍感幸福的事。“做你真正喜欢的工作,你就会有无穷的动力。不同于以往你被迫地学习、考试、满足学校、社会、家庭的各种KPI。现在你为自己而活,你真正地在为自己做一些事情。而这些事情,或许在某一天,it takes your breath away.”

Hello World

我开始写博客,并决定要坚持下去,不一定是和技术相关。我要把自己想的东西记录下来,把我想说的用文字描述下来。当我开始写的时候,我才意识到自己的表达能力有多么糟糕,我觉得我有很多丰富的经历,但是当我和朋友讲述的时候,却只能用只言片语描述。我失去了人类最重要的工具——表达能力,语言匮乏,才是一个人真正的贫瘠,我立志要讲好故事。写博客和写公众号文章不一样,这种内容的关注度有限,但我能否凭一己之力改变搜索推荐机制呢?就看我以后能写出什么样的文章吧。写我所想,记我所思。或许某一天我小有成就,又或许某一天我觉得人生碌碌无为。我想看看,如果那一天到来,我是否也在社会这个江湖上闯出一些名声。

如果人终将毁灭与自己所热爱的东西,那么在此之前,请让我沉沦。

0%