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

总览 · ← 返回运行 20261003-171955-search-t2-embryo-interp-chain-12h

节点 n23

T2EI-04 块抽样:以空间 k-NN 块(种子+14 近邻,k=15)为抽样单元、按块主类型分层以 (1−t):t 从两端阶段混抽,表达与坐标原样保留;jitter 关闭。

运行?一次完整的自动搜索或 Agent 会话,有自己的锁定配置和证据包。20261003-171955-search-t2-embryo-interp-chain-12h
父节点(种子,没有父节点)
子节点n26
操作?种子:人写的起点;改进:在父节点上改;草稿:从头写;修复:修父节点的报错。草稿
状态已打分
分数搜索目标分 58.40 · proxy 58.40
审查未审查
用时?从运行开始到结束(或到现在)的挂钟时间。27 分
程序版本32a898b8f62e04162656aea52677b98e56eb544b (programs.git)
导入自20261003-094242-search-t2-embryo-interp-g24-D-s2#23
备注re-scored at launch (origin 20261003-094242-search-t2-embryo-interp-g24-D-s2 node 23, score there 58.40)

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

来自 programs.git 32a898b8f6:solution/METHOD.md

T2EI-04 块抽样:以空间 k-NN 块(种子+14 近邻,k=15)为抽样单元、按块主类型分层以 (1−t):t 从两端阶段混抽,表达与坐标原样保留;jitter 关闭。

方法(family T2EI-04,最小版本)

插值榜:interp_bracket 取目标两侧输入 (a, b, t)。

  1. align_pair(procrustes3d) 把两朵坐标云放进同一帧,再各自均匀缩放到目标 RMS = log 插值(scale_damp=0.5)。
  2. 每侧建块:随机顺序遍历细胞,未覆盖者当种子,块 = 种子 + 其 kNN 中尚未覆盖的至多 k−1 个近邻(k=15,与评分器 15-近邻含自身的定义一致;kNN 在对齐后坐标上用 cKDTree 算一次)。整侧形成互不重叠的块池(覆盖全部细胞)。
  3. 抽块:按块的主类型(块内众数)做类型分层——各类型配额 ∝ 该侧池内该类型细胞数 × n_side,块在类型内随机排列依次整块取出,超额的最后一块截断(保留离种子最近的细胞)。n_side 按 (1−t):t 分配(n_b = round(t·n)),总细胞数 n = log 插值夹到 [min_cells, max_cells]。
  4. 组装:块内表达(源细胞 CSR 行原样)与坐标原样拼入输出;可选整块刚性平移 jitter(幅度 α×平均最近邻距离),默认 α=0(关闭)。
  5. 输出云整体均匀缩放到目标 RMS,write_t2 写出。表达数值全程不修改。
  6. 单输入退路:目标不被括号时全部块取自 anchor 阶段,RMS 不变。

关键参数(默认,均可用环境变量覆盖,提交保持默认)

  • T2EI04_K=15(块大小);T2EI04_ALPHA=0.0(jitter,关闭);T2EI04_SCALE_DAMP=0.5;T2EI04_ALIGN=procrustes3d。
  • 随机性全部来自 np.random.default_rng(seed):种子顺序、块抽取、jitter;同 seed 逐位确定(已验证与显式 α=0 运行 X、坐标逐位相同)。

机制对照(mechanism_off_control:T2EI04_K=1,其余流程完全相同)

配置榜分local_spatialnbhd rawshape_scale
k=15(机制开,seed 0)57.8056.160.059778.68
k=15(机制开,seed 1)57.5357.720.056175.57
k=1(机制关,seed 0)57.1354.590.063576.37
  • 机制生效证据 (b):输出中每个细胞 15 近邻的同侧(同来源阶段)占比 k=15 为 0.906,k=1 为 0.813——块确实把同阶段细胞聚在一起。
  • 机制生效证据 (a):neighborhood_mmd raw 0.0597/0.0561(开)vs 0.0635(关),local_spatial 组 +1.6~+3.1,两个 seed 一致;k=10(56.60)、k=25(56.69)同样高于 k=1,说明是块机制而非单个 k 值的巧合。
  • (c)(d):mmd_u、组成未受分层块抽取损害(cell_state 组 36.2/36.7 vs 关 37.3,差异在噪声内);d2_shape/occupancy_dice 未因块间空隙劣化(shape 组两次运行 75.6–78.7,与关对照同区间)。
  • 总分差 +0.4~+0.7 小于噪声(~1 分),但机制的目标组 local_spatial 提升一致且方向明确。k=1 对照 57.13 与全树 mix 基线(节点 2 = 57.23)复现一致,说明对照确实退化为标准 mix。

