想象你收到一张面积堪比整座城市的超大卫星地图,任务是从中找到几个藏匿的 “可疑居民”。地图细节密集到万亿像素,可疑分子可能只占其中针尖大的区域,而且混在成千上万个普通人中间几乎无法一眼辨认。这就是病理医生每天面对的任务:一张淋巴结全切片图像(WSI),要判断有没有从乳腺原发灶“逃逸”过来的癌细胞。Camelyon16 正是 2016 年荷兰多家医学中心联合发起的“城市搜捕演习”:提供 270 张训练地图、129 张测试地图,要求 AI 自动标出转移病灶。本文将沿着这套“城市搜捕”的故事主线,讲清从地图(WSI)到街区(patch)、从巡逻路线(采样策略)到情报综合(注意力聚合)的完整技术链条。

岗位关联

掌握“切片→采样→判别→聚合→评估”这条完整流水线,你就拿到了数字病理 AI 系统的设计蓝图,这是医学影像 AI 从业者的核心武器,也是面试和实战中拉开差距的关键。

目录
  1. 可视化演示
  2. 任务与数据:从一张“城市地图”说起
  3. 切片金字塔:城市的多比例尺地图
  4. Patch采样策略:设计巡逻路线
  5. 分类模型:给每个街区配一台鉴定器
  6. 注意力聚合:指挥中心怎么抓重点
  7. 损失设计:不能因为全城干净就盖章正常
  8. 评估指标:用 FROC 成绩单验收巡逻成果
  9. 临床落地:从演习场走进医院病理科
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示WSI全切片图像patch采样、CNN分类与注意力聚合、转移热图生成流程,切换模式理解病理AI核心。

本节配有交互动效(进入视口后自动加载,约 3KB)。

WSI patch采样策略 组织区域分割后按固定步长提取patch,过滤空白背景区域 patch = WSI[tissue_mask] 有效组织区域占比约40%,采样密度影响检测粒度

任务与数据:从一张“城市地图”说起

城市的卫星地图大到超乎想象,一个像素对应组织里的一个小角落,可疑分子可能只占据几个像素。想站在地图前一眼锁定目标?不可能。这就是病理科医生望着显微镜时的真实感受,也是 Camelyon16 挑战赛要攻克的难题:把“找人”这件事交给 AI。

医学上把癌细胞从原发部位溜到淋巴结的过程称为淋巴结转移,而前哨淋巴结是癌细胞转移的第一站,它有没有被侵犯直接决定手术方案和后续治疗强度。Camelyon16 挑战赛因此给出 270 张带标注的训练切片和 129 张测试切片,要求算法在整张 WSI 上自动定位癌转移区域。注意,标注不是简单写一句“这张有癌”,而是给出具体坐标——就像演习地图上画出了可疑分子的大致藏身街区,但 AI 得自己学会识别。

40 倍扫描下,一张 WSI 的像素规模极其惊人:

正因为整座城市塞不进巡逻队的视野,才必须把地图切成小方块逐个排查——这是后面所有 patch 操作的起点。城市太大,先从地图边缘开始打格子吧。

npix=W×H×C≈1×1010\displaystyle n_{pix} = W \times H \times C \approx 1 \times 10^{10}
通俗说明:一张 WSI 的像素总数约等于宽度乘以高度再乘以颜色通道数,规模达到百亿级,远超任何深度学习模型的直接输入能力。
  1. W 与 H 的含义WSI 在 40 倍扫描层级的宽度与高度,通常都在 10 万像素数量级。
    W≈H≈105\displaystyle W \approx H \approx 10^{5}
  2. 通道数 C标准彩色图像的 RGB 三个通道。
    C=3\displaystyle C = 3
  3. 总量估算三者相乘得到百亿级像素规模。
    105×105×3=3×1010\displaystyle 10^{5} \times 10^{5} \times 3 = 3 \times 10^{10}

切片金字塔:城市的多比例尺地图

城市地图有多个比例尺:全局卫星图看地貌,行政区图看街道,街区详图看楼宇。比例尺越大细节越丰富,但能覆盖的城区范围越小。数字病理同样内置了这套“多比例尺地图系统”,称为图像金字塔。

