一个没有边界的 3D 大自然:起伏的地形、石头、蘑菇、花、浆果丛、水洼、昼夜、风和雨,有重力——浆果熟了会掉下来、弹几下、顺坡滚,停稳后发酵;石头挡路;上坡走得慢;太阳把石板晒烫;风把气味吹向下风;甲虫会爬过来。 果蝇只通过真实的感受器知道这个世界;往哪转、飞不飞、吃不吃,由这颗 15,055 个神经元的连接组决定——没有一条"看见甲虫就跑""闻到果子就过去"的规则。
朝果蝇扔球、喂糖粒苦粒、往触角上撒灰。决定往哪转、何时起飞、吃不吃、要不要梳理的,是在你浏览器里实时运行的 4,599 个 FlyWire 连接组神经元;起飞后是真实果蝇的逃逸飞行轨迹。
世界自己运转:昼夜、烂果子和水洼、气味、嗡嗡声、热源、风、灰尘、偶尔飞来的球。每一样都只作为物理量送到真实的感受器上(视、听、嗅、味、触,外加温度和湿度); 往哪转、飞不飞、吃不吃,全由这颗 个神经元的连接组决定——没有一条"闻到就过去""听到就跑"的规则。 它有蘑菇体:气味和多巴胺同时出现,对应的突触就被压低——它会记住。身体由六条腿推着走,可以截腿,也可以按住按钮直接遥控某几个神经元。
和果蝇下五子棋(含黑方禁手)。每条线值多少,是同一颗果蝇脑(一个突触都不训练)加一层训练出来的线性读出给的;「推理步数」选的是用哪一个读出层——每个都是拿「向前看 N 步的结果」当标签训练出来的,下棋时不做搜索。
七个手术任务,玩家只准动开关(断突触 / 敲真实神经元 / 打乱接线 / 光遗传脉冲),不准改规则。
问题是:怎么知道一关设计得对不对?我们的做法是把每关都用 node dodge/mission_test.js
跑两遍——不干预和参考解,各 3 个种子 × 60 秒,然后照着实测分布定通过线。
一关如果不干预也能过,它就没在考任何东西;参考解如果过不了,那这关就是不可能完成的。下表是全部实测值。
两处按数据改过设计,值得写出来:①「一个不够,两个才行」原本把线画在 8 s, 结果单敲也能过;实测逐次是——不干预 6.1/8.6/11.2 s、单敲 Clavicle 5.4/8.1/5.1、单敲 G2N-1 4.8/6.8/7.9、 两个一起 2.8/2.9/2.0,于是线改到 4 s,落在「单敲最低的 4.8」与「双敲最高的 2.9」之间。 ②「证明连接组真的在起作用」原本判闪避率,但打乱之后逐次是 0.07/0.79/0.43——波动大到有一次跟没打乱差不多, 所以判据换成起跳次数(打乱后 18/5/0 次 vs 不干预 33/32/33,完全不重叠),这也更贴近机制: 打乱先打断的是 looming → 巨纤维那一路。
有人看到果蝇走到场边会一直顶着围栏往前走。查下来不是它认不出墙:场地边界只是每一步末尾的 一次几何钳位,而手写的视觉前端只读球的世界坐标算张角增长率,场景里别的东西一概不看。 实测(无球、120 秒 × 3 种子):92.8% 的时间贴着墙,最长一次连续 111 秒。
那为什么不用摄像机?——那条路本来就有:eyecam 用一台立方体贴图摄像机从果蝇头部
渲染整个 3D 场景,墙、看台、别的果蝇都在里面,以后往场里加任何物体也都会自动在里面。
左上角复眼视窗里那条新的横条就是它的原始投影(采样成 721 个小眼之前的样子)。
实测它确实看得见墙(静止时读数恒为 0,一走就有),但「朝墙走」(LPLC2 中位 34.2 Hz)
和「背墙走」(23.5 Hz)几乎分不开——读到的主要是走路本身造成的全场光流。
真果蝇有平衡棒与传出拷贝来抵消自体运动,这个模型里没有。
那就只给它物理量,让它自己决定。这是最干净的做法,我们试了:触角碰到围栏 → 驱动机械感觉神经元。 结果是果蝇完全没反应(贴墙率一个百分点都没动)。原因在连接组里:游戏用的子回路里唯一的 机械感觉是触角 JO,而 JO 只通到梳理指令 aDN1,根本不通到转向或逃逸。
于是去全脑里查出路(dodge/sensor_reach.py,只走 ≥3 突触的边):
脑连接组里有 305 个头部刚毛(真正的触觉感受器)、29 个温度、74 个湿度,
而且它们到 DNa01/DNa02/巨纤维/MDN 只要 2–3 跳。通路是有的,只是我们裁子回路时没把它们当输入。
重裁一版 v3(5,563 个神经元)把这三路加进去之后:
这就是那条原则的完整答案:只给物理量、不写判断逻辑是可行的—— 触感送到头部刚毛之后,果蝇走过的格子从 9 涨到 31.5、路程从 156 mm 涨到 603 mm, 中间没有任何我们写的判断。但前提是子回路里真的有那条通路; 送错地方(JO)就等于没给。游戏页现在仍然跑 v2 + 那个手写的「看得见围栏」, 是因为切到 v3 会让已经发布的任务关卡、突变体、敲除比值全部需要重测——这是工程取舍,不是科学结论。
加了新的感觉通道,就得先量它有没有落点。把 v3 的每一路输入单独驱动 1 秒, 看六个运动读出组里有没有哪个被推起来(判据:任一读出 > 5 Hz):
几点必须说明:苦味「没有落点」是对的——它是抑制性的,单独给它本来就不该驱动任何东西, 它的作用是把糖驱动的 MN9 压下去(论文 Fig 3B–C,我们复现过:最强糖下苦压掉 82.4%)。 湿度是真的没有落点,给多强都推不动运动输出。温度要 400 Hz 以上才有, 在合理的刺激强度下等于没有。触感在 100–200 Hz 就有,这也是为什么它能让果蝇绕开围栏。 还有一条有意思的:触感双侧同时给反而更弱(200 Hz 时六个读出全是 0)—— 左右对称的驱动把转向所需的左右差抵消了,这不是 bug,是回路本来的样子。
还有一处是我们自己的指标错误,值得写出来:第一版用「贴墙时间占比」当指标, 而它分不出「钉死在一点」和「贴着墙滑行」——两种情况这个标志都为真。 按那个指标,触感「没用」(85.6% → 85.6%);换成走过的格子数才看出来果蝇其实一直在沿墙移动。 指标选错,阴性结果就是假的。
顶部第三个标签。这是单独的一只果蝇,用的是新裁的子回路 v4(),比球场那只多了听觉、两个嗅小球(醋 / 土臭素)和 4 个内感受神经元。 世界自己运转——昼夜、烂果子、水洼、气味、嗡嗡声、热源、风、灰尘、来球——每一样都只作为物理量送到对应的感受器上;没有一条"闻到就过去""听到就跑""卡住就掉头"的规则。 所以下面这张表不是功能清单,而是实测:每一路感觉,在这个模型里到底通不通到行为。
哪些是手写的:世界的节奏与强度;声强随距离的衰减与左右耳差;气味羽流的形状;身体状态(能量、水分)的消耗速率,以及它对味觉感受器灵敏度的调制幅度(方向有文献依据); 「MN9 超过 10 Hz 算开吃」这条读出阈值;「它在想什么」的句子模板与阈值(每个词的触发量是实测的频率)。它不是语言模型,也不是果蝇会说话。
第一个标签默认进的就是它(「世界」开关可以切回篮球场)。地形、石头、蘑菇、花、草、浆果丛、水洼都按种子确定性生成,游戏核心和 3D 渲染用的是同一个世界对象——画出来的石头就是它撞上的石头。 物理用真实单位:重力 9,810 mm/s²,浆果从枝头掉下来、按地形法线反弹、顺坡滚、停稳后发酵;石头不可穿透;上坡走得慢;太阳把深色石板晒烫;洼地下雨蓄水、晴天蒸发;风把气味吹向下风。 果蝇只通过真实的感受器知道这一切(坡度 → 步速,实心物件 → 挡路 + 触角被压,石板 → 温度感受器,雨 → 湿度感受器和听觉神经元,发酵的浆果 → 嗅觉感受神经元,甲虫和下落的浆果 → 逼近视觉),没有新增任何一条行为规则。 威胁从「飞来的球」换成了「爬过来的甲虫」:毫米尺度上,60 mm/s 的抛体 0.1 秒就落地、滚几毫米就停,慢慢滚过来的球不物理。
玩法像「我的世界」:场景底部的工具栏(数字键选、点地面用)可以放果子、挖水洼、放石头、种蘑菇 / 浆果丛 / 花、放甲虫、抬高 / 挖低地形、拆除——改的是同一个世界对象,放下的石头真的挡路,改动按种子存在本机。世界分草甸 / 林下 / 砾石滩 / 湿地四种生物群系。 C 切视角(轨道 / 跟在身后 / 第一人称);按住 A D S 空格 = 直接驱动真实的转向 / 后退 / 巨纤维神经元(遥控的是神经元,不是身体);F 把场景铺满窗口,大脑和实时 spike 改在同一个 3D 画面里渲染。
如实说明:默认的视觉前端仍是手写的逼近检测,石头、草、地形不在它的视觉里——它绕开石头靠的是被挡住之后贴着滑过去加上触角触碰。复眼视窗里「只靠眼睛躲」那条真实像素通路现在也接到了这一只上,但它一走动满地的草就成了假逼近,所以默认关。 果蝇自己的身体是运动学,不会摔倒;受重力支配的是浆果。水洼是水位,不是流体。地形的幅度、物件的密度、恢复系数 0.35、滚动阻力 0.15、坡度系数、天气节奏全部手选。
「生活」模式的果蝇现在用子回路 v5()= v4 + 连接组里的蘑菇体学习回路:嗅觉感受神经元 → 投射神经元 → 凯尼恩细胞 → 蘑菇体输出神经元(MBON), 外加 多巴胺神经元(PAM、PPL1)和全局抑制 APL。接线全部来自 FlyWire;唯一会变的是凯尼恩细胞 → MBON 这一层突触: 凯尼恩细胞刚放过电、同一隔室里又有多巴胺,这条突触就被压低(文献里果蝇嗅觉记忆的位置与规则:Hige 2015;Cohn 2015;Handler 2019)。 哪个多巴胺神经元管哪个隔室,由连接组里 DAN → MBON 的突触数决定,我们没有写对照表。下面每一张表都是实测,判据都写在跑之前。 想在 3D 篮球场里看这一只:「篮球场」控件最上面的「大脑」开关切到「五感全开」——同一只、同一份记忆,气味、热源、声音都画在球场里,六条腿各按自己的相位走;任务、突变体、光遗传仍属于 5,563 个神经元的球场版(那些已发布的数字是在它上面量的)。
哪些是手写的:① 触角叶局部神经元一律取抑制性(递质符号的纠正,有文献依据);② 碰到蘑菇体的边只留 ≥2 个突触的(页面装不下全部 129 万条); ③ 糖 / 烫 → 多巴胺这根线(连接组自己叫不起多巴胺神经元,见 ②);④ 可塑性规则的三个数(学习率、资格迹 200 ms、不遗忘);⑤ 气味 A / B 是合成的(各 20 个嗅小球),不是真实气味的受体谱; ⑥ 下行神经元 → 「步频 + 左右步幅」的映射,以及六条腿之间的相位耦合——腹神经索连接组给不出三足步态(上面「腹神经索」一节);⑦ 「记忆 → 转向的桥」整个是手写的,所以默认关。
把「主画面」切到「五子棋」就能和它下(含黑方禁手:长连、四四、三三)。先说清楚分工:
① 每条线值多少——果蝇脑 + 一层线性读出。对一个候选点,沿 4 个方向各取一条线(两侧各 4 格, 每格 = 空 / 我方 / 对方 / 边界)。这样的线型一共只有 14,641 种,少到可以让这颗 5,563 个神经元的果蝇脑把每一种都跑一遍:8 个格子 × 3 种状态 = 24 个通道,各接约 61 个真实感觉神经元, 泊松驱动 300 ms,数下游神经元的脉冲,对 3 个随机种子取平均。脑子里一个突触都不训练, 训练的只有一层线性读出,它把放电换成这条线的对数价值。落子分 = 4 条进攻线 + λ × 4 条防守线的价值之和。
② 「推理 N 步」也是训练出来的,不是下棋时现算的。数据集里每个局面都标了「向前搜 1–6 步、8 步」各自的最佳着; 对每个 N,用它当标签各训练一个读出层。棋盘上方的推理步数选的就是用哪一个——下棋时只有模型推理:线型 → 果蝇脑的放电 → 这个读出层 → 落子分最大的点,没有任何搜索。 随时可切,正在想的那一步会立刻按新的重想。棋盘上画的就是它的想法:橙色圆圈 = 模型给各候选点的概率,编号的虚子 = 它预想的后续(同一个模型替双方轮流走 N 步)。 ③ 禁手与胜负由规则引擎判。每走一步,页面会把这一步的线型放进真脑里现场重跑,与价值表比对—— 这时左边的 spike 图、下行神经元、感觉输入显示的就是下棋这只果蝇的放电。 ④ 外挂搜索(默认关)才是 alpha-beta 算法:把同一张价值表交给它往前搜,只懂规则、不含棋形分值。它强得多,但那是算法的功劳,页面上单独标着。
两个阶段。阶段一:把「落子评分算法」当训练数据——让读出层去拟合老师对全部线型的对数分值 (训练局面里只出现过其中 6,266 种,只靠着法标签的话,其余的价值会随意漂移,搜索一走进没见过的局面就崩)。 阶段二:用深度 8 搜索的最佳着做微调()。 下图左:果蝇从没见过「活三」「冲四」这些类别,类别只用来给学到的价值分组——看它自己排出的顺序对不对。 右:阶段二的验证集一致率随训练步数的变化,起点就是阶段一的成绩。
三因子规则,只改读出层:Δw = 学习率 × δ × 资格迹,δ = 这一局的结果 − 近期平均 = 奖励预测误差, 在强化学习里对应多巴胺信号。必须说清楚:这个模型自己的多巴胺神经元在非失控条件下根本不放电(PAM 0/307), 所以 δ 是在连接组外面算的。下图:每轮训练对局的胜率,和每一局的 δ。
v1 的全部数字作废。这张卡片以前写的是「果蝇脑的一致率 0.025、只比随机高一点;连接组对下棋没有贡献」。 那一版有一个 bug:给大脑换输入时上一批刺激没清掉,输入一个接一个地叠上去。 于是所有棋盘喂进去几乎是同一个输入,读出层学到的是噪声。 另一半原因是架构:v1 把整盘棋随机铺到感觉神经元上,丢掉了五子棋最要紧的局部性与平移不变性。 游戏本身不受这个 bug 影响(每一步都整组重写输入;修复前后 7 个任务、10 个突变体的结果逐项相同)。
「抽一只突变体」会随机敲掉十个真实神经元中的一个而不告诉你是哪个,你靠喂糖、喂水、落灰、扔球把它诊断出来。
但在把这个玩法做出来之前得先回答一个问题:这些敲除,行为上真的看得见吗?
node dodge/mutant_test.js 把每一个都跑了 60 秒 × 3 个种子,六个指标全测,
判「可诊断」用的是最保守的口径——该指标三次的区间与不敲的三次完全不重叠。
看不出来的那三个才是重点:G2N-1、Zorro、CB0883 敲掉之后,六个行为指标全都落在正常范围里。 这不是游戏做得不好——这正是全脑筛选反复得到的那条结论的行为版:单敲除常常什么也看不见。 CB0883 在全脑里是枢纽(单独敲 0.76,配上 Clavicle/G2N-1 塌到 0.24),Zorro 是真实实验证实「伸喙必需」的类型, 可它们单独被敲掉时,果蝇看起来完全正常。要发现它们,必须成对地敲——那就是「一个不够,两个才行」那一关。
2026 年 9 月 3 日 Janelia 与 Google 公开雄性果蝇全中枢神经系统连接组 MaleCNS v1.0(约 16.6 万神经元)之后的两周里, 网上冒出一批"把果蝇脑接进游戏"的项目:接《Minecraft》、接《DOOM》、接《Beat Saber》、当桌面宠物。 下面这几个是我自己打开仓库页核实过的(星数与说明取自各自 README,2026-09-19):
它们的共同套路:①把像素亮度灌进 R1–R6 光感受器;②手选几个下行神经元当按键(左右差转向、某个 DN 前进、DNp01 触发跳); ③在读出端加一层训练出来的网络(CEM / 岭回归 / 反向传播)。第三条是关键——一旦读出层足够灵活, 连接组是不是真的在起作用就说不清了。
一篇值得读的批评:Patrick Mineault,Deconstructing viral fly sims(2026-09-14,neuroai.science)。 他的核心质疑是:多数演示并没有建立从感觉到运动的完整闭环,视觉输入的作用近似于随机种子; 而"足够大的网络本来就能学会很多事,是不是果蝇都一样"。他提出的严格标准是: 行为要由感觉真正驱动、活动要在电生理上说得过去、并且能表现出个体性。
我们和它们的差别,只有一条,但是这条最贵:这个页面里没有任何训练出来的读出层。 从 LC4/LPLC2 到巨纤维、从糖味受体到 MN9、从 JO 到 aDN1,中间全是连接组里的真实突触, 参数就是论文那一套 LIF。代价是它笨——躲球靠一个手写的 dθ/dt 前端,真让它只用像素看,45 mm 外就完全是瞎的(我们把这个负结果也做成了开关)。 换来的是可以做对照:任务模式里的「证明连接组真的在起作用」那一关,就是打乱接线之后看它垮掉。 论文补充表 1D 做的是同一件事,我们实测糖 → MN9 从 82.9 Hz 掉到 3.5 Hz。
这个项目不是"跑通了一个模型",是拿已发表的论文逐条对答案。下表是全部结果,
包括做不到的和结果是阴性的——那两类同样是结果。
每条都能追到产出它的脚本与结果文件;完整过程日志在仓库的 docs/log/report.md。
本项目自己的结果——不是对某篇论文的复现,是这个项目自己做出来的结论,阴性的也在里面。
上传一张图,果蝇用它真实的视觉系统看一遍,再画出来。链路上没有示意图:你的图先变成 450×512 的相机画面,按 FlyGym 的真实复眼采样表(含鱼眼畸变)归并到 721 个小眼,再喂进 flyvis 的预训练连接组视觉网络(65 类细胞 × 721 柱,2355 个卷积抽头),最后由六边形卷积解码器还原。
一个必须说明的限制:flyvis 自带 50 个预训练集成成员——连接组约束下解不唯一,作者本人强调成员间差异。我们用的是其中一个(flow/0000/000)。把 50 个全跑一遍之后:「整个网络是六边形卷积」这条 50/50 成立;但 T4/T5 各亚型的偏好方向在成员之间离散极大,我们基于成员 000 得到的若干视觉结论不能当作 flyvis 的普遍性质(详见报告 §33)。
果蝇会扫视,原因有两个,都不是为了好看。其一,T4/T5 是运动检测器,盯着不动的图几乎不响应。其二:相邻小眼间距 16.4 像素,不动的话永远只采到同一批点,就是 721 块马赛克;一动起来,同一个小眼会落到世界的不同位置(真果蝇也这么干,叫微扫视超敏锐度)。
但光把多帧平均起来没有用——实测提升 −0.2%,因为每次测量本身就是小眼的模糊平均,再叠加只是把更多张模糊图平均。真正管用的是反投影:解一个反问题,找出一张高分辨图,使得"用小眼去测它"能复现全部 69,216 次测量。实测与原图的相关:单帧 0.851 → 朴素累积 0.849 → 反投影 0.920,超过了单次接受角决定的天花板 0.883 —— 说明它真的从多帧里解出了单帧不含的信息。用的全是实测值和已知几何,不引入训练数据的统计,和右边四栏的解码器不同。
但别指望变成照片:果蝇单眼 721 个小眼、角分辨率约 4.5°;人眼约 1.2 亿感光细胞、中央凹 0.008°。差五个数量级,低分辨不是模拟得糙,就是果蝇眼里的世界。
关于颜色:果蝇有色觉,而且比人多一维——R7 是紫外(pale 型 Rh3 ~345 nm / yellow 型 Rh4 ~375 nm),R8 是蓝(Rh5 ~437 nm)或绿(Rh6 ~508 nm),但几乎看不见红。小眼分 pale / yellow 两型,实测比例 216 : 505 ≈ 30% : 70%,和真果蝇一致。第二栏按这个分型上色:蓝格子是 pale(取蓝通道),绿格子是 yellow(取绿通道),像拜耳滤镜的彩色马赛克。试试「色块」预设——红色那块在果蝇眼里几乎是黑的。
两个诚实的限制:紫外无法从照片恢复(sRGB 里根本没有紫外,而那是果蝇最重要的通道之一);flyvis 每个小眼只吃一个标量(它是用光流任务训练的,没有颜色通道),所以颜色在进入神经网络那一步必然被压成一维——后面几栏是黑白,是模型的限制,不是果蝇的。
后四栏是解码重建,会「补」出神经活动里其实没有的细节——解码器学的是训练图像的统计规律。第三栏是未经解码的原始活动,两者的差距本身就是要看的东西(同 §15 解码器压力测试)。越往右的层,画面丢得越多:到 T4/T5 只剩大形状,细条纹没了。
舞台右上角那个小窗是同一套东西的实时版:把 3D 世界从果蝇眼睛的位置再渲一遍(左右眼各偏前方 50°),走同一张采样表成像。为了实时跳过了 flyvis 和解码器,只到「视网膜接收到什么」为止。原来每秒只刷 3 次,因为同步读回 GPU 像素要等一次约 4–5 ms 的往返;实测那次停顿与读几个面、读多少像素、贴图多大全都无关(读 1 面 6.3 ms ≈ 读 6 面 6.8 ms;边长 192→48 不降),所以降分辨率没用。改成 WebGL2 的 PBO 异步读回(下一帧取数,已验证与同步逐字节相同)后,单次从 10.6 ms 降到 2.9 ms,现在实测 23 fps,主画面仍 60 fps。
「只靠眼睛躲」这个开关把游戏的视觉前端换掉:平时果蝇是直接读球的世界坐标算张角增长率(手写规则,而且只看球、无视场景里其它一切);打开之后它只能靠这 721 个小眼的读数——真实像素 → Rh1(R1–R6)→ flyvis → 按 Klapoetke et al. 2017 的形式汇集背离中心的 T4/T5 → LC4/LPLC2。结果是负的,而且可以量:球半径 2.5 mm、小眼间角 5.7°,所以球在 60 mm 外还不到一个小眼;实测这套真实视觉在 45 mm 外完全瞎(读数恒为 0),要到 25 mm 以内才把球从噪声里分出来,而手写前端从 110 mm 就在跟。更要命的是果蝇光是走路,自体运动的全场光流就能给自己造出峰值 126 Hz 的假「逼近」——所以开着它果蝇会一边走一边被自己吓得起飞。这不是模型坏了,是真果蝇要靠触角和平衡棒去抵消的那个问题,而这个模型两样都没有。开关默认关,打开会掉帧(每次约 12 ms)。渲染时会把果蝇自己的网格藏起来——摄像机在身体前方,不藏的话它自己的头会糊满半个视野。
每一类感光细胞按自己的光谱积分:以前是「pale 小眼取蓝通道、yellow 取绿通道」的类拜耳近似,现在用 Govardovskii et al. 2000 的视色素模板,按各自 λmax 逐 1 nm 积分出对三个渲染波段(紫外 300–400、蓝 400–500、绿 500–700)的权重:Rh3 (R7p, 345nm) 99.8% 来自紫外、Rh4 (R7y, 375nm) 88.3%、Rh5 (R8p, 437nm) 蓝占 66.4%、Rh6 (R8y, 508nm) 绿占 43.4%。更要紧的是补上了 Rh1 (R1–R6)——八个感光细胞里的六个、运动与亮度的主通道,此前完全没有建模,而它正是 flyvis 的主输入。两个假设要说明:场景是 sRGB 著作的、没有真实光谱,三个通道被当作各波段的平均反射率;果蝇 R1–6 还有一个 3-羟基视黄醇增感色素在 ~350nm 给第二个峰,不在该模板里,这里按手选幅度补上(两套权重都写在 results/vision/opsin_weights.json 里,可以对比)。
视野边缘不再是近似:一开始用透视投影 + FlyGym 的鱼眼校正,但透视投影天生到不了 180°,157° 时边缘已经被拉得极扁,鱼眼公式还有个奇点。现在改成立方体贴图——六个 90° 面覆盖整个球面,每个小眼按自己的视线方向精确采样,不需要任何畸变补偿。小眼方向按六边形点阵定义:半径 15、小眼间角 5.7°,单眼视野 171°,接近真果蝇(文献 ~160–180°)。几何自检实测:9 个已知方位平均误差 0.7°。
紫外(R7)那一路是手写设定:sRGB 渲染里没有紫外,所以给场景里每类物体指定了一个紫外反射率。依据是真实的紫外世界结构——自然界里天空是压倒性的紫外源、绝大多数表面紫外很暗,果蝇正是靠这个对比找开阔空间、判断上下。但数值没有任何测量依据,只是结构上说得通;上传的照片里更是完全没有紫外信息。
为什么看着一片绿:一开始整场都用页面主题色(地板 #1a211e),绿蓝两通道只差 11%,果蝇眼里等于没有颜色;加上每个小眼只测一路,画面按感受器类型上色,70% 是 yellow 型 → 天生 70% 的格子是绿的。现在球场改成真实配色(木地板、白线、蓝红座椅),并做去马赛克——把每个小眼缺的那一路用邻居里另一型的平均补上,和拜耳滤镜同一回事(果蝇本身并没有在每个小眼上同时拿到两路,这是显示用的插值,可以关掉看原始马赛克)。
改完实测:场景 G/B 从 36.6/32.5 变成 76/49,眼内 G/B = 0.310/0.205,41 个小眼偏蓝(蓝座椅)。但整体仍然偏绿,而这是对的——木地板是橙色,果蝇没有红色感受器,橙色只刺激它的绿通道、几乎不刺激蓝通道。篮球场在果蝇眼里就是一片暗绿。
仍然是近似的:小眼间角取了常数 5.7°(真果蝇从正前 ~4.5° 到侧面 ~8° 渐变);天空是游戏的 CSS 渐变、不在 3D 场景里,离屏渲染时用舞台天空色补了个底。
每 4 s 在果蝇前方放一颗:糖 → 苦 → 混合 轮换(3 局 × 120 s,无球)。头部碰到颗粒 → 两侧糖/苦味觉神经元 200 Hz;MN9 平均 > 30 Hz 就停下伸喙吃。
每 5 s 随机一侧触角落灰 → 该侧 JO 神经元 200 Hz,直到梳理完。aDN1 > 20 Hz 就停下梳理 0.6 s。
真实果蝇里,糖的奖赏信号由 PAM 簇多巴胺神经元送到蘑菇体(Liu et al. 2012;Burke et al. 2012),惩罚信号主要走 PPL1 簇。这里直接在全脑模型(138,639 个神经元,0.5 s × 4 次独立试次)里数这两群神经元有没有放电。事先定的判据:只有不失控(活跃神经元 < 2,000)时 PAM 放电才算数。
依据 Sen et al. 2017:LC16 对 looming 反应,经 MDN 引发后退。先在全脑模型里测 LC16 刺激强度与 MDN 的关系,再按事先写定的设计重做对局:正前方 ±10° 一次来一个威胁,3 档速度 × 2 种威胁 × 5 种切除 × 3 档 LC16 编码强度,每格 24 个威胁,原地起跳模式。
全脑:LC16 刺激 → 下行神经元(Hz)
对局:正面威胁的击中率(括号内 = 出现后退的威胁比例)
全脑模型刺激一侧嗅觉受体神经元。降低刺激强度、只看全脑失控之前的窗口、找出失控的“点火”神经元并沉默它们,判据都事先写定:左右刺激的下行神经元侧化 LI =(左 − 右)/(左 + 右)符号相反且 |LI| ≥ 0.2。
降低强度:失控起点与失控前的脉冲(每次试验的脉冲数,左/右)
沉默点火神经元(探索性)
游戏里的“闻着找”(手写:高斯气味场 σ = 15 mm,左右触角比浓度,以 120°/s 转向;吃不吃仍由连接组的糖/苦 → MN9 决定)
换成 Pugliese et al. 2025 的腹神经索发放率模型(作者代码本机调用,仓库无许可证,未复制代码):MANC 前足神经节 4,604 个神经元,含左右前腿运动神经元各 72 个;每个条件 16 组随机神经元参数,2 s。节律判据用作者的振荡得分(≥ 0.5)。再把运动模块的摆动/支撑发放率换成 NeuroMechFly 前腿关节角(方向取自 FlyGym 真实步态数据),和 CPG 步态对照。
舞台下方「动脑子」那一排,对应论文(Shiu et al. 2024)自己做过的对照。 打乱接线是补充表 1D:保持每个神经元的出度和它那串权重,只把突触后靶点随机重排—— 接线一乱,糖味觉到伸喙运动神经元 MN9 的信号几乎全没了。 权重 / 抑制滑块是补充表 11B–E(论文取 ±30%),谷氨酸改兴奋性是表 11F (默认模型把谷氨酸当抑制性;子回路里有 635 个谷氨酸能神经元,占 13.8%)。
打乱接线:正确接线 vs 随机重排
参数扰动下,10 个神经元的敲除判定还一样吗
同一个发放率模型换成完整的 MANC 腹神经索(23,532 个神经元,6 条腿)。作者代码是稠密矩阵、要约 9 GB,这里改成稀疏存储,方程、参数抽样和求解器不变;先在前足网络上和作者稠密版逐点对齐。刺激强度按“DNg100 发放率与前足网络默认实验相同”标定。
全腹神经索:刺激条件
主条件(DNg100 双侧 I = 350)每条腿
驱动 NeuroMechFly 6 条腿(腿尖前后摆幅 · 4–30 Hz 节律占比;< 0.1 mm 记为基本不动)
巨纤维 → TTMn(跳跃肌运动神经元)电突触(4 个副本的中位数)
本体感觉闭环(手写感受器编码;DNg100 双侧 I = 350)
用作者的全突触数据重跑(W_20260522_allSynapses:多 393 万条弱连接,中位 1 个突触;原有连接权重完全相同)
和真实实验对照:10 个做过光遗传沉默的细胞类型(比值 = 沉默后 MN9 放电 ÷ 不沉默;≤ 0.8 判为“伸喙必需”)
穷举 50 Hz 下全部活跃神经元:效应最强的 12 个
沉默后 MN9 反而升高最多的 5 个
双敲除:有没有“单独敲都没事、一起敲就断”的备份通路
换一条通路:水味觉(论文表 5 的实验全是这篇论文自己做的)
糖和水用的是同一批神经元吗(同一个 MN9 读出,两条输入通路)
三条通路:味觉糖、味觉水、触角机械感觉(同一套方法,不同输入与读出)
什么时候可以不跑模拟:只看连线能预测到什么程度
解码器压力测试:只看得到一部分神经元时,还能读出“果蝇在说哪些词”吗(12 个词的平均平衡准确率,0.5 = 瞎猜)
记录有噪声
通道中途坏掉(1,000 个有信号的通道)
同一批球(提前量瞄准:果蝇若毫无反应必被击中),每个条件 5 局 × 120 s、球速 60 mm/s,由本页同一套引擎无画面运行。
固定的 NeuroMechFly 看一个黑球,FlyGym 复眼(200 Hz)逐帧驱动 Lappalainen et al. 2024 预训练视觉网络 flyvis。30 种柱状细胞在每个柱上的活动,按 Codex 柱坐标(Matsliah et al. 2024)注入 FlyWire 全脑 LIF 里对应的 44,252 个神经元,读出下游的 LPLC2 与 LC4。这一步不是游戏里的手写视觉前端,游戏也没有用到它(离线运行,1.2 s 刺激约 90 s)。每个条件后来重复跑了 3 次:“逼近强于远离”8/8/8、“强于远距平移”8/7/8、“弱于近距平移”0/0/1,方向都不变;条件之间真正的不确定性来自格子对齐方式(朝向间标准差是重复间的 3–4 倍)。
事先声明的指标:刺激后 0.5–0.95 s 平均发放率(Hz),左侧神经元,8 种格子朝向的中位数
探索性指标(看过曲线后才加):碰撞前最后 150 ms 的峰值(Hz)
同样给 LC4 + LPLC2 注入 100 Hz、0.5 s × 4 次独立试次,对比子回路与 138,639 个神经元的全脑 Brian2 模型(全脑参考已按独立试次重跑)。左右转向与逃逸的模式一致;数值上子回路高估巨纤维、低估 DNa02。
node dodge/measure_neurons.js:颗粒钉在头前 1.3 mm 持续接触 → 味觉神经元 200 Hz,3 个种子,2.5 s 取后 1.25 s 平均);悬停可以看到全脑的对应值(python dodge/measure_neurons_fullbrain.py,138,639 个神经元,同样一起敲 + 零参照归一化 + 6 个实现)。
但必须说清楚:这个子回路只有 4,599 个神经元(全脑的 3%),单个神经元上会有明显失真。差别最大的是 CB0883——全脑里它是“单独敲 0.76、配上 Clavicle/G2N-1 才塌到 0.24”的枢纽,子回路里完全没作用(糖 ×1.07、水 ×1.00),配对也只是可加,因为它依赖的旁路在裁剪时被切掉了。反过来,Clavicle + G2N-1 在子回路里仍是协同(各自 0.91 / 0.67,一起 0.50,独立预期 0.61)。
最值得看的是糖和水的反差:Clavicle 对糖几乎没影响(×0.91)、对水直接归零(×0.01);CB0051 同样(×0.80 / ×0.02);G2N-1 反过来只管糖(×0.67 / ×1.07);Phantom 是抑制性的,敲掉它水通路的 MN9 反而翻倍(×2.06)。这正是报告 §21 那个结论在游戏里的样子——同一个伸喙动作,背后是两套线路。
把这 10 个神经元在全脑和子回路里的比值整体对照【探索性,n = 10】:糖 Pearson +0.75、水 +0.89,子回路整体上重现了全脑的敲除模式,水那一侧更贴近一些。最漂亮的一行还是 Phantom:敲掉它,全脑水通路的 MN9 变 3.02 倍、子回路 2.06 倍,方向和量级都对上了。
(这一格先前写的是“糖 +0.21、水 +0.92,子回路只重现水通路”,是错的:糖用了未归一化的值、又只取了 6 个有命名的类型;统一口径并补全 10 个后糖升到 +0.75。)