验证过 / 没验证

  • 验证过(proxy 视图,A 半,共用 9 次查分):k∈{1,10,15,25}、jitter α∈{0,0.3}、块池 {FPS 顺序取块, FPS 池随机抽, 全池随机抽+类型分层}。教训:FPS 早停块池偏向外周(nbhd 0.072–0.080,明显差),改为全阶段块池后恢复并超过 mix 水平;jitter α=0.3 轻微劣化 local_spatial(55.99 vs 56.16),故默认关闭。seed 0/1 均跑通且分数稳定(57.8/57.5)。单输入退路在合成视图上跑通并通过 vec-check。运行 <5 s、内存远低于上限,EXECUTION.json 声明纯 CPU。
  • 没验证:真实括号(E7.25+E8.0,t=1/3)上的表现(无真值);k 在 15–25 间更细的扫描;块抽样与节点 5/8/11/13 的类型级表达位移叠加的效果(PLAN 明确要求本 draft 不混入表达修改,留给 improve)。
  • 与全树比较:本 draft 高于 mix 基线(节点 2,57.23),低于表达位移家族(节点 13,62.52);预期 improve 节点在本块抽样管线上叠加节点 11/13 的表达管线可同时拿到 62 级表达分与更高的 local_spatial。

知识来源

  • 未使用任何生物学先验数据或文献知识;k=15 的选择仅来自任务书公开的评分简报中 neighborhood_mmd 的“15 个最近邻(含自身)”定义,procrustes3d/log-RMS/scale_damp=0.5 来自任务书方法卡的推荐配置。未使用保留阶段/基因型的任何信息。

调研员的计划

名称T2EI-04 保邻域块抽样:以空间近邻块为单位混抽两端阶段
动机当前最佳节点13的local_spatial=60.34(neighborhood_mmd raw 0.05112),而mix基线(节点2)仅57.27。评分简报明确指出:保留真实细胞表达-坐标配对时邻域组稳定52-55,OT插值新点则掉到23-41。节点5/8/10/11/13的表达管线已把expression_change推到62.11,但邻域项始终是短板。结构门=shape_skill×min(nbhd_skill/0.5,1),邻域skill若从~0.55提到>0.6,形状组额外获益。T2EI-04的核心假设:独立混抽破坏局部空间一致性(来自不同阶段的细胞互为近邻),以空间块为单位抽样可保持块内表达-坐标配对,直接改善neighborhood_mmd。
做法1) 读取两端输入阶段(a,b)的表达矩阵与spatial_3D坐标。
2) Procrustes 3D对齐:把b侧坐标刚性对齐到a侧(旋转+平移,不缩放),使两端坐标在同一参考系。
3) 构建块:对每侧分别计算3D坐标的k-NN(k=15,与评分器的15-近邻一致)。用贪心最远点采样(FPS)选出N_seed个种子细胞,每个种子与其k个近邻构成一个块(去重:已被其他块包含的细胞不再当种子)。初始N_seed按目标细胞数/(k+1)估算,实际用覆盖-去重循环调到目标数。
4) 按(1−t):t比例从a/b侧抽取块:t=0.5时各半。用分层策略保证每侧的空间均匀性(FPS已保证)。
5) 组装输出:把选中块的表达和坐标原样拼入输出。块内坐标保持相对位移不变。
6) 块间jitter:对每个块整体加一个随机平移向量,幅度=α×块内平均最近邻距离(初始α=0.3,搜索[0.1,1.0]),防止块间精确重叠。
7) 全局RMS缩放:把输出云均匀缩放到目标RMS(取a侧RMS的(1−t)+b侧RMS的t的log插值),保持坐标单位一致。
8) 表达完全不动(原样保留源细胞的表达值),不施加任何位移或缩放。
9) 单输入阶段退路:若只有一个阶段,全部块从该阶段抽取,无混合,jitter和RMS缩放仍执行。
10) vec-score快速筛选:先在子样(每侧1500细胞、k=15)上跑3组参数(α=0.1/0.3/1.0),确认neighborhood_mmd优于独立混抽后再全量。查分上限20次,前6次用于参数扫描(k∈{10,15,25}×α∈{0.3,1.0}),保留余量给最优配置的多seed确认。
风险1) 块间边界不自然:不同阶段的块拼接处可能出现表达-坐标不连续,反而拉高neighborhood_mmd。Engineer应在小样上先比较块抽样与独立混抽的neighborhood_mmd raw,若块抽样反而更差则立即停止。2) FPS种子选取可能偏向高密度区域,导致低密度区域细胞缺失、组成偏移,损害mmd_u。对策:FPS后检查输出细胞数是否达标,不足时降低k或增加种子数。3) 表达组可能略低于节点13(62.11),因为本方案不施加类型级表达位移,预期接近mix水平(~59-60)。若表达组降幅>2分,应在后续improve节点叠加表达位移而非在本draft中混入。4) 块太大(k过大)会让输出变成若干孤立团,破坏整体形状(d2_shape、occupancy_dice劣化);k=15是与评分器对齐的初始值,不应超过30。

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