扫描仪生成 WSI 时并不只保存一张超大图,而是同时保存多个分辨率版本:最底层是最高倍率(40x),往上每层边长缩为一半,依次得到 20x、10x、5x 等图层。程序随时可以切换到合适倍率:低倍快速浏览组织全貌,高倍仔细确认细胞形态。这个设计还让同一坐标在不同层间可以相互换算。

patch 采样通常选在 20x 层而非最高倍 40x 层。原因很直接:40x 下视野太窄,看一个街区就要消耗大量算力;而 20x 下每个像素约 0.5 微米,刚好能分辨细胞核的形态细节,是精度与效率的平衡点。金字塔各层尺寸满足简单关系:

巡逻队先看城市全局地图规划路线,再放大到重点街区确认细节——图像金字塔就是支撑这套“先宏观后微观”策略的技术底座。

W(k)=⌊W(0)2k⌋,H(k)=⌊H(0)2k⌋\displaystyle W^{(k)} = \left\lfloor \frac{W^{(0)}}{2^{k}} \right\rfloor, \quad H^{(k)} = \left\lfloor \frac{H^{(0)}}{2^{k}} \right\rfloor
通俗说明:第 k 层金字塔的宽高等于基础层除以 2 的 k 次方再向下取整。有了这个公式,从 40x 标到 20x 位置就像查地图比例尺一样方便。
  1. 基础层 W(0)、H(0)最高分辨率层的宽度与高度,是金字塔的底座。
    W(0)×H(0)≈1010\displaystyle W^{(0)} \times H^{(0)} \approx 10^{10}
  2. 降采样倍数每向上爬一层,边长除以 2,面积变为原来的四分之一。
    2k (k=1,2,3,… )\displaystyle 2^{k} \ (k=1,2,3,\dots)
  3. 向下取整像素坐标必须是整数,用取整符号保证计算不越界。
    ⌊⋅⌋\displaystyle \lfloor \cdot \rfloor

Patch采样策略:设计巡逻路线

全城待查街区数以万计,巡逻队不可能每寸土地都踩一遍,必须设计路线。路线有三种典型风格:从东到西均匀拉网(均匀网格采样)、闭着眼随机抽查(随机采样)、跟着情报部门的重点关注区域图集中突击(引导式采样)。

patch 采样是整条流水线的入口。做法很简单:在选定的金字塔层上把 WSI 切成 256×256 的网格,再依据策略挑一部分 patch 送入模型。均匀网格采样不遗漏任何区域,但大量算力浪费在正常组织上;随机采样代码最容易写,可肿瘤区域占比极低,常常几百个 patch 里一个阳性都没有;引导式采样则先用一个低倍率快速模型筛出高嫌疑区域,再只对高嫌疑区域密集采样——Camelyon16 的多个高分方案都采用了这种“先粗筛、后精查”的组合拳。

采样时还有一个致命细节:正负样本比例。肿瘤区域占整片比例常不足 5%,若按均匀概率采样,一个训练 batch 可能全是正常 patch,模型根本没见过肿瘤长什么样。因此训练时通常强制每个 batch 中阳性 patch 占比保持在 1/10 甚至 1/3:

巡逻路线决定了小队的投入产出比。想让模型真正认识可疑分子,必须专门给可疑街区“排班”,这就是采样策略的全部意义。

N=⌊Wp⌋×⌊Hp⌋,Phit=∑i=1NyiN\displaystyle N = \left\lfloor \frac{W}{p} \right\rfloor \times \left\lfloor \frac{H}{p} \right\rfloor, \quad P_{\text{hit}} = \frac{\sum_{i=1}^{N} y_{i}}{N}
通俗说明:第一个公式算出整张图能切出多少个 p×p 的 patch;第二个公式表示随机采样时命中肿瘤 patch 的概率恰好等于肿瘤 patch 占比——你看,不做引导时这个数字低得可怜。
  1. patch 尺寸 p×pp 通常取 256 像素,代表一个方形街区。
    p=256\displaystyle p = 256
  2. 网格总数 N宽高方向能铺下的 patch 数相乘,得到全图 patch 总量。
    N=⌊Wp⌋×⌊Hp⌋\displaystyle N = \left\lfloor \frac{W}{p} \right\rfloor \times \left\lfloor \frac{H}{p} \right\rfloor
  3. 标签求和与命中率y_i 为 1 表示该 patch 含肿瘤,求和得到阳性数,除以 N 得到随机命中概率。
    Phit=∑yiN\displaystyle P_{\text{hit}} = \frac{\sum y_{i}}{N}

