返回博客

2026.09.13

十四个人在问同一个问题,没有一个人跑完那个对照

九月初 MaleCNS 数据一放出来,十四个项目把果蝇大脑接进了 Doom、Minecraft、马里奥、赛车。我扒了这十四个仓库的方法文档,发现一件事:所有人都在问「生物连线到底有没有用」,但没有一个人跑完「真连线 vs 保度随机重连」的成绩对照。我跑了,双环境十组,打平。

连接组强化学习实验方法对照设计

九月初有个东西在推特上炸了:果蝇的大脑在玩 Doom。

接着是 Minecraft、超级马里奥 64、赛车、拳皇、Chrome 小恐龙。Google AI 官方账号转了一波。全都跑在同一份数据上——MaleCNS v1.0,一只雄果蝇的完整中枢神经系统,166,700 个神经元、3.1 亿个突触接触,六月放出来,九月三号论文上 Cell。

我扒了十四个项目的仓库文档。所有人都在问同一个问题:生物进化调出来的连线,比随机接线强吗?

没有一个人跑完那个对照。

不是没想到。FlyDoom 把保度随机重连的代码都写好了,跑完的结果表没有。Flyhard 把它列在「E08 未来计划」。Connectome Fighter 的协议文档里写了,状态页写着「尚未证明生物结构优势」。

我跑了。两个游戏、十组对照、三层判据。打平。

想直接看数字的去第 3 章。想知道我怎么被自己的探针骗了两次、以及第二次骗得多离谱,看第 5 章。

1. 十四个项目,和那个共同的空白

先说这批项目在干什么。它们的形状惊人地一致:拿连接组的连线当固定网络,前面接一个手写的感官编码器(把游戏画面变成神经元输入),后面接一个手写的动作解码器(把神经元活动变成按键),中间那坨真实连线不动。

然后问:这坨真实连线,有用吗?

要回答这个问题只有一种方法——把连线打乱,其他全不变,再跑一遍。打乱也有讲究,不能瞎连:得保住每个神经元的入度和出度(谁收多少条、发多少条不变),只换「接给谁」。这叫保度随机重连。不这么做的话,真实连线赢了你也说不清是拓扑的功劳,还是单纯因为某几个神经元连得特别多。

我逐个读了十四个仓库的 README 和方法文档。按「有没有跑完这个对照」分类:

代码写了,结果没有:FlyDoom。仓库里实现了保度重连和匹配的随机图基线,README 写着「A null or negative result is a valid answer; this project does not assume that biological topology helps」。这是研究态度,不是已得结果。

列在未来计划:Flyhard。原文写得很清楚:「It does not establish that fly topology is better than a shuffled graph or an ordinary policy.」

协议有了,成绩没有:Connectome Fighter。状态文档承认尚未证明塑性带来可复现的行为改善,也未证明生物结构优势。

主动声明没测:Swat 的作者写了一句我很欣赏的话——「No graph advantage over shuffled wiring has been claimed or tested.」

真的跑了,但只跑了零样本:fly-craftax。这是唯一一个有真实对照数字的:

真实图      生存 154.8 步
打乱图      生存 164.9 步
断开连接    生存 168.0 步

作者自己的判词是 no separation。三档打平,甚至断开连接的还最高。但他只做了零样本(不训练直接跑),没做「真实图和重连图各自等预算训练一遍」。

剩下九个,要么在比「塑性开/关」,要么是固定网络的交互演示,要么在做输入输出的因果检查(把视觉输入掐掉,看动作会不会变)。都是有价值的工作,但都不回答拓扑那个问题。

这里有个我自己差点搞混的地方,值得单独说:「学习规则有没有用」和「拓扑有没有用」是两件事。

FlyPong 报告了负结论——它的多巴胺塑性实验没让球拍打得更好。Doomfly 报告得更狠,学习不但没用,还有害:一个测试起点上,学习分支和时序打乱分支都在 3.657 秒死掉,冻结权重的分支活到了 8 秒上限,擦除记忆之后又恢复成冻结分支的表现。原文用词是 the observed difference was harmful

但这两个比的都是塑性开关,不是拓扑。它们证明「这套局部学习规则没收益」,不证明「这坨连线没收益」。我第一次看列表描述的时候就混淆了这两件事,扒完仓库才纠正过来。

2. 我接的那个回路

我没接全脑,只接了蘑菇体——果蝇的学习中枢,苍蝇记住「这个气味之后会挨电击」就靠它。

从 MaleCNS 里提出来是这样:

