尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

549张病理图实现99.74%识别率:迁移学习+图像分块+多数投票组合策略

549张病理图实现99.74%识别率:迁移学习+图像分块+多数投票组合策略 简介针对乳腺癌病理图像分类任务一份PDF文献深入研究基于卷积神经网络与迁移学习的可行方案面向医学图像处理、深度学习及机器学习方向的研究者与从业者。内容以AlexNet为架构搭建模型将HE染色病理图像细分为乳腺导管原位癌、浸润性导管癌、纤维腺瘤和乳腺增生四类针对高分辨率图像采用分块处理结合多数投票算法完成分类同时引入迁移学习与数据增强缓解标注样本不足导致的过拟合实验识别率达到99.74%。资源包内含1个PDF文件大小955KB适合需要了解CNN医疗影像落地思路、迁移学习调参技巧及图像分块集成方法的读者。目前已有349人学习可作为论文写作、课程设计或模型建模的参考辅助资料。1. 549张病理图做到99.74%识别率一个靠组合策略取胜的经典方案拿到这篇论文的时候第一反应是549张标注病理图四类数据还参差不齐图像识别率却做到了99.74%。这不是某个新算法的功劳而是卷积神经网络、迁移学习、图像分块、多数投票这四样成熟技术被组合进了一条完整流水线专门解决医学图像分类的两个经典死结——样本少、单张图分辨率太高。论文把任务从常见的良恶性二分类推进到四分类乳腺导管原位癌、乳腺浸润性导管癌、乳腺纤维腺瘤、乳腺增生。最后一类属于良性病变区分意义在于临床上不同类别对应完全不同的处理策略。对做医学图像分析、数字病理辅助诊断或者手里有标注数据但量不够的从业者来说这篇论文的技术路线可以直接借鉴参数也给得很完整。下面按拆这条线的顺序把模型选型、数据预处理、训练配置和实际复现最容易被绊住的细节逐步过一遍。2. 模型选型与迁移学习为什么是AlexNet而不是ResNet或从头训练2.1 四分类任务拆解从良恶性二分类到临床需要的细粒度分类先还原论文要解决的原始问题。当时已有的乳腺癌病理图像分类工作大多只输出两个结果良性还是恶性。临床上这不够用因为不同恶性类型的治疗方案差异很大导管原位癌可以保守处理浸润性导管癌需要更积极干预而纤维腺瘤和乳腺增生虽然是良性也需要明确区分出来。所以论文把分类目标定为四类乳腺导管原位癌、乳腺浸润性导管癌、乳腺纤维腺瘤、乳腺增生。这类细粒度分类对特征的要求比二分类高一个档次。良恶性二分类可能只看细胞密度、核异型程度这类整体差异就行四分类需要模型同时感知腺体结构、细胞排列方式、间质分布这些更细的线索。这意味着模型不能太浅浅层网络抽象能力不够但也不能太深医学小样本场景下深层网络更容易过拟合。论文在LeNet、VGG、GoogLeNet、ResNet、AlexNet这堆经典结构里选中了AlexNet理由其实很务实模型层数特点在这个场景的短板或优势LeNet5手写数字识别起家结构最简单卷积层太浅特征抽象能力不足AlexNet8ImageNet 2012冠军ReLULRN结构经典参数规模适中预训练权重好找GoogLeNet22Inception模块计算效率高结构复杂迁移改造和排查都费劲ResNet50起残差连接解决梯度消失网络深小样本微调阶段容易翻车AlexNet在ImageNet上的预训练模型到处都能拿到参数规模刚好卡在一个不上不下的位置比LeNet能提特征比ResNet不容易过拟合。对一个只有549张原图的医学数据集来说这个选择是合理的。2.2 AlexNet网络结构拆解五层卷积加三层全连接的具体参数AlexNet的组成是5个卷积层加3个全连接层输入227x227x3的RGB图像。每一层卷积后面接ReLU激活前两个卷积层和第五个卷积层后面带池化并且用LRN做局部响应归一化。具体参数如下层卷积核数量卷积核大小步长池化conv19611x11x343x3, stride2conv22565x5x313x3, stride2conv33843x3x31无conv43843x3x31无conv53843x3x313x3, stride2fc64096---fc74096---fc81000论文改成4---这组参数在Caffe的prototxt里长这样我按论文描述还原了一份作为参考layer { name: conv1 type: Convolution bottom: data top: conv1 convolution_param { num_output: 96 kernel_size: 11 stride: 4 weight_filler { type: gaussian std: 0.01 } bias_filler { type: constant value: 0 } } }这里有个细节值得说论文的全部描述和Caffe模型库里的bvlc_alexnet几乎一致。实际复现时不需要从零写网络直接拿现成的AlexNet prototxt把最后一层fc8的num_output从1000改成4就行。改动量极小这恰恰是选AlexNet的另一个隐形好处——生态里到处是现成模板。2.3 迁移学习用ImageNet预训练权重解决小样本过拟合迁移学习在整条流水线里的作用不是锦上添花而是能不能训练起来的先决条件。深度卷积网络参数以千万计只用几百张病理图从头训练结果必然是训练集上识别率很高、验证集上一塌糊涂。论文的做法是先在ImageNet——120多万张自然图像、1000个类别——上把AlexNet预训练好然后把预训练权重作为目标任务初始化迁移到乳腺癌数据集上做全局微调。这套做法的依据在于CNN不同层学到的东西不一样。前几层卷积学的是点、线、边缘、颜色块这类通用特征不管图像是猫还是病理切片低层特征都适用后几层学的是数据集专属的高层组合特征这部分需要在新数据上重新拟合。所以迁移学习不是简单拿来初始化而是让网络站在一个已经学会怎么看图像的起点上只用少量医学样本去调整高层的判别逻辑。论文用的是全局微调而不是只调最后几层这个选择也有讲究。病理图像和ImageNet的自然图像差异非常大如果冻结前面几层底层特征可能和病理图像的纹理不匹配全部放开微调虽然慢一点但给了网络把低层特征也适配到医学图像的机会。后面的实验对比证明了这个策略的价值迁移学习的AlexNet迭代10轮就达到99.74%而随机初始化的同类网络迭代20轮只能到97.4%。3. 数据预处理实战染色归一化、数据增强与图像分块的完整流程3.1 Reinhard染色归一化把不同实验室的切片颜色拉到同一标准病理图像和自然图像最大的区别在于颜色本身就可能是干扰。HE染色后的切片不同实验室用的染色剂批次、染色时间、切片厚度、扫描仪型号都不一样直接表现就是同一类组织的颜色分布差异很大。如果网络把颜色当成判别特征换一批数据就很可能失效。论文采用的Reinhard方法思路非常直接把图像转到lαβ色彩空间然后把l亮度和α、β颜色对立通道三个分量的均值和标准差统计匹配到一张参考图像上。这样所有输入图像的颜色分布都被拉到了与参考图一致。对应到代码大约是import numpy as np from skimage import color def reinhard_normalize(img, ref_mean, ref_std): # 转到 lab 色彩空间l 通道管亮度a/b 通道管颜色 lab color.rgb2lab(img) # 逐通道做均值、标准差对齐 for i in range(3): channel lab[:, :, i].astype(np.float32) channel (channel - channel.mean()) / (channel.std() 1e-8) channel channel * ref_std[i] ref_mean[i] lab[:, :, i] channel # 转回 RGB 供模型输入 return color.lab2rgb(lab)这段代码的核心逻辑是把每个通道先减去自身均值、除以自身标准差得到一个标准化分布再乘上参考图的ref_std、加上参考图的ref_mean。ref_mean和ref_std是提前从一张色彩标准的参考切片上统计出来的三通道数值。1e-8是防止标准差为0时除零只是数值稳定性不影响结果。要注意的是Reinhard归一化只解决颜色层面的差异不解决光照不均匀、组织折叠、切片气泡这类问题。论文只处理了染色归一化说明这个数据集的图像质量问题主要出在颜色分布上。3.2 数据增强旋转与色彩扰动把训练样本扩充十倍原始数据集只有549张图就算每张切成若干个patch样本量对微调一个千万级参数的AlexNet来说仍然偏紧。论文的数据增强用了两种手段一是几何变换把图像旋转90度、180度、270度二是颜色和对比度扰动在RGB通道上做随机扰动。两种手段组合使用把数据量扩充到原来的10倍最终参与训练的patch数达到20余万。常见做法是几何增强和颜色增强同时实施在patch级别而不是原图级别即先分块再增强。旋转操作要注意一个边界问题90度旋转会让图像的宽高互换如果是非正方形图像旋转后尺寸不一致。论文数据里浸润性导管癌是1329x993导管原位癌和纤维腺瘤是1360x1024都不是正方形所以增强流程里通常还会跟着一个中心裁剪或resize操作来统一尺寸。数据增强的实现类似from PIL import Image import random def augment(patch): # 随机选择旋转角度0/90/180/270 angle random.choice([0, 90, 180, 270]) patch patch.rotate(angle) # 颜色扰动对 RGB 三通道乘上随机系数 r, g, b patch.split() factor_r 1 random.uniform(-0.1, 0.1) factor_g 1 random.uniform(-0.1, 0.1) factor_b 1 random.uniform(-0.1, 0.1) r r.point(lambda x: max(0, min(255, int(x * factor_r)))) g g.point(lambda x: max(0, min(255, int(x * factor_g)))) b b.point(lambda x: max(0, min(255, int(x * factor_b)))) return Image.merge(RGB, (r, g, b))扰动系数-0.1~0.1是常见范围论文没有给出具体数值我一般会控制在这个量级。系数太小增强效果不够系数太大会让图像颜色失真网络学到错误的颜色映射。这里有一个容易忽略的点数据增强只应该作用于训练集。验证集和测试集保持原始分布不变否则模型在增强数据上见过类似变体验证结果就不客观了。论文的训练/验证/测试划分是60/20/20增强发生在划分之后只看训练集那一部分。3.3 图像分块227x227的patch与50%重叠率的取舍AlexNet的输入固定是227x227而原始图像都在1000像素以上直接resize到227会丢掉大量细节。病理图像里的诊断依据经常是细胞级别的形态如果整体缩小腺体边界和细胞核形态全糊了。论文的思路是图像分块把高分辨率原图切成多个小patch每个patch独立送入模型分类再用投票整合出整张图的结果。分块策略分训练和测试两套。训练阶段用重叠分块重叠率50%也就是滑动窗口步长取patch大小的一半大约113像素。这样相邻patch之间有大量重叠内容每张图能产出的训练样本数翻了几倍网络见过更多不同位置的局部视图。测试阶段改用不重叠分块每张图分成30块或48块块大小都是227x227。分块的实现核心是一个双循环滑动窗口def extract_patches(img, patch_size227, overlap0.5): h, w img.shape[:2] stride int(patch_size * (1 - overlap)) patches [] for y in range(0, h - patch_size 1, stride): for x in range(0, w - patch_size 1, stride): patches.append(img[y:y patch_size, x:x patch_size]) return patches训练时调用extract_patches(img, overlap0.5)测试时调用extract_patches(img, overlap0.0)。overlap0.0时stride227窗口之间完全不重叠。边缘处理上如果图像尺寸不能整除227最后一行或一列不足227的区域会被跳过这也是为什么不同尺寸的图像分出的块数不一样论文里30块和48块的差别就是这么来的。提示训练和测试务必分开设置 overlap测试阶段设 0否则结果虚高。关于重叠率还有一点50%不是拍脑袋定的。重叠率越高训练样本越多但相邻patch高度相似网络容易对位置产生过拟合重叠率太低又回到样本量不足的问题。论文选了50%且测试时归零兼顾了训练数据量和测试客观性。4. 训练配置与多数投票从Caffe训练到结果整合的全过程4.1 Caffe训练参数batch_size、学习率与迭代策略怎么定论文实验基于Caffe框架环境是CentOS Tesla K10 GPU训练大约3小时。关键超参数论文给得很明确batch_size104初始学习率0.001最大迭代10个epoch。训练集、验证集、测试集按60/20/20划分最终结果是五次随机分配数据集后的平均。batch_size104不是常规取值一般预训练模型微调用128或64更常见。这里选104可能是为了配合数据集的patch总数让每个epoch的迭代次数是整数也可能单纯是K10的显存上限。复现时不用死守104根据自己的显存调整即可但要把学习率同步缩放——batch翻倍时学习率也应适当放大这是后续在更大显存卡上踩出来的经验。Caffe里对应到solver的配置大约是这样net: alexnet_breast.prototxt base_lr: 0.001 momentum: 0.9 weight_decay: 0.0005 lr_policy: step stepsize: 5000 gamma: 0.1 max_iter: 20000 solver_mode: GPU注意max_iter单位是迭代次数而不是epoch。训练集有20余万patchbatch_size104一个epoch大约是2000次迭代10个epoch对应约2万次迭代。所以solver里的数值要按实际数据集大小折算不能照抄。上面配置里的momentum、weight_decay是按Caffe和AlexNet惯例补的论文只给了batch_size、学习率和epoch数。为什么预训练模型微调只用0.001的学习率因为初始化权重已经在一个合理的最优点附近学习率大了会直接把预训练学到的特征冲掉等于白做迁移。论文在验证集上patch准确率能达到95%~98%这个水平说明微调策略是收敛的。4.2 多数投票算法把patch级别的结果还原成图像级别图像分块后一张图产生30或48个patch每个patch都会输出一个softmax概率分布。这些patch的分类结果可能不一致尤其边缘patch可能落在背景或染色异常区域。需要一个整合规则把它们合成单个图像的结论。论文比较了最大规则、最小规则、中值规则、多数投票四种融合方法后选择了多数投票。多数投票的规则很简单每个patch投出自己概率最大的那一类统计所有patch的票数得票最多的类别作为整张图的分类结果。实现上几行代码就够import numpy as np from collections import Counter def majority_vote(patch_probs): # patch_probs: (N, C)N 为 patch 数C 为类别数 votes np.argmax(patch_probs, axis1) counter Counter(votes) winner, vote_count counter.most_common(1)[0] return winner, vote_count, len(votes)np.argmax(patch_probs, axis1)对每个patch取出置信度最高的类别下标Counter统计四个类的票数most_common(1)取出票数最高的类。如果出现平票most_common会返回先出现的那一类更稳妥的做法是在平票时改用平均概率对比取平均概率最高的类。四种融合规则的差异可以从一个表里看明白融合规则做法适用场景最大规则取各patch概率最大值对应的类单块置信度高时最小规则取最小概率对应的类保守场景中值规则取概率中位数对应的类平滑噪声多数投票票数最多的类获胜分块多且独立时多数投票相比其他规则的优势在于它对少数patch的误判不敏感。只要超过一半patch分类正确整张图的最终结果就是对的。论文给了个很直观的例子——一张纤维腺瘤图像被分成30个patch其中26个分类正确、4个被错分成了浸润性导管癌投票后依然是纤维腺瘤胜出。4.3 实验结果怎么读patch准确率99.32%与图像准确率99.74%论文的最终结果是一组让第一次看的人觉得矛盾的数字patch级别的平均识别率99.32%图像级别99.74%后者反而比前者高。想明白之后会觉得这个设计很巧妙。图像准确率高于patch准确率正是因为多数投票的容错机制。一张图像有30个patch即使其中有少数patch被错分剩下的同类别patch依然占多数投票结果仍然正确。换句话说patch是基分类器多数投票是集成器集成之后整体性能自然高于单个patch的性能。这个模式在集成学习里很常见。四个类别的详细结果如下图像类别patch识别率图像识别率乳腺浸润性导管癌99.80%100%乳腺导管原位癌99.84%100%乳腺纤维腺瘤99.24%100%乳腺增生98.39%98.95%平均99.32%99.74%注意乳腺增生这一类它是唯一低于99%的类patch识别率98.39%图像识别率98.95%。深究原因乳腺增生的训练样本只有99张是四类里最少的150张一类和99张一类的样本量差距在结果里直接体现出来了。这是一个信号如果数据量再均衡一些整体准确率还能往上走。作为对比论文还试了一个类似cifar10的网络结构、patch大小64x64、随机初始化从头训练的模型迭代20轮只能到97.4%。同样数据下迁移学习的AlexNet用10轮就收敛到99.74%训练代价明显更低。而传统SIFT特征加SVM的方法在四分类上落后更多说明这个场景里深度学习路线确实有代差优势。5. 复现避坑指南五个绕不过去的坎论文里有些坑写得很隐晦甚至是靠实验对比才暴露出来的。以下五条都是复现时会真实碰到的每一条都按现象、原因、解决的顺序拆开。5.1 小样本直接训练CNN必然过拟合现象现有549张原图直接搭一个从零初始化的AlexNet就开始训练。训练集准确率一路冲到99%以上验证集却停在80%~85%震荡并且随着迭代次数增加训练和验证的差距越拉越大。原因AlexNet参数量以千万计549张图即使分块增强有效独立信息也远不够。网络在训练集上把每个样本的独特细节当作判别特征记了下来比如染色残留、切片划痕、气泡边缘而不是真正学组织学特征。这就是典型的过拟合。解决论文的解法是迁移学习——用ImageNet上预训练好的权重初始化网络再全局微调。预训练权重提供了通用的低层视觉特征网络不用从头摸索边缘、纹理这些只需要花力气调整高层语义组合。一个直观的对比是论文里cifar10_64模型随机初始化训练20轮只到97.4%而AlexNet迁移学习10轮就到99.74%。复现时判断过拟合有一个简单信号训练曲线里训练loss持续下降、验证loss掉头上涨两条曲线分叉。一旦出现这个信号优先检查是否用了预训练权重而不是盲目加正则化。5.2 高分辨率图像直接塞进模型导致显存溢出现象把1329x993或1360x1024的原始病理图直接缩放到227x227输入AlexNet显存没有压力但分类结果明显不对尝试把原图原尺寸输入显存直接溢出训练根本跑不起来。原因直接缩放的代价是把细胞层面的诊断信息压缩没了。病理诊断依赖腺体结构、细胞核形态、染色分布这些局部细节整体resize后这些线索全部糊成一团。原尺寸输入的问题更直接AlexNet的输入层固定227x227网络维度写死分辨率超限要么结构不匹配要么显存放不下。解决按论文做法对图像分块。用227x227的窗口在原图上滑窗切割每个patch独立输入模型。这么做有两个直接收益一是patch保留了足够的分辨率细节细胞核边界还清晰可辨二是分块天然增加了训练样本数549张图切完后patch数量足以支撑微调。复现时注意分块要在染色归一化之后做顺序不能反否则每一块的色彩统计差异会重新引入颜色漂移。还有一个工程细节病理切片长宽不一定是227的整数倍边缘不足227的区域要么丢弃、要么补齐我一般选丢弃更稳妥补齐会产生假的组织边界。5.3 测试时也用重叠分块导致结果虚高现象测试阶段沿用训练时的重叠分块图像识别率比论文报告值还能高出一截验证集上稳定。但把同一个模型拿到外部数据一测准确率马上掉回正常水平。原因重叠分块让测试patch之间信息高度冗余。模型在训练时已经见过几乎相同的位置和特征测试等于做一张做过的卷子。具体来说50%重叠率意味着相邻patch有一半面积完全重合这些patch的输出高度相关投票结果天然倾向于训练时见过的模式虚高是必然的。解决训练和测试分成两套分块策略。训练阶段overlap0.5最大化样本量测试阶段overlap0.0步长等于patch大小保证patch之间没有信息重叠。论文正是在推理阶段采用不重叠分块每张图固定产出30或48个patch。复现时在测试脚本里把overlap参数设置成0并在测试报告里注明分块策略防止后续阅读理解偏差。5.4 预训练模型微调学习率过大导致权重被冲掉现象微调阶段沿用从头训练时习惯的0.01或0.1学习率训练一开始loss不降反升验证集准确率在50%上下抖动接近随机猜测。原因预训练权重是从ImageNet学出来的已经处在一个loss很低的最优区域附近。学习率过大时每一步参数更新幅度都很大相当于每次都在最优点附近乱跳一次更新就跳飞。迁移学习的梯度方向和从头训练完全不同初始阶段的梯度主要由类别差异驱动大学习率会让低层特征直接崩坏。解决论文使用0.001作为初始学习率这是预训练模型微调的安全起点。实际复现时我会在0.001基础上再降一个量级到0.0005或0.0003配合StepLR在验证集loss进入平台期后衰减学习率。如果初始学习率设得过大最直接的信号是训练loss从第一个batch起就是NaN或者一直在涨这时候不是调优化器而是先调学习率。如果换到更大的预训练模型比如ResNet50学习率还要再降模型越大预训练权重包含的信息越多微调时步子更不能迈大。5.5 类别不平衡让样本少的类识别率拖后腿现象乳腺增生在四类中只有99张原图最终结果里它的patch识别率98.39%图像识别率98.95%都是四类最低。错分的样本部分被判成了纤维腺瘤两类在形态上确实接近。原因数据增强是类内操作的99张图的类别增强后仍然比150张图的类少三分之一。模型各类别先验概率差异直接反映到测试表现上。激进的增强虽然能把样本量拉齐但如果旋转角度或色彩扰动幅度过大又可能让增强后的图像远离真实分布引入新的噪声。解决论文没有做任何类别平衡处理直接用原始样本分布训练结果已经足够好。如果复现时希望四个类别更均衡有两个常用方案。一是在数据增强阶段对少数类做额外增强把增强倍数从10倍提高到15倍二是采用加权交叉熵损失给乳腺增生类更高的权重。两条路都试过的话加权重的方式更稳因为它不会改变训练数据的分布只是改变梯度贡献比例。评估时要分开看每个类别的指标只盯平均准确率很容易被大类的表现掩盖住论文给的四分类结果表就是很好的模板。6. 进阶验证用PyTorch快速搭出四分类模型的两个实用技巧6.1 用torchvision的AlexNet快速搭出四分类模型如果不想碰Caffe的老环境现代框架里复现这篇论文的模型改动量很小。torchvision里直接有AlexNet的实现和预训练权重import torchvision.models as models import torch.nn as nn model models.alexnet(pretrainedTrue) # classifier 是 Sequential最后一位是 1000 类的全连接层 in_features model.classifier[6].in_features model.classifier[6] nn.Linear(in_features, 4)models.alexnet(pretrainedTrue)返回结构和ImageNet预训练权重都对齐的模型。model.classifier包含三层全连接层classifier[6]就是最后的输出层读它的in_features再换成4类输出。训练时用比Caffe更低的初始学习率比如0.0005配合ReduceLROnPlateau在验证集loss进入平台期时降学习率。6.2 跑通验证管道再训练先过拟合十张图再全量训练复现这篇论文最稳妥的顺序是先小后大。拿10张训练图像关掉数据增强强制模型在10张图上过拟合到100%这一步验证的是代码管道有没有bug——如果十张图都学不进去说明模型定义或数据加载有问题全量训练只会浪费时间。管道确认没问题后再开启数据增强、切回预训练权重、把整个流程跑起来。从那以后我做任何病理图像分类项目都先走一遍这个十张图过拟合的验证流程这个习惯已经帮我抓出过三次数据归一化写错的问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表