consciousness-research / docs/studies/03-prios/缺陷报告_零模型源限制_2026-09-02

DEF-001 · 缺陷 · 已复核 · 2026-09-02

缺陷报告:零模型允许「从未被刺激的电极」发出边

日期:2026-09-02 严重性:高。它很可能让 PRIOS完整方法学与结果报告.md §6.4 的整段结论作废。 主检验(清醒 vs 麻醉,p=0.0625)不受影响,理由见 §4。


1. 缺陷是什么

prios_code/prios_分析.py 里,观测图和零模型对「谁能发出边」的约束不一致。

观测图weighted_graph):一条有向边表示「刺激 A 之后在 B 上测到诱发反应」。 所以出边只可能来自被刺激过的电极

srcs = [e for e in p if e in idx] if C.EDGE_FROM_BOTH else [p[0]]
...
for s in srcs:
    W[idx[s], idx[c]] = max(W[idx[s], idx[c]], abs(pk[i]))

从没被刺激过的电极,那一行全是零。

零模型null_stats,修正前):把边重排到全部 n(n−1) 个有序对上:

prs = [(i, j) for i in range(n) for j in range(n) if i != j]

一个没有出边的节点不可能落在环上。 零模型的可用源集合比观测图大, 它的环占比就被系统性抬高,于是 CE_excess = 观测 − 零模型 被系统性压低。 压多少,取决于「被刺激的电极占全部节点的比例」(下称源占比)。

图建在全部共同电极上(W = np.zeros((len(chans), len(chans)))), 所以源占比在 PRIOS 各被试间从 0.43 变到 1.00——差异很大。


2. 证据一:项目自己存的结果里,这个相关性是 +0.908

prios_results_旧零模型_2026-08-29.json(已归档于 git 历史的 pre-migration tag, 不在当前工作树),取 d*=0.05 清醒条件:

被试 节点数 刺激对数 源占比 清醒 CE_excess 零模型基线
PRIOS01 42 36 1.00 +0.032 0.515
PRIOS09 50 44 1.00 +0.051 0.656
PRIOS02 69 30 0.87 −0.147 0.892
PRIOS05 63 23 0.73 −0.382 0.838
PRIOS04 56 15 0.54 −0.385 0.753
PRIOS03 61 13 0.43 −0.380 0.826
corr(源占比, 清醒 CE_excess) = +0.908

仅有的两个 CE_excess 为正的被试,恰好就是仅有的两个源占比 = 1.00 的被试。

(源占比按 min(2×刺激对数, 节点数)/节点数 估算,是上界; 重跑后 diag.n_src 会给出精确值。)


3. 证据二:纯随机图能凭空造出同样的模式

零模型源限制偏差检验.py:60 个节点,密度 0.05,图里没有任何真实结构, 只有随机边。唯一保留的差异就是「观测图限制源、零模型不限制」。

源占比 观测 CE 现行零模型 凭空产生的超出量 修正后零模型 修正后超出量
1.00 0.766 0.809 −0.043 0.809 −0.043
0.87 0.694 0.809 −0.115 0.706 −0.012
0.73 0.643 0.809 −0.166 0.602 +0.041
0.54 0.457 0.809 −0.352 0.447 +0.011
0.43 0.369 0.809 −0.440 0.368 +0.001

对照:同密度随机无环图的 CE = 0.0000。环判定本身没写错, 所以上表不是判定 bug 造成的。

把这一列跟 §2 的真实数据并排看:

源占比 真实数据 纯随机仿真
1.00 +0.032 −0.043
0.87 −0.147 −0.115
0.73 −0.382 −0.166
0.54 −0.385 −0.352
0.43 −0.380 −0.440

形状和量级都对上了。 也就是说,一个完全没有递归结构的随机图, 在当前零模型下会产出和我们观测到的几乎一样的「负超出量」。


4. 哪些结论受影响,哪些不受影响

受影响:报告 §6.4 的逐条件零模型检验。 原文的结论是 「清醒 CE_excess 均值 −0.202(p=0.065),麻醉 −0.396(p=0.027,显著地比随机图更不递归)」。 这段现在必须当作待定——它很可能整个是零模型设定错误的产物, 而不是关于大脑递归结构的发现。

