总览 · ← 返回运行 20261003-070222-search-t2-heart-extrap-g24-D
节点 n12 在终选来历上
去除伪批量位移(α=0)+ 确定性 PCA 坐标规范化 + 各向同性缩放到 0.946×max_k RMS(input_k),shape_scale 66.60
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261003-070222-search-t2-heart-extrap-g24-D |
|---|---|
| 父节点 | n10 |
| 子节点 | n13 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 54.15(+0.2) · proxy 54.15(+0.2) · 3 次复测均分 54.00 |
| 审查 | 通过 1 越界读取:未发现问题。run.py 仅通过框架 view_io(load_manifest/panel_genes/read_stage/extrap_step/inputs_by_time)读视图内输入阶段,无绝对路径、'..'、external/、prior/、目标阶段文件或网络访问(run.py:41-51, 100-116)。; 2 硬编码目标统计量:未发现问题。唯一常量 FACTOR=0.946(run.py:53)是作用在由输入现场计算的 max_k RMS(input_k) 上的阻尼标量(run.py:116-118),代码中无细胞比例、表达值、细胞数、基因列表等目标统计常… |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 8 分 |
| 程序版本 | 7cfce055ec45f4dcd9c74c5592856a24fffedac0 (programs.git) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git 7cfce055ec:solution/METHOD.md
去除伪批量位移(α=0)+ 确定性 PCA 坐标规范化 + 各向同性缩放到 0.946×max_k RMS(input_k),shape_scale 66.60
榜 / 视图
T2:heart:val_extrap,proxy = E8.25_late + E8.75 → E9.5(步长比 1.5)。纯 CPU,约 4.6 s,峰值内存 <1 GB(EXECUTION.json: gpu=false)。
方法(PLAN family T2HX-06)
- 表达:完全照抄末阶段(α=0)。父节点 10 保留的「表达匹配型伪批量位移」被删除,不再只是调小。依据:树内数据一致显示该机制在 α∈{0.02, 0.05} 上都不产生超出噪声的收益,且方向偏负(cell_state 49.04 / 49.40,α=0 时为 50.00;expression_change 49.87 / 50.00 vs 50.00)。删除后 cell_state、expression_change 都回到 50.00。
- 坐标:确定性 PCA 规范化(本节点相对父节点 10 的结构修复)。末阶段点云 → 去质心 → 协方差
eigh→ 特征值降序(stable排序,并列按列序)→ 若 det<0 翻转最后一列(固定手性)→ 每根轴按第三中心矩为正定向(退化时依次用第一矩、特征向量最大绝对值分量的符号),得到与采集姿态无关、且对同一朵点云唯一的规范帧。 - 坐标:各向同性缩放到
FACTOR × max_k RMS(input_k),FACTOR=0.946。理由(生物学/几何,不依赖保留阶段):一个样本测到的空间 RMS 半径取决于它覆盖了多少结构(视野、保留切片数),只能低估真实尺寸,因此不是随时间单调的生物量——本视图上它从 354(较早)降到 217(较晚),沿趋势外推必错;胚胎随时间长大,更晚的目标阶段其尺度至少是各输入观测到的最大值,再乘一个阻尼因子。只做刚体旋转 + 全局缩放,细胞间相对几何、z 切片结构都不动。 - 细胞数 / 组成:不动,
n = clip(last.n, min_cells, max_cells),超上限时用src.task2_spatial.sample.take按细胞类型分层无放回抽样(proxy 上 53742 → 24826)。 - 单输入退路:无 prev 时同样走规范化 + 缩放(此时 max RMS 就是该点云自身的 RMS,缩放为恒等),表达照抄,分层抽到 max_cells,不崩。
FACTOR 是唯一被扫的标量;VEC_FACTOR 环境变量只为扫参存在,提交时不设置,走代码里的 0.946。
机制生效的证据
- 实际改变了什么:只改坐标(旋转 + 全局缩放),表达矩阵与细胞集合完全等同 copy_last。proxy seed 0 实测:
rms_inputs=[354.13, 216.88]→rms_out=335.00,scale_log_ratio=-0.0027(≈0),PCAeig_frac=[0.398, 0.350, 0.252],axis_signs=[+1,-1,+1](确实翻了轴,不是恒等变换)。 - 四组分变化(父节点 10 → 本节点,同一 proxy):cell_state 49.40 → 50.00;expression_change 50.00 → 50.00;local_spatial 50.07 → 50.00(噪声内);shape_scale 66.27 → 66.60。榜分 53.94 → 54.15。
- 机制关闭对照(
VEC_MECH_OFF=1,跳过 PCA 规范化与缩放,输出末阶段原始坐标):shape_scale 66.60 → 50.00,scale_log_ratio→ −0.4375,其余三组不变(表达未动),榜分 50.00(= copy_last 地板)。即 +4.15 榜分全部来自该坐标机制,与 PLAN 的mechanism_off_control预期一致。
验证过的
- FACTOR 细扫(9 次
vec-score,全部 proxy seed 0):0.90→53.32 / 0.92→53.68 / 0.94→54.06 / 0.943→54.12 / 0.946→54.15 / 0.948→54.11 / 0.95→54.07 / 0.96→53.88 / 0.98→53.53。shape_scale 分别为 63.28 / 64.71 / 66.25 / 66.49 / 66.60 / 66.44 / 66.28 / 65.51 / 64.10。峰值在 0.946(scale_log_ratio最接近 0),两侧单调下降,与父节点 ANALYSIS 的「factor 是最强单一杠杆」一致。 - 种子稳定性:seed 0 / 1 / 2 榜分均为 54.1494(分层抽样对指标影响可忽略),rank3 期望 ≈54.15。
- 确定性:同 seed 重跑,
.X(data/indices/indptr)、obsm/spatial_3D、obs、var逐字节相同。 - 视图无关:在伪装视图(随机路径、manifest 键序打乱、所有阶段时间统一 +1 天)上重跑 seed 0,
.X与spatial_3D与真实视图完全相同。代码不读board/mode/source/dataset,不依赖绝对发育时间(只用extrap_step的步长比,且仅用于日志)。 - 格式:
vec-check --data <proxy view>→{"status": "ok", "errors": []}。
没验证 / 风险
- 0.946 是在 proxy 括号(E8.25+E8.75)上扫出的阻尼因子;真实外推括号是 E8.75+E9.5 → E10.5,输入 RMS 组合不同,
max_k RMS(input_k) × 0.946未必同样贴近真值。方法卡记录的 final 上 E8.75→E9.5 只剩 5 个同名类型——本方法不用类型名也不用 prev 的表达(表达照抄末阶段),所以类型名对不上不影响;PCA 也不依赖类型名。 - PCA 规范化把点云旋到一个由自身三阶矩决定的帧。若评分器的对齐是各向同性的(Procrustes/Kabsch),旋转本身是中性的,收益应主要来自尺度;实测加 PCA 比不加(父节点 10 的 66.27)高 0.33,落在 T2 噪声(约 1 分)内,不能确认 PCA 规范化本身有独立收益,只能说没有害处。
- shape_scale 已被
d2_shape=0.04891(形状项,跨所有 factor 恒定)钉住,只靠全局缩放无法再涨;要突破需要改变点云形状,而这需要关于保留阶段形态的信息,属禁用范围。 - cell_state / expression_change / local_spatial 三组恒为 50.00(= copy_last 水平)。本节点没有提出任何能真正预测 E9.5→E10.5 表达变化的机制,这是已知的最大缺口。
知识来源
只用通用机制知识:胚胎随发育时间增大(尺度非递减),以及「样本测到的空间范围受视野/切片保留限制、只能低估真实结构尺寸」这一测量学事实。未使用任何保留阶段(E10.5、E12.5,禁窗 9.5<E≤13.5)或保留基因型的实测数据、比例、标记基因或形态信息;external/(Qiu E8.75)与 prior/ 均未被 run.py 读取。
调研员的计划
| 名称 | 去除伪批量位移 + PCA规范化 + 缩放因子细扫 |
|---|---|
| 动机 | 父节点10的cell_state 49.40是四组最弱,ANALYSIS确认α=0.02位移在噪声内且方向偏负(node 6证明α=0恢复50.00)。shape_scale 66.27是唯一增益来源但缺PCA规范化(node 6加后66.60)。ANALYSIS明确建议:细扫factor 0.90/0.925/0.975,去掉位移。当前最佳node 6=54.15(rank3 54.00),本节点从node 10代码出发,结构修复后有望追平或超过。 |
| 做法 | 1) 删除matched_delta调用,α硬编码为0(表达照抄末阶段);2) 加入确定性PCA规范化(质心归零→PCA旋转→det=+1→第三矩定向,与node 6相同);3) 缩放因子f细扫:在{0.90, 0.92, 0.94, 0.946, 0.95, 0.96}上各跑一次vec-score(6次),选shape_scale最高的f;4) 若最优f与0.946差异<0.01则取0.946(与参考335一致)。总查分预算≤10次。单输入退路:无prev时直接copy_last+PCA(缩放为恒等)。用VEC_MECH_OFF=1跳过PCA和缩放作为对照。 |
| 风险 | 1) factor最优点可能不在扫描范围内(0.90以下或0.96以上),但已有数据显示>1.0和<0.9方向均变差,风险低;2) PCA规范化实现与node 6不一致导致手性翻转——Engineer应对照node 6代码;3) 30分钟内6次查分+代码修改时间紧,建议先改代码跑一次确认无报错再扫因子。尽早发现:第一次vec-score若shape_scale<62,说明PCA实现有误。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 519bca4055。改动的文件:solution/METHOD.md +25 −41、solution/README.md +9 −6、solution/run.py +87 −119
diff --git a/solution/METHOD.md b/solution/METHOD.mdindex c28b540..6c9f001 100644--- a/solution/METHOD.md+++ b/solution/METHOD.md@@ -1,56 +1,40 @@-tune of #4: α from 0.05 to 0.02 (recover expression_change and cell_state gains seen in rounds 0-1), and target RMS scale factor from 1.0 to 0.95 (round 1 showed factor>1.0 hurts shape_scale; try slightly below max to approach the reference scale of 335).--表达匹配型伪批量位移(α=0.05,覆盖全部细胞)+ 坐标尺度取各输入阶段观测到的最大 RMS 半径(形状不变,仅整体缩放)。+去除伪批量位移(α=0)+ 确定性 PCA 坐标规范化 + 各向同性缩放到 0.946×max_k RMS(input_k),shape_scale 66.60 ## 榜 / 视图 -`T2:heart:val_extrap`,proxy = E8.25_late + E8.75 → E9.5(步长比 1.5)。--## 方法--1. **配对(PLAN family T2HX-01)**:prev 阶段每个细胞类型的表达质心(余弦,只用两阶段中至少一方 ≥1% 细胞检出的基因,本视图 480/500);last 阶段每个细胞按余弦相似度取最近的 prev 质心,得到"表达匹配类型"。不依赖两阶段类型同名,因此名字对不上时(方法卡记录的 final 上只剩 5 个同名类型)机制仍然覆盖全部细胞。-2. **位移**:对每个匹配类型 t,`delta_t = mean(last | 匹配到 t) − mean(prev | t)`(全基因面板上算),每细胞加 `α·delta_t`,α=0.05,clip ≥0。不乘步长比(`time_scale=false`)。-3. **坐标尺度**:把 last 阶段的点云整体缩放到 `max_k RMS(input_k)`。理由:一个阶段测到的空间 RMS 半径取决于该样本覆盖了多少结构(视野 / 保留的切片数),只能**低估**真实尺寸,不是单调的生物量;本视图上它从 354(较早)降到 217(较晚),沿趋势外推一定错。胚胎随时间长大,所以更晚的目标阶段其尺度至少是各输入里观测到的最大值。形状、细胞间相对位置、z 切片结构都不动(只做各向同性缩放)。单输入时是恒等操作。-4. **组成 / 细胞数**:不动(α_comp=0,n=last.n 夹到 [min_cells, max_cells])。实测外推组成趋势明显有害(见下)。-5. **单输入退路**:直接复制该阶段(分层抽到 max_cells)。--对照开关:环境变量 `VEC_MECH_OFF=1` → α=0,其余代码路径完全不变(PLAN 的 mechanism_off_control)。提交状态为**打开**(α=0.05)。--## 机制生效的证据(proxy,seed 0,程序 stderr)+`T2:heart:val_extrap`,proxy = E8.25_late + E8.75 → E9.5(步长比 1.5)。纯 CPU,约 4.6 s,峰值内存 <1 GB(`EXECUTION.json`: `gpu=false`)。 -- `n_shifted = 24826`:全部输出细胞都拿到非零位移(父节点按名字匹配时也是 24826 —— 见"PLAN 前提被证伪")。-- `n_matched_types = 33`(prev 的 33 个类型全部被匹配到),`max_matched_frac = 0.153`(没有单一类型吞掉 >50% 细胞)。-- `agree_with_name = 0.725`:表达匹配与标签一致率 72.5%,说明匹配大体合理但不是照抄标签。-- `mean_delta_cos = 0.500`:33 个位移向量两两余弦均值 0.50(<0.9),方向有分化。-- 四组分变化(ON 52.93 vs OFF 53.21,OFF = 只做坐标尺度):expression_change 50.0→49.67(de_score 0→−0.042,de_direction 0→+0.017)、cell_state 50.0→49.06(mmd_u 0.0586→0.0584 变好,variogram 0.0586→0.0634 变差,净负)、local_spatial 50.0→50.15(neighborhood_mmd 0.1144→0.1138)、shape_scale 62.83→62.83(位移不动坐标)。+## 方法(PLAN family T2HX-06) -## 查分结果(proxy A 半,共 11 次)+1. **表达:完全照抄末阶段(α=0)**。父节点 10 保留的「表达匹配型伪批量位移」被删除,不再只是调小。依据:树内数据一致显示该机制在 α∈{0.02, 0.05} 上都不产生超出噪声的收益,且方向偏负(cell_state 49.04 / 49.40,α=0 时为 50.00;expression_change 49.87 / 50.00 vs 50.00)。删除后 cell_state、expression_change 都回到 50.00。+2. **坐标:确定性 PCA 规范化**(本节点相对父节点 10 的结构修复)。末阶段点云 → 去质心 → 协方差 `eigh` → 特征值降序(`stable` 排序,并列按列序)→ 若 det<0 翻转最后一列(固定手性)→ 每根轴按**第三中心矩为正**定向(退化时依次用第一矩、特征向量最大绝对值分量的符号),得到与采集姿态无关、且对同一朵点云唯一的规范帧。+3. **坐标:各向同性缩放到 `FACTOR × max_k RMS(input_k)`,FACTOR=0.946**。理由(生物学/几何,不依赖保留阶段):一个样本测到的空间 RMS 半径取决于它覆盖了多少结构(视野、保留切片数),只能**低估**真实尺寸,因此不是随时间单调的生物量——本视图上它从 354(较早)降到 217(较晚),沿趋势外推必错;胚胎随时间长大,更晚的目标阶段其尺度至少是各输入观测到的最大值,再乘一个阻尼因子。只做刚体旋转 + 全局缩放,细胞间相对几何、z 切片结构都不动。+4. **细胞数 / 组成**:不动,`n = clip(last.n, min_cells, max_cells)`,超上限时用 `src.task2_spatial.sample.take` 按细胞类型分层无放回抽样(proxy 上 53742 → 24826)。+5. **单输入退路**:无 prev 时同样走规范化 + 缩放(此时 max RMS 就是该点云自身的 RMS,缩放为恒等),表达照抄,分层抽到 max_cells,不崩。 -| 配置 | board | expr_change | cell_state | shape | local |-|---|---:|---:|---:|---:|---:|-| copy_last(父节点 1 / 机制关闭且不改尺度) | 50.00 | 50.0 | 50.0 | 50.0 | 50.0 |-| 父节点 2:damped_shift α=0.1 按名字,尺度不动 | 49.51 | 49.35 | 48.41 | 50.0 | 50.27 |-| RMS = 两输入算术均值 285 | 51.93 | 50 | 50 | 57.73 | 50 |-| **RMS = max(输入) = 354(机制关闭)** | **53.21** | 50.0 | 50.0 | **62.83** | 50.0 |-| RMS = max + 表达匹配位移 α=0.05(**提交版**) | **52.93** | 49.67 | 49.06 | 62.83 | 50.15 |-| RMS = max + 表达匹配位移 α=0.1 | 52.86 | 49.67 | 48.65 | 62.83 | 50.31 |-| RMS = max + 组成外推 α_comp=1.0,n=0.8·last | 50.75 | 44.55 | 48.04 | 62.02 | 48.37 |-| 逐轴 RMS 取各输入最大(改纵横比) | 51.80 | 50.0 | 50.0 | 57.11 | 50.09 |+`FACTOR` 是唯一被扫的标量;`VEC_FACTOR` 环境变量只为扫参存在,提交时不设置,走代码里的 0.946。 -关键量:`scale_log_ratio` 从 −0.4375(copy_last,217/335)到 +0.0528(354/335),shape_scale 50→62.83,是本节点全部增益来源。逐轴缩放把 `d2_shape` 从 0.0489 改到 0.0299(更好)但 `occupancy_dice` 从 0.815 掉到 0.771,净负,故不采用。+## 机制生效的证据 -## PLAN 前提被证伪(重要)+- **实际改变了什么**:只改坐标(旋转 + 全局缩放),表达矩阵与细胞集合完全等同 copy_last。proxy seed 0 实测:`rms_inputs=[354.13, 216.88]` → `rms_out=335.00`,`scale_log_ratio=-0.0027`(≈0),PCA `eig_frac=[0.398, 0.350, 0.252]`,`axis_signs=[+1,-1,+1]`(确实翻了轴,不是恒等变换)。+- **四组分变化**(父节点 10 → 本节点,同一 proxy):cell_state 49.40 → **50.00**;expression_change 50.00 → **50.00**;local_spatial 50.07 → **50.00**(噪声内);shape_scale 66.27 → **66.60**。榜分 53.94 → **54.15**。+- **机制关闭对照**(`VEC_MECH_OFF=1`,跳过 PCA 规范化与缩放,输出末阶段原始坐标):shape_scale 66.60 → **50.00**,`scale_log_ratio` → −0.4375,其余三组不变(表达未动),榜分 **50.00**(= copy_last 地板)。即 +4.15 榜分全部来自该坐标机制,与 PLAN 的 `mechanism_off_control` 预期一致。 -PLAN 假设父节点弱在"按名字匹配 → 只有 5 个同名类型 → 机制空转"。在 proxy 上不是这样:E8.25_late 与 E8.75 的 33 个类型**全部同名**,父节点的 `n_shifted` 已经是 24826(全部细胞)。方法卡说的"只剩 5 个同名类型"指的是 final 视图的 E8.75→E9.5 那一步,不是 proxy。所以表达匹配在 proxy 上只是换了一套略不同的位移向量(与名字一致率 72.5%),没有"激活"任何新细胞,结果同样轻微为负:**这一族(沿上一阶段观测到的伪批量变化方向做阻尼位移)在本 proxy 上方向就是错的**,α=0.05→0.1 单调变差,与方法卡网格一致。父节点 cell_state 48.41 的最弱项不是覆盖问题,而是位移方向问题。+## 验证过的 -提交版仍保持机制打开(α=0.05,PLAN 要求),代价 0.28 分(53.21→52.93,远小于评分噪声量级),并在 METHOD.md 里如实记录 OFF 更高。+- **FACTOR 细扫(9 次 `vec-score`,全部 proxy seed 0)**:0.90→53.32 / 0.92→53.68 / 0.94→54.06 / 0.943→54.12 / **0.946→54.15** / 0.948→54.11 / 0.95→54.07 / 0.96→53.88 / 0.98→53.53。shape_scale 分别为 63.28 / 64.71 / 66.25 / 66.49 / **66.60** / 66.44 / 66.28 / 65.51 / 64.10。峰值在 0.946(`scale_log_ratio` 最接近 0),两侧单调下降,与父节点 ANALYSIS 的「factor 是最强单一杠杆」一致。+- **种子稳定性**:seed 0 / 1 / 2 榜分均为 54.1494(分层抽样对指标影响可忽略),rank3 期望 ≈54.15。+- **确定性**:同 seed 重跑,`.X`(data/indices/indptr)、`obsm/spatial_3D`、`obs`、`var` 逐字节相同。+- **视图无关**:在伪装视图(随机路径、manifest 键序打乱、所有阶段时间统一 +1 天)上重跑 seed 0,`.X` 与 `spatial_3D` 与真实视图完全相同。代码不读 `board`/`mode`/`source`/`dataset`,不依赖绝对发育时间(只用 `extrap_step` 的步长比,且仅用于日志)。+- **格式**:`vec-check --data <proxy view>` → `{"status": "ok", "errors": []}`。 -## 验证过 / 没验证过+## 没验证 / 风险 -- 验证过:proxy 上 seed 0 的 11 次查分(额度余 9)(上表);`vec-check` 通过;机制开/关两条路径都能跑(3 s,峰值内存约 0.6 GB,纯 CPU,`EXECUTION.json` 声明 `gpu:false`);α=0 时输出与 copy_last + 尺度缩放逐指标一致。-- 没验证过:final 视图(3 个输入,max=354 而 last=335,尺度规则会把点云放大 5.7%,方向未知);其他 seed(程序只在 n<last.n 时用 rng 抽样,本视图 n=last.n=24826 ≤ max_cells=25179,走 `arange`,与 seed 无关);单输入退路只做了代码走查,没有对应视图可跑;`VEC_MECH_OFF=1` 之外的环境变量没有分支。-- 视图无关性:只用输入的时间**顺序**、时间**差**(步长比)和数据本身,不读 `board`/`mode`/路径/绝对时间;时间整体平移不改变任何输出。+- 0.946 是在 proxy 括号(E8.25+E8.75)上扫出的阻尼因子;真实外推括号是 E8.75+E9.5 → E10.5,输入 RMS 组合不同,`max_k RMS(input_k) × 0.946` 未必同样贴近真值。方法卡记录的 final 上 E8.75→E9.5 只剩 5 个同名类型——本方法**不用类型名也不用 prev 的表达**(表达照抄末阶段),所以类型名对不上不影响;PCA 也不依赖类型名。+- PCA 规范化把点云旋到一个由自身三阶矩决定的帧。若评分器的对齐是各向同性的(Procrustes/Kabsch),旋转本身是中性的,收益应主要来自尺度;实测加 PCA 比不加(父节点 10 的 66.27)高 0.33,落在 T2 噪声(约 1 分)内,**不能确认 PCA 规范化本身有独立收益**,只能说没有害处。+- shape_scale 已被 `d2_shape=0.04891`(形状项,跨所有 factor 恒定)钉住,只靠全局缩放无法再涨;要突破需要改变点云形状,而这需要关于保留阶段形态的信息,属禁用范围。+- cell_state / expression_change / local_spatial 三组恒为 50.00(= copy_last 水平)。本节点没有提出任何能真正预测 E9.5→E10.5 表达变化的机制,这是已知的最大缺口。 ## 知识来源 -只用了通用机制知识:小鼠胚胎/心脏在该时间窗内整体尺寸随时间单调增长(发育生物学常识,非任何保留阶段的测量);以及"一个样本测到的空间范围受视野与保留切片数限制,只会低估不会高估结构真实尺寸"(成像/采样的几何性质)。没有使用 E9.5 / E10.5 / E12.5 或禁窗内任何测量值、类型清单或比例;`external/`(Qiu E8.75)和 `prior/` 都没有读取。所有数值都在运行时从 manifest 指定的输入现场计算。+只用通用机制知识:胚胎随发育时间增大(尺度非递减),以及「样本测到的空间范围受视野/切片保留限制、只能低估真实结构尺寸」这一测量学事实。未使用任何保留阶段(E10.5、E12.5,禁窗 9.5<E≤13.5)或保留基因型的实测数据、比例、标记基因或形态信息;`external/`(Qiu E8.75)与 `prior/` 均未被 `run.py` 读取。diff --git a/solution/README.md b/solution/README.mdindex 4cef581..c47e482 100644--- a/solution/README.md+++ b/solution/README.md@@ -1,9 +1,12 @@-# T2:heart:val_extrap — 表达匹配伪批量位移 + 观测最大空间尺度+# T2:heart:val_extrap — α=0 照抄末阶段表达 + PCA 规范化坐标 + 0.946×max RMS 缩放 -`run.py --data <view> --out <pred.h5ad> --seed <int>`(纯 CPU,约 3 s,见 `EXECUTION.json`)。+`run.py --data <view> --out <pred.h5ad> --seed <int>`(纯 CPU,约 5 s,`EXECUTION.json`: gpu=false)。 -- 表达:last 阶段每个细胞按余弦相似度匹配到 prev 阶段最近的类型质心,加 α=0.05·该匹配类型的伪批量差(`VEC_MECH_OFF=1` 关闭机制 → α=0)。-- 坐标:整体缩放到各输入阶段中最大的 RMS 半径(各向同性,形状不变)。-- 组成与细胞数:保持 last 阶段;单输入时直接复制该阶段。+- 表达:末阶段原样照抄(位移机制已删除,α=0)。+- 坐标:确定性 PCA 规范帧(去质心 → eigh 降序 → det=+1 → 第三矩定向),再各向同性缩放到 `FACTOR × max_k RMS(input_k)`,FACTOR=0.946。+- 细胞数:`clip(last.n, min_cells, max_cells)`,超上限时按细胞类型分层无放回抽样。+- 单输入:同一代码路径(缩放退化为恒等),不崩。+- `VEC_MECH_OFF=1`:关闭 PCA 规范化与缩放(= copy_last 空间部分),用于机制对照。+- `VEC_FACTOR=<f>`:仅扫参用,提交时不设置。 -分数、对照与已证伪的 PLAN 前提见 `METHOD.md`。+细节与验证见 `METHOD.md`。无模型权重,故无 `ARTIFACTS.json`。diff --git a/solution/run.py b/solution/run.pyindex 94d8100..544ab96 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,29 +1,32 @@ #!/usr/bin/env python3-"""T2 extrapolation: expression-matched damped pseudobulk shift + observed-extent coordinate scale.--Mechanism (PLAN family T2HX-01). The two latest inputs give one observed step-(prev -> last). Every ``last`` cell is assigned the ``prev`` cell type whose-expression centroid is closest (cosine, on genes detected in >=1% of cells of-either stage), so coverage does not depend on the two stages sharing type-*names*. For each matched type ``t`` the pseudobulk delta is-``mean(last | matched to t) - mean(prev | t)`` and each cell receives-``alpha * delta_t`` (alpha = 0.05). Set ``VEC_MECH_OFF=1`` for the PLAN's-mechanism-off control (alpha = 0, expression = copy of the last stage).--Coordinate scale. The measured spatial RMS radius of a stage is set by how-much of the specimen the sample covers (field of view / sections kept), so it-is not a monotone biological quantity: on this view it goes 354 (earlier) ->-217 (later). A sample can only *underestimate* the extent of the structure it-comes from, and the embryo grows with time, so the extent of a later target is-taken to be at least the largest RMS radius observed among the inputs; the last-stage's cloud is rescaled to that radius (shape preserved, cells not moved-relative to each other). With one input this is a no-op.--Composition and cell count are left at the last stage's (alpha_comp = 0);-extrapolating the observed composition trend measured clearly worse than-holding it (expression_change 44.6 vs 50.0).--Single input stage: fall back to copying it (stratified to max_cells).+"""T2 extrapolation: copy-last expression + deterministic PCA-normalised, rescaled coordinates.++Mechanism (PLAN family T2HX-06). Two parts:++1. Expression: the last observed stage is copied verbatim (alpha = 0). On the+ proxy, pseudobulk displacement with alpha in [0.02, 0.05] never produced a+ gain above noise (cell_state 49.04-49.40 vs 50.00 at alpha = 0), so the+ shift mechanism is removed rather than tuned.++2. Coordinates: the last stage's point cloud is put in a deterministic+ canonical frame (centroid at origin, PCA rotation with fixed chirality and+ axis orientation via third moments) and then rescaled isotropically to+ ``FACTOR * max_k RMS(input_k)``. Rationale: the measured RMS radius of a+ sample can only underestimate the extent of the structure (field of view /+ sections kept), it is not monotone in time (proxy inputs: 354 -> 217), and+ the embryo grows, so the target extent is at least the largest observed+ input RMS, damped by FACTOR. PCA normalisation removes the arbitrary+ acquisition rotation of the sample so the scorer's alignment step compares+ clouds in a reproducible frame. Cell-to-cell relative geometry is+ preserved (rigid rotation + global scale only).++``VEC_MECH_OFF=1`` disables PCA normalisation and rescaling (raw last-stage+coordinates = copy_last spatial part). ``VEC_FACTOR`` overrides FACTOR+(scan knob, defaults to the committed value).++Single input stage: coordinates still get the canonical frame (scaling is+identity since the single input RMS is the cloud's own RMS); expression is+copied, stratified to max_cells. """ from __future__ import annotations@@ -34,7 +37,6 @@ import os import sys import numpy as np-import scipy.sparse as sp from src.task2_spatial.frame import rms_radius, scale_to_rms from src.task2_spatial.sample import take@@ -48,69 +50,41 @@ from src.task2_spatial.view_io import ( write_t2, ) -ALPHA = 0.02-MIN_CELL_FRAC = 0.01---def _row_normalise(X: sp.csr_matrix) -> sp.csr_matrix:- Y = X.copy().astype(np.float64)- norms = np.sqrt(np.add.reduceat(np.asarray(Y.data) ** 2, Y.indptr[:-1])) if Y.nnz else np.zeros(Y.shape[0])- norms = np.where(norms > 1e-12, norms, 1.0)- Y.data /= np.repeat(norms, np.diff(Y.indptr))- return Y.tocsr()---def _detect_frac(X: sp.csr_matrix) -> np.ndarray:- idx = X.indices- counts = np.bincount(idx, minlength=X.shape[1])- return counts / max(X.shape[0], 1)---def _group_means(X: sp.csr_matrix, codes: np.ndarray, k: int) -> np.ndarray:- ind = sp.csr_matrix(- (np.ones(len(codes), dtype=np.float64), (codes, np.arange(len(codes)))),- shape=(k, len(codes)),- )- sums = np.asarray((ind @ X.astype(np.float64)).todense())- cnt = np.bincount(codes, minlength=k).astype(np.float64)[:, None]- return sums / np.maximum(cnt, 1.0)---def matched_delta(prev, last, alpha: float):- """Cosine-match last cells to prev type centroids; return (codes, types, delta, diag)."""- keep = (_detect_frac(prev.X) >= MIN_CELL_FRAC) | (_detect_frac(last.X) >= MIN_CELL_FRAC)- keep = np.flatnonzero(keep)- Xp = prev.X[:, keep].tocsr()- Xl = last.X[:, keep].tocsr()- types = sorted(set(np.asarray(prev.labels).astype(str).tolist()))- t_of = {t: i for i, t in enumerate(types)}- pcode = np.array([t_of[t] for t in np.asarray(prev.labels).astype(str)], dtype=np.int64)- cen = _group_means(Xp, pcode, len(types))- cen /= np.maximum(np.linalg.norm(cen, axis=1, keepdims=True), 1e-12)- sim = np.asarray(_row_normalise(Xl) @ cen.T)- code = sim.argmax(1)- lcode = code.astype(np.int64)- # deltas are computed on the full panel (matching used the filtered subset)- full_prev = _group_means(prev.X, pcode, len(types))- full_last = _group_means(last.X, lcode, len(types))- delta = (full_last - full_prev).astype(np.float32)- counts = np.bincount(lcode, minlength=len(types))- diag = {- "n_kept_genes": int(keep.size),- "n_prev_types": len(types),- "n_matched_types": int((counts > 0).sum()),- "max_matched_frac": float(counts.max() / max(len(lcode), 1)),- "mean_max_sim": float(sim.max(1).mean()),- "agree_with_name": float((np.asarray(types)[lcode] == np.asarray(last.labels).astype(str)).mean()),- }- used = counts > 0- if used.sum() > 1:- d = delta[used].astype(np.float64)- dn = d / np.maximum(np.linalg.norm(d, axis=1, keepdims=True), 1e-12)- cos = dn @ dn.T- iu = np.triu_indices(len(dn), 1)- diag["mean_delta_cos"] = float(cos[iu].mean())- return lcode, types, delta, diag+FACTOR = 0.946+++def pca_canonicalise(coords: np.ndarray) -> tuple[np.ndarray, dict]:+ """Center, PCA-rotate with fixed chirality (det=+1) and axis signs (3rd moment).++ Deterministic given the point cloud: eigen-decomposition of the covariance+ with descending eigenvalues; ties broken by column order; each axis flipped+ so its third central moment is positive (fallback: positive first moment,+ then positive largest-|entry| of the eigenvector).+ """+ x = np.asarray(coords, dtype=np.float64)+ x = x - x.mean(axis=0)+ cov = (x.T @ x) / max(len(x) - 1, 1)+ evals, evecs = np.linalg.eigh(cov)+ order = np.argsort(-evals, kind="stable")+ evals, evecs = evals[order], evecs[:, order]+ if np.linalg.det(evecs) < 0:+ evecs[:, -1] *= -1.0+ diag = {"eig_frac": [float(v / max(evals.sum(), 1e-12)) for v in evals]}+ for j in range(evecs.shape[1]):+ y = x @ evecs[:, j]+ m3 = float((y**3).mean())+ if abs(m3) < 1e-12:+ m1 = float(y.mean())+ if abs(m1) < 1e-12:+ k = int(np.argmax(np.abs(evecs[:, j])))+ s = 1.0 if evecs[k, j] > 0 else -1.0+ else:+ s = 1.0 if m1 > 0 else -1.0+ else:+ s = 1.0 if m3 > 0 else -1.0+ evecs[:, j] *= s+ diag.setdefault("axis_signs", []).append(int(s))+ return x @ evecs, diag def main() -> None:@@ -120,7 +94,8 @@ def main() -> None: parser.add_argument("--seed", type=int, default=0) args = parser.parse_args() - alpha = 0.0 if os.environ.get("VEC_MECH_OFF", "") == "1" else ALPHA+ mech_off = os.environ.get("VEC_MECH_OFF", "") == "1"+ factor = float(os.environ.get("VEC_FACTOR", "") or FACTOR) manifest = load_manifest(args.data) genes = panel_genes(args.data, manifest)@@ -129,38 +104,31 @@ def main() -> None: prev_e, last_e, ratio = extrap_step(manifest) last = read_stage(args.data, last_e, genes) - if prev_e is None:- n = int(np.clip(last.n, lo, hi))- rows = np.sort(take(last.labels, min(n, last.n), rng))- write_t2(args.out, last.X[rows].toarray(), last.coords[rows], genes, seed=args.seed)- print(json.dumps({"fallback": "copy_last", "n": int(len(rows))}), file=sys.stderr)- return-- prev = read_stage(args.data, prev_e, genes)- n = int(np.clip(last.n, lo, hi))+ n = int(np.clip(min(last.n, hi), lo, hi)) idx = np.arange(last.n) if n >= last.n else np.sort(take(last.labels, n, rng))-- info: dict = {"alpha": alpha, "dt_ratio": float(ratio), "n": int(len(idx))} block = as_dense(last.X, idx).astype(np.float32)- if alpha:- code, types, delta, diag = matched_delta(prev, last, alpha)- add = delta[code[idx]]- block = np.clip(block + np.float32(alpha) * add, 0.0, None).astype(np.float32)- info["match"] = diag- info["n_shifted"] = int((np.abs(add).sum(1) > 0).sum())-- radii = [rms_radius(read_stage(args.data, e, genes).coords) for e in inputs_by_time(manifest)]- target_rms = float(np.max(radii)) * 0.95- coords = scale_to_rms(last.coords[idx], target_rms)- info.update(- step=[prev_e["stage"], last_e["stage"], manifest["target"]["stage"]],- rms_inputs=[round(r, 2) for r in radii],- rms_out=round(rms_radius(coords), 2),- expr_mean_in=float(as_dense(last.X, idx).mean()),- expr_mean_out=float(block.mean()),- )++ if mech_off:+ coords = last.coords[idx].astype(np.float32)+ info: dict = {"mech": "off", "n": int(len(idx)), "rms_out": round(rms_radius(coords), 2)}+ else:+ canon, pca_diag = pca_canonicalise(last.coords[idx])+ radii = [rms_radius(read_stage(args.data, e, genes).coords) for e in inputs_by_time(manifest)]+ target_rms = float(np.max(radii)) * factor+ coords = scale_to_rms(canon, target_rms).astype(np.float32)+ info = {+ "mech": "on",+ "factor": factor,+ "dt_ratio": float(ratio),+ "n": int(len(idx)),+ "pca": pca_diag,+ "rms_inputs": [round(r, 2) for r in radii],+ "rms_out": round(rms_radius(coords), 2),+ }+ if prev_e is not None:+ info["step"] = [prev_e["stage"], last_e["stage"], manifest["target"]["stage"]] print(json.dumps(info, default=float), file=sys.stderr)- write_t2(args.out, block, coords.astype(np.float32), genes, seed=args.seed)+ write_t2(args.out, block, coords, genes, seed=args.seed) if __name__ == "__main__":
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k007 | Interval staging and held-out-window filtering of external data | notes/official/来件/virtualembryo.ai/rules.md |
| k026 | Canonicalise predicted 3D coordinates before submission | notes/pitfalls/04_scorer_invariance.md |
| k027 | Joint expression-geometry generation with relative geometry | notes/competition/03_solution_landscape.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 删除表达匹配伪批量位移(α=0,表达照抄末阶段),新增确定性PCA坐标规范化(去质心→eigh降序→det=+1→第三矩定轴),并把各向同性缩放因子从0.95细扫定为0.946(缩放目标仍为max_k RMS(input_k))。 |
|---|---|
| 各组分数的变化 | cell_state:略变好但在T2噪声(~1)内:49.40 → 50.00(+0.60),方向与node 6『α=0恢复到50.00』的预测一致 expression_change:噪声内:50.00 → 50.00(+0.00) local_spatial:噪声内:50.07 → 50.00(-0.07) shape_scale:噪声内:66.27 → 66.60(+0.33),FACTOR 0.946 相对 0.95 的细扫收益未超出噪声 |
| family_id | T2HX-06 |
| 假设是否成立 | unclear |
| 经验 |
|
| mechanism_active | 是 |
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 4 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 3 次:bash 2、write 1 |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 15,209 · 输出 1,031 · 思考 465 |
| 任务(第一行) | 分析节点 n12 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/analyst.jsonl 8 KB /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/analyst.stderr |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 22 |
| 工具调用 | 共 23 次:bash 20、write 2、edit 1 |
| 用时 | 5 分 |
| token 数 | 输入 41,338 · 输出 8,364 · 思考 3,926 |
| 任务(第一行) | 按计划修改程序:去除伪批量位移 + PCA规范化 + 缩放因子细扫 |
| 最后的回答(摘录) | **做了什么**:删除父节点的伪批量表达位移(α=0,表达照抄末阶段),加入确定性 PCA 坐标规范化(去质心→eigh降序→det=+1→第三矩定轴),坐标各向同性缩放到 `FACTOR×max_k RMS(input_k)`,FACTOR 细扫定为 0.946。 **查分结果**(proxy,8 次额度剩余):FACTOR 0.90→53.32、0.92→53.68、0.94→54.06、0.943→54.12、**0.946→54.15**、0.948→54.11、0.95→54.07、0.96→53.88、0.98→53.53;seed 0/1/2 均 54.1494。分组:cell_state 50.00(父 49.40)、expression_change 50.00、local_spatial 50.00、shape_scale 66.60(父 66.27)。机制对照 `VEC_MECH_OFF=1` → 50.00(shape_scale 50.00,scale_log_ratio −0.4375)。确定性、伪装视图(时间+1、路径与 manifest 键序打乱)逐字节一致、`vec-check` ok 均已验证。 **下一步最值得试**:三组表达/邻域指标恒在 50.00,shape_scale 被 d2_shape 钉死,全局缩放已无空间。真正的缺口是一个不依赖保留阶段信息、能预测末阶段→目标阶段表达变化的机制(如用 `prior/` 的通路/TF 调控知识做谱系方向性微调,或用 `external/` 数据学「同阶段细胞间变异结构」再叠加),而不是继续调 factor。 |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/engineer.jsonl 87 KB /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/engineer.stderr |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 1 |
| 工具调用 | 共 0 次:— |
| 用时 | 1 分 |
| token 数 | 输入 11,744 · 输出 797 · 思考 4,039 |
| 任务(第一行) | 为节点 n12(改进,父节点 n10)写改进计划 PLAN.json |
| 最后的回答(摘录) | {
"name": "去除伪批量位移 + PCA规范化 + 缩放因子细扫",
"motivation": "父节点10的cell_state 49.40是四组最弱,ANALYSIS确认α=0.02位移在噪声内且方向偏负(node 6证明α=0恢复50.00)。shape_scale 66.27是唯一增益来源但缺PCA规范化(node 6加后66.60)。ANALYSIS明确建议:细扫factor 0.90/0.925/0.975,去掉位移。当前最佳node 6=54.15(rank3 54.00),本节点从node 10代码出发,结构修复后有望追平或超过。",
"approach": "1) 删除matched_delta调用,α硬编码为0(表达照抄末阶段);2) 加入确定性PCA规范化(质心归零→PCA旋转→det=+1→第三矩定向,与node 6相同);3) 缩放因子f细扫:在{0.90, 0.92, 0.94, 0.946, 0.95, 0.96}上各跑一次vec-score(6次),选shape_scale最高的f;4) 若最优f与0.946差异<0.01则取0.946(与参考335一致)。总查分预算≤10次。单输入退路:无prev时直接copy_last+PCA(缩放为恒等)。用VEC_MECH_OFF=1跳过PCA和缩放作为对照。",
"expected_groups": ["shape_scale", "cell_state"],
"risks": "1) factor最优点可能不在扫描范围内(0.90以下或0.96以上),但已有数据显示>1.0和<0.9方向均变差,风险低;2) PCA规范化实现与node 6不一致导致手性翻转——Engineer应对照node 6代码;3) 30分钟内6次查分+代码修改时间紧,建议先改代码跑一次确认无报错再扫因子。尽早发现:第一次vec-score若shape_scale<62,说明PCA实现有误。",
"family_id": "T2HX-06",
"mechanism": "确定性PCA规范化(固定手性与轴朝向,消除评分器对齐的旋转方差)+ 各向同性缩放到优化因子×max输入RMS。",
"vs_constant_shift": "不做任何表达位移或组成重加权;机制是坐标的刚体规范化(旋转+反射固定)加全局各向同性缩放,不改变细胞间相对位置,不修改表达值。",
"mechanism_evidence": "1) shape_scale从62.83(无PCA,f=1.0)→66+(有PCA,f≈0.946);2) scale_log_ratio≈0(缩放后RMS≈335=参考);3) VEC_MECH_OFF=1时shape_scale回到~50(无缩放无PCA);4) cell_state从49.40恢复到50.00(去除α位移)。",
"mechanism_off_control": "VEC_MECH_OFF=1:跳过PCA规范化和坐标缩放,直接输出末阶段原始坐标(=copy_last的空间部分)。预期:shape_scale从66+降到~50,其余三组不变(表达未动)。",
"sources": []
} |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/researcher.jsonl 3 KB /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/researcher.stderr |
审查员
| 角色 | 审查员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 6 |
| 工具调用 | 共 10 次:read 5、bash 4、write 1 |
| 用时 | 1 分 |
| token 数 | 输入 17,953 · 输出 1,308 · 思考 2,808 |
| 任务(第一行) | 审查节点 n12 的程序是否越界读取、写死目标、钻评分器漏洞 |
| 最后的回答(摘录) | REVIEW.json written |
| 原始记录位置 | /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/reviewer.jsonl 74 KB /home/spark-longxinyang/vec/runs/formal/20261003-070222-search-t2-heart-extrap-g24-D/nodes/12/reviewer.stderr |