把 13.9 万个果蝇神经元搬进 Mac mini:不给任何输入,它会自己动起来吗?
把 13.9 万个果蝇神经元搬进 Mac mini:不给任何输入,它会自己动起来吗?
2026 年 3 月,Eon Systems 发了一篇「上传果蝇」:用 FlyWire 全脑连接组搭一个脉冲神经网络,接上物理仿真的果蝇身体,让它在虚拟世界里觅食、梳理、逃跑。演示很震撼,但关键的「神经元怎么变成动作」那一层没有开源。
我想弄清楚一个更朴素的问题:把这只果蝇的脑复刻出来以后,它能自己生成什么行为? 于是用他们用到的开源组件在一台 Mac mini 上拼了一套,给输入测了一轮,不给输入又测了一轮。
先给结论:
- 给输入时,连接组本身就能把感觉转成可用的运动信号。 腿部尝到糖,0.66 秒后果蝇转向进食;复眼看到黑球逼近,1.89 秒巨纤维放电、身体后退。
- 不给输入时,原模型完全静默——它是个被动的响应器,不会自己动。
- 补上膜噪声 + 放电适应这两项「生理常识」后,它确实会自发地在静止、前进、后退、梳理、惊跳之间切换。但第一版的节奏像节拍器;第二版改了三处,节律才变得不规则、片段有长有短,更像活的动物。
- 所有这些「行为」都经过一层我手写的低维接口(下行神经元 → 动作),不是运动神经元级别的仿真。这一点决定了你该怎么读下面的数字。
问题背景:「上传」到底上传了什么
我复刻用的组合由四块组成:前两块是 Eon 文章里用到的开源项目,flybody 是我额外加的飞行身体,最后一块 Eon 没有开源:
| 组件 | 作用 | 本文用法 |
|---|---|---|
| FlyWire v783 + Shiu 2024 LIF 模型(Eon fly-brain) | 138,639 个神经元、约 500 万条突触的全脑脉冲网络 | 原样使用,Brian2 C++ 后端 |
| NeuroMechFly v2(flygym) | MuJoCo 里的果蝇身体 + CPG 步态 | 行走、转向、后退 |
| flybody(Janelia / DeepMind) | 另一套身体,偏飞行 | 飞行素材,本文略过 |
| 下行神经元 → 运动指令 | 把脑的输出翻译成腿的动作 | 未开源,我自己写 |
FlyWire 只覆盖脑,不含腹神经索(相当于脊髓),所以脑和腿之间天然断了一截。脑能给出的最后一层信号是下行神经元(DN)的放电,怎么从 DN 走到关节力矩,每个复刻者都得自己补。
问题分析:「自动生成」要拆成两个问题
「复刻后能自动生成什么」其实是两件事:
- 刺激驱动:给感觉输入,脑能不能算出方向正确的运动信号?这检验的是连接组的「接线」。
- 自发行为:什么都不给,它会不会自己动?真实果蝇放在空盒子里也会走、停、梳理,这是动物和反射机器的分界线。
第二个问题更难,也更有意思。Shiu 2024 的 LIF 模型是为「刺激某群神经元,看哪些下游被激活」设计的,没有背景活动——没有输入就一个脉冲都没有。要它自己动,就得在方程上加东西,而加什么、加多少,都会直接影响结论。所以我给自己定了一条规则:只加有生理依据的最少项,每加一项都用数字说明它改变了什么。
技术方案与选型
脑的后端:Brian2 C++,排除 GPU。 我在本机实测了三种后端跑 0.1 秒脑时间:
| 后端 | 耗时 |
|---|---|
| Brian2 C++(CPU) | 1.26 s(首次另加约 8 s 编译) |
| PyTorch MPS(Apple GPU) | 9.6 s |
| PyTorch CPU | 36.8 s |
MPS 没有 CSR 稀疏内核,只能退化成 gather + index_add,结果比 Brian2 慢 7 倍,批量跑还随 batch 线性变慢——稀疏矩阵乘向量是内存带宽受限的任务,统一内存的 GPU 占不到便宜。局域网里另一台机器是 Intel Arc 核显,没有 CUDA,预期不会好于 MPS,也排除了。真要加速只能租 NVIDIA 跑 Brian2CUDA。
感觉输入:只用味觉和触角机械感受,排除嗅觉与温湿度。 扫了一遍各感觉通道:嗅觉 ORN、温度、湿度感受神经元哪怕只给 30 Hz,就让约 8000 个神经元进入饱和态,而且左右刺激结果一样(嗅觉双侧投射),拿来导航等于没有方向。味觉和触角 JO 神经元的下行响应是左右分离的,可用。
下行接口:只读功能明确的几组 DN。 前进 DNp09/DNg100、转向 DNa01/DNa02 左右差、后退 MDN、梳理 DNg11/DNg12、逃逸 DNp01(巨纤维)。转向信号扣掉 DN 群体固有的约 +0.19 左偏——这个偏置是标定出来的,不扣掉果蝇会一直左转。
排除项: 不做运动神经元级仿真(没有腹神经索数据);flybody 的训练策略是 TF 2.8,Apple Silicon 装不上,只导出权重用 numpy 推理,行走策略因缺 3 GB 参考轨迹数据没做。
落地/实测过程
一、刺激驱动:连接组的接线是通的
先不接身体,直接刺激两组神经元各 0.3 秒脑时间:
| 刺激 | 被激活神经元 | 脉冲总数 |
|---|---|---|
| 糖味觉 GRN,200 Hz | 384 | 5,125 |
| P9 前进下行神经元,100 Hz | 85 | 291 |