投射神经元 ALPN     686 个    ← 感官输入进来
凯尼恩细胞 KC     4,064 个    ← 中间层,稀疏编码
输出神经元 MBON      97 个    ← 决策输出
多巴胺神经元 DAN    340 个    ← 奖惩信号
                 ─────────
                 5,187 个节点

中间那两条连线是真数据:PN→KC 22,586 条边、KC→MBON 61,210 条边。

生物学上这套回路的工作方式很直观:气味进来激活一批 KC(每次只有 200 个左右被激活,非常稀疏),如果这时候挨了电击,多巴胺神经元就会把「刚才活跃的那批 KC」到「趋近型输出神经元」之间的连接压低。下次同样的气味进来,趋近的驱动变弱,苍蝇就躲开了。

学习规则长这样:

dw = -eta × KC活跃度 × (多巴胺 - 基线)

三个因子相乘,全是局部信息——突触前的活动、突触后的通道、一个全局的多巴胺信号。没有反向传播。 这是这类项目最吸引人的地方:它是真的按生物学的方式学,不是把连接组当成一个初始化矩阵然后拿梯度下降去训。

我把它接到了 CartPole(那个平衡杆子的经典任务)和 LunarLander(登月着陆)上。

这里必须先说清楚一件事,因为它是所有这类项目的共同软肋:前后两端是我手搓的。 状态怎么变成神经元输入、神经元活动怎么变成按键,全是人工设计的。上游那个开赛车的项目在这一层做得更露骨——它的代码里有个函数叫 calibrate(target_action_overlap=0.5),意思是「把不同动作的神经编码重叠率调到 50%」,这是个手调的目标值。作者自己在文档里写了,把这个参数调好之后完赛率从个位数涨到了每分钟 100 圈。

所以接口层可以调出巨大的性能差异,而它跟连接组一点关系都没有。

这是我整个实验设计里最较真的一条:随机重连组必须用真实组算出来的那套接口参数,一个字都不许重新校准。 我把这条写进了参数校验里——脚本检测到你换了回路但没提供冻结接口文件,直接拒绝运行:

if args.circuit != DEFAULT and not args.interface:
    p.error('alternative circuit requires --interface; no independent recalibration')

写进注释是提醒,写进校验才是约束。

3. 两个游戏,十组对照,打平

先看随机重连做得干不干净。保度重连的质量有个直接判据——重连之后还剩多少条原来的边

PN→KC      原边保留   4.72%
KC→MBON    原边保留  25.64%

每个神经元的入度、出度、以及全部权重的分布完全没变,但接给谁基本全换了。不是换了个名字的原图。

CartPole,五组初始化各训 20,000 步(分数是杆子不倒的步数,越高越好):

          真实连线    随机重连
seed 1014    51.2       37.2
seed 1015    31.1       32.7
seed 1016    21.6       34.4
seed 1017    11.2       20.0
seed 1018    18.1       17.3
          ────────    ────────
均值         26.64      28.32

真实赢 2/5,均值还略低。

我当时的判断是 CartPole 太简单了——两个动作、四维输入,可能根本区分不出来。所以换了 LunarLander:八维输入、四个动作、奖励密集且有正有负。

          真实连线              随机重连
seed 1014  -434.7 → -359.6     -459.2 → -339.0
seed 1015  -375.9 → -265.0     -304.9 → -376.1
seed 1016  -450.1 → -263.6     -345.3 → -319.4
seed 1017  -792.4 → -368.9     -438.4 → -363.7
seed 1018  -786.1 → -344.1     -249.1 → -226.3
          ──────────────      ──────────────
均值       -567.8 → -320.2     -359.4 → -324.9
提升          +247.6              +34.5

这里有个陷阱,我差点掉进去:提升量差了七倍,看起来真实连线大胜。

但终点是 -320.2 vs -324.9,几乎一模一样。真实组涨了 248 分,只是因为它起点低了 208 分——它涨那么多,只是爬回到随机组几乎不用训练就在的位置。

同一组数据,选「提升量」当指标真实连线大胜,选「终点」当指标打平。 起点不同的时候,这两个口径会给出相反的结论,而两个都不算撒谎。这种时候唯一诚实的做法是两个都报出来,并说清楚为什么它们不一致。

还有一条比这更重要:两组都远差于随机乱按(-201.1)。 LunarLander 里乱点引擎会一直烧油扣分,贴着地不动反而比乱飞强。两组很可能都只学会了「少动」,谁都没学会着陆。在两个人都没做出这道题的时候比谁分高,意义有限。

4. 第三层判据:编码可分性

成绩打平有个可能的解释:瓶颈在学习规则,不在拓扑。那我绕过学习,直接测那坨连线产生的编码本身。