不受影响:主检验(清醒 vs 麻醉配对比较,Wilcoxon W=1.0,双侧 p=0.0625)。 理由是代码里两个条件用的是同一个电极交集和同一组匹配刺激对common = [c for c in L["clin"][1] if c in set(L["prop"][1])]matched = {p: min(cnt["clin"][p], cnt["prop"][p]) ...}), 所以源集合在两条件间是逐位匹配的,偏差在做差时相消。

间接受影响:跨被试的可比性,进而是功效。 零模型基线在被试间从 0.515 漂到 0.892, corr(节点数, 零模型基线) = +0.990。 n=69 的被试,其 CE_excess 最多只能到 +0.108,却能低到 −0.892—— 正负空间严重不对称。这会往组间差里注入与假设无关的方差,吃掉功效


5. 修法

零模型的候选边槽限制为「源是被刺激过的电极」的有序对, 与观测图遵守同一条生成约束。

已实施(prios_分析.py): - weighted_graph 额外返回 src_idx / n_src - null_stats 增加 src_idx 参数;为 None 时保持旧行为(留作对照) - metrics 新旧两种零模型都算,修正版的键名带 _srcfix 后缀

两个都保留,是为了避免「换了算法才变好看」的嫌疑—— 对照就在同一个结果文件里,任何人都能自己比。

源集合取自「被刺激过的电极」这一结构事实,不随密度阈值化变化: 零模型要遵守的是与观测图同一条生成约束,不是阈值化后的残留。

冒烟测试(源占比 0.43,纯随机权重):

观测 CE           = 0.379
旧零模型 (不限源) = 0.802   超出量 -0.423
新零模型 (限制源) = 0.362   超出量 +0.016

旧结果 prios_results_旧零模型_2026-08-29.json 与旧脚本 prios_分析_旧版备份.py 已归档于 git 历史(pre-migration tag),取回命令:

git show pre-migration:prios_code/prios_results_旧零模型_2026-08-29.json
git show pre-migration:prios_code/prios_分析_旧版备份.py

5b. 第二个问题:固定 d*=0.05 让零模型基线随节点数大幅漂移

这是独立于源限制的另一个问题,同样吃功效。

密度规则标定.py(纯图论,不碰真实数据):

节点数 固定 d*=0.05 时的零模型基线
42 0.523
50 0.660
56 0.750
61 0.816
63 0.877
69 0.888
82 0.952
100 0.979

极差 0.455。 真实数据里的对应值是 0.515 → 0.892, corr(节点数, 零模型基线) = +0.990,完全吻合。

后果是正负空间不对称:n=69 的被试其 CE_excess 最多只能到 +0.112, 却能低到 −0.888。这往组间差里注入了与假设完全无关的方差。

闸门⑤拦不住它:那道闸门只要求随机图 CE < 0.95,而 n=69 时基线是 0.888, 刚好压线通过。闸门检验的是「有没有到天花板」,不是「基线在被试间稳不稳」。

修法:把 d* 从固定常数改成随节点数走的规则 d* = k·ln(n)/n

节点数 k=0.34 时的 d* 保留边数 零模型基线
42 0.030 52 0.183
56 0.024 75 0.160
69 0.021 98 0.186
82 0.018 121 0.174
100 0.016 155 0.161

基线极差从 0.455 降到 0.026。

诚实的附注:我扫 k 时每个点只跑了 10 次重复,所以 k 在 0.3–0.7 之间 各值的优劣差异有相当部分落在噪声里,「k=0.34 最优」这个具体数字不牢靠。 牢靠的是方向:这一族规则里随便取一个都比固定 d* 好一个数量级 (极差 0.03–0.16 对 0.455)。真要定 k,得加大重复次数重标。

而且这条规则不能事后直接套到现有结果上——我们已经看过哪些密度显著了 (敏感性分析里 d*=0.02 和 0.06 出了 p=0.031)。事后按这条规则重选密度, 等于用看过的结果挑参数。

正确做法:先用 ds004080 那 74 个清醒单态被试(那批数据里没有状态对比可偷看) 把 k 定死,写进预注册,再应用到状态对比上。

5c. 重跑结果(2026-09-02,六个被试全部重跑完成)

prios_code/重跑_源限制修正_2026-09-02.log。PRIOS06 仍按原闸门 3 排除,与旧版一致。

修正确实起作用了

检查项 旧零模型 修正后
corr(源占比, 清醒 CE_excess) +0.969 −0.316
corr(节点数, 零模型基线) +0.992 −0.184