对比:这个提交的上一版(种子程序:相对空仓库)。改动的文件:solution/EXECUTION.json +1 −0、solution/METHOD.md +40 −0、solution/run.py +220 −0

diff --git a/solution/EXECUTION.json b/solution/EXECUTION.jsonnew file mode 100644index 0000000..9d5125c--- /dev/null+++ b/solution/EXECUTION.json@@ -0,0 +1 @@+{"gpu": false}diff --git a/solution/METHOD.md b/solution/METHOD.mdnew file mode 100644index 0000000..e5a4f1d--- /dev/null+++ b/solution/METHOD.md@@ -0,0 +1,40 @@+T2EI-04 块抽样:以空间 k-NN 块(种子+14 近邻,k=15)为抽样单元、按块主类型分层以 (1−t):t 从两端阶段混抽,表达与坐标原样保留;jitter 关闭。++# 方法(family T2EI-04,最小版本)++插值榜:`interp_bracket` 取目标两侧输入 (a, b, t)。++1. `align_pair(procrustes3d)` 把两朵坐标云放进同一帧,再各自均匀缩放到目标 RMS = log 插值(scale_damp=0.5)。+2. 每侧建块:随机顺序遍历细胞,未覆盖者当种子,块 = 种子 + 其 kNN 中尚未覆盖的至多 k−1 个近邻(k=15,与评分器 15-近邻含自身的定义一致;kNN 在对齐后坐标上用 cKDTree 算一次)。整侧形成互不重叠的块池(覆盖全部细胞)。+3. 抽块:按块的**主类型**(块内众数)做类型分层——各类型配额 ∝ 该侧池内该类型细胞数 × n_side,块在类型内随机排列依次整块取出,超额的最后一块截断(保留离种子最近的细胞)。n_side 按 (1−t):t 分配(n_b = round(t·n)),总细胞数 n = log 插值夹到 [min_cells, max_cells]。+4. 组装:块内表达(源细胞 CSR 行原样)与坐标原样拼入输出;可选整块刚性平移 jitter(幅度 α×平均最近邻距离),**默认 α=0(关闭)**。+5. 输出云整体均匀缩放到目标 RMS,`write_t2` 写出。表达数值全程不修改。+6. 单输入退路:目标不被括号时全部块取自 anchor 阶段,RMS 不变。++## 关键参数(默认,均可用环境变量覆盖,提交保持默认)++- `T2EI04_K=15`(块大小);`T2EI04_ALPHA=0.0`(jitter,关闭);`T2EI04_SCALE_DAMP=0.5`;`T2EI04_ALIGN=procrustes3d`。+- 随机性全部来自 `np.random.default_rng(seed)`:种子顺序、块抽取、jitter;同 seed 逐位确定(已验证与显式 α=0 运行 X、坐标逐位相同)。++## 机制对照(mechanism_off_control:T2EI04_K=1,其余流程完全相同)++| 配置 | 榜分 | local_spatial | nbhd raw | shape_scale |+|---|---:|---:|---:|---:|+| k=15(机制开,seed 0) | **57.80** | 56.16 | 0.0597 | 78.68 |+| k=15(机制开,seed 1) | 57.53 | 57.72 | 0.0561 | 75.57 |+| k=1(机制关,seed 0) | 57.13 | 54.59 | 0.0635 | 76.37 |++- 机制生效证据 (b):输出中每个细胞 15 近邻的**同侧(同来源阶段)占比** k=15 为 0.906,k=1 为 0.813——块确实把同阶段细胞聚在一起。+- 机制生效证据 (a):neighborhood_mmd raw 0.0597/0.0561(开)vs 0.0635(关),local_spatial 组 +1.6~+3.1,两个 seed 一致;k=10(56.60)、k=25(56.69)同样高于 k=1,说明是块机制而非单个 k 值的巧合。+- (c)(d):mmd_u、组成未受分层块抽取损害(cell_state 组 36.2/36.7 vs 关 37.3,差异在噪声内);d2_shape/occupancy_dice 未因块间空隙劣化(shape 组两次运行 75.6–78.7,与关对照同区间)。+- 总分差 +0.4~+0.7 小于噪声(~1 分),但机制的目标组 local_spatial 提升一致且方向明确。k=1 对照 57.13 与全树 mix 基线(节点 2 = 57.23)复现一致,说明对照确实退化为标准 mix。++## 验证过 / 没验证++- 验证过(proxy 视图,A 半,共用 9 次查分):k∈{1,10,15,25}、jitter α∈{0,0.3}、块池 {FPS 顺序取块, FPS 池随机抽, 全池随机抽+类型分层}。教训:FPS 早停块池偏向外周(nbhd 0.072–0.080,明显差),改为全阶段块池后恢复并超过 mix 水平;jitter α=0.3 轻微劣化 local_spatial(55.99 vs 56.16),故默认关闭。seed 0/1 均跑通且分数稳定(57.8/57.5)。单输入退路在合成视图上跑通并通过 vec-check。运行 <5 s、内存远低于上限,`EXECUTION.json` 声明纯 CPU。+- 没验证:真实括号(E7.25+E8.0,t=1/3)上的表现(无真值);k 在 15–25 间更细的扫描;块抽样与节点 5/8/11/13 的类型级表达位移叠加的效果(PLAN 明确要求本 draft 不混入表达修改,留给 improve)。+- 与全树比较:本 draft 高于 mix 基线(节点 2,57.23),低于表达位移家族(节点 13,62.52);预期 improve 节点在本块抽样管线上叠加节点 11/13 的表达管线可同时拿到 62 级表达分与更高的 local_spatial。++## 知识来源++- 未使用任何生物学先验数据或文献知识;k=15 的选择仅来自任务书公开的评分简报中 neighborhood_mmd 的“15 个最近邻(含自身)”定义,procrustes3d/log-RMS/scale_damp=0.5 来自任务书方法卡的推荐配置。未使用保留阶段/基因型的任何信息。diff --git a/solution/run.py b/solution/run.pynew file mode 100644index 0000000..922b6c8--- /dev/null+++ b/solution/run.py@@ -0,0 +1,220 @@+#!/usr/bin/env python+"""T2EI-04: neighbourhood-preserving spatial block sampling for stage interpolation.++Sampling unit = a spatial block (random uncovered seed + its k-1 nearest+uncovered neighbours, k=15 by default, matching the scorer's 15-nearest-neighbour+definition). Blocks are drawn from each bracket stage in a (1-t):t cell ratio,+type-stratified by each block's dominant cell type; expression and coordinates+are carried over verbatim so the local expression-position pairing inside each+block is never broken. An optional per-block rigid jitter (alpha x mean+nearest-neighbour distance, default 0 = off) can de-overlap blocks; the assembled+cloud is uniformly rescaled to a damped log-interpolated target RMS. Expression+values are never modified.++Mechanism-off control: set T2EI04_K=1 (each block is the seed alone) which+degenerates the pipeline to independent type-stratified cell mixing (standard mix).+"""++from __future__ import annotations++import argparse+import json+import os+from pathlib import Path++import numpy as np+from scipy.spatial import cKDTree++from src.task2_spatial import view_io+from src.task2_spatial.frame import align_pair, log_interp, rms_radius, scale_to_rms+from src.task2_spatial.sample import clip_n, interp_count+++def _env(name, default, cast):+    raw = os.environ.get(name)+    if raw is None or raw == "":+        return default+    return cast(raw)+++def fps_seeds(coords: np.ndarray, rng: np.random.Generator, max_seeds: int):+    """Lazy farthest-point-sampling seed order (deterministic given rng)."""+    n = len(coords)+    min_d = np.full(n, np.inf)+    cur = int(rng.integers(n))+    for _ in range(min(int(max_seeds), n)):+        yield cur+        d = ((coords - coords[cur]) ** 2).sum(axis=1)+        np.minimum(min_d, d, out=min_d)+        cur = int(np.argmax(min_d))+++def seed_order(coords: np.ndarray, rng: np.random.Generator, mode: str):+    if mode == "fps":+        return fps_seeds(coords, rng, len(coords))+    return (int(i) for i in rng.permutation(len(coords)))+++def build_blocks(coords: np.ndarray, k: int, rng: np.random.Generator, cover: int,+                 seeds: str = "random"):+    """FPS block partition: each uncovered seed claims its uncovered (k-1) nearest neighbours.++    Stops once `cover` cells are blocked (FPS seeds are space-filling, so early+    blocks spread over the whole stage; blocks are drawn at random downstream).+    """+    n = len(coords)+    k = int(max(1, min(k, n)))+    cover = int(min(max(cover, 1), n))+    tree = cKDTree(coords)+    nbrs = tree.query(coords, k=k)[1]+    if nbrs.ndim == 1:+        nbrs = nbrs[:, None]+    if n > 1:+        nn = tree.query(coords, k=2)[1]+        d_nn = float(np.sqrt(((coords[nn[:, 0]] - coords[nn[:, 1]]) ** 2).sum(axis=1)).mean())+    else:+        d_nn = 0.0+    covered = np.zeros(n, dtype=bool)+    blocks: list[np.ndarray] = []+    total = 0+    for s in seed_order(coords, rng, seeds):+        if total >= cover:+            break+        if covered[s]:+            continue+        mem = nbrs[s][~covered[nbrs[s]]]+        if mem.size == 0:+            continue+        covered[mem] = True+        blocks.append(mem)+        total += int(mem.size)+    if total < cover:+        rest = np.flatnonzero(~covered)+        if rest.size:+            blocks.append(rest)+    return blocks, d_nn+++def draw_blocks(blocks, need: int, rng: np.random.Generator, labels: np.ndarray | None = None):+    """Random draw of whole blocks until `need` cells; last block trimmed (closest to seed).++    With `labels`, blocks are drawn type-stratified: each block is charged to its+    dominant cell type and per-type quotas follow the stage's own type fractions,+    so the output composition mirrors a stratified cell-level draw.+    """+    if labels is None:+        order = list(rng.permutation(len(blocks)))+    else:+        dom = []+        by_type: dict[str, list[int]] = {}+        for bi, m in enumerate(blocks):+            labs, cnt = np.unique(np.asarray(labels)[m], return_counts=True)+            d = str(labs[int(np.argmax(cnt))])+            dom.append(d)+            by_type.setdefault(d, []).append(bi)+        types = sorted(by_type)+        fracs = np.array([len(by_type[t_]) for t_ in types], dtype=np.float64)+        # quota over blocks weighted by block sizes: allocate cells per type+        cell_counts = np.array([sum(int(blocks[bi].size) for bi in by_type[t_]) for t_ in types],+                               dtype=np.float64)+        quota = np.floor(cell_counts / max(cell_counts.sum(), 1.0) * need).astype(int)+        quota[-1] += need - quota.sum()+        order = []+        for qi, t_ in enumerate(types):+            cand = list(rng.permutation(len(by_type[t_])))+            filled = 0+            for ci in cand:+                if filled >= quota[qi]:+                    break+                order.append(by_type[t_][ci])+                filled += int(blocks[by_type[t_][ci]].size)+        # top up with any unused blocks (random order) to guarantee reaching `need`+        used = set(order)+        spare = [bi for bi in range(len(blocks)) if bi not in used]+        rng.shuffle(spare)+        order.extend(spare)+    picked, tot = [], 0+    for bi in order:+        if tot >= need:+            break+        m = blocks[bi]+        if tot + m.size > need:+            m = m[: need - tot]+        picked.append(m)+        tot += int(m.size)+    return picked+++def assemble(coords: np.ndarray, picked, alpha: float, d_nn: float, rng: np.random.Generator):+    """Concatenate picked blocks with per-block rigid jitter; return (idx, coords)."""+    idx_list, coord_parts = [], []+    for m in picked:+        idx_list.append(m)+        c = coords[m]+        if alpha > 0.0 and d_nn > 0.0:+            c = c + rng.normal(0.0, alpha * d_nn, size=3)+        coord_parts.append(c)+    idx = np.concatenate(idx_list).astype(np.int64)+    C = np.vstack(coord_parts)+    return idx, C+++def main() -> None:+    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()++    k = _env("T2EI04_K", 15, int)+    alpha = _env("T2EI04_ALPHA", 0.0, float)+    damp = _env("T2EI04_SCALE_DAMP", 0.5, float)+    align = _env("T2EI04_ALIGN", "procrustes3d", str)++    view = args.data+    manifest = json.loads((Path(view) / "manifest.json").read_text(encoding="utf-8"))+    genes = view_io.panel_genes(view, manifest)+    lo, hi = int(manifest["min_cells"]), int(manifest["max_cells"])+    rng = np.random.default_rng(int(args.seed))++    a_entry, b_entry, t = view_io.interp_bracket(manifest)+    stage_a = view_io.read_stage(view, a_entry, genes)++    if b_entry is None:+        n = clip_n(stage_a.n, lo, hi)+        blocks_a, dn_a = build_blocks(stage_a.coords, k, rng, stage_a.n)+        ia, ca = assemble(stage_a.coords, draw_blocks(blocks_a, n, rng), alpha, dn_a, rng)+        X = np.asarray(stage_a.X[ia].todense(), dtype=np.float32)+        coords = scale_to_rms(ca, rms_radius(stage_a.coords))+    else:+        stage_b = view_io.read_stage(view, b_entry, genes)+        t = float(t)+        aa, ab, _info = align_pair(stage_a.coords, stage_b.coords, stage_a.labels,+                                   stage_b.labels, align)+        rms_a = rms_radius(stage_a.coords)+        rms_b = rms_radius(stage_b.coords)+        target_rms = log_interp(rms_a, rms_b, t, damp)+        ca = scale_to_rms(aa, target_rms)+        cb = scale_to_rms(ab, target_rms)+        n = interp_count(stage_a.n, stage_b.n, t, lo, hi)+        n_b = int(np.clip(round(t * n), 0, min(n, stage_b.n)))+        n_a = min(n - n_b, stage_a.n)+        n_b = min(n - n_a, stage_b.n)++        blocks_a, dn_a = build_blocks(ca, k, rng, stage_a.n)+        blocks_b, dn_b = build_blocks(cb, k, rng, stage_b.n)+        ia, CA = assemble(ca, draw_blocks(blocks_a, n_a, rng, stage_a.labels), alpha, dn_a, rng)+        ib, CB = assemble(cb, draw_blocks(blocks_b, n_b, rng, stage_b.labels), alpha, dn_b, rng)++        X = np.clip(+            np.vstack([+                np.asarray(stage_a.X[ia].todense(), dtype=np.float32),+                np.asarray(stage_b.X[ib].todense(), dtype=np.float32),+            ]), 0.0, None)+        coords = scale_to_rms(np.vstack([CA, CB]), target_rms)++    view_io.write_t2(args.out, X, coords.astype(np.float32), genes, seed=int(args.seed))+++if __name__ == "__main__":+    main()

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