分类模型:给每个街区配一台鉴定器

街区被抽查后,要有一台靠谱的鉴定器判断“这里面有没有可疑分子”。这台鉴定器就是 CNN 分类网络:它盯着 256×256 的 patch 小图,输出一个 0 到 1 之间的“嫌疑分”。嫌疑分越高,这个 patch 越可能含肿瘤细胞。

Camelyon16 时代的常用骨架是 VGG 和 ResNet 系列,输入是 RGB 三通道 patch。模型一般先用 ImageNet 上预训练好的权重初始化,再在病理 patch 上微调——因为病理图像同样包含边缘、纹理这类低层通用特征,预训练让模型从“见过世界的眼睛”起步,而不是从零乱猜。数据增强方面则考虑旋转 90 度、翻转、颜色抖动和光学畸变,帮助模型抵抗不同医院染色差异造成的颜色偏移。

对某个 patch 的前向过程可以写成:

每个街区的嫌疑分被打出来了。可一座城市有成千上万个街区,怎么把这些分数汇总成整座城市的最终结论?这就需要指挥中心的情报综合系统。

fi=ϕ(xi;θ),pi=σ(fi)\displaystyle f_{i} = \phi(x_{i}; \theta), \quad p_{i} = \sigma(f_{i})
通俗说明:φ 是 CNN 特征提取器,把图像变成特征向量;σ 是 sigmoid 函数,把实数压缩到 0 到 1 之间,得到含肿瘤概率。
  1. 输入 x_i第 i 个 256×256×3 的 patch 张量。
    xi∈R256×256×3\displaystyle x_{i} \in \mathbb{R}^{256 \times 256 \times 3}
  2. 特征提取器 φ 与参数 θCNN 的全部卷积和全连接层,θ 是其中可学习的权重。
    θ={Wconv,bconv,Wfc,bfc}\displaystyle \theta = \{ W_{\text{conv}}, b_{\text{conv}}, W_{\text{fc}}, b_{\text{fc}} \}
  3. logit f_i分类层输出的原始分数,取值范围正负无穷。
    fi∈(−∞,+∞)\displaystyle f_{i} \in (-\infty, +\infty)
  4. sigmoid 压缩概率把 logit 映射到 0~1,作为含肿瘤概率。
    σ(fi)=11+e−fi\displaystyle \sigma(f_{i}) = \frac{1}{1 + e^{-f_{i}}}

注意力聚合:指挥中心怎么抓重点

巡逻小队把全部街区的嫌疑报告交回指挥中心。绝大多数报告写着“一切正常”,只有零星几份标注“这里有可疑分子”。如果搞少数服从多数的投票,全会结论必然是“全城健康”——这恰恰是最危险的漏诊。聪明的指挥中心会给嫌疑值高的街区更大话语权,同时也不彻底忽略其他街区的微弱线索,这就是注意力机制。

在机器学习语言里,patch 是“示例”,整张切片是“包”,这种设定叫多示例学习(MIL)。朴素的聚合方式有两种:max pooling 只保留最大概率,丢掉上下文;mean pooling 则被海量正常 patch 稀释,肿瘤信号荡然无存。注意力聚合(ABMIL)让网络自己学习每个 patch 的重要程度:

指挥中心根据热力图决策,AI 高亮的区域与医生的视线重合——这就是“第二双眼睛”的雏形。但要让这双眼睛不偷懒,还得配一套严厉的考核制度。

wi=exp⁡(g(fi))∑j=1Nexp⁡(g(fj)),z=∑i=1Nwifi\displaystyle w_{i} = \frac{\exp(g(f_{i}))}{\sum_{j=1}^{N} \exp(g(f_{j}))}, \quad \mathbf{z} = \sum_{i=1}^{N} w_{i} f_{i}
通俗说明:g 是一个小型打分网络,输出每个 patch 的“重要程度”;softmax 把分数归一化成总和为 1 的权重;最后按权重把所有 patch 特征加权求和,得到整张切片的特征向量 z。
  1. 打分网络 g常为两层 MLP,输入 patch 特征,输出一个标量注意力分数。
    g(fi)=v⊤tanh⁡(Ufi)\displaystyle g(f_{i}) = \mathbf{v}^{\top} \tanh(\mathbf{U} f_{i})
  2. softmax 归一化对分数取指数再除以总和,保证权重非负且和为 1。
    wi=eg(fi)∑jeg(fj)\displaystyle w_{i} = \frac{e^{g(f_{i})}}{\sum_{j} e^{g(f_{j})}}
  3. 加权求和特征向量按权重混合,权重大的 patch 主导切片级特征。
    z=∑i=1Nwifi\displaystyle \mathbf{z} = \sum_{i=1}^{N} w_{i} f_{i}
  4. 热力图输出把每个 patch 的权重映射回空间坐标,得到高亮可疑区域的热力图。
    heatmap(x,y)=wi\displaystyle \text{heatmap}(x, y) = w_{i}

