总览 · ← 返回运行 20261003-043412-search-t2-heart-interp-g24-D-s1
节点 n21 终选程序?按该运行锁定的规则最终选出的程序;可能是候选节点,也可能由护栏回退到基线。在终选来历上
实现 PLAN 的逐类型自适应簇粒度(n_clusters_c=clip(n_cells_c/TCS,3,25)、k_c 同步自适应)并做 ON/OFF 对照;代理上 4 档 TCS 全部低于父配置(local_spatial 66.92→64.7–66.3),按 PLAN 步骤 5 提交关闭态,输出与父节点 19 逐位一致。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-043412-search-t2-heart-interp-g24-D-s1 |
|---|---|
| 父节点 | n19 |
| 子节点 | n23、n24、n28 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 68.38(+0.0) · proxy 68.38(+0.0) · 3 次复测均分 66.96 |
| 审查 | 通过 检查1(越界读取):未发现问题——所有数据 IO 均通过 src.task2_spatial.view_io 的 load_manifest/read_stage/panel_genes 基于 --data 完成(run.py:226-236),solution/*.py 中无 open()、无绝对路径、无 ..//mnt//home/data/raw、无网络库导入;README.md:5 提到的 data/processed h5ad 仅为文档性对比说明,程序不读取。; 检查2(硬编码目标统计量):未发现问题——类型配额、簇数、伪 bulk 均值均运行时从输入现场计算(cluster_sa… |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 13 分 |
| 程序版本 | 8463a4e50c85d54f4d3c0d9ff8913144f5846bc6 (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git 8463a4e50c:solution/METHOD.md
实现 PLAN 的逐类型自适应簇粒度(n_clusters_c=clip(n_cells_c/TCS,3,25)、k_c 同步自适应)并做 ON/OFF 对照;代理上 4 档 TCS 全部低于父配置(local_spatial 66.92→64.7–66.3),按 PLAN 步骤 5 提交关闭态,输出与父节点 19 逐位一致。
提交配置(family T2HI-04,机制=自适应簇粒度,状态:已实现、对照后关闭)
提交 = 父节点 19 全部路径逐位不动:matched-align Procrustes 对齐(r≥0.3 贪心、保 z)、K=0.62 压实尺度、固定簇抽样(k=25、每型 n_clusters=10、random 播种、jitter 0.02)、类型级表达均值时间插值(同名共有类型、min_mean=2、γ=2)。单输入阶段退路(b is None)同父,不经过簇抽样。
机制实现(PLAN approach 步骤 1–3、6)
cluster_sample.py::cluster_take新增adaptive参数:每类型 n_clusters_c = clip(round(n_cells_c / TARGET_CLUSTER_SIZE), MIN_CLUSTERS=3, MAX_CLUSTERS=25),n_cells_c 为该类型在源阶段的实测细胞数(现场计算,非硬编码);k_c = clip(round(TCS/5), 10, 40)(T2_ADAPTIVE_K=0 可固定 k=25)。run.py环境变量:T2_ADAPTIVE_CLUSTERS(提交默认 0=关)、T2_TARGET_CLUSTER_SIZE、T2_MIN_CLUSTERS、T2_MAX_CLUSTERS、T2_ADAPTIVE_K。新增逻辑全在if adaptive is not None块内。- 机制生效证据(TCS=120 开启时 stderr 打印逐类型诊断):大类型簇确实变小(EXE-Endoderm 8063 细胞 quota1452:父 10 簇×~145 → 自适应 25 簇×59),小类型不再过碎(Forebrain 631:父 10 簇×~12 → 自适应 5 簇×23),k=24。即机制按设计改变了抽样粒度。
机制关闭对照(PLAN mechanism_off_control)
T2_ADAPTIVE_CLUSTERS=0 时 n_clusters 固定 10、k 固定 25,代码路径与父节点 19 完全相同;实测输出与父配置 h5ad 逐位一致(X data/indices/indptr 与 spatial_3D 全部 np.array_equal),即四组分数与父相同(A 半 seed 0:board 67.79,expr 66.13 / cell_state 67.10 / local 66.92 / shape 71.01)。
代理查分结果(T2:heart:val_interp A 半,seed 0,共 5 次)
| 配置 | board | expr | cell_state | local | shape |
|---|---|---|---|---|---|
| 父配置(OFF,=提交) | 67.79 | 66.13 | 67.10 | 66.92 | 71.01 |
| 自适应 TCS=120, k=24 | 67.05 | 65.58 | 66.97 | 64.71 | 70.94 |
| 自适应 TCS=200, k=40 | 66.80 | 65.36 | 66.26 | 66.32 | 69.26 |
| 自适应 TCS=300, k=40 | 66.39 | 65.15 | 66.48 | 66.18 | 67.75 |
| 自适应 TCS=200, k=25 | 66.82 | 65.36 | 66.24 | 66.29 | 69.41 |
结论:PLAN 假设(固定 10 簇使大类型簇过大、邻域被平均化)被证伪——把大类型的大块连续簇打碎(25 簇)对 neighborhood_mmd 的伤害(0.0476→0.0490–0.0525)远大于小类型去碎片化的收益;k 固定与否不改变方向。local_spatial 在所有档位都 ≤ 66.32 < 67.32+1,触发 PLAN 步骤 5 的回退判据(步骤 4 的筛选条件 local_spatial>67.5 亦未满足),且风险 1(收益已饱和)应验。shape_scale 随 TCS 增大而单调下降(大簇提供的空间覆盖更连续),说明父的固定粒度同时是 shape 的最优附近。
验证过 / 没验证
- 验证过:提交配置在 proxy 视图 seed 0 复跑确定且与父逐位一致;vec-check 通过;耗时 ~4s、内存 <0.5GB(远低于 limits 28GB/30min);机制 ON 的诊断输出证明自适应逻辑本身按 PLAN 工作。
- 没验证:TCS<120(更小簇)——TCS=120 已使 local_spatial 掉 2.2,方向单调向下,无必要;final 视图(E8.25+E8.75→E8.5)不可用于本 run 查分,但提交配置与父逐位相同,父在 final 的行为即本节点行为。
知识来源与合规
无新增外部知识;全部统计量(类型细胞数、簇参数、均值)运行时从 view 现场计算。TCS 网格用 vec-score A 半标定(允许通道),提交取回退态故不引入新超参。视图无关:不读 board/路径/绝对时间,时间平移不变。
下一步建议
- local_spatial 的簇粒度维度已双向探明饱和(父固定 10 簇是局部最优;更细碎有害)。剩余空间更可能在 neighborhood 的表达侧(如型内 kNN 局部平滑 vs 全局均值平移)或 shape 与 local 的联合(簇的空间覆盖均衡化)。
调研员的计划
| 名称 | 自适应簇粒度匹配类型规模改善 local_spatial |
|---|---|
| 动机 | local_spatial 67.32 是可改善的最弱组。簇抽样参数(n_clusters=10, k=25)在 node 7 设定,此后经历了 K=0.62 压实(坐标空间缩小 ~38%)和表达平移,但簇粒度从未重新适配。5 个共有类型细胞数差异大(如 V-CM 远多于 NCC),固定 n_clusters=10 导致大类型簇过大(跨越多个空间子区域,邻域结构被平均化)、小类型簇过碎。node 11 尝试过 FPS 播种和全局簇数微调但失败,原因:(1) FPS 是不同机制(播种策略),(2) 全局调参未区分类型,(3) 当时无压实/表达平移,坐标几何不同。本方案按类型自适应调整簇数,是结构变更而非全局调参。 |
| 做法 | 步骤: 1. 在 cluster_sample.py 的 cluster_mix_indices 中,将 n_clusters 从固定值改为按类型计算:n_clusters_c = clip(round(n_cells_c / TARGET_CLUSTER_SIZE), MIN_CLUSTERS, MAX_CLUSTERS)。初值 TARGET_CLUSTER_SIZE=120(使 ~2400 细胞的类型得 20 簇,~300 细胞的得 3 簇),MIN_CLUSTERS=3,MAX_CLUSTERS=25。搜索范围 TARGET_CLUSTER_SIZE ∈ [60, 80, 100, 120, 160, 200]。 2. k(k-NN 区域生长的邻居数)同步自适应:k_c = max(10, min(40, round(TARGET_CLUSTER_SIZE / 5))),确保簇生长步长与目标簇大小匹配。 3. 其余路径(matched-align、K=0.62、表达平移 γ=2 min_mean=2)逐位不动。 4. 快速筛选:用 vec-score 查 A 半,先跑 TARGET_CLUSTER_SIZE=120 确认 local_spatial > 67.5 且其他三组不降超 0.5;再网格搜索 [80,100,120,160,200],取 local_spatial 最高且 cell_state ≥ 67.5、expression_change ≥ 66.0 的配置。 5. 若所有配置的 local_spatial 均 ≤ 67.32+1(噪声内),提交父配置不变。 6. 单输入阶段退路:b is None 时不走簇抽样插值路径,行为同父。 |
| 风险 | 1) 簇粒度对 local_spatial 的边际收益可能已饱和(node 7 的 +10.6 是主要收益,后续微调空间有限)——Engineer 应在第一次查分后若 local_spatial ≤ 67.5 即停止网格搜索,节省查分次数。2) 增大簇数可能使小类型抽样过于碎片化,反而破坏邻域——监控 cell_state 是否下降。3) 压实后坐标空间缩小,k=25 的邻域图已更紧凑,可能当前 k 已接近最优——若 k 自适应无额外收益,固定 k=25 只调 n_clusters。4) 提升可能 <1 分(噪声内),需至少查 2 次确认方向。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 5a4242ec5a。改动的文件:solution/METHOD.md +24 −28、solution/cluster_sample.py +28 −8、solution/run.py +28 −1
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex 4ba71cd..0a455a5 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,44 +1,40 @@-在父 mix+簇抽样+压实尺度上叠加类型级表达均值时间插值:共有类型细胞加逐基因偏移 γ·t·Δ(a 侧)/−γ·(1−t)·Δ(b 侧),只校正高表达基因(mean>2),γ=2,坐标不动。+实现 PLAN 的逐类型自适应簇粒度(n_clusters_c=clip(n_cells_c/TCS,3,25)、k_c 同步自适应)并做 ON/OFF 对照;代理上 4 档 TCS 全部低于父配置(local_spatial 66.92→64.7–66.3),按 PLAN 步骤 5 提交关闭态,输出与父节点 19 逐位一致。 -## 方法(提交配置,family T2HI-01,机制=表达空间时间插值)+## 提交配置(family T2HI-04,机制=自适应簇粒度,状态:已实现、对照后关闭) -- 父节点 13 的全部路径(matched-align Procrustes、k-NN 簇抽样、K=0.62 压实尺度)不变,坐标与父逐位相同(本地 `np.array_equal` 验证,shape_scale/local 的空间部分不受影响)。-- 新机制(`apply_type_mean_shift`,run.py):对两个括号阶段的每个**同名共有类型** c(代理上 5 个:NCC/Peri/V-CM/aPHM/pPHM),计算 pseudobulk 均值 mean_a[·,c]、mean_b[·,c],Δ=mean_b−mean_a;来自 a 的 c 型细胞加 +γ·t·Δ,来自 b 的加 −γ·(1−t)·Δ(t 与坐标插值同一时间分数)。同类型所有细胞加同一逐基因向量 → 型内方差/共变结构逐位保留,只平移型均值。截断到 [0,∞)(输入 log1p 非负)。-- 提交参数:`T2_EXPR_MIN_MEAN=2.0`(仅对 max(mean_a,mean_b)>2 的高表达基因施加偏移;低表达基因的 Δ 主要是技术噪声,全基因偏移会伤 cell_state)、`T2_EXPR_GAMMA=2.0`(γ=1 是把型均值放到线性插值点;代理上 γ 增大单调改善到 2–3 后平台)。-- 单输入阶段退路(b is None)不经过该代码,行为同父。+提交 = 父节点 19 全部路径逐位不动:matched-align Procrustes 对齐(r≥0.3 贪心、保 z)、K=0.62 压实尺度、固定簇抽样(k=25、每型 n_clusters=10、random 播种、jitter 0.02)、类型级表达均值时间插值(同名共有类型、min_mean=2、γ=2)。单输入阶段退路(b is None)同父,不经过簇抽样。 -## 机制生效证据(PLAN mechanism_evidence)+## 机制实现(PLAN approach 步骤 1–3、6) -- 实际改变的细胞:默认配置下 3553/17616 行表达被平移(其余行属于单侧独有类型或偏移被 [0,∞) 截断归零),全部来自 5 个共有类型;坐标 0 行改变。-- 型均值向 (1−t)·mean_a+t·mean_b 移动(γ=2 时越过插值点 2 倍于到目标的距离,即外插),型内逐基因偏移为常数向量 → 型内 std 结构不变(同一行加同一向量,方差不变,仅截断处微调)。-- 四组变化(A 半,seed 0,对照=同代码 T2_EXPR_SHIFT=0):expression_change 64.60→66.13(+1.53),cell_state 66.80→67.10(+0.30),local_spatial 65.02→66.92(+1.90,表达参与的 neighborhood_mmd/variogram 变好),shape_scale 71.01→71.01(逐位不变)。+- `cluster_sample.py::cluster_take` 新增 `adaptive` 参数:每类型 n_clusters_c = clip(round(n_cells_c / TARGET_CLUSTER_SIZE), MIN_CLUSTERS=3, MAX_CLUSTERS=25),n_cells_c 为该类型在源阶段的实测细胞数(现场计算,非硬编码);k_c = clip(round(TCS/5), 10, 40)(T2_ADAPTIVE_K=0 可固定 k=25)。+- `run.py` 环境变量:T2_ADAPTIVE_CLUSTERS(提交默认 0=关)、T2_TARGET_CLUSTER_SIZE、T2_MIN_CLUSTERS、T2_MAX_CLUSTERS、T2_ADAPTIVE_K。新增逻辑全在 `if adaptive is not None` 块内。+- 机制生效证据(TCS=120 开启时 stderr 打印逐类型诊断):大类型簇确实变小(EXE-Endoderm 8063 细胞 quota1452:父 10 簇×~145 → 自适应 25 簇×59),小类型不再过碎(Forebrain 631:父 10 簇×~12 → 自适应 5 簇×23),k=24。即机制按设计改变了抽样粒度。 ## 机制关闭对照(PLAN mechanism_off_control) -`T2_EXPR_SHIFT=0` 跳过整个校正块,代码路径与父节点 13 完全相同(新增代码全部在 if 块内);实测输出与父配置逐位一致,A 半查分 66.8572(= 节点 13 报告的同配置 66.86,四组 64.6/66.8/71.01/65.02 完全一致)。开关默认开。+T2_ADAPTIVE_CLUSTERS=0 时 n_clusters 固定 10、k 固定 25,代码路径与父节点 19 完全相同;实测输出与父配置 h5ad 逐位一致(X data/indices/indptr 与 spatial_3D 全部 np.array_equal),即四组分数与父相同(A 半 seed 0:board 67.79,expr 66.13 / cell_state 67.10 / local 66.92 / shape 71.01)。 -## 代理查分网格(T2:heart:val_interp A 半,共 12 次)+## 代理查分结果(T2:heart:val_interp A 半,seed 0,共 5 次) | 配置 | board | expr | cell_state | local | shape | |---|---:|---:|---:|---:|---:|-| OFF(=父 13) | 66.86 | 64.60 | 66.80 | 65.02 | 71.01 |-| name 全基因 γ=1 | 66.96 | 65.59 | 65.19 | 66.04 | 71.01 |-| name 全基因 γ=0.5 | 66.88 | 65.14 | 65.80 | 65.56 | 71.01 |-| name center γ=1 | 66.90 | 65.22 | 65.53 | 65.86 | 71.01 |-| matched-pair γ=1 / 0.5 / center | 64.02 / 65.00 / 64.05 | 68.74/66.34/67.62 | 54.14/58.89/55.21 | — | 71.01 |-| name min_mean=0.5 / 1 / 3 / 4 | 67.07 / 67.16 / 67.30 / 67.21 | — | — | — | 71.01 |-| name min_mean=1+center | 67.12 | 65.20 | 66.31 | 65.98 | 71.01 |-| **提交 mm2 γ=2** | **67.79** | 66.13 | 67.10 | 66.92 | 71.01 |-| mm2 γ=1.5 / γ=3 | 67.60 / 67.84 | 65.88/66.06 | 66.93/67.33 | 66.58/66.97 | 71.01 |--结论:全基因均值平移伤 cell_state(跨阶段全局表达水平差有技术成分);限制到高表达基因后 cell_state 不再受损且 expression/local 均升。matched-pair(跨类型相关匹配对做偏移)大幅提 expression 但摧毁 cell_state(错配类型把细胞推离其身份),弃用。γ=2 与 γ=3 差 0.05(噪声内),取更保守的 γ=2。+| 父配置(OFF,=提交) | **67.79** | 66.13 | 67.10 | **66.92** | 71.01 |+| 自适应 TCS=120, k=24 | 67.05 | 65.58 | 66.97 | 64.71 | 70.94 |+| 自适应 TCS=200, k=40 | 66.80 | 65.36 | 66.26 | 66.32 | 69.26 |+| 自适应 TCS=300, k=40 | 66.39 | 65.15 | 66.48 | 66.18 | 67.75 |+| 自适应 TCS=200, k=25 | 66.82 | 65.36 | 66.24 | 66.29 | 69.41 |++结论:PLAN 假设(固定 10 簇使大类型簇过大、邻域被平均化)被证伪——把大类型的大块连续簇打碎(25 簇)对 neighborhood_mmd 的伤害(0.0476→0.0490–0.0525)远大于小类型去碎片化的收益;k 固定与否不改变方向。local_spatial 在所有档位都 ≤ 66.32 < 67.32+1,触发 PLAN 步骤 5 的回退判据(步骤 4 的筛选条件 local_spatial>67.5 亦未满足),且风险 1(收益已饱和)应验。shape_scale 随 TCS 增大而单调下降(大簇提供的空间覆盖更连续),说明父的固定粒度同时是 shape 的最优附近。++## 验证过 / 没验证++- 验证过:提交配置在 proxy 视图 seed 0 复跑确定且与父逐位一致;vec-check 通过;耗时 ~4s、内存 <0.5GB(远低于 limits 28GB/30min);机制 ON 的诊断输出证明自适应逻辑本身按 PLAN 工作。+- 没验证:TCS<120(更小簇)——TCS=120 已使 local_spatial 掉 2.2,方向单调向下,无必要;final 视图(E8.25+E8.75→E8.5)不可用于本 run 查分,但提交配置与父逐位相同,父在 final 的行为即本节点行为。 ## 知识来源与合规 -- 只用了视图内两个输入阶段(E8.25_late、E9.5,均为 T2 心脏允许的已发布输入)的现场 pseudobulk 统计和时间差 t;无保留阶段/禁窗/保留基因型信息,无外部数据,无硬编码统计量(均值、类型集合、基因过滤全部运行时从 view 计算)。-- γ、min_mean 为通过 vec-score 通道标定的常数超参(与父 K=0.62 同通道),只依赖 t 与表达分布,视图时间平移不变;同 seed 输出确定(seed 0 复跑逐位一致)。+无新增外部知识;全部统计量(类型细胞数、簇参数、均值)运行时从 view 现场计算。TCS 网格用 vec-score A 半标定(允许通道),提交取回退态故不引入新超参。视图无关:不读 board/路径/绝对时间,时间平移不变。 -## 风险+## 下一步建议 -- γ=2 是越过线性插值点的外插,其最优值在代理(E8.25+E9.5→E8.75)上标定;final 括号/目标不同,若真实轨迹接近线性,γ=2 会过冲(但 min_mean=2 限制了作用基因数,过冲幅度有限,且 γ∈[1.5,3] 平台宽)。-- 提升 +0.93(66.86→67.79)接近 T2 噪声(~1 分);B 半未必完全复现,但四组中三组同向改善、shape 逐位不变,方向性较可信。+- local_spatial 的簇粒度维度已双向探明饱和(父固定 10 簇是局部最优;更细碎有害)。剩余空间更可能在 neighborhood 的表达侧(如型内 kNN 局部平滑 vs 全局均值平移)或 shape 与 local 的联合(簇的空间覆盖均衡化)。diff --git a/solution/cluster_sample.py b/solution/cluster_sample.pyindex 1080127..da00b48 100644--- a/solution/cluster_sample.py+++ b/solution/cluster_sample.py@@ -186,7 +186,8 @@ def grow_clusters(coords: np.ndarray, q: int, rng: np.random.Generator, def cluster_take(labels: np.ndarray, coords: np.ndarray, n: int, rng: np.random.Generator, k: int = 15, n_clusters: int = 5, cluster_frac: float = 1.0, seed_mode: str = "random",- jitter_frac: float = 0.02, max_layers: int = 4) -> np.ndarray:+ jitter_frac: float = 0.02, max_layers: int = 4,+ adaptive: dict | None = None, diag: dict | None = None) -> np.ndarray: labels = np.asarray(labels).astype(str) n = int(min(max(n, 1), len(labels))) if n >= len(labels):@@ -197,8 +198,20 @@ def cluster_take(labels: np.ndarray, coords: np.ndarray, n: int, if kk <= 0: continue idx = np.flatnonzero(labels == lab)+ k_use, nc_use = k, n_clusters+ if adaptive is not None:+ tcs = float(adaptive.get("target_cluster_size", 120))+ nc_use = int(np.clip(round(idx.size / max(tcs, 1.0)),+ int(adaptive.get("min_clusters", 3)),+ int(adaptive.get("max_clusters", 25))))+ if adaptive.get("adaptive_k", True):+ k_use = int(max(10, min(40, round(tcs / 5.0))))+ if diag is not None:+ diag[str(lab)] = {"n_cells": int(idx.size), "quota": int(kk),+ "n_clusters": nc_use, "k": k_use,+ "cluster_target": int(max(1, np.ceil(kk / max(1, nc_use))))} q_cl = int(np.clip(round(int(kk) * float(cluster_frac)), 0, int(kk)))- sel = grow_clusters(coords[idx], q_cl, rng, k=k, n_clusters=n_clusters,+ sel = grow_clusters(coords[idx], q_cl, rng, k=k_use, n_clusters=nc_use, seed_mode=seed_mode, jitter_frac=jitter_frac, max_layers=max_layers) chosen = idx[sel]@@ -218,16 +231,23 @@ def cluster_take(labels: np.ndarray, coords: np.ndarray, n: int, def cluster_mix_indices(labels_a, labels_b, coords_a, coords_b, t: float, n: int, rng: np.random.Generator, k: int = 15, n_clusters: int = 5, cluster_frac: float = 1.0, seed_mode: str = "random",- jitter_frac: float = 0.02, max_layers: int = 4):+ jitter_frac: float = 0.02, max_layers: int = 4,+ adaptive: dict | None = None, diag: dict | None = None): """Drop-in replacement for sample.mix_indices using cluster sampling.""" n_b = int(round(float(t) * n)) n_b = int(np.clip(n_b, 0, n)) n_a = n - n_b kw = dict(k=k, n_clusters=n_clusters, cluster_frac=cluster_frac,- seed_mode=seed_mode, jitter_frac=jitter_frac, max_layers=max_layers)+ seed_mode=seed_mode, jitter_frac=jitter_frac, max_layers=max_layers,+ adaptive=adaptive, diag=diag)+ kw_a = dict(kw)+ kw_b = dict(kw)+ if diag is not None:+ kw_a["diag"] = diag.setdefault("a", {})+ kw_b["diag"] = diag.setdefault("b", {}) if n_a == 0:- return np.array([], dtype=int), cluster_take(labels_b, coords_b, n_b, rng, **kw)+ return np.array([], dtype=int), cluster_take(labels_b, coords_b, n_b, rng, **kw_b) if n_b == 0:- return cluster_take(labels_a, coords_a, n_a, rng, **kw), np.array([], dtype=int)- return (cluster_take(labels_a, coords_a, n_a, rng, **kw),- cluster_take(labels_b, coords_b, n_b, rng, **kw))+ return cluster_take(labels_a, coords_a, n_a, rng, **kw_a), np.array([], dtype=int)+ return (cluster_take(labels_a, coords_a, n_a, rng, **kw_a),+ cluster_take(labels_b, coords_b, n_b, rng, **kw_b))diff --git a/solution/run.py b/solution/run.pyindex 9b57de0..3d2e4c2 100644--- a/solution/run.py+++ b/solution/run.py@@ -175,6 +175,27 @@ def axis_std_norm(coords, ca, cb, t): return coords * (st / np.maximum(sc, 1e-8)) +# --- node 21: adaptive per-type cluster granularity (PLAN T2HI-04 improve) ---+# Fixed n_clusters=10 makes cluster size scale with type size: large types get+# huge clusters spanning several spatial sub-regions (neighbourhood structure+# averaged away), small types get over-fragmented ones. Adaptive mode sets+# n_clusters_c = clip(round(n_cells_c / TARGET_CLUSTER_SIZE), MIN, MAX) so the+# spatial scale of a cluster is consistent across types, and k_c =+# clip(round(TARGET_CLUSTER_SIZE/5), 10, 40) matches region-growing step size+# to target cluster size. Measured on the proxy (A half, seed 0): parent 67.79+# (local_spatial 66.92); TCS=120 67.05 (local_spatial 64.71); TCS=200 66.80;+# TCS=300 66.39; TCS=200 with fixed k=25 66.82 -- the mechanism is harmful at+# every granularity tested (fragmenting large types' big contiguous clusters+# costs more neighbourhood structure than it saves on small types), so per+# PLAN step 5 it ships OFF. T2_ADAPTIVE_CLUSTERS=1 re-enables it.+ADAPTIVE_CLUSTERS = os.environ.get("T2_ADAPTIVE_CLUSTERS", "0") != "0"+ADAPTIVE_PARAMS = {+ "target_cluster_size": float(os.environ.get("T2_TARGET_CLUSTER_SIZE", "120")),+ "min_clusters": int(os.environ.get("T2_MIN_CLUSTERS", "3")),+ "max_clusters": int(os.environ.get("T2_MAX_CLUSTERS", "25")),+ "adaptive_k": os.environ.get("T2_ADAPTIVE_K", "1") != "0",+}+ if os.environ.get("T2_CLUSTER_SAMPLING") == "0": CLUSTER_SAMPLING = False if os.environ.get("T2_MATCHED_ALIGN") == "0":@@ -239,8 +260,11 @@ def main() -> None: ca = scale_to_rms(aligned_a, target_rms) cb = scale_to_rms(aligned_b, target_rms) n = _limits(params, stage_a.n, stage_b.n, t, "interp")+ cluster_diag = {} if ADAPTIVE_CLUSTERS else None ia, ib = cluster_mix_indices(- stage_a.labels, stage_b.labels, ca, cb, t, n, rng, **CLUSTER_PARAMS)+ stage_a.labels, stage_b.labels, ca, cb, t, n, rng, **CLUSTER_PARAMS,+ adaptive=ADAPTIVE_PARAMS if ADAPTIVE_CLUSTERS else None,+ diag=cluster_diag) x_parts, p_parts = [], [] if ia.size: x_parts.append(as_dense(stage_a.X, ia))@@ -261,6 +285,9 @@ def main() -> None: if AXIS_ON: coords = axis_std_norm(coords, ca, cb, t) coords = np.asarray(coords, dtype=np.float32)+ if cluster_diag is not None:+ print(json.dumps({"adaptive_clusters": ADAPTIVE_PARAMS, "diag": cluster_diag},+ default=float), file=sys.stderr) info.update(t=t, n=int(expr.shape[0]), rms_a=rms_a, rms_b=rms_b, density_ratio=dratio, contract_factor=cf, target_rms=target_rms, out_rms=rms_radius(coords), n_from_a=int(ia.size), n_from_b=int(ib.size),
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| k016 | Degenerate-solution checks for population predictions | notes/handover/02_知识学习路线.md |
| k024 | World-model evaluation dimensions for state-transition predictors | notes/competition/07_biomedical_world_models.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 在 cluster_sample.py/run.py 实现了逐类型自适应簇粒度(n_clusters_c=clip(n_cells_c/TCS,3,25)、k_c 同步自适应,带 T2_ADAPTIVE_CLUSTERS 开关与逐类型诊断输出),但代理查分显示所有档位均劣于父配置,按 PLAN 步骤 5 以关闭态提交,输出与父节点 19 逐位一致,故榜分与四组分数 +0.00。 |
|---|---|
| 各组分数的变化 | cell_state:噪声内(实为逐位不变):67.95 → 67.95,+0.00 expression_change:噪声内(实为逐位不变):66.67 → 66.67,+0.00 local_spatial:噪声内(实为逐位不变):67.32 → 67.32,+0.00;代理上开启机制时 local_spatial 反而从 66.92 降到 64.71–66.32(TCS=120/200/300) shape_scale:噪声内(实为逐位不变):71.58 → 71.58,+0.00 |
| family_id | T2HI-04 |
| 假设是否成立 | 否 |
| 经验 |
|
| mechanism_active | 否 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 4 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 3 次:bash 2、write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 11,879 · 输出 1,032 · 思考 416 |
| 任务(第一行) | 分析节点 n21 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/analyst.jsonl 9 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 28 |
| 工具调用 | 共 31 次:bash 21、edit 7、read 2、write 1 |
| 用时 | 9 分 |
| token 数 | 输入 58,562 · 输出 8,899 · 思考 4,118 |
| 任务(第一行) | 按计划修改程序:自适应簇粒度匹配类型规模改善 local_spatial |
| 最后的回答(摘录) | 做了什么:按 PLAN 实现逐类型自适应簇粒度(n_clusters_c=clip(n_cells_c/TCS,3,25)、k_c 自适应),带 T2_ADAPTIVE_CLUSTERS 开关和逐类型诊断输出;ON/OFF 对照验证关闭态与父节点 19 逐位一致。 查分结果(5 次,余 15):父配置 67.79(local_spatial 66.92);自适应 TCS=120/200/300/200+k25 → 67.05/66.80/66.39/66.82,local_spatial 全部更低(64.7–66.3),PLAN 假设被证伪——打碎大类型的大块连续簇对 neighborhood_mmd 的伤害大于小类型去碎片化的收益。按 PLAN 步骤 5 回退,提交=父配置(默认关闭),vec-check 通过、seed 确定。 下一步最值得试:local_spatial 的簇粒度维度已饱和,建议转向表达侧邻域改善(型内 kNN 局部平滑替代/叠加全局均值平移)或簇的空间覆盖均衡化(shape 与 local 联合)。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/engineer.jsonl 169 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 4 分 |
| token 数 | 输入 12,813 · 输出 1,292 · 思考 7,604 |
| 任务(第一行) | 为节点 n21(改进,父节点 n19)写改进计划 PLAN.json |
| 最后的回答(摘录) | 5)在 node 7 设定,此后经历了 K=0.62 压实(坐标空间缩小 ~38%)和表达平移,但簇粒度从未重新适配。5 个共有类型细胞数差异大(如 V-CM 远多于 NCC),固定 n_clusters=10 导致大类型簇过大(跨越多个空间子区域,邻域结构被平均化)、小类型簇过碎。node 11 尝试过 FPS 播种和全局簇数微调但失败,原因:(1) FPS 是不同机制(播种策略),(2) 全局调参未区分类型,(3) 当时无压实/表达平移,坐标几何不同。本方案按类型自适应调整簇数,是结构变更而非全局调参。", "approach": "步骤:\n1. 在 cluster_sample.py 的 cluster_mix_indices 中,将 n_clusters 从固定值改为按类型计算:n_clusters_c = clip(round(n_cells_c / TARGET_CLUSTER_SIZE), MIN_CLUSTERS, MAX_CLUSTERS)。初值 TARGET_CLUSTER_SIZE=120(使 ~2400 细胞的类型得 20 簇,~300 细胞的得 3 簇),MIN_CLUSTERS=3,MAX_CLUSTERS=25。搜索范围 TARGET_CLUSTER_SIZE ∈ [60, 80, 100, 120, 160, 200]。\n2. k(k-NN 区域生长的邻居数)同步自适应:k_c = max(10, min(40, round(TARGET_CLUSTER_SIZE / 5))),确保簇生长步长与目标簇大小匹配。\n3. 其余路径(matched-align、K=0.62、表达平移 γ=2 min_mean=2)逐位不动。\n4. 快速筛选:用 vec-score 查 A 半,先跑 TARGET_CLUSTER_SIZE=120 确认 local_spatial > 67.5 且其他三组不降超 0.5;再网格搜索 [80,100,120,160,200],取 local_spatial 最高且 cell_state ≥ 67.5、expression_change ≥ 66.0 的配置。\n5. 若所有配置的 local_spatial 均 ≤ 67.32+1(噪声内),提交父配置不变。\n6. 单输入阶段退路:b is None 时不走簇抽样插值路径,行为同父。", "expected_groups": ["local_spatial"], "risks": "1) 簇粒度对 local_spatial 的边际收益可能已饱和(node 7 的 +10.6 是主要收益,后续微调空间有限)——Engineer 应在第一次查分后若 local_spatial ≤ 67.5 即停止网格搜索,节省查分次数。2) 增大簇数可能使小类型抽样过于碎片化,反而破坏邻域——监控 cell_state 是否下降。3) 压实后坐标空间缩小,k=25 的邻域图已更紧凑,可能当前 k 已接近最优——若 k 自适应无额外收益,固定 k=25 只调 n_clusters。4) 提升可能 <1 分(噪声内),需至少查 2 次确认方向。", "family_id": "T2HI-04", "mechanism": "将固定 n_clusters=10 改为按类型细胞数自适应(n_cells_c / TARGET_CLUSTER_SIZE),使每个簇的空间尺度跨类型一致,大类型获得更多更小的簇以保留局部邻域结构,小类型避免过度碎片化。", "vs_constant_shift": "这不是对输出做全局平移或重加权;它改变的是抽样算法的空间粒度——决定从源阶段提取哪些细胞、以多大的空间连续单元提取,直接影响输出点云的局部邻域拓扑。", "mechanism_evidence": "1) 打印每类型的实际 n_clusters 和平均簇大小(细胞数),验证大类型簇变小、小类型簇不过碎。2) 对比父配置与自适应配置的 local_spatial 子指标(neighborhood_mmd、variogram),确认改善来自邻域结构。3) 四组分变化:预期 local_spatial +1~2,cell_state/expression_change 变化 <0.5,shape_scale 不变(坐标未全局缩放)。", "mechanism_off_control": "环境变量 T2_ADAPTIVE_CLUSTERS=0 时,n_clusters 回退到固定值 10、k 固定 25,代码路径与父节点 19 完全相同(新增逻辑全在 if 块内),预期输出逐位一致、四组分数不变。", "sources": []} ``` |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/researcher.jsonl 5 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/researcher.stderr |
审查员
| 角色 | 审查员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 9 |
| 工具调用 | 共 14 次:read 9、bash 4、write 1 |
| 用时 | 3 分 |
| token 数 | 输入 31,736 · 输出 1,869 · 思考 7,515 |
| 任务(第一行) | 审查节点 n21 的程序是否越界读取、写死目标、钻评分器漏洞 |
| 最后的回答(摘录) | REVIEW.json written |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/reviewer.jsonl 145 KB /home/spark-longxinyang/vec/runs/formal/20261003-043412-search-t2-heart-interp-g24-D-s1/nodes/21/reviewer.stderr |