Virtual Embryo Challenge更新于 10-03 18:47(北京时间) / 每 5 分钟更新

总览 · ← 返回运行 20261001-233756-search-t1-abc-r0-A-era

节点 n27

T1-01 解剖族组成重加权+稀疏表达偏移(draft 完整基线)

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261001-233756-search-t1-abc-r0-A-era
父节点(种子,没有父节点)
子节点—
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。草稿
状态已打分
分数搜索目标分 52.58 · proxy 53.28 · proxy2 53.28 · X3 51.19
审查未审查
用时?从运行开始到结束(或到现在)的挂钟时间。20 分
程序版本1acc67f55fd5ca0a2c299209879687a865a1ce34 (programs.git)

方法说明?节点程序自带的 METHOD.md:这个程序做了什么、为什么。

没有 METHOD.md。

调研员的计划

名称T1-01 解剖族组成重加权+稀疏表达偏移(draft 完整基线)
动机实验表中 cell_state 是提升空间最大的组:种子节点1仅32.67,当前最佳节点24达58.41(+25.7)。节点20/24证明解剖族重加权(heart×1.6、extraembryonic×0.25、删Neural Tube)+ 稀疏nnz-only表达偏移(ε=0.15)是proxy从50.04→58.48的主路径。de_recovery(51.33→52.41)和covariation(52.56→54.29)仍是最弱组,但本方向以cell_state为主攻。当前树中所有improve链共享同一祖先,本draft建立独立、可复现的完整实现,为后续改进提供新父节点。
做法run.py 分7步:

【1. 输入与阶段识别】用 view_io.inputs_by_time() 获取所有输入阶段。取最新阶段 adata_last 作为抽样基底。若 proxy2 返回两个输入(E8.5 + Qiu E9.0),取第二个(Qiu E9.0)为基底;其缺失基因(27883/32285)用 E8.5 按基因均值填充(view_io 默认行为)。若只有一个输入,直接用它。X3 走恒等路径(copy_last,不修改),因全树无方法移动过X3。

【2. 细胞类型族分类】对 adata_last.obs 中的细胞类型列(自动检测列名,如 'celltype'/'cell_type'/'annotation'),按以下优先级关键词匹配(不区分大小写)分为6族:
- heart_lineage: cardiomyocyte, cm, cardiac, heart, shf, second heart field, endocardium, endocardial, pericardium, proepicardium, jcf, juxtacardiac, epicardium, coronary, v-cm, bec, aphm, pphm, outflow, ofc
- extraembryonic: extraembryonic, exem, exe, exE, visceral endoderm, ve, parietal endoderm, pe, ectoplacental, epc, trophoblast, giant cell, spongiotrophoblast, labyrinth
- surface_ectoderm: surface ectoderm, periderm, epidermis, surface
- paraxial: paraxial, somite, psm, presomitic
- neural_tube: neural tube, neural fold, neuroectoderm, neural crest, nt
- other: 未匹配任何上述关键词的类型(默认族)
注意:'surface' 关键词只在不含 'ectoderm' 之外的神经/内胚层关键词时才匹配 surface_ectoderm,避免误分。final 板上 E9.5 的 V-CM、Endocardium、BEC、aPHM、pPHM、Proepicardium 会被 heart_lineage 族捕获。

【3. 类型级权重】初始权重向量 w_type:heart_lineage=1.6, extraembryonic=0.25, surface_ectoderm=0.25, paraxial=0.25, neural_…
风险1) 类型族关键词在 final E9.5 上可能漏匹配某些类型(如 BEC 可能是 brain endothelial 而非 cardiac),导致权重分配错误——Engineer 应在 proxy 上打印各类型→族映射表,人工确认合理性。2) 稀疏表达偏移的 mu_c 计算依赖类型注释质量;若 final 上类型注释粒度不同,偏移方向可能失效——通过 VEC_EPSA=0 关闭偏移做消融对比可快速定位。3) proxy2 的 Qiu E9.0 只有心脏谱系,extraembryonic/surface 族可能为空,保底逻辑需处理空族。4) 30分钟时限紧张:步骤1-5(组成重加权)约15分钟实现,步骤6(表达偏移)约10分钟,剩余5分钟调试;若超时,优先保证步骤1-5(纯组成重加权已可达~56),偏移作为可选开关。5) 增殖/凋亡基因集若不在 prior/ 中,需硬编码标准列表(如 Tirosh 2016 的 G1/S + G2/M 基因),这不涉及禁窗数据。