损失设计:不能因为全城干净就盖章正常

城市里 99% 的街区干干净净。如果训练时给正常和可疑街区同等对待,模型很快会发现“全部判正常”就能拿到 99% 准确率——这是偷懒但致命的捷径。于是考核制度修改:漏掉可疑分子记大过,已经确认干净的街区偶尔误判则从轻发落。

在训练层面,这就是类别不平衡问题。Camelyon16 训练集中,正常 patch 数量是肿瘤 patch 的数十甚至上百倍。若直接使用标准交叉熵,模型会偏向预测多数类。加权交叉熵给出两类不同的惩罚系数,让模型重视少数类:

更进一步的手段还有 Focal Loss,它把已被正确分类的简单样本的损失压得更低,让模型专注难样本;硬负样本挖掘则把每轮预测错误或最难的正常 patch 挑出来重点训练。考核制度一改,巡逻队就不敢闭眼盖章了——这是医学场景里“漏诊”比“误报”危险得多这一事实的直接体现。

L=−1N∑i=1N[α yiln⁡pi+β (1−yi)ln⁡(1−pi)]\displaystyle \mathcal{L} = -\frac{1}{N}\sum_{i=1}^{N} \left[ \alpha \, y_{i} \ln p_{i} + \beta \, (1-y_{i}) \ln (1-p_{i}) \right]
通俗说明:α 是“漏掉肿瘤”的惩罚倍数,β 是“误报正常”的惩罚倍数。临床中 α 通常大于 β,宁可多报、不可漏报。
  1. 标签 y_i1 表示该 patch 含肿瘤,0 表示正常。
    yi∈{0,1}\displaystyle y_{i} \in \{0, 1\}
  2. 预测概率 p_i模型输出的含肿瘤概率。
    pi=σ(fi)\displaystyle p_{i} = \sigma(f_{i})
  3. 权重 α 与 β正负类的惩罚系数,常用 α 远大于 β。
    α/β≈10/1\displaystyle \alpha / \beta \approx 10 / 1
  4. 对数损失y=1 而 p 很小时,ln p 是很大的负数,损失被放大。
    −ln⁡pi→+∞当 pi→0\displaystyle -\ln p_{i} \to +\infty \quad \text{当} \ p_{i} \to 0

评估指标:用 FROC 成绩单验收巡逻成果

演习结束,整个城市等着看成绩单:抓到了几个可疑分子?误抓了多少普通市民?如果把标准放得很严,“宁可错杀一千”,抓得全但误抓多;如果“绝不冤枉好人”,误抓少但漏得多。FROC 曲线就是一张在不同宽严标准下记录“抓得全与冤枉多”的成绩单。

Camelyon16 采用 free-response ROC 作为官方评估标准。算法对每张测试 WSI 输出若干可疑坐标及置信度,设定一个阈值后,落在标注区域内的检测被记为真正例,落在非标注区域的记为假阳性。不断放宽阈值,就能画出一条从苛刻到宽松的灵敏度—假阳性率曲线。曲线越高、左侧越陡,代表模型在少误报的同时还能高召回。

两条核心公式如下:

这套成绩单决定了算法设计导向:不能只回答“这切片有没有癌”,还要像病理医生一样把病灶定位出来。光是“整片判断对”可拿不到高分。