源占比依赖被消除了(n=6 时 −0.32 与零无法区分), 节点数对基线的系统性驱动也消除了。这是修正有效的直接证据。

(注:旧版这两个相关系数用的是精确的 n_src,比我先前用 2×刺激对数 估算的 +0.908 更高。估算是上界,实测更糟。)

逐条件结果整个反过来了

d*=0.05,六个被试:

被试 节点 刺激源 源占比 清醒(旧) 清醒(修正) 麻醉(旧) 麻醉(修正)
PRIOS01 42 41 0.98 +0.067 +0.072 −0.152 −0.145
PRIOS02 69 44 0.64 −0.198 +0.121 −0.415 −0.041
PRIOS03 61 20 0.33 −0.375 +0.163 −0.435 +0.118
PRIOS04 56 24 0.43 −0.320 +0.087 −0.678 −0.154
PRIOS05 63 34 0.54 −0.369 +0.006 −0.756 −0.331
PRIOS09 50 50 1.00 +0.080 +0.080 +0.088 +0.125
旧零模型 修正零模型
清醒均值 −0.186 +0.088,95% CI [+0.033, +0.143],6/6 为正,双侧 p=0.031
麻醉均值 −0.391(显著为负) −0.071,95% CI [−0.256, +0.113],2/6 为正,p=0.31

旧结论说「两态都没有正向超出、麻醉态显著地比随机更不递归」。 修正后是「清醒态有正向超出,麻醉态与零无法区分」。方向整个反了。

但这个新结论不稳健,不能拿去下结论

跨六个密度阈值看,修正零模型下:

d* 清醒均值 正号 p 麻醉均值 p 主检验均值差 同向 p
0.02 +0.245 6/6 0.031 +0.114 0.44 +0.130 6/6 0.031
0.03 +0.215 5/6 0.063 +0.101 0.31 +0.114 5/6 0.063
0.04 +0.106 4/6 0.22 −0.017 1.00 +0.122 6/6 0.031
0.05(预注册) +0.088 6/6 0.031 −0.071 0.31 +0.159 5/6 0.094
0.06 +0.020 3/6 0.44 −0.075 0.16 +0.095 5/6 0.063
0.08 −0.021 3/6 0.84 −0.042 0.31 +0.021 4/6 0.84

「清醒态有正向超出」只在 6 个密度里的 2 个上显著(0.02 与 0.05),不稳健。 均值随密度单调下降到零,这本身是预期的(密度越高越接近饱和、超出空间越小), 但它意味着这个结论强烈依赖阈值选择。

而且 n=6 时全部同号能达到的最好双侧 p 就是 0.03125。 所以 p=0.031 是功效天花板,不是强证据——它等价于「六个人方向全一致」,仅此而已。

主检验:我先前说「不受影响」,说得不准

旧零模型 修正零模型
d*=0.05(预注册主检验) 5/6 同向,均值差 +0.205,W=1,p=0.0625 5/6 同向,均值差 +0.159,W=2,p=0.0938

方向、同向人数、逐被试符号全部不变(5 正 1 负,负的仍是 PRIOS09), 但 p 从 0.0625 变到 0.0938。

我先前判断「两条件源集合匹配、偏差在做差时相消,主检验不受影响」—— 方向上对,程度上说过头了。原因是每个条件用的是自己那一侧的源集合 (pairs_used 在两条件间不完全相同,例如 PRIOS01 是 36 对 vs 34 对), 所以偏差只是大部分相消,不是完全相消。

总判决不变:两个 p 都没过预注册的 α=0.05,仍然是「不确认,不证伪,功效不足」。

一条防止事后挑参数的说明

修法是从结构论证推出来的,并且在看到修正后的真实数据之前就已经在 纯随机图上验证过(零模型源限制偏差检验.py 先跑,重跑在后)。 新旧两版结果都存在同一个 JSON 里,键名分别是 ce_excessce_excess_srcfix。 任何人可以自己比。

5d. 第三次重跑:节点集改为预注册规定的双角色电极(最终版)

决定_节点集与密度规则_2026-09-02.md:预注册 §2 与报告 §4.1 都写明节点=双角色电极, 代码用的是全部共同通道,属实现不符规格。改正后重跑(重跑_双角色节点_2026-09-02.log)。

节点数变化

被试 原共同通道 双角色电极 仅记录未刺激(被剔除)
PRIOS01 42 41 1
PRIOS02 69 44 25
PRIOS03 61 20 41
PRIOS04 56 24 32
PRIOS05 63 34 29
PRIOS09 50 50 0