代码改动?这个节点的程序和父节点程序的逐行差别:绿色是新增,红色是删除。

对比:这个提交的上一版(种子程序:相对空仓库)。改动的文件:solution/run.py +266 −0

diff --git a/solution/run.py b/solution/run.pynew file mode 100644index 0000000..5ff968a--- /dev/null+++ b/solution/run.py@@ -0,0 +1,266 @@+"""T1 draft: anatomical family composition reweighting + sparse nnz-only expression shifts.++Families by celltype keyword; cell-level modulation (proliferation, apoptosis, marker+typicality); temperature-smoothed weighted sampling with per-type floor; two sparse+additive shifts (type-contrast + proliferation-differentiation) applied only at+existing nonzeros.+"""+import argparse+import os++import numpy as np+import scipy.sparse as sp++import src.task1_temporal.view_io as vio++ENV = {+    "VEC_HEART": 1.6,+    "VEC_EXE": 0.25,+    "VEC_SURFACE": 0.25,+    "VEC_PARAXIAL": 1.0,+    "VEC_NEURAL": 0.0,+    "VEC_BETA": -0.5,     # proliferation cell-level exponent+    "VEC_GAMMA": 0.2,     # apoptosis penalty+    "VEC_RHO": 0.45,      # marker typicality+    "VEC_TEMP": 0.85,     # weight temperature+    "VEC_K": 1,           # per-type floor cells+    "VEC_EPSA": 0.15,     # type-contrast shift+    "VEC_EPSB": 0.15,     # proliferation-differentiation shift+    "VEC_ADDFRAC": 0.5,   # top fraction of |d| genes kept per direction+}+for k in ENV:+    if k in os.environ:+        ENV[k] = float(os.environ[k])++PROLIF = [+    "Mki67", "Top2a", "Cdk1", "Ccna2", "Ccnb1", "Ccne1", "Ccnd1", "Ccnb2",+    "Pcna", "Rrm2", "Bub1", "Plk1", "Aurkb", "Mcm2", "Mcm3", "Mcm5", "Mcm6",+    "Tyms", "Birc5", "Kpna2", "Cdkn2c", "E2f1", "Hmgb2", "Nusap1", "Tuba1b",+]+APOPT = [+    "Bax", "Casp3", "Casp7", "Casp9", "Tp53", "Cdkn1a", "Bbc3", "Gadd45a",+    "Apaf1", "Cdkn2a", "Trp53", "Bcl2l11", "Cflar",+]++FAMILIES = [+    ("heart_lineage", ["cardiomyocyte", "-cm", "cm-", " cm", "cm ", "cardiac", "heart",+                       "shf", "endocardium", "endocardial", "pericardium", "proepicardium",+                       "epicardium", "jcf", "juxtacardiac", "coronary", "v-cm", "bec", "endothel",+                       "aphm", "pphm", "outflow", "oft", "avc", "ift", "sv-cm"]),+    ("extraembryonic", ["extraembryonic", "exem", "exe ", "visceral endoderm", "parietal",+                        "ectoplacental", "trophoblast", "giant cell", "spongio", "labyrinth",+                        "yolk sac", "amnion", "chorion", "allantois"]),+    ("surface_ectoderm", ["surface ectoderm", "periderm", "epidermis", "surface"]),+    ("paraxial", ["paraxial", "somite", "presomitic", "psm"]),+    ("neural_tube", ["neural tube"]),+]+++def classify(ct_names):+    fam = np.full(len(ct_names), "other", dtype=object)+    for i, name in enumerate(ct_names):+        low = str(name).lower()+        for fname, kws in FAMILIES:+            if any(k in low for k in kws):+                fam[i] = fname+                break+    return fam+++def gene_score(X, genes_idx, varnames):+    idx = [genes_idx[g] for g in varnames if g in genes_idx]+    if not idx:+        return np.zeros(X.shape[0], dtype=np.float64)+    sub = X[:, idx]+    if sp.issparse(sub):+        sub = sub.toarray()+    return np.asarray(sub, dtype=np.float64).mean(axis=1)+++def sparse_shift(Xc, d, sigma, eps, addfrac):+    """Add eps * d/(sigma+0.1) at existing nonzeros for top-|d| genes, clip >= 0."""+    if eps == 0 or Xc.nnz == 0:+        return+    k = max(1, int(len(d) * addfrac))+    top = np.argpartition(-np.abs(d), k - 1)[:k]+    shift = np.zeros(len(d), dtype=np.float32)+    shift[top] = (eps * d[top] / (sigma[top] + 0.1)).astype(np.float32)+    csr = Xc.tocsr()+    coo = csr.tocoo()+    s = shift[coo.col]+    nz = s != 0+    coo.data[nz] += s[nz]+    np.clip(coo.data, 0, None, out=coo.data)+    return coo.tocsr()+++def apply_shifts(Xc, types, prolif, sigma, genes_n):+    out = Xc.tocsr().copy()+    if ENV["VEC_EPSA"] > 0:+        mu_all = np.asarray(out.mean(axis=0)).ravel()+        for c in np.unique(types):+            m = types == c+            if m.sum() < 6:+                continue+            mu_c = np.asarray(out[m].mean(axis=0)).ravel()+            d = mu_c - mu_all+            sub = out[m]+            res = sparse_shift(sub, d, sigma, ENV["VEC_EPSA"], ENV["VEC_ADDFRAC"])+            out[m] = res+    if ENV["VEC_EPSB"] > 0:+        for c in np.unique(types):+            m = np.where(types == c)[0]+            if len(m) < 6:+                continue+            med = np.median(prolif[m])+            lo = m[prolif[m] <= med]+            hi = m[prolif[m] > med]+            if len(lo) == 0 or len(hi) == 0:+                continue+            d = np.asarray(out[lo].mean(axis=0)).ravel() - np.asarray(out[hi].mean(axis=0)).ravel()+            res = sparse_shift(out[m], d, sigma, ENV["VEC_EPSB"], ENV["VEC_ADDFRAC"])+            out[m] = res+    return out+++def main():+    ap = argparse.ArgumentParser()+    ap.add_argument("--data", required=True)+    ap.add_argument("--out", required=True)+    ap.add_argument("--seed", type=int, default=0)+    args = ap.parse_args()+    rng = np.random.default_rng(args.seed)++    view = args.data+    m = vio.load_manifest(view)+    genes = vio.panel_genes(view, m)+    genes_idx = {g: i for i, g in enumerate(genes)}++    official = [e for e in vio.inputs_by_time(m) if e.get("source") != "external"]+    if not official:+        # External-only board (e.g. X3): identity copy_last path.+        ins = vio.inputs_by_time(m)+        a = vio.read_stage(view, ins[-1], genes)+        n = vio.target_n_cells(m, a.n_obs)+        if a.n_obs > n:+            sel = rng.choice(a.n_obs, size=n, replace=False)+        else:+            sel = np.arange(a.n_obs)+        X = a[sel].X+        X = sp.csr_matrix(X) if sp.issparse(X) else sp.csr_matrix(X)+        X = X.astype(np.float32)+        X.data[~np.isfinite(X.data)] = 0.0+        vio.write_prediction(X, genes, args.out, seed=args.seed)+        return++    a = vio.read_stage(view, official[-1], genes)+    X = a.X+    X = sp.csr_matrix(X).astype(np.float32)++    types = np.asarray(a.obs["celltype"].astype(str)) if "celltype" in a.obs else np.zeros(a.n_obs, dtype=object)+    uniq, tcode = np.unique(types, return_inverse=True)+    fam = classify(uniq)+    fam_w = {"heart_lineage": ENV["VEC_HEART"], "extraembryonic": ENV["VEC_EXE"],+             "surface_ectoderm": ENV["VEC_SURFACE"], "paraxial": ENV["VEC_PARAXIAL"],+             "neural_tube": ENV["VEC_NEURAL"], "other": 1.0}+    type_w = np.array([fam_w[f] for f in fam])++    w = type_w[tcode].astype(np.float64).copy()++    live = w > 0+    if live.sum() == 0:+        live = np.ones(a.n_obs, dtype=bool)++    # cell-level modulation on live cells+    prolif = gene_score(X, genes_idx, PROLIF)+    apopt = gene_score(X, genes_idx, APOPT)++    if ENV["VEC_BETA"] != 0:+        ratio = np.ones(a.n_obs)+        for c in range(len(uniq)):+            mm = np.where((tcode == c) & live)[0]+            if len(mm) == 0:+                continue+            tm = max(prolif[mm].mean(), 1e-9)+            r = tm / np.maximum(prolif[mm], 1e-9)+            r = np.clip(r, 0.25, 4.0)+            ratio[mm] = r ** ENV["VEC_BETA"]+        w *= ratio++    if ENV["VEC_GAMMA"] > 0:+        av = apopt[live]+        if av.max() > av.min():+            an = (apopt - av.min()) / (av.max() - av.min())+        else:+            an = np.zeros(a.n_obs)+        w *= (1.0 - ENV["VEC_GAMMA"] * np.clip(an, 0, 1))++    if ENV["VEC_RHO"] != 0:+        Xd = X+        for c in range(len(uniq)):+            mm = np.where((tcode == c) & live)[0]+            if len(mm) < 6:+                continue+            mu_c = np.asarray(Xd[mm].mean(axis=0)).ravel()+            top = np.argpartition(-mu_c, 49)[:50]+            sub = Xd[:, top]+            sub = sub.toarray() if sp.issparse(sub) else np.asarray(sub)+            mu = mu_c[top]+            v_all = np.asarray(Xd[:, top].power(2).mean(axis=0)).ravel() - mu ** 2+            sd = np.sqrt(np.maximum(v_all, 1e-12))+            z = (sub - mu) / sd+            zi = z.mean(axis=1)+            w[mm] *= np.clip(1.0 + ENV["VEC_RHO"] * zi[mm], 0.1, 10.0)++    w = np.where(live, np.maximum(w, 0.0), 0.0)+    if ENV["VEC_TEMP"] > 0:+        w = w ** ENV["VEC_TEMP"]++    n = vio.target_n_cells(m, a.n_obs)+    n = min(n, a.n_obs)+    wsum = w.sum()+    if wsum <= 0:+        sel = rng.choice(a.n_obs, size=n, replace=False)+    else:+        # per-type floor+        forced = []+        k = max(0, int(ENV["VEC_K"]))+        if k > 0:+            for c in range(len(uniq)):+                mm = np.where((tcode == c) & live)[0]+                if len(mm) == 0 or w[mm].sum() <= 0:+                    continue+                p = w[mm] / w[mm].sum()+                forced.extend(rng.choice(mm, size=min(k, len(mm)), replace=False, p=p).tolist())+        forced = np.array(sorted(set(forced)), dtype=int)+        rest_mask = np.ones(a.n_obs, dtype=bool)+        rest_mask[forced] = False+        rest_idx = np.where(rest_mask & live)[0]+        n_rest = max(0, n - len(forced))+        n_rest = min(n_rest, len(rest_idx))+        if n_rest > 0 and rest_idx.size > 0:+            p = w[rest_idx]+            p = p / p.sum()+            rest_sel = rng.choice(rest_idx, size=n_rest, replace=False, p=p)+        else:+            rest_sel = np.array([], dtype=int)+        sel = np.concatenate([forced, rest_sel]).astype(int)+        sel = np.sort(sel)++    Xs = sp.csr_matrix(X[sel])++    sigma = np.asarray(Xs.power(2).mean(axis=0)).ravel()+    mu = np.asarray(Xs.mean(axis=0)).ravel()+    var = np.maximum(sigma - mu ** 2, 0.0)+    sigma = np.sqrt(var).astype(np.float32)++    stypes = tcode[sel]+    sprof = prolif[sel]+    Xs = apply_shifts(Xs, stypes, sprof, sigma, len(genes))+    Xs.data[~np.isfinite(Xs.data)] = 0.0++    vio.write_prediction(Xs, genes, args.out, seed=args.seed)+++if __name__ == "__main__":+    main()

调研来源?调研员查到并用到的知识条目和文献检索结果(只列标题和编号)。

用到的知识库条目

编号标题出处
k012Official T1 scoring, output contract and adversarial controlsnotes/official/来件/virtualembryo.ai/task1-temporal.md; notes/official/来件/virtualembryo.ai/baselines.md
k007Interval staging and held-out-window filtering of external datanotes/official/来件/virtualembryo.ai/rules.md
k031Offline OT toolkit in the sandbox: moscot TemporalProblem, wot OTModel, POT, geomloss10.1038/s41586-024-08453-2 (moscot); 10.1016/j.cell.2019.01.006 (Waddington-OT)

分析结果?分析员写的 ANALYSIS.json:改了什么、各组分数怎么变、假设是否成立、经验和下一步建议。

改了什么从空目录新建 solution/run.py(266 行):按 celltype 关键词分 6 族做类型级权重重加权 + 细胞级调制(增殖 beta=-0.5、凋亡 gamma=0.2、标记典型性 rho=0.45)+ 温度 T=0.85 平滑的加权无放回抽样(每类型保底 k=1)+ 两个 nnz-only 稀疏加性偏移(类型对比 epsA=0.15、增殖分化 epsB=0.15,各取 |d| 前 50% 基因);X3/无官方输入走恒等 copy_last 下采样路径。
各组分数的变化X3:51.19 vs 40.53(+10.66)——恒等路径本身就贡献约 +10.66,说明'按目标细胞数下采样最新阶段'是免费收益
cell_state:变好但远低于目标:50.98 vs 32.67(+18.31);PLAN 期望 ≥54、树内节点24 已达 58.41
covariation:变好:51.22 vs 22.56(+28.66)
de_recovery:噪声内偏弱:50.31 vs 49.13(+1.18,T1 噪声约 2 分)
direction:变好:57.87 vs 50.96(+6.91)
proxy:53.28 vs 50.04(+3.24),未达 PLAN 设定的 ≥56 门槛,比最佳节点24 的 58.48 低约 5 分
proxy2:53.28 vs 27.43(+25.85)
runtime:51.3s vs 1.9s;内存峰值 2.09GB vs 1.48GB,均安全
假设是否成立unclear
经验
  1. 重写基线时若偏离已验证配置就丢分:本节点把 paraxial 权重写成 1.0(PLAN 要求 0.25),且 neural 族关键词只剩 'neural tube'(PLAN 还列了 neural crest/neuroectoderm/nt),结果 cell_state 只到 50.98,比节点24 的 58.41 低约 7.4 分——同一思路的完整重实现并不自动复现最佳分数。
  2. 宽泛子串关键词会误捕:heart_lineage 用了 'bec'、'endothel',会把 brain/其他内皮当成心脏谱系并 ×1.6 上调;Engineer 自己也报告 family 映射有误(NCC 被错误处理),说明族映射必须先打印再信任。
  3. Engineer 自报的 52.87 与变化量表不符(榜分 52.58、proxy 53.28),且它把结果判为 'below target' 后仍在改映射,报告口径应以变化量表为准。
  4. nnz-only 稀疏偏移 + 组成重加权的组合在 covariation(+28.66)和 proxy2(+25.85)上收益最大,但在 de_recovery 上只有 +1.18(噪声内),说明该方向几乎不改善 DE 恢复。
  5. X3 恒等路径拿到 51.19(对照 40.53),任何 T1 方案都不应让 X3 走会产生偏移/重加权的分支。
  6. 全流程 51.3s / 2.09GB,性能不是瓶颈,可以把时间预算花在参数扫描而非优化实现上。
下一步建议
  1. 以本节点为父做 improve(针对 cell_state):把 ENV['VEC_PARAXIAL'] 从 1.0 改回 0.25,并给 neural 族补上 'neural crest'/'ncc'/'neuroectoderm'/'nt' 关键词使 VEC_NEURAL=0 真正生效,预期把 cell_state 从 50.98 推向节点24 的 58 量级。
  2. 在 proxy 视图上打印每个 celltype→family 的映射表(针对 cell_state、direction):确认 'bec'/'endothel'/' cm'/'-cm' 没有把非心脏内皮或无关类型拉进 heart_lineage;若有,改成带词边界的正则精确匹配(bec 前后加词边界)。
  3. 做细胞级调制消融(针对 proxy/cell_state):固定族权重与偏移,用环境变量跑 VEC_BETA=0、VEC_RHO=0、VEC_GAMMA=0 三组单独关闭,每组用 proxy 单视图查分,差 <2 分视为噪声,以定位这 5 分差距是来自调制项还是族权重。
  4. 若两代内 cell_state 仍 <55,改为直接以节点24(proxy 58.41)为父节点做增量改进,不再维护本独立重实现分支。

对话摘要?每个角色和大模型对话的统计:轮数、工具调用、用时、token 数和最后的回答摘录;原始记录只给路径。

只给统计和最后回答的摘录;完整对话请到原始记录位置里列出的文件看。

分析员

角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。分析员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数?大模型一共回复了几轮(每轮可以调用多个工具)。6
工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。共 5 次:bash 3、write 1、edit 1
用时1 分
token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。输入 12,670 · 输出 1,674 · 思考 882
任务(第一行)分析节点 n27 的打分结果,写 ANALYSIS.json
最后的回答(摘录)
ANALYSIS.json written
原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/analyst.jsonl 20 KB
/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/analyst.stderr

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次(失败 1 次)
轮数16
工具调用共 17 次:bash 15、write 1、edit 1
用时11 分
token 数输入 36,589 · 输出 7,568 · 思考 7,129
任务(第一行)按计划修改程序:T1-01 解剖族组成重加权+稀疏表达偏移(draft 完整基线)
最后的回答(摘录)
52.87 — below target. Fixing family mapping (NCC wrongly deleted, paraxial down-weighted vs. proven config):
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/engineer.jsonl 63 KB
/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/engineer.stderr

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数1
工具调用共 0 次:—
用时6 分
token 数输入 11,267 · 输出 7,990 · 思考 8,192
任务(第一行)为节点 n27(草稿,父节点 nNone)写改进计划 PLAN.json,思路 T1-01
最后的回答(摘录)
、Proepicardium 会被 heart_lineage 族捕获。\n\n【3. 类型级权重】初始权重向量 w_type:heart_lineage=1.6, extraembryonic=0.25, surface_ectoderm=0.25, paraxial=0.25, neural_tube=0.0, other=1.0。neural_tube 权重为0意味着该类型不抽样、不占保底名额。若某族在数据中无细胞,跳过。\n\n【4. 细胞级权重调制】在类型权重基础上叠加细胞级因子(不改表达值,只影响抽样概率):\n(a) 增殖评分:用预定义小鼠细胞周期基因集(从 prior/ 或硬编码标准列表)对每个细胞打分 s_i(取增殖相关基因均值表达)。细胞权重乘 (s_typemean / s_i)^beta,beta=-0.5,即低增殖细胞权重更高。clip比值到[0.25, 4]。\n(b) 凋亡罚分:用凋亡基因集打分 a_i,权重乘 (1 - gamma*a_normalized),gamma=0.2。\n(c) 典型性:对每个类型,用该类型标记基因(类型内均值表达前50基因)计算每细胞的z-score均值 z_i,权重乘 (1 + rho*z_i),rho=0.45,clip到[0.1, 10]。\n\n【5. 温度平滑与保底】对最终细胞权重做温度平滑:w_i = w_i^T,T=0.85。然后按类型做保底抽样:每个权重和>0的类型至少保留 k=1 个细胞(按权重随机选)。总抽样目标 N = min(adata_last.n_obs, 目标细胞数),按归一化权重无放回抽样。若总细胞数已小于N,保留全部。\n\n【6. 稀疏表达偏移(可选,默认启用)】抽样后对表达矩阵做两个nnz-only加性偏移(参照节点22/24):\n偏移A(类型对比):对每个类型c,方向向量 d_c = mu_c - mu_all(类型均值减全数据均值),只取|d_c|前50%基因(VEC_ADDFRAC=0.5),shift = eps * d_c / (sigma_g + 0.1),eps=0.15(VEC_EPSA=0.15)。sigma_g 是基因g的全局标准差。只加到CSR矩阵已有非零位置(nnz不变),clip>=0。\n偏移B(增殖分化):在每个类型内按增殖评分中位数分两半,方向 = mean(低增殖半) - mean(高增殖半),同样取前50%基因、σ归一、nnz-only、eps=0.15(VEC_DIFF=0.15)。\n对细胞数<6的类型跳过偏移。X3恒等路径不做偏移。\n\n【7. 输出】构造AnnData,基因顺序按视图 genes.txt,无标签列。对 proxy2 的缺失基因位点,输出时用E8.5均值填充(与输入处理一致)。\n\n【快速筛选】Engineer 先只跑 proxy 视图(单输入,最快),用 vec-score 查分。预期 proxy ≥ 56(超过种子55.97)。若 cell_state 组 ≥ 54,说明组成重加权生效;若 < 50,检查类型族分类是否匹配到细胞(打印各族细胞数)。然后跑 proxy2 确认双输入路径不报错。最后跑 X3 确认恒等路径输出50。全部通过后提交。\n\n【参数搜索(若时间允许)】优先扫 VEC_EPSA ∈ {0.10, 0.15, 0.20} 和 heart_weight ∈ {1.4, 1.6, 1.8},每次用 proxy 单视图查分,差距<2分视为噪声。",
  "expected_groups": ["cell_state", "covariation"],
  "risks": "1) 类型族关键词在 final E9.5 上可能漏匹配某些类型(如 BEC 可能是 brain endothelial 而非 cardiac),导致权重分配错误——Engineer 应在 proxy 上打印各类型→族映射表,人工确认合理性。2) 稀疏表达偏移的 mu_c 计算依赖类型注释质量;若 final 上类型注释粒度不同,偏移方向可能失效——通过 VEC_EPSA=0 关闭偏移做消融对比可快速定位。3) proxy2 的 Qiu E9.0 只有心脏谱系,extraembryonic/surface 族可能为空,保底逻辑需处理空族。4) 30分钟时限紧张:步骤1-5(组成重加权)约15分钟实现,步骤6(表达偏移)约10分钟,剩余5分钟调试;若超时,优先保证步骤1-5(纯组成重加权已可达~56),偏移作为可选开关。5) 增殖/凋亡基因集若不在 prior/ 中,需硬编码标准列表(如 Tirosh 2016 的 G1/S + G2/M 基因),这不涉及禁窗数据。"
}
```
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/researcher.jsonl 29 KB
/home/spark-longxinyang/vec/runs/formal/20261001-233756-search-t1-abc-r0-A-era/nodes/27/researcher.stderr