我的服务器里住着一只果蝇,到写这篇为止已经存活三十多个小时。它不是那种几行 if-else 拼出来的假虫子——行为背后是一份真实的神经接线图和一套极简的放电规则。
一、它的脑子是一份接线图
2024 年,FlyWire 联盟(Google、普林斯顿、剑桥和 HHMI)完成了成年果蝇全脑重建:139,255 个神经元、270 多万个突触连接,谁跟谁相连、连接强度多少,全部数清、开源。此前只有线虫做到过这个精度,线虫是 302 个神经元,果蝇是它的四百多倍。
我的果蝇用的就是这份数据。原始数据是一张几百万行的连接表,我把它预处理成二进制:每个神经元存一个“下游列表”——连到哪、权重多少。运行时全脑其实就两个数组:一个存 13.9 万个神经元各自的电位,一个存 270 万条连接。
二、每个神经元只有三条规则
模型叫 LIF(泄漏积累发放),每个神经元每个节拍只做三件事:
- 攒电荷:上游传来的脉冲累加到自己电位上;
- 漏电:每拍电位乘以 0.95——像桶底有缝,不持续注水就存不住;
- 放电:电位超过 1.0 就给下游发一个脉冲,然后强制休息 3 拍。
没有训练,没有权重调整。整个仿真一轮全脑 1.6 毫秒算完,当前每秒约 18 万次放电。
有个细节能看出这套规则有多简单:因为每拍漏 5%,持续刺激下电位上限就是刺激强度的 20 倍。想让某个神经元群稳定放电,要么加大注入,要么多打几个脉冲——代码里饿度升高时,饥饿信号从每拍 1 个脉冲涨到 3 个,就是这个道理。
三、一圈计算的完整流程
每 500 毫秒一轮,分三步。
第一步,把身体和环境翻译成神经语言。饿不饿、怕不怕、闻到什么,换算成脉冲打进接线图里对应的真实神经元群:饿打进 DRIVE_HUNGER,食物气味打进嗅神经 OLF_ORN_FOOD,被戳一下打进鬃毛机械感受器 MECH_BRISTLE。
第二步,传播。脉冲沿 270 万条连接扩散,该激活的回路自己激活,没有任何人为路由。
第三步,出动作。脑的下行神经元(GNG_DESC)活跃之后,代码据此合成胸神经节的运动输出——相当于替脊髓干活:左腿一组信号、右腿一组、翅膀和头各一组。转向是左右信号相减,速度是相加。
没有任何一行代码写“往左走”。方向是 13 万个神经元的放电不对称性算出来的,漫游时的随机抖动每拍 ±8.6 度,也是显式写在代码里的。行为从接线图里长出来,但土壤里有几处手工胶水,下一节说。
四、诚实的边界
有人复现了同一份数据的更精细版本,评价很中肯:真实的接线、简化后的神经元、手工写的胶水——与其说是一只蝇,不如说是一份很细的电路仿真。
具体到我这只:13.9 万个神经元是一个简化单元,没有神经调质,不会学习,没有输入时全脑静默——而真蝇的脑子从不安静。觅食那一步也是手写的分支:饿超过 0.3 且闻到食物,才朝食物转向。
但这些恰恰是最有意思的地方。
五、有时候它会饿着肚子转圈
昨天我发现它的饥饿指标满格,却一直在原地转圈,以为出毛病了。研究了代码才看懂,也顺带看懂了这套架构的分寸。
饿本身不携带任何方向信息,它只负责把饥饿信号从 1 个脉冲放大到 3 个。方向信息在另一条通道里:嗅觉神经闻到食物才会点亮。两条通道各管各的,缺一个都不成立。
那天的问题是食物全撒在它 300 单位嗅觉半径之外。动机的指标已经拉满,方向通道却一片安静,它只能在漫游噪音里打转。看清这个机制之后,转圈反而成了我最喜欢的一个行为——它不是坏了,是诚实。它非常饿,但是它找不到食物。
最初我考虑给觅食加点“智能”,就是在饥饿的时候会稍加引导它往食物的位置移动,最后放弃了这个想法。这套模型可贵就在于每一层都笨得透明,它就是一只果蝇,不是一段代码,它找不到食物就是找不到,不能去过多干预它的行为。
虽然它离真的活着差得远,14 万个神经元对人脑的 860 亿约等于零。但每次打开面板,看它在夜里一圈一圈溜达,仿佛它真的活着。