总览 · ← 返回运行 20261003-043412-search-t2-heart-interp-g24-D-s1
节点 n12
在父节点10实现跨类型表达相关匹配对齐(matched-align)替代exact-name作Procrustes地标;机制激活(22匹配对)但shape_scale未提升(55.2–56.2 vs 56.21),按PLAN回退关闭,提交=父节点行为。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-043412-search-t2-heart-interp-g24-D-s1 |
|---|---|
| 父节点 | n10 |
| 子节点 | n14 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 63.01(+0.0) · proxy 63.01(+0.0) |
| 审查 | 未审查 |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 25 分 |
| 程序版本 | 79854afc0c59cfb6ebabf099846d1b2cf5dc4b33 (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git 79854afc0c:solution/METHOD.md
在父节点10实现跨类型表达相关匹配对齐(matched-align)替代exact-name作Procrustes地标;机制激活(22匹配对)但shape_scale未提升(55.2–56.2 vs 56.21),按PLAN回退关闭,提交=父节点行为。
方法族与实现
PLAN 指定家族 T2HI-01(插值:对齐 → logit 组成插值 → 共有类型内 k-NN 簇抽样 + 逐细胞表达凸组合,坐标 hold)。本节点按 PLAN 新增 matched_align 子机制:把 Procrustes 对齐地标从 exact-name 共有类型换成跨类型表达相关贪心匹配对。核心家族机制(INTERP_ENABLED=1, EXPR_INTERP=1, COORD_MODE=hold, 簇抽样 K=25/C=10)全部保持打开。
matched_align 实现(solution/run.py):
- 各侧按 celltype 算 pseudobulk 平均表达(每侧 ≥
MATCH_MIN_CELLS细胞的类型)。 - 计算 a-型 × b-型 Pearson 相关矩阵,按相关从高到低贪心选匹配对(每型至多用一次,r ≥
MATCH_R),权重 = min(两侧该型细胞数)。 - 用匹配对质心做 Procrustes:xy Kabsch(允许反射),z 不旋转、符号由匹配质心 z 相关定。
- 匹配对 < 3 → 回退 exact-name
align_pair(其内部再回退 PCA)。 - 开关
T2_MATCHED_ALIGN=1/0;另有T2_MATCH_R、T2_MATCH_MIN_CELLS、T2_MATCH_WEIGHT(count/corr/corr2)、T2_MATCH_ZFLIP供搜索。
机制生效证据(实际改变了什么)
机制确实激活并改变输出(非空转)。proxy 上 r≥0.3 时贪心匹配到 22 对(远超回退阈值 3),含大量生物合理的跨类型匹配:Endo↔Great Artery Endoth(0.863)、V-CM↔V-CM(0.851)、JCF↔Peri(0.844)、Intra-Endoth-2↔BEC(0.812)、Lateral FG↔Foregut(0.812)、IFT-CM↔A-CM(0.807)、d-CSE↔SE(0.77)、pPHM↔aPHM(0.751) 等。相比 exact-name 仅 5 个共有类型(NCC/Peri/V-CM/aPHM/pPHM)。匹配地标改变了作用于全部细胞坐标的整体刚体+反射变换:det、xy 旋转、以及 z 符号判定(matched 给出 z_flipped=True,exact-name 为 False)都随之改变,因此每个细胞的落点随位置而异——不是常数位移,也不是组成重加权。
对照结果(A 半,seed 0,官方评分器)
| 配置 | board | shape_scale | local_spatial | cell_state | expr_change |
|---|---|---|---|---|---|
| 提交:matched OFF(=父节点10) | 62.59 | 56.21 | 63.66 | 65.73 | 64.77 |
| matched ON r0.3 count(z-flip) | 62.39 | 55.23 | 63.81 | 65.73 | 64.77 |
| matched ON r0.3 count(no z-flip) | 62.36 | 55.45 | 63.49 | 65.73 | 64.77 |
| matched ON r0.7(11 对) | 62.51 | 55.67 | 63.89 | 65.73 | 64.77 |
| matched ON weight=corr | 62.39 | 55.28 | 63.80 | 65.73 | 64.77 |
| matched ON weight=corr2(最佳) | 62.56 | 56.19 | 63.57 | 65.73 | 64.77 |
关闭对照:T2_MATCHED_ALIGN=0 与父节点10输出逐位一致(X 与 spatial_3D 均 array_equal=True),board 62.59、shape_scale 56.21,符合 PLAN 的 mechanism_off_control 预期。
结论:机制生效但未带来收益。 所有 matched-ON 配置的 shape_scale(55.2–56.2)均不高于父节点 exact-name 的 56.21;board 全部 ≤ 62.59。最佳的 corr2 加权(用 r² 压低可疑低相关对)仅把 shape_scale 拉回 56.19 ≈ 父节点,board 62.56 仍在噪声内略低。PLAN 的成功判据是「shape_scale 提升 > 1 且总分不降」——未满足,故按 PLAN 明确指示「否则回退 0」,提交 T2_MATCHED_ALIGN=0。
为什么不奏效(与节点8的差异)
节点8在 node7(mix 组成、shape_scale 52.78)上把地标换成 matched-align 得到 +3.15(→55.93)。但本父节点10(logit 组成 + coord=hold)的 exact-name 对齐已经达到 shape_scale 56.09/56.21,即 matched-align 在节点8 上换来的水平,node10 用 5 个空间可靠的同名类型就已达到。matched-align 再叠加只会在饱和点上波动:表达相关匹配把空间上并不对应的类型(如 Unknown↔OFT-CM、Allantois↔Branch Arch、Peri↔ST)也拉来当地标,Kabsch 为对齐这些表达相似但位置不符的质心而扭动 xy 旋转,反而不如同名地标稳。这正是 PLAN 风险 #1(与节点8/11 重叠、增益已被父节点吸收)的实现。
附加搜索:簇抽样参数(均 matched OFF)
顺带扫了 local_spatial 的最大杠杆——簇抽样 KNN_K × N_CLUSTERS(父=25×10):
| K×C | board | shape_scale | local_spatial |
|---|---|---|---|
| 25×10(父) | 62.59 | 56.21 | 63.66 |
| 50×10 | 62.58 | 55.01 | 64.04 |
| 25×20 | 62.57 | 55.20 | 63.90 |
| 25×5 | 61.63 | 51.50 | 65.10 |
| 40×8 | 61.95 | 51.85 | 64.93 |
| 15×10 | 61.31 | 50.79 | 64.39 |
shape_scale 对 K 极敏感,K=25/C=10 是尖峰最优;增大 K 或改 C 都以 shape 换 local,总分不升反降。父节点已坐在该前沿最优处,无免费改进。
已验证 / 未验证
已验证:(1) proxy 全流程跑通(~2s,纯 CPU,峰值内存远低于 limits),vec-check ok;(2) 提交默认(无环境变量)与父节点10逐位一致,seed 0 两次逐位相同,np.random.default_rng(seed),无全局随机;(3) matched ON/OFF 对照如上,机制激活时确实改变 22 对地标、xy 旋转与 z 符号(改变全部细胞坐标),OFF 时回退父节点;(4) 视图无关:matched_align/matched_landmarks 只用 coords、labels、X,不读 board/mode/path、不用绝对时间;t 由 interp_bracket 用时间差算,统一平移不变;默认 OFF 路径 = 父节点(已证视图无关)。
未验证 / 风险:(1) matched-align 在 final(31 共有类型) 上是否更有价值未测——proxy 仅 5 个同名类型,matched 的 22 对里很多是跨类型,若 final 同名类型已足够多,matched 的边际收益可能仍为负;本节点据 proxy 判其不奏效并关闭。(2) shape_scale 受目标 RMS 制约:两端 log-线性给 346,proxy 真值 E8.75≈217(心脏尺度非单调 354→217→335),scale_log_ratio≈0.467 是 shape 的主惩罚,无法从括号两端推出更小的目标,matched-align 不改 RMS 故对此无能为力——这是方法卡已知弱点,未硬调 scale_damp 到 proxy 真值(会破坏视图无关)。(3) 未在 final/伪装视图实跑,仅代码层面论证视图无关。
生物学知识来源
未引入外部文献/数据库/检索知识,也未用保留阶段或保留基因型的任何信息。细胞类型名、表达、坐标全部来自视图给定的官方输入阶段(proxy: E8.25_late、E9.5,均在允许范围;目标 E8.75 未使用)。跨类型表达相关匹配、Pearson 相关、Kabsch/Procrustes、贪心匹配均为通用统计/几何方法,不含针对禁窗阶段的先验。未读 external/ 与 prior/。
下一步
- matched-align 在本 base 饱和,不要再在 node10 系上重试同款对齐(本节点已证伪);若要验证它在多类型场景的价值,应在 final-like(多共有类型)视图上单独测,而非 proxy。
- shape_scale 被目标 RMS 卡死:真正需要的是不使用真值的更好 RMS 估计(如 RMS 随 log 细胞数/阶段间隔的回归、或对称验证的 SCALE_DAMP),但要警惕过拟合 proxy 的非单调 dip。
- local_spatial/expr 的前沿已被父节点占满;若要突破,需换机制(如容量约束/双向互匹配的表达插值、或亚型细分以在多类型下激活 expr interp),而非调簇参数。
调研员的计划
| 名称 | 在 T2HI-01 框架引入跨类型表达相关匹配对齐,修复最弱组 shape_scale |
|---|---|
| 动机 | 父节点10四组分 shape_scale=56.09 最弱(比最高 cell_state 66.54 低 10.45),ANALYSIS 归因于 exact-name 共有类型对齐较弱+目标 RMS 估偏。同树节点8已实证:把 Procrustes 对齐地标从 exact-name 换成跨类型表达相关贪心匹配对(Pearson r≥0.3、每侧≥30细胞),在 mix+簇抽样下使 shape_scale 52.78→55.93(+3.15,远超 T2 噪声~1),是全部增益来源。父节点10仍用 exact-name 对齐、未吸收该有效部件,故最弱组有明确、可解释且已被验证的结构问题可修,且不与已失败改法重复。 |
| 做法 | 在父节点10的 run.py 新增 matched_align 模块(移植/按节点8描述重写):对齐阶段计算两侧各型 pseudobulk 表达,按 Pearson 相关贪心选跨类型匹配对(初值 r≥0.3、每侧≥30 细胞、权重=min(两侧该型细胞数)),用匹配对质心做 Procrustes(xy Kabsch 允许反射,z 不旋转、符号由匹配质心 z 相关决定);匹配对<3 回退 exact-name,再回退 PCA。logit 组成插值、k-NN 簇抽样、逐细胞表达凸组合全部保持不变,只换对齐地标。开关 T2_MATCHED_ALIGN=1/0。单输入/外推退路不变(分层复制 anchor)。搜索范围小而快:r∈{0.25,0.3,0.4}×每侧最小细胞数∈{20,30}。用 vec-score 先跑 proxy seed0:仅当 shape_scale 提升>1 且总分不降才提交,否则回退 0。 |
| 风险 | 1) 与节点11(节点8后续)可能重叠——Engineer 应先确认节点11是否已用同款对齐;2) proxy 只有5个共有类型,表达相关匹配对可能<3 而触发回退,导致机制未激活——必须打印实际匹配对数与是否回退;3) 节点10的 logit 组成插值改变配额,理论上与对齐独立,但若交互异常应先对齐后抽样验证;4) 30分钟时限紧,移植后先用单侧抽样子集确认对齐收敛再全量跑。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 47ac7bd258。改动的文件:solution/METHOD.md +46 −24、solution/run.py +137 −6
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex 751f688..142c49c 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,44 +1,66 @@-T2HI-01 draft:对齐后按 logit 插值类型组成,共有类型内用 k-NN 区域生长抽样并对每个细胞与对侧表达最近邻做伪批量凸组合插值,坐标保留真实对齐位置。+在父节点10实现跨类型表达相关匹配对齐(matched-align)替代exact-name作Procrustes地标;机制激活(22匹配对)但shape_scale未提升(55.2–56.2 vs 56.21),按PLAN回退关闭,提交=父节点行为。 ## 方法族与实现 -实现 PLAN 指定的 **T2HI-01** 家族:插值括号两侧阶段 → 对齐 → logit 组成插值 → 共有类型内逐细胞最近邻伪批量插值。核心机制在 `solution/run.py`,由常量/环境开关控制,提交时**全部打开**(`INTERP_ENABLED=1`, `EXPR_INTERP=1`, `COORD_MODE=hold`)。+PLAN 指定家族 **T2HI-01**(插值:对齐 → logit 组成插值 → 共有类型内 k-NN 簇抽样 + 逐细胞表达凸组合,坐标 hold)。本节点按 PLAN 新增 **matched_align** 子机制:把 Procrustes 对齐地标从 exact-name 共有类型换成**跨类型表达相关贪心匹配对**。核心家族机制(`INTERP_ENABLED=1`, `EXPR_INTERP=1`, `COORD_MODE=hold`, 簇抽样 K=25/C=10)全部保持打开。 -流程:-1. `vio.interp_bracket(manifest)` 取目标两侧最近输入 (a, b) 与 `t=(target−a)/(b−a)`;不被括号(外推/单输入)则退路为分层复制 anchor 阶段到 [min,max],不崩。-2. **坐标对齐**:`frame.align_pair(..., "procrustes")`(xy Kabsch 允许反射,z 符号按共有类型质心相关定向,z 不旋转),两侧再 `scale_to_rms` 到 log-线性目标 RMS(`scale_damp=1`)。-3. **logit 组成插值(机制核心之一)**:共有类型(两侧各 ≥5 细胞)用 `q=sigmoid((1−t)·logit(p_a)+t·logit(p_b))`;单侧类型按该侧权重 `×(1−t)` 或 `×t`;归一化后按总细胞数 `N=interp_count`(log-线性,夹到 [min,max])分配每型配额(带上限+余额重分配)。-4. **类型内最近邻伪批量插值(机制核心之二)**:共有类型按配额从两侧各用 **k-NN 贪心区域生长**(`KNN_K=25`、约 `N_CLUSTERS=10` 个空间连续簇)抽细胞;对每个抽出细胞,在**同类型对侧**的表达 PCA(前 `PCA_DIM=50` 维)空间找最近邻,做凸组合 `x=(1−t)·x_self + t·x_nn`(产生连续过渡的合成细胞,非整细胞混抽)。单侧类型直接取真实细胞。-5. 合并 → `_jitter` 去重 → `scale_to_rms(target_rms)` → `write_t2` 输出(表达+`spatial_3D`)。+matched_align 实现(`solution/run.py`):+1. 各侧按 celltype 算 pseudobulk 平均表达(每侧 ≥ `MATCH_MIN_CELLS` 细胞的类型)。+2. 计算 a-型 × b-型 Pearson 相关矩阵,按相关从高到低**贪心**选匹配对(每型至多用一次,r ≥ `MATCH_R`),权重 = min(两侧该型细胞数)。+3. 用匹配对质心做 Procrustes:xy Kabsch(允许反射),z 不旋转、符号由匹配质心 z 相关定。+4. 匹配对 < 3 → 回退 exact-name `align_pair`(其内部再回退 PCA)。+5. 开关 `T2_MATCHED_ALIGN=1/0`;另有 `T2_MATCH_R`、`T2_MATCH_MIN_CELLS`、`T2_MATCH_WEIGHT`(count/corr/corr2)、`T2_MATCH_ZFLIP` 供搜索。 -## 关键参数+## 机制生效证据(实际改变了什么) -`MIN_TYPE_CELLS=5`, `KNN_K=25`, `N_CLUSTERS=10`, `PCA_DIM=50`, `SCALE_DAMP=1.0`, `COUNT_DAMP=1.0`。坐标:`COORD_MODE=hold`(抽出细胞保留自己所在阶段的对齐坐标,不插值坐标)。+机制**确实激活并改变输出**(非空转)。proxy 上 r≥0.3 时贪心匹配到 **22 对**(远超回退阈值 3),含大量生物合理的跨类型匹配:Endo↔Great Artery Endoth(0.863)、V-CM↔V-CM(0.851)、JCF↔Peri(0.844)、Intra-Endoth-2↔BEC(0.812)、Lateral FG↔Foregut(0.812)、IFT-CM↔A-CM(0.807)、d-CSE↔SE(0.77)、pPHM↔aPHM(0.751) 等。相比 exact-name 仅 5 个共有类型(NCC/Peri/V-CM/aPHM/pPHM)。匹配地标改变了作用于**全部细胞坐标**的整体刚体+反射变换:det、xy 旋转、以及 z 符号判定(matched 给出 z_flipped=True,exact-name 为 False)都随之改变,因此每个细胞的落点随位置而异——不是常数位移,也不是组成重加权。 -## 机制生效证据+## 对照结果(A 半,seed 0,官方评分器) -proxy 上共有类型 5 个(NCC, Peri, V-CM, aPHM, pPHM),**2972 / 17616 ≈ 17% 细胞的表达被逐细胞插值改动**(其余为单侧真实细胞)。打印见 stderr `[interp] ... interp_cells=2972`。四组分(A 半,seed 0):--| 配置 | board | expr_change | cell_state | shape_scale | local_spatial |+| 配置 | board | shape_scale | local_spatial | cell_state | expr_change | |---|---:|---:|---:|---:|---:|-| **提交(机制全开, coord=hold)** | **62.59** | 64.77 | 65.73 | 56.21 | 63.66 |-| 对照 `INTERP_ENABLED=0`(=seed mix 混抽真实细胞) | 59.07 | 63.83 | 65.93 | 53.07 | 53.46 |-| 消融 `EXPR_INTERP=0`(仅 logit 组成+簇抽样+真实表达) | 62.92 | 65.09 | 66.49 | 56.21 | 63.90 |-| `COORD_MODE=interp`(表达+坐标都插值) | 61.57 | 64.77 | 65.73 | 53.18 | 62.61 |-| 参考:当前最佳 node 7(簇抽样 mix) | 62.61 | 64.90 | 67.58 | 52.78 | 65.20 |+| **提交:matched OFF(=父节点10)** | **62.59** | **56.21** | 63.66 | 65.73 | 64.77 |+| matched ON r0.3 count(z-flip) | 62.39 | 55.23 | 63.81 | 65.73 | 64.77 |+| matched ON r0.3 count(no z-flip) | 62.36 | 55.45 | 63.49 | 65.73 | 64.77 |+| matched ON r0.7(11 对) | 62.51 | 55.67 | 63.89 | 65.73 | 64.77 |+| matched ON weight=corr | 62.39 | 55.28 | 63.80 | 65.73 | 64.77 |+| matched ON weight=corr2(最佳) | 62.56 | 56.19 | 63.57 | 65.73 | 64.77 |++**关闭对照**:`T2_MATCHED_ALIGN=0` 与父节点10输出**逐位一致**(X 与 spatial_3D 均 `array_equal=True`),board 62.59、shape_scale 56.21,符合 PLAN 的 mechanism_off_control 预期。++**结论:机制生效但未带来收益。** 所有 matched-ON 配置的 shape_scale(55.2–56.2)均**不高于**父节点 exact-name 的 56.21;board 全部 ≤ 62.59。最佳的 corr2 加权(用 r² 压低可疑低相关对)仅把 shape_scale 拉回 56.19 ≈ 父节点,board 62.56 仍在噪声内略低。PLAN 的成功判据是「shape_scale 提升 > 1 且总分不降」——**未满足**,故按 PLAN 明确指示「否则回退 0」,提交 `T2_MATCHED_ALIGN=0`。++## 为什么不奏效(与节点8的差异)++节点8在 node7(mix 组成、shape_scale 52.78)上把地标换成 matched-align 得到 +3.15(→55.93)。但本父节点10(logit 组成 + coord=hold)的 exact-name 对齐**已经达到 shape_scale 56.09/56.21**,即 matched-align 在节点8 上换来的水平,node10 用 5 个空间可靠的同名类型就已达到。matched-align 再叠加只会在饱和点上波动:表达相关匹配把**空间上并不对应**的类型(如 Unknown↔OFT-CM、Allantois↔Branch Arch、Peri↔ST)也拉来当地标,Kabsch 为对齐这些表达相似但位置不符的质心而扭动 xy 旋转,反而不如同名地标稳。这正是 PLAN 风险 #1(与节点8/11 重叠、增益已被父节点吸收)的实现。++## 附加搜索:簇抽样参数(均 matched OFF)++顺带扫了 local_spatial 的最大杠杆——簇抽样 KNN_K × N_CLUSTERS(父=25×10):++| K×C | board | shape_scale | local_spatial |+|---|---:|---:|---:|+| 25×10(父) | 62.59 | 56.21 | 63.66 |+| 50×10 | 62.58 | 55.01 | 64.04 |+| 25×20 | 62.57 | 55.20 | 63.90 |+| 25×5 | 61.63 | 51.50 | 65.10 |+| 40×8 | 61.95 | 51.85 | 64.93 |+| 15×10 | 61.31 | 50.79 | 64.39 | -对照结果:机制全开 **62.59 vs 关闭 59.07(+3.5)**,主要来自 local_spatial(簇抽样保留局部邻域 53.46→63.66)与 shape_scale(coord=hold 保真实坐标 53.07→56.21)。关闭后确实回到 seed mix 水平(≈59),证明代码路径激活且机制改变输出。+shape_scale 对 K 极敏感,K=25/C=10 是尖峰最优;增大 K 或改 C 都以 shape 换 local,总分不升反降。父节点已坐在该前沿最优处,无免费改进。 ## 已验证 / 未验证 -**已验证**:(1) proxy 全流程跑通(~4s,纯 CPU),`vec-check` ok;(2) seed 0 两次逐位一致(X 与坐标),`np.random.default_rng(seed)`,无全局随机;(3) 机制开关对照如上;(4) 视图无关:`t` 只用时间**差**(括号两端),目标 RMS/组成/配额全部现场从数据算,无绝对时间分支、无写死细胞数/阶段名/视图名,时间统一平移不改变输出。未读 `external/` 与 `prior/`,只用视图给的两个官方输入阶段,无保留阶段/基因型信息。+**已验证**:(1) proxy 全流程跑通(~2s,纯 CPU,峰值内存远低于 limits),`vec-check` ok;(2) 提交默认(无环境变量)与父节点10**逐位一致**,seed 0 两次逐位相同,`np.random.default_rng(seed)`,无全局随机;(3) matched ON/OFF 对照如上,机制激活时确实改变 22 对地标、xy 旋转与 z 符号(改变全部细胞坐标),OFF 时回退父节点;(4) 视图无关:matched_align/matched_landmarks 只用 coords、labels、X,不读 board/mode/path、不用绝对时间;t 由 `interp_bracket` 用时间**差**算,统一平移不变;默认 OFF 路径 = 父节点(已证视图无关)。 -**未验证 / 风险**:(1) proxy 仅 5 个共有类型,类型内表达插值几乎退化——消融显示 `EXPR_INTERP=0` 反而略高(62.92 vs 62.59,差 0.33 << 噪声 ~2,属噪声内),说明 proxy 上增益主要来自 logit 组成插值+簇抽样,表达插值近乎中性;按 PLAN,表达插值是家族定义机制、在 final(31 共有类型)预期作用更大,故**提交保持打开**,未因 proxy 噪声关掉。(2) 坐标:PLAN 建议坐标也插值,但 proxy 上 coord=interp 明显更差(61.57),故改 hold(保真实对齐坐标)——这是相对 PLAN 的实证偏离,final 上是否仍 hold 更好未验证。(3) 目标 RMS 用两端 log-线性(346),proxy 真值 E8.75≈217,`scale_log_ratio=0.467` 偏弱,但两端都大、无法从括号推出更小的目标,属方法卡已知弱点,未针对 proxy 硬调 `scale_damp`(会破坏视图无关)。(4) 未在 final/伪装视图实跑,仅代码层面论证视图无关。+**未验证 / 风险**:(1) matched-align 在 **final(31 共有类型)** 上是否更有价值未测——proxy 仅 5 个同名类型,matched 的 22 对里很多是跨类型,若 final 同名类型已足够多,matched 的边际收益可能仍为负;本节点据 proxy 判其不奏效并关闭。(2) shape_scale 受目标 RMS 制约:两端 log-线性给 346,proxy 真值 E8.75≈217(心脏尺度非单调 354→217→335),scale_log_ratio≈0.467 是 shape 的主惩罚,**无法从括号两端推出更小的目标**,matched-align 不改 RMS 故对此无能为力——这是方法卡已知弱点,未硬调 scale_damp 到 proxy 真值(会破坏视图无关)。(3) 未在 final/伪装视图实跑,仅代码层面论证视图无关。 ## 生物学知识来源 -未引入外部文献/数据库知识;细胞类型名、表达、坐标全部来自视图给定的官方输入阶段(proxy: E8.25_late, E9.5,均在允许范围,目标 E8.75 未使用)。logit 组成插值、k-NN 区域生长、表达 PCA 最近邻伪批量插值均为通用统计/几何方法,不含针对禁窗阶段的先验。+未引入外部文献/数据库/检索知识,也未用保留阶段或保留基因型的任何信息。细胞类型名、表达、坐标全部来自视图给定的官方输入阶段(proxy: E8.25_late、E9.5,均在允许范围;目标 E8.75 未使用)。跨类型表达相关匹配、Pearson 相关、Kabsch/Procrustes、贪心匹配均为通用统计/几何方法,不含针对禁窗阶段的先验。未读 `external/` 与 `prior/`。 ## 下一步 -final(31 共有类型)上验证表达插值是否真正提升 cell_state;试 coord=interp vs hold 在多类型下谁更好;组成插值可试线性 vs logit 的取舍。+1. matched-align 在本 base 饱和,**不要再在 node10 系上重试同款对齐**(本节点已证伪);若要验证它在多类型场景的价值,应在 final-like(多共有类型)视图上单独测,而非 proxy。+2. shape_scale 被目标 RMS 卡死:真正需要的是**不使用真值**的更好 RMS 估计(如 RMS 随 log 细胞数/阶段间隔的回归、或对称验证的 SCALE_DAMP),但要警惕过拟合 proxy 的非单调 dip。+3. local_spatial/expr 的前沿已被父节点占满;若要突破,需换机制(如容量约束/双向互匹配的表达插值、或亚型细分以在多类型下激活 expr interp),而非调簇参数。diff --git a/solution/run.py b/solution/run.pyindex f6d347d..565ec2f 100644--- a/solution/run.py+++ b/solution/run.py@@ -21,21 +21,144 @@ from scipy.spatial import cKDTree from scipy.spatial.distance import cdist from src.task2_spatial import view_io as vio-from src.task2_spatial.frame import align_pair, log_interp, rms_radius, scale_to_rms+from src.task2_spatial.frame import (+ align_pair,+ center,+ kabsch,+ log_interp,+ rms_radius,+ scale_to_rms,+) from src.task2_spatial.methods import _jitter from src.task2_spatial.sample import interp_count, take INTERP_ENABLED = os.environ.get("T2HI_INTERP", "1") != "0" COORD_MODE = os.environ.get("T2HI_COORD", "hold") # interp | hold EXPR_INTERP = os.environ.get("T2HI_EXPR", "1") != "0" # blend shared-type expr toward NN+# matched-align: replace exact-name Procrustes landmarks with cross-type+# expression-correlation greedy matched pairs. Empirically REJECTED on this base+# (see METHOD.md): node-10 exact-name align already saturates shape_scale, and+# expression-correlation landmarks distort the in-plane rotation. Submitted OFF+# per the PLAN criterion (fall back to 0 when shape_scale does not improve >1).+# Set T2_MATCHED_ALIGN=1 to re-enable the implemented mechanism for comparison.+MATCHED_ALIGN = os.environ.get("T2_MATCHED_ALIGN", "0") != "0"+MATCH_R = float(os.environ.get("T2_MATCH_R", "0.3"))+MATCH_MIN_CELLS = int(os.environ.get("T2_MATCH_MIN_CELLS", "30"))+MATCH_ZFLIP = os.environ.get("T2_MATCH_ZFLIP", "1") != "0" # diagnostic: apply matched z-sign decision+MATCH_WEIGHT = os.environ.get("T2_MATCH_WEIGHT", "count") # count | corr | corr2 MIN_TYPE_CELLS = 5-KNN_K = 25-N_CLUSTERS = 10+KNN_K = int(os.environ.get("T2_KNN_K", "25"))+N_CLUSTERS = int(os.environ.get("T2_N_CLUSTERS", "10")) PCA_DIM = 50 SCALE_DAMP = 1.0 COUNT_DAMP = 1.0 +def _type_pseudobulk(X, labels: np.ndarray, min_cells: int):+ """Mean log-expression per cell type with >= min_cells; returns {label: (vec, count)}."""+ labs = np.asarray(labels).astype(str)+ out = {}+ for lab in np.unique(labs):+ idx = np.flatnonzero(labs == lab)+ if idx.size >= min_cells:+ vec = np.asarray(X[idx].mean(axis=0), dtype=np.float64).ravel()+ out[str(lab)] = (vec, idx)+ return out+++def matched_landmarks(coords_a, coords_b, labels_a, labels_b, X_a, X_b,+ r_thresh: float, min_cells: int, weight: str = "count"):+ """Greedy cross-type expression-correlation matching -> paired centroids + weights.++ Returns (ca, cb, w, pairs) where ca/cb are (m, 3) centroids of matched a/b+ types, w is the per-pair weight, and pairs is a list of (label_a, label_b, r).+ Types are matched at most once each. weight: count=min(n_a,n_b);+ corr=count*r; corr2=count*r^2 (downweights low-confidence matches).+ """+ pb_a = _type_pseudobulk(X_a, labels_a, min_cells)+ pb_b = _type_pseudobulk(X_b, labels_b, min_cells)+ keys_a = sorted(pb_a)+ keys_b = sorted(pb_b)+ if not keys_a or not keys_b:+ return (np.zeros((0, 3)), np.zeros((0, 3)), np.zeros(0), [])+ A = np.stack([pb_a[k][0] for k in keys_a])+ B = np.stack([pb_b[k][0] for k in keys_b])+ Ac = A - A.mean(axis=1, keepdims=True)+ Bc = B - B.mean(axis=1, keepdims=True)+ An = Ac / (np.linalg.norm(Ac, axis=1, keepdims=True) + 1e-12)+ Bn = Bc / (np.linalg.norm(Bc, axis=1, keepdims=True) + 1e-12)+ C = An @ Bn.T # (na, nb) Pearson correlation+ order = np.dstack(np.unravel_index(np.argsort(-C, axis=None), C.shape))[0]+ used_a, used_b = set(), set()+ pairs, ca, cb, w = [], [], [], []+ for i, j in order:+ r = float(C[i, j])+ if r < r_thresh:+ break+ if i in used_a or j in used_b:+ continue+ used_a.add(int(i))+ used_b.add(int(j))+ ka, kb = keys_a[i], keys_b[j]+ ia = pb_a[ka][1]+ ib = pb_b[kb][1]+ pairs.append((ka, kb, r))+ ca.append(coords_a[ia].mean(axis=0))+ cb.append(coords_b[ib].mean(axis=0))+ cnt = float(min(ia.size, ib.size))+ if weight == "corr":+ wv = cnt * r+ elif weight == "corr2":+ wv = cnt * r * r+ else:+ wv = cnt+ w.append(wv)+ if not pairs:+ return (np.zeros((0, 3)), np.zeros((0, 3)), np.zeros(0), [])+ return np.stack(ca), np.stack(cb), np.array(w, dtype=np.float64), pairs+++def matched_align(coords_a, coords_b, labels_a, labels_b, X_a, X_b,+ r_thresh: float, min_cells: int, apply_zflip: bool = True,+ weight: str = "count"):+ """Procrustes-align b onto a using matched-pair centroids (z held, xy Kabsch+ with reflection). Falls back to exact-name align_pair when < 3 matched pairs.+ Both clouds are returned in a common centred frame, rescaled to nothing here+ (caller applies target RMS). Diagnostic dict reports matched-pair details.+ """+ ca, cb, w, pairs = matched_landmarks(+ coords_a, coords_b, labels_a, labels_b, X_a, X_b, r_thresh, min_cells, weight+ )+ a = center(coords_a)+ b = center(coords_b)+ info = {"align": "matched", "n_matched": int(len(pairs))}+ # recompute centroids in the centred frame+ if len(pairs) >= 3:+ ca_c = ca - coords_a.mean(axis=0)+ cb_c = cb - coords_b.mean(axis=0)+ r, _, _ = kabsch(cb_c[:, :2], ca_c[:, :2], w, allow_reflection=True)+ by = b[:, :2] @ r+ ay = a[:, :2]+ za = ca_c[:, 2] - np.average(ca_c[:, 2], weights=w)+ zb = cb_c[:, 2] - np.average(cb_c[:, 2], weights=w)+ z_dot = float(np.dot(za, zb))+ flip = bool(z_dot < 0) and apply_zflip+ bz = -b[:, 2] if flip else b[:, 2]+ out_a = np.column_stack([ay, a[:, 2]])+ out_b = np.column_stack([by, bz])+ info.update(det=float(np.linalg.det(r)), z_flipped=bool(flip),+ z_dot=z_dot, fallback=None,+ pairs=[(pa, pb, round(pr, 3)) for pa, pb, pr in pairs])+ return center(out_a), center(out_b), info+ # not enough matched pairs -> exact-name procrustes (which itself -> PCA)+ aligned_a, aligned_b, info2 = align_pair(coords_a, coords_b, labels_a, labels_b, "procrustes")+ info2["align"] = "matched"+ info2["n_matched"] = int(len(pairs))+ info2["fallback"] = "exact_name" if info2.get("n_shared_types", 0) >= 3 else "pca"+ info2["pairs"] = [(pa, pb, round(pr, 3)) for pa, pb, pr in pairs]+ return aligned_a, aligned_b, info2++ def cluster_take(coords: np.ndarray, n: int, rng: np.random.Generator) -> np.ndarray: """Greedy k-NN region growth: ~N_CLUSTERS spatially contiguous blocks totalling n cells.""" m = len(coords)@@ -153,7 +276,13 @@ def run(view: str, out: str, seed: int) -> None: t = float(t) # align + rescale both stages to the log-linear target RMS- aligned_a, aligned_b, info = align_pair(sa.coords, sb.coords, sa.labels, sb.labels, "procrustes")+ if MATCHED_ALIGN:+ aligned_a, aligned_b, info = matched_align(+ sa.coords, sb.coords, sa.labels, sb.labels, sa.X, sb.X,+ MATCH_R, MATCH_MIN_CELLS, MATCH_ZFLIP, MATCH_WEIGHT,+ )+ else:+ aligned_a, aligned_b, info = align_pair(sa.coords, sb.coords, sa.labels, sb.labels, "procrustes") rms_a, rms_b = rms_radius(sa.coords), rms_radius(sb.coords) target_rms = log_interp(rms_a, rms_b, t, SCALE_DAMP) ca = scale_to_rms(aligned_a, target_rms)@@ -265,8 +394,10 @@ def run(view: str, out: str, seed: int) -> None: coords = scale_to_rms(_jitter(coords, rng), target_rms) print( f"[interp] t={t:.3f} n={len(expr)} shared={len(shared)} {shared} "- f"interp_cells={n_interp_cells} align_types={info.get('n_types')} "- f"rms={rms_radius(coords):.1f}",+ f"interp_cells={n_interp_cells} align={info.get('align')} "+ f"n_matched={info.get('n_matched')} fallback={info.get('fallback')} "+ f"n_types={info.get('n_shared_types')} z_flipped={info.get('z_flipped')} "+ f"pairs={info.get('pairs')} rms={rms_radius(coords):.1f}", file=sys.stderr, ) vio.write_t2(out, expr, coords.astype(np.float32), genes, seed=seed)
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| k024 | World-model evaluation dimensions for state-transition predictors | notes/competition/07_biomedical_world_models.md |
| k023 | Time-split validation with a held-out intermediate or next time point | notes/handover/02_知识学习路线.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 在父节点10 的 run.py 中新增 matched_align 子模块(跨类型 pseudobulk Pearson 相关贪心匹配对作 Procrustes 地标,含 T2_MATCHED_ALIGN/T2_MATCH_R/T2_MATCH_MIN_CELLS/T2_MATCH_WEIGHT/T2_MATCH_ZFLIP 开关,KNN_K 与 N_CLUSTERS 改为可环境变量覆盖),并按 PLAN 判据在提交时设为 OFF——提交产物与父节点10 逐位一致,故所有分组变化均为 +0.00。 |
|---|---|
| 各组分数的变化 | cell_state:噪声内(66.54 → 66.54,+0.00,提交=父节点) expression_change:噪声内(65.25 → 65.25,+0.00,提交=父节点) local_spatial:噪声内(64.17 → 64.17,+0.00) shape_scale:噪声内(56.09 → 56.09,+0.00);Engineer 自测 matched-ON 各配置 shape_scale 仅 55.2–56.2,均不高于 exact-name 的 56.21,即目标组未被改善 |
| family_id | T2HI-01 |
| 假设是否成立 | 否 |
| 经验 |
|
| mechanism_active | 否 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 4 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 3 次:bash 2、write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 14,034 · 输出 1,413 · 思考 249 |
| 任务(第一行) | 分析节点 n12 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/analyst.jsonl 12 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 40 |
| 工具调用 | 共 41 次:bash 19、edit 14、read 7、write 1 |
| 用时 | 18 分 |
| token 数 | 输入 82,034 · 输出 14,729 · 思考 19,953 |
| 任务(第一行) | 按计划修改程序:在 T2HI-01 框架引入跨类型表达相关匹配对齐,修复最弱组 shape_scale |
| 最后的回答(摘录) | All checks pass. Submission is complete and verified. **做了什么**:按 PLAN 在父节点10 (T2HI-01) 实现了 matched_align 子机制——用跨类型表达相关贪心匹配对(Pearson r≥阈值、每侧≥min细胞、权重=min细胞数)的质心替代 exact-name 共有类型作 Procrustes 地标(xy Kabsch 允许反射、z 符号由匹配质心定、<3对回退 exact-name→PCA),带 `T2_MATCHED_ALIGN` 开关。机制确实激活:proxy 上匹配到 22 对(含 Endo↔Great Artery Endoth 0.863、JCF↔Peri 0.844 等合理跨类型对),改变了作用于全部细胞坐标的整体变换。 **查分结果**(A半 seed0,用了 11/20 次):机制虽生效但**不奏效**。所有 matched-ON 配置 shape_scale 55.2–56.2 均不高于父节点 exact-name 的 56.21,board 全部 ≤62.59(最佳 corr2 加权 62.56 仅打平)。顺带扫的簇参数(K×C)也证实父节点 25×10 是尖峰最优,增大 K 以 shape 换 local、总分反降。按 PLAN 判据(shape 提升>1 且总分不降才提交)**未满足 → 回退 `T2_MATCHED_ALIGN=0`**。提交默认与父节点10逐位一致(62.59),deterministic、vec-check ok、视图无关。 **根因**:节点8 在 node7(shape 52.78)上靠 matched-align +3.15,但 node10 的 exact-name 对齐已达 56.09——增益已被父节点吸收(PLAN 风险#1 应验);表达相关匹配把空间不对应的类型也拉来当地标,反而扭动 xy 旋转。 **下一步最值得试**:(1) 不要再在 node10 系重试同款对齐(已证伪);(2) shape_scale 真正被目标 RMS 卡死(346 vs 真值217,心脏尺度非单调),需**不用真值**的更好 RMS 估计(RMS~log细胞数/阶段间隔回归),但警惕过拟合 proxy 的 dip;(3) 换机制而非调参——容量约束/双向互匹配的表达插值,或亚型细分以在多类型下激活 expr interp。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/engineer.jsonl 248 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 5 分 |
| token 数 | 输入 12,362 · 输出 5,928 · 思考 8,192 |
| 任务(第一行) | 为节点 n12(改进,父节点 n10)写改进计划 PLAN.json |
| 最后的回答(摘录) | 了符合"父节点所属方法族",写T2HI-01。并在mechanism里说清楚。
我认为方案合理。输出。
实际上再想:题目强调"针对最弱的一组"。我的方案针对shape_scale(最弱),符合。
还有一个担心:节点8的对齐改进是在没有组成插值的情况下。节点10有组成插值。引入表达相关对齐后,是否会有负面交互?例如表达相关匹配对可能选中不同表达状态的类型,对齐后与组成插值的配额不一致。但应该问题不大,因为对齐只影响坐标,组成插值影响细胞数量分配,两者独立。
好,确定。输出。
我会保持输出只有这个JSON。
</think>
```json
{
"name": "在 T2HI-01 框架引入跨类型表达相关匹配对齐,修复最弱组 shape_scale",
"motivation": "父节点10四组分 shape_scale=56.09 最弱(比最高 cell_state 66.54 低 10.45),ANALYSIS 归因于 exact-name 共有类型对齐较弱+目标 RMS 估偏。同树节点8已实证:把 Procrustes 对齐地标从 exact-name 换成跨类型表达相关贪心匹配对(Pearson r≥0.3、每侧≥30细胞),在 mix+簇抽样下使 shape_scale 52.78→55.93(+3.15,远超 T2 噪声~1),是全部增益来源。父节点10仍用 exact-name 对齐、未吸收该有效部件,故最弱组有明确、可解释且已被验证的结构问题可修,且不与已失败改法重复。",
"approach": "在父节点10的 run.py 新增 matched_align 模块(移植/按节点8描述重写):对齐阶段计算两侧各型 pseudobulk 表达,按 Pearson 相关贪心选跨类型匹配对(初值 r≥0.3、每侧≥30 细胞、权重=min(两侧该型细胞数)),用匹配对质心做 Procrustes(xy Kabsch 允许反射,z 不旋转、符号由匹配质心 z 相关决定);匹配对<3 回退 exact-name,再回退 PCA。logit 组成插值、k-NN 簇抽样、逐细胞表达凸组合全部保持不变,只换对齐地标。开关 T2_MATCHED_ALIGN=1/0。单输入/外推退路不变(分层复制 anchor)。搜索范围小而快:r∈{0.25,0.3,0.4}×每侧最小细胞数∈{20,30}。用 vec-score 先跑 proxy seed0:仅当 shape_scale 提升>1 且总分不降才提交,否则回退 0。",
"expected_groups": ["shape_scale", "local_spatial"],
"risks": "1) 与节点11(节点8后续)可能重叠——Engineer 应先确认节点11是否已用同款对齐;2) proxy 只有5个共有类型,表达相关匹配对可能<3 而触发回退,导致机制未激活——必须打印实际匹配对数与是否回退;3) 节点10的 logit 组成插值改变配额,理论上与对齐独立,但若交互异常应先对齐后抽样验证;4) 30分钟时限紧,移植后先用单侧抽样子集确认对齐收敛再全量跑。",
"family_id": "T2HI-01",
"mechanism": "核心机制:以跨类型表达相关贪心匹配对的质心作为 Procrustes 对齐地标(替代 exact-name 共有类型),让两侧在表达相似而非仅同名的区域对齐,改善作用于全部细胞坐标的整体刚体+反射+缩放变换,从而更准确地定位目标形状。",
"vs_constant_shift": "它不是对每个型加一个常位移向量、也不是全局组成重加权;而是改变对齐地标集合,进而改变作用于所有细胞的统一几何变换(旋转/平移/反射/缩放),每个细胞坐标的变化由全局对齐决定、随位置而异,不是统一常数偏移。",
"mechanism_evidence": "Engineer 打印:实际选中匹配对数、各对 Pearson r、是否触发回退;并对比 matched-align 与 exact-name 两配置下的 shape_scale、local_spatial 与总分(A 半、多 seed)。匹配对数≥3 且 shape_scale 较 exact-name 提升>1(超噪声)即证明机制生效。",
"mechanism_off_control": "T2_MATCHED_ALIGN=0 时回退父节点 exact-name 对齐、其余逻辑不变,预期输出与父节点10逐位一致(分数≈63.01、shape_scale≈56.09);若关闭后分数不变,说明机制未运行。",
"sources": []
}
``` |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/researcher.jsonl 24 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/12/researcher.stderr |