总览 · ← 返回运行 20261001-124831-search-t1-fake
节点 n4
增殖驱动的组成重加权(负结果):类型权重×(1+γ·增殖z),γ=0 复现父本 55.97,γ=±0.25/0.35/1.0 与 5118 细胞均降分。
| 运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。 | 20261001-124831-search-t1-fake |
|---|---|
| 父节点 | n2 |
| 子节点 | n9、n12 |
| 操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。 | 改进 |
| 状态 | 已打分 |
| 分数 | 搜索目标分 55.97(+0.0) · proxy 55.97(+0.0) · proxy2 55.97(+0.0) |
| 审查 | 未审查 |
| 用时?从运行开始到结束(或到现在)的挂钟时间。 | 32 分 |
| 程序版本 | 5ba0ea2aa1a76a14692dd56d741729afc76ceddf (programs.git) |
| 导入自 | 20261001-032558-search-t1-spark-c3#4 |
| 备注 | re-scored at launch (origin 20261001-032558-search-t1-spark-c3 node 4, score there 55.97) |
方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。
来自 programs.git 5ba0ea2aa1:solution/METHOD.md
增殖驱动的组成重加权(负结果):类型权重×(1+γ·增殖z),γ=0 复现父本 55.97,γ=±0.25/0.35/1.0 与 5118 细胞均降分。
做了什么
父本(节点 2,55.97)只重加权组成、不动表达,最弱组是 de_recovery 53.06。本节点按 PLAN 试了两条只依赖输入阶段数据的新杠杆:
- 逐类型分化轴位移(PLAN 步骤 1-3,未采用):在 E8.5 每个类型内,用
prior/里的细胞周期基因集 (reactome/go/msigdb 的*cell cycle*|*replication*|*mitotic*|*E2F*|*G2M*,命中面板 2091 个基因)算每细胞增殖 z 分数, 再取类型内 1000 HVG 的前 5 个 PC,选 |corr(PC, s)| 最大者作时间轴,δ_c = 晚端三分之一 − 早端三分之一。 实测:18 个类型无一通过 |corr| >= 0.15(周期信号不在类型内前 5 个主成分里),全部 δ_c = 0,该分支等价于父本,故已从代码中移除。 诊断(未查分):直接用增殖分数三等分(不经 PCA)时,δ 的前 20 个基因是 Rpl/Rps/Tmsb10/Malat1,且 IFT-CM 的 "晚端"下调 Myh6/Ttn/Actc1/Ankrd1 —— 与心肌成熟方向相反。说明这条轴在 E8.5 单快照上主要是核糖体/深度等技术变异,不是发育时间。 - 增殖驱动的组成增长(已实现,默认关闭):
growth.py里type_growth给每个类型一个增殖 z 分数(对阶段中位数居中、阶段 std 归一, 细胞数 < 30 的类型记 0),growth_sample把父本的解剖权重(heart ×1.6、edge ×0.25、丢 Neural Tube)乘上1 + γ·clip(g, -1, 1),其余抽样逻辑(类型顺序、largest_remainder、take、RNG 调用序列)与heart_reweight逐调用一致。
查分结果(T1:val proxy,seed 0,单次)
| 配置 | 榜分 | de_recovery | direction | cell_state | covariation |
|---|---|---|---|---|---|
| γ=0(=父本,逐位相同) | 55.97 | 53.06 | 58.56 | 56.90 | 54.97 |
| γ=+0.35 | 55.29 | 54.17 | 57.20 | 55.49 | 54.03 |
| γ=-0.25 | 54.96 | 53.06 | 58.06 | 55.12 | 53.18 |
| γ=+1.0 | 52.68 | 53.06 | 55.36 | 50.18 | 52.63 |
| N_CELLS=5118(max_cells),γ=0 | 55.12 | 53.06 | 58.38 | 55.16 | 53.57 |
γ 的两个方向都单调劣于 γ=0,说明父本手调的心脏权重已经吃掉了增殖能提供的组成信息(增殖排序本身是合理的:
NCC +0.89、aSHF +0.47、Endothelium +0.27 高,Foregut -0.50、Blood -0.68 低)。加大细胞数也降分。
所以提交的默认配置 γ=0、N_CELLS=4000,输出与父本逐位相同(已用 np.array_equal 核对);机制保留在代码里,VEC_GAMMA 可复现上表。
验证过 / 没验证
- 验证过:
γ=0与父本预测逐位相同;vec-check在 seed 0 和 seed 3 上均 ok;run.py只按len(inputs_by_time(manifest))读最后一个输入阶段,proxy/final 同一段代码,无阶段名分支、无硬编码统计量;prior 基因集缺失或命中 < 20 时 γ 自动置 0。 - 未验证:所有结论只基于 seed 0 单次查分(低于 PLAN 要求的 3-seed 均值),γ=+0.35 的 de_recovery +1.11 可能只是噪声; PLAN 步骤 4 的两阶段伪批量融合分支没有写进提交版本(单快照轴在 proxy 上就没有信号,融合无意义);final 视图上未运行。
- 运行 ~40 s、峰值内存 ~1.5 GB(
type_growth只在 4000 细胞 × 2091 基因的子块上做稠密计算)。
下一步最值得试
- 表达侧必须换信号源:单快照的增殖轴在 E8.5 上是技术轴。final 视图有 E8.5+E9.5 两个阶段, 同名类型的两阶段伪批量差 δ2_c 是唯一有真值方向的位移信号(配 T1-02 式逐基因 t 统计收缩,k≈4), 在 final 上直接可算;proxy 上无法验证,只能靠 final 的一次查分。
- 组成侧改成网格搜索父本的三个手调数(heart 权重、edge 权重、是否丢 Neural Tube), 这是唯一被证明有效的杠杆(copy_last 49.77 → 55.97),比任何表达位移的期望收益都高;用 3-seed 均值防噪声。
- covariation 54.97 与 mmd 是第二弱项:可试在抽出的真实细胞上做同类型内的轻微 jitter(保留协方差结构的量级), 而不是加独立噪声。
调研员的计划
| 名称 | 类型内分化轴位移 + t 统计收缩(叠加父本心脏重加权) |
|---|---|
| 动机 | 父节点 2(55.97)最弱的组是 de_recovery 53.06,而节点 1 copy_last 的 de_recovery 恰为 50.00 地板,即父本靠纯组成重加权只把 de_recovery 抬了 +3.06,covariation 54.97 也只 +3.43;父本 run.py 只调用 heart_reweight 抽样、完全不改表达,所以任何逐基因的表达位移都还是空白。要提 de_recovery 必须给类型均值一个正确符号的位移,但方向库里唯一能在单输入阶段(proxy)真正算出位移信号的是 T1-06(单快照伪时间方向),T1-02/04/07/08 在 proxy 上都退化成 copy_last、无法查分验证。因此本节点做 T1-06 的轻量版 + T1-02 式经验贝叶斯收缩,组成部分完全不动(沿用 heart_reweight),实现单变量对照。 |
| 做法 | 【结构】保留父本管线:load_manifest -> panel_genes -> inputs_by_time -> read_stage(最后一个输入阶段) -> heart_reweight 抽 n=target_n_cells(manifest,4000) 个真实细胞 -> 在这些细胞上叠加逐类型位移 -> write_prediction。N_CELLS、heart_reweight、抽样顺序全部不改,唯一新变量是位移。 【步骤 1:逐类型分化轴(在完整输入阶段上算,不是在 4000 抽样上算)】对最后输入阶段的每个细胞类型 c(labels_of),若 n_c>=100:(a) 取该类型内方差最大的 1000 个基因,log1p 表达做列中心化,TruncatedSVD/np.linalg.svd 取前 5 个 PC(n_c x 1000,float32,秒级);(b) 算增殖打分 s_i = 该类型细胞在 prior 基因集(data/external/prior/reactome/gene_sets.gmt 与 go/gene_sets_bp.gmt 中名字含 cell cycle / DNA replication / mitotic 的集,10-500 基因)内基因的 z 分数均值;(c) 在 5 个 PC 中选 |corr(PC_k, s)| 最大者 k*,并要求 |corr|>=0.15,否则该类型位移置 0(宁缺勿错);把轴定向为 s 递减方向(增殖高=早端)。 【步骤 2:基因空间位移】沿 k* 轴把该类型细胞分成三个等份,δ_c = mean_log(高分位组) − mean_log(低分位组),在全部面板基因上计算(只用两组均值,不做全基因 SVD)。随后三道清洗:(i) 把细胞周期基因集内的 δ_g 强制置 0——周期变化不是发育时间,否则 de_recovery 会被假 DE 污染;(ii) 逐基因可靠性收缩(关键,直接打 de_recovery 的精度):se_g = sqrt(2*v_g/max(n_third,1)),v_g 为该类型内基因 g 的方差,t_g = δ_g/se_g,δ_g <- δ_g * t_g^2/(t_g^2 + k),初值 k=4(等价要求 |t|>~2 才基本保留),搜索范围 k∈{2,4,8};(iii) 幅度截断:|δ_g| 夹到该类型 |δ| 的 99 分位。n_c<100 的类型 δ_c=0。 【步骤 3:施加】对抽样出的每个细胞 i:x_i <- clip(x_i + β * δ_{c(i)}, 0, None)(log1p 空间)。β 初值网格 {0, 0.25, 0.5},可加 0.75;β=0 必须逐位复现父本 55.97,用作管线自检。因为每个类型加的是常向量,… |
| 风险 | 1) 轴向定错符号或选到细胞周期轴:会让 de_recovery/direction 反而下降。早期发现:不查分就能自检——打印每个类型的 |corr(PC,s)|、被选中的 PC 序号、以及 δ_c 的前 20 个上调基因是否富集细胞周期集(若富集说明步骤 2(i) 没生效或轴仍是周期轴);|corr|<0.15 的类型必须 δ=0。2) β 过大导致越过目标阶段(overshoot):表现为 de_recovery 随 β 单调下降到 β=0 之下,粗网格 + 99 分位截断可控,不要细调 β(proxy 上调参会过拟合噪声)。3) 提升幅度可能落在噪声内:所有比较一律用 3 seed 均值和 +2 分门槛,单次结果不采纳;β=0 必须复现 55.97,否则说明抽样/写出路径被改动,先修管线再评分。4) 运行时与内存:逐类型 SVD 若在全基因上做会炸内存(父本峰值 1.21 GB),必须只在 1000 个 HVG 上做 SVD、在全基因上只算两组均值;细胞数多的类型先子采样到 3000 再算轴。若单类型 PCA 仍慢,退化方案是全局一次 PCA + 类型内投影。5) prior 基因集文件缺失或基因名对不上面板:读不到 gmt 时,细胞周期集退化为空 -> 此时必须禁用整个位移分支(β 强制 0)而不是跳过清洗,避免生成被周期驱动的假 DE;开工前 5 分钟先确认 prior 路径与命中率(T1 面板 Reactome 覆盖约 27%、GO 约 73%)。6) final 两阶段融合分支无法在 proxy 上验证:只要求它语法可达、cos<=0 时退回 δ1_c,且分支键是输入阶段数;不要为了 final 在 proxy 上调任何参数。 |
代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。
对比:父节点版本 39e48cc98a。改动的文件:solution/METHOD.md +47 −0、solution/README.md +15 −3、solution/growth.py +137 −0、solution/run.py +31 −8
diff --git a/solution/METHOD.md b/solution/METHOD.mdnew file mode 100644index 0000000..c711d1c--- /dev/null+++ b/solution/METHOD.md@@ -0,0 +1,47 @@+增殖驱动的组成重加权(负结果):类型权重×(1+γ·增殖z),γ=0 复现父本 55.97,γ=±0.25/0.35/1.0 与 5118 细胞均降分。++## 做了什么++父本(节点 2,55.97)只重加权组成、不动表达,最弱组是 de_recovery 53.06。本节点按 PLAN 试了两条只依赖输入阶段数据的新杠杆:++1. **逐类型分化轴位移(PLAN 步骤 1-3,未采用)**:在 E8.5 每个类型内,用 `prior/` 里的细胞周期基因集+ (reactome/go/msigdb 的 `*cell cycle*|*replication*|*mitotic*|*E2F*|*G2M*`,命中面板 2091 个基因)算每细胞增殖 z 分数,+ 再取类型内 1000 HVG 的前 5 个 PC,选 |corr(PC, s)| 最大者作时间轴,δ_c = 晚端三分之一 − 早端三分之一。+ **实测:18 个类型无一通过 |corr| >= 0.15**(周期信号不在类型内前 5 个主成分里),全部 δ_c = 0,该分支等价于父本,故已从代码中移除。+ 诊断(未查分):直接用增殖分数三等分(不经 PCA)时,δ 的前 20 个基因是 Rpl/Rps/Tmsb10/Malat1,且 IFT-CM 的+ "晚端"下调 Myh6/Ttn/Actc1/Ankrd1 —— 与心肌成熟方向相反。说明这条轴在 E8.5 单快照上主要是核糖体/深度等技术变异,不是发育时间。+2. **增殖驱动的组成增长(已实现,默认关闭)**:`growth.py` 里 `type_growth` 给每个类型一个增殖 z 分数(对阶段中位数居中、阶段 std 归一,+ 细胞数 < 30 的类型记 0),`growth_sample` 把父本的解剖权重(heart ×1.6、edge ×0.25、丢 Neural Tube)乘上+ `1 + γ·clip(g, -1, 1)`,其余抽样逻辑(类型顺序、`largest_remainder`、`take`、RNG 调用序列)与 `heart_reweight` 逐调用一致。++## 查分结果(T1:val proxy,seed 0,单次)++| 配置 | 榜分 | de_recovery | direction | cell_state | covariation |+|---|---|---|---|---|---|+| γ=0(=父本,逐位相同) | **55.97** | 53.06 | 58.56 | 56.90 | 54.97 |+| γ=+0.35 | 55.29 | 54.17 | 57.20 | 55.49 | 54.03 |+| γ=-0.25 | 54.96 | 53.06 | 58.06 | 55.12 | 53.18 |+| γ=+1.0 | 52.68 | 53.06 | 55.36 | 50.18 | 52.63 |+| N_CELLS=5118(max_cells),γ=0 | 55.12 | 53.06 | 58.38 | 55.16 | 53.57 |++γ 的两个方向都单调劣于 γ=0,说明父本手调的心脏权重已经吃掉了增殖能提供的组成信息(增殖排序本身是合理的:+NCC +0.89、aSHF +0.47、Endothelium +0.27 高,Foregut -0.50、Blood -0.68 低)。加大细胞数也降分。+所以**提交的默认配置 γ=0、N_CELLS=4000,输出与父本逐位相同**(已用 `np.array_equal` 核对);机制保留在代码里,`VEC_GAMMA` 可复现上表。++## 验证过 / 没验证++- 验证过:`γ=0` 与父本预测逐位相同;`vec-check` 在 seed 0 和 seed 3 上均 ok;`run.py` 只按 `len(inputs_by_time(manifest))`+ 读最后一个输入阶段,proxy/final 同一段代码,无阶段名分支、无硬编码统计量;prior 基因集缺失或命中 < 20 时 γ 自动置 0。+- 未验证:所有结论只基于 seed 0 单次查分(低于 PLAN 要求的 3-seed 均值),γ=+0.35 的 de_recovery +1.11 可能只是噪声;+ PLAN 步骤 4 的两阶段伪批量融合分支没有写进提交版本(单快照轴在 proxy 上就没有信号,融合无意义);final 视图上未运行。+- 运行 ~40 s、峰值内存 ~1.5 GB(`type_growth` 只在 4000 细胞 × 2091 基因的子块上做稠密计算)。++## 下一步最值得试++1. **表达侧必须换信号源**:单快照的增殖轴在 E8.5 上是技术轴。final 视图有 E8.5+E9.5 两个阶段,+ 同名类型的两阶段伪批量差 δ2_c 是唯一有真值方向的位移信号(配 T1-02 式逐基因 t 统计收缩,k≈4),+ 在 final 上直接可算;proxy 上无法验证,只能靠 final 的一次查分。+2. **组成侧改成网格搜索父本的三个手调数**(heart 权重、edge 权重、是否丢 Neural Tube),+ 这是唯一被证明有效的杠杆(copy_last 49.77 → 55.97),比任何表达位移的期望收益都高;用 3-seed 均值防噪声。+3. covariation 54.97 与 mmd 是第二弱项:可试在抽出的真实细胞上做同类型内的轻微 jitter(保留协方差结构的量级),+ 而不是加独立噪声。diff --git a/solution/README.md b/solution/README.mdindex e66c7c3..9d4b241 100644--- a/solution/README.md+++ b/solution/README.md@@ -1,4 +1,16 @@-# heart_jcf_peri+# node 4 (improve, parent = heart_jcf_peri 55.97) -run2(20260927-172643-grok-t1)的 T1 胜者:对最新输入阶段按细胞类型重加权再抽 4000 个真实细胞,不改表达。丢 Neural Tube;心脏类(CM、内皮/心内膜、SHF/PHM、JCF、心包、心外膜原基)×1.6;表面外胚层、EXEM、轴旁中胚层 ×0.25;其余 ×1。-proxy 预期 55.97(seed 0,与 run2 的预测逐位相同)。final 读 E9.5,类型集合已含 E9.5 的名字(V-CM、Endocardium、BEC、aPHM、pPHM、Proepicardium)。+Composition is the parent's: latest input stage resampled by cell type with `heart_reweight`'s+anatomical weights (heart x1.6, dissection edges x0.25, Neural Tube dropped), 4000 real cells,+expression untouched. Added here: `growth.py` scales those weights by each type's own+proliferation level, measured at run time from the view's `prior/` cell cycle gene sets+(`VEC_GAMMA`, default 0.0 = parent bit for bit).++Measured on T1:val proxy, seed 0: gamma 0.0 -> 55.97, +0.35 -> 55.29, -0.25 -> 54.96,++1.0 -> 52.68; N_CELLS 5118 -> 55.12. Both growth directions are worse, so the shipped+default keeps the parent's composition. See METHOD.md for the single-snapshot+differentiation-axis attempt that was dropped (no type reached |corr(PC, cell cycle)| >= 0.15,+and the score-based axis was ribosome/depth-driven with the wrong sign on CM maturation genes).++Runs on proxy (E8.5 -> E9.5) and final (E8.5, E9.5 -> E10.5) with the same code: it reads only+`inputs_by_time(manifest)[-1]` and branches on nothing else.diff --git a/solution/growth.py b/solution/growth.pynew file mode 100644index 0000000..c77c2f6--- /dev/null+++ b/solution/growth.py@@ -0,0 +1,137 @@+"""Proliferation-driven composition growth for a T1 temporal prediction.++Each cell type of the latest observed stage gets a growth factor from its own+proliferation level: the mean z score of that type's cells over the cell cycle+gene sets shipped in the view's ``prior/`` directory, centred on the stage+median and scaled by the stage's spread. The parent's hand-set anatomical+weights are multiplied by ``1 + gamma * clip(growth, -1, 1)``, so a type that+cycles harder than the stage median gains relative mass over the interval and+a slower one loses it. Every number is measured on the input stage at run+time; no stage name and no held-out statistic appears in this file.++``gamma = 0`` (or a view without usable cell cycle priors) reproduces+``src.task1_temporal.reweight.heart_reweight`` exactly.+"""++from __future__ import annotations++import re+from pathlib import Path++import numpy as np+from scipy import sparse++MIN_CELLS = 30+SUBSAMPLE = 4000+MIN_CC_GENES = 20+_CC_NAME = re.compile(r"cell cycle|dna replication|mitotic|e2f|g2m|s phase|m phase", re.I)+++def _read_gmt(path: Path) -> list[tuple[str, list[str]]]:+ sets = []+ with path.open("r", encoding="utf-8") as fh:+ for line in fh:+ parts = line.rstrip("\n").split("\t")+ if len(parts) < 3:+ continue+ label = parts[1] if parts[0].startswith(("R-", "GO:")) else parts[0]+ genes = [g for g in parts[2:] if g] or [g for g in parts[1:] if g]+ sets.append((label, genes))+ return sets+++def cell_cycle_genes(view: str | Path, manifest: dict, genes: list[str]) -> np.ndarray:+ """Boolean mask over the panel: genes belonging to a proliferation set in ``prior/``."""+ root = Path(view)+ mask = np.zeros(len(genes), dtype=bool)+ index = {g: i for i, g in enumerate(genes)}+ for entry in manifest.get("prior") or []:+ path = root / entry["path"]+ if path.is_dir():+ candidates = sorted(path.glob("*.gmt"))+ elif path.suffix == ".gmt" and path.exists():+ candidates = [path]+ else:+ continue+ for gmt in candidates:+ for label, members in _read_gmt(gmt):+ if not _CC_NAME.search(label):+ continue+ if len(members) < 10 or len(members) > 600:+ continue+ for g in members:+ j = index.get(g)+ if j is not None:+ mask[j] = True+ return mask+++def type_growth(+ X,+ labels: np.ndarray,+ cc_mask: np.ndarray,+ rng: np.random.Generator | None = None,+) -> dict[str, float]:+ """Per-type proliferation z score, centred on the stage median (0 = average)."""+ rng = rng if rng is not None else np.random.default_rng(0)+ n = X.shape[0]+ rows = np.arange(n) if n <= SUBSAMPLE else np.sort(rng.choice(n, size=SUBSAMPLE, replace=False))+ D = np.asarray(X[rows].todense() if sparse.issparse(X) else X[rows], dtype=np.float32)+ cc_idx = np.flatnonzero(cc_mask)+ if cc_idx.size < MIN_CC_GENES:+ return {}+ B = D[:, cc_idx]+ mu = B.mean(axis=0, keepdims=True)+ sd = B.std(axis=0, keepdims=True)+ keep = sd[0] > 1e-6+ if int(keep.sum()) < MIN_CC_GENES:+ return {}+ score = ((B[:, keep] - mu[:, keep]) / sd[:, keep]).mean(axis=1)+ lab = np.asarray(labels)[rows]+ centre = float(np.median(score))+ scale = float(np.std(score)) or 1.0+ out: dict[str, float] = {}+ for t in np.unique(lab):+ m = lab == t+ out[str(t)] = 0.0 if int(m.sum()) < MIN_CELLS else float((score[m].mean() - centre) / scale)+ return out+++def growth_sample(+ X,+ labels: np.ndarray,+ n_cells: int,+ growth: dict[str, float],+ gamma: float,+ seed: int = 0,+):+ """Resample ``n_cells`` rows with anatomical weights scaled by proliferation.++ Mirrors ``src.task1_temporal.reweight.heart_reweight`` call for call, so+ ``gamma = 0`` (or an empty ``growth``) reproduces it exactly.+ """+ from src.task1_temporal.reweight import (+ DROP_TYPES,+ EDGE_WEIGHT,+ HEART_WEIGHT,+ largest_remainder,+ take,+ type_weights,+ )++ rng = np.random.default_rng(seed)+ types = [str(t) for t in np.unique(labels) if str(t) not in DROP_TYPES]+ counts = np.array([(labels == t).sum() for t in types], dtype=np.float64)+ w = type_weights(types, HEART_WEIGHT, EDGE_WEIGHT)+ if gamma != 0.0 and growth:+ g = np.array([np.clip(growth.get(t, 0.0), -1.0, 1.0) for t in types], dtype=np.float64)+ w = np.maximum(w * (1.0 + gamma * g), 1e-3)+ alloc = largest_remainder(counts * w, n_cells)+ blocks = []+ for t, k in zip(types, alloc):+ if k <= 0:+ continue+ blocks.append(take(X, np.flatnonzero(labels == t), int(k), rng))+ out = sparse.vstack(blocks, format="csr").astype(np.float32)+ out.eliminate_zeros()+ return outdiff --git a/solution/run.py b/solution/run.pyindex a752b21..5161138 100644--- a/solution/run.py+++ b/solution/run.py@@ -1,17 +1,24 @@ #!/usr/bin/env python3-"""heart_jcf_peri: run2's T1 winner. Reweight the latest input stage by cell type.+"""heart_jcf_peri + proliferation-driven composition growth. -Neural Tube dropped; heart types (CM, endothelium/endocardium, SHF/PHM, JCF,-pericardium, proepicardium) x1.6; surface ectoderm, EXEM, paraxial mesoderm-x0.25; everything else x1. 4000 real cells, no expression shift. Proxy reads-E8.5, final reads E9.5; the type set already names both stages' labels.+Composition: the latest input stage is resampled by cell type with the parent's+anatomical weights (heart x1.6, dissection edges x0.25, neural tube dropped),+and those weights are additionally scaled by each type's own proliferation+level measured on the input stage: a type whose cells cycle harder than the+stage median gains relative mass over the interval, a slower one loses it.+The per-type growth factor is computed from the data at run time (cell cycle+gene sets come from the view's ``prior/``), never hard-coded, so the same code+runs on the proxy view (E8.5 -> E9.5) and the final view (E9.5 -> E10.5).+Expression is not shifted: cells are real cells of the last observed stage.++``gamma = 0`` reproduces the parent (heart_jcf_peri) bit for bit. """ from __future__ import annotations import argparse+import os -from src.task1_temporal.reweight import heart_reweight from src.task1_temporal.view_io import ( inputs_by_time, labels_of,@@ -22,7 +29,12 @@ from src.task1_temporal.view_io import ( write_prediction, ) +from growth import cell_cycle_genes, growth_sample, type_growth+ N_CELLS = 4000+# measured on proxy seed 0: 0.0 -> 55.97, +0.35 -> 55.29, -0.25 -> 54.96, +1.0 -> 52.68+GAMMA = float(os.environ.get("VEC_GAMMA", "0.0"))+VERBOSE = bool(os.environ.get("VEC_VERBOSE", "")) def main() -> None:@@ -34,9 +46,20 @@ def main() -> None: manifest = load_manifest(args.data) genes = panel_genes(args.data, manifest)- last = read_stage(args.data, inputs_by_time(manifest)[-1], genes)+ stages = inputs_by_time(manifest)+ last = read_stage(args.data, stages[-1], genes)+ labels = labels_of(last)++ cc_mask = cell_cycle_genes(args.data, manifest, genes)+ growth = type_growth(last.X, labels, cc_mask, rng=__import__("numpy").random.default_rng(args.seed))+ if VERBOSE:+ print(f"cell cycle genes: {int(cc_mask.sum())}")+ for t, g in sorted(growth.items(), key=lambda kv: -kv[1]):+ print(f" {t}: g={g:+.3f} -> w_mult={max(1.0 + GAMMA * max(min(g, 1.0), -1.0), 1e-3):.3f}")++ gamma = GAMMA if int(cc_mask.sum()) >= 20 and growth else 0.0 n = target_n_cells(manifest, N_CELLS)- X = heart_reweight(last.X, labels_of(last), n_cells=n, seed=args.seed)+ X = growth_sample(last.X, labels, n_cells=n, growth=growth, gamma=gamma, seed=args.seed) write_prediction(X, genes, args.out, seed=args.seed)
调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。
用到的知识库条目
| 编号 | 标题 | 出处 |
|---|---|---|
| k014 | Scorer noise and invariance on our proxy boards | notes/pitfalls/04_scorer_invariance.md |
| k020 | Correcting sampling-scope (dissection) bias in composition | notes/guides/modeling_and_evaluation_guide.html |
| k007 | Interval staging and held-out-window filtering of external data | notes/来件/virtualembryo.ai/rules.md |
分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。
| 改了什么 | 新增 solution/growth.py:按 prior/ 细胞周期基因集算每类型增殖 z 分数,把父本解剖权重乘 (1+γ·clip(g,-1,1)),其余抽样/RNG 序列与 heart_reweight 逐调用一致;提交默认 γ=0、N_CELLS=4000,输出与父节点逐位相同(变化量表四组全为 +0.00,与'逐位相同'一致)。PLAN 步骤 1-3 的单快照分化轴位移分支因无信号已删除,未进入提交版本。 |
|---|---|
| 各组分数的变化 | board:55.97 -> 55.97,+0.00;代价是耗时 1.0s -> 2.4s、内存 1.21GB -> 1.30GB(多算的 type_growth 在 γ=0 时纯属开销) cell_state:56.90 -> 56.90,+0.00,无变化 covariation:54.97 -> 54.97,+0.00,无变化 de_recovery:53.06 -> 53.06,+0.00,提交配置与父本逐位相同,无任何变化 direction:58.56 -> 58.56,+0.00,无变化 |
| 假设是否成立 | 否 |
| 经验 |
|
| 下一步建议 |
|
对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。
只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。
分析员
| 角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。 | 分析员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数?大模型一共回复了几轮(每轮可以调用多个工具)。 | 5 |
| 工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。 | 共 4 次:— |
| 用时 | 不到 1 分 |
| token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。 | 输入 12,344 · 输出 1,528 · 思考 450 |
| 任务(第一行) | 分析节点 n4 的打分结果,写 ANALYSIS.json |
| 最后的回答(摘录) | ANALYSIS.json written |
| 原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。 | /home/spark-longxinyang/vec/g18_wt/agent/runs/20261001-124831-search-t1-fake/nodes/4/analyst.jsonl (文件不在) |
工程师
| 角色 | 工程师 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 35 |
| 工具调用 | 共 40 次:— |
| 用时 | 26 分 |
| token 数 | 输入 63,222 · 输出 16,735 · 思考 12,528 |
| 任务(第一行) | 按计划修改程序:类型内分化轴位移 + t 统计收缩(叠加父本心脏重加权) |
| 最后的回答(摘录) | **做了什么**:按 PLAN 试了两条只依赖输入阶段数据的新杠杆。(1) 单快照分化轴位移:E8.5 内 18 个类型无一达到 |corr(PC, 细胞周期分)| ≥ 0.15,δ 全为 0;改用增殖分数直接三等分时,δ 的 top 基因是 Rpl/Rps/Tmsb10/Malat1,IFT-CM "晚端"还下调 Myh6/Ttn/Actc1(与成熟方向相反)——这条轴是技术变异,已弃用并从代码移除。(2) 增殖驱动的组成增长(`solution/growth.py`,保留):父本解剖权重 × (1+γ·clip(增殖z,−1,1)),抽样与 RNG 调用序列与 `heart_reweight` 逐调用一致。 **查分**(proxy,seed 0,单次,用掉 5/20):γ=0 → **55.97**(与父本预测 `np.array_equal` 为真);γ=+0.35 → 55.29(de_recovery 54.17,其余全降);γ=−0.25 → 54.96;γ=+1.0 → 52.68;N_CELLS=5118 → 55.12。两个方向都单调劣于 γ=0,手调心脏权重已吃掉增殖信息,故提交默认 γ=0、4000 细胞,`vec-check` 在 seed 0/3 均 ok,运行 2.3 s。所有结论只基于单 seed,低于 PLAN 的 3-seed 门槛。 **下一步最值得试**:① 组成侧网格搜索父本三个手调数(heart/edge 权重、是否丢 Neural Tube)——唯一被证明有效的杠杆(49.77→55.97),用 3-seed 均值;② 表达位移只能在 final 的两阶段上做(同名类型 E8.5→E9.5 伪批量差 + t 统计收缩),proxy 无法验证;③ covariation 侧试同类型内轻微 jitter 而非独立噪声。 |
| 原始记录位置 | /home/spark-longxinyang/vec/g18_wt/agent/runs/20261001-124831-search-t1-fake/nodes/4/engineer.jsonl (文件不在) |
调研员
| 角色 | 调研员 alibaba-token-plan-cn/qwen3.8-max |
|---|---|
| 调用次数 | 1 次 |
| 轮数 | 7 |
| 工具调用 | 共 11 次:— |
| 用时 | 3 分 |
| token 数 | 输入 22,622 · 输出 3,026 · 思考 5,189 |
| 任务(第一行) | 为节点 n4(改进,父节点 n2)写改进计划 PLAN.json |
| 最后的回答(摘录) | PLAN.json written |
| 原始记录位置 | /home/spark-longxinyang/vec/g18_wt/agent/runs/20261001-124831-search-t1-fake/nodes/4/researcher.jsonl (文件不在) |