Download index.html from Pathstudy/imagebind-pathology-report: direct link, hf CLI and curl.
- Browser
- Download file 31.6 kB
-
https://huggingface.co/spaces/Pathstudy/imagebind-pathology-report/resolve/main/index.html
- Command line
-
hf download hf://spaces/Pathstudy/imagebind-pathology-report/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Pathstudy/imagebind-pathology-report/resolve/main/index.html
31.6 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>四模态病理嵌入模型</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@600;700&family=Noto+Sans+SC:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap"> | |
| <style> | |
| /* Layout: one reading column (~68ch) with wide tables/figures allowed to break out; H&E stain palette (hematoxylin violet, eosin pink) */ | |
| :root { | |
| --bg: #fbf9fb; | |
| --surface: #f3eef4; | |
| --fg: #231c2b; | |
| --muted: #675d70; | |
| --rule: #ddd3e0; | |
| --hema: #4b3a8c; /* hematoxylin: accent, links, final model */ | |
| --eosin: #c2477a; /* eosin: second series */ | |
| --steel: #7f8aa3; /* neutral series */ | |
| --good: #2f7d5b; | |
| --warn: #a8641a; | |
| --bar-track: #ebe4ed; | |
| --code-bg: #f0eaf2; | |
| --f-display: "Noto Serif SC", "Songti SC", "STSong", serif; | |
| --f-body: "Noto Sans SC", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; | |
| --f-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace; | |
| --step-0: 15px; --step-1: 17px; --step-2: 21px; --step-3: 28px; --step-4: 36px; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root:not([data-theme="light"]) { | |
| --bg: #17131c; --surface: #211b28; --fg: #ece6f0; --muted: #a99fb3; --rule: #3a3143; | |
| --hema: #a99af0; --eosin: #ec86b0; --steel: #9aa5bd; --good: #6cc59b; --warn: #e0a35a; | |
| --bar-track: #2c2434; --code-bg: #241d2b; color-scheme: dark; | |
| } | |
| } | |
| :root[data-theme="dark"] { | |
| --bg: #17131c; --surface: #211b28; --fg: #ece6f0; --muted: #a99fb3; --rule: #3a3143; | |
| --hema: #a99af0; --eosin: #ec86b0; --steel: #9aa5bd; --good: #6cc59b; --warn: #e0a35a; | |
| --bar-track: #2c2434; --code-bg: #241d2b; color-scheme: dark; | |
| } | |
| * { box-sizing: border-box; } | |
| body { background: var(--bg); color: var(--fg); font-family: var(--f-body); font-size: var(--step-0); line-height: 1.75; } | |
| .wrap { max-width: 980px; margin: 0 auto; padding-inline: 20px; padding-block: 40px 80px; } | |
| .col { max-width: 42em; } | |
| header { display: grid; gap: 14px; padding-bottom: 28px; border-bottom: 1px solid var(--rule); } | |
| .eyebrow { font-family: var(--f-mono); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); } | |
| h1 { font-family: var(--f-display); font-size: var(--step-4); line-height: 1.25; margin: 0; text-wrap: balance; } | |
| h2 { font-family: var(--f-display); font-size: var(--step-3); line-height: 1.3; margin: 56px 0 12px; text-wrap: balance; } | |
| h3 { font-size: var(--step-1); margin: 28px 0 8px; } | |
| p { margin: 0 0 12px; } | |
| .lede { font-size: var(--step-1); color: var(--fg); } | |
| .meta { font-size: 13px; color: var(--muted); } | |
| a { color: var(--hema); } | |
| code, .mono { font-family: var(--f-mono); font-size: .9em; } | |
| code { background: var(--code-bg); padding: 1px 5px; border-radius: 4px; } | |
| pre.code { background: var(--code-bg); padding: 16px 18px; border-radius: 8px; overflow-x: auto; font-family: var(--f-mono); font-size: 13px; line-height: 1.6; margin: 12px 0 16px; } | |
| ul, ol { padding-left: 1.3em; margin: 0 0 12px; } | |
| li { margin: 4px 0; } | |
| .tbl { overflow-x: auto; margin: 12px 0 18px; } | |
| table { border-collapse: collapse; width: 100%; font-size: 14px; font-variant-numeric: tabular-nums; } | |
| th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; } | |
| th { font-weight: 500; color: var(--muted); font-size: 13px; white-space: nowrap; } | |
| td.n, th.n { text-align: right; font-family: var(--f-mono); white-space: nowrap; } | |
| tr.final td { background: var(--surface); font-weight: 500; } | |
| tr.final td:first-child { box-shadow: inset 3px 0 0 var(--hema); } | |
| .kind { font-family: var(--f-mono); font-size: 11px; letter-spacing: .04em; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--rule); color: var(--muted); white-space: nowrap; } | |
| .kind.trained { color: var(--hema); border-color: var(--hema); } | |
| .kind.emergent { color: var(--eosin); border-color: var(--eosin); } | |
| .kind.virtual { color: var(--warn); border-color: var(--warn); } | |
| .ok { color: var(--good); font-weight: 500; } | |
| .bad { color: var(--warn); font-weight: 500; } | |
| .note { color: var(--muted); font-size: 13px; } | |
| .callout { background: var(--surface); border-radius: 8px; padding: 16px 20px; margin: 16px 0; } | |
| .callout p:last-child { margin-bottom: 0; } | |
| .panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: 18px; margin: 16px 0 6px; } | |
| .panel { min-width: 0; } | |
| .panel h4 { margin: 0 0 6px; font-size: 14px; font-weight: 500; } | |
| .panel svg { width: 100%; height: auto; display: block; } | |
| .legend { display: flex; flex-wrap: wrap; gap: 14px; font-size: 13px; color: var(--muted); margin: 8px 0 0; } | |
| .legend span::before { content: ""; display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; background: var(--c); } | |
| .findings { display: grid; gap: 14px; counter-reset: f; padding: 0; list-style: none; } | |
| .findings li { display: grid; grid-template-columns: 2.2em 1fr; gap: 6px; margin: 0; } | |
| .findings li::before { counter-increment: f; content: counter(f); font-family: var(--f-mono); color: var(--hema); font-weight: 500; } | |
| .findings b { font-weight: 700; } | |
| .flow { background: var(--surface); border-radius: 8px; padding: 16px; margin: 12px 0 16px; display: grid; gap: 10px; overflow-x: auto; } | |
| .flow .row { display: grid; grid-template-columns: 7.5em 1.2em minmax(11em, 1.4fr) 1.2em minmax(9em, 1fr) 1.2em 9em; align-items: center; min-width: 640px; } | |
| .flow .b { border: 1px solid var(--rule); background: var(--bg); border-radius: 6px; padding: 6px 9px; font-size: 13px; line-height: 1.45; text-align: center; } | |
| .flow .b small { display: block; color: var(--muted); font-size: 11.5px; } | |
| .flow .in { font-weight: 500; } | |
| .flow .he .b { border-color: var(--hema); } | |
| .flow .z { border-color: var(--hema); color: var(--hema); font-weight: 700; } | |
| .flow .ar { text-align: center; color: var(--muted); font-family: var(--f-mono); } | |
| pre.mermaid { background: var(--surface); border-radius: 8px; padding: 16px; overflow-x: auto; margin: 12px 0 16px; text-align: center; } | |
| .toc { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; margin-top: 4px; } | |
| .toc a { text-decoration: none; color: var(--muted); } | |
| .toc a:hover, .toc a:focus-visible { color: var(--hema); text-decoration: underline; } | |
| a:focus-visible { outline: 2px solid var(--hema); outline-offset: 2px; } | |
| @media (max-width: 560px) { | |
| h1 { font-size: 28px; } h2 { font-size: 23px; } | |
| th, td { padding: 6px 7px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <div class="eyebrow">ImageBind-style · H&E / mIF / RNA / text · 最终结果与独立复现 · 2026-10-30</div> | |
| <h1>四模态病理嵌入模型</h1> | |
| <p class="lede col">把 H&E 图像、多重免疫荧光(mIF,蛋白)、空间转录组(RNA)和病理文本放进同一个 1536 维空间。 | |
| 锚点是冻结的 UNI2-h 图像空间;所有模态只用真实配对数据训练,虚拟和合成数据只用于评估。 | |
| 最终模型(EJ-ad+MV,3 种子适配器平均)已训练完成,并提供从原始输入直接生成嵌入的接口 <code>embed.py</code>。 | |
| 2026-10-29 又在独立 A800 环境中完成了发布模型评估、fold 0 重训、5 折重训和 release 重训,全部复现检查通过。</p> | |
| <nav class="toc" aria-label="目录"> | |
| <a href="#setup">设定与数据</a><a href="#model">最终模型</a><a href="#results">结果</a><a href="#reproduction">独立复现</a><a href="#findings">主要发现</a> | |
| <a href="#encoders">编码器选择</a><a href="#virtual">虚拟模态</a><a href="#limits">局限</a><a href="#usage">使用方法</a><a href="#files">文件</a> | |
| </nav> | |
| </header> | |
| <section id="setup"> | |
| <h2>设定与数据</h2> | |
| <div class="col"> | |
| <p>没有任何数据集同时测量四种模态,所以采用 ImageBind 的思路:每个模态只和 H&E 配对训练,H&E 以外的模态两两之间的对齐是“涌现”出来的。</p> | |
| <ul> | |
| <li><b>训练配对只用真实数据:</b>ORION 结直肠癌(H&E–mIF)、HEST-1k Visium(H&E–RNA)、TCGA/PathGen(H&E–文本)。</li> | |
| <li><b>划分:</b>按病人(HEST 按研究|病人组)分成 train/val/test,5 折交叉验证;val 只用于早停,所有数字都是 test 上的。</li> | |
| <li><b>虚拟和合成数据只用于测试:</b>STPath 虚拟 RNA、MIPHEI 虚拟 mIF、GPT 合成描述。它们只用来给“没有真实配对”的模态对构建测试集。</li> | |
| <li><b>指标:</b>100 个样本的检索库里的 R@1(双向平均,随机水平 0.01),抽 50 次取平均;配对准确率是同一 100 库里的一对一匈牙利匹配(随机水平 0.01)。</li> | |
| </ul> | |
| </div> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>数据集</th><th>配对</th><th>规模</th><th>用途</th></tr></thead> | |
| <tbody> | |
| <tr><td>ORION CRC</td><td>H&E – mIF(16 个标记)</td><td class="mono">3,187 图块 · 约 40 名病人</td><td>训练 + 测试</td></tr> | |
| <tr><td>HEST-1k Visium</td><td>H&E – RNA(14,802 基因)</td><td class="mono">9,484 spots · 57 项研究 · 16 器官</td><td>训练 + 测试</td></tr> | |
| <tr><td>TCGA / PathGen-1.6M</td><td>H&E – 文本</td><td class="mono">6,144 图像 · 6,115 条清洗后文本</td><td>训练 + 测试</td></tr> | |
| <tr><td>STPath / MIPHEI / GPT 描述</td><td>虚拟 RNA、虚拟 mIF、合成文本</td><td class="mono">ORION、TCGA 测试图块</td><td>只用于测试</td></tr> | |
| <tr><td>DLPFC(spatialLIBD)</td><td>H&E – RNA(脑)</td><td class="mono">482 spots · 3 名供体</td><td>外部测试</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section id="model"> | |
| <h2>最终模型</h2> | |
| <div class="col"> | |
| <p>两阶段训练。第一阶段给每个编码器单独训练一个映射头,映射到冻结的 UNI2 空间。第二阶段冻结所有映射头,只训练一个 H&E 残差适配器,它同时读 UNI2 和 MedVTok 的特征。</p> | |
| </div> | |
| <div class="flow" role="img" aria-label="模型结构:四种模态各自经编码器和映射头进入 1536 维共享空间"> | |
| <div class="row he"><div class="b in">H&E 图像</div><div class="ar">→</div><div class="b">UNI2-h(冻结)<br>+ MedVTok(冻结)</div><div class="ar">→</div><div class="b">UNI2 + 残差适配器<small>3 个种子取平均</small></div><div class="ar">→</div><div class="b z">1536 维共享空间</div></div> | |
| <div class="row "><div class="b in">mIF 图块</div><div class="ar">→</div><div class="b">VirTues<small>16 个标志物</small></div><div class="ar">→</div><div class="b">MLP 头</div><div class="ar">→</div><div class="b z">1536 维共享空间</div></div> | |
| <div class="row "><div class="b in">RNA 计数</div><div class="ar">→</div><div class="b">hvg · Tahoe-x1<br>SE-600M · AIDO.Cell</div><div class="ar">→</div><div class="b">4 个线性头<small>后期融合</small></div><div class="ar">→</div><div class="b z">1536 维共享空间</div></div> | |
| <div class="row "><div class="b in">文本</div><div class="ar">→</div><div class="b">KEEP · BiomedCLIP<br>Qwen3-Emb-0.6B</div><div class="ar">→</div><div class="b">3 个线性头<small>后期融合</small></div><div class="ar">→</div><div class="b z">1536 维共享空间</div></div> | |
| </div> | |
| <div class="col"> | |
| <ul> | |
| <li><b>第一阶段:</b>每个映射头用 InfoNCE 对齐冻结的 UNI2(文本是多正样本 InfoNCE,同一图像的多条文本都算正样本)。RNA 和文本是后期融合:<code>z = normalise(Σ normalise(head_e(x_e)))</code>。</li> | |
| <li><b>第二阶段:</b>只训练 H&E 适配器和温度参数:<code>H&E = UNI2 + mean_s A_s([UNI2, c·MedVTok])</code>。损失是三种配对损失之和加上对 UNI2 相似度结构的蒸馏(λ=1),学习率 1e-4,1000 步,按验证集三种 R@1 的几何平均早停。用 3 个随机种子各训练一个适配器,取平均。</li> | |
| <li><b>后果:</b>mIF、RNA、文本的嵌入和第一阶段完全一样,所以涌现配对不受第二阶段影响;只有 H&E 这一侧变好。</li> | |
| <li><b>发布版:</b><code>models/ej_ad_mv/</code>,用 fold 0 的 train + test 训练(ORION 3,000 图块、HEST 8,938 spots、TCGA 5,615 条文本),fold 0 的 val 用于早停。它没有自己的测试集,预期性能就是下面的 5 折交叉验证结果。</li> | |
| </ul> | |
| </div> | |
| </section> | |
| <section id="results"> | |
| <h2>结果</h2> | |
| <h3>最终模型,5 折测试集</h3> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>配对</th><th>类型</th><th>数据集</th><th class="n">R@1</th><th class="n">R@10</th><th class="n">配对准确率</th></tr></thead> | |
| <tbody> | |
| <tr><td>H&E ↔ mIF</td><td><span class="kind trained">训练</span></td><td>ORION</td><td class="n">0.801 ± 0.043</td><td class="n">0.991</td><td class="n">0.942</td></tr> | |
| <tr><td>H&E ↔ RNA</td><td><span class="kind trained">训练</span></td><td>HEST</td><td class="n">0.203 ± 0.033</td><td class="n">0.625</td><td class="n">0.258</td></tr> | |
| <tr><td>H&E ↔ 文本</td><td><span class="kind trained">训练</span></td><td>TCGA</td><td class="n">0.323 ± 0.007</td><td class="n">0.813</td><td class="n">0.347</td></tr> | |
| <tr><td>mIF ↔ RNA(虚拟)</td><td><span class="kind emergent">涌现</span></td><td>ORION</td><td class="n">0.141 ± 0.007</td><td class="n">0.657</td><td class="n">0.194</td></tr> | |
| <tr><td>mIF ↔ 文本(合成)</td><td><span class="kind emergent">涌现</span></td><td>ORION</td><td class="n">0.089 ± 0.008</td><td class="n">0.491</td><td class="n">0.104</td></tr> | |
| <tr><td>文本 ↔ RNA(虚拟)</td><td><span class="kind emergent">涌现</span></td><td>TCGA</td><td class="n">0.095 ± 0.006</td><td class="n">0.436</td><td class="n">0.100</td></tr> | |
| <tr><td>文本 ↔ mIF(MIPHEI 虚拟)</td><td><span class="kind emergent">涌现</span></td><td>TCGA</td><td class="n">0.046 ± 0.005</td><td class="n">0.271</td><td class="n">0.051</td></tr> | |
| <tr><td>H&E ↔ RNA</td><td><span class="kind virtual">外部</span></td><td>DLPFC(脑)</td><td class="n">0.012</td><td class="n">0.112</td><td class="n">0.012</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <p class="note col">随机水平:R@1 0.01,R@10 0.10,配对准确率 0.01。涌现配对的一侧是虚拟或合成数据,这些数据本身有误差,所以数值偏保守。DLPFC 是第一阶段(E)的结果;第二阶段不改变 RNA 侧。</p> | |
| <h3>各阶段的提升(训练配对 R@1,5 折均值)</h3> | |
| <div class="panels" id="chart"></div> | |
| <div class="legend" aria-hidden="true"> | |
| <span style="--c: var(--steel)">单编码器(S4)</span> | |
| <span style="--c: var(--eosin)">多编码器融合(E)</span> | |
| <span style="--c: color-mix(in srgb, var(--hema) 55%, var(--bg))">+ H&E 适配器 / + MedVTok</span> | |
| <span style="--c: var(--hema)">最终(3 种子平均)</span> | |
| </div> | |
| <h3>涌现配对(R@1)</h3> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>阶段</th><th class="n">mIF↔RNA(虚拟)</th><th class="n">mIF↔文本(合成)</th><th class="n">文本↔RNA(虚拟)</th><th class="n">文本↔mIF(虚拟)</th></tr></thead> | |
| <tbody> | |
| <tr><td>单编码器,单独训练(S4)</td><td class="n">0.122</td><td class="n">0.076</td><td class="n">0.069</td><td class="n">0.038</td></tr> | |
| <tr><td>单编码器,联合微调所有头(J8)</td><td class="n">0.122</td><td class="n">0.073</td><td class="n">0.065</td><td class="n">0.035</td></tr> | |
| <tr><td>多编码器融合(E)</td><td class="n">0.141</td><td class="n">0.089</td><td class="n">0.095</td><td class="n">0.046</td></tr> | |
| <tr><td>融合 + 联合微调 mIF 头(EJ-frz)</td><td class="n">0.134</td><td class="n">0.087</td><td class="n">0.095</td><td class="n">0.042</td></tr> | |
| <tr class="final"><td>最终:只训练 H&E 适配器</td><td class="n">0.141</td><td class="n">0.089</td><td class="n">0.095</td><td class="n">0.046</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section id="reproduction"> | |
| <h2>独立复现:发布结果可重现</h2> | |
| <div class="col"> | |
| <p><strong>总体结论:</strong>在 cuipeng 服务器的一张 NVIDIA A800-SXM4-80GB 上,从发布的编码器特征重新执行完整的 <code>reproduce.sh</code>。流程包括评估已发布 fold-0 checkpoint、重训 fold 0、重训 5 折、逐项比较发布结果,以及用 fold-0 train + test 重训 release 模型。任务最终状态为 <strong class="ok">Success</strong>,日志给出 <code>REPRODUCED within tolerance</code> 和 <code>FOURMODAL_REPRODUCTION_COMPLETE</code>。</p> | |
| <ul> | |
| <li><b>任务记录:</b><code>t-20261029185830-n649r</code>;实际运行约 9 分钟(2026-10-29 16:12–16:21:57 UTC)。</li> | |
| <li><b>环境:</b>Python 3.12.1、PyTorch 2.7.1+cu128、CUDA 12.8、NumPy 2.4.6、Pandas 2.3.3、SciPy 1.16.3。</li> | |
| <li><b>数据检查:</b>26 个特征数组,共 0.933 GiB;全部可读取、非空且无 NaN/Inf;三组数据的 5 折划分完整。</li> | |
| <li><b>发布模型检查:</b>重新评估发布 fold-0 模型,7 个模态对与随模型发布的 <code>test.csv</code> 最大 R@1 差异为 0.0001。</li> | |
| </ul> | |
| </div> | |
| <h3>fold 0 重训:留出测试集</h3> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>模态对</th><th class="n">本次重训 R@1</th><th class="n">发布 R@1</th><th class="n">绝对差异</th><th>复现检查</th></tr></thead> | |
| <tbody> | |
| <tr><td>H&E ↔ mIF(ORION)</td><td class="n">0.7608</td><td class="n">0.7834</td><td class="n">0.0226</td><td class="ok">通过</td></tr> | |
| <tr><td>H&E ↔ RNA(HEST)</td><td class="n">0.1853</td><td class="n">0.1917</td><td class="n">0.0064</td><td class="ok">通过</td></tr> | |
| <tr><td>H&E ↔ 文本(TCGA)</td><td class="n">0.3240</td><td class="n">0.3203</td><td class="n">0.0037</td><td class="ok">通过</td></tr> | |
| <tr><td>mIF ↔ RNA(虚拟,ORION)</td><td class="n">0.1358</td><td class="n">0.1352</td><td class="n">0.0006</td><td class="ok">通过</td></tr> | |
| <tr><td>mIF ↔ 文本(合成,ORION)</td><td class="n">0.0978</td><td class="n">0.0987</td><td class="n">0.0009</td><td class="ok">通过</td></tr> | |
| <tr><td>文本 ↔ RNA(虚拟,TCGA)</td><td class="n">0.0999</td><td class="n">0.1000</td><td class="n">0.0001</td><td class="ok">通过</td></tr> | |
| <tr><td>文本 ↔ mIF(MIPHEI,TCGA)</td><td class="n">0.0484</td><td class="n">0.0491</td><td class="n">0.0007</td><td class="ok">通过</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <h3>5 折重训:最终 <code>ens-es</code> 规则</h3> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>模态对</th><th class="n">本次 5 折 R@1</th><th class="n">发布 R@1</th><th class="n">绝对差异</th><th>复现检查</th></tr></thead> | |
| <tbody> | |
| <tr><td>H&E ↔ mIF(ORION)</td><td class="n">0.7967</td><td class="n">0.8010</td><td class="n">0.0042</td><td class="ok">通过</td></tr> | |
| <tr><td>H&E ↔ RNA(HEST)</td><td class="n">0.2021</td><td class="n">0.2030</td><td class="n">0.0009</td><td class="ok">通过</td></tr> | |
| <tr><td>H&E ↔ 文本(TCGA)</td><td class="n">0.3231</td><td class="n">0.3232</td><td class="n">0.0001</td><td class="ok">通过</td></tr> | |
| <tr><td>mIF ↔ RNA(虚拟,ORION)</td><td class="n">0.1414</td><td class="n">0.1414</td><td class="n">0.0000</td><td class="ok">通过</td></tr> | |
| <tr><td>mIF ↔ 文本(合成,ORION)</td><td class="n">0.0885</td><td class="n">0.0887</td><td class="n">0.0001</td><td class="ok">通过</td></tr> | |
| <tr><td>文本 ↔ RNA(虚拟,TCGA)</td><td class="n">0.0966</td><td class="n">0.0953</td><td class="n">0.0013</td><td class="ok">通过</td></tr> | |
| <tr><td>文本 ↔ mIF(MIPHEI,TCGA)</td><td class="n">0.0464</td><td class="n">0.0465</td><td class="n">0.0001</td><td class="ok">通过</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <p class="note col">所有 fold-0 和 5 折对比均落在发布代码预定义的容差内。最终还生成并独立加载验证了 <code>models/repro_fold0/model.pt</code>、<code>models/repro_release/model.pt</code> 和三张 <code>final_cv_*_repro.csv</code>。本次执行的是特征级复现;没有执行可选的 <code>RAW=1</code> 原始输入编码器检查。</p> | |
| </section> | |
| <section id="findings"> | |
| <h2>主要发现</h2> | |
| <ol class="findings col"> | |
| <li><div><b>UNI2-h 是最好的冻结锚点。</b>在 mIF、RNA、文本三种训练配对上都没有被显著超过;GigaPath 接近(RNA 差 0.035),CONCH 在 H&E↔mIF 上低 0.155。MedVTok 当锚点也更差(H&E↔RNA 0.131 对 0.192)。</div></li> | |
| <li><div><b>多编码器后期融合是最大的单项提升。</b>RNA 用 4 个编码器、文本用 3 个,H&E↔RNA +0.037、H&E↔文本 +0.062,4 个涌现配对也全部提升(5/5 折)。拼接(早期融合)和同一编码器多种子平均都达不到这个效果,说明这些编码器是互补的。</div></li> | |
| <li><div><b>联合训练要只动 H&E 一侧。</b>从头训练共享的 H&E 头会让 H&E↔mIF 变好,但涌现配对损失 26–74%。冻结所有模态头、只训练零初始化的残差适配器,H&E↔mIF +0.10,涌现配对完全不变。</div></li> | |
| <li><div><b>MedVTok 作为适配器的额外输入有效,作为锚点无效。</b>额外输入使 H&E↔mIF +0.015、H&E↔文本 +0.016。换成同尺寸的 UNI2 随机投影就没有提升,说明增益来自 MedVTok 的信息而不是参数量。</div></li> | |
| <li><div><b>第二阶段的早停不稳定,3 个种子平均解决了这个问题。</b>验证曲线在 100 步后基本是平的,单次早停选中的步数在 100–900 之间波动,同一设置重跑 H&E↔mIF 可以差 0.05。3 个早停适配器取平均后,H&E↔文本比单种子再 +0.015(5/5 折,p=0.02),也是三种规则里几何平均最高的。</div></li> | |
| <li><div><b>线性头比 MLP 头更适合 RNA 和文本。</b>训练配对持平,涌现配对 +5%;mIF 保留 MLP 头。</div></li> | |
| <li><div><b>用虚拟配对直接训练不如走锚点的涌现路线。</b>文本↔蛋白在真实 mIF 上 0.596 对 0.678(AUROC)。所以虚拟数据只用作测试,从不进入训练。</div></li> | |
| <li><div><b>实例级的涌现对齐仍然弱。</b>mIF↔RNA 0.141 是随机水平的 14 倍,但比训练配对低一个数量级;概念级对齐(AUROC 0.6–0.7)更可靠。</div></li> | |
| </ol> | |
| </section> | |
| <section id="encoders"> | |
| <h2>编码器选择</h2> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>模态</th><th>最终使用</th><th>依据(5 折配对检验)</th></tr></thead> | |
| <tbody> | |
| <tr><td>H&E</td><td>UNI2-h + MedVTok(只进适配器)</td><td>UNI2 在所有训练配对上不被显著超过;MedVTok 额外输入 mIF +0.015、文本 +0.016</td></tr> | |
| <tr><td>mIF</td><td>VirTues(16 标记)+ MLP 头</td><td>从头训练的 ConvNeXt-T 在 H&E↔mIF 上高 0.03,但 VirTues 的跨模态迁移好得多(mIF↔markers 0.66 对 0.48),且不绑定固定标记面板</td></tr> | |
| <tr><td>RNA</td><td>hvg + Tahoe-x1 + SE-600M + AIDO.Cell,各一个线性头</td><td>单个基础模型最多 +0.016;4 个融合 +0.037,接近 10 个融合(+0.044)</td></tr> | |
| <tr><td>文本</td><td>KEEP + BiomedCLIP + Qwen3-Emb-0.6B,各一个线性头</td><td>KEEP 单个最好(0.278);3 个融合 0.299,接近 5 个融合(0.303)</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section id="virtual"> | |
| <h2>虚拟模态(只用于评估)</h2> | |
| <div class="col"> | |
| <p>每种虚拟模态先做校准:在有真实数据的同一批样本上,用虚拟数据替换真实数据,看配对结果是否接近。</p> | |
| </div> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>虚拟模态</th><th>校准(真实 → 虚拟,同一批样本)</th><th>结论</th></tr></thead> | |
| <tbody> | |
| <tr><td>STPath 虚拟 RNA</td><td>HEST H&E↔RNA 0.148 → 0.126;STPath 未见过的样本 0.086 → 0.080</td><td class="ok">可用</td></tr> | |
| <tr><td>GPT 合成描述</td><td>TCGA H&E↔文本 0.219 → 0.162(约 0.75 倍)</td><td class="ok">可用,偏保守</td></tr> | |
| <tr><td>MIPHEI 虚拟 mIF</td><td>ORION 未见过的测试图块:配对准确率 0.92 → 0.55(约 0.6 倍)</td><td class="ok">可用</td></tr> | |
| <tr><td>GigaTIME 虚拟 mIF</td><td>ORION H&E↔mIF 0.644 → 0.038</td><td class="bad">不可用,已弃用</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section id="limits"> | |
| <h2>局限</h2> | |
| <ul class="col"> | |
| <li><b>外部泛化差:</b>脑组织 DLPFC 上所有非循环配对都在随机水平(H&E↔RNA 0.012)。训练集 HEST 里脑组织也只有 0.021。模型目前适用于训练数据覆盖的组织类型。</li> | |
| <li><b>H&E↔文本的 MedVTok 增益可能偏高:</b>MedVTok 的预训练图像包含 PathGen 1.6M,见过我们的 TCGA 测试图像(只有图像,没有文本)。ORION 上的 mIF 增益不受影响。</li> | |
| <li><b>验证集小:</b>ORION 每折只有 187–276 个验证图块,所以第二阶段用了 3 种子平均来降低早停的随机性。</li> | |
| <li><b>涌现配对的测试数据一侧是虚拟或合成的,</b>数值是保守估计,不是真实数据上的精确值。</li> | |
| <li><b>新数据的 RNA 批次效应:</b>默认用 HEST 训练集的统计量做标准化;新平台或新实验室的数据建议用 <code>zscore="self"</code>(外部测试就是这样做的)。</li> | |
| </ul> | |
| </section> | |
| <section id="usage"> | |
| <h2>使用方法</h2> | |
| <div class="col"> | |
| <p>运行环境:<code>.venv/bin/python -I</code>,需要 GPU。所有输出都是 L2 归一化的 (N, 1536) 向量,相似度就是点积。</p> | |
| </div> | |
| <pre class="code">from embed import Embedder | |
| E = Embedder("models/ej_ad_mv") # 发布版模型 | |
| z_he = E.he(images) # H&E 图块:PIL 图像或 (N,H,W,3) uint8;约 0.5 µm/px,112–166 µm 视野 | |
| z_txt = E.text(["Tumor cells with ...", ...]) | |
| z_rna = E.rna(counts, genes, zscore="train") # 原始计数(spots × 基因)+ HGNC 基因名;新数据集可用 zscore="self" | |
| z_mif = E.mif(tiles, slide) # (N,16,H,W) mIF 图块,通道顺序 = embed.MIF_MARKERS,8 位强度;附每个图块的切片 ID | |
| sim = z_he @ z_rna.T # 跨模态相似度 | |
| # 已有特征时:E.he_from_feats(uni2, medvtok)、E.mif_from_feats(virtues)、E.rna_from_feats({...})、E.text_from_feats({...})</pre> | |
| <h3>接口验证(<code>53_embed_check.py</code>)</h3> | |
| <div class="col"><p>从原始像素、字符串和计数矩阵出发,用 <code>embed.py</code> 重新计算特征,和训练时缓存的特征逐项比较;再用 fold 0 模型在留出测试集上做端到端检索。</p></div> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>检查</th><th>结果</th></tr></thead> | |
| <tbody> | |
| <tr><td>UNI2-h、MedVTok(ORION、PathGen、HEST 图像)</td><td class="mono">余弦中位数 1.0000,最低 ≥ 0.9997</td></tr> | |
| <tr><td>KEEP、BiomedCLIP、Qwen3(TCGA 文本)</td><td class="mono">中位数 1.0000 / 1.0000 / 0.9998(BiomedCLIP 个别 0.93,bf16 批次差异)</td></tr> | |
| <tr><td>hvg、AIDO.Cell / Tahoe-x1、SE-600M(HEST)</td><td class="mono">1.0000 / 0.991、0.994(后两者按官方做法随机采样基因)</td></tr> | |
| <tr><td>VirTues(全部 3,187 个 ORION 图块)</td><td class="mono">中位数 1.0000,最低 1.0000</td></tr> | |
| <tr><td>端到端 R@1:原始输入 vs 缓存特征</td><td class="mono">mIF 0.792 / 0.792 · RNA 0.194 / 0.194 · 文本 0.335 / 0.336</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| <section id="files"> | |
| <h2>文件</h2> | |
| <div class="tbl"> | |
| <table> | |
| <thead><tr><th>路径</th><th>内容</th></tr></thead> | |
| <tbody> | |
| <tr><td><code>embed.py</code></td><td>推理接口(Embedder、Bind4)</td></tr> | |
| <tr><td><code>52_final_model.py</code></td><td>最终模型训练:<code>cv</code>(选择规则)、<code>fold0</code>、<code>release</code></td></tr> | |
| <tr><td><code>53_embed_check.py</code></td><td>原始输入验证,结果在 <code>results/embed_check.csv</code></td></tr> | |
| <tr><td><code>models/ej_ad_mv/</code></td><td>发布版模型:<code>model.pt</code>(77 MB,含标准化统计量)、<code>meta.json</code>、验证曲线</td></tr> | |
| <tr><td><code>models/ej_ad_mv_fold0/</code></td><td>fold 0 模型及其留出测试结果 <code>test.csv</code></td></tr> | |
| <tr><td><code>results/final_cv_*.csv</code></td><td>5 折 × 3 种子的选择规则实验</td></tr> | |
| <tr><td><code>RESULTS.md</code></td><td>全部实验的完整记录(01_–53_)</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </section> | |
| </div> | |
| <script> | |
| (function () { | |
| // Trained-pair R@1, 5-fold means (RESULTS.md: 48_ S4 / E, 51_ EJ-ad / EJ-ad+MV, 52_ cv ens-es) | |
| var stages = ["单编码器 S4", "多编码器融合 E", "+ H&E 适配器", "+ MedVTok", "最终(3 种子)"]; | |
| var colors = ["var(--steel)", "var(--eosin)", "color-mix(in srgb, var(--hema) 55%, var(--bg))", | |
| "color-mix(in srgb, var(--hema) 55%, var(--bg))", "var(--hema)"]; | |
| var pairs = [ | |
| { name: "H&E ↔ mIF(ORION)", max: 1.0, ticks: [0, 0.25, 0.5, 0.75, 1.0], v: [0.675, 0.675, 0.776, 0.792, 0.801] }, | |
| { name: "H&E ↔ RNA(HEST)", max: 0.25, ticks: [0, 0.05, 0.1, 0.15, 0.2, 0.25], v: [0.155, 0.192, 0.201, 0.200, 0.203] }, | |
| { name: "H&E ↔ 文本(TCGA)", max: 0.35, ticks: [0, 0.1, 0.2, 0.3], v: [0.236, 0.299, 0.299, 0.315, 0.323] } | |
| ]; | |
| var NS = "http://www.w3.org/2000/svg"; | |
| var W = 320, labelW = 104, valW = 40, rowH = 26, top = 6, axisH = 22; | |
| var plotW = W - labelW - valW; | |
| var H = top + stages.length * rowH + axisH; | |
| function el(n, a, t) { var e = document.createElementNS(NS, n); for (var k in a) e.setAttribute(k, a[k]); if (t != null) e.textContent = t; return e; } | |
| var host = document.getElementById("chart"); | |
| pairs.forEach(function (p) { | |
| var div = document.createElement("div"); div.className = "panel"; | |
| var h = document.createElement("h4"); h.textContent = p.name; div.appendChild(h); | |
| var svg = el("svg", { viewBox: "0 0 " + W + " " + H, role: "img", "aria-label": p.name + " R@1: " + stages.map(function (s, i) { return s + " " + p.v[i].toFixed(3); }).join(",") }); | |
| var x = function (v) { return labelW + v / p.max * plotW; }; | |
| p.ticks.forEach(function (t) { | |
| svg.appendChild(el("line", { x1: x(t), x2: x(t), y1: top, y2: top + stages.length * rowH, stroke: "var(--rule)", "stroke-width": 1 })); | |
| svg.appendChild(el("text", { x: x(t), y: H - 6, "text-anchor": "middle", "font-size": 10, fill: "var(--muted)", "font-family": "var(--f-mono)" }, t === 0 ? "0" : String(t))); | |
| }); | |
| stages.forEach(function (s, i) { | |
| var y = top + i * rowH; | |
| svg.appendChild(el("text", { x: labelW - 8, y: y + rowH / 2 + 4, "text-anchor": "end", "font-size": 11, fill: "var(--fg)" }, s)); | |
| svg.appendChild(el("rect", { x: labelW, y: y + 5, width: x(p.v[i]) - labelW, height: rowH - 10, rx: 2, fill: colors[i] })); | |
| svg.appendChild(el("text", { x: x(p.v[i]) + 5, y: y + rowH / 2 + 4, "font-size": 11, fill: i === stages.length - 1 ? "var(--hema)" : "var(--muted)", "font-family": "var(--f-mono)", "font-weight": i === stages.length - 1 ? 600 : 400 }, p.v[i].toFixed(3))); | |
| }); | |
| div.appendChild(svg); host.appendChild(div); | |
| }); | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |