File size: 12,243 Bytes
c2ede1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
---
license: other
license_name: non-commercial-research
license_link: LICENSE.md
library_name: pytorch
pipeline_tag: feature-extraction
tags:
  - pathology
  - multimodal
  - imagebind
  - spatial-transcriptomics
  - multiplex-immunofluorescence
  - contrastive-learning
---

# 四模态病理嵌入模型(H&E · mIF · RNA · 文本)

这个模型把四种模态嵌入到同一个 1536 维空间里:
- **H&E** 图像;
- **mIF**:多重免疫荧光,即蛋白;
- **RNA**:空间转录组 spot 表达;
- **病理文本**。

做法和 ImageBind 一样。冻结的 UNI2-h 图像空间是锚点,其余每种模态只和 H&E 配对训练。mIF↔RNA、mIF↔文本、文本↔RNA 之间的对齐是"涌现"出来的,没有直接训练过。

- **训练数据:** 只用真实的配对数据。
- **评估数据:** 虚拟数据和合成数据只用于评估,从不参与训练。

| 内容 | 位置 |
|---|---|
| 发布版模型 | `models/ej_ad_mv/` |
| fold-0 模型(带留出测试集) | `models/ej_ad_mv_fold0/` |
| 推理接口 | `embed.py` |
| 训练 | `52_final_model.py` |
| 训练用的编码器特征和数据划分 | 私有数据集仓库 [`Pathstudy/fourmodal-pathology-features`](https://huggingface.co/datasets/Pathstudy/fourmodal-pathology-features) |
| 图文报告 | `report/fourmodal_report.html` |
| 全部实验记录 | `RESULTS.md` |

## 1. 数据

没有任何数据集同时测量了全部四种模态,所以每种模态各用一个和 H&E 配对的真实数据集。

| 数据集 | 配对 | 规模 | 用途 |
|---|---|---|---|
| ORION CRC(1% 图块子集) | H&E – mIF(17 通道,使用 16 个标记) | 3,187 个 333×333 图块,约 40 名病人 | 训练 + 测试 |
| HEST-1k Visium | H&E – RNA(14,802 基因) | 9,484 spots,57 项研究,16 个器官 | 训练 + 测试 |
| TCGA / PathGen-1.6M | H&E – 文本 | 6,144 张图像,6,115 条清洗后的描述 | 训练 + 测试 |
| STPath 虚拟 RNA / MIPHEI 虚拟 mIF / GPT 合成描述 | 给没有真实配对的模态对构建测试集 | ORION、TCGA 测试图块 | **只用于测试** |
| DLPFC(spatialLIBD,脑) | H&E – RNA | 482 spots,3 名供体 | 外部测试 |

- **划分:** 按病人划分 train/val/test。HEST 按研究划分,病人分组见 `splits/`。做 5 折交叉验证。
- **验证集:** val 只用于早停,所有报告的数字都是 test 上的。
- **虚拟模态的校准:** 每种虚拟模态先在同一批有真实数据的样本上校准,即用虚拟数据替换真实数据,看配对结果能否接近。

| 虚拟模态 | 校准结果(真实 → 虚拟) | 结论 |
|---|---|---|
| STPath 虚拟 RNA | 0.148 → 0.126 | 可用 |
| GPT 合成描述 | 0.219 → 0.162 | 可用,偏保守 |
| MIPHEI 虚拟 mIF | 配对准确率 0.92 → 0.55 | 可用 |
| GigaTIME 虚拟 mIF | 0.644 → 0.038 | 不可用,已弃用 |

## 2. 最佳架构:EJ-ad+MV(两阶段,3 种子适配器平均)

```
H&E ──► UNI2-h (frozen, 1536) ───────────────┬──────────────► (+) ──► z_HE
     └► MedVTok (frozen, 1152) ─► Adapter×3 ◄┘ (zero-init, mean of 3 seeds)  ┐
mIF  ──► VirTues (16 markers, 512) ─► MLP head ─────────────────────► z_mIF   │ 1536-d
RNA  ──► hvg · Tahoe-x1 · SE-600M · AIDO.Cell ─► 4 linear heads ─► fuse ─► z_RNA  │ shared
text ──► KEEP · BiomedCLIP · Qwen3-Emb-0.6B ─► 3 linear heads ─► fuse ─► z_text  ┘ space
```

**第一阶段:每个编码器一个映射头。**
- 每个映射头用 InfoNCE 对齐到冻结的 UNI2 CLS 特征。
- 文本用多正样本 InfoNCE:同一张图像的多条文本都算正样本。
- RNA 和文本是后期融合:`z = normalise(Σ_e normalise(head_e(x_e)))`。

**第二阶段:只训练 H&E 一侧。**
- 冻结所有映射头,只训练一个零初始化的残差适配器:`H&E = UNI2 + mean_s A_s([UNI2, c·MedVTok])`。
- 损失:三种配对的 InfoNCE 之和,加上对 UNI2 相似度结构的蒸馏(λ=1)。
- 优化:AdamW,学习率 1e-4,1000 步,OneCycle 学习率调度。
- 选择规则:每个种子按验证集上三种 R@1 的几何平均早停,再把 3 个种子的适配器残差取平均。

**为什么这样设计:** mIF、RNA、文本的嵌入和第一阶段完全一样,所以涌现配对不受第二阶段影响,只有 H&E 这一侧变好。以下结论都来自 5 折配对检验,详见 `RESULTS.md`:

| 设计选择 | 依据 |
|---|---|
| UNI2-h 做锚点 | 在任何训练配对上都没有被显著超过。GigaPath 在 RNA 上低 0.035;CONCH 在 H&E↔mIF 上低 0.155;MedVTok 当锚点在 H&E↔RNA 上只有 0.131(UNI2 是 0.192)。 |
| 多编码器后期融合 | 最大的单项提升:H&E↔RNA +0.037,H&E↔文本 +0.062,4 个涌现配对全部提升(5/5 折)。拼接(早期融合)和单编码器多种子平均都达不到这个效果。 |
| 只动 H&E 侧的联合训练 | 共享 H&E 头会让涌现配对损失 26–74%;只训练残差适配器时,H&E↔mIF 提升 0.10,涌现配对完全不变。 |
| MedVTok 作为额外输入 | H&E↔mIF +0.015,H&E↔文本 +0.016。换成同尺寸的 UNI2 随机投影没有提升。 |
| 3 种子平均 | 单次早停的步数在 100–900 步之间波动,同一设置重跑 H&E↔mIF 可以差 0.05。3 个种子平均后,H&E↔文本再提高 0.015(5/5 折,p=0.02)。 |
| RNA 和文本用线性头 | 训练配对持平,涌现配对 +5%。mIF 保留 MLP 头。 |
| 编码器挑选 | RNA:4 个编码器融合(+0.037)接近 10 个融合(+0.044)。文本:3 个融合(0.299)接近 5 个融合(0.303)。mIF:VirTues 的跨模态迁移比从头训练的 CNN 好得多。 |

## 3. 结果(5 折测试集均值,100 个样本的检索库,随机水平 R@1 = 0.01)

| 配对 | 类型 | R@1 | R@10 | 配对准确率 |
|---|---|---|---|---|
| H&E ↔ mIF(ORION) | 训练 | **0.801** ± 0.043 | 0.991 | 0.942 |
| H&E ↔ RNA(HEST) | 训练 | **0.203** ± 0.033 | 0.625 | 0.258 |
| H&E ↔ 文本(TCGA) | 训练 | **0.323** ± 0.007 | 0.813 | 0.347 |
| mIF ↔ RNA(虚拟) | 涌现 | 0.141 ± 0.007 | 0.657 | 0.194 |
| mIF ↔ 文本(合成) | 涌现 | 0.089 ± 0.008 | 0.491 | 0.104 |
| 文本 ↔ RNA(虚拟) | 涌现 | 0.095 ± 0.006 | 0.436 | 0.100 |
| 文本 ↔ mIF(MIPHEI 虚拟) | 涌现 | 0.046 ± 0.005 | 0.271 | 0.051 |
| H&E ↔ RNA(DLPFC 脑,外部) | 外部 | 0.012 | 0.112 | 0.012 |

各阶段的提升见下表(训练配对 R@1,5 折均值)。逐折结果在 `results/final_cv_long.csv`,各阶段的完整比较在 `results/medvtok_*.csv`、`results/joint_ens_*.csv`。

| 阶段 | H&E↔mIF | H&E↔RNA | H&E↔文本 |
|---|---|---|---|
| 单编码器(S4) | 0.675 | 0.155 | 0.236 |
| 多编码器融合(E,第一阶段) | 0.675 | 0.192 | 0.299 |
| + H&E 适配器 | 0.776 | 0.201 | 0.299 |
| + MedVTok | 0.792 | 0.200 | 0.315 |
| **最终(3 种子平均)** | **0.801** | **0.203** | **0.323** |

## 4. 使用方法

环境:Python 3.11 和一块 CUDA GPU。依赖见 `requirements.txt`。所有脚本都用 `python -I` 运行。

```bash
pip install -r requirements.txt
```

**用预先算好的编码器特征**(不需要任何冻结编码器):

```python
from embed import Embedder
E = Embedder("models/ej_ad_mv")
z_he   = E.he_from_feats(uni2_feats, medvtok_feats)      # (N,1536) / (N,1152)
z_mif  = E.mif_from_feats(virtues_feats)                 # (N,512)
z_rna  = E.rna_from_feats({"hvg": X, "tahoex1": X, "se600m": X, "aidocell": X})
z_text = E.text_from_feats({"keep": X, "biomedclip": X, "qwen3-0.6b": X})
sim = z_he @ z_mif.T          # 输出都是 L2 归一化的 (N, 1536) CPU tensor
```

**用原始输入**。需要先运行 `bash setup_encoders.sh` 下载第三方代码和权重。UNI2-h 是受限仓库,需要账号有访问权限,或者设置 `UNI2_PATH`。

```python
z = E.he(images)                       # PIL 图像列表或 (N,H,W,3) uint8;约 0.5 µm/px,视野 112–166 µm
z = E.text(["Tumor cells with ...", ...])
z = E.rna(counts, genes)               # 原始计数(spots × genes),HGNC 基因名
z = E.mif(tiles, slide)                # (N,16,H,W) 8 位强度,通道顺序为 embed.MIF_MARKERS,每个图块附 slide id
```

注意:
- **mIF 标准化:** 统计量是在一次调用给定的全部图块上计算的,所以请一次传入整张切片或整个数据集。
- **新平台的 RNA 数据:** 建议用 `zscore="self"`,并且一次至少传入几百个 spots。

## 5. 复现(已验证:从 HF 下载到全新目录 + 全新虚拟环境)

```bash
PYTHON=python bash reproduce.sh          # 约 20 分钟(单卡 RTX A5000)
RAW=1 PYTHON=python bash reproduce.sh    # 再加上原始输入检查(需要先运行 setup_encoders.sh)
```

`reproduce.sh` 依次执行:
1. 下载特征仓库,得到 `cache/` 和 `splits/`,共约 1.1 GB。
2. `evaluate_saved.py`:用已发布的 fold-0 模型在留出测试集上重算结果,必须和 `models/ej_ad_mv_fold0/test.csv` 完全一致。
3. 从特征重新训练 fold 0 和 5 折交叉验证,然后用 `compare_reproduction.py` 和已发布的数字对比。
   - 在相同硬件和相同依赖版本下,结果逐位一致。2026-10-28 在 RTX A5000 上用全新的 `requirements.txt` 环境验证:fold 0 和 5 折的全部配对差值都是 0。
   - 换 GPU 或库版本时,第二阶段(H&E 适配器)可能在种子波动范围内变化,约 ±0.01;`compare_reproduction.py` 的容差就是按这个设的。
4. 重新训练发布版模型,输出到 `models/repro_release/`。
5. 加 `RAW=1` 时运行 `example_check.py`:把 `examples/`(每个数据集 64 个 fold-0 测试样本)的原始输入送进 `embed.py`,检查编码器特征和缓存特征是否一致,再检查检索结果。

已发布的结果不会被覆盖:复现的输出写到 `repro_*` 名下。

## 6. 局限

- **外部泛化差:** 脑组织 DLPFC 上处于随机水平。训练用的 HEST 里脑组织也只有 R@1 0.021。所以模型只适用于训练数据覆盖过的组织类型。
- **H&E↔文本的 MedVTok 增益可能偏高:** MedVTok 预训练时见过 PathGen 的图像,包括我们 TCGA 测试集的图像(只有图像,没有文本)。ORION 上的 mIF 增益不受影响。
- **涌现配对的数值偏保守:** 涌现配对的一侧是虚拟或合成数据,所以这些数值是保守估计。
- **实例级涌现对齐仍然弱:** mIF↔RNA 只有 0.141。概念级的对齐(AUROC 0.6–0.7)更可靠。

## 7. 许可证

**只能用于非商业研究。** 本仓库的权重是在以下冻结编码器的特征上训练的,使用时需要分别遵守它们的许可:

| 编码器 | 许可 |
|---|---|
| UNI2-h | CC-BY-NC-ND,受限访问 |
| MedVTok | MIT;它对齐的 CONCH v1.5 是 CC-BY-NC-ND |
| SE-600M、AIDO.Cell | 非商业 |
| Tahoe-x1 | Apache-2.0 |
| VirTues、KEEP、BiomedCLIP | MIT |
| Qwen3-Embedding | Apache-2.0 |

数据本身也有各自的条款,见各数据集的原始发布:ORION、HEST-1k(CC-BY-NC-SA)、TCGA、PathGen-1.6M、spatialLIBD。

## 8. 文件

```
embed.py                 推理接口 (Bind4 模型 + Embedder)
52_final_model.py        训练 (cv / fold0 / release)
evaluate_saved.py        特征级评估 (fold-0 测试, 与 test.csv 完全一致)
example_check.py         原始输入端到端检查 (examples/)
reproduce.sh, compare_reproduction.py, setup_encoders.sh, requirements.txt
14_ 43_ 45_ 45b_ 50_ *.py, biomedclip_text.py, common.py, rna_sets.py   冻结编码器的特征提取 (embed.py 调用)
53_embed_check.py, 54_make_examples.py   在完整原始数据上做的检查 / 生成 examples/ (需要原始数据集)
models/                  ej_ad_mv (发布版), ej_ad_mv_fold0 (+ test.csv, test_items.npz)
results/                 所有实验的 csv 结果
experiments/             01_–51_ 全部实验脚本 (记录用; 依赖原始数据, 不在本仓库)
RESULTS.md               完整实验记录;  report/fourmodal_report.html  图文报告
```