用到的知识库条目

编号标题出处
k023Time-split validation with a held-out intermediate or next time pointnotes/handover/02_知识学习路线.md
k024World-model evaluation dimensions for state-transition predictorsnotes/competition/07_biomedical_world_models.md
k016Degenerate-solution checks for population predictionsnotes/handover/02_知识学习路线.md

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

改了什么从零实现 T2EI-04 空间 k-NN 块抽样插值:procrustes3d 对齐 + 阻尼 log-RMS 缩放后,每侧以随机种子+未覆盖近邻成块(k=15),按块主类型分层以 (1−t):t 整块抽取,表达/坐标原样拼接,jitter 默认关闭(α=0)。
各组分数的变化cell_state:噪声内:mmd_u 0.01657→0.01687(-0.06 分),variogram 0.02952→0.02906(+0.04 分),组 -0.08;类型分层块抽取未损害组成。
expression_change:噪声内:de_score 0.2069→0.2414(+0.15 分),de_direction 0.3757→0.3607(-0.08 分),组 +0.27,符合预期(本方案不改表达数值)。
local_spatial:变好但幅度接近噪声:neighborhood_mmd 0.05791→0.054(skill 0.573→0.590,+0.43 分),组 +1.73;方向与 k=1 对照(raw 0.0635)一致,是机制的目标收益。
shape_scale:变好:d2_shape 0.00841→0.00514(skill 0.934→0.989,+0.46 分),occupancy_dice 0.7735→0.7919(+0.23 分),scale_log_ratio 不变,组 +2.76;块抽样+统一 RMS 缩放让整体形态更贴近真值。
family_idT2EI-04
假设是否成立是
经验
  1. 在 T2 插值上以空间 k-NN 块(k=15)为抽样单元混抽两端阶段、表达坐标原样保留,neighborhood_mmd raw 从 mix 的 ~0.058-0.064 降到 0.054,local_spatial 组 +1.7,且 d2_shape 同时大幅改善(0.0084→0.0051),块结构不损害形状。
  2. FPS 早停建的块池空间上偏向外周(nbhd raw 0.072-0.080,明显差于 mix),改用全阶段块池+随机/分层抽块后恢复并超过 mix——建池必须覆盖整个阶段,抽样偏置会直接反映在邻域指标上。
  3. 整块 jitter(α=0.3×平均最近邻距离)轻微劣化 local_spatial(55.99 vs 56.16),块抽样本身已避免精确重叠,加噪无益,应默认关闭。
  4. k=1 对照(57.13)复现 mix 基线(57.23),且 k∈{10,15,25} 均优于 k=1、同侧近邻占比 0.906 vs 0.813——用'参数退化到已知基线'做机制对照能有效证明收益来自块结构而非其他改动。
  5. Engineer 报告的 57.80(seed 0)低于最终评分 58.40,同管线不同 seed 榜分波动 ~0.3-0.9 分;单次查分不宜当作稳定估计,机制判断应看多 seed 一致的组级方向。