方法是这样:给它一堆游戏状态,看它的 KC 编码。然后问——从这个编码里,能不能读出「这时候该往左还是往右」? 用交叉验证训一个线性分类器,准确率越高说明编码保住的任务信息越多。

关键是标签必须跟被测模型完全无关。我用的是 gymnasium 库自带的 heuristic 控制器——上游库的代码,跟我这套东西零关系。

CartPole 上:

原始四维输入         98.64%
真实连线 KC 编码     98.87%
随机重连 KC 编码     98.86%
纯随机投影对照       93.59%

真实和随机差 0.01 个百分点。但注意那个纯随机投影落后 5 个百分点——说明稀疏编码本身是有用的,只是真实连线相比随机重连没有额外收益。

LunarLander 上,真实组的数字先跑出来,我当时写下了这么一句判断:真实 KC 编码 82.98%,比原始八维输入的 80.26% 高了 2.7 个百分点,任务变难终于有区分空间了。

然后随机组跑完:

多数类基线           33.32%
原始八维输入         80.26%
真实连线 KC 编码     82.98%
随机重连 KC 编码     82.96%
纯随机投影对照       82.94%

三个打平。连纯随机投影都追上来了。

那 2.7 个百分点是任何高维稀疏投影都能拿到的,跟连接组、跟生物拓扑全都没关系。我在只看到实验组的时候就急着解释现象,被自己的数据打了脸。

对照组没出来之前,任何差异都可能是方法本身的性质。

唯一稳定存在的差异是编码的稀疏度,这个不依赖任何拟合,是纯统计量:

             真实连线   随机重连
从未激活的 KC   1,601     2,778
状态间编码重叠   0.175     0.365

真实连线用掉了更多的编码空间,不同状态之间的编码更不容易混淆。这是个真实存在的结构优势,它在我测的任何一个功能指标上都没有兑现。

5. 我的探针骗了我两次

这部分比上面的结论有用,因为它可迁移。

第一次:那个叫「共识」的字段。

MaleCNS 的神经递质表里有好几列:predicted_nt(单个神经元的预测)、ground_truth(实验真值)、celltype_predicted_nt(细胞型层面的预测),还有一列叫 consensus_nt

我想验证一件事:神经递质的预测到底有多准?因为递质决定这个神经元是兴奋性还是抑制性,也就是连接的正负号——而符号错了,整个网络的行为就变了。

我挑了 consensus_nt 去跟 ground_truth 对账。8.5 万个样本,准确率 1.0000。完美的对角混淆矩阵,一个错都没有。

这不是好消息,这是警报。同一批数据上 predicted_nt 只有 88.61%,凭什么「共识」列能满分?

查下来是这样:有真值的时候 consensus_nt 100% 等于真值,没真值的时候 99.6% 等于细胞型预测。 它是个「有答案抄答案、没答案才预测」的复合字段。我拿它验证真值,等于拿答案验证答案。

这个诱饵特别甜,因为它叫「共识」——听起来正该是最可信的那一列。

换成干净的预测列重测,真实的符号翻转率是 0.1077%(细胞型粒度)。顺带说,这个数字让我下调了之前一个结论的措辞:我之前做过一个扰动实验,发现人为翻转 20% 的连接符号会让网络打哑,10% 是悬崖,当时写下「符号恰恰是连接组里唯一不是测量出来的东西,折扣正好打在最要命的地方」。现在看,符号确实是最脆弱的维度,但真实误差比致命阈值低了两个数量级。这句话说重了。

第二次:判据取错了层。

我在另一个全脑仿真实验里写了个防呆断言:提高输入速率,总放电数应该显著增加。跑出来:

50Hz 输入   →  全脑总 spike  447,012
300Hz 输入  →  全脑总 spike  469,164     只涨 5%

断言报警:速率参数没生效。

我差点就去修参数传递的代码了。拆开看:

被刺激的那 60 个神经元:   50.0 Hz  →  291.7 Hz     完全生效
其余的 8,386 个神经元:    52.9 Hz  →   53.9 Hz     纹丝不动

参数工作得好好的。全脑 99.3% 的活动是递归自激,跟外部输入无关。 那 60 个神经元的贡献只占总放电数的 0.6%,被淹得干干净净。

我拿全脑总量当判据,等于让 99.3% 的噪声去淹没 0.6% 的信号。

判据要取在被测变量直接作用的那一层,不是它所在的最大容器。 测某组神经元的驱动就量那组自己的放电率,不是全网总量。测某个接口的改动就量经过那个接口的请求,不是全站 QPS。