TPR(τ)=TP(τ)TP(τ)+FN(τ),FPI(τ)=FP(τ)Nslide\displaystyle TPR(\tau) = \frac{TP(\tau)}{TP(\tau) + FN(\tau)}, \quad FPI(\tau) = \frac{FP(\tau)}{N_{\text{slide}}}
通俗说明:TPR 是真阳性率,衡量漏网程度;FPI 是平均每张切片产生的假阳性数量。不同阈值 τ 下的一组组 (FPI, TPR) 连成 FROC 曲线。
  1. TP 与 FN真正例(检出的肿瘤区)与假阴例(漏检的肿瘤区)。
    TP(τ)+FN(τ)=全部真实病灶\displaystyle TP(\tau) + FN(\tau) = \text{全部真实病灶}
  2. TPR 计算检出的真阳性除以全部真阳性,漏得越少越接近 1。
    TPR=TPTP+FN\displaystyle TPR = \frac{TP}{TP + FN}
  3. FPI 计算假阳性总数除以切片数,代表平均每张误报几个区域。
    FPI=FPNslide\displaystyle FPI = \frac{FP}{N_{\text{slide}}}
  4. 官方评分取 FPI 为 0.25、0.5、1、2、4、8 时 TPR 的平均值作为最终分数。
    Score=16∑τ∈{0.25,0.5,1,2,4,8}TPR(τ)\displaystyle \text{Score} = \frac{1}{6} \sum_{\tau \in \{0.25,0.5,1,2,4,8\}} TPR(\tau)

临床落地:从演习场走进医院病理科

演习毕业,巡逻方案正式编入城市安防系统。医院病理科的“城市”每天产生几十张切片,AI 作为第一道智能巡逻队:低倍粗扫全城,圈出少量可疑街区,再调高倍精查这些街区,最终形成一份带证据的“可疑分子报告”,交给医生做终审。

临床部署要考虑三件事:速度、泛化、可解释。推理时间公式非常直接——总耗时约等于单个 patch 的推理时间乘以需要处理的 patch 数。两阶段方法把 M 急剧压缩:先用低倍率模型过滤出 Top-K 个高嫌疑区域,再只对这些区域对应的 40x 位置密集采样精查。相比全片密集网格采样,M 从十万级降到数千级,单张 WSI 的推理从小时级压缩到分钟级:

除了速度,不同医院的染色差异会破坏模型的颜色假设,因此部署时通常加染色归一化预处理;注意力热力图则提供了“AI 为什么标记这里”的视觉证据,这让医生敢复核、愿信任。城市安防的最终形态不是取代警察,而是让 AI 先跑腿巡街、标出可疑点并提交证据,由人类做终审——这就是医学影像 AI 真正落地时该有的样子。

tslide=tpatch×M,Mtwo-stage≪Mdense\displaystyle t_{\text{slide}} = t_{\text{patch}} \times M, \quad M_{\text{two-stage}} \ll M_{\text{dense}}
通俗说明:整片推理时间约等于单 patch 耗时乘以 patch 数量;两阶段方案让参与精查的 patch 数量大幅下降,从而把处理时间压到医生可接受的范围内。
  1. 单 patch 耗时GPU 上跑一次前向传播约几毫秒。
    tpatch≈3 ms\displaystyle t_{\text{patch}} \approx 3\text{ ms}
  2. 密集采样数量20 倍层全图网格 patch 数,可达数万。
    Mdense≈104∼105\displaystyle M_{\text{dense}} \approx 10^{4} \sim 10^{5}
  3. 两阶段压缩只有低倍筛选出的疑似区进入精查,数量级锐减。
    Mtwo-stage≈103\displaystyle M_{\text{two-stage}} \approx 10^{3}

AI 应用场景

术中前哨淋巴结快速评估

阶段:推理部署(patch采样 + 注意力聚合)

解决的问题:手术中切下的前哨淋巴结需在 30 分钟内完成冰冻切片判读,以决定是否清扫腋窝淋巴结。传统方式依赖病理医生在显微镜下快速浏览,时间紧张、漏检风险高。AI 先对 WSI 低倍粗筛并输出可疑区域热力图,医生只需重点复核这些 patch,再结合镜检快速确认。

具体案例:某三甲医院病理科把 Camelyon16 风格检测模型接入术中冰冻流程。AI 对一张切片生成热力图并标注 3~5 个可疑区,医生在 5 分钟内完成复核,冰冻等待时间从 25 分钟缩短到 12 分钟。

数字病理批量阅片预筛

阶段:离线批量推理

解决的问题:病理科每天产生几十张淋巴结切片,人工逐张浏览极易疲劳漏检。AI 部署在切片扫描仪后端,批量处理 WSI,按可疑程度排序并输出 FROC 评分报告,医生按优先级重点复核高风险切片。

