第二天早晨,顾老师没有给十五个人发新题,只让他们把前一天下午没做完的那道电磁问题带回来。
黑板上还是那张已经被他拆得很难看的区域图:边界弯弯曲曲,材料性质在不同位置也不一样,原先那些能让计算一下子变漂亮的对称条件几乎全被拿掉了。昨天江遥最后留下的想法,是把整个区域拆成许多小块,每一个小块只处理附近的信息,再让相邻区域彼此传递结果。她回宿舍以后又想过一阵,觉得这个办法至少有一个很直观的好处:如果一张巨大的地图难以一次看清,也许可以让许多人各守一小块地,每个人只和邻居交换消息,最后慢慢拼出整张地图。
顾老师开课以后却没有让他们继续算,而是在黑板上画了六个圆点,用线把相邻的点连起来。
“现在不谈电磁。”他说,“假设这六个人各自有一个数字,而且谁也看不见全局。每个人只能和直接相连的邻居说话。我们希望过一段时间以后,他们都得到同一个数,这个数接近六个人最初数字的平均值。你们会怎么做?”
这个问题一下子简单了许多。有人马上说,每个人把自己的数字告诉邻居,再取平均;下一轮继续交换,反复做下去,差距就可能越来越小。顾老师让他们真的试。六个人各代表一个点,把自己的数字写在纸上,每一轮只允许看左右相邻者,再更新一次。开始时有人是二,有人是十九,有人是负三,几轮以后,最大的数开始往下走,最小的数开始往上走,原来相差很大的六张纸渐渐靠到一起。
韩思遥不在核心组,这种课堂少了她一句“大家最后都变得没个性了”,反倒让江遥觉得有点安静。她看着那些数字逐渐接近,很容易理解这里发生了什么:没有任何人知道六个数的总和,也没有一个中央裁判负责计算平均值,可每个人只根据局部邻居做一点调整,整体依然可能慢慢达成一致。
顾老师等他们做完,忽然问:“如果不是所有人同时更新呢?”
有人抬头。
“什么意思?”
“刚才我喊一、二、三,你们一起看邻居,一起算,一起写新数。现在不喊了。有人算得快,有人算得慢;有人这轮已经写了新数,旁边那个人还拿着上上一轮的数据。消息传过去也可能晚一点。还会不会收敛?”
教室里一下安静下来。
江遥第一反应觉得应该还是会,只不过慢一些,可真正模拟起来马上出了问题。六个人不再统一听口令,而是各自按照自己“收到”的最新数字更新。有人两秒一次,有人五秒一次,顾老师还故意扣下一两张传递纸条,隔一会儿才递出去。整个过程立刻变得混乱:某个人刚把自己的值往十调整,邻居却还拿着他几分钟前的五继续计算;另一个人连续更新两次后,旁边的人才收到第一条消息。纸面上再也没有整齐的一轮、一轮,只有一串互相穿插的变化。
“现在谁能告诉我这是第几轮?”顾老师问。
没人能回答。
因为“第几轮”这个东西已经不存在了。
江遥突然觉得这个变化比前面的计算本身更有意思。刚才所有人同时更新时,他们其实偷偷拥有了一个全局时钟。虽然没有人知道全局数值,却至少知道现在大家都在进行同一步。这个条件太自然,以至于第一遍做题的时候几乎没人把它当成条件。等顾老师把统一节奏拿掉,他们才发现,所谓“只靠局部信息”还远远不够;你甚至不能默认别人和你生活在同一个时刻。
六个人又试了几轮。数值仍然有逐渐接近的趋势,但速度明显慢了,有时还会短暂往反方向走。一个节点刚接近大家,收到一条迟到的旧消息以后又被拉开。许观澜提出给每条消息加上时间标记,至少让接收者知道这是不是旧信息;陆徊则说,如果所有旧消息都直接丢掉,会不会有些节点因为网络太慢,长时间几乎不参与。简宁很快发现,真正的问题已经不是“怎么算平均值”,而是“在不知道别人什么时候更新、消息什么时候到的情况下,什么规则还能保证大家最后不要彻底跑散”。
江遥开始在灰色本子上画时间线。
她没有急着写公式,只画六条横线,每一条代表一个节点。某条消息从第一条线上发出,过了一段时间才落到第三条线上;另一条消息稍晚发出却先到。图画完以后,原本看起来抽象的“异步”突然变得像生活里极常见的事:两个人同时给第三个人发消息,先发的不一定先被看见;一个人正在依据旧信息做决定时,另一个人的状态可能已经变了。
顾老师这时才在黑板角落写下“异步”两个字。
他没有讲任何正式定义,只说:“真实系统经常没有一个人站在外面喊一、二、三。不同部分各做各的,消息也有延迟。如果一个办法只有所有人同时动作才成立,那它能用的地方会少很多。”
江遥抬头看着黑板,脑子里却忽然闪过很多并不属于这堂课的东西。蚂蚁不会等全体集合后再统一出发,鸟群转弯时也不可能先召开一次会议;城市里的五个传感器更不会保证每一秒都同时上传;甚至她和原既白现在的聊天,也越来越不像以前那样有一段完整、同步的晚上。她发出一句话时,他可能正在考试;他凌晨做完实验后留下的结果,她第二天早晨才看到。两个人并没有因此“失去联系”,只是联系早就不再依赖同一个时刻。
这个联想很快被她压下去。课堂上的问题首先还是课堂上的问题。
上午后半段,十五个人被分成三组,每组都要设计一套在这种“没人统一喊口令”的情况下仍能工作的局部规则。江遥所在这一组很快提出一个直观方案:每个节点收到邻居新值后,不直接替换自己的数,而只往对方靠一点;消息越旧,影响越小。这样就算偶尔收到迟到信息,也不至于一下把自己拖回很远。
第一次模拟效果不错。
第二次,顾老师让一个节点断开了三分钟。
剩下五个仍然逐渐接近,等那个失联节点重新接入以后,系统又开始调整。江遥原以为这是好事,可很快发现另一个问题:失联节点带回来的是一个和其他人差距很大的旧值,它重新加入以后,相当于整个系统突然又被塞进一块过去。大家当然可以慢慢把它拉回来,可如果节点不断掉线、重连,所谓“最终一致”就变得越来越难判断。
沈策说:“那就把掉太久的人权重降低。”
方禾马上反问:“降低多少?如果掉线的是最重要的数据呢?”
“数字哪有重要不重要?”
“现在是平均值没有。换成别的问题就有。”
江遥没有参加这两句争论,她正看着那条重新接入的时间线。她忽然想到,如果一个系统允许局部不断加入、退出,可能真正应该追求的就不是“某一刻所有人完全一样”。在持续变化的世界里,绝对一致或许本身就是不现实的目标。更有意义的问题可能是:即使每个人看到的都是稍微不同、稍微滞后的世界,整体是否还能保持在一个不会失控的范围内?
她把这个想法说出来以后,简宁先皱眉:“那不是降低标准吗?”
“也可能是换标准。”江遥说,“如果系统永远在动,你要求所有人任何时候都完全一样,可能本来就做不到。比如一群人往前走,每个人的位置肯定不一样,但可以保持同一个大方向。”
陆徊说:“那得先定义多不一样还算没散。”
“对。”
几个人又绕回计算。
顾老师一直到最后才点评。他没有宣布谁的方案最好,只指出三组都犯了一个相似错误:一开始大家总想恢复那个已经被拿掉的中央节奏,给消息编号、补时钟、等待慢节点,本质上都在试图重新制造“大家一起走”的条件。这些办法有时当然必要,但如果系统规模足够大、延迟无法控制,等最慢的人本身就会变成代价。
“有时候,”他说,“你们不是要把现实改造成一个同步系统,而是要学会在不同步里工作。”
这句话让江遥在灰色本子上停了很久。
她没有抄。
因为它太像一句适合被单独划线的话,她现在对这种句子已经本能地有点警惕。她只把上午那六条时间线继续补完整,在旁边写下两个问题:**什么时候必须等别人?什么时候不等也可以继续?**
---
原既白那边,几乎在同一周遇到了一个更加难看的版本。
自动测试系统经过前几轮改造以后,终于可以连续运行,不再因为磁盘写满或者单个任务卡死就整体停摆。宋弈于是做了一件他们之前一直没有胆子做的事:把两个不同版本的模型同时接进去,让测试器并行跑。
本意很简单。他们想比较同一批自动生成的测试,在模型A和模型B上分别会发现什么弱点。按原既白最初设计,每个任务生成一张测试图片以后,同时发给两个模型,两边都返回结果,再统一写入数据库。只要把模型版本记清楚,后面便能比较。
第一晚看起来一切正常。
第二天上午,结果开始出现无法解释的矛盾。
同一张图片在日志里有三次记录,两次显示模型A判断正确,一次却显示模型B输出;某些测试任务标记的是A版本,真正调用的模型进程却已经在半小时前被替换成新权重;还有一批图片生成时依据的是旧模型的弱点,等真正进入评测队列,模型已经更新,结果当然和搜索时完全对不上。
宋弈看着数据库里那些互相打架的记录,第一反应是:“是不是写错标签了?”
查了半天,标签本身没有写错。
真正的问题出在时间。
模型训练、测试生成、任务调度和结果写入四件事并不是按一条整齐的流水线一步一步发生。有些测试任务生成很快,有些慢;模型更新后,队列里仍然躺着按照旧版本生成的任务;某个进程重启以后拿到的是新模型,任务本身却还认为自己在测试旧模型。每一个小模块单独看都“做对了”,组合起来却产生了一批连他们自己都无法再解释的结果。
原既白第一次完整画出时间线时,沉默了好一会儿。
宋弈说:“要不每次模型一更新,就把旧队列全清掉。”
“可以。”
“那不就完了?”
“旧任务全浪费。”
“总比结果不能信好。”
原既白没有反驳。他一开始也倾向于这么做:模型版本变了,就停掉所有测试,清空队列,从头开始。这样最干净,最容易保证一致。可他们很快算了一下,如果以后模型更新频率更高,长时间任务不断被清空,系统大部分时间都在重复做已经做过的事。更何况有些旧任务本身仍有价值,因为比较同一测试在新旧模型上的变化,恰好能告诉他们模型升级以后哪些弱点消失、哪些弱点又出现。
真正需要做的,不是让整个系统每次都停下来等所有东西对齐,而是让每一条结果都能回答:它是在什么模型、什么数据、什么测试器版本和什么时间条件下产生的。
宋弈听完说:“所以不要求大家一起变,但必须知道自己跟谁生的?”
原既白抬头看他。
“你这个说法很难听。”
“能懂就行。”
确实能懂。
他们开始给每一个结果附上完整的“来历”:这张测试图由哪个测试器版本生成,生成时针对的是哪个模型,真正执行时用的又是哪一个模型,数据集版本是什么,任务何时进入队列、何时运行。原本一行结果只需要图片编号和预测值,现在后面多出一长串看起来很烦的元数据。
宋弈抱怨说他们是在给每一张图片办身份证。
原既白却越来越觉得这些“身份证”比想象中重要。一个数字本身几乎什么都不说明。模型准确率从九十二降到八十八,如果不知道测试集有没有变、模型版本有没有变、数据处理有没有变,这四个百分点根本不能直接比较。系统越复杂,“同一个结果”背后可能来自的路径越多,而路径一旦丢掉,很多事实就会变成看起来精确、实际上无法追溯的数字。
叶老师看完新版以后,问的第一件事是:“如果一个任务跨了两个模型版本怎么办?”
原既白已经有准备:“结果不覆盖,分开记。”
“如果测试生成基于A,执行时模型已经是B?”
“也记清楚。它不再是A的纯测试,但可以作为A生成、B执行的一类迁移测试。”
“谁解释?”
“报告层解释,不让底层数据自己假装是一回事。”
叶老师这次没有继续追。
宋弈等视频挂断以后,靠在椅背上说:“你现在越来越喜欢把不能比的东西写成不能比。”
原既白笑了:“总比硬比好。”
“以前你最爱统一。”
“现在也爱。”
“看不出来。”
原既白把桌上的几张时间线叠起来,没有回答。统一当然仍然是他的本能,但这几个月他越来越明白,真正可靠的统一不是把差异藏掉,而是先知道差异在哪里。如果两个结果来自不同版本、不同路径、不同时间条件,最偷懒的办法是给它们一个共同名字然后直接比较;真正困难的是保留差异,同时还找到更高一层可以成立的共同结构。
下午学校有第三次高三模拟考成绩分析。
原既白这次年级第二。
第一名是同班一个长期稳定的女生,名字叫林澄。她没有原既白在数学上的极端优势,也不参加算法项目,却几乎没有明显短板。语文比他高十一分,英语高七分,数学低六分,理综低三分,加起来仍然领先。成绩贴出来以后,班里有人半开玩笑地说原既白终于被“正常高考生”打败了,他也笑了笑,并没有太在意第二这个数字。
真正让他在意的是秦老师把两个人的分项放到一起以后说:“你要是把所有时间都用在把数学从一百四十七提到一百五,也不一定比把语文多拿八分划算。”
原既白说:“我没有在补数学。”
秦老师看着他:“那你晚上在干什么?”
“项目。”
“我知道。”
秦老师没有要求他停掉。他沉默了几秒,只说:“你自己选。但是选择不是写在纸上说我都要。一天就是二十四小时。”
这句话并不新鲜。
可原既白回座位以后还是把最近一周的时间重新算了一遍。机器学习项目平均占去两个晚上,有时遇到故障会拖到第三个;训练营那边江遥进入核心组以后,聊天虽然更少,却也经常在他准备睡觉时出现一个让他忍不住继续想的问题。高考复习的时间没有突然减少很多,真正减少的是那些看起来很零碎的空档:晚自习结束后二十分钟,午休没睡的半小时,回家以后原本可以发呆的一小段时间。
他第一次认真考虑,要不要把项目再压缩一点。
这个决定并不让人舒服。因为和高考不同,机器学习项目没有明确分数,也没有一张倒计时告诉他“错过以后就没机会”。恰恰因此,更容易被现实中有明确截止日期的事情一点点挤掉。
晚上他给叶老师发消息,说接下来两个月只能固定保留每周两个晚上,不再额外加时。原本以为对方会觉得投入不够,叶老师却很快回:“可以。把能持续的边界先定出来。”
原既白看着那句话,忽然想起陈叔的六桌。
这一次他也没有把它变成什么人生道理,只在自己的时间表上把周三和周六晚框起来,其余时间全部留给高考。项目如果在周三晚上没解决,除非系统完全不能运行,否则不挪到周四继续。
第二天他第一次真正执行这个规则时,就碰上了一个没解决完的问题。
测试队列里出现一批跨版本任务,按照新设计可以追溯,却还没有决定报告层如何统计。宋弈说再做半小时就能搞定,原既白看了一眼时间,已经十点二十。
“周六再弄。”
宋弈愣了一下:“你能忍?”
“不能。”
“那你还走?”
“明天英语早测。”
宋弈盯着他几秒,笑得非常欠揍:“爱情和高考终于联手把你改造成人了。”
原既白关电脑:“跟爱情没关系。”
“那江遥呢?”
“在训练。”
“更惨,女朋友都没空管你。”
原既白把机房灯关了。
---
训练营第一次正式的核心组内部排序安排在周末。
这一次十五个人不再只看一个分数,而是三部分:传统理论一百六十分,实验基础一百分,开放建模由三名老师分别评价,最后再综合考虑。顾老师提前说得很明确,开放建模不会因为想法“新”就自动高分,一个新表示如果不能验证,甚至不如老老实实用旧方法。
江遥反而因为这句话轻松了一点。
她越来越清楚,自己真正需要防的不是“不够聪明”,而是太容易喜欢一个刚长出来的结构。前几天异步课堂已经给了她新的提醒:很多漂亮结论成立,是因为我们偷偷假设所有人共享同一个时间;一旦条件拿掉,问题会完全变形。她现在做题时开始有一个近乎机械的小动作——每当自己突然觉得“原来如此”,先在草稿旁边写一句:**我偷偷用了什么条件?**
传统理论考试里,这个动作让她少犯了一个很险的错误。第三题给出的系统在前半段可以近似看成稳定状态,她几乎顺手把这个假设带进后半段,写到第二行时停下来检查,才发现题目已经改变外部条件,原来的近似不再有效。她退回去重算,浪费了不到五分钟,比真正做错以后再返工划算得多。
实验部分没有惊喜。
她做得稳定,也只做得稳定。
第二项有一个读数比预期偏高,她检查连接、重复测量,再把异常保留进误差分析;成绩出来以后仍然大致处于核心组中游。江遥看到分项时几乎没有情绪波动,反而觉得这正说明过去一年那段最辛苦的补短板已经完成。实验不再决定她能不能继续走,却会一直站在旁边提醒她,理论世界之外还有一张不会配合想象的桌子、一根会漂的探头和一组难看的数据。
真正拉开差距的是开放建模。
题目只有一个日常情景:一栋宿舍楼每天晚上用热水的人很多,热水供应能力有限。过去的方法是按历史平均需求设定加热时间和储水量,可实际每天人数、洗澡时间和天气都会变化。现在要求他们设计一种“比固定时间表更聪明”的控制办法,同时说明它什么时候会失败。
江遥看到题目的第一反应不是上模型,而是先问:系统真正想要什么?
如果只追求“绝不缺热水”,最简单的办法就是全天保持大量热水,可能源浪费会非常严重;如果只追求省电,最简单的办法是少加热,用户晚上就会洗冷水。于是这个系统从一开始就不是只有一个目标,而是在舒适、能耗和风险之间做选择。
她先写了一个最笨的方案:按照过去一周同一天的用水情况预测今晚需求。这个方法人人都能懂,也肯定会有用。接着再加天气、宿舍入住人数、节假日等信息,预测会更准一些。做到这里,她完全可以继续往复杂模型上走,但异步课留下的问题突然冒出来:这些信息到达的时间并不一致。人数统计可能早上就有,天气预报会不断更新,真实用水数据则是一分钟一分钟进来。如果控制系统必须等所有信息“齐了”才动作,它可能永远等不到一个完美时刻。
她于是把方案改成两层。
第一层是一个很保守的底线,不管预测怎么变,都保证基本热水量,不让系统因为一次预测错误直接失控;第二层才根据不断到来的新信息逐步调整加热。晚高峰开始前发现实际用水比预计多,就提前补一点;天气突然变冷,再调整一点;如果传感器坏了,就退回那条不聪明但可靠的底线。
这个办法并不华丽。
甚至有点像陈叔的六桌:先知道自己不能坏到哪里,再谈如何更聪明。
老师问她:“为什么不直接让模型实时决定全部加热量?”
江遥说:“因为模型如果错了,用户晚上会真的没热水。它可以负责多聪明一点,但不应该同时拥有把底线也删掉的权力。”
“那如果底线定得太高,节能效果就没了。”
“所以底线本身也要根据长期数据改,但不能和每一分钟的预测一起乱动。”
另一个老师追问:“谁决定这个底线?”
江遥停了一下。
这个问题和原既白前几天问机器学习目标时其实很像。她可以给出技术上的风险概率,也可以算储水量,可“允许多少人偶尔等十分钟热水,换来多少能源节约”并不是纯数学答案。
她说:“最后应该是使用这栋楼的人和管理者一起定。模型可以告诉他们不同选择会发生什么,但不能替他们假装这个取舍不存在。”
三名老师没有继续追问。
江遥走出教室以后,自己却还在想这句话。她原本以为人工智能最吸引她的地方,会是那些复杂得像生命一样的系统如何从简单规则里长出来;现在她开始看见另一半:一个系统就算真的很聪明,也总得回答它替谁做事、什么东西不能为了优化而牺牲。
这并没有让问题变得不美。
反而更难了。
成绩第二天上午公布。
十五人里,江遥传统理论第六,实验第九,开放建模第一,综合第三。
榜单贴出来的时候,没有人尖叫。
前几轮名次已经让所有人逐渐适应这种变化。简宁传统理论第二,实验第十一,开放建模第七,综合第五;许观澜几乎没有明显短板,综合第二;第一名是另一个江遥此前不太熟的男生方启,理论第三、实验第四、开放建模第三,三个方向都很强。
江遥从头看到尾,在自己的综合第三停了一会儿,又去看方启那一行。
她第一次非常清楚地意识到,自己正在接近最前面,却仍然不是“最完整”的那个。开放问题让她的天赋越来越明显,可在一个需要多种能力共同支撑的筛选体系里,她仍有很多地方不如别人。这个事实没有削弱那种上升感,反而让它变得更可信。
顾老师下午只说了一句:“第三不是结论。”
江遥以为他是提醒不要骄傲。
结果他接着说:“第一也不是。”
大家都笑了。
训练营距离下一轮更小候选组还有时间,没人会因为一次排序直接进最终五人。顾老师也没有告诉他们多少名能留下,只说后面题目会越来越少,问题会越来越大。
晚上,江遥把成绩发给原既白。
“理论6,实验9,开放1,综合3。”
那边没有马上回。
二十七分钟以后,原既白才发来:“刚做完英语。”
江遥:“多少?”
“还不知道。”
“那你先评价我。”
原既白隔了一会儿:“实验终于不涨了。”
江遥盯着这句话,笑出声。
“你就看见这个?”
“还有开放第一。”
“然后?”
“比较像你。”
江遥没有问为什么。
她其实知道。
过了一会儿,她问原既白最近模拟考怎么样。他把年级第二发过去,没有附任何解释。
江遥回:“比较不像你。”
原既白:“?”
“我以前以为你会一直第一。”
“谢谢。”
“现在觉得第二也挺像。”
“为什么?”
江遥想了很久,本来想说因为世界不会围着任何一个人转,也想说因为他现在在做别的事情,还想说年级第一那个女生本来就很强。最后都没有发。
她只回:“因为有人比你考得好,很正常。”
原既白看着屏幕笑了一会儿。
他回:“这句话也送给综合第三。”
江遥:“不一样。”
“哪里不一样?”
“我以后会第一。”
原既白这次隔了很久才回。
“行。”
只有一个字。
江遥看着它,知道他不是在哄她。
她把手机放到桌边,打开灰色本子。前几页已经画满时间线、局部节点、蚂蚁路径和热水系统。绿色燕子本里那些少年时代的公式和句子显得更私人、更杂乱,这本灰色的新本子却开始出现一种她自己还没有察觉的变化:她越来越少记录“答案”,越来越多记录问题之间的关系。
她在今天成绩下面没有写名次。
只补了一句:
**不同的地方,不一定非要同时变成一样。**
写完以后,她看了两秒。
还是觉得太像一句可以被人摘出来的话。
于是又划掉。
页面最后只剩下六条彼此错开的时间线。