PANDA 这个名字容易让人先想到大熊猫,但在医学图像多模态学习这个方向,它更值得关注的是另一种含义:Prototype-Anchored Alignment,即原型锚定对齐。这套思路解决的问题很具体——部分未配对多模态学习(Partially Unpaired Multimodal Learning)。简单来说,就是一批样本里只有一部分同时具备两种模态,另一部分只有其中一种模态,但你又希望能借助缺失模态提升模型表现。这个场景在阿尔茨海默病 MRI 和 TCGA 病理数据里非常常见。如果你正在做医学影像分析、弱监督跨模态对齐或者多模态表征学习,这篇文章值得往下看。
我先把最值得关注的结论放在前面:PANDA 不是一个端到端的黑盒模型,而是一套把“原型”当作跨模态锚点,通过原型分配一致性来对齐不完全配对数据的训练思路。它的核心价值不是让模型在完美配对的测试集上刷分,而是让缺失模态样本也能从已配对的样本中学到有效表征。下面我会按一个问题背景、方法理解、工程落地、评测验收、常见坑的顺序拆开讲。
1. 先理解它要解决的问题:部分未配对,到底难在哪
1.1 多模态学习为什么通常依赖配对样本
传统多模态学习的基本假设是:每一个训练样本都有完整的多种模态输入。比如一个阿尔茨海默病受试者同时有 MRI、PET、临床量表;一个 TCGA 病例同时有病理切片、基因表达谱、临床信息。模型要做的就是把不同模态的信息映射到一个联合表征空间,然后在这个空间里做分类、回归或者风险预测。
这个思路很直接,但真实医学数据很难满足。一个受试者可能做了 MRI,但没有 PET;一个 TCGA 病例可能只有病理切片,基因表达数据因为样本质量不合格而缺失;不同参与中心的数据采集时间不同,导致模态覆盖不完全。如果只保留模态齐全的样本,样本量会大幅缩水,而且留下的人群可能有偏。这时候就需要处理“部分未配对”的情况。
1.2 部分未配对和完全未配对的差别
很多人会把“部分未配对”和“完全未配对”混在一起。事实上,两者难度完全不同。
- 完全未配对:两个模态的样本来自不同个体,没有任何对应关系,只能靠域自适应、循环一致性这类方法做跨域对齐。
- 部分未配对:有一部分样本是配对的,另一部分只有单模态。配对样本相当于“锚”,可以教模型理清模态之间的对应关系,再把这些关系推广到未配对样本上。
PANDA 要解决的是第二种,难度比完全未配对低,但实际收益更直接。关键问题是:怎么利用少量配对样本,把大量单模态样本的效用发挥出来?如果只用配对样本训练,丢掉单模态样本,浪费严重;如果把未配对样本也当配对样本硬训练,会让模型学到错误映射。原型锚定对齐就是为了避免这两种极端。
1.3 MRI 和 TCGA 病理这两种数据为什么适合验证
标题里的两个应用场景,我理解是同一套方法在两个不同医学任务上做验证,而不是把 MRI 和病理切片强行放进同一个模型。
阿尔茨海默病 MRI 的特点是:公开数据集通常有清晰的疾病标签,比如 AD、MCI、NC,但不同模态的采集率差异很大。一个研究队列里,可能大部分受试者都做了 T1 结构像,但只有一部分做了 PET 或认知量表。如果只保留全部模态齐全的样本,样本量会少得可怜。PANDA 这类方法的价值就在于,可以把缺失模态的 MRI 样本也放进训练过程。
TCGA Pathology 的特点是:病理切片是数字化的全切片图像(WSI),样本量很大,但分子分型、基因表达、生存结局等模态经常不是每个病例都有。很多下游任务需要用病理图像预测基因表达或分子亚型,如果只拿配对病例训练,会浪费大量只有切片没有分子数据的病例。这种部分未配对问题,正好是原型锚定对齐能发挥作用的场景。
2. 原型锚定对齐的核心思路:不硬配,而是找公共锚点
2.1 原型是什么
原型可以理解为一组可学习的“类别代表向量”,它在表征空间里表示某一类语义概念。在图像分类中,原型通常对应类别中心;在多模态学习中,原型更像模态无关的共享语义锚点。
比如在 MRI 任务里,原型可能代表“典型阿尔茨海默病改变”“轻度认知障碍改变”“正常老化改变”;在病理任务里,原型可能代表不同的分子亚型或组织学模式。模型把每种模态的表征映射到同一套原型的分布上,而不是直接去回归另一个模态的原始特征。这样可以绕开模态之间特征维度、数值范围、语义粒度不一致的问题。
PANDA 的全称是 Prototype-Anchored Alignment,直译是“以原型为锚的对齐”。核心逻辑是:我不要求你从 MRI 准确预测出病理切片长什么样,也不要求你从病理切片还原 MRI,而是要求 MRI 和病理切片在同样的原型集合上产生一致的分配概率。两张属于同一类别或同一病人的模态表征,应该得到相似的原型分布。
2.2 为什么原型能当锚点
如果直接用配对样本做特征回归,比如让 MRI encoder 的输出逼近病理 encoder 的输出,会面临很大的自由度。MRI 特征和病理特征虽然描述同一疾病状态,但一个是宏观结构,一个是微观组织,信息维度并不完全重合。直接回归容易丢掉模态特有信息,或者学到一些表面相关性。
原型的好处是提供了一个“中间表示”。模型只需要把每种模态的表征投射到原型空间,原型空间是共享的、低维的、语义化的。配对样本提供了“不同模态应该落到同一个原型分布”的监督信号,未配对样本则可以利用原型分布的一致性约束继续训练。这样既不需要生成缺失模态,也不要求不同模态特征完全一致。
2.3 对齐的几种常见损失形式
从工程角度看,原型锚定对齐一般会组合多种损失。常见做法包括:
- 原型预测损失:将编码后的模态表征输入一个原型预测头,得到 K 个原型的概率分布,然后计算交叉熵。
- 一致性损失:对配对样本,让两个模态的原型分布互相靠近,可以用 KL 散度、JS 散度或者对称的 L2 距离。
- 任务损失:在原型分布之上接一个分类头或回归头,用实际标签监督。
- 原型更新约束:为了让原型不随意漂移,通常会加入动量更新、正交化约束或熵正则化。
这些损失不是论文原始代码里的具体实现,只是训练思路上的通用骨架。你需要根据实际数据和标签类型选择具体组合。如果原始论文有公开代码,最好以代码仓库为准。
3. 从论文标题到可复现工程:数据与环境准备
3.1 数据目录与配对关系记录
不管你是复现 PANDA,还是借鉴这个思路做自己的医学影像项目,第一步都不是写模型,而是把数据组织好。部分未配对场景最怕的是配对关系混乱。
我一般会在项目根目录建一个metadata.csv,至少包含以下几列:
| 字段 | 含义 | 示例 |
|---|---|---|
| patient_id | 受试者或病例唯一编号 | ADNI_002_S_0413 |
| mri_path | MRI 文件路径(如果有) | /data/mri/002.nii |
| pathology_path | 病理 WSI 路径(如果有) | /data/tcga/TCGA-XX-XXXX.svs |
| label | 分类标签或生存时间等标签 | AD / MCI / NC |
| split | train / val / test | train |
注意区分“模态缺失”和“标签缺失”。模态缺失是指这个样本没有某种模态输入,标签缺失是指没有结果变量。PANDA 处理的是前者,但实际病历数据里两者经常同时出现。训练前要单独统计每个模态的缺失率、每个类别的样本量,形成数据报告。
3.2 影像预处理的常见步骤
MRI 和病理切片的预处理差别很大,不能共用一条流水线。
MRI 的常见步骤包括:重定向到标准空间、偏置场校正、去头皮、配准到模板、裁剪 ROI。如果做体素级分析,还需要强度归一化。很多公开数据集已经做过初步处理,但你要确认原始数据是否处于同一坐标系,不同中心的图像矩阵大小和 spacing 是否一致。
病理 WSI 的常见步骤包括:在合适的倍率下切 patch,过滤背景区域,做颜色归一化,然后按 patch 序列进入模型。TCGA 的 SVS 文件尺寸很大,一般不会把整张图直接喂给网络,而是用 patch-level 的特征聚合,或者用多实例学习(MIL)框架。
预处理就是为了减少模态内部和模态之间的无关差异。如果你的 MRI 数据强度分布差异大,原型对齐会很不稳定。如果病理 patch 里混入大量空白背景,原型可能会学到背景模式而不是组织学模式。
3.3 硬件和依赖选型
这类方法对硬件的要求取决于编码器类型:
- 如果只是拿现成特征做原型对齐,CPU 就能跑,速度也快。
- 如果要从 MRI 体素或病理 patch 训练编码器,需要 GPU,显存建议至少 16GB 起步。
- 病理 WSI 的 patch 数量很多,通常需要先缓存 patch 特征,或者使用预训练的特征抽取器,不能每次训练都现场切 patch。
软件依赖方面,PyTorch 是这类算法最常见的实现框架,配合 NVIDIA 显卡需要安装 CUDA 和 cuDNN。如果处理 WSI,通常还会用到 OpenSlide、tifffile 这类库。但我不建议一开始就装一整套复杂环境,先用最小依赖跑通一个小数据集,再逐步增加库。
3.4 训练流程的最小骨架
下面这段代码是示意,不是论文原始实现。它主要帮助你理解一个最小训练循环里应该包含哪几部分:
import torch import torch.nn.functional as F # 示意:两个模态的编码器,输出为特征向量 mri_encoder = MRIEncoder() path_encoder = PathEncoder() # 示意:共享原型矩阵,K 个原型,每个维度与特征维度一致 prototypes = torch.randn(K, feat_dim, requires_grad=True) for batch in dataloader: mri_feat = mri_encoder(batch["mri"]) path_feat = path_encoder(batch["pathology"]) # 计算每个样本与所有原型的相似度 mri_logits = mri_feat @ prototypes.T * temperature path_logits = path_feat @ prototypes.T * temperature # 如果是配对样本,希望两个模态的原型分布接近 loss_align = F.kl_div( F.log_softmax(mri_logits, dim=-1), F.softmax(path_logits, dim=-1), reduction="batchmean", ) # 如果有标签,可以加上任务损失 loss_task = task_loss(mri_logits, batch["label"]) loss = loss_align + loss_task loss.backward() optimizer.step()上面代码里temperature是缩放参数,会让相似度分布更平滑或更尖锐。K是原型的数量,这个参数很关键,后面会单独讲。
4. 训练细节:原型初始化、对齐权重和稳定性控制
4.1 原型初始化方式
原型初始化看起来是小问题,实际影响很大。如果所有原型随机初始化但分布太集中,模型可能一开始就把所有样本分到同一个原型,训练崩溃。
推荐的初始化方式有三种:
- 用训练集里每个类别的模态特征均值作为初始原型。这样至少每个类别都有对应的锚点。
- 对预训练特征做 KMeans 聚类,把聚类中心作为原型初始值。
- 如果完全没有标签,可以先用自编码器或对比学习预训练一个单模态特征空间,再聚类初始化。
我建议先做第二种或者第一种,不要直接使用未经处理的随机向量。原型初始化接近真实分布,训练会更稳定,收敛也更快。
4.2 对齐损失和任务损失组合
对齐损失和任务损失的比例需要调。如果对齐损失权重太大,模型只需要让两种模态的原型分布一致,可能忽略真实标签;如果任务损失权重太大,模型又会学会只利用单模态信息,原型对齐变成摆设。
常规做法是先固定一个较小的对齐权重,比如 0.1 或 0.01,然后观察任务指标和分布一致性指标的变化。判断标准很简单:
- 任务指标上升,同时配对样本的跨模态原型一致性也在上升,说明对齐有正面帮助。
- 任务指标下降,说明对齐可能引入了错误信息,需要降低权重或检查配对样本质量。
- 任务指标几乎不变,说明未配对样本没被充分利用,可能需要增加对齐权重或增大原型数量。
这里不要一次性把权重调得很大。先跑小规模实验,确定梯度量级在可控范围内,再放大训练。
4.3 部分未配对比例对训练的影响
部分未配对比例不同,最优做法也不同。我一般会先统计训练集里配对样本占比,再决定损失结构。
- 配对样本占比高,比如高于 80%,可以简单地把未配对样本也加入训练,用原型分布一致性做弱约束。
- 配对样本占比很低,比如只有 20%,那就必须给配对样本单独采样,保证每个 batch 里都有足够多的配对样本。否则模型很容易在大量未配对样本上忘掉对齐信号。
- 如果只是某个类别缺失严重,比如 AD 样本多为单模态,MCI 样本多为配对,那么原型可能会偏向于模态完整的类别。这种情况要按类别统计缺失率,必要时做重采样。
在 batch 采样时,我建议至少保证每个 batch 里配对样本数量不低于 25%。你可以写一个特殊的采样器,先按病人 ID 分组,优先采样配对组。
4.4 收敛判断和日志设计
这个类型的模型不太容易直接从 loss 数值判断好坏,因为对齐 loss 和任务 loss 的量纲不同。我一般会记录四类指标:
| 指标 | 说明 | 关注点 |
|---|---|---|
| train task loss | 任务损失 | 是否下降 |
| align loss | 跨模态原型一致性损失 | 是否稳定下降 |
| train accuracy / AUC | 任务指标 | 是否波动 |
| paired consistency | 配对样本原型预测一致率 | 是否接近合理水平 |
如果训练多轮后,任务 loss 在下降但对齐 loss 一直不降,说明两个模态的原型分布根本没有对齐。这时候先检查是不是特征尺度差距太大,再看配对样本的监督信号够不够。如果对齐 loss 下降很快,但任务准确率很低,说明原型只学到了模态共性,没学到类别区分性。
日志里还要记录每个 epoch 的原型矩阵均值、方差,以及类别原型之间的距离。原型之间距离过近,说明表达不够有区分度;距离过远,可能说明原型过度分散,未见过的样本会很难归属。
5. 验证与评测:不能只看准确率
5.1 分类、风险分层指标
如果你的下游任务是阿尔茨海默病三分类或 TCGA 分子亚型分类,常规指标是准确率、F1、混淆矩阵。但如果类别不平衡,准确率会骗人。医学场景里我更关注敏感度和特异性,尤其是高风险类别不能漏。
如果下游任务是生存分析,比如从病理图像预测总生存期,那么评价指标应该选 C-index、时间相关 AUC,而不是简单的分类准确率。原型分配可以用于提取注意力权重,帮助定位关键区域,但最终落地评价还是要回到临床问题本身。
5.2 未配对样本上的表现怎么评估
原型对齐的一个关键卖点是“未配对样本也能受益”。所以评测时,不能只报整体指标,还要把测试集分成三组来报告:
- 两种模态都齐全的测试样本
- 只有 MRI 的测试样本
- 只有病理切片的测试样本
如果三组之间差异很大,尤其是单模态组显著低于配对组,那说明对齐没有真正把共享语义迁移到未配对样本上。只报一个平均指标会掩盖这个问题。
5.3 表征空间质量评估
除了任务指标,还要看表征空间是否合理。可以用 TSNE 或者 UMAP 把特征降到二维,按类别和模态着色。理想情况下,同一类别的不同模态样本应该聚在一起,而不是按模态分成两个簇。
如果 TSNE 图上 MRI 和病理样本各自聚成一团,说明模态特异性信息太强,共享原型没有起到底层对齐作用。如果同一类别的两个模态混在一起,说明对齐效果好,但也要警惕特征是否丢失了模态特有信息。
原型本身也可以可视化。比如在 MRI 上,把每个原型对应的注意力区域投影回原图,观察是否落在海马体、脑室等阿尔茨海默病相关区域;在病理切片上,观察原型对应的 patch 是否集中在肿瘤区域、炎性区域或特定组织学形态。如果原型落到无关区域,说明对齐学到的语义可能不准确。
6. 常见坑与排查顺序
6.1 特征尺度不一致
MRI 特征和病理特征的数值范围可能差别很大。如果不对齐尺度就计算相似度,温度参数很难调,原型更新也会不稳定。
排查方式很简单:打印两个模态特征的均值、标准差和最大最小值。如果数值范围差一个数量级以上,建议先做 L2 归一化,或者对特征做 LayerNorm。不要直接把原始特征和原型做点积,这是新手最容易踩的坑。
6.2 模态编码器差异过大
如果一个模态用 3D CNN,另一个模态用 Transformer,两个编码器的特征空间容量和语义层次完全不同。这时候直接用原型对齐,可能只对齐了低层统计特征,而不是高层语义。
解决方法有两种:一是固定一个模态的编码器参数,只训练另一个模态的编码器和对齐模块;二是在编码器之后加一个浅层映射网络,先把各模态特征映射到同一个维度,再接原型计算。我建议先做第二种,至少能在不改变骨干网络的情况下完成对齐。
6.3 原型坍缩
原型坍缩是指多个原型收敛到同一个位置,导致原型矩阵的有效秩很低。表现是训练后期不同类别之间的原型相似度接近 1,所有样本都被预测到同一个或少数几个原型上。
应对手段有:
- 降低学习率,尤其是原型矩阵的学习率。
- 对原型加上正交化正则化,鼓励不同原型互相正交。
- 使用动量更新,让原型以指数滑动平均方式更新。
- 在相似度计算时调高温度,让概率分布更尖锐,可控程度更高。
遇到坍缩时,先不要急着改模型结构,先把温度、学习率、权重衰减这三个参数排查一遍。
6.4 数据泄漏与隐私合规
医学数据有隐私和合规问题。TCGA 是公共数据,但使用前也要看具体数据的发表许可;阿尔茨海默病研究常用的 ADNI 数据需要申请账号并签署数据使用协议。无论用哪种数据,都不能在项目仓库里直接提交原始受试者 ID 和影像文件。
数据划分时要按患者 ID 划分,不能按 patch 或切片划分,否则同一个病人的训练样本会泄漏到测试集里,导致评估虚高。病理 WSI 的 patch 来自同一张切片,如果一个病人同时出现在训练集和测试集,模型的性能会被严重高估。
6.5 跑批任务时的资源排查
如果你要一次性跑大量实验,比如对比不同原型数量、不同对齐权重,建议先做资源规划。可以先在 1/10 的小数据集上跑通完整流程,记录显存、内存、单 epoch 耗时,再决定并行策略。
任务卡住时,不要只看模型代码。先确认这几个点:
- 数据加载是否卡在 I/O,尤其读取 SVS 文件时。
- GPU 显存是否足够,是否存在碎片化。
- 是否有多个 worker 同时读同一个文件,造成锁冲突。
- 输出目录是否存在、是否有写入权限。
- 日志是否实时刷新,能看出卡在哪一步。
7. 这种方法的边界和适合场景
7.1 适合什么数据和研究目标
PANDA 这类原型锚定对齐,最适合的数据状态是:你有一定数量的配对样本,但配对比例不高,单模态样本又有明确的标签或任务目标。典型情况包括:
- ADNI 队列中,一部分受试者有 MRI 和 PET,另一部分只有 MRI,想提升单模态 MRI 的分类性能。
- TCGA 数据中,一部分病例有病理切片和基因表达,另一部分只有病理切片,想用病理图像预测分子亚型。
- 多中心数据,不同中心采集的模态覆盖不一样,想把所有中心数据联合训练。
研究方向可以从“对齐算法本身的设计”和“应用场景验证”两个角度切入。如果你不是发文章,而是做工程落地,重点应该放在数据清洗、特征缓存、训练稳定性和评测鲁棒性上。
7.2 不能替代什么
必须清醒一点:原型锚定对齐不能让模型真正“看到”缺失模态。它只能让模型利用配对样本中的跨模态共享语义,来指导单模态表征的学习。如果某个模态包含互补的独特信息,而这个信息只在配对样本中出现过,那么未配对样本依然无法获得这部分信息,除非你引入生成模型做模态补全。
另外,如果模态语义差异太大,比如 MRI 和基因表达之间,原型对齐的效果可能不如 MRI 和病理图像之间那样明显。不是说不能做,而是需要更多的监督信号、更细的原型设计,以及更小的语义鸿沟。
7.3 和完全配对、单模态基线的对比思路
发论文或者做技术汇报时,对比实验至少要包含三组:
- 只用单模态数据,不进行跨模态对齐。
- 只保留配对样本训练,放弃未配对样本。
- 使用 PANDA 或其他原型锚定对齐方法,充分利用所有样本。
如果第三组不能同时超过前两组,那说明这个方法在当前数据上不一定适合。你要先检查配对样本质量、原型数量、训练稳定性,而不是直接下结论说方法无效。
最后留一个我自己的经验:这类多模态弱监督方法,真正跑通很容易,跑稳定很难。最容易出问题的不是损失函数,而是数据配对关系没有维护好。先把配对信息表、模态缺失统计、分类别指标拆清楚,再训练,能少踩一大半坑。