而这个误报本身比原本要测的东西更值钱——驱动涨 6 倍,全网响应只涨 1.9%,这是第三条独立证据链证实那个模型根本没有工作区间,只有自激态。别急着修掉一个误报探针,先问它为什么误报。

6. 怎么看这类 demo

这波浪潮里的项目质量差别很大,但有几个作者的自我描述相当诚实,值得学。

Chrome 小恐龙那个项目(Fly Dino)的作者写得最清楚。他的做法是取 80 个神经元的固定回路,外面接一个 243 参数的小网络当读出器,用交叉熵方法训练。结果是训练后 99/100 通关,把回路静默掉之后 0/100。看起来是个漂亮的正面结果,但他在文档里明确写了一句:not superiority of biological topology

他证明的是「外接的读出器能学会 + 神经活动确实被用上了」,不是「生物拓扑更强」。 这两件事之间隔着一个他没做的对照——把那 80 个神经元换成随机连的 80 个,同样训练一遍。

所以看这类 demo 的时候,四个问题按顺序问:

第一,那坨神经元是不是真的参与了决策? 判据是把它掐掉,行为会不会变。这一关大部分项目都过了,也是最容易过的一关。

第二,学会的东西存在哪? 如果存在一个外接的读出器里(PPO、CEM、bandit 训出来的那些参数),那学习全发生在人工零件上,连接组只是个不动的特征提取器。

第三,有没有随机重连对照? 没有这一步,前面全部结论都只能说到「这个系统能工作」,说不到「生物连线有贡献」。

第四,重连对照用的参数是重新调过的吗? 两组各自调参的话,比的就是「谁调得好」,跟连线没关系。这一步最容易漏,因为调参那个动作通常藏在一个叫 calibrate 的函数里,看着特别无辜。

7. 这个实验有哪些地方不能信

训练量严重不足。 20,000 步对 LunarLander 来说什么都不是。两组都卡在 -320 左右而随机乱按是 -201,说明谁都没学会着陆。在两边都没解出这道题的前提下比高下,结论的强度很有限。

只有五个种子。 配对差的均值是 +4.7,组内波动比这个数大得多。这点样本量只够说「我没看出差别」,撑不起「我证明了没差别」。

编码可分性只在 CartPole 和 LunarLander 上测过。 两个都是低维控制任务。蘑菇体本职是处理嗅觉——高维、稀疏、类别化的输入,跟这两个任务的结构完全不同。我可能在用错误的题目考它。

LunarLander 那个探针自己判自己没通过。 我设了三道门禁:已知可分的对照要高分、标签打乱后要掉回瞎猜、所有拟合必须收敛。四分类的主判据全过了(比基线高 47 个百分点,打乱后掉到 26-33%),但我另外加的一个二分类辅助标签没过——它比多数类基线只高 2.6 个百分点,置信下界是负的。探针因为这一项不合格就把整体标记成了 conclusion_permitted: false。上面那些数字我按它的标准称为观测值,不是获批结论。

接口层的所有参数都来自真实组。 这个设计保证了对照的公平(唯一变量是拓扑),但也意味着我测的是「在为真实连线调好的接口下,换成随机连线会怎样」。真实连线可能因此占了便宜,也可能吃了亏——两种方向都有可能,我没有测。

神经递质到兴奋/抑制的映射是建模假设。 不是逐突触测出来的。整个网络的符号结构建立在这个假设上。

8. 下次做这种对照,按这个顺序来

第一,先确认你要测的那个变量真的是个决策点。问自己一句:这一步完全不做,系统会不会照样跑对?会的话,测它等于给自己发奖。

第二,对照组的参数必须冻结,而且要把这条写进参数校验,不是写进注释。

第三,判据取在被测变量直接作用的那一层。量之前先问一句:这个分母里有多大一块跟我要测的东西根本没关系?

第四,标签或者参考答案必须来自跟被测系统完全无关的地方。名字听起来最权威的那一列,很可能是掺了答案的复合字段。

第五,给探针加双向自检——已知能分开的对照要接近满分,标签打乱之后要掉回瞎猜。只验一头,等于什么都没验。

第六,把「我有没有资格下结论」写成一个布尔字段。门禁不过就只报观测数字,明确拒绝下结论。调预算可以,放宽判据不行。

第七,任何要跑十分钟以上的批量任务,先真跑一个单元,测出耗时乘以总数,跟你的预算比。我有一次因为跳过这一步,选了个比基线大 130 倍的刺激群,跑了 2 小时 51 分,完成组合数 0。

第八,看到实验组的漂亮数字先别解释。对照组出来之前,那个差异可能只是方法本身的性质。