糖刺激扩散得很广(13.9 万里有 384 个被点亮),这符合「进食是全脑级决策」的直觉;刺激 P9 这种已经在输出端的神经元,涟漪就小得多。
然后把身体接上,做两个闭环:
味觉趋食:果蝇腿上的味觉神经元按左右浓度差放电 → 全脑 LIF → DN 左右差 → CPG 左右步幅。脑和身体每 15 ms 同步一次。3.48 秒的行为里,0.66 秒进入进食状态。
视觉逃逸:flygym 复眼画面 → flyvis(连接组约束的视觉网络)→ T4/T5 运动检测神经元映射到 FlyWire → LIF 脑。一个黑球从前左方加速逼近:
| 时间 | 事件 |
|---|---|
| 1.635 s | 左侧 LPLC2(逼近检测)首次放电,峰值 6.2 Hz |
| 1.830 s | 右侧 LPLC2 放电,峰值 7.8 Hz |
| 1.890 s | 右侧巨纤维 DNp01 放电,峰值 66.7 Hz → 后退逃逸 |

LPLC2 → 巨纤维 → 逃逸这条通路完全是从连接组里「长」出来的,我只写了最后一步「巨纤维放电 = 后退」。另一类逼近检测神经元 LC4 和左侧巨纤维全程为 0,我目前的接口只看「有没有放电」,不区分左右。
二、自发行为 v1:加噪声,再加适应
原模型没有输入就静默。我在膜电位方程上加两项:
- 膜噪声 σ:真实神经元有离子通道噪声,会自发放电;
- 放电适应 a:每放一次电,一个慢电流 +b,按 τa 衰减。这让活动「自限」,形成一阵活动、一阵安静。
扫了 σ × b 九组(每组脑时间 1 秒),挑几组有代表性的:
| σ | b | 全脑均率 | 活跃比例 | 群体率 CV | 转向 DN 左/右 |
|---|---|---|---|---|---|
| 2 mV | 0 | 4.03 Hz | 17.9% | 0.04 | 34 / 7 Hz |
| 2 mV | 3 mV | 0.33 Hz | 13.8% | 1.03 | 2 / 4 Hz |
| 3 mV | 0 | 6.12 Hz | 72.2% | 0.04 | 39 / 7 Hz |
| 3 mV | 3 mV | 1.29 Hz | 74.4% | 0.25 | 7 / 9 Hz |
| 4 mV | 0 | 9.46 Hz | 88.9% | 0.04 | 46 / 4 Hz |
这张表里最有意思的是 b=0 那几行:只加噪声、不加适应,群体活动平得像一条直线(CV 0.04),转向 DN 却固定左偏 5 倍以上——果蝇会一直原地左转。原因是噪声被连接组结构放大后,找到了一个稳定吸引子;适应电流把它打破了,左右偏差消失,出现阵发。
选定 σ=2.5 mV、b=3 mV、τa=400 ms(全脑均率约 0.75 Hz,不饱和),接上身体空跑 12 秒:

30 个片段,时间占比:静止 58%、前进 11%、梳理 15%、后退 8%、惊跳 9%;前进速度 12.4 mm/s,总路径 27.5 mm。没有任何感觉输入,它确实自己在走、在停、在梳理。
但把脑时间拉到 60 秒做统计,问题就暴露了:
- 全脑阵发间隔中位 1.67 秒,变异系数只有 0.05——几乎是个节拍器;
- 前进片段 82%、梳理片段 84% 恰好等于解码器设的最短时长 0.3 秒,最长的前进片段只有 0.42 秒;
- 惊跳每分钟 7.1 次,多得离谱。
这说明 v1 的「行为」大部分是我加的东西决定的:节律来自单一的 τa,片段时长来自解码器参数,惊跳来自巨纤维单个神经元被噪声随机触发。
三、自发行为 v2:让连接组多说话,让我的参数少说话
针对这三点各改一处:
- 适应时间常数异质化:每个神经元的 τa 从对数正态分布里抽(中位 400 ms,5–95% 约 0.1~1.5 s),再加一个缓慢起伏的全脑「唤醒度」(OU 过程,τ=4 s)。
- 读 DN 的突触输入而不是脉冲:每组 DN 只有 1~24 个神经元,脉冲基本是点过程噪声;但每个 DN 的突触输入 g 汇总了上千个上游神经元,信号平滑得多。动作选择改用互相抑制 + 自兴奋的竞争单元,片段时长由动力学自然产生。
- 惊跳要求巨纤维 20 ms 内 ≥4 个脉冲(强同步驱动),单个噪声脉冲不算;巨纤维自身噪声调小到 0.5 mV(它是很粗的神经元,否则空闲时约 5 Hz 乱放电)。
同样 60 秒脑时间,两个随机种子:
| 指标 | v1 | v2 种子 0 | v2 种子 1 |
|---|---|---|---|
| 阵发间隔中位 / CV | 1.67 s / 0.05 | 2.25 s / 1.19 | 1.16 s / 1.16 |
| 群体率自相关峰 | 0.61 | 0.40 | 0.47 |
| 惊跳 / 分钟 | 7.1 | 2.0 | 1.0 |
| 前进片段 中位 / 最长 | 0.30 / 0.42 s | 0.86 / 4.63 s | 0.60 / 3.48 s |
| 前进片段贴 0.3 s 下限 | 82% | 0% | 12% |
| 前进片段 >1 s | 0% | 44% | 29% |
| 梳理片段 中位 / 最长 | 0.30 / 0.39 s | 0.72 / 1.87 s | 0.65 / 2.23 s |
| 占比 静止/前进/后退/梳理/惊跳 | 57/12/14/13/5% | 27/35/9/28/1% | 33/29/13/24/1% |

