YOLO11+SCConv改进血细胞目标检测与自动计数实战

发布时间:2026/10/18 19:28:51
YOLO11+SCConv改进血细胞目标检测与自动计数实战 做白细胞分类检测项目时我最终选了YOLO11-SCConv这条改进路线目标是把血涂片显微图像里的RBC、Baso、Eosino、Lympho、Mono和Neutro六类细胞识别出来并且自动输出各类数量。刚看到任务时以为很简单血细胞嘛形态差别在教科书上写得清清楚楚直接跑个目标检测不就行了。真把标注好的数据铺开一看红细胞一片一片嗜碱性粒细胞翻几十张图可能才遇到一个淋巴细胞和单核细胞在染色浅一点的照片里几乎“长一个样”直接把基线模型按标准流程训一遍整体mAP还说得过去拉开每个类单独看就露馅了。这篇文章把从数据准备、SCConv模块设计、训练调参到计数落地的完整过程写下来包括很多文档里不会写、但我实际踩过的东西希望对做细胞检测、小目标检测或者想在YOLO系结构里加轻量注意力模块的朋友有参考价值。1. 为什么血细胞识别值得为它单独做一版改进模型1.1 这个场景的临床痛点和深度学习的机会血液涂片镜检是非常传统但不可替代的检查方式。目前自动化血液分析仪能给出散点图和分类参数但对形态异常和稀有细胞还是需要检验人员在油镜下人工镜检。血涂片一张片子几十个视野每个视野几十到上百个细胞人工分类计数费眼睛也费时间而且不同资历的检验人员对同一种细胞的判断可能不一致。深度学习目标检测能在一定程度上做成“先筛查、人复核”的工作流模型把六个类别的细胞框出来并计数检验人员只对有疑议的视野复核效率提升非常明显。我做的这个任务不是要替代检验医生而是做辅助识别。这决定了技术选型的一些细节宁可漏检一部分也不能大量误检因为误检会直接污染计数结果稀有类细胞漏多了这个模型对临床就是没用。所以“检测计数”这两个词是绑在一起的模型输出的框不仅要准数量统计还要能解释。这也是为什么我没选那种只看重mAP的通用模型路线而是花时间在数据、模块改进和阈值调优上做文章。1.2 六类细胞的形态差异看着简单模型却很头疼先说六个类别方便后面理解模型为什么在某些类上翻车。这里也说明一下项目名义上是白细胞分类但实际把RBC也放了进来因为红细胞在涂片里数量太大不框出来会影响计数和背景判断做检测时六类一起出框更省事。RBC红细胞数量最多无核双凹圆盘状瑞氏染色后呈淡橘红色中央比边缘颜色浅。Neutro中性粒细胞占白细胞总数的一半以上核分2到5叶胞质淡粉有很多细小的中性颗粒。Lympho淋巴细胞大小不一成熟小淋巴细胞核圆深染胞质少且呈淡蓝色。Mono单核细胞白细胞里体积最大的核形态多变肾形、马蹄形都有胞质灰蓝色常有不规则伪足。Eosino嗜酸性粒细胞核多为2叶胞质里充满粗大的橘红色颗粒特征很明显。Baso嗜碱性粒细胞数量极少胞质含蓝紫色粗大颗粒颗粒常把核盖住边界模糊。看到这里就知道难点在哪了。第一是类别极度不平衡RBC占了绝大多数Baso可能一百个白细胞里才几个标注数据天然稀缺。第二是相似类难分小淋巴和单核在某些染色批次里差异很小中性粒和嗜酸如果颗粒染色不够明显也容易混。第三是尺度和密度问题红细胞小且密集白细胞大但经常互相粘连重叠普通检测头对密集小目标并不友好。这些问题不是简单堆训练时间能解决的需要在网络结构上做点文章。1.3 为什么选择YOLO11而不是别的模型选YOLO11的理由比较务实。一是它作为YOLO系列的较新版本在检测精度和速度的平衡上已经做得很好nano版本很小适合后面部署到检验仪器或边缘设备。二是它的结构里有C3k2这种CSP风格模块模块内部卷积层清晰替换和改造非常方便适合用来验证SCConv这类即插即用模块。三是配套的训练流程成熟数据增强、多尺度训练、锚框策略都现成不需要我从零写检测器。如果当时选两阶段检测器或更重的分割模型也不是不行但血细胞这类稠密小目标的场景一阶段检测配上合适的改进通常性价比最高。我也试过只在YOLO11上加普通注意力模块效果有提升但不稳定后面细说。2. 核心改进思路SCConv在YOLO11里到底干了什么2.1 先看YOLO11的什么位置最值得引入新模块在动手改之前要理解YOLO11的大致骨架输入图像经过backbone逐级提取特征neck用PAN结构做多尺度融合head输出不同尺度的检测框。改进一般集中在这几个位置backbone的C3k2模块、neck的上采样融合处、head的前处理层。SCConv的核心是减少特征冗余类似的空间重标定和通道融合操作最适合放在特征分辨率还比较高、信息量大的早期和中期阶段而不是放在已经高度抽象的检测头附近。我最后选择把SCConv放进backbone里的C3k2模块尤其是网络较深层的那几个C3k2。原因是血细胞里的细节差异颗粒颜色、核形、胞质占比在深层特征上体现为通道间的响应差异SCConv的通道重建正好能强化这些差异。而浅层特征主要保留边缘和纹理改造收益不大还会增加不少计算量。2.2 SCConv的两板斧空间重建SRU和通道重建CRUSCConv模块由两个子模块组成理解起来很直观。第一个是空间重建单元SRU。输入的卷积特征已经包含大量空间信息但其中很多区域比如血涂片里大片空白背景几乎没有判别性属于冗余。SRU先对特征做GroupNorm利用分组统计量算出一个空间响应权重权重高的区域说明信息丰富权重低的就是背景或安静区域然后按权重把特征在空间上“拆”成信息丰富和信息稀疏两部分对两部分分别强化和抑制再用一个1x1卷积把两者重建回原尺寸。这样相当于让网络跟着细胞结构走而不是把背景和目标的特征一样对待。第二个是通道重建单元CRU。它把输入特征在通道维度分成两部分一部分走普通卷积提取细粒度变化另一部分先池化再卷积再上采样提取尺度更大的上下文信息最后通过通道注意力把两部分按权重融合。这样做的好处是不同类别的细胞会在不同的通道组合里被激活CRU让这些通道组合以更高效的方式合并减少通道维度上的重复表达。我在项目里使用的是按SCConv思想实现的适配版本不完全逐行对齐论文但保留了GroupNorm空间权重分离和通道分割-变换-融合这两个核心动作。结构上足够轻量参数增加很少训练时间基本可以接受。2.3 替换策略与预期收益具体操作是把C3k2模块内部的Bottleneck卷积替换成SCConv或者直接在C3k2输出后接一个SCConv。我对比过两种接法最后用的是前一种把C3k2里每个Bottleneck的1x1卷积换成SCConv短接结构保留这样SCConv拿到的输入已经经过了前几层卷积的初步加工改造效果更容易传导到后续特征。替换位置也做了消融。只替换backbone里的第一和第二个C3k2模型整体mAP提升约0.5个百分点只替换最后两个深层C3k2提升约1个百分点全部替换数值差不多速度慢一些。所以最终选择了替换backbone深层两个C3k2的方案兼顾效果和推理速度。预期收益不是单纯堆精度而是希望在不明显增加参数量和计算量的前提下把稀有类尤其是Baso的召回率拉上来把相似类Lympho和Mono的混检降下去。这个目标和SCConv“减冗余、保关键信息”的定位是吻合的。3. 数据集和标注模型能不能学明白七成看这里3.1 数据来源与清洗数据用的是公开可获取的血涂片显微图像数据集以瑞氏-吉姆萨染色为主图像分辨率普遍在1280x960以上实际训练时统一缩放。我先把原始数据里明显不合用的图片清理掉包括染色过浅几乎看不清细胞质、大量细胞重叠到连人都没法下标签的那种这种图放进训练集只会让模型学噪声。清洗后剩下约一万张左右的有效图像六类标签框大约六万多个。类别分布非常不均衡RBC标签最多占了将近六成Neutro次之Baso标签最少只有几百个占不到百分之一。这个分布如果不处理模型学到的大概率是“把东西全当RBC”。对Baso少的问题我用了两种办法一是对这些稀有类图像做更重的在线增强二是从公开数据里多收集包含Baso的片源。如果你也做类似项目第一优先级一定是想办法扩充稀有类样本而不是指望网络结构本身能力挽狂澜。3.2 标注规范小目标、粘连细胞、类别不平衡的处理标注是这里最花时间的一环。我用开源标注工具按YOLO格式输出txt文件类和坐标框一一对应。关于框的规范我自己定了一套规则避免两个人标出不同效果单个细胞完整可见时框紧贴细胞边缘不留过多背景。细胞粘连但边界可辨认时逐个框出允许少量重叠。多个细胞重叠严重、边界确实分不清时只框边界清晰的那个其他放弃。嗜碱性粒细胞即使被颗粒盖住只要通过核形和颗粒颜色能判断类别就要框出来。背景里的杂质、染料颗粒不框。这套规则直接影响模型学到的目标定义。很多项目在标注阶段很随意后来模型输出一堆碎框问题往往不在模型而在标签本身。如果你的数据是别人标好的也建议抽几十张图人工检查一遍看标注风格是否一致。3.3 数据增强与划分训练时我开启的增强包括mosaic、随机翻转、随机缩放、色域扰动其中色域扰动对血细胞特别重要。不同染色批次、不同显微镜光源下同一个类别的颜色差很多增强时的色相和饱和度抖动能让模型泛化不少。数据集按7:2:1划分训练、验证、测试。划分的时候要确保Baso在三个集合里都有出现我直接按类别分层抽样而不是简单随机切分。否则很可能验证集里一个Baso都没有评测结果虚高。4. 从实现到训练YOLO11-SCConv的落地配置4.1 环境准备与基准测试环境这件事看起来基础但血细胞数据集比较吃显存。我的机器是单卡显存24G输入尺寸640x640batch size设16可以稳定训练。如果你显存小优先降batch size而不是降输入尺寸血细胞目标小输入分辨率太低会直接损失性能。建议先跑通未改动的YOLO11n基线确认环境没问题、验证集mAP能正常评估再切换到SCConv版本。这样后续对比时如果精度出现变化可以确定是结构改动带来的而不是环境或代码差异造成的。4.2 SCConv模块代码实现下面给出我在项目中使用的SCConv适配版核心代码结构清晰可以直接作为模块插入YOLO11。需要注意代码里做了两个工程化处理一是通道数很小时GroupNorm的组数会被钳制防止报错二是CRU的通道分割比例alpha设成0.5让两个分支计算量均衡。import torch import torch.nn as nn class SRU(nn.Module): 空间重建单元通过分组归一化提取空间响应权重 将特征分为信息丰富与信息稀疏两部分再拼接重建。 def __init__(self, in_c, group4, reduction8): super().__init__() self.group group self.avg_pool nn.AdaptiveAvgPool2d(1) self.gn nn.GroupNorm( num_channelsin_c, num_groupsmin(group, max(1, in_c // 8)) ) hidden max(in_c // reduction, 4) self.conv1 nn.Conv2d(self.gn.num_groups, hidden, 1) self.conv2 nn.Conv2d(hidden, in_c, 1) self.sigmoid nn.Sigmoid() self.rebuild nn.Conv2d(in_c * 2, in_c, 1) def forward(self, x): b, c, h, w x.shape g self.gn(x).view(b, self.gn.num_groups, c // self.gn.num_groups, h, w) desc g.mean(dim(2, 3, 4), keepdimTrue).view(b, self.gn.num_groups, 1, 1) weight self.sigmoid(self.conv2(self.conv1(desc))) x_rich x * weight x_sparse x * (1 - weight) return self.rebuild(torch.cat([x_rich, x_sparse], dim1)) class CRU(nn.Module): 通道重建单元通道分割、双路变换、注意力融合。 def __init__(self, in_c, out_c, alpha0.5, kernel_size1, stride1, padding0): super().__init__() self.alpha alpha c1 max(int(in_c * alpha), 1) c2 in_c - c1 self.conv_local nn.Conv2d(c1, out_c, kernel_size, stride, padding) self.pool nn.AvgPool2d(2, 2) self.conv_ctx nn.Conv2d(c2, out_c, kernel_size, 1, padding) self.upsample nn.Upsample(scale_factor2, modenearest) self.fuse nn.Sequential( nn.Conv2d(out_c * 2, out_c, 1), nn.BatchNorm2d(out_c), nn.ReLU(inplaceTrue) ) def forward(self, x): c x.shape[1] c1 int(c * self.alpha) x_local, x_ctx torch.split(x, [c1, c - c1], dim1) y_local self.conv_local(x_local) y_ctx self.upsample(self.conv_ctx(self.pool(x_ctx))) return self.fuse(torch.cat([y_local, y_ctx], dim1)) class SCConv(nn.Module): def __init__(self, in_c, out_c, kernel_size1, stride1, padding0): super().__init__() self.sru SRU(in_c) self.cru CRU(in_c, out_c, kernel_sizekernel_size, stridestride, paddingpadding) def forward(self, x): return self.cru(self.sru(x))说明一下这个SRU里的分组统计和1x1卷积相当于把“哪些区域值得关注”做成逐通道的动态权重CRU里的local分支看细节、context分支看周边再融合这样既照顾到小细胞边缘又不丢掉细胞周围背景的上下文。如果你的数据集里密集粘连特别严重可以把CRU里上下文的池化改成3x3步长2感受野更大一点。4.3 模型注册与训练配置把SCConv插入YOLO11时我会新建一个自定义C3k2模块类Bottleneck不变把其中两个1x1卷积之一替换成SCConv保持输入输出通道一致注册到模型构建字典后在数据集配置文件里把backbone要替换的那两个C3k2名称改成新模块。模型配置文件不会大改只动两行。训练配置上输入640x640batch size 16初始学习率0.01配合余弦退火权重衰减0.0005训练300轮。稀有类处理上我给Baso在损失里加了一个类别权重同时开启过采样具体权重值需要在实验里调我这边是Baso权重设为Neutro的两倍左右效果最好设太大反而把模型带偏出现大量Baso误检。5. 训练实验与调参记录基线对比和趟过的坑5.1 训练参数设定与监控训练过程里除了看总损失还要看分类分支损失和置信度分支损失。血细胞类别多且分布不均分类损失不降往往就是稀有类在“摆烂”。所以我在每个epoch结束后都会在单独验证集上跑一次评估而不是只在最后评估。因为稀有类样本少验证指标波动很大最后那一下的结果很可能是运气。我记录300轮里每轮的mAP50和各类AP取验证集上连续10轮平均值最高的那版权重而不是最后一轮权重。学习率上SCConv版本比基线更敏感。基线用0.01没问题但SCConv版本如果初始学习率还是0.01前几十轮损失会下得偏慢我改成0.006左右更稳后面再余弦退火。这个差异可能和GroupNorm带来的统计特性变化有关具体原理没完全深挖但实践上确实存在。5.2 基线YOLO11与YOLO11-SCConv结果对比同样的数据、同样的增强参数、同样的训练轮数我跑出的对比结果大致如下不同随机种子下波动约1个百分点模型mAP50mAP50-95Baso AP50Lympho召回率Mono召回率参数量YOLO11n基线0.8820.6150.7980.860.77基准YOLO11nSCConv本项目0.9040.6480.8520.890.82约2%表格里Lympho和Mono我给的是召回率而不是AP因为这个场景里两者的“混检”问题比单类AP更能反映实际好不好用。SCConv版本里Lympho被误检成Mono的比例从大约11%降到7%这是提升最明显的地方。Baso的AP50涨了5个百分点左右但依然低于其他类瓶颈还是样本量太少。Eosino因为颗粒特征非常明显两个版本都接近0.9SCConv的收益不大这也是符合预期的。5.3 中间踩过的一些弯路第一次改造我单独把所有C3k2全换成SCConv版本精度没比只换深层两个高多少推理速度掉了接近一成所以最后选了折中方案。另一个弯路是CRU里上下文分支一开始用的标准卷积加最大池化结果密度高的区域特征被过度压缩RBC计数偏差反而变大后来把池化换成平均池化并加回一层上采样才好。还有一个印象很深的坑用全局混合精度训练时SCConv里的GroupNorm在个别批次会出现梯度异常损失跳到NaN关掉混合精度之后问题消失。如果你也遇到NaN先检查是不是这个原因。另外替换模块后第一次训练时损失曲线会比基线难看别急着判定失败多等几十轮再看。6. 推理与计数落地从检测框到一张可读的计数表6.1 推理流程与计数逻辑模型训练好之后实际使用流程是输入一张血涂片视野图模型输出所有类别的检测框每个框带置信度再按类别统计框数量得到这张图的细胞计数。这里有个关键细节计数用的置信度阈值和画可视化图用的阈值要分开。画图可以放宽到0.25视觉上能看到疑似细胞就行计数我建议放到0.4以上宁可漏掉一些低置信度框也不能让背景噪声混进计数里。这个0.4不是拍脑袋我统计过测试集上置信度在0.3到0.4之间的框误检比例明显高于正确比例卡在0.4刚好。计数代码很短直接对模型输出循环统计就行def count_cells(results, conf_thres0.4): cell_names [RBC, Baso, Eosino, Lympho, Mono, Neutro] counts {name: 0 for name in cell_names} for r in results: for box in r.boxes: if float(box.conf) conf_thres: continue name r.names[int(box.cls)] counts[name] 1 return counts6.2 全视野大图的滑窗处理单张显微视野图直接推理没问题但如果要处理一张全片扫描大图输入尺寸根本塞不下整个高分辨率图像。我用的是滑窗推理加重叠去重把大图切成1280x1280的窗口相邻窗口重叠128像素每个窗口单独推理最后把所有检测框汇总重叠区域的同一个细胞会被多次检测到再用一次NMS或按位置和类别合并去重。这一步一定要做否则计数结果轻易多出两三成。滑窗重叠选128像素主要是考虑白细胞直径在图像里一般不会超过80像素重叠128既能保证细胞不会被窗口边界切断又不会带来太多重复计算。滑窗的另一个好处是可以按窗口统计细胞密度。血涂片里白细胞本来就不均匀分布边缘视野白细胞少中心视野白细胞多分窗口计数后还能算出一个空间分布比单纯总数信息量大很多。6.3 计数结果与实用性分析最后在测试集上我拿模型计数结果和人工标注数量做了对比。RBC计数误差最小基本上在5%以内因为它量大、形态清晰Neutro和Eosino也在可接受范围Baso和Mono因为稀有和形态模糊单视野计数偏差较大但给到检验人员做复核提醒仍然有用。实用中我建议把模型输出理解成“第一轮自动筛查”尤其对Baso这种稀有类只要模型标出来了就值得人工多看两眼。我在测试的模拟场景里验证过模型能稳定找出人工第一遍可能略过的嗜碱性粒细胞这正是这类辅助工具最有价值的地方。7. 一些后续可以做的方向就我个人实际操作体会这个项目最有价值的地方不在于把mAP提高了几个点而在于理解了“检测”和“计数”在医疗辅助场景里是怎么互相约束的模型要准也要能让医生解释单纯的指标好看是不够的。如果后续继续做我会优先考虑两个方向。一是把Baso的样本问题从根本解决用更贴近真实涂片分布的数据或做细胞级合成增强而不是反复调损失权重。二是部署时把模型导出成ONNX再转成对应推理引擎在算力受限的仪器上跑实时的视野级计数辅助。最后再分享一个实用小技巧SCConv放进YOLO11后训练前期如果发现损失下降比基线慢别急着调结构把初始学习率降到原来的六成左右多训几十轮通常精度会反超。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询