具体案例:系统在 20x 层做均匀网格加随机采样,CNN 打分后经注意力聚合得出切片级风险分数,可疑切片再进入高倍精查。全科每周约 300 张切片,AI 预筛后需要重点复核的切片压缩到约 15%,医生的精力集中在最关键的 10~15 张上。

多中心临床试验中心病理复核

阶段:训练与评估(FROC)

解决的问题:肿瘤药物临床试验要求多位病理专家按统一标准复核入组患者淋巴结状态,人工复核周期长且中心间一致性差。AI 作为“第二阅片人”先在多中心做预判,出现分歧时再人工仲裁,提升一致性。

具体案例:某跨国药企在乳腺癌临床试验中,把 Camelyon16 风格模型部署到各中心站点,对前哨淋巴结 WSI 输出转移概率与病灶热力图。中心病理科依据 FROC 评分选择复核策略,将病理质控周期从 14 天缩短至 5 天,并保留完整 AI 标记作为审计证据。

Python 演示

python82 行
import numpy as np  class WsiSimulator:    """模拟一张WSI的patch网格结构, 包含少量聚集的肿瘤区域."""     def __init__(self, grid_size=40, clumps=4, seed=42):        rng = np.random.default_rng(seed)        self.grid_size = grid_size        self.labels = np.zeros((grid_size, grid_size), dtype=int)        for _ in range(clumps):            r = int(rng.integers(0, grid_size))            c = int(rng.integers(0, grid_size))            radius = int(rng.integers(2, 5))            for dr in range(-radius, radius + 1):                for dc in range(-radius, radius + 1):                    rr = r + dr                    cc = c + dc                    if 0 <= rr < grid_size and 0 <= cc < grid_size:                        if dr * dr + dc * dc <= radius * radius:                            self.labels[rr, cc] = 1     def sample_grid(self, n=300):        """均匀网格采样, 返回patch坐标列表与标签列表."""        step = max(1, self.grid_size // int(np.sqrt(n)))        rows = list(range(0, self.grid_size, step))        coords = [(r, c) for r in rows for c in rows][:n]        return coords, [int(self.labels[r, c]) for r, c in coords]     def sample_random(self, n=300, seed=1):        """随机采样, 返回patch坐标列表与标签列表."""        rng = np.random.default_rng(seed)        rs = rng.integers(0, self.grid_size, n)        cs = rng.integers(0, self.grid_size, n)        coords = [(int(r), int(c)) for r, c in zip(rs, cs)]        return coords, [int(self.labels[r, c]) for r, c in coords]  class AttentionAggregator:    """可微注意力聚合: 把patch特征向量加权为切片级特征."""     def __init__(self, feat_dim=16, seed=0):        rng = np.random.default_rng(seed)        self.proj = rng.normal(0, 1, (feat_dim, feat_dim)) / np.sqrt(feat_dim)        self.score_vec = rng.normal(0, 1, (feat_dim,)) / np.sqrt(feat_dim)        self.feat_dim = feat_dim     def forward(self, feats):        """输入(N, feat_dim)特征矩阵, 输出注意力权重与切片级特征."""        hidden = np.tanh(feats @ self.proj)        energy = hidden @ self.score_vec        weights = np.exp(energy - energy.max())        weights = weights / weights.sum()        slide = weights @ feats        return weights, slide  def main():    slide = WsiSimulator(grid_size=40, clumps=4)    total = slide.grid_size ** 2    print("总patch数:", total, "肿瘤patch数:", int(slide.labels.sum()))     for name, sampler in [("均匀网格", slide.sample_grid),                          ("随机采样", slide.sample_random)]:        coords, labels = sampler(300)        pos = int(sum(labels))        print(f"{name}: 采样300个patch, 命中肿瘤{pos}个, 占比{pos / 300:.1%}")     agg = AttentionAggregator(feat_dim=16)    coords, labels = slide.sample_random(300, seed=2)    feats = np.random.randn(300, agg.feat_dim)    feats[np.array(labels) == 1, 0] += 2.5    weights, slide_feat = agg.forward(feats)    prob = 1.0 / (1.0 + np.exp(-float(slide_feat @ agg.score_vec)))    print("注意力聚合-切片含肿瘤概率:", round(prob, 3))    top_idx = int(np.argmax(weights))    print("权重最高的patch真实标签:", labels[top_idx],          "| 前5高权重patch阳性数:", int(sum(labels[i] for i in np.argsort(weights)[-5:])))  if __name__ == "__main__":    main()
目的:用 NumPy 模拟 Camelyon16 的 patch 采样与注意力聚合全流程,对比均匀网格采样和随机采样对肿瘤 patch 的命中率,并检验注意力权重能否聚焦到真正的肿瘤 patch。
  1. self.labels = np.zeros((grid_size, grid_size), dtype=int)初始化 patch 标签网格,1 代表该街区含肿瘤。
  2. if dr * dr + dc * dc <= radius * radius:用圆内判断模拟肿瘤的聚集形态,而不是散成孤点。
  3. step = max(1, self.grid_size // int(np.sqrt(n)))均匀网格采样的步长:想让 n 个点尽量铺满全图,步长就取边长除以根号 n。
  4. coords = [(r, c) for r in rows for c in rows][:n]生成格点坐标列表,超出的部分截断。
  5. rs = rng.integers(0, self.grid_size, n)随机采样时一次性生成 n 个行坐标。
  6. weights = np.exp(energy - energy.max())softmax 分子的数值稳定写法,减去最大值防止指数爆炸。
  7. weights = weights / weights.sum()注意力权重归一化,保证总和为 1。
  8. slide = weights @ feats把 patch 特征按注意力权重加权求和,得到切片级特征向量。
  9. prob = 1.0 / (1.0 + np.exp(-float(slide_feat @ agg.score_vec)))用 sigmoid 把切片级特征转换成“整张切片含肿瘤”的概率。
  10. print("权重最高的patch真实标签:", labels[top_idx])检验注意力机制是否真的把最高权重给了肿瘤 patch。

常见误区

误区“病理切片不就是一张大图嘛,我把它缩放到 224×224 丢进 ResNet 不就行了,何必切 patch 这么麻烦?”

正解WSI 的肿瘤区域可能小到只有几百像素,整图缩放时转移灶细节会被抹成几个像素,模型根本看不见;而且万亿像素压缩到 224×224 意味着丢掉 99.99% 的信息。真正做法是切成 256×256 patch,在保留细胞级细节的同时让 CNN 吃得下。好比拿一张远距离卫星图找针尖大的可疑分子,正确做法是放大局部地图逐块排查。

为何常见:自然图像分类任务习惯了整图输入,忽略了 WSI 的百亿像素量级本身就是“另一个物种”,需要完全不同的处理粒度。

误区“切片里有什么我就随机采什么,按真实分布学习才客观,刻意控制比例会引入偏差。”

正解真实分布中肿瘤 patch 占比不足 5%,照这个分布训练,模型学到的是“输出正常几乎总是对的”,最终全线漏检。需要刻意把每个 batch 的阳性 patch 比例抬到 10%~30%,再用加权损失或 Focal Loss 纠正概率偏差。巡逻队如果每天只逛干净街区,永远学不会认可疑分子。

为何常见:机器学习课程反复强调样本独立同分布,但病理采样分布极度偏斜,必须主动干预数据分布才能让模型学到少数类特征。

误区“我的 patch 分类器有 98% 准确率,整张切片的检测效果肯定也差不了。”

正解patch 准确率高可能是因为绝大多数 patch 本就是正常组织——全判正常也能拿 98%。但临床要的是在极低误报率下定位真正的转移灶,这必须用 FROC 曲线在切片级别评估,看不同阈值下的灵敏度与假阳性数权衡。看成绩单要看整场搜捕行动的战果,而不是单台鉴定器的自测分数。

为何常见:分类准确率是最熟悉的单指标,容易忽略定位任务与极度不均衡 patch 分布带来的“准确率幻觉”。

面试题

Q1. Camelyon16 挑战赛的主要目标是什么?

单选题 · easy

  • A.检测乳腺前哨淋巴结转移区域← 正确答案
  • B.对皮肤痣图像做良恶性分类
  • C.检测肺部CT图像中的肺结节
  • D.分割大脑MRI影像的肿瘤区

答案:A。Camelyon16 正是围绕乳腺癌前哨淋巴结 WSI 上的转移灶检测展开,要求算法输出病灶位置而非仅做良恶性判别,因此选 A。

Q2. 一张 40 倍扫描的 WSI 为何不能直接送入 CNN?

单选题 · easy

  • A.分辨率超过亿级像素显存不足← 正确答案
  • B.染色过程造成图像严重失真
  • C.切片太厚导致对焦模糊不清
  • D.手工标注的样本数量太稀少

答案:A。核心矛盾是百亿级像素远超 GPU 显存与算力上限,必须切成 patch 处理。染色失真和对焦问题虽有发生,但不是“不能整图输入”的根本原因。

Q3. WSI 图像金字塔中,每升一层图像的宽高如何变化?

单选题 · easy

  • A.变为原来的 1/2← 正确答案
  • B.变为原来的 2 倍
  • C.宽高保持不变
  • D.变为原来的 1/4

答案:A。金字塔每层按 2 倍降采样,边长变为二分之一,面积变为四分之一。选 1/4 是把面积变化和边长变化搞混了。

Q4. patch 采样时正负样本比例严重失衡,最直接的应对方法是什么?

单选题 · mid

  • A.训练时限制正负样本的比例← 正确答案
  • B.把模型网络层数继续加深
  • C.将输入patch尺寸减半处理
  • D.降低学习率并增加训练轮数

答案:A。最直接的手段是采样时强制 batch 内阳性 patch 占比(如 1:10 甚至 1:3),再配合加权损失。加深网络或调学习率不解决类别分布问题。

Q5. 注意力聚合中,patch 权重用什么方式归一化?

单选题 · mid

  • A.softmax 归一化为总和为 1← 正确答案
  • B.sigmoid 归一化到 0 到 1 之间
  • C.min-max 缩放到 0 到 1 之间
  • D.L2 归一化为单位向量

答案:A。注意力权重用 softmax,保证权重非负且总和为 1,从而可解释为“重要程度分布”。sigmoid 无法保证跨 patch 总和为 1。

Q6. FROC 曲线的横轴表示什么?

单选题 · mid

  • A.平均每张WSI的假阳性数量← 正确答案
  • B.肿瘤patch的假阴性数量
  • C.模型推理的总耗时毫秒
  • D.训练集正负样本的比值

答案:A。FROC 横轴是 FPI,即平均每张切片的假阳性数;纵轴是真阳性率灵敏度。其他选项与评估范式无关。

Q7. 下列哪些做法常用于应对病理 WSI 的采样难题?

多选题 · hard

  • A.低倍率初筛加高倍率精查← 正确答案
  • B.训练时控制正负样本比例← 正确答案
  • C.对困难负样本进行挖掘← 正确答案
  • D.把patch尺寸增大到1024

答案:ABC。两阶段初筛精查、正负比例控制、硬负样本挖掘都是有效手段。把 patch 增到 1024 会破坏显存和细节平衡,一般不采用。

Q8. 关于 WSI 图像金字塔的说法,正确的有哪些?

多选题 · mid

  • A.相邻两层分辨率相差2倍← 正确答案
  • B.同一位置可在各层相互定位← 正确答案
  • C.低倍层适合快速全局粗筛← 正确答案
  • D.高倍层包含所有层级的全部信息

答案:ABC。金字塔各层以 2 倍关系降采样,坐标可互算,低倍层正适合全局粗筛。高倍层只是最底层原始图,不含其他层信息。

Q9. 两阶段推理(低倍初筛加高倍精查)的主要收益是什么?

单选题 · hard

  • A.大幅减少精查patch数量提速← 正确答案
  • B.让网络层数可以大幅加深
  • C.消除所有正常组织的干扰
  • D.无需再做染色归一化处理

答案:A。两阶段的核心收益是减少进入高倍模型的 patch 数量,推理时间约等于单 patch 耗时乘以 patch 数,因此显著提速。但它无法彻底消除干扰,也不能替代染色归一化。

Q10. 关于注意力聚合在病理 WSI 中的说法,正确的有哪些?

多选题 · extreme

  • A.权重由可学习打分网络产生← 正确答案
  • B.softmax让权重总和为1← 正确答案
  • C.热力图可辅助定位肿瘤区域← 正确答案
  • D.权重高就一定对应真正肿瘤

答案:ABC。注意力权重来自可学习打分网络并经 softmax 归一化,权重热力图常用于辅助定位。但注意力高权重可能被染色伪影或正常结构误导,不能无条件等同于肿瘤。