mechanism_active是
下一步建议
  1. 在本块抽样管线上叠加节点 11/13 的类型级表达位移(针对 expression_change 组,当前 59.99 vs 节点 13 的 62.11),预期合并 62 级表达分与 59 级 local_spatial,冲击 62+。
  2. 对 shape_scale 组:d2_shape skill 已 0.989 接近饱和,occupancy_dice skill 仅 0.395(raw 0.79)是该组短板,可试各向异性坐标整形(按真值主轴缩放)提升 dice,注意保持块内配对不破坏 neighborhood_mmd。
  3. 细扫 k∈{15,20,25} 与块抽取的分层粒度(主类型 vs 无分层),针对 local_spatial:k=15/25 已同向优于 k=1,可能存在更优点;同时验证 cell_state 不因分层变化而波动。

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

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

分析员

角色?调研员写计划、工程师改代码、分析员解读分数、审查员检查作弊。分析员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数?大模型一共回复了几轮(每轮可以调用多个工具)。3
工具调用?大模型调用读文件、执行命令等工具的次数,按工具名统计。共 3 次:—
用时不到 1 分
token 数?输入 / 输出(含思考)的 token 数,来自接口返回的用量。输入 16,562 · 输出 1,351 · 思考 606
任务(第一行)分析节点 n23 的打分结果,写 ANALYSIS.json
最后的回答(摘录)
ANALYSIS.json written
原始记录位置?完整对话记录在 Spark 上的文件路径;看板不复制原始内容。/home/spark-longxinyang/vec/runs/formal/20261003-171955-search-t2-embryo-interp-chain-12h/nodes/23/analyst.jsonl (文件不在)

