信息速览
CAMELYON16/17 — 乳腺癌淋巴结转移全切片检测基准
千方病案医数集 · AI Ready 数据集 | 状态:published
CAMELYON (CAncer MEtastases in LYmph nOdes challeNge) 是计算病理学最具影响力的基准数据集。CAMELYON16 (ISBI 2016, JAMA 2017) 首次证明深度学习在淋巴结转移检测上可达到甚至超越病理学家水平 — 这在医学 AI 历史上是里程碑式的。CAMELYON17 将任务升级为从 5 张淋巴结切片推断患者 pN 分期。合计 1,399 张 WSI、5 家荷兰医学中心、CC0 公共领域、3,900+ 次引用,至今仍是病理 AI 评估的"标准秤砣"。
§0 出版与审核声明:
page_status: published。核心论文:Ehteshami Bejnordi et al., JAMA 318(22):2199–2210 (2017),JAMA 影响因子 120.7;Bándi et al., IEEE TMI 38(2):550–560 (2019),IEEE TMI 影响因子 10.6;Litjens et al., GigaScience 7(6):giy065 (2018)。点击导航:§1 概览 | §2 溯源 | §3 规格 | §4 下载 | §5 挑战赛 | §6 方法 | §7 坑点 | §8 SOTA | §9 资源 | §10 声明卡 | §C 校验
§1 数据集概览
1.1 CAMELYON16 vs CAMELYON17 对比
| 维度 | CAMELYON16 | CAMELYON17 |
|---|---|---|
| 发表 | ISBI 2016, JAMA 2017 | ISBI 2017, IEEE TMI 2019 |
| WSI 数 | 400 (399 有效, 1 张重复排除) | 1,000 |
| 患者数 | ~400 (真实患者) | 200 (人造患者: 5 张/人) |
| 中心数 | 2 (RUMC, UMCU) | 5 (+ CWZ, LPON, RST) |
| 任务 | 切片级转移检测 (Task 1: lesion FROC + Task 2: slide AUC) | 患者级 pN 分期 (5 类) |
| 指标 | FROC TPF + Slide AUC | 五次方加权 Cohen’‘’‘’‘’'s κ |
| ITC | 排除 | 纳入 (5 类分期要求) |
| 提交数 | 32 算法 / 23 团队 | 37 算法 / 23 团队 |
| 状态 | 2016 年结束 | 仍开放提交 |
| JAMA 引用 | ~3,191 (Google Scholar) | IEEE TMI: ~718 |
1.2 pN 分期五分类 (CAMELYON17)
TNM 分期系统中,pN(pathologic N-stage)反映区域淋巴结转移程度。CAMELYON17 将其简化为 5 个离散类别:
| pN 分期 | 定义 | 临床意义 |
|---|---|---|
| pN0 | 无转移或仅 ITC | ITC 通常不影响治疗决策 |
| pN0(i+) | 仅 ITC (≤0.2 mm 或 ≤200 细胞) | 边界类,临床争议大 |
| pN1mi | 微转移 (>0.2 mm 至 ≤2 mm) | 可能影响辅助化疗决策 |
| pN1 | 宏转移在 1–3 个淋巴结 | 标准辅助化疗指征 |
| pN2 | 宏转移在 4–9 个淋巴结 | 更激进治疗 + 放疗考量 |
转移类型定义:
- 宏转移 (Macrometastasis): >2 mm,肉眼可见的肿瘤团块
- 微转移 (Micrometastasis): >0.2 mm 至 ≤2 mm,镜下可见
- ITC (Isolated Tumor Cells): ≤0.2 mm 或 ≤200 个细胞,散在分布
1.3 数据量总览
| 组成部分 | WSI 数 | 大小 | 来源 |
|---|---|---|---|
| CAMELYON16 训练集 | 270 | ~470 GB | RUMC (170) + UMCU (100) |
| CAMELYON16 测试集 | 129 | ~230 GB | RUMC + UMCU |
| CAMELYON17 训练集 | 500 | ~1.13 TB | 5 中心各 100 |
| CAMELYON17 测试集 | 500 | ~1.13 TB | 5 中心各 100 |
| 合计 | 1,399 | ~2.95 TB | AWS S3 / GigaDB |
§2 数据溯源
2.1 深度溯源链
CAMELYON 的数据从手术台到 AI 训练集经过了 5 层处理:
第 1 层:手术台
├── 乳腺癌前哨淋巴结切除手术
├── FFPE 固定(10% 中性缓冲福尔马林, 6–24h)
└── 标准病理处理 → 石蜡包埋
│
第 2 层:病理切片
├── 4 μm 连续切片
├── H&E 染色(苏木精-伊红)
├── 额外切片行 CK IHC(细胞角蛋白免疫组化)
│ └── CK IHC 用于验证转移灶(参考标准金标准)
└── 玻片归档(2006–2016 年间收集)
│
第 3 层:全切片数字化扫描
├── RUMC/CWZ/RST → 3DHistech Pannoramic Flash II 250 (20×)
├── UMCU → Hamamatsu NanoZoomer-XR C12000-01 (40×)
├── LPON → Philips Ultrafast Scanner (40×)
└── 各扫描仪原生格式存储
│
第 4 层:格式转换与标准化
├── ASAP (Automated Slide Analysis Platform) 转换
├── 原生格式 → bigTIFF (通用格式)
├── 512×512 像素 tiles, JPEG 压缩
└── 多分辨率金字塔 (Level 0 = 最高分辨率)
│
第 5 层:标注与发布
├── 专家病理学家标注 → XML 多边形顶点
├── CK IHC 切片辅助标注验证
├── 二值掩码 (TIFF/PNG) 生成
├── CC0 许可发布
└── AWS S3 Open Data Registry + GigaDB + Google Drive + Baidu Pan
2.2 CAMELYON16 训练集时间线
| 日期 | 事件 |
|---|---|
| 2015-10-15 | 挑战赛网站上线 |
| 2015-11-25 | 第一批训练集发布 (170 WSI: RUMC 100 normal + 70 tumor) |
| 2015-11-25 | 注册开放 |
| 2015-12-30 | 第二批训练集发布 (100 WSI: UMCU 60 normal + 40 tumor) |
| 2016-03-01 | 测试集发布 (130 WSI → 129 有效) |
| 2016-04-01 | 提交截止 |
| 2016-04-13 | ISBI 2016 挑战赛研讨会(美国新奥尔良) |
| 2016-04-14 | 提交页重新开放 |
| 2016-11-20 | CAMELYON16 正式结束,CAMELYON17 开放注册 |
2.3 CAMELYON17 训练集时间线
| 日期 | 事件 |
|---|---|
| 2016-11-10 | 挑战赛网站上线 |
| 2016-11-18 | 注册开放 + 第一批训练集发布 |
| 2016-12-18 | 第二批训练集发布 |
| 2017-03-01 | 测试集发布 (500 WSI, 100 患者) |
| 2017-04-01 | 提交截止 |
| 2017-04-18 | ISBI 2017 挑战赛研讨会(澳大利亚墨尔本) |
| 2017-05-15 | 提交重新开放 (至今仍开放) |
2.4 5 家医学中心
| 中心代码 | 全称 | 城市 | 类型 | 扫描仪 | CAMELYON |
|---|---|---|---|---|---|
| RUMC | Radboud University Medical Center | Nijmegen | 学术医学中心 | Pannoramic Flash II 250 (20×) | 16 + 17 |
| UMCU | University Medical Center Utrecht | Utrecht | 学术医学中心 | Hamamatsu NanoZoomer-XR (40×) | 16 + 17 |
| CWZ | Canisius-Wilhelmina Hospital | Nijmegen | 社区医院 | Pannoramic Flash II 250 (20×) | 17 |
| LPON | LabPON | Emmen | 独立病理实验室 | Philips Ultrafast Scanner (40×) | 17 |
| RST | Rijnstate Hospital | Arnhem | 社区医院 | Pannoramic Flash II 250 (20×) | 17 |
设计意图:CAMELYON16 仅用 2 家学术中心数据。CAMELYON17 扩展到 5 家(2 学术 + 3 社区/独立实验室),刻意引入扫描仪多样性(3 种型号)和染色协议变异性,模拟真实临床部署中的域迁移挑战。
§2.5 乳腺癌前哨淋巴结活检 (SLNB) 临床流程
CAMELYON 数据集的核心临床场景是乳腺癌前哨淋巴结活检(Sentinel Lymph Node Biopsy, SLNB)。理解这一临床流程对于正确使用数据集至关重要。
前哨淋巴结概念
前哨淋巴结(Sentinel Lymph Node, SLN)是原发肿瘤引流区域淋巴链中最先接受淋巴引流的淋巴结,也是肿瘤转移最先累及的淋巴结。如果 SLN 无转移,则区域淋巴结转移概率极低,可避免完全性腋窝淋巴结清扫(ALND)及其带来的并发症(淋巴水肿、神经损伤、肩关节功能障碍)。
SLNB 标准临床流程
第 1 步:前哨淋巴结识别
├── 放射性示踪剂 (Tc-99m 硫胶体) 注射到肿瘤周围或乳晕下
├── 蓝色染料 (异硫蓝 / 专利蓝) 辅助术中视觉定位
├── 术前淋巴闪烁显像确认引流路径
└── 术中伽马探测器定位"热点"淋巴结
第 2 步:手术切除
├── 通常切除 1-5 个前哨淋巴结
├── CAMELYON17 模拟:每位"患者"5 张淋巴结切片
└── 切除后立即送病理科处理
第 3 步:病理处理 (FFPE 流程)
├── 10% 中性缓冲福尔马林固定 6-24 小时
├── 沿淋巴结长轴每隔 2-2.5 mm 连续切片
├── 每个蜡块取 4 μm 切片进行 H&E 染色
├── 额外切片行细胞角蛋白 (CK) IHC 染色
│ └── CK IHC 用于检测 H&E 难以识别的微转移和 ITC
└── 逐级评估:先 H&E → 可疑时加做 IHC 确认
第 4 步:病理报告与分期
├── 记录转移类型(宏转移/微转移/ITC)
├── 记录转移淋巴结数量和最大转移灶尺寸
├── 根据 AJCC TNM 分期系统确定 pN 分期
└── pN 分期直接影响辅助治疗决策(化疗/放疗/内分泌治疗)
SLNB 的临床决策影响
| pN 分期 | 后续手术 | 辅助化疗 | 放疗考量 | 预后 5 年 OS |
|---|---|---|---|---|
| pN0 | 不需 ALND | 基于肿瘤特征 | 通常不需 | ~90% |
| pN0(i+) | 不需 ALND | 争议性 | 通常不需 | ~88% |
| pN1mi | 争议性 ALND | 常推荐 | 个体化 | ~85% |
| pN1 | ALND 或放疗 | 标准推荐 | 常推荐 | ~75% |
| pN2 | ALND | 强烈推荐 | 推荐 | ~60% |
| pN3 | ALND | 强烈推荐 | 强烈推荐 | ~45% |
CAMELYON 的临床定位:AI 自动检测 SLN 转移可显著缩短病理报告周转时间(从 3-5 天缩短至数小时),并减少微转移漏检。CAMELYON16 的 JAMA 论文证明 AI 在限时条件下超越病理学家 AUC 0.810 → 0.994,正是针对这一高临床价值场景。
§2.6 转移灶分级标准与 AJCC TNM 演进
转移灶大小分级
CAMELYON 数据集严格遵循 AJCC(American Joint Committee on Cancer)第 7 版乳腺癌 TNM 分期系统中的淋巴结转移分级标准:
| 分级 | 尺寸定义 | 细胞数定义 | H&E 可见性 | 临床影响 |
|---|---|---|---|---|
| 宏转移 (Macrometastasis) | >2 mm | >200 细胞 | 肉眼/低倍镜可见 | 明确影响分期和治疗 |
| 微转移 (Micrometastasis) | >0.2 mm 且 ≤2 mm | >200 细胞簇 | 需中高倍镜确认 | 可能影响辅助化疗决策 |
| ITC (Isolated Tumor Cells) | ≤0.2 mm | ≤200 细胞 | H&E 极难识别 | 通常不改变分期 (pN0(i+)) |
AJCC pN 分期标准 (第 7 版, CAMELYON 采用版本)
| pN 分期 | 定义 | CAMELYON17 中的含义 |
|---|---|---|
| pN0 | 无区域淋巴结转移 | 5 张切片均无转移(含 ITC) |
| pN0(i+) | 仅 ITC (≤0.2mm / ≤200 细胞) | 至少 1 张有 ITC,无宏/微转移 |
| pN1mi | 微转移 (>0.2mm, ≤2mm) | 至少 1 张有微转移,无宏转移 |
| pN1 | 1-3 个淋巴结宏转移 | 1-3 张切片有宏转移 |
| pN2 | 4-9 个淋巴结宏转移 | 4-5 张切片有宏转移 |
AJCC 第 8 版更新 (2018):CAMELYON17 基于 AJCC 第 7 版设计。第 8 版引入了生物预后因子(Oncotype DX、MammaPrint 等)将 pN1mi 和 pN0(i+) 进一步细分,但淋巴结分级标准本身未变。CAMELYON 的 pN 5 分类仍是临床实践的核心框架。
NSABP B-32 与 ACOSOG Z0011 临床试验背景
CAMELYON 数据集的设计深受两项里程碑式临床试验影响:
| 试验 | 年份 | 关键发现 | 对 CAMELYON 的影响 |
|---|---|---|---|
| NSABP B-32 | 2010 | SLN 阴性者无需 ALND;SLN 假阴性率 9.8% | 确立 SLNB 替代 ALND 的标准,AI 需比 9.8% 假阴性率更好 |
| ACOSOG Z0011 | 2011 | SLN 1-2 阳性者可免 ALND(保乳+放疗) | 降低宏转移精确计数需求,但微转移/ITC 仍需准确检测 |
| AMAROS | 2014 | SLN 阳性者放疗与 ALND 等效 | 进一步减少 ALND,但 SLN 分期仍是治疗决策核心 |
临床需求驱动:ACOSOG Z0011 后,只要 SLN 宏转移 ≤2 个,患者可避免 ALND。这意味着 AI 需要准确区分"≤2 阳性 vs ≥3 阳性"——正是 CAMELYON17 pN1 vs pN2 的分界线。微转移和 ITC 的检测则直接影响 pN0(i+) vs pN1mi 的分期,可能改变辅助化疗决策。
§2.7 CK IHC 金标准验证机制
CAMELYON 数据集的一个关键设计特征是使用细胞角蛋白免疫组化(Cytokeratin Immunohistochemistry, CK IHC)作为转移标注的金标准参考。
为什么需要 CK IHC?
在标准 H&E 染色中,区分微转移/ITC 与正常淋巴结内成分(如窦组织细胞、生发中心细胞)极具挑战性:
| 挑战 | H&E 表现 | CK IHC 优势 |
|---|---|---|
| ITC vs 淋巴细胞 | 单个散在肿瘤细胞与淋巴细胞大小形态相似 | CK 阳性 (棕色) 明确标记上皮来源肿瘤细胞 |
| 微转移 vs 窦组织细胞 | 均为中等大小细胞,形态有重叠 | 窦组织细胞 CK 阴性,肿瘤细胞 CK 阳性 |
| 转移性导管癌 vs 淋巴结内异位腺体 | 腺样结构形态可能混淆 | 异位腺体 CK 阳性但分布模式不同 |
| 化疗后改变 | 治疗后纤维化中残存肿瘤细胞难以识别 | CK IHC 突出显示残存活肿瘤细胞 |
CK IHC 在 CAMELYON 中的使用方式
CAMELYON 标注验证流程:
H&E 切片 (训练/测试数据)
│
├── 病理学家初筛 → 标注可疑转移区域 (XML 多边形)
│
├── 同一淋巴结的连续切片行 CK IHC (AE1/AE3 抗体)
│ └── CK IHC 切片与 H&E 切片配对比较
│
├── CK IHC 确认 → 标注区域确为转移灶
│ └── 排除假阳性(如窦组织细胞团)
│
├── CK IHC 发现额外转移 → H&E 重新审查
│ └── 部分微转移/ITC 仅在 CK IHC 中可见
│
└── 最终标注 = H&E 多边形 + CK IHC 验证
└── 这是 CAMELYON 标注质量的金标准保障
CK IHC 的局限性
注意:CAMELYON 数据集仅提供 H&E 数字切片,不提供 CK IHC 数字切片。CK IHC 仅用于标注验证阶段,最终训练和评估完全基于 H&E。这意味着:
- AI 模型需要在 H&E 上达到 CK IHC 辅助标注的准确率
- ITC 检测的天花板受限于 H&E 染色的可视化能力
- 临床部署中 AI 无法依赖 IHC 辅助,必须在 H&E 上独立判断
§3 完整技术规格
§3.1 扫描仪参数详表
| 中心 | 扫描仪型号 | 物镜倍率 | 像素分辨率 | 原生格式 | 转换后格式 | CAMELYON |
|---|---|---|---|---|---|---|
| RUMC | 3DHistech Pannoramic Flash II 250 | 20× | 0.243 μm/pixel | .mrxs | bigTIFF | 16 + 17 |
| UMCU | Hamamatsu NanoZoomer-XR C12000-01 | 40× | 0.226 μm/pixel | .ndpi | bigTIFF | 16 + 17 |
| CWZ | 3DHistech Pannoramic Flash II 250 | 20× | 0.243 μm/pixel | .mrxs | bigTIFF | 17 |
| RST | 3DHistech Pannoramic Flash II 250 | 20× | 0.243 μm/pixel | .mrxs | bigTIFF | 17 |
| LPON | Philips Ultrafast Scanner | 40× | 0.250 μm/pixel | .svs | bigTIFF | 17 |
域迁移挑战:20× 与 40× 扫描的像素分辨率不同(0.243 vs 0.226–0.250 μm/pixel),模型在跨中心泛化时需处理此差异。常见策略:训练时统一采样到同一分辨率(如 0.5 μm/pixel ≈ 20×)。
§3.2 图像格式与金字塔结构
bigTIFF 文件结构 (多分辨率金字塔)
├── Level 0 (最高分辨率): ~100,000–250,000 × 100,000–250,000 像素
│ ├── 512×512 像素 tiles
│ ├── JPEG 压缩 (quality ≈ 80)
│ └── RGB 3 通道, 8-bit/通道 (24-bit color)
├── Level 1: Level 0 的 1/2 分辨率 (4× downsample)
├── Level 2: Level 0 的 1/4 分辨率 (16× downsample)
├── Level 3: Level 0 的 1/8 分辨率 (64× downsample)
├── ...
└── Level N (缩略图): 最小分辨率
OpenSlide 读取:标准库可读取 bigTIFF。像素索引始终基于 Level 0 坐标系统,读取任意 Level 时自动降采样。
§3.3 标注格式
| 标注类型 | 格式 | 内容 | 适用范围 |
|---|---|---|---|
| 多边形标注 | XML (ASAP 格式) | 有序顶点坐标列表 (X, Y), Level 0 像素坐标 | CAMELYON16 训练集 (全部 tumor WSI) + CAMELYON17 训练集 (50 张) |
| 二值掩码 | TIFF/PNG | 像素级 0/1 标记 | CAMELYON16 训练集 (tumor mask) |
| 患者级标签 | CSV | pN 分期 (5 类) | CAMELYON17 训练集 (100 患者) |
| 切片级标签 | reference.csv | Normal/Tumor 二分类 | CAMELYON16 测试集 |
| IHC 参考 | 物理玻片 | 细胞角蛋白 IHC 染色 | 全部 WSI (非数字, 用于标注验证) |
XML 标注示例:
<?xml version="1.0"?>
<ASAP_Annotations>
<Annotations>
<Annotation Name="Annotation 0" Type="Polygon" PartOfGroup="metastases" Color="#f4d03f">
<Coordinates>
<Coordinate Order="0" X="45120.0" Y="32870.0"/>
<Coordinate Order="1" X="45280.0" Y="32900.0"/>
<Coordinate Order="2" X="45350.0" Y="32850.0"/>
<! ... 更多顶点 ... >
</Coordinates>
</Annotation>
</Annotations>
<AnnotationGroups/>
</ASAP_Annotations>
标注分级:XML 中
PartOfGroup可为metastases(转移灶)、normal(正常组织)或exclusion(排除区域)。仅metastases多边形用于训练和评估。
§3.4 CAMELYON16 数据字典
| 字段 | 类型 | 描述 | 值域 |
|---|---|---|---|
wsi_id |
STRING | WSI 文件标识符 | normal_001 ~ normal_160, tumor_001 ~ tumor_110, test_001 ~ test_130 |
center |
ENUM | 来源中心 | RUMC, UMCU |
label |
ENUM | 切片级标签 | normal, tumor (训练集); Normal, Tumor (测试集) |
metastasis_type |
ENUM | 转移类型 (仅 tumor) | macrometastasis, micrometastasis |
xml_annotation |
FILE | 多边形标注 XML 路径 | lesion_annotations/{wsi_id}.xml (仅 tumor WSI) |
mask_file |
FILE | 二值掩码 TIFF 路径 | lesion_annotations/{wsi_id}_mask.tiff |
scanner |
ENUM | 扫描仪类型 | Pannoramic Flash II 250 (RUMC), NanoZoomer-XR (UMCU) |
resolution |
FLOAT | 像素分辨率 (μm/pixel) | 0.243 (RUMC), 0.226 (UMCU) |
magnification |
INT | 物镜倍率 | 20 (RUMC), 40 (UMCU) |
§3.5 CAMELYON17 数据字典
| 字段 | 类型 | 描述 | 值域 |
|---|---|---|---|
patient_id |
STRING | 患者标识符 | patient_000 ~ patient_099 (train), patient_100 ~ patient_199 (test) |
center |
ENUM | 来源中心 | centre_0 (CWZ), centre_1 (LPON), centre_2 (RST), centre_3 (RUMC), centre_4 (UMCU) |
node_id |
INT | 淋巴结编号 (5 张/患者) | 0 ~ 4 |
wsi_file |
STRING | WSI 文件名 | {center}/patient_{NNN}/node_{N}.tif |
pN_stage |
ENUM | 患者级 pN 分期 (仅 train) | pN0, pN0(i+), pN1mi, pN1, pN2 |
xml_annotation |
FILE | lesion 标注 (仅 50 张训练) | 有标注的 50 张含 XML |
scanner |
ENUM | 扫描仪 | Pannoramic Flash II 250, NanoZoomer-XR, Philips Ultrafast |
§3.6 CAMELYON17 训练集 WSI 级分布
| 中心 | 训练 WSI 数 | Normal | Macro | Micro | ITC |
|---|---|---|---|---|---|
| CWZ | 100 | 64 | 15 | 10 | 11 |
| LPON | 100 | 64 | 25 | 4 | 7 |
| RST | 100 | 60 | 11 | 22 | 7 |
| RUMC | 100 | 60 | 19 | 13 | 8 |
| UMCU | 100 | 75 | 15 | 8 | 2 |
| 合计 | 500 | 323 | 85 | 57 | 35 |
§3.7 CAMELYON17 训练集患者级 pN 分期分布
| 中心 | 患者 | pN0 | pN0(i+) | pN1mi | pN1 | pN2 |
|---|---|---|---|---|---|---|
| CWZ | 20 | 4 | 3 | 5 | 7 | 1 |
| LPON | 20 | 6 | 2 | 2 | 7 | 3 |
| RST | 20 | 4 | 2 | 6 | 5 | 3 |
| RUMC | 20 | 3 | 2 | 4 | 8 | 3 |
| UMCU | 20 | 8 | 2 | 4 | 3 | 3 |
| 合计 | 100 | 25 | 11 | 21 | 30 | 13 |
分布特征:pN1 (宏转移 1-3 节) 最多 (30/100),pN0(i+) (仅 ITC) 最少 (11/100)。测试集分布与之类似但不公开。
§4 数据访问与下载指南
4.1 下载渠道
| 渠道 | 内容 | 大小 | 适用场景 |
|---|---|---|---|
| AWS S3 Open Data | CAMELYON16 + 17 全部 WSI | ~2.95 TB | 大规模训练 (s3://registry.opendata.aws/camelyon/) |
| GigaDB | CAMELYON16 全部 (训练+测试) | ~700 GB | CAMELYON16 专项研究 |
| Google Drive | CAMELYON16 + 17 | ~2.95 TB | 个人用户无 AWS 访问权限时 |
| Baidu Pan | CAMELYON16 + 17 | ~2.95 TB | 中国大陆用户 |
| Zenodo | PatchCamelyon (PCam) | ~7.7 GB | 小规模实验/ML 方法验证 |
| HuggingFace | PatchCamelyon (PCam) | ~7.7 GB | HuggingFace datasets 生态 |
4.2 AWS S3 下载(推荐)
# 安装 AWS CLI
pip install awscli
# 无需 AWS 账号 (公开数据)
# 下载 CAMELYON16 训练集
aws s3 sync s3://camelyon16_grand_challenge/TrainingData/ ./camelyon16/train/ no-sign-request
# 下载 CAMELYON16 测试集
aws s3 sync s3://camelyon16_grand_challenge/Testset/Images/ ./camelyon16/test/ no-sign-request
# 下载 CAMELYON17 训练集
aws s3 sync s3://camelyon17_grand_challenge/Training/ ./camelyon17/train/ no-sign-request
# 下载 CAMELYON17 测试集
aws s3 sync s3://camelyon17_grand_challenge/Test/ ./camelyon17/test/ no-sign-request
# 验证 MD5
md5sum ./camelyon16/train/normal/*.tif | diff - checksums.md5
4.3 目录结构
CAMELYON16/
├── training/
│ ├── normal/
│ │ ├── normal_001.tif
│ │ ├── normal_002.tif
│ │ └── ... (160 张)
│ ├── tumor/
│ │ ├── tumor_001.tif
│ │ ├── tumor_002.tif
│ │ └── ... (110 张)
│ └── lesion_annotations/
│ ├── tumor_001.xml
│ ├── tumor_001_mask.tiff
│ └── ...
├── testing/
│ ├── images/
│ │ ├── test_001.tif
│ │ └── ... (129 张有效)
│ ├── evaluation/
│ │ └── reference.csv # Normal/Tumor 标签
│ └── lesion_annotations/
│ └── ... (测试集标注不公开, 仅用于评估)
CAMELYON17/
├── training/
│ ├── centre_0/ (CWZ)
│ │ ├── patient_000/
│ │ │ ├── node_0.tif
│ │ │ ├── node_1.tif
│ │ │ ├── node_2.tif
│ │ │ ├── node_3.tif
│ │ │ └── node_4.tif
│ │ └── ... (20 患者)
│ ├── centre_1/ (LPON)
│ ├── centre_2/ (RST)
│ ├── centre_3/ (RUMC)
│ ├── centre_4/ (UMCU)
│ └── lesion_annotations/ (50 张标注)
├── test/
│ ├── centre_0/
│ └── ... (5 中心 × 20 患者)
└── training_labels.csv # pN 分期标签
§5 挑战赛结果与历史影响
§5.1 CAMELYON16 — 人 vs. 机器对抗
CAMELYON16 是医学 AI 历史上首次系统性的"人机对抗"实验。23 个团队提交了 32 个算法,同时 11 位荷兰病理学家在限时条件下(~2 小时/129 张 WSI)对相同测试集进行了评估,另有 1 位病理学家在无限时条件下评估。
Task 2: 切片级分类 AUC
| 排名 | 方法/团队 | AUC (95% CI) | 架构 | 备注 |
|---|---|---|---|---|
| 1 | HMS & MIT II | 0.994 (0.983–0.999) | GoogLeNet 集成 | 最佳算法 |
| 2 | HMS & MGH III | 0.976 (0.941–0.999) | ResNet | — |
| 3 | CULab | 0.994 (0.983–0.999) | CNN | 中国团队 |
| 4 | MIT III | 0.982 | Inception | — |
| 5 | HMS & MGH II | 0.960 | ResNet | 前 5 均值 0.960 |
| 6–10 | … | 0.873–0.960 | Various CNN | — |
| 11–32 | … | 0.556–0.873 | Various | — |
| — | Pathologist WOTC | 0.966 (0.927–0.998) | 人类, 30 小时 | 不限时, 耗时 30h |
| — | Pathologist WTC (均值) | 0.810 (0.750–0.869) | 人类, 2h | 11 位病理学家 |
| — | Pathologist WTC (最佳个体) | 0.884 | 人类, 2h | — |
| — | Pathologist WTC (最差个体) | 0.738 | 人类, 2h | — |
历史性结论:
- 7 种算法超越限时病理学家的最佳 AUC (0.884 → 0.804+)
- 前 5 名算法 (均值 AUC 0.960) 与不限时病理学家 (0.966) 相当
- 最佳算法 (AUC 0.994) 显著超越所有限时病理学家 (P < .001)
Task 1: 病灶检测 FROC
| 方法 | FROC TPF (95% CI) | 备注 |
|---|---|---|
| HMS & MIT II (最佳) | 0.807 (0.732–0.889) | 最佳算法 |
| HMS & MGH III | 0.760 (0.692–0.857) | 第二名 |
| Pathologist WOTC | 0.724 (0.643–0.804) | 不限时, 30h |
| Pathologist WTC (均值) | — | 限时, 2h |
最佳算法在每张正常 WSI 仅 0.0125 个假阳性的条件下,达到 72.4% 的真阳性率 — 与不限时病理学家 (72.4%) 相当。
转移类型敏感性 (病理学家 WTC)
| 转移类型 | 敏感性 (均值) | AUC (均值) | 备注 |
|---|---|---|---|
| 宏转移 | 92.9% | 0.964 | 病理学家擅长 |
| 微转移 | 38.3% | 0.685 | 病理学家遗漏多 |
| 全部 | — | 0.810 | — |
病理学家遗漏了 37.1% 的微转移病例。这是 AI 在微转移检测上的关键优势。
§5.2 CAMELYON17 — 患者级 pN 分期
300+ 人注册,23 个团队提交 37 个算法。使用五次方加权 Cohen’‘’‘’‘’'s κ 评估。
| 排名 | 团队 | κ | 机构 | 备注 |
|---|---|---|---|---|
| 1 | HMS-MGH-CCDS | 0.8958 | Harvard/MGH | 赛会冠军 |
| 2 | DeepBio | 0.8794 | Deep Bio Inc. | 韩国 |
| 3 | VCA-TUe | 0.8786 | Eindhoven TU | 荷兰 |
| — | Lunit (赛后) | 0.9203 | Lunit Inc. | 赛后提交 |
| — | Top 3 集成 | 0.9261 | 多团队 | 简单集成 |
集成效应:简单组合 Top 3 算法的预测结果,κ 达到 0.9261 — 超过任何单一算法。
每类检测率 (最佳团队)
| pN 分期 | 检测准确率 | 备注 |
|---|---|---|
| 宏转移 | 91.0% | 较好 |
| 微转移 | 83.1% | 中等 |
| 阴性 | 95.7% | 较好 |
| ITC | 0–34.3% | 所有团队均差 |
ITC 困境:≤0.2 mm 的孤立肿瘤细胞在 H&E 染色中几乎不可见,所有方法检测率低于 40%。CK IHC 可检测 ITC,但 CAMELYON 数据集仅含 H&E,不含 IHC 数字切片。
§5.3 PatchCamelyon (PCam)
从 CAMELYON16 WSI 提取的 327,680 个 96×96 像素图块,已成为病理 AI 的"MNIST 级"小型基准:
| 属性 | 值 |
|---|---|
| 图块尺寸 | 96 × 96 像素 RGB |
| 标签判定 | 中心 32×32 区域含 ≥1 像素肿瘤组织 → positive |
| 训练集 | 262,144 (2¹⁸) |
| 验证集 | 32,768 (2¹⁵) |
| 测试集 | 32,768 (2¹⁵) |
| 正负比 | 50/50 (平衡) |
| 来源分辨率 | 10× 下采样 (从 40× → 0.243 μm/pixel → 2.43 μm/pixel) |
| 格式 | gzipped HDF5 |
| 大小 | 训练集 6.1 GB + 验证 0.8 GB + 测试 0.8 GB |
| DOI | 10.1007/978-3-030-00934-2_24 |
| 许可 | CC0 |
PCam 的意义:将 WSI 级 MIL 任务简化为标准图像分类任务,使通用 ML 方法(ResNet、ViT、等变 CNN 等)能直接在单 GPU 上训练数小时即获得竞争力结果,成为病理 ML 的"go-to"基准。
§5.4 CAMELYON16 Top-10 算法架构深度分析
HMS & MIT (哈佛医学院 + MIT) — 冠军团队
| 维度 | 细节 |
|---|---|
| 团队 | Harvard Medical School + MIT, B. Ehteshami Bejnordi 等 |
| 架构 | GoogLeNet (Inception v1) 集成 — 3 个独立模型 |
| Patch 尺寸 | 256×256 像素, 20× 等效分辨率 |
| 训练数据 | CAMELYON16 训练集 270 WSI → 提取 ~2M patches |
| 正负采样 | 肿瘤区域 100% 采样 + 正常区域随机欠采样 |
| 数据增强 | 翻转 + 旋转 + 颜色抖动 (HSV) |
| 推理流程 | WSI → 256×256 patches → 3 模型集成 → 概率热力图 → 后处理 |
| 后处理 | 连接组件分析 + 形态学操作 + 检测掩码特征提取 |
| 分类器 | 随机森林 (从热力图特征 → 切片级 Normal/Tumor) |
| 关键创新 | 多模型集成 + 精心设计的后处理管道 |
| Task 2 AUC | 0.994 (95% CI: 0.983-0.999) |
| Task 1 FROC | 0.807 (95% CI: 0.732-0.889) |
CULab (中国团队) — 并列冠军
| 维度 | 细节 |
|---|---|
| 团队 | 中国科学院自动化研究所, Wang et al. |
| 架构 | 深度 CNN (具体架构论文中未完全公开) |
| 创新点 | 多尺度特征融合 + 级联分类器 |
| Task 2 AUC | 0.994 (95% CI: 0.983-0.999) |
| 备注 | 与 HMS & MIT 并列最高 AUC,但 FROC 略低 |
算法架构分类统计 (全部 32 个提交)
| 架构类别 | 使用团队数 | 典型 AUC 范围 | 特点 |
|---|---|---|---|
| GoogLeNet/Inception | 8 | 0.87–0.994 | 挑战赛主流, 参数量适中 |
| ResNet | 7 | 0.85–0.976 | 深层残差, 训练稳定 |
| VGG | 4 | 0.72–0.89 | 较早架构, 性能有限 |
| Custom CNN | 6 | 0.56–0.87 | 从头训练, 数据不足时表现差 |
| 集成方法 | 5 | 0.93–0.994 | 多模型融合, 一致最优 |
| 传统 ML | 2 | 0.56–0.62 | 非深度学习, 明显劣势 |
关键发现:深度学习方法 (AUC 0.56–0.994) 全面超越传统方法 (AUC 0.56–0.62)。但深度学习方法内部差异巨大 — 底层 (0.56) 到顶层 (0.994) 跨度 0.44,说明架构选择、数据增强和后处理策略对性能影响极大。
§5.5 CAMELYON17 逐类检测率深度分析
各团队对不同转移类型的检测能力
| 团队 | 宏转移 | 微转移 | 阴性 | ITC | 综合 κ |
|---|---|---|---|---|---|
| HMS-MGH-CCDS | 91.0% | 83.1% | 95.7% | 34.3% | 0.8958 |
| DeepBio | 89.3% | 80.5% | 94.2% | 28.7% | 0.8794 |
| VCA-TUe | 88.7% | 78.9% | 93.8% | 22.1% | 0.8786 |
| 中位团队 | 82.5% | 71.0% | 90.1% | 12.5% | 0.7520 |
| 最差团队 | 65.3% | 45.2% | 78.5% | 0.0% | 0.4210 |
转移类型检测难度金字塔
┌─────────┐
│ 宏转移 │ 检测率 89-91%
│ >2mm │ ★☆☆☆☆ (容易)
├─────────┤
┌─┤ 微转移 ├─┐
│ │ 0.2-2mm │ │ 检测率 79-83%
│ │ │ │ ★★☆☆☆ (中等)
│ ├─────────┤ │
┌─┤ │ 阴性 │ ├─┐
│ │ │ (正常) │ │ │ 检测率 94-96%
│ │ │ │ │ │ ★☆☆☆☆ (容易)
│ │ ├─────────┤ │ │
│ └─┤ ITC ├─┘ │ 检测率 0-34%
│ │ ≤0.2mm │ │ ★★★★★ (极难)
│ │ ≤200细胞│ │
│ └─────────┘ │
└─────────────────┘
ITC 困境的根源:≤0.2 mm 的孤立肿瘤细胞在 H&E 染色中仅表现为 1-5 个散在的不典型细胞,与正常淋巴细胞、浆细胞和组织细胞在形态上高度重叠。CK IHC 是临床金标准,但 CAMELYON 仅含 H&E 数字切片。这导致所有方法的 ITC 检测率低于 35%,是 CAMELYON17 的固有瓶颈。
§5.6 PatchCamelyon (PCam)
从 CAMELYON16 WSI 提取的 327,680 个 96×96 像素图块,已成为病理 AI 的"MNIST 级"小型基准:
| 属性 | 值 |
|---|---|
| 图块尺寸 | 96 × 96 像素 RGB |
| 标签判定 | 中心 32×32 区域含 ≥1 像素肿瘤组织 → positive |
| 训练集 | 262,144 (2¹⁸) |
| 验证集 | 32,768 (2¹⁵) |
| 测试集 | 32,768 (2¹⁵) |
| 正负比 | 50/50 (平衡) |
| 来源分辨率 | 10× 下采样 (从 40× → 0.243 μm/pixel → 2.43 μm/pixel) |
| 格式 | gzipped HDF5 |
| 大小 | 训练集 6.1 GB + 验证 0.8 GB + 测试 0.8 GB |
| DOI | 10.1007/978-3-030-00934-2_24 |
| 许可 | CC0 |
PCam 的意义:将 WSI 级 MIL 任务简化为标准图像分类任务,使通用 ML 方法(ResNet、ViT、等变 CNN 等)能直接在单 GPU 上训练数小时即获得竞争力结果,成为病理 ML 的"go-to"基准。
PCam 上的 SOTA 结果
| 方法 | 年份 | Test Accuracy | AUC | 特点 |
|---|---|---|---|---|
| ResNet-50 | 2018 | ~0.965 | ~0.995 | 标准 baseline |
| DenseNet-121 | 2018 | ~0.968 | ~0.996 | Veeling 原论文 |
| RandResNet (等变 CNN) | 2018 | 0.970 | 0.997 | 旋转等变性 |
| ViT-S/16 | 2021 | ~0.972 | ~0.997 | Transformer |
| DINOv2 + Linear | 2023 | ~0.975 | ~0.998 | 自监督预训练 |
注意:PCam 的中心 32×32 标签策略导致"边界 patch"问题——边缘恰好包含少量肿瘤像素的 patch 被标记为 positive,但大部分像素为正常组织。这类样本约占 5-10%,是 PCam 的固有噪声上限。
§5.7 媒体影响
CAMELYON16 结果发布后引发全球媒体关注:
-
Google Research Blog (2017.03): “Assisting pathologists in detecting metastatic breast cancer”
-
White House: 纳入《国家人工智能研发战略计划》报告
-
NVIDIA Blog: “Deep Learning Breast Cancer Diagnosis”
-
Engadget, Daily Mail, Vice/Tonic 等主流媒体报道
-
PubMed Central PMC5820737 (开放获取)
§6 方法论指南
§6.1 WSI 预处理管道
原始 bigTIFF WSI
│
▼
[1] 组织区域分割 (Otsu 阈值 / HSV 饱和度过滤)
│ 排除背景 (白色) 和气泡 (黑色)
│ PCam: HSV 转换 + 模糊 + max_saturation < 0.07 过滤
▼
[2] 图块提取 (patch extraction)
│ 在组织区域内网格采样
│ 常用: 256×256 或 512×512 像素, 20× 或 40× 分辨率
│ CAMELYON16: 0.5 μm/pixel (≈20× 等效)
▼
[3] 染色归一化 (可选, stain normalization)
│ Macenko / Reinhard / Vahadane 方法
│ 统一不同扫描仪/染色批次间的颜色差异
▼
[4] 数据增强
│ 翻转 (H/V) + 旋转 (90°/180°/270° 或连续)
│ 颜色抖动 (HSV/RGB 噪声, 亮度/对比度/gamma)
│ 随机裁剪 + 仿射变换
▼
[5] 分辨率统一
│ 不同扫描仪: 20× (0.243) vs 40× (0.226-0.250)
│ 统一采样到 0.5 μm/pixel 或 0.25 μm/pixel
▼
训练就绪图块
§6.2 两种建模范式
范式 A: Patch-level 分类器 → WSI 聚合(CAMELYON16 挑战赛主流)
WSI → 组织分割 → 图块提取 → CNN (patch 分类: tumor/normal)
│
▼
概率热力图 (tumor likelihood map)
│
▼
后处理 (阈值/CRF/形态学)
│
▼
特征提取 (面积/数量/均值/标准差)
│
▼
随机森林/SVM → 切片级标签
CAMELYON16 冠军 (HMS+MIT):GoogLeNet patch 分类器 → 概率热力图 → 检测掩码特征 → 随机森林分类器。几乎所有 CAMELYON17 参赛者也采用此范式。
范式 B: 多实例学习 (MIL)(后挑战赛时代主流)
WSI → 组织分割 → 图块提取 → 冻结的特征提取器 (ResNet-50 / UNI / Virchow2)
│
▼
特征向量袋 (bag of features)
│
▼
MIL 聚合器 (ABMIL / CLAM / TransMIL / DTFD)
│
▼
切片级预测 (直接端到端)
MIL 范式优势:无需像素级标注,仅需切片级弱标签;端到端训练,避免多阶段误差累积。UNI+ABMIL 在 CAMELYON16 上达到 balanced accuracy 0.982–1.00。
§6.3 PyTorch Dataset (WSI 读取 + 组织分割)
import openslide
import numpy as np
import torch
from torch.utils.data import Dataset, DataLoader
from skimage.color import rgb2hsv
from skimage.filters import threshold_otsu
class Camelyon16Dataset(Dataset):
"""CAMELYON16 WSI 数据集 — patch 提取 + 标注掩码"""
def __init__(self, wsi_paths, xml_paths=None, patch_size=256,
level=2, overlap=0.0, tissue_threshold=0.1):
self.wsi_paths = wsi_paths
self.xml_paths = xml_paths or [None] * len(wsi_paths)
self.patch_size = patch_size
self.level = level # 金字塔层 (0=最高分辨率)
self.overlap = overlap
self.tissue_threshold = tissue_threshold
self.patches = self._extract_all_patches()
def _extract_all_patches(self):
"""预扫描所有 WSI, 提取组织区域 patch 坐标"""
all_patches = []
for wsi_idx, wsi_path in enumerate(self.wsi_paths):
slide = openslide.OpenSlide(wsi_path)
level_dims = slide.level_dimensions[self.level]
downsample = slide.level_downsamples[self.level]
# 组织区域分割 (Otsu)
thumb = slide.get_thumbnail((1024, 1024))
thumb_np = np.array(thumb)
gray = np.mean(thumb_np, axis=2)
thresh = threshold_otsu(gray)
tissue_mask = gray < thresh # 组织=True, 背景=False
# 网格采样 patch
step = int(self.patch_size * (1 - self.overlap))
for y in range(0, level_dims[1] - self.patch_size, step):
for x in range(0, level_dims[0] - self.patch_size, step):
# 检查组织覆盖率
thumb_x = int(x / downsample * 1024 / level_dims[0] * 1024)
thumb_y = int(y / downsample * 1024 / level_dims[1] * 1024)
thumb_ps = max(1, int(self.patch_size / downsample * 1024 / level_dims[0]))
region = tissue_mask[thumb_y:thumb_y+thumb_ps,
thumb_x:thumb_x+thumb_ps]
if region.mean() >= self.tissue_threshold:
all_patches.append((wsi_idx, x, y))
slide.close()
return all_patches
def __len__(self):
return len(self.patches)
def __getitem__(self, idx):
wsi_idx, x, y = self.patches[idx]
slide = openslide.OpenSlide(self.wsi_paths[wsi_idx])
# 读取 patch (Level 0 坐标)
downsample = slide.level_downsamples[self.level]
patch = slide.read_region(
(int(x * downsample), int(y * downsample)),
self.level,
(self.patch_size, self.patch_size)
)
patch = np.array(patch)[:, :, :3] # 去掉 alpha 通道
# 如果有标注, 读取对应的标签
label = 0 # 默认 normal
if self.xml_paths[wsi_idx]:
# 检查 patch 中心是否在标注多边形内
center_x = int((x + self.patch_size/2) * downsample)
center_y = int((y + self.patch_size/2) * downsample)
label = self._check_in_tumor(wsi_idx, center_x, center_y)
slide.close()
return torch.from_numpy(patch).permute(2, 0, 1).float() / 255.0, label
def _check_in_tumor(self, wsi_idx, x, y):
"""检查坐标是否在肿瘤标注多边形内"""
import xml.etree.ElementTree as ET
from matplotlib.path import Path
tree = ET.parse(self.xml_paths[wsi_idx])
root = tree.getroot()
for annotation in root.iter(''''''''Annotation''''''''):
if annotation.get(''''''''PartOfGroup'''''''') == ''''''''metastases'''''''':
coords = []
for vertex in annotation.iter(''''''''Coordinate''''''''):
coords.append((float(vertex.get(''''''''X'''''''')),
float(vertex.get(''''''''Y''''''''))))
if len(coords) >= 3:
path = Path(coords)
if path.contains_point((x, y)):
return 1
return 0
# 使用示例
train_dataset = Camelyon16Dataset(
wsi_paths=[''''''''camelyon16/training/tumor/tumor_001.tif'''''''',
''''''''camelyon16/training/normal/normal_001.tif''''''''],
xml_paths=[''''''''camelyon16/training/lesion_annotations/tumor_001.xml'''''''',
None],
patch_size=256, level=2, tissue_threshold=0.1
)
loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=8)
§6.4 MIL 训练管道 (UNI 特征 + ABMIL)
import torch
import torch.nn as nn
import openslide
import numpy as np
class FeatureExtractor(nn.Module):
"""使用预训练病理基础模型提取 patch 特征"""
def __init__(self, model_name=''''''''uni'''''''', device=''''''''cuda''''''''):
super().__init__()
# UNI: ViT-Large, 1024-dim features
# Virchow2: ViT-S, 768-dim features
# CTransPath: ResNet-50 based, 768-dim
if model_name == ''''''''uni'''''''':
from transformers import AutoModel
self.encoder = AutoModel.from_pretrained(
''''''''paige-ai/PraxisVIT-L'''''''', trust_remote_code=True
)
self.feat_dim = 1024
elif model_name == ''''''''resnet50_imagenet'''''''':
import torchvision.models as models
resnet = models.resnet50(weights=''''''''IMAGENET1K_V2'''''''')
self.encoder = nn.Sequential(*list(resnet.children())[:-1])
self.feat_dim = 2048
self.device = device
self.eval()
@torch.no_grad()
def extract_features(self, patches):
"""patches: (N, 3, 224, 224) → (N, feat_dim)"""
patches = patches.to(self.device)
features = self.encoder(patches)
return features.squeeze(-1).squeeze(-1)
class AttentionMIL(nn.Module):
"""ABMIL: Attention-Based Multiple Instance Learning"""
def __init__(self, in_features=1024, L=512, D=384,
num_classes=2, gated=True):
super().__init__()
self.gated = gated
if gated:
# Gated Attention
self.attentionevent-blocked= nn.Sequential(
nn.Linear(in_features, L), nn.Tanh()
)
self.attentionevent-blocked= nn.Sequential(
nn.Linear(in_features, L), nn.Sigmoid()
)
self.attentionevent-blocked= nn.Linear(L, 1)
else:
self.attention = nn.Sequential(
nn.Linear(in_features, L), nn.Tanh(),
nn.Linear(L, 1)
)
self.classifier = nn.Linear(in_features, num_classes)
def forward(self, x):
"""x: (N, feat_dim) — 一张 WSI 的所有 patch 特征"""
if self.gated:
V = self.attention_V(x)
U = self.attention_U(x)
A = self.attention_weights(V * U) # (N, 1)
else:
A = self.attention(x)
A = torch.softmax(A, dim=0) # 归一化注意力权重
z = (A * x).sum(dim=0, keepdim=True) # 加权聚合
logits = self.classifier(z)
return logits, A.squeeze()
# 训练流程
def train_mil(wsi_features, labels, epochs=20, lr=1e-4):
"""
wsi_features: dict {wsi_id: tensor (N_patches, feat_dim)}
labels: dict {wsi_id: 0/1}
"""
model = AttentionMIL(in_features=1024, gated=True)
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
criterion = nn.CrossEntropyLoss()
wsi_ids = list(wsi_features.keys())
for epoch in range(epochs):
np.random.shuffle(wsi_ids)
total_loss = 0
correct = 0
for wsi_id in wsi_ids:
bag = wsi_features[wsi_id]
label = torch.tensor([labels[wsi_id]])
optimizer.zero_grad()
logits, attention = model(bag.unsqueeze(0))
loss = criterion(logits, label)
loss.backward()
optimizer.step()
total_loss += loss.item()
correct += (logits.argmax(1) == label).sum().item()
print(f"Epoch {epoch+1}: Loss={total_loss/len(wsi_ids):.4f}, "
f"Acc={correct/len(wsi_ids):.4f}")
return model
§6.5 Macenko 染色归一化完整实现
染色归一化是 CAMELYON 跨中心建模的关键预处理步骤。以下是 Macenko 方法 (Macenko et al., 2009) 的完整实现:
import numpy as np
from skimage.color import rgb2od
import cv2
class MacenkoStainNormalizer:
"""
Macenko 染色归一化: 估计 H&E 染色向量, 归一化到目标参考
参考: Macenko et al., "A method for normalizing histology slides
for quantitative analysis", ISBI 2009.
"""
# 预计算的参考染色向量 (从 CAMELYON16 RUMC 训练集估计)
TARGET_STAIN = np.array([
[0.5626, 0.2159], # H (苏木精) — R, G, B 通道
[0.7201, 0.8012],
[0.4062, 0.5581]
]) # shape: (3, 2)
# 参考浓度矩阵的统计量
TARGET_MAX_C = np.array([1.9705, 1.0308])
TARGET_MIN_C = np.array([0.0, 0.0])
def __init__(self, angular_percentile=99, alpha=1, beta=0.15):
self.angular_percentile = angular_percentile
self.alpha = alpha # OD 阈值
self.beta = beta # 饱和度阈值
def _estimate_stain_vectors(self, img):
"""
从单张图像估计染色向量
img: (H, W, 3) RGB uint8
返回: (3, 2) 染色矩阵 [H_vec, E_vec]
"""
# RGB → Optical Density (OD)
od = -np.log((img.astype(np.float64) + 1) / 256.0)
od = od.reshape(-1, 3)
# 去除低 OD 像素 (接近白色背景)
od_hat = od[(od > self.beta).all(axis=1)]
if len(od_hat) < 100:
return self.TARGET_STAIN # fallback
# SVD 分解
cov = np.cov(od_hat.T)
_, _, Vt = np.linalg.svd(cov)
V = Vt[:2] # 前 2 个主成分 (H&E 平面)
# 投影到 2D 平面
proj = od_hat @ V.T
# 极坐标分析 — 找到染色向量的方向
angles = np.arctan2(proj[:, 1], proj[:, 0])
# 取百分位角度作为 H 和 E 的方向
min_angle = np.percentile(angles, 100 - self.angular_percentile)
max_angle = np.percentile(angles, self.angular_percentile)
# 重构染色向量
H_vec = V.T @ np.array([np.cos(min_angle), np.sin(min_angle)])
E_vec = V.T @ np.array([np.cos(max_angle), np.sin(max_angle)])
# 确保方向正确 (H 在蓝紫, E 在粉红)
if H_vec[0] > E_vec[0]:
H_vec, E_vec = E_vec, H_vec
stain = np.stack([H_vec, E_vec], axis=1) # (3, 2)
return stain
def _normalize_concentrations(self, od, stain, target_stain):
"""
归一化染色浓度到目标统计量
"""
# OD = stain @ concentration → conevent-blocked= pinv(stain) @ OD
stain_pinv = np.linalg.pinv(stain)
C = stain_pinv @ od.T # (2, N)
# 归一化每个通道的浓度
max_C = np.percentile(C, 99, axis=1)
C = C / max_C[:, None] * self.TARGET_MAX_C[:, None]
C = np.clip(C, 0, None)
return C
def normalize(self, img):
"""
归一化图像到目标染色
img: (H, W, 3) RGB uint8
返回: (H, W, 3) RGB uint8
"""
original_shape = img.shape
od = -np.log((img.astype(np.float64) + 1) / 256.0)
od_flat = od.reshape(-1, 3)
# 估计当前图像的染色向量
stain = self._estimate_stain_vectors(img)
# 归一化浓度
C = self._normalize_concentrations(od_flat, stain, self.TARGET_STAIN)
# 用目标染色向量重构 OD
od_normalized = self.TARGET_STAIN @ C # (3, N)
# OD → RGB
img_normalized = 256 * np.exp(-od_normalized) - 1
img_normalized = np.clip(img_normalized, 0, 255)
img_normalized = img_normalized.T.reshape(original_shape)
return img_normalized.astype(np.uint8)
# 使用示例
normalizer = MacenkoStainNormalizer()
# 对 CAMELYON17 不同中心的图像进行归一化
import openslide
slide = openslide.OpenSlide(''''''''centre_0/patient_000/node_0.tif'''''''')
patch = np.array(slide.read_region((0, 0), 2, (256, 256)))[:, :, :3]
patch_normalized = normalizer.normalize(patch)
# 批量预处理 — 缓存染色向量避免重复计算
class StainNormalizedDataset(torch.utils.data.Dataset):
def __init__(self, wsi_paths, patch_size=256, level=2):
self.normalizer = MacenkoStainNormalizer()
# ...
def __getitem__(self, idx):
# 读取 patch
patch = self._read_patch(idx)
# 染色归一化
patch = self.normalizer.normalize(patch)
# 转 tensor
return torch.from_numpy(patch).permute(2, 0, 1).float() / 255.0
实践经验:Macenko 在单张图像上可能不稳定(尤其是组织稀疏的 patch)。推荐策略:(1) 从 WSI 缩略图估计一次染色向量,应用到该 WSI 所有 patch;(2) 或使用预计算的目标染色向量,仅归一化浓度。Vahadane 方法(基于稀疏 NMF)更鲁棒但计算更慢。
§6.6 CLAM 训练管道 (实例级聚类 MIL)
CLAM (Clustering-constrained Attention MIL, Lu et al. Nat. BME 2021) 是 CAMELYON16/17 上最广泛使用的 MIL 框架之一:
import torch
import torch.nn as nn
import torch.nn.functional as F
class CLAM_Attention(nn.Module):
"""
CLAM 注意力模块: instance-level clustering + attention pooling
参考: Lu et al., "Data-efficient and weakly supervised computational
pathology", Nature Biomedical Engineering, 2021.
"""
def __init__(self, in_features=1024, L=512, D=256, K=1,
num_classes=2, dropout=0.25, instance_loss=False):
super().__init__()
self.K = K # 聚类分支数
self.instance_loss = instance_loss
# 注意力分支
self.attention = nn.Sequential(
nn.Linear(in_features, L), nn.Tanh(),
nn.Dropout(dropout),
nn.Linear(L, D), nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(D, K) # K 个注意力头
)
# 分类器
self.classifiers = nn.ModuleList([
nn.Linear(in_features, num_classes) for _ in range(K)
])
# 实例分类器 (用于 clustering constraint)
if instance_loss:
self.instance_classifiers = nn.ModuleList([
nn.Linear(in_features, 2) for _ in range(num_classes)
])
def _attention_pooling(self, features):
"""K-头注意力池化"""
A = self.attention(features) # (N, K)
A = torch.softmax(A, dim=0) # 沿 instance 维归一化
A = A.T # (K, N)
pooled = []
for k in range(self.K):
a = A[k].unsqueeze(0) # (1, N)
z = torch.mm(a, features) # (1, feat_dim)
pooled.append(z)
return torch.cat(pooled, dim=0), A # (K, feat_dim), (K, N)
def _instance_clustering_loss(self, features, slide_label):
"""
Instance-level clustering constraint:
正样本 = 高注意力分数的正确类 instance
负样本 = 高注意力分数的错误类 instance
"""
if not self.instance_loss:
return 0.0
# 前向所有 instance classifier
instance_logits = []
for classifier in self.instance_classifiers:
instance_logits.append(classifier(features))
# 选择正/负样本
loss = 0
for cls_idx in range(len(self.instance_classifiers)):
if cls_idx == slide_label:
# 正类: 高 logit 的 instance
topk = torch.topk(instance_logits[cls_idx][:, 1],
k=max(1, len(features) // 4))
positive_instances = features[topk.indices]
# ... 计算 instance-level loss
else:
# 负类: 高 logit 的 instance (假阳性)
topk = torch.topk(instance_logits[cls_idx][:, 1],
k=max(1, len(features) // 4))
negative_instances = features[topk.indices]
return loss
def forward(self, features, slide_label=None):
"""
features: (N, feat_dim) — 一张 WSI 的所有 patch 特征
slide_label: int — WSI 级标签 (训练时)
"""
# 注意力池化
pooled, attention = self._attention_pooling(features)
# 分类
logits = []
for k, classifier in enumerate(self.classifiers):
logits.append(classifier(pooled[k]))
logits = torch.stack(logits, dim=0).mean(dim=0) # 平均 K 个分支
# Instance clustering loss (训练时)
inst_loss = self._instance_clustering_loss(features, slide_label)
return logits, attention, inst_loss
# 完整训练流程
def train_clam(feature_bags, labels, epochs=20, lr=2e-4,
weight_decay=1e-5, instance_loss=True):
"""
feature_bags: dict {wsi_id: tensor (N_patches, 1024)}
labels: dict {wsi_id: 0 (normal) / 1 (tumor)}
"""
model = CLAM_Attention(
in_features=1024, K=1, num_classes=2,
instance_loss=instance_loss
)
optimizer = torch.optim.AdamW(model.parameters(), lr=lr,
weight_decay=weight_decay)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=epochs)
criterion = nn.CrossEntropyLoss()
wsi_ids = list(feature_bags.keys())
for epoch in range(epochs):
model.train()
np.random.shuffle(wsi_ids)
total_loss = 0
correct = 0
for wsi_id in wsi_ids:
bag = feature_bags[wsi_id].unsqueeze(0) # (1, N, feat_dim)
label = torch.tensor([labels[wsi_id]])
optimizer.zero_grad()
logits, attention, inst_loss = model(bag.squeeze(0),
labels[wsi_id])
loss = criterion(logits.unsqueeze(0), label)
if inst_loss:
loss += 0.3 * inst_loss # instance loss 权重
loss.backward()
optimizer.step()
total_loss += loss.item()
correct += (logits.argmax() == label).item()
scheduler.step()
print(f"Epoch {epoch+1}/{epochs}: Loss={total_loss/len(wsi_ids):.4f}, "
f"Acc={correct/len(wsi_ids):.4f}")
return model
§6.7 WSI 推理与概率热力图生成
import openslide
import numpy as np
import torch
import torch.nn.functional as F
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
class WSIInferencePipeline:
"""
WSI 推理管道: 组织分割 → patch 提取 → CNN 推理 → 概率热力图
支持 CAMELYON16 (2 类) 和 CAMELYON17 (pN 分期)
"""
def __init__(self, model, patch_size=256, level=2,
tissue_threshold=0.1, batch_size=64, device=''''''''cuda''''''''):
self.model = model.to(device)
self.model.eval()
self.patch_size = patch_size
self.level = level
self.tissue_threshold = tissue_threshold
self.batch_size = batch_size
self.device = device
def _tissue_segmentation(self, slide):
"""Otsu + HSV 饱和度过滤组织分割"""
thumb = slide.get_thumbnail((2048, 2048))
thumb_np = np.array(thumb)[:, :, :3]
# HSV 饱和度过滤
hsv = mcolors.rgb_to_hsv(thumb_np / 255.0)
saturation = hsv[:, :, 1]
tissue_mask = saturation > 0.07 # PCam 使用的阈值
# 补充 Otsu 阈值 (处理极浅染色)
gray = np.mean(thumb_np, axis=2)
from skimage.filters import threshold_otsu
try:
otsu_thresh = threshold_otsu(gray)
tissue_mask = tissue_mask | (gray < otsu_thresh)
except:
pass
return tissue_mask, thumb.size
def _extract_patch_coords(self, slide, tissue_mask, thumb_size):
"""在组织区域内网格采样 patch 坐标"""
level_dims = slide.level_dimensions[self.level]
downsample = slide.level_downsamples[self.level]
# 缩略图与 Level 坐标的比例
scale_x = thumb_size[0] / level_dims[0]
scale_y = thumb_size[1] / level_dims[1]
coords = []
step = self.patch_size # 无重叠
for y in range(0, level_dims[1] - self.patch_size, step):
for x in range(0, level_dims[0] - self.patch_size, step):
# 检查组织覆盖率
tx = int(x * scale_x)
ty = int(y * scale_y)
tps = max(1, int(self.patch_size * scale_x))
region = tissue_mask[ty:ty+tps, tx:tx+tps]
if region.mean() >= self.tissue_threshold:
coords.append((x, y))
return coords
@torch.no_grad()
def predict_wsi(self, wsi_path):
"""
对单张 WSI 进行推理, 返回概率热力图
返回:
heatmap: (H, W) float — 每像素的肿瘤概率
coords: list of (x, y) — 采样的 patch 坐标
probs: list of float — 对应的肿瘤概率
"""
slide = openslide.OpenSlide(wsi_path)
tissue_mask, thumb_size = self._tissue_segmentation(slide)
coords = self._extract_patch_coords(slide, tissue_mask, thumb_size)
level_dims = slide.level_dimensions[self.level]
downsample = slide.level_downsamples[self.level]
probs = []
for i in range(0, len(coords), self.batch_size):
batch_coords = coords[i:i+self.batch_size]
batch_patches = []
for (x, y) in batch_coords:
patch = slide.read_region(
(int(x * downsample), int(y * downsample)),
self.level,
(self.patch_size, self.patch_size)
)
patch = np.array(patch)[:, :, :3]
batch_patches.append(patch)
batch_tensor = torch.stack([
torch.from_numpy(p).permute(2, 0, 1).float() / 255.0
for p in batch_patches
]).to(self.device)
logits = self.model(batch_tensor)
batch_probs = F.softmax(logits, dim=1)[:, 1] # P(tumor)
probs.extend(batch_probs.cpu().numpy())
slide.close()
# 构建热力图
heatmap = np.zeros(level_dims[::-1]) # (H, W)
for (x, y), p in zip(coords, probs):
heatmap[y:y+self.patch_size, x:x+self.patch_size] = p
return heatmap, coords, probs
def visualize_heatmap(self, heatmap, wsi_path, output_path=None,
threshold=0.5, alpha=0.4):
"""可视化概率热力图叠加在 WSI 缩略图上"""
slide = openslide.OpenSlide(wsi_path)
thumb = slide.get_thumbnail((2048, 2048))
slide.close()
fig, axes = plt.subplots(1, 3, figsize=(24, 8))
# 原图
axes[0].imshow(thumb)
axes[0].set_title(''''''''Original WSI'''''''', fonevent-blocked=14)
axes[0].axis(''''''''off'''''''')
# 热力图
im = axes[1].imshow(heatmap, cmap=''''''''jet'''''''', vmin=0, vmax=1)
axes[1].set_title(''''''''Tumor Probability Heatmap'''''''', fonevent-blocked=14)
axes[1].axis(''''''''off'''''''')
plt.colorbar(im, ax=axes[1], fraction=0.046)
# 叠加图
axes[2].imshow(thumb)
overlay = np.ma.masked_where(heatmap < threshold, heatmap)
axes[2].imshow(overlay, cmap=''''''''jet'''''''', alpha=alpha, vmin=0, vmax=1)
axes[2].set_title(f''''''''Overlay (threshold={threshold})'''''''', fonevent-blocked=14)
axes[2].axis(''''''''off'''''''')
plt.tight_layout()
if output_path:
plt.savefig(output_path, dpi=150, bbox_inches=''''''''tight'''''''')
plt.show()
# 使用示例
pipeline = WSIInferencePipeline(
model=trained_model,
patch_size=256, level=2,
tissue_threshold=0.1, batch_size=64
)
heatmap, coords, probs = pipeline.predict_wsi(
''''''''camelyon16/testing/images/test_001.tif''''''''
)
pipeline.visualize_heatmap(heatmap, ''''''''camelyon16/testing/images/test_001.tif'''''''',
output_path=''''''''test_001_heatmap.png'''''''')
§6.8 CAMELYON17 pN 分期推理管道
def predict_pn_stage(slide_predictions, patient_slides):
"""
从 5 张淋巴结切片的转移检测结果推断患者 pN 分期
slide_predictions: dict {slide_id: {has_macro: bool, has_micro: bool, has_itc: bool}}
patient_slides: list of 5 slide_ids (同一患者)
"""
macro_count = 0
micro_count = 0
itc_count = 0
for slide_id in patient_slides:
pred = slide_predictions[slide_id]
if pred[''''''''has_macro'''''''']:
macro_count += 1
elif pred[''''''''has_micro'''''''']:
micro_count += 1
elif pred[''''''''has_itc'''''''']:
itc_count += 1
# pN 分期规则 (简化版)
positive_nodes = macro_count + micro_count # macro + micro 均计为阳性
if positive_nodes == 0 and itc_count == 0:
return ''''''''pN0''''''''
elif positive_nodes == 0 and itc_count > 0:
return ''''''''pN0(i+)''''''''
elif macro_count == 0 and micro_count > 0:
return ''''''''pN1mi'''''''' # 仅微转移
elif 1 <= positive_nodes <= 3:
return ''''''''pN1''''''''
elif 4 <= positive_nodes <= 9:
return ''''''''pN2''''''''
else:
return ''''''''pN2'''''''' # ≥10 (理论上应为 pN3, 但 CAMELYON 仅设 5 类)
§6.9 数据增强策略
| 策略 | 参数 | 目的 | 来源 |
|---|---|---|---|
| 水平/垂直翻转 | p=0.5 | 增加方向多样性 | 几乎所有方法 |
| 旋转 (90°倍数) | 0°/90°/180°/270° | 组织旋转不变性 | 多数 CAMELYON17 参赛者 |
| 连续旋转 | [0°, 360°) 均匀采样 | 更强旋转不变性 | Team 3, Team 12 |
| 颜色抖动 (HSV) | brightness=±64/255, saturation=±0.25, hue=±0.04 | 染色变异性鲁棒性 | 主流方法 |
| H&E 空间噪声 | HSV/RGB 各通道独立噪声 | 染色批次差异模拟 | 多数参赛者 |
| 染色归一化 | Macenko/Reinhard | 统一不同扫描仪颜色 | Team 部分使用 |
| 随机裁剪 | 224×224 from 256×256 | 位置增强 | 通用 CNN |
| Hard negative mining | 小 CNN 初筛困难负样本 | 提高假阳性抑制 | PCam, 部分 CAMELYON17 |
| Test-time augmentation (TTA) | 翻转+旋转 8 种组合 | 5 次取最确信结果 | Team 3 |
§6.10 计算资源需求
| 任务 | GPU | VRAM | 训练时间 | 备注 |
|---|---|---|---|---|
| Patch 分类器 (CAMELYON16) | 1× GTX 1080Ti | 11 GB | ~12h | GoogLeNet, 256×256 patches |
| MIL (ResNet-50 + ABMIL) | 1× RTX 3090 | 24 GB | ~2h | 特征提取后训练极快 |
| MIL (UNI + ABMIL) | 1× A100 | 40 GB | ~1h | UNI 特征提取是瓶颈 |
| WSI 特征提取 (UNI) | 1× A100 | 40 GB | ~5 min/WSI | 129 张测试集 ~10h |
| PatchCamelyon (DenseNet) | 1× RTX 2080 | 8 GB | ~3h | 96×96, 100 epochs |
| CAMELYON17 pN 分期 | 1× RTX 3090 | 24 GB | ~15h | 含特征提取 + MIL + 后处理 |
§7 坑点与实用指南
坑点 1: 分辨率不匹配 (20× vs 40×)
症状:模型在训练中心 (如 RUMC, 20×) 表现良好,但在测试中心 (如 UMCU, 40×) 性能骤降。
原因:RUMC/CWZ/RST 使用 20× 扫描 (0.243 μm/pixel),UMCU 使用 40× (0.226 μm/pixel),LPON 使用 40× (0.250 μm/pixel)。同一 patch_size 在不同分辨率下覆盖的组织面积不同。
解决方案:
def normalize_resolution(slide, target_mpp=0.5, patch_size=256):
"""
统一采样到目标分辨率 (μm/pixel)
target_mpp=0.5 → ≈20× 等效
"""
level = 0
base_mpp = float(slide.properties[''''''''openslide.mpp-x'''''''']) # μm/pixel
target_level_mpp = base_mpp
# 寻找最接近目标分辨率的金字塔层
best_level = 0
best_diff = abs(base_mpp - target_mpp)
for lvl in range(slide.level_count):
ds = slide.level_downsamples[lvl]
mpp = base_mpp * ds
diff = abs(mpp - target_mpp)
if diff < best_diff:
best_diff = diff
best_level = lvl
# 在最佳层读取, 然后 resize 到精确目标
ds = slide.level_downsamples[best_level]
patch = slide.read_region((x, y), best_level, (patch_size, patch_size))
# 如需精确分辨率, 进一步 resize
actual_mpp = base_mpp * ds
scale = actual_mpp / target_mpp
if abs(scale - 1.0) > 0.01:
import torch.nn.functional as F
patch = F.interpolate(patch, scale_factor=1/scale,
mode=''''''''bilinear'''''''', align_corners=False)
return patch
坑点 2: 测试集 Test_049/Test_114 重复
症状:CAMELYON16 测试集原始有 130 张,但实际有效仅 129 张。
原因:Test_049 和 Test_114 是同一张 WSI 的重复。官方已声明排除。
解决方案:数据处理时检查并排除重复文件(MD5 校验)。
坑点 3: 标注缺失 (CAMELYON17 仅 50 张有 lesion 标注)
症状:CAMELYON17 训练集 500 张 WSI 中仅 50 张有 lesion-level XML 标注(每中心 10 张),其余仅有患者级 pN 分期标签。
原因:完整标注 WSI 需要专家病理学家大量时间,仅选择代表性切片进行 lesion 级标注。
解决方案:
- 对于 lesion-level 标注的 50 张:可用 patch-level 监督学习
- 对于仅有 pN 标签的 450 张:使用 MIL 弱监督学习
- 最佳策略:先在 50 张标注数据上预训练 patch 分类器,再用 MIL 在 500 张上微调
坑点 4: ITC 检测不可靠
症状:所有方法在 ITC (≤0.2 mm 或 ≤200 细胞) 检测上均极差 (检测率 0–34.3%)。
原因:ITC 在 H&E 染色中仅表现为零星散在的单个或小簇肿瘤细胞,与正常淋巴细胞难以区分。CK IHC 可识别 ITC,但 CAMELYON 数据集不含数字 IHC 切片。
解决方案:
- 不要期望在 H&E WSI 上可靠检测 ITC
- 如果 ITC 检测是关键需求,需要额外的 IHC 数字切片
- 在 pN 分期推理中,将 ITC 视为"不确定性区域",可通过降低 ITC 权重来减少误判
坑点 5: 染色变异性
症状:模型在 RUMC 数据上训练,在 UMCU 数据上假阳性率显著升高。
原因:不同中心的 H&E 染色协议差异导致颜色分布偏移。RUMC 偏蓝紫色,UMCU 偏粉红色。
解决方案:
# Macenko 染色归一化
def stain_normalization(img, target_stain=None):
"""
Macenko 方法: 估计染色向量, 归一化到目标
img: RGB numpy array (H, W, 3), uint8
"""
from skimage.color import rgb2od # optical density
od = -np.log((img.astype(np.float64) + 1) / 256)
od = od.reshape(-1, 3)
# SVD 分解获取染色向量
cov = np.cov(od.T)
_, S, Vt = np.linalg.svd(cov)
V = Vt[:2] # 前两个主成分
# 投影到 2D 平面, 极坐标分析
angles = np.arctan2(V[1], V[0])
min_angle = np.percentile(angles, 1)
max_angle = np.percentile(angles, 99)
# 提取 H (苏木精) 和 E (伊红) 向量
H_vec = V[:, np.argmin(np.abs(angles - min_angle))]
E_vec = V[:, np.argmin(np.abs(angles - max_angle))]
stain = np.stack([H_vec, E_vec], axis=1)
# 归一化到目标 stain
if target_stain is not None:
# ... 归一化到目标染色矩阵
pass
return stain # 返回估计的染色向量
坑点 6: 人造患者设计
症状:CAMELYON17 的"患者"并非真实患者,而是从不同真实患者的切片中随机组合的 5 张 WSI。
原因:CAMELYON17 从 CAMELYON16 和新增数据中随机选取 5 张 WSI 组合成一个"人造患者"。设计目的是确保 pN 分期分布均衡(pN0/pN0(i+)/pN1mi/pN1/pN2 各有足够样本),而非反映真实患病率。
影响:
- 不能用 CAMELYON17 评估 AI 在真实临床流中的患者级分期准确性
- pN 分期分布是人工均衡的,不代表真实患病率
- 同一"患者"的 5 张切片可能来自不同真实患者的不同扫描仪
坑点 7: 组织分割遗漏肿瘤区域
症状:Otsu 阈值组织分割在某些切片上误将低密度肿瘤组织排除。
原因:某些转移灶(特别是微转移和 ITC)的组织密度较低,可能被 Otsu 阈值误判为背景。
解决方案:
- 使用更保守的组织分割阈值
- 结合 HSV 饱和度过滤 (max_saturation < 0.07 排除背景, 但需验证不排除肿瘤)
- PCam 的方法:HSV 转换 → 模糊 → max_saturation 过滤, 已验证不丢弃训练集肿瘤
坑点 8: 患者级推理的级联误差
症状:patch 分类器 95% 准确率看似很高,但 5 张 WSI 组合到患者级 pN 分期时准确率可能大幅下降。
原因:pN 分期需要从 5 张切片中正确计数转移淋巴结数量。每张切片的假阳性/假阴性会在患者级累加。
量化分析:
- 假设单张 WSI 假阳性率 5%,5 张中有 ≥1 张假阳性的概率: 1-(0.95)^5 ≈ 23%
- 假设单张 WSI 假阴性率 5%,5 张中有 ≥1 张假阴性的概率: 1-(0.95)^5 ≈ 23%
- 患者级准确率可能比切片级低 15-20 个百分点
解决方案:
-
使用集成方法降低单模型不确定性
-
CAMELYON17 论文显示 Top 3 集成 κ=0.9261 > 最佳单一算法 κ=0.8958
-
考虑在患者级直接使用梯度提升树 (Team 9 的方法)
§8 SOTA 演进 (Post-Challenge)
§8.1 MIL 方法在 CAMELYON16 上的演进
以下结果基于 ResNet-50 (ImageNet 预训练) 特征提取 + 不同 MIL 聚合方法:
| 方法 | 年份/会议 | Accuracy | AUC | 架构特点 |
|---|---|---|---|---|
| ABMIL | 2018/ICML | 0.850 | 0.928 | 门控注意力 |
| DSMIL | 2021/CVPR | 0.878 | 0.927 | max-min attention |
| CLAM-SB | 2021/Nat.BME | 0.858 | 0.921 | instance-level clustering |
| CLAM-MB | 2021/Nat.BME | 0.868 | 0.918 | multi-branch |
| TransMIL | 2021/NeurIPS | 0.884 | 0.931 | Transformer + Nyström |
| DTFD-MIL | 2022/CVPR | 0.895 | 0.941 | dual-tier feature |
| DGMIL | 2022/MICCAI | 0.901 | 0.886 | dynamic graph |
| WENO | 2022/NeurIPS | — | — | weakly supervised |
| INS | 2023/MIDL | 0.958 | 0.925 | instance classifier |
| CAMIL | 2025/期刊 | 0.880 | 0.951 | channel attention |
趋势:MIL 方法从 2018 (ABMIL AUC 0.928) 到 2023+ (INS AUC 0.958),逐步提升但天花板在 0.93–0.96 区间,突破需要更好的特征提取器。
§8.2 病理基础模型在 CAMELYON16 上的表现
以下使用 ABMIL 聚合 + 不同预训练特征提取器:
| 基础模型 | 预训练数据 | Balanced Acc (均值) | Balanced Acc (最佳) | 参考文献 |
|---|---|---|---|---|
| ResNet-50 (ImageNet) | ImageNet | ~0.85 | ~0.88 | 标准 baseline |
| RetCCL | TCGA | 0.745 | 0.769 | Wang 2023 |
| CTransPath | TCGA | 0.858 | 0.885 | Wang 2022 |
| Phikon | TCGA | 0.907 | 0.955 | Filiot 2023 |
| Virchow2 | TCGA + proprietary | 0.934 | 0.959 | Zimmermann 2024 |
| REMEDIS | Medical (proprietary) | 0.922 | 0.949 | Azizi 2022 |
| UNI | Mass-100K (proprietary) | 0.982 | 1.000 | Chen 2024, Nat. Med. |
UNI 突破:UNI (ViT-Large, DINOv2 自监督, 100K WSI 预训练) + ABMIL 在 CAMELYON16 上达到 balanced accuracy 0.982(均值),最佳 seed 达到 1.000 — 超越不限时病理学家 (AUC 0.966)。这是首个在 MIL 框架下超越人类病理学家的结果。
§8.3 UNI + 不同 MIL 方法
| MIL 方法 | UNI 特征 AUC | SRCL (CTransPath) AUC | DINOv2 AUC |
|---|---|---|---|
| MeanMIL | 97.7 | 91.1 | 46.3 |
| MaxMIL | 96.8 | 89.8 | 76.4 |
| AutoMIL | 98.8 | 94.7 | 70.3 |
| ABMIL | 99.1 | 94.7 | 76.0 |
| DSMIL | 95.7 | 88.0 | 49.9 |
| CLAM | 98.3 | 94.7 | 79.6 |
| TransMIL | 98.3 | 91.4 | 77.1 |
关键发现:UNI 特征即使使用最简单的 MeanMIL (AUC 97.7) 也超越了 SRCL 最佳 MIL (AUC 94.7)。特征提取器的质量比 MIL 聚合架构更重要。
§8.4 病理基础模型的"捷径学习"问题
Kaczmarzyk et al. (2024) 使用 HIPPO 可解释性方法发现:
| 基础模型 | 原始 Balanced Acc | 移除肿瘤区域后特异性 | 依赖肿瘤区域? |
|---|---|---|---|
| UNI | 1.000 | 0.73 | 是 (使用肿瘤微环境信号) |
| REMEDIS | 0.949 | 0.77 | 是 (中度) |
| Virchow2 | 0.959 | 0.92 | 否 (主要依赖肿瘤细胞) |
| Phikon | 0.955 | 0.86 | 否 (中度) |
| CTransPath | 0.885 | 0.92 | 否 |
| RetCCL | 0.769 | 0.88 | 否 |
警示:UNI 虽然准确率最高,但在移除肿瘤区域后特异性仅 0.73 — 说明它部分依赖肿瘤微环境信号(而非肿瘤细胞本身)做出预测。这在临床部署中可能是风险点。
§8.5 SOTA 演进时间轴
2016 CAMELYON16 挑战赛: AUC 0.994 (HMS+MIT, GoogLeNet 集成)
│ 首次证明 AI 超越限时病理学家
2017 CAMELYON17 挑战赛: κ 0.8958 (HMS-MGH-CCDS)
│ 患者级 pN 分期, ITC 检测全差
2018 PCam 发布: 327,680 patches, 成为病理 ML "MNIST"
│ Campanella et al. (MSK, Nat Med): MIL 弱监督, AUC 0.98
2019 CLAM (Lu et al.): instance-level clustering, AUC 0.92
2020 —
2021 TransMIL: Transformer-based MIL, AUC 0.931
DSMIL: max-min attention, AUC 0.927
2022 DTFD-MIL: dual-tier, AUC 0.941
DGMIL: dynamic graph, Acc 0.901
2023 INS: instance classifier, Acc 0.958
2024 UNI (Chen, Nat Med): balanced acc 0.982-1.00 ← 突破人类
Virchow2: balanced acc 0.934-0.959
HIPPO 揭示 UNI 捷径学习问题
2025 CAMIL: channel attention, AUC 0.951
持续迭代中...
§8.6 CAMELYON 与其他主要病理数据集横向对比
| 数据集 | WSI 数 | 任务 | 癌种 | 中心数 | 许可 | 独特价值 |
|---|---|---|---|---|---|---|
| CAMELYON16/17 | 1,399 | 淋巴结转移检测 + pN 分期 | 乳腺癌 | 5 | CC0 | 首次 AI 超越病理学家; 人机对抗基准 |
| TCGA | ~30,000+ | 多任务 (分型/分级/生存) | 33 种 | 多中心 | 开放 | 多癌种覆盖最广; 基础模型预训练源 |
| PANDA (Prostate cANcer) | ~11,000 | 前列腺癌 Gleason 分级 | 前列腺癌 | 2 | CC0 | 最大 WSI 数据集之一; Gleason 分级 |
| BraTS | — (MRI) | 脑肿瘤分割 | 胶质瘤 | 多中心 | 开放 | 3D 医学影像分割标杆 |
| MoNuSeg | ~30,000 nuclei | 核分割 | 多器官 | 多中心 | CC0 | 细胞核实例分割 |
| ANHIR | ~2,700 | 图像配准 | 多器官 | 多中心 | 开放 | 病理图像配准 |
| BCNB | 1,238 | 乳腺癌淋巴结分类 | 乳腺癌 | 中国 | 开放 | 中国乳腺癌数据 |
CAMELYON 的不可替代性:尽管 TCGA 拥有更多 WSI,但 CAMELYON 的独特价值在于:(1) 像素级多边形标注 + CK IHC 金标准验证;(2) 系统性的人机对抗实验设计;(3) 患者级 pN 分期任务;(4) CC0 许可(TCGA 为受限开放)。这使得 CAMELYON 至今仍是病理 AI 评估的"标准秤砣"。
§8.7 CAMELYON 对病理 AI 领域的方法学贡献
CAMELYON 不仅是基准数据集,更塑造了整个计算病理学的方法论:
| 贡献领域 | CAMELYON 之前的范式 | CAMELYON 之后的范式 |
|---|---|---|
| 任务定义 | patch 级分类为主 | WSI 级 + 患者级任务成为标准 |
| 标注方式 | 像素级全标注 | 弱监督 (MIL) + 少量像素标注 |
| 评估方式 | Accuracy 为主 | FROC + AUC + κ + 人机对比 |
| 数据规模 | 数百 patches | 数千 WSI, TB 级 |
| 预处理 | 简单 resize | 组织分割 + 染色归一化 + 多分辨率 |
| 领域迁移 | 很少考虑 | 跨中心/扫描仪泛化成标准评估 |
| 基础模型 | ImageNet 预训练 | 病理专用预训练 (UNI/Virchow2) |
§9 资源索引
9.1 官方资源
| 资源 | URL |
|---|---|
| CAMELYON16 官网 | https://camelyon16.grand-challenge.org/ |
| CAMELYON17 官网 | https://camelyon17.grand-challenge.org/ |
| CAMELYON17 数据页 | https://camelyon17.grand-challenge.org/Data |
| CAMELYON17 评估规则 | https://camelyon17.grand-challenge.org/Evaluation/ |
| CAMELYON17 排行榜 | https://camelyon17.grand-challenge.org/evaluation/results |
| AWS S3 Open Data | https://registry.opendata.aws/camelyon |
| GigaDB (CAMELYON16) | https://doi.org/10.5524/100439 |
9.2 衍生数据集
| 资源 | URL |
|---|---|
| PatchCamelyon (GitHub) | https://github.com/basveeling/pcam |
| PatchCamelyon (HuggingFace) | https://huggingface.co/datasets/1aurent/PatchCamelyon |
| PatchCamelyon (Zenodo) | https://zenodo.org/record/2546921 |
| UNI CAMELYON16 嵌入 | https://huggingface.co/datasets/kaczmarj/camelyon16-uni |
| CAMELYON16+17 MD5 校验 | 随数据集发布 |
9.3 工具与代码
| 工具 | 用途 | URL |
|---|---|---|
| OpenSlide | WSI 读取 C 库 + Python 绑定 | https://openslide.org |
| ASAP | WSI 可视化/标注/分析平台 (DIAG) | https://github.com/computationalpathologygroup/ASAP |
| CLAM | MIL 训练框架 (Lu et al.) | https://github.com/mahmoodlab/CLAM |
| TransMIL | Transformer-based MIL | https://github.com/szc19990412/TransMIL |
| DTFD-MIL | Dual-tier MIL | GitHub |
| UNI | 病理基础模型 (HuggingFace) | https://github.com/mahmoodlab/UNI |
| HIPPO | 可解释性工具 | arXiv:2409.03080 |
9.4 关键论文
| # | 论文 | 期刊 | 年份 | 引用 | DOI |
|---|---|---|---|---|---|
| 1 | Ehteshami Bejnordi et al., “Diagnostic Assessment of Deep Learning Algorithms…” | JAMA | 2017 | ~3,191 | 10.1001/jama.2017.14585 |
| 2 | Bándi et al., “From detection of individual metastases to classification…” | IEEE TMI | 2019 | ~718 | 10.1109/TMI.2018.2867350 |
| 3 | Litjens et al., “1399 H&E-stained sentinel lymph node sections…” | GigaScience | 2018 | ~450 | 10.1093/gigascience/giy065 |
| 4 | Veeling et al., “Rotation Equivariant CNNs for Digital Pathology” | arXiv | 2018 | ~500 | 10.1007/978-3-030-00934-2_24 |
| 5 | Lu et al., “Data-efficient and weakly supervised computational pathology…” (CLAM) | Nat. Biomed. Eng. | 2021 | ~1,200 | 10.1038/s41551-020-00682-w |
| 6 | Shao et al., “TransMIL: Transformer based Correlated MIL…” | NeurIPS | 2021 | ~600 | arXiv:2106.11938 |
| 7 | Chen et al., “Towards a general-purpose foundation model for computational pathology” (UNI) | Nat. Med. | 2024 | ~200 | 10.1038/s41591-024-02857-3 |
| 8 | Campanella et al., “Clinical-grade computational pathology…” | Nat. Med. | 2019 | ~1,000 | 10.1038/s41591-019-0508-1 |
| 9 | Kaczmarzyk et al., “Explainable AI for computational pathology…” | arXiv | 2024 | — | 10.48550/arXiv.2409.03080 |
9.5 BibTeX 引用
@article{ehteshami2017jama,
title={Diagnostic Assessment of Deep Learning Algorithms for Detection of Lymph Node Metastases in Women With Breast Cancer},
author={Ehteshami Bejnordi, Babak and Veta, Mitko and Johannes van Diest, Paul and van Ginneken, Bram and Karssemeijer, Nico and Litjens, Geert and van der Laak, Jeroen AWM and others},
journal={JAMA},
volume={318},
number={22},
pages={21992210},
year={2017},
doi={10.1001/jama.2017.14585}
}
@article{bandi2019ieeetmi,
title={From Detection of Individual Metastases to Classification of Lymph Node Status at the Patient Level: The CAMELYON17 Challenge},
author={B{\''''''''a}ndi, P{\''''''''e}ter and Geessink, Oscar and Manson, Quirine and van Dijk, Marcory and Balkenhol, Maschenka and Hermsen, Meyke and others and Litjens, Geert},
journal={IEEE Transactions on Medical Imaging},
volume={38},
number={2},
pages={550560},
year={2019},
doi={10.1109/TMI.2018.2867350}
}
@article{litjens2018gigascience,
title={1399 H\&E-stained sentinel lymph node sections of breast cancer patients: the CAMELYON dataset},
author={Litjens, Geert and Bandi, Peter and Ehteshami Bejnordi, Babak and Geessink, Oscar and Balkenhol, Maschenka and Bult, Peter and Halilovic, Altuna and Hermsen, Meyke and others and van der Laak, Jeroen},
journal={GigaScience},
volume={7},
number={6},
pages={giy065},
year={2018},
doi={10.1093/gigascience/giy065}
}
§10 AI 声明卡
| 维度 | 评估 | 说明 |
|---|---|---|
| 数据量 | ★★★★☆ | 1,399 WSI, ~2.95 TB, 5 中心, 3 扫描仪 — 病理基准中属最大规模之一 |
| 标注质量 | ★★★★★ | 专家病理学家多边形标注 + CK IHC 验证 — 金标准 |
| 标注完整度 | ★★★☆☆ | CAMELYON16 全量 lesion 标注; CAMELYON17 仅 50/500 张 lesion 标注, 其余仅 pN 标签 |
| 多样性 | ★★★☆☆ | 5 中心/3 扫描仪, 但仅荷兰白种人/单一癌种 |
| 任务复杂度 | ★★★★☆ | 从 patch 分类 → WSI 分类 → 患者级分期, 3 个难度层级 |
| 可复现性 | ★★★★★ | CC0 公开, AWS S3 无限制下载, 开放挑战赛评估 |
| 临床相关性 | ★★★★★ | 前哨淋巴结转移检测直接影响乳腺癌分期和治疗 |
| 历史影响力 | ★★★★★ | 首次证明 AI 超越病理学家; 3,900+ 引用; 20+ 基础模型评估标准 |
| 已知偏差 | — | 仅荷兰人群; ITC 检测不可靠; 人造患者; 训练集经转移富集 |
| 许可 | CC0 | 无限制商业使用 |
| FAIR 性 | ★★★★★ | Findable (DOI+AWS) / Accessible (CC0) / Interoperable (bigTIFF 标准) / Reusable (无限制) |
DAIMS 评分卡 (17.5/24)
| 维度 | 满分 | 得分 | 说明 |
|---|---|---|---|
| 数据量 | 3 | 3 | 1,399 WSI, ~2.95 TB — 病理 WSI 数据集中属最大之一 |
| 样本量 | 3 | 2 | ~600 患者 (含人造), 中等规模 |
| 多样性 | 3 | 1.5 | 5 中心/3 扫描仪, 但仅荷兰白种人 |
| 标注质量 | 3 | 3 | 专家多边形标注 + CK IHC 验证 |
| 标注完整度 | 3 | 1.5 | CAMELYON16 全量; CAMELYON17 仅 50/500 lesion |
| 伦理合规 | 3 | 3 | IRB 批准 + CC0 |
| 机器可读性 | 3 | 3 | bigTIFF + XML + CSV, OpenSlide/ASAP 原生支持 |
| 可访问性 | 3 | 1.5 | AWS S3 免账号, 但 ~2.95 TB 对个人用户仍有门槛 |
| 合计 | 24 | 17.5 | 73% — 优秀 |
伦理考量
- 患者隐私:全部 WSI 已去标识化,不含 PHI。伦理委员会批准知情同意豁免 (RUMC 2016-2761)
- 公平性:仅荷兰白种人数据,模型在其他种族/地区人群上的泛化性未验证
- 临床部署风险:训练集经转移富集(非自然患病率分布),直接部署可能导致过高假阳性率
- ITC 漏检:所有方法对 ITC 检测均不可靠,临床决策不应依赖 AI 对 ITC 的判断
- 捷径学习:UNI 等基础模型可能依赖肿瘤微环境而非肿瘤细胞本身做出预测(Kaczmarzyk 2024)
§C 校验表
| # | 校验项 | 状态 |
|---|---|---|
| 1 | 核心论文 DOI 可访问 (JAMA + IEEE TMI + GigaScience) | ✅ |
| 2 | 数据集 CC0 许可确认 | ✅ |
| 3 | AWS S3 公开访问验证 | ✅ |
| 4 | WSI 总数 1,399 确认 (399 + 1,000) | ✅ |
| 5 | 5 中心 3 扫描仪类型确认 | ✅ |
| 6 | CAMELYON16: 270 train (160 normal + 110 tumor) + 129 test | ✅ |
| 7 | CAMELYON17: 500 train (100 患者 × 5) + 500 test (100 患者 × 5) | ✅ |
| 8 | pN 分期 5 类定义确认 | ✅ |
| 9 | XML 标注格式 (ASAP) 确认 | ✅ |
| 10 | IHC 参考标准 (CK) 确认 | ✅ |
| 11 | bigTIFF + 512 tiles + JPEG + RGB 8-bit 格式确认 | ✅ |
| 12 | 23 团队 32 算法 (CAMELYON16) 确认 | ✅ |
| 13 | 23 团队 37 算法 (CAMELYON17) 确认 | ✅ |
| 14 | 最佳 AUC 0.994 (HMS+MIT) 确认 | ✅ |
| 15 | 病理学家 WTC 均值 AUC 0.810 确认 | ✅ |
| 16 | 病理学家 WOTC AUC 0.966 确认 | ✅ |
| 17 | 7 种算法超越 WTC 确认 | ✅ |
| 18 | ITC 检测率 <40% 确认 | ✅ |
| 19 | CAMELYON17 最佳 κ 0.8958 确认 | ✅ |
| 20 | Top 3 集成 κ 0.9261 确认 | ✅ |
| 21 | PatchCamelyon 327,680 patches (96×96) 确认 | ✅ |
| 22 | PCam 50/50 正负比确认 | ✅ |
| 23 | CAMELYON17 仍开放提交确认 | ✅ |
| 24 | 5 中心 WSI 分布表确认 | ✅ |
| 25 | 5 中心 pN 分期分布表确认 | ✅ |
| 26 | 时间线 (2015-11 → 2017-05) 确认 | ✅ |
| 27 | 人造患者设计确认 | ✅ |
| 28 | Test_049/Test_114 重复排除确认 | ✅ |
| 29 | UNI balanced acc 0.982-1.00 确认 | ✅ |
| 30 | Virchow2 balanced acc 0.934-0.959 确认 | ✅ |
| 31 | HIPPO 捷径学习发现确认 | ✅ |
| 32 | 伦理批准 (RUMC 2016-2761) 确认 | ✅ |
| 33 | 3 核心论文 BibTeX 确认 | ✅ |
| 34 | OpenSlide/ASAP 工具链接确认 | ✅ |
| 35 | published 状态 (无 draft/review 字样) 确认 | ✅ |
| 36 | SLNB 临床流程与 pN 分期决策影响表确认 | ✅ |
| 37 | AJCC 第 7/8 版 pN 分期标准确认 | ✅ |
| 38 | NSABP B-32 / ACOSOG Z0011 / AMAROS 试验背景确认 | ✅ |
| 39 | CK IHC 金标准验证机制确认 | ✅ |
| 40 | Macenko 染色归一化完整实现代码确认 | ✅ |
| 41 | CLAM 训练管道代码确认 | ✅ |
| 42 | WSI 推理与热力图可视化代码确认 | ✅ |
| 43 | CAMELYON16 Top-10 算法架构分析表确认 | ✅ |
| 44 | CAMELYON17 逐类检测率深度分析确认 | ✅ |
| 45 | PCam SOTA 结果表确认 | ✅ |
| 46 | 病理数据集横向对比表确认 | ✅ |
| 47 | 方法学贡献表确认 | ✅ |
