OTLesMix实战:用Wasserstein Barycenter与最优传输合成医学病灶
训练医学图像分割或检测模型时,病灶样本不足是比网络结构更常见的瓶颈。OTLesMix 这个名称指向一种合成病灶生成方案:通过 Wasserstein Barycenter 和 Optimal Transport Map 构造新的病灶,使其形状和位置具备更多多样性。真正训练一个病灶分割模型,往往面临三方面困难:阳性样本数量少、不同病灶形状差异大、病灶在解剖图像中出现的位置跨度广。如果只用旋转、缩放、弹性形变这类数据增强,很难让模型见到“从未组合过”的病灶位置和形态。
这篇内容从工程实现角度拆解 OTLesMix 背后的数学工具和落地步骤。先理解最优传输、Wasserstein 距离、Wasserstein Barycenter 和最优传输映射各自解决什么问题,再用 Python 与 POT 库跑通一个最小模拟,最后把合成病灶流程接入分割训练管线,并讨论常见参数、排错路径和验证清单。目标不是替代原始论文源码,而是让读者在自己的数据集上也能搭建一条类似的可复现合成管线。
1. OTLesMix 解决什么问题:病灶数据的三重稀缺
1.1 病灶样本数量少之外,更麻烦的是形状和位置不平衡
在一个典型的医学图像分割任务里,模型需要的不是“固定的几千张训练图”,而是足够覆盖真实分布的训练样本。病灶数据的稀缺通常体现在三个层次:
- 数量稀缺:阳性样本可能是几十例到几百例,远少于自然图像数据集。
- 形状稀缺:某些病灶形态只在个别样本中出现,例如边缘不规则的结节、带分叶的肿瘤、跨叶病灶。
- 位置稀缺:病灶在 CT、MRI 或超声图像中出现的位置受到解剖结构约束,现有训练集可能只覆盖少数几个区域。
数量问题可以通过类别权重、重采样、半监督等方式缓解。但形状和位置的不平衡无法简单解决。一个模型如果只见过肝脏右下缘的肿瘤,遇到肝顶部或贴近血管的肿瘤时,边界预测很容易失准。合成病灶的目标,就是让训练集覆盖更多“病灶形态 × 病灶位置”的组合。
1.2 为什么传统数据增强无法完成这种覆盖
传统数据增强做的是同一样本内部的变换:
- 旋转:改变病灶方向,但不改变它相对于器官的位置。
- 缩放:改变病灶大小,但很难同时适配不同解剖区域的相对比例。
- 平移:在固定范围内移动整张图像,可能把病灶移动到背景或解剖关系错误的位置。
- 弹性形变:改变局部形状,但强度有限,并且容易出现不自然的拉伸。
- 灰度扰动:只改变亮度、对比度,不改变形态和位置。
这些方法有一个共同缺陷:它们没有建立“病灶外观”和“目标区域”之间的对应关系。把一个真实病灶从一个器官区域搬到另一个器官区域,需要知道病灶到目标区域的形状映射,同时保证边界自然、灰度分布合理。简单地把病灶 mask 贴到另一张图上,会产生明显贴纸感和边缘伪影。
1.3 OTLesMix 的切入点:把病灶当作概率分布来搬移
OTLesMix 的关键转变,是把病灶图像看作一个概率分布,而不是普通像素矩阵。每一幅病灶 patch 可以理解成在二维图像空间上的一堆“质量”,灰度值就是质量密度。
在这个视角下:
- 两个病灶之间的差异,可以通过把一份质量搬移到另一份质量所在位置的最小成本来衡量,这就是最优传输问题。
- 多个病灶之间的“折中形态”,可以通过 Wasserstein Barycenter 得到,它不是在像素上做平均,而是在空间分布上做传输后的加权平均。
- 把一个病灶放到一个新位置,可以使用 Optimal Transport Map 把源病灶的质量映射到目标区域,从而形成新位置、新大小的病灶。
因此,OTLesMix 可以理解为由两个组件配合完成合成任务:
- Wasserstein Barycenter 负责形状多样性。
- Optimal Transport Map 负责位置多样性。
如果原始论文没有给出完整实现,这个理解依然可以用作搭建合成管线的设计基础。过程中需要格外注意:合成病灶不一定在医学上合理,必须结合解剖约束、质量评估和真实数据验证来使用。
2. 理解标题中的两个数学工具:Wasserstein 距离与 Barycenter
2.1 最优传输:把一堆土搬到另一堆土的最小成本
最优传输问题的雏形是“搬土问题”。假设源图像 (A) 的灰度值总和是一堆土,目标图像 (B) 是另一堆土。现在要把源图像上的灰度质量搬到目标图像位置,搬运成本等于“搬运质量 × 搬运距离”。
在离散图像中,Kantorovich 形式可以写成:
[ \min_{\pi \in \Pi(\mu,\nu)} \sum_{i,j} \pi_{i,j} c(x_i, y_j) ]
其中 (\mu) 是源分布,(\nu) 是目标分布,(\pi) 是传输矩阵,(c(x_i, y_j)) 是源像素 (x_i) 到目标像素 (y_j) 的搬运成本,通常取欧氏距离或欧氏距离平方。
传输矩阵 (\pi) 的行对应源像素,列对应目标像素,矩阵元素表示从源像素搬到目标像素的质量。这个矩阵就是 Optimal Transport Plan,也就是离散情况下的 Optimal Transport Map。
2.2 Wasserstein 距离为什么适合病灶图像比较
Wasserstein 距离是由最优传输成本诱导出的分布距离。对于 (p) 阶 Wasserstein 距离:
[ W_p(\mu,\nu)=\left(\inf_{\pi \in \Pi(\mu,\nu)} \int c(x,y)^p d\pi(x,y)\right)^{1/p} ]
当 (c) 取欧氏距离平方时,得到 (W_2) 距离。
Wasserstein 距离和普通逐像素损失的区别在于,它考虑空间位移。两幅病灶图像即使灰度分布类似,如果病灶中心从左上角移到右下角,逐像素 L2 距离会很大,而 Wasserstein 距离恰好能反映这种位移成本。同样,一个圆形病灶和一个椭圆病灶的 (W_2) 距离,会体现出形状变化带来的搬运成本。
在 OTLesMix 场景下,Wasserstein 距离的意义有两个:
- 它是构建 Barycenter 的优化目标。
- 它是衡量合成病灶是否接近真实病灶分布的可选指标。
2.3 Wasserstein Barycenter:多个病灶之间的“形状重心”
Wasserstein Barycenter 解决的是“在多个分布之间找一个折中分布”的问题。给定 (k) 个病灶分布 (\mu_1,\dots,\mu_k) 和权重 (w_1,\dots,w_k),Barycenter 定义为:
[ \min_{\mu} \sum_{i=1}^k w_i W_2^2(\mu, \mu_i) ]
可以把它看成几何中心的推广。普通像素平均会产生模糊的灰度重叠,而 Wasserstein Barycenter 通过最优传输重新分配质量,得到的结果是在空间形态上“离所有输入都不太远”的中间分布。
在病灶生成中,Wasserstein Barycenter 的典型用途是:
- 从多个真实病灶合成一个中间形态的病灶。
- 改变不同病灶的权重,控制合成结果更接近哪一类形态。
- 在形状轴上采样,生成从病灶 A 到病灶 B 的中间样本。
这也解释了 OTLesMix 中“Diverse Shapes”的部分。
2.4 Optimal Transport Map:从源病灶到目标区域的位置控制
最优传输映射可以把源分布的质量搬运到目标分布。离散情况下,这个映射对应一个传输矩阵。如果目标分布来自一个参考区域 mask,那么最优传输映射就可以把源病灶搬运到目标区域。
例如,源病灶是一个左肺上叶的小结节,目标区域是右肺下叶的某个局部 patch。将源病灶的灰度分布与目标区域位置分布做最优传输,可以得到一个传输矩阵。这个矩阵会把源病灶的质量“搬”到目标区域,形成位于新位置的病灶。
这样做的好处是:
- 位置由目标区域 mask 控制,不会随机飘移。
- 形状会向目标区域的结构靠近,而不是简单平移。
- 病灶灰度来自真实病灶,不是从噪声或随机纹理生成的。
这也对应了标题中“Diverse Locations”和“Optimal Transport Map”。
2.5 两个组件如何配合
在实际实验设计里,可以把两个组件放在同一条流水线上:
- 从训练集中收集一批真实病灶 patch 和对应 mask。
- 选择一小批病灶,使用 Wasserstein Barycenter 生成新的中间形状病灶。
- 从健康图像中选取一组可放置病灶的目标区域。
- 对每个新病灶和目标区域计算最优传输矩阵。
- 用传输矩阵把新病灶灰度搬到目标区域,并同步生成病灶 mask。
- 将合成病灶融合回健康图像,得到新的训练样本。
这样,Barycenter 负责“造出没见过但合理的新形状”,Optimal Transport Map 负责“把这些形状放到不同位置”。两者组合后,训练样本的数量、形状覆盖和位置覆盖都能提升。
3. 先跑通最小示例:用 Python 和 POT 模拟病灶合成
3.1 环境准备
最小示例只需要 Python、NumPy、OpenCV、Matplotlib 和 POT。POT 是 Python Optimal Transport 库,提供 EMD、Sinkhorn、Wasserstein 距离和 Barycenter 等实现。
安装命令:
pip install numpy opencv-python matplotlib pot安装完成后,验证是否可用:
python -c "import ot; print(ot.__version__)"依赖说明如下表:
| 依赖 | 用途 | 检查方式 |
|---|---|---|
| numpy | 图像矩阵和分布计算 | import numpy |
| opencv-python | 生成模拟病灶、图像融合 | import cv2 |
| matplotlib | 可视化结果 | import matplotlib |
| pot | 最优传输、Sinkhorn、Barycenter | import ot; print(ot.__version__) |
版本以 pip 安装时的最新稳定版为准,不同版本的 POT 在部分 API 参数上可能有细微差异。
3.2 生成两个模拟病灶图像
先创建两个不同位置、不同大小、不同旋转角度的椭圆病灶。OpenCV 的ellipse可以直接画实心椭圆。
import numpy as np import cv2 import matplotlib.pyplot as plt import ot def make_lesion(size=64, center, axes, angle=30, intensity=1.0): img = np.zeros((size, size), dtype=np.float32) img = cv2.ellipse( img, center=center, axes=axes, angle=angle, startAngle=0, endAngle=360, color=intensity, thickness=-1, ) return img lesion_a = make_lesion(64, center=(20, 22), axes=(8, 5), angle=30) lesion_b = make_lesion(64, center=(45, 40), axes=(12, 7), angle=75)这段代码生成两个椭圆病灶:lesion_a偏左上,lesion_b偏右下,形状和方向都不同。后续最优传输会在两个病灶之间寻找映射。
3.3 把图像变成概率分布并计算最优传输矩阵
把图像展平成一个一维向量,再除以总和,就得到离散概率分布。代价矩阵使用像素坐标的欧氏距离平方,对应 Wasserstein-2 距离。
def to_distribution(img): flat = img.ravel().astype(np.float64) s = flat.sum() if s < 1e-12: flat[:] = 0.0 flat[0] = 1.0 else: flat /= s return flat size = 64 xs = np.arange(size) / size ys = np.arange(size) / size xx, yy = np.meshgrid(xs, ys) coords = np.stack([xx.ravel(), yy.ravel()], axis=1) M = ot.dist(coords, coords, metric="sqeuclidean") a = to_distribution(lesion_a) b = to_distribution(lesion_b) T = ot.emd(a, b, M) print("transport matrix shape:", T.shape) print("row sum:", T.sum(axis=1).min(), T.sum(axis=1).max())在 64×64 图像上,N=4096,矩阵 (M) 和 (T) 的大小约为 128MB 每个,可以接受。如果把分辨率提高到 128×128,每个矩阵会超过 2GB,需要改成 Sinkhorn 或分块计算。
3.4 用 Wasserstein Barycenter 合成中间病灶
使用 POT 的barycenter_sinkhorn计算两个病灶之间的 barycenter。输入参数中,A的每一列是一个分布,weights是各分布的权重,reg是熵正则化系数。
A = np.stack([a, b], axis=1) reg = 0.01 weights = [0.5, 0.5] bary_hist = ot.barycenter_sinkhorn( A, M, reg=reg, weights=weights, numItermax=1000, ) bary_img = bary_hist.reshape(size, size)reg越小,结果越接近严格最优传输,但也越容易出现数值不稳定性;reg越大,结果越平滑,但可能丢失病灶的锐利边界。权重决定了合成结果更接近哪一个输入病灶。
3.5 把病灶迁移到目标位置
现在模拟“把病灶放到新的位置”。先创建一个圆形目标区域 mask,然后以这个 mask 作为目标分布,计算从源病灶到目标区域的传输矩阵。
target_mask = np.zeros((size, size), dtype=np.float32) target_mask = cv2.circle(target_mask, center=(45, 20), radius=12, color=1.0, thickness=-1) t = to_distribution(target_mask) T2 = ot.emd(a, t, M) moved_flat = T2.T @ lesion_a.ravel() moved = moved_flat.reshape(size, size) moved /= moved.max() if moved.max() > 0 else 1.0这里的矩阵方向需要特别注意。T2的行对应源像素,列对应目标区域像素。要从源图像得到目标位置的灰度图,需要使用T2.T @ source_flat,把源像素质量按传输矩阵聚合到目标像素上。
3.6 可视化与结果解读
把源病灶、真实病灶、barycenter 合成结果、传输后结果放在同一张图里。
fig, axes = plt.subplots(1, 4, figsize=(12, 3)) axes[0].imshow(lesion_a, cmap="gray") axes[0].set_title("source lesion") axes[1].imshow(lesion_b, cmap="gray") axes[1].set_title("real lesion") axes[2].imshow(bary_img, cmap="gray") axes[2].set_title("barycenter") axes[3].imshow(moved, cmap="gray") axes[3].set_title("moved to target") for ax in axes: ax.axis("off") plt.tight_layout() plt.savefig("otlesmix_demo.png", dpi=150)正常结果应该是:barycenter 的形状介于两个真实病灶之间;moved的图像主体出现在右侧中间位置的目标区域附近。如果moved出现大量碎裂点,说明离散传输矩阵过于严格,可以改用ot.sinkhorn并增加正则化系数。
4. 从示例到实际病灶分割管线
4.1 整体流程
把最小示例扩展成可训练的分割数据增强管线,需要以下步骤:
- 准备真实病灶 patch 和对应分割 mask。
- 选择健康背景图像,并明确病灶可以放置的解剖区域。
- 对病灶 patch 做强度预处理,转换为概率分布。
- 使用 Wasserstein Barycenter 生成中间形态病灶。
- 使用最优传输映射把病灶迁移到目标区域。
- 将迁移后的病灶融合回健康图像。
- 同步生成病灶 mask,保存为离线训练样本。
- 混合真实样本和合成样本训练分割模型。
这里最关键的一点是:病灶迁移的不仅是图像,还包括 mask。最优传输矩阵作用于图像灰度,也必须作用于 mask,才能保证标签和图像一致。
4.2 真实医学图像中如何构造病灶分布
真实医学图像通常来自 NIfTI 或 DICOM 文件。把病灶区域裁剪成 patch 后,需要先做强度归一化。以 CT 为例,通常会先做窗宽窗位处理,再裁剪到 [0,1] 区间。
def normalize_from_ct(patch, window_center=-400, window_width=1500): low = window_center - window_width / 2.0 high = window_center + window_width / 2.0 patch = np.clip(patch, low, high) patch = (patch - low) / (high - low) return patch.astype(np.float32)病灶分布可以取 patch 的灰度值,也可以取病灶 mask 的距离图或权重图。使用 mask 的缺点是忽略病灶内部纹理;使用灰度图则想保留更多外观信息。建议根据任务选择:
- 只关注病灶几何形态:使用 mask 的二值图。
- 关注病灶内部密度或信号变化:使用灰度 patch。
- 两者都关注:使用 mask 和灰度 patch 的加权组合。
4.3 用最优传输生成病灶并覆盖到健康图像
传输完成后,需要把合成病灶融合回背景图像。最简单的做法是直接覆盖,但边界可能很硬。更稳妥的做法是使用 alpha 融合或泊松融合。
def overlay_synthetic_lesion(bg_image, bg_mask, moved, moved_mask, x, y, alpha=0.8): patch_h, patch_w = moved.shape[:2] roi = bg_image[y:y+patch_h, x:x+patch_w] blended = np.clip(alpha * moved + (1 - alpha) * roi, 0, 1) bg_out = bg_image.copy() bg_out[y:y+patch_h, x:x+patch_w] = blended mask_out = bg_mask.copy() roi_mask = mask_out[y:y+patch_h, x:x+patch_w] roi_mask = np.maximum(roi_mask, moved_mask) mask_out[y:y+patch_h, x:x+patch_w] = roi_mask return bg_out, mask_out实际覆盖时要注意提前检查x + patch_w和y + patch_h是否超出边界。对于不同模态图像,alpha的选择也不同。背景纹理较细的图像,alpha太高会显得像贴纸;背景均匀的图像,alpha可以高一些。
4.4 掩膜同步:源病灶 mask 和目标位置 mask 都要经过同一变换
如果在最小示例中对病灶灰度使用了传输矩阵T2,那么对 mask 也应该使用同样的矩阵:
source_mask = (lesion_a > 0.5).astype(np.float32) moved_mask_flat = T2.T @ source_mask.ravel() moved_mask = (moved_mask_flat.reshape(size, size) > 0.5).astype(np.uint8)由于离散传输会拆分质量,mask 的阈值可能需要调低,例如从0.5调成0.2,然后再做一次形态学闭运算,避免内部出现空洞。
在真实医学图像中,病灶 mask 不只是二元区域,有时需要保留边界距离信息。可以用合成病灶的边界距离图来生成更平滑的 mask 边缘,但前提是合成病灶质量足够高,否则反而会引入错误标注。
4.5 与 PyTorch 训练管线集成
推荐把合成病灶离线生成并缓存到磁盘,而不是在训练时实时计算最优传输。训练阶段每次读取大量样本,实时算 N×N 传输矩阵会显著拖慢数据加载。
缓存后的训练集可以这样加载:
from torch.utils.data import Dataset import numpy as np class MixedLesionDataset(Dataset): def __init__(self, real_samples, synthetic_samples): self.samples = real_samples + synthetic_samples def __len__(self): return len(self.samples) def __getitem__(self, idx): image_path, mask_path = self.samples[idx] image = np.load(image_path).astype(np.float32) mask = np.load(mask_path).astype(np.float32) return image, mask混合比例可以先用真实样本和合成样本 1:1,再根据验证集 Dice 或 mIoU 调整。比较稳妥的做法是先只加少量合成样本,确认验证集不掉点后再逐步增加。
5. 关键参数理解与实验设置
5.1 Barycenter 权重
weights参数决定了 barycenter 更接近哪些输入病灶。如果希望通过合成样本平衡不同类别的数量,可以让权重与类别样本数成反比。例如 A 类病灶有 100 个,B 类病灶有 20 个,那么 B 类的单个样本权重可以更高,但不能简单设置一个固定值,需要根据实验调整。
表:Barycenter 权重的影响
| 权重设置 | 影响 | 适用场景 |
|---|---|---|
| 均匀权重 | 合成结果居中,不偏向任何一类 | 类别均衡时 |
| 偏向某类 | 合成结果更像该类 | 类别不平衡或形状覆盖不足 |
| 权重大于 1 | 无意义,权重会被归一化 | 不推荐 |
| 权重全为 0 | 优化不确定 | 不推荐 |
5.2 熵正则化系数
使用 Sinkhorn 计算时,reg是不可回避的参数。
reg偏大时:
- 结果平滑。
- 数值稳定。
- 收敛快。
- 但可能丢失病灶边界细节。
reg偏小时:
- 结果更接近严格最优传输。
- 边界更锐利。
- 但收敛慢,甚至出现 NaN。
- 矩阵中的质量分布更集中,合成可能产生碎片化。
建议从reg=0.01到0.1开始,针对 64×64 patch 的实验,不需要一开始选择过大值。如果看到明显模糊,可以逐步调小。
5.3 图像分辨率与计算内存
最优传输的时间复杂度和内存开销都依赖像素数量 (N)。稠密矩阵的内存是 (N^2) 数量级。
| 图像尺寸 | N | 单矩阵大约内存 | 建议 |
|---|---|---|---|
| 32×32 | 1024 | 8MB | 快速验证 |
| 64×64 | 4096 | 128MB | 常用模拟 |
| 128×128 | 16384 | 2GB | 谨慎使用 |
| 256×256 | 65536 | 32GB | 不建议稠密计算 |
实际医学图像通常远大于 256×256。因此不要把整张 CT 放进最优传输,而是裁剪成病灶 patch 或下采样到低分辨率,计算完传输后再放大回原分辨率。这样既能保留形状信息,又避免内存爆炸。
5.4 学习环境和生产环境的差异
学习环境里,单张 patch 离线生成就能满足验证。生产环境则需要考虑批量生成、缓存目录、版本管理和质量过滤。
| 维度 | 学习环境 | 生产环境 |
|---|---|---|
| 图像尺寸 | 64×64 patch | 按器官裁剪,通常 128×128 或 192×192 patch |
| 计算方式 | 单进程离线执行 | 多进程并行合成,写入缓存 |
| 参数保存 | 代码内写死 | 使用 YAML 或 JSON 保存 |
| 质量检查 | 肉眼查看 | FID、边界距离、人工抽检 |
| 缓存管理 | 临时目录 | 版本化缓存,避免重复计算 |
6. 常见问题与排查路径
6.1 合成图像出现空洞和锯齿
现象:moved图像出现大量离散亮点,病灶不连续。
原因:严格最优传输ot.emd会把质量完全搬运到成本最低的像素,离散化后容易出现“像素级跳变”。目标分布和源分布形状差异大时,这种问题更明显。
检查方式:
- 打印
T2的行和列最小、最大值,看是否有大量接近 0。 - 调大
reg使用ot.sinkhorn。 - 观察不同
reg下moved的平滑度。
处理建议:
- 将
ot.emd换成ot.sinkhorn。 - 对
moved做轻度高斯滤波。 - 后面融合时加入泊松融合或 alpha 融合。
6.2 传输矩阵和代价矩阵内存过高
现象:程序在构建M或计算T时内存溢出。
原因:图像分辨率提高后,(N^2) 矩阵内存增长非常快。
检查方式:
- 查看任务管理器或系统监控中的内存占用。
- 打印
M.nbytes和T.nbytes。 - 确认是不是在整张大图上直接计算。
处理建议:
- 把输入图像裁剪成 patch。
- 使用下采样后的低分辨率图计算传输矩阵。
- 如果必须计算大图,使用 Sinkhorn,但代价矩阵同样很大。
- 可以将代价矩阵分段计算,或者使用稀疏表示,但实践复杂度高,不建议一开始做。
6.3 病灶放到新位置后看起来像“贴纸”
现象:合成病灶边界清晰,内部纹理和周围组织差异过大,训练时模型学到的是“贴纸特征”而不是病灶特征。
原因:直接覆盖只移动了病灶灰度,没有考虑目标区域的局部亮度、噪声水平和边缘过渡。
检查方式:
- 把合成图像和真实图像放在同一窗口下对比。
- 计算合成病灶边界处的梯度。
- 分别训练“真实 only”和“合成 only”分割模型,观察是否在真实验证集上退化。
处理建议:
- 使用泊松融合处理边界。
- 根据目标区域的局部均值和方差,对合成病灶做亮度归一化。
- 在边界的 mask 上做腐蚀或膨胀,让 mask 边缘更符合图像边缘。
- 合成样本只作为补充,不全部替代真实样本。
6.4 Mask 与图像错位或 mask 出现虚影
现象:合成图像的病灶位置和对应 mask 位置不一致,或者 mask 边缘模糊。
原因:传输矩阵方向使用错误;给 mask 和图像用了不同的处理流;阈值选择不当。
检查方式:
- 确认
T2的行是源像素、列是目标像素。 - 打印
moved和moved_mask的质心坐标。 - 可视化 overlay:把 mask 边缘画在图像上,检查是否对齐。
处理建议:
- 强制复用同一份
T2计算图像和 mask。 - mask 阈值调低后做形态学闭运算。
- 在代码里加断言,确保数组坐标范围一致。
6.5 分割模型在合成数据上训练后真实数据指标下降
现象:训练集上 Dice 很高,但真实验证集 Dice 明显下降。
原因:合成数据与真实数据之间存在域偏移。模型可能更依赖合成病灶中某些不自然的边界、纹理或灰度特征。
检查方式:
- 分别记录 real-only、synthetic-only、mixed 三个实验的验证集 Dice 和 mIoU。
- 使用 FID 或 Kernel MMD 比较合成病灶和真实病灶的特征分布。
- 对错误分割样例做可视化,观察模型是否在病灶边界处持续误判。
处理建议:
- 降低合成样本在总训练集中的比例,例如从 10% 开始。
- 合成样本只参与某些训练阶段,比如先在真实数据上预训练,再混合合成数据微调。
- 对合成病灶做更严格的解剖约束,不要放到解剖关系错误的位置。
- 在评估指标上同时关注边界距离,不只关注 Dice。
7. 最佳实践与可复用检查清单
7.1 在病灶合成前先确定解剖约束
最优传输只保证数学上的分布映射,不保证医学上的合理性。一个病灶被搬到血管外、器官边界外或骨骼内部,即使形状很真实,对训练也是噪声。
实际操作中,需要准备一份“可放置区域 mask”。可以在正常图像上手动标注,也可以用器官分割结果减去真实病灶区域得到。放置区域不能覆盖目标结构以外的组织。
7.2 多尺度合成
病灶大小差异大时,单一 patch 尺寸无法覆盖所有情况。可以在 32×32、64×64、128×128 多个尺度上分别计算 barycenter 和传输映射。不同尺度对应不同病理表现,也避免模型只看到固定大小的病灶。
7.3 质量验证不能只看 Dice
合成数据的质量指标分为两层:
- 图像质量:合成病灶是否真实,可以用 FID、Gram 矩阵距离、人工评分。
- 任务收益:加入合成数据后,模型在保留的真实测试集上是否变好。
这两者不一定一致。图像质量很高的合成数据可能对模型收益不大,反而引入过拟合。最直接的标准是真实验证集上的分割指标,而不是合成图像是否“以假乱真”。
7.4 混合比例和重采样策略
推荐从合成样本占比 10% 开始实验。
| 合成样本占比 | 建议场景 |
|---|---|
| 0% | 基线 |
| 10%-20% | 小样本数据增强 |
| 30%-50% | 病灶数量严重不足 |
| 大于50% | 需要严格控制域偏移,不推荐直接使用 |