工程师

角色工程师 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数38
工具调用共 43 次:—
用时24 分
token 数输入 86,960 · 输出 17,620 · 思考 19,626
任务(第一行)按计划修改程序:T2EI-04 保邻域块抽样:以空间近邻块为单位混抽两端阶段
最后的回答(摘录)
**完成。** draft 节点 23(T2EI-04 保邻域块抽样)已提交在 `solution/`。

- **做了什么**:从零实现空间 k-NN 块抽样插值——随机种子 + 未覆盖近邻成块(k=15),全阶段块池、按块主类型分层以 (1−t):t 整块抽取,表达/坐标原样,procrustes3d 对齐 + 阻尼 log-RMS 缩放,jitter 默认关闭;单输入退路已测。
- **查分**(9/20 次):机制开 k=15 → **57.80**(seed 1: 57.53,local_spatial 56.2/57.7,nbhd raw 0.0597/0.0561);机制关对照 k=1 → 57.13(local 54.6,nbhd 0.0635,复现 mix 基线 57.23);同侧近邻占比 0.906 vs 0.813,机制生效。k=10/25 同向。FPS 早停池偏外周(nbhd 0.072–0.080)已修复;α=0.3 jitter 轻微劣化,关闭。
- **下一步最值得试**:在本块抽样管线上叠加节点 11/13 的类型级表达位移 + 各向异性坐标整形,有望把 expression 62 级与 local_spatial 56+ 合并,冲击 63+。
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261003-171955-search-t2-embryo-interp-chain-12h/nodes/23/engineer.jsonl (文件不在)

