下一章 上一章 目录 设置
143、第143章:2016年1月,波士顿与旧金山的凌晨
【波士 ...
-
【波士顿,剑桥市,MIT人工智能实验室】
2016年1月12日,凌晨2点17分
陆星衍按下回车键,屏幕上的代码开始运行。实验室里只有他一个人,其他博士生要么早就回去睡觉了,要么今晚根本没来。白色的LED灯管发出均匀而冷冽的光,照在银灰色的机箱阵列上,那些机器正在嗡嗡低鸣,为他的神经网络训练提供算力。
窗外的波士顿冬夜漆黑如墨,只有远处查尔斯河对岸的哈佛校园还亮着几点灯光。实验室位于MIT斯塔塔中心的三楼,巨大的玻璃窗外能看到校园里光秃秃的树木和积着薄雪的小径。
陆星衍盯着屏幕上滚动的日志:
Epoch 100/1000
Training Loss: 0.437
Validation Loss: 0.598
梯度范数: 0.003
梯度范数太小了。小到几乎可以忽略不计。
他靠在椅背上,闭上眼睛,右手拇指和食指按在鼻梁两侧。这是他从高中就有的习惯,思考难题时会这样做。
梯度消失。深度神经网络训练中最经典也最恼人的问题之一。网络层数太深,反向传播时梯度信息逐层衰减,传到最前面的层时已经近乎为零,导致前面的参数几乎无法更新。
他试过几种方法:批归一化、残差连接、梯度裁剪。都有改善,但都不够彻底。模型在训练集上表现尚可,但在验证集上很快就过拟合了。
他睁开眼睛,从桌上拿起一叠草稿纸。最上面那张已经写满了公式:链式法则的展开,激活函数的导数,权重矩阵的更新规则。
他在空白处写道:“如果是清辞,会用动量优化。”
这不是随意想到的。是高三那年,他们一起准备信息学竞赛时,沈清辞解决过一个类似的问题:递归搜索时,在深层分支中容易迷失方向,找不到最优解。沈清辞当时提出了一个方法:给搜索过程加上“动量”,让它不会在某个局部停滞太久,而是能够跳出陷阱,继续探索。
“就像滑雪,”沈清辞当时解释说,“如果你在某个小坑里停住了,就永远出不来。但如果你有足够的速度,就能冲过去。”
陆星衍记得自己当时的回答:“动量在物理中是惯性,在算法中是历史信息的积累。”
“对!就是这个意思!”
那是2012年的冬天,云城一中的计算机教室里,暖气片发出咝咝的声音,窗外在下小雪。他们挤在一台电脑前,沈清辞的手指在键盘上快速敲击,陆星衍在旁边看着他写代码,偶尔提出修改建议。
四年了。
陆星衍放下笔,看向窗外。波士顿的雪比云城大得多,也更冷。但他记忆里最清晰的,还是云城那种湿冷的冬天,和计算机教室里那台老旧的暖气片。
他重新看向屏幕。动量优化。他试过了,Adam优化器本身就包含动量概念。但也许可以调整参数?或者尝试Nesterov加速梯度?
他打开另一个终端窗口,开始修改代码。手指在键盘上敲击的速度很快——这是他在华清四年训练出来的速度,也是……当年和沈清辞一起写代码时养成的节奏。
他们曾经比赛谁打字更快。沈清辞总是赢,因为他手指更长,而且有种天生的节奏感。但陆星衍的逻辑更严谨,bug更少。
“我们是完美组合,”沈清辞有一次说,“我负责速度,你负责正确性。”
陆星衍当时没说什么,但心里同意。
完美的组合。
但已经四年没有组合了。
【旧金山湾区,斯坦福大学格林图书馆】
2016年1月12日,晚上11点03分
沈清辞盯着屏幕上的递归函数,眉头紧锁。图书馆的这个角落很安静,只有他一个人,还有几排布满灰尘的旧书——这里存放的是1990年代以前的计算科学文献,几乎没人来看。
窗外是斯坦福的夜晚。加州冬天温和得多,没有雪,只有偶尔的细雨。路灯在湿漉漉的路面上投下黄色的光晕。
屏幕上的代码看起来很简单:
```python
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
```
经典的非波那契数列递归实现。问题出在性能:计算fibonacci(40)需要几秒,fibonacci(50)需要几分钟,fibonacci(100)理论上需要……他不想算。
作业要求优化这个函数,使它能快速计算fibonacci(1000)。
沈清辞试过几种方法:记忆化(缓存已计算的结果)、迭代法(用循环代替递归)、矩阵快速幂(数学优化)。但教授的要求是:“展示你对递归本质的理解,而不仅仅是绕过它。”
他靠在椅背上,揉了揉眼睛。从晚上七点坐到现在,四个小时了。桌上的能量棒包装纸和空咖啡杯证明了他的努力。
“阿衍说过这个问题可以用尾递归优化。”
这句话是自然而然出现在脑海里的。不是刻意回忆,而是像条件反射一样跳出来。
高三那年,他们一起准备数学竞赛,有一道题涉及到递归数列。陆星衍在草稿纸上写:“递归的本质是栈,但尾递归可以被编译器优化为循环,避免栈溢出。”
沈清辞当时没完全懂:“尾递归是什么?”
“就是递归调用发生在函数的最后一步,没有后续操作。”陆星衍用他那典型的、清晰的解释方式说,“这样编译器可以复用当前栈帧,而不是创建新的。理论上,尾递归的空间复杂度是O(1)。”
“就像……循环?”
“对,就像循环。但保持了递归的思维模式。”
沈清辞当时觉得这很酷。不是因为他完全理解了尾递归,而是因为陆星衍解释时的样子:专注,耐心,眼睛里有一种分享知识时的微光。
四年了。
沈清辞重新看向屏幕。尾递归。在Python中,尾递归优化并不像函数式语言那样原生支持,但可以手动模拟。或者……可以展示如何将普通递归转换为尾递归形式,然后解释为什么这种形式可以被优化。
他开始写代码。手指在键盘上的速度很快——这是他在斯坦福三年训练出来的速度,也是……当年和陆星衍一起学习时养成的习惯。
他们曾经共用一台电脑写代码。沈清辞负责输入,陆星衍负责审查。沈清辞的手指在键盘上飞舞,陆星衍的目光在屏幕上扫描,偶尔说:“这里,括号不匹配。”或者“这个变量名起得不好。”
“你怎么总是能发现我的小错误?”沈清辞有一次问。
“因为我看得仔细,”陆星衍说,“而且我了解你的代码风格。”
了解你的代码风格。
四年了,他的代码风格变了吗?应该变了。斯坦福教会了他更规范的命名,更清晰的注释,更模块化的结构。
但有些东西没变:他还是喜欢在复杂算法前写一行注释说明核心思路;还是习惯用i、j、k作为循环变量;还是在函数结束时喜欢留一个空行。
如果陆星衍现在看他的代码,还能认出是他的风格吗?
沈清辞写完尾递归版本的fibonacci函数,运行测试。fibonacci(1000)瞬间计算出结果,数字长得屏幕都显示不下。
成功了。
他看着那个巨大的数字,突然感到一阵失落。
成功了,但陆星衍看不到。
【波士顿,凌晨2点48分】
陆星衍的咖啡喝完了。他起身走向实验室角落的咖啡机——那是博士生们集资买的,一台半专业的意式咖啡机,能打奶泡,能做拿铁。
他拿出自己的杯子:MIT纪念杯,红色logo,是入学时学校发的。他从柜子里拿出咖啡豆,研磨,装入手柄,压实,放到机器上。按钮按下,浓缩咖啡缓缓流出,深棕色,表面有一层油脂。
他不需要加糖或奶。从高中起就喝黑咖啡,沈清辞总是说他“自虐”,然后给自己的咖啡加两包糖。
“你这样喝不苦吗?”沈清辞问过。
“苦,但清醒,”陆星衍回答。
“我加糖也能清醒。”
“糖会让人迟钝。”
“胡说!糖分是大脑的能量!”
那是高三的某个晚自习后,他们在学校门口的便利店买咖啡。云城一中的便利店很小,咖啡是速溶的,但对他们来说足够了。他们坐在便利店外的塑料椅上,看着冬天的夜空,喝着廉价的咖啡,讨论着当天的数学题。
陆星衍端着咖啡回到座位。热气升腾,在冷冽的实验室空气里形成白色雾气。
他拿起一支笔,在咖啡杯的纸套上画了一个图案:一颗五角星。很简单,几笔勾勒。
高中时,沈清辞有个奇怪的习惯:在草稿纸角落画小星星。问他为什么,他说:“标记重要的时刻。”
“做数学题算什么重要时刻?”
“每解出一道难题,都是一个重要时刻,”沈清辞认真地说,“都应该被标记。”
后来陆星衍也养成了这个习惯。在重要的草稿纸页,在解出难题的瞬间,画一颗小星星。
四年了,这个习惯还在。
他放下笔,看向屏幕。新的训练开始了,梯度范数有所改善,但还不够。他需要更激进的方法。
也许……可以试试沈清辞当年提出的另一个想法:“分层训练”。
那是一次讨论深度网络时,沈清辞随口说的:“如果一次训练整个网络太难,为什么不一层一层来?先训练第一层,固定它,再训练第二层,以此类推。”
当时陆星衍觉得这想法太简单,不符合深度学习端到端训练的理念。但现在想来,在某些情况下,这种“贪婪分层训练”也许有效,尤其是当梯度传播困难时。
他打开新的代码文件,开始实现分层训练框架。
窗外,波士顿的凌晨三点,雪开始下了。
【旧金山,晚上11点37分】
沈清辞也起身去冲咖啡。图书馆地下一层有24小时开放的休息室,里面有咖啡机、微波炉和自动售货机。
他拿出自己的杯子:一个在斯坦福二手店买的马克杯,蓝色,上面有褪色的斯坦福标志,1美元。他喜欢这个杯子,因为它有使用痕迹,不完美,像他自己。
咖啡是免费的,图书馆提供的,质量一般,但足够提神。他按下按钮,热水冲进速溶咖啡粉,形成深色液体。
他端着咖啡回到座位。热气在图书馆温暖的空气中不那么明显。
他拿起笔,在杯子的纸质杯套上画了一个图案:一颗星星,稍微有点歪,因为杯子是圆的,不好画。
高中时,陆星衍发现他有画星星的习惯后,曾经问:“你为什么总是画五角星?不画其他形状?”
“因为五角星最简单,”沈清辞说,“五笔就能画完。”
“但六芒星也简单。”
“六芒星要六笔,多一笔。”
陆星衍当时笑了——那种很少见的、真正被逗乐的笑:“你连这个都要省?”
“效率第一,”沈清辞理直气壮。
后来陆星衍偶尔也会在草稿纸上画星星,但总是画得很工整,每个角都很对称,像用尺子量过一样。
“你画得太完美了,”沈清辞有一次说,“星星不需要完美。”
“但数学需要,”陆星衍回答。
四年了,沈清辞画星星的技术没进步,还是歪歪扭扭。但他依然在重要的时刻画。
比如现在。写完了一个困难的算法,解决了一个技术难题,在凌晨的图书馆里。
他放下笔,看向窗外。斯坦福的夜晚很安静,偶尔有学生骑车经过,车灯在湿漉漉的路面上划出光带。
他想,此时此刻,陆星衍在哪里?在华清的实验室?在宿舍?还是……已经去了MIT?
他不知道。他们四年没联系了。
但他知道,无论陆星衍在哪里,一定也在努力,也在思考,也在解决难题。
因为他们是一类人。是被数学和代码吸引的人,是愿意在凌晨熬夜工作的人,是相信“重要时刻值得标记”的人。
沈清辞重新看向屏幕。作业完成了,但他不想离开。图书馆很安静,适合思考。
他打开一个新的文档,开始写一些与作业无关的东西:一个简单的加密算法,用他们高中时约定的规则——字母移位,加上日期作为密钥。
他输入:“2016.1.12。我在斯坦福图书馆。刚解决了尾递归问题。我想你。”
然后加密。输出是一串乱码。
只有知道规则的人能解密。
而知道规则的人,在很远的地方。
【波士顿,凌晨3点15分】
陆星衍的分层训练框架写完了。他运行代码,屏幕开始滚动新的日志:
训练第1层...
Loss: 0.892
训练第2层...
Loss: 0.761
有改善。虽然还不是最佳,但有改善。
他靠在椅背上,感到一丝疲惫,但也有一丝满足。解决问题带来的满足感,是纯粹的,不依赖任何外部认可。
他打开另一个窗口,写了一个简单的程序:生成分形图形。Mandelbrot集合,那个无限复杂又无限美丽的数学对象。
代码很简单,但运行需要时间。他设置好参数,让程序在后台运行。
然后他打开一个文本文件,开始写一些与工作无关的东西:一段数学证明的草稿,关于拓扑空间中的连通性。但写着写着,内容变了。
他开始写:“假设有两个点,在同一个度量空间中,但被一个闭集隔开。如果存在一条路径连接它们,那么……”
他停住了。
这不是数学。这是隐喻。
两个点,被隔开,但存在连接路径。
就像他和沈清辞。被太平洋隔开,被时间隔开,被沉默隔开。但存在连接吗?理论上,存在。一封邮件,一个电话,一次飞行。
但实际上呢?
他删掉了那段文字。
重新开始写真正的数学:关于神经网络损失曲面的拓扑性质。这才是他应该专注的。
但那个想法挥之不去:两个点,被隔开,但可以连接。
只需要一条路径。
【旧金山,凌晨0点08分】
沈清辞的加密程序写完了。他测试了一下,能正确加密和解密。
然后他做了一件有点傻的事:他加密了一整段话,关于他这三年在斯坦福的经历,关于洗碗,关于篮球,关于创业,关于想念。
然后他把加密文本保存起来,命名为“archive_20160112.txt”。
他知道这没有实际意义。陆星衍不会看到这个文件,即使看到,也不一定知道解密规则——四年了,他还记得吗?
但他还是做了。
因为有些事,不需要实际意义,只需要完成它本身。
就像在草稿纸上画星星。就像在咖啡杯上画星星。
标记重要时刻。
纪念存在。
他关掉电脑,收拾东西。图书馆的钟显示凌晨0点15分。
他该回去了。明天还有课,还有工作,还有生活。
离开图书馆前,他看了一眼那个画了星星的咖啡杯。纸杯套上的星星已经因为水汽有点模糊了,但形状还在。
五笔。五角星。
简单,但完整。
他拿起杯子,把剩下的咖啡喝完,然后扔进垃圾桶。
星星消失了。
但记忆还在。
【波士顿,凌晨3点30分】
陆星衍的Mandelbrot集合生成完了。屏幕上是一个绚丽的分形图案:无限的细节,无限的复杂,无限的美。
他盯着那个图案看了很久。
分形的特点是自相似:无论放大多少倍,都会看到相似的图案。
他想,人生是不是也像分形?在大的时间尺度上,有相遇,有分离,有等待。在小的时间尺度上,也有相遇,有分离,有等待。只是细节不同,但模式相似。
他和沈清辞,在大的尺度上:高中相遇,毕业后分离,现在等待。
在小的尺度上:每一天,每一刻,也许都在以某种方式相遇(在思想中),分离(在现实中),等待(在希望中)。
他保存了分形图像,命名为“mandelbrot_20160112.png”。
然后他关掉电脑,收拾东西。实验室的钟显示凌晨3点40分。
他该回去了。宿舍离这里不远,但波士顿的冬夜很冷,雪还在下。
离开实验室前,他看了一眼那个画了星星的咖啡杯。纸套上的星星很工整,每个角都对称。
他拿起杯子,把最后一口咖啡喝完,然后扔进垃圾桶。
星星消失了。
但证明它存在过的,是记忆,是习惯,是深夜里无意识的动作。
陆星衍穿上羽绒服,围上围巾,走出实验室。
波士顿的雪夜很安静,只有他的脚步声在雪地上咯吱作响。
他想,此时此刻,沈清辞在哪里?在睡觉吗?还是在某个地方,也像他一样,刚结束工作,走在回家的路上?
他不知道。
但他知道,无论沈清辞在哪里,一定也在努力生活,也在成长,也在……也许,也在想他。
因为他们曾经是彼此生活中那么重要的一部分。
重要到四年后,在波士顿的雪夜,在MIT的实验室,陆星衍还在用沈清辞教他的思维方式解决问题。
重要到在草稿纸上写“如果是清辞,会用动量优化”。
重要到在咖啡杯上画星星。
重要到……永远无法真正忘记。
陆星衍走在回宿舍的路上,雪落在他的肩上。
他想,也许有一天,他们会再见面。
到那时,他要告诉沈清辞:你这四年教我的东西,我一直在用。
不仅是高中时教的数学和算法。
还有:重要时刻要标记。
还有:在困难面前不要放弃。
还有:有些连接,即使被隔开,理论上也存在路径。
他抬头看天空。波士顿的冬夜看不到星星,被云层和雪遮住了。
但星星在那里。
就像有些人,即使看不见,也在那里。
【同一时刻,不同时区】
波士顿,凌晨3点50分。陆星衍走进宿舍楼。
旧金山,凌晨0点50分。沈清辞骑车回宿舍。
波士顿在下雪。
旧金山在下雨。
两人都在想对方。
都不知道对方也在想自己。
但宇宙知道。
时间知道。
那些在咖啡杯上画下的星星知道。