节律从「节拍器」变成了不规则(CV 0.05 → 1.2),前进可以连续走 3~4 秒,梳理可以持续 2 秒,惊跳降到每分钟 1~2 次。两个种子的占比有差异但量级一致,说明不是某次运气。
最后把 v2 接上身体空跑 30 秒(种子 0):46 个片段,静止 34%、前进 21%、后退 14%、梳理 30%,这 30 秒里没有惊跳;总路径 135 mm,而 v1 的 12 秒只走了 27.5 mm。

不过 v2 没有完全解决问题。看图最下面一栏:唤醒度(橙线)偏低的后半段,全脑阵发(灰线)又变回了比较整齐的节奏。异质适应让节律变松了,但单靠它还压不住;真实果蝇的不规则性可能还需要别的来源,比如神经调质或身体反馈。
实践效果:哪些是连接组的,哪些是我的
把这一轮实测收成一张归属表,这是我认为读这类「数字果蝇」演示时最该问的问题:
| 现象 | 来自连接组 | 来自我加的东西 |
|---|---|---|
| 糖 → 转向进食 | 味觉到 DN 的左右分离通路 | 糖浓度 → 放电率编码;DN 差 → CPG 步幅 |
| 黑球 → 后退 | LPLC2 → 巨纤维通路与时序 | 巨纤维放电 = 后退 |
| 无输入时有活动 | —(原模型静默) | 膜噪声 |
| 活动一阵一阵 | 阵发在全脑的传播模式 | 适应电流及其时间常数分布 |
| 只加噪声时一直左转 | DN 群体固有左偏 | — |
| 动作之间切换 | 各组 DN 输入的涨落 | DN 分组、竞争单元参数 |
| 走路、梳理的具体动作 | — | CPG 步态、手写梳理/惊跳轨迹 |
所以准确的说法是:连接组 + 两项生理常识(噪声、适应),足以自发产生可解码、有持续时间结构的动作序列。 它还不能说明这只虚拟果蝇的行为统计和真实果蝇一致——我没有拿真实果蝇的自发行为数据做对照,这是下一步。
算力上,60 秒脑时间在 M4 上要跑 18~25 分钟(约 20 倍慢于实时),这决定了网页上只能放离线生成的视频,不能实时跑脑。
踩坑
- 只加噪声不加适应,会得到一只永远左转的果蝇。 转向 DN 左 34 Hz、右 7 Hz,而且噪声越大越偏。不看左右分列的读数,只看「全脑有没有活动」就会错过。
- 小群体的脉冲不能直接当信号。 DN 组只有 1~24 个神经元,100 ms 分箱里基本是点过程噪声。v1 用「相对均值」做阈值时,24 个神经元的梳理组几乎永远赢不了,只好改成组内 z 分数——而 z 分数又让各动作占比被人为拉平。v2 改读突触输入后信号平滑多了,但仍沿用组内 z 分数,占比被拉平这一点没有解决。
- 启动瞬态会被误判成逃逸。 加噪声的网络在开头 0.5 秒有一次全局齐放,巨纤维跟着放电。必须预跑 0.5 秒丢掉。
- 视觉网络要用真实画面预热。 flyvis 先看灰屏再切到场景,会产生一次强瞬态,足以让巨纤维放电触发假逃逸。
- 嗅觉不是「最自然的输入」就能用。 直觉上果蝇靠闻找食物,但在这个模型里嗅觉输入让 8000 个神经元饱和、左右无差。先扫通道再选输入,别凭直觉。
- 片段时长要看分布,不要只看视频。 v1 的 12 秒视频看起来挺像回事;拉到 60 秒一统计,八成片段卡在解码下限上。单段演示视频证明不了行为的真实性。