调研员

角色调研员 alibaba-token-plan-cn/qwen3.8-max
调用次数1 次
轮数1
工具调用共 0 次:—
用时1 分
token 数输入 11,348 · 输出 1,446 · 思考 2,681
任务(第一行)为节点 n23(草稿,父节点 nNone)写改进计划 PLAN.json,思路 T2EI-04
最后的回答(摘录)
标配对,直接改善neighborhood_mmd。",
  "approach": "1) 读取两端输入阶段(a,b)的表达矩阵与spatial_3D坐标。\n2) Procrustes 3D对齐:把b侧坐标刚性对齐到a侧(旋转+平移,不缩放),使两端坐标在同一参考系。\n3) 构建块:对每侧分别计算3D坐标的k-NN(k=15,与评分器的15-近邻一致)。用贪心最远点采样(FPS)选出N_seed个种子细胞,每个种子与其k个近邻构成一个块(去重:已被其他块包含的细胞不再当种子)。初始N_seed按目标细胞数/(k+1)估算,实际用覆盖-去重循环调到目标数。\n4) 按(1−t):t比例从a/b侧抽取块:t=0.5时各半。用分层策略保证每侧的空间均匀性(FPS已保证)。\n5) 组装输出:把选中块的表达和坐标原样拼入输出。块内坐标保持相对位移不变。\n6) 块间jitter:对每个块整体加一个随机平移向量,幅度=α×块内平均最近邻距离(初始α=0.3,搜索[0.1,1.0]),防止块间精确重叠。\n7) 全局RMS缩放:把输出云均匀缩放到目标RMS(取a侧RMS的(1−t)+b侧RMS的t的log插值),保持坐标单位一致。\n8) 表达完全不动(原样保留源细胞的表达值),不施加任何位移或缩放。\n9) 单输入阶段退路:若只有一个阶段,全部块从该阶段抽取,无混合,jitter和RMS缩放仍执行。\n10) vec-score快速筛选:先在子样(每侧1500细胞、k=15)上跑3组参数(α=0.1/0.3/1.0),确认neighborhood_mmd优于独立混抽后再全量。查分上限20次,前6次用于参数扫描(k∈{10,15,25}×α∈{0.3,1.0}),保留余量给最优配置的多seed确认。",
  "expected_groups": ["local_spatial", "shape_scale"],
  "risks": "1) 块间边界不自然:不同阶段的块拼接处可能出现表达-坐标不连续,反而拉高neighborhood_mmd。Engineer应在小样上先比较块抽样与独立混抽的neighborhood_mmd raw,若块抽样反而更差则立即停止。2) FPS种子选取可能偏向高密度区域,导致低密度区域细胞缺失、组成偏移,损害mmd_u。对策:FPS后检查输出细胞数是否达标,不足时降低k或增加种子数。3) 表达组可能略低于节点13(62.11),因为本方案不施加类型级表达位移,预期接近mix水平(~59-60)。若表达组降幅>2分,应在后续improve节点叠加表达位移而非在本draft中混入。4) 块太大(k过大)会让输出变成若干孤立团,破坏整体形状(d2_shape、occupancy_dice劣化);k=15是与评分器对齐的初始值,不应超过30。",
  "family_id": "T2EI-04",
  "mechanism": "以空间k-NN块(种子+15近邻,保持相对坐标和表达配对)为抽样单元,按(1−t):t比例从两端阶段抽取完整块拼成输出,块内表达-坐标配对不被打破,从而保持评分器15-近邻平均表达分布的局部一致性。",
  "vs_constant_shift": "常数位移对每个细胞(或每个类型)施加统一表达偏移,不改变空间配对关系。本方案不修改任何细胞的表达值,而是改变'哪些细胞被选中、以什么空间构型进入输出'——通过保持空间邻域完整来改善表达与位置的配对质量。机制作用在空间抽样策略上,不在表达数值上。",
  "mechanism_evidence": "Engineer应对比三组输出的以下指标:(a) neighborhood_mmd raw:块抽样 vs k=1独立混抽(即off对照),预期块抽样更低;(b) 块内细胞的15-近邻是否确实来自同一块(抽样后检查:输出中每个细胞的15个最近邻中,同块占比应>70%);(c) mmd_u不应显著变差(组成未改变);(d) d2_shape和occupancy_dice不应因块间空隙而显著劣化。若(a)无改善或同块占比<50%,说明块结构在拼接后被打散,机制未生效。",
  "mechanism_off_control": "将k设为1(每个'块'只含种子自身,无近邻),其余流程完全相同(FPS选种、(1−t):t比例、jitter、RMS缩放)。k=1退化为独立细胞混抽,等价于标准mix。预期差别:k=1时neighborhood_mmd raw应与节点2的mix水平(~0.055-0.060)相当;k=15时若机制有效应明显更低。若k=15与k=1的neighborhood_mmd无差异,说明块结构未起作用。",
  "sources": []
}
原始记录位置/home/spark-longxinyang/vec/runs/formal/20261003-171955-search-t2-embryo-interp-chain-12h/nodes/23/researcher.jsonl (文件不在)