自洽检查:修正在正确节点集下应退化为空操作

被试 源占比 旧零模型基线 限源零模型基线
全部六人 1.00 0.0000

六个被试的新旧零模型逐位相同。这证明 §5 的源限制修正与节点集修正是同一个问题的两种修法, 节点集改对之后前者自动失效——正是决定文档里预期的结果。

最终结果(预注册节点集,d*=0.05)

全通道节点(旧,有缺陷) 全通道 + 限源零模型 双角色节点(预注册规定)
清醒均值 −0.186 +0.088 +0.106(6/6 为正,p=0.031)
麻醉均值 −0.391 −0.071 −0.024(2/6 为正,p=0.84)
主检验同向 5/6 5/6 4/6
主检验均值差 +0.205 +0.159 +0.130
主检验 W / p 1 / 0.0625 2 / 0.0938 4 / 0.2188

跨密度稳健性(双角色节点集)

d* 清醒均值 正号 p 麻醉均值 正号 p 主检验差 同向 p
0.02 +0.249 6/6 0.031 +0.045 3/6 0.44 +0.203 6/6 0.031
0.03 +0.232 6/6 0.031 +0.126 3/6 0.44 +0.106 5/6 0.16
0.04 +0.159 5/6 0.063 +0.025 3/6 0.56 +0.135 5/6 0.16
0.05(预注册) +0.106 6/6 0.031 −0.024 2/6 0.84 +0.130 4/6 0.219
0.06 +0.095 4/6 0.56 −0.079 1/6 0.31 +0.174 6/6 0.031
0.08 −0.067 1/6 0.44 −0.185 1/6 0.22 +0.118 4/6 0.22

怎么读这三轮

三次修正把主检验的 p 从 0.0625 推到了 0.094 再到 0.219。 每一次修正都是向着 预注册规格靠近,不是向着好看靠近。方向一次都没变:主检验均值差在全部 18 个 「修正版本 × 密度」组合里全部为正;清醒态正向超出在双角色节点集下于 5/6 个密度为正、 3 个密度上 6/6 被试同号。

判决不变,且比之前更清楚地是「功效不足」:n=6 时六人同号的双侧 p 天花板就是 0.031, 主检验只在 2/6 个密度上碰到这个天花板。方向稳定、显著性随密度跳动、 一个被试(PRIOS03,双角色节点仅 20 个)的噪声足以翻转主检验—— 这就是样本量不够的标准长相。

这轮修正最有价值的产出不是 p 值,而是:原报告 §6.4「麻醉态显著地比随机图更不递归」 这一「发现」被证实是缺陷产物,撤回。修正后麻醉态在任何密度下都与零无法区分。

6. 另一个待处理的问题:文档与实现对不上

PRIOS完整方法学与结果报告.md §4.1 写的是节点 = 双角色电极 (既被刺激过又被记录的电极)。代码实现用的是全部共同通道。

如果按文档办(节点集收缩成双角色电极),源占比恒等于 1,本缺陷自动消失。 如果按代码办,就需要 §5 的修正。

这两条路都成立,但必须选一条,并让文档和实现一致。 我倾向按 §5 修零模型而不是收缩节点集,因为被刺激电极之外的记录电极 承载了真实的接收端信息,删掉它们会丢数据; 但这一条应该由你或元宝那边定,因为它触及预注册的措辞。


7. 这个缺陷是怎么被发现的,以及它说明什么

不是靠复查代码发现的,是在找数据时顺带发现的: 一个并行的检索任务在核对数据格式时读了 prios_分析.py, 注意到 srcsprs 两处对「谁能发边」的约定不一致。

教训:这个缺陷在所有既有闸门下都是隐形的。 闸门①(阴性对照)、闸门③(距离)、闸门⑤(CE 饱和阈值)、 ce_mass 判别力标定——没有一个会触发,因为它们检验的都是 「检出的边是不是真的」,没有一个检验 「零模型和观测图是不是在同一个约束下生成的」。

建议新增一条通用闸门:任何置换零模型,都要先问一句 「观测图不可能出现的边,零模型会不会产生?」 如果会,零模型就比观测图更自由,差值里混进了纯结构性的偏移。 廉价的验法就是 §3 那个——把真实结构全部抹掉,看零模型还能不能造出差异。