简介:这一数据集是围绕猫网织红细胞显微图像构建的深度学习中训练样本,适用于医学图像分类、目标检测及卷积神经网络相关项目。资源源于一篇利用CNN自动测定猫网织红细胞百分比的研究,图像由标准实验室显微镜配合普通显微镜相机或智能手机相机采集,贴近基层兽医诊断设备条件。压缩包内共2000个XML标注文件,配套约2333张显微图像,整体体积约98.51MB;训练图像置于图像文件夹,标签文件对应存放,测试集则采用替代设备拍摄,可用于验证模型在不同采集条件下的表现。对于需要真实医学显微图像数据开展模型验证的开发者,可直接将XML标签与原始图像配对使用,省去大量标注时间,同时可基于拍摄设备差异构建泛化能力更强的分类器。这类数据对兽医临床细胞学自动分析、医学影像辅助诊断等方向具有直接实用价值。目前已有218人学习浏览,适合医疗AI、显微图像识别方向的入门及进阶实践。
1. 细胞显微图像数据集:深度学习落地前先解决数据这一关
做细胞图像识别的人,大概率都经历过这种尴尬:模型结构早就熟透了,损失函数调来调去也没问题,最后发现卡住整个项目进度的,居然是「没有一份能用的显微图像数据集」。不同细胞类型的显微图像,形态差异大、染色方式多、背景噪声重,跟常规自然图像数据集完全是两个世界的玩法。这份以细胞类型为维度的显微图像数据集,主要解决的就是深度学习里的数据冷启动问题——它把细胞图片、类别标签和标注信息打包成可以直接喂给训练管线的格式,适合做目标检测、图像分类、语义分割三类任务的入门与中期验证。不管你是刚接触医学影像的算法工程师,还是实验室里需要自己标注数据的生物信息方向研究者,这份资源都能帮你把「跑通流程」这件事从一周压缩到半天。
2. 数据选型与标注格式:先搞清楚标签长什么样再动手
拿到一份显微图像数据集,第一反应不应该是扔进训练脚本里跑,而是先花半小时回答三个问题:这份数据是干什么任务的、标签格式是什么、类别分布长什么样。这三件事直接决定了你要写哪套代码、怎么配 dataloader、以及后面调参时优先看哪个指标。我见过不少人在这一步翻车——拿目标检测的数据集去做分类训练,或者标注是 VOC 格式却套用了 COCO 的 API,结果光格式转换就耗掉两天。
2.1 显微图像任务类型先对齐:检测、分类还是分割
细胞显微图像这个场景里,最常见的三个任务是目标检测、图像分类和语义分割。目标检测负责定位每个细胞并给出类别,适合「一张视野图里有多少个细胞、分别是什么类型」的场景;图像分类只判断整张图属于哪个细胞类别,适合已经裁剪好的单细胞图片;语义分割则要精细到像素级细胞轮廓,用来算面积、统计形态参数。不同的任务对标注要求完全不同,检测只需要一个边界框坐标,分割却要把每个细胞的边缘逐像素描出来,成本差一个数量级。
选任务时我一般会这样判断:如果项目目标是计数和细胞分型,优先做检测;如果下游还有形态学分析(比如核质比、面积分布),那就直接上分割。最怕的是任务没定清楚就急着开工,做到一半发现标注信息不够,再回去补标注基本等于重来。这份数据集如果自带框级标注,先按检测跑通流程是最稳妥的路径。
2.2 标注格式三选一:VOC、COCO 还是 YOLO
显微图像数据集最常见的三种标注格式是 Pascal VOC、COCO 和 YOLO。VOC 格式本质是 XML 文件,每个文件对应一张图片,标签名、边界框坐标都写在里面;COCO 格式是 JSON,所有图片的标注集中在同一个文件里,还额外带 categories、annotations 等字段;YOLO 格式则是纯文本文件,每行一个目标,格式是「类别id cx cy w h」,坐标全部归一化到 0 到 1。
三种格式各有优劣,选型主要看你准备用什么框架。if 你打算用 ultralytics 的 YOLOv8 系列,那最省事的路径是直接转成 YOLO 格式;if 你要做对比实验、跑 mmdetection,那 COCO 格式更方便。我个人的习惯是统一转成 YOLO 格式,因为它文件小、读取快、不依赖额外解析库,而且 ultralytics 生态对它的支持最完整。下面是一个简单的转换脚本,把 VOC 的 XML 转成 YOLO 的 txt:
import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, out_path, class_names): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue cls_id = class_names.index(name) bndbox = obj.find('bndbox') xmin = float(bndbox.find('xmin').text) ymin = float(bndbox.find('ymin').text) xmax = float(bndbox.find('xmax').text) ymax = float(bndbox.find('ymax').text) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h box_w = (xmax - xmin) / img_w box_h = (ymax - ymin) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") out_file = Path(out_path) / (Path(xml_path).stem + '.txt') out_file.write_text('\n'.join(lines), encoding='utf-8')这段代码的核心逻辑是:从 XML 里读出图片宽高,遍历每个目标对象,把左上角右下角坐标换算成归一化的中心点坐标和宽高。注意class_names列表的顺序必须和配置文件里的类别顺序完全一致,否则类别 id 会对不上,训练时标签全乱。另外bndbox里的坐标有时候会出现 xmin 大于 xmax 的情况,通常是标注软件导出 bug,转换前最好加一个排序保护,否则训练时 loss 会异常跳变。
2.3 类别分布与标注质量:决定你是调参还是补标
格式看懂了,下一步统计类别分布。用一个脚本把整个数据集扫一遍,数出每个类别的样本数,输出一个柱状图或者表格。这一步太重要了——显微图像的类别不平衡往往比自然图像更极端,比如健康细胞类型数量是病变细胞的几十倍,这种情况不做处理,模型训出来会说「全预测成健康细胞」准确率还很高。统计完分布以后,再去抽检一部分标注框,看看边界框是不是贴边、有没有把两个粘连细胞框在一起、有没有类别标错。标注质量直接决定模型的精度上限,一个「框偏了半个细胞身位」的训练集,再怎么调参也拉不回 mAP。
常见的标注问题有三个:框太小(把细胞碎片当完整细胞框了)、框太大(把背景和相邻细胞都框进去)、标签噪声(正常细胞标成病变细胞)。前两个可以通过后处理过滤掉小框、用 NMS 合并重叠框来缓解,第三个问题最难办,只能靠抽检人工复核。显微图像的标注成本本身就高,如果数据集是自动标注+人工修正的,建议重点检查边界情况——细胞密集区域往往就是错误高发区。
3. 显微图像预处理流水线:光照补偿、去噪与细胞增强策略
显微图像和自然图像最大的区别在于成像机制。自然图像的光照是相对均匀的,但显微图像受光源、物镜、载玻片厚度影响,经常出现中心亮边缘暗、或者某个区域整体偏色的问题。这种系统性的光照不均直接喂给模型,会让模型学到「位置先验」而不是「形态先验」——比如它可能靠「在图像中心」来判断细胞类型,而不是靠细胞本身的纹理特征。所以预处理不是可选步骤,而是决定模型泛化能力的关键一环。
3.1 显微图像的三个噪声来源:光照不均、散粒噪声与背景杂讯
第一个噪声来源是光照不均,表现为同一张图里不同区域的亮度基线不一样,滑动窗口滑过去,每个窗口的亮度分布都不同,数据增强根本扛不住这种系统性偏移。第二个来源是传感器散粒噪声,显微成像为了减少光损伤通常会降低曝光,结果就是暗区域信噪比很低,细胞边缘模糊。第三个来源是背景杂讯,培养基残留、染料沉淀、载玻片划痕都会制造伪纹理,模型很容易把这些伪纹理当成细胞特征。
针对这三个问题,我常用的预处理组合是:先做背景估计和相减,再做一个对比度归一化。背景估计最稳定的方式是形态学开闭运算——用一个足够大的结构元素做形态学开运算,把细胞区域抹掉,剩下的就是背景亮度场。然后用原图减去背景场,得到光照补偿后的图。对比度归一化则可以用 z-score,对整张图按均值和标准差归一化,保证不同染色批次之间的亮度分布对齐。预处理脚本参考如下:
import cv2 import numpy as np def flat_field_correct(image, kernel_size=127): # 形态学开运算估计背景亮度场 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size)) background = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel) # 背景相减,补偿光照不均 corrected = cv2.subtract(image, background) # z-score 归一化,消除染色批次差异 corrected = corrected.astype(np.float32) mean = corrected.mean() std = corrected.std() if std == 0: return corrected normalized = (corrected - mean) / std return normalized def train_transform(image): # 对单张训练图像做完整预处理链路 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) if len(image.shape) == 3 else image corrected = flat_field_correct(gray) clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(corrected.astype(np.uint8)) return enhanced这段代码里的kernel_size参数需要按细胞尺寸调整,一般取「细胞直径的 3 到 5 倍」——如果细胞在图像里大约占 30 个像素,kernel 直接取 127 左右不会出大错,但更严谨的做法是先做一次连通域分析量一下细胞核的直径分布。clipLimit是 CLAHE 的对比度限制阈值,太小会对比度不足,太大会放大噪声。我在血细胞图像上通常用到 2.0,在染色较浅的组织切片上会调到 3.0,这个参数建议可视化对比后再定,不要盲抄。
3.2 细胞粘连与边缘模糊:增强策略要按显微图像特性设计
预处理做完以后,数据增强需要注意显微图像特有的问题。自然图像常用的 RandomCrop 和 RandomRotation 在细胞图像上依然有效,但有两个增强操作要谨慎:一是色彩抖动要关掉或者调小,因为显微图像的染色颜色是有诊断意义的,乱动饱和度会让模型丢掉染色信息;二是翻转和旋转要考虑细胞的形态对称性,上皮细胞有明显的极性,旋转 90 度可能就让类别特征失效了。
显微图像增强里最有价值的是形变类增强——弹性形变(elastic deformation)和缩放。细胞在制片过程中本来就会发生形变,模拟这种形变能让模型对细胞形态变化更鲁棒。另一个强调的是马赛克增强(mosaic),尤其对检测任务,把四张图拼在一起训练,相当于变相增大了 batch 尺寸,对密集小细胞的检测效果好得明显。下面这段是 mosaic 增强的核心逻辑,我用的是 YOLOv8 内置变体:
import cv2 import numpy as np def mosaic_augment(image_paths, labels_list, img_size=640): canvas = np.zeros((img_size * 2, img_size * 2, 3), dtype=np.uint8) positions = [(0, 0), (img_size, 0), (0, img_size), (img_size, img_size)] for idx, path in enumerate(image_paths[:4]): img = cv2.imread(path) img = cv2.resize(img, (img_size, img_size)) x, y = positions[idx] canvas[y:y+img_size, x:x+img_size] = img # 在拼接图上随机生成裁剪框 cx = np.random.randint(img_size // 2, img_size * 3 // 2) cy = np.random.randint(img_size // 2, img_size * 3 // 2) x1 = max(0, cx - img_size // 2) y1 = max(0, cy - img_size // 2) x2 = min(img_size * 2, cx + img_size // 2) y2 = min(img_size * 2, cy + img_size // 2) mosaic = canvas[y1:y2, x1:x2] # 坐标需要同步映射到裁剪后的区域 return mosaic, labels_list这个方案的核心只有一句话:四张图拼一张大画布,然后随机裁剪一个和原图一样大的区域。这样每个 batch 里的图像内容多样性瞬间翻好几倍,细胞跨图粘连的情况也能让模型提前适应。但注意,拼接时如果原图尺寸不一致,resize 之后标注框的坐标也要同步缩放,否则模型学到的框位置是错的。所以业界实现里通常是先把标注框映射到拼接画布坐标系,再做随机裁剪,这个映射逻辑比图像拼接本身更容易写错,建议做完后渲染几张增强后的图人工检查下。
3.3 预处理和增强的验证闭环:先渲染再进训练
无论预处理脚本写得多么严谨,都建议先进一批可视化渲染再开训练。把预处理前、预处理后的图并排输出,标出检测框,确认三件事:框和细胞对得上、对比度没有被拉爆、细胞边缘没有被预处理抹掉。这一步看起来费时间,实际是后悔药——我踩过最狠的坑是预处理脚本里归一化参数配错,所有图像亮度被压成纯黑色,模型训练 loss 降到 0.01,但推理输出全是一个类别,最后检查数据才发现图像早就废了。从那以后我强制自己在任何预处理管道改动后,先输出 20 张渲染图,用眼睛过一遍再进训练流程。
4. 训练细胞检测模型:从数据集目录到 YOLOv8 实测
数据准备到位以后,训练环节反而简单了。我在这节用 YOLOv8 做一个完整的细胞检测流程,因为这套工具链对显微图像场景足够友好:默认的 anchor-free 设计对密集小目标效果好、训练生态完善、调参成本低。整个流程分四步:整理数据集目录、写数据配置、启动训练、评估输出。
4.1 数据集目录组织与数据配置
ultralytics 框架要求数据集的目录结构是「图片和标签分开放」,而且标注文件名必须和图像文件名一一对应。推荐的结构是 images 和 labels 两个主目录下各放 train、val、test 三个子目录,注意不是「一个子目录下同时放 images 和 labels」,这个细节写错会导致加载时找不到配对文件。目录树长这样:
datasets/cell_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamltrain、val、test 的划分比例我一般用 7:2:1。划分的时候要特别注意:同一个病人或者同一批培养皿的图片只能进一个集合,否则验证集会泄漏训练信息,mAP 虚高得离谱。显微图像的切片之间高度相关,如果随机划分,很可能同一视野的相邻切片一张在训练集、一张在验证集,模型等于「开卷考试」。正确做法是按样本来源分组划分——先把来源 ID 作为一个整体,在这个粒度上做随机切分,保证训练集和验证集没有任何同源图片。
data.yaml 是连接数据集和训练脚本的桥梁,内容如下:
path: datasets/cell_dataset train: images/train val: images/val test: images/test nc: 4 names: ['normal_cell', 'diseased_cell', 'lymphocyte', 'granulocyte']这里的path是数据集根目录,train和val是相对path的路径,nc必须和 names 里的类别数一致,names 的顺序更是要命——模型输出的类别 id 直接对应这个列表的顺序,训练时候选的顺序和推理时代码里的顺序不一致,结果大概率是「框对了但标签全错」。我自己的习惯是把 names 写在一个独立的 classes.txt 里,训练脚本和推理脚本都读同一个文件,从源头杜绝顺序不一致的问题。
4.2 训练参数设置与启动命令
YOLOv8 的训练启动命令很简单,但参数的取舍有几个关键点。显微图像的目标普遍小,imgsz建议直接设为 640 甚至 1280——如果你显存允许的话,1280 在小细胞检测上的收益非常明显。batch根据显存调,一般配合--cache参数,把数据集预加载到显存,省去每次读取磁盘的 I/O 时间。训练建议的启动命令如下:
yolo train \ model=yolov8n.pt \ data=datasets/cell_dataset/data.yaml \ imgsz=640 \ batch=16 \ epochs=200 \ cache=True \ patience=50 \ project=cell_detection \ name=cell_yolov8n参数里的model=yolov8n.pt表示从 COCO 预训练权重开始迁移学习,对高倍显微图像来说,从自然图像迁移的起点虽然不完美,但比从零训练快得多;如果数据集比较小(少于 2000 张),建议用yolov8n(nano 版本)而不是yolov8s或更大模型,大模型在小数据集上更容易过拟合。patience=50是早停阈值,连续 50 个 epoch 验证集指标不涨就停。细胞检测任务的验证集 loss 曲线通常不会像自然图像那样平滑下降,经常出现震荡,所以 patience 不要设太小,30 到 50 之间比较合适。显存有限的时候,去掉--cache,把batch降到 8,显存占用大概能控制在 6GB 左右。
4.3 训练输出解读与 badcase 分析
训练结束后,project/cell_yolov8n/目录下会生成 weights 文件夹,里面是 best.pt 和 last.pt,以及一堆图表。我一般最先看 confusion_matrix.png 和 results.png 这两张图。results.png 里关键看两个曲线:train/box_loss 和 val/box_loss,如果两个 loss 收敛到差不多的水平、没有明显 gap,说明没有严重过拟合;如果 val loss 在某个 epoch 后直线上升而 train loss 继续降,就是过拟合信号,需要加强增强、加 dropout 或者换更小的模型。
confusion_matrix 能直观暴露类别混淆问题。细胞图像里最典型的是两类形态相似的细胞互相混淆——比如正常的粒细胞和病变的粒细胞,形态差异极小,混淆矩阵里这两个类别的格子数值会很高。如果出现这种问题,优先考虑是不是染色方式导致二者色调过于接近,可以尝试把训练图转成灰度做对比实验,有时候丢掉了颜色信息,模型反而被迫去学形态特征。
推理验证的时候用下面这段脚本:
from ultralytics import YOLO model = YOLO('cell_detection/cell_yolov8n/weights/best.pt') results = model.predict( source='datasets/cell_dataset/images/test', imgsz=640, conf=0.25, save=True, project='cell_inference', name='test_output' ) for r in results: boxes = r.boxes for i in range(len(boxes)): cls = int(boxes.cls[i]) conf = float(boxes.conf[i]) xyxy = boxes.xyxy[i].tolist() print(f'类别: {model.names[cls]}, 置信度: {conf:.3f}, 边界框: {xyxy}')这段脚本读取训练好的模型对测试集做推理,conf=0.25是置信度阈值,低于这个值的预测会被丢弃。显微图像里的细胞通常对比度低,模型输出的置信度整体会偏低,0.25 是一个相对合理的初始值,如果发现漏检太多,把这个值降到 0.1 再试;如果误检太多,往上调到 0.4。注意model.names[cls]的类别顺序和训练时的 data.yaml 是一致的,不需要手动映射。跑完以后别急着看指标,先打开保存的推理结果图,肉眼判断漏检和误检的类型,这一步的反馈比任何指标都直接。
5. 避坑指南:细胞显微图像训练最常见的五个坑
5.1 训练集和验证集同源泄漏导致 mAP 虚高
现象:验证集 mAP 高达 0.95,看起来模型已经完美了,但一到新数据上推理就垮掉,mAP 直接掉到 0.5 以下。
原因:数据集按图片随机划分而不是按样本来源划分。显微图像通常从同一张切片连续拍摄,相邻视野重叠度高,随机划分后训练集和验证集里出现了来自同一视野的图片,模型等于记住了答案。
解决:按样本来源(病人 ID、培养皿编号、切片编号)做分组划分,保证同一来源的所有图片只在训练集或验证集里出现。用 scikit-learn 的GroupShuffleSplit就能实现,划分完成后渲染几对验证集图片人工确认和训练集没有视觉上的相似性。
5.2 类别不平衡导致小类别全部漏检
现象:训练 loss 正常收敛,但检测结果里数量少的细胞类别基本检不出来,输出的全是数量占优的类别。
原因:细胞显微图像的类别分布极度偏斜,比如正常细胞占 90% 以上。模型发现只要全预测成大类,整体 loss 就很低,小类别对 loss 的贡献完全被淹没。
解决:先做类别重采样,对小类别过采样,或者用 class weights 给 loss 加权。YOLOv8 里可以通过--class_weights配合类别统计文件实现,也可以在数据增强阶段对小类别图片做更多次复制增强。我一般会先看类别数量比值,如果少的类别接近多数类的 1/10 甚至更多,直接做人工采样扩充数据往前走;如果比值高于 1/20,再引入 focal loss 变体做加权,否则权重会扭曲正常类别的特征学习。
5.3 染色批次差异造成跨域失效
现象:模型在自己的训练集上 mAP 很好,换一个实验室、换一种染色方案的数据后,精度断崖式下跌,连大类都分不清。
原因:不同批次的染色深浅、色温、背景颜色差异巨大,模型学到了「这个颜色=这个类别」的表面关联,而不是细胞形态特征。
解决:预处理阶段统一做 z-score 归一化加直方图匹配,在强化数据增强时加入 HSV 扰动(饱和度±30%,色相±10%),迫使模型忽略颜色信息、关注形态结构。如果染色差异实在太大,还可以用灰度图训练做对比实验,验证模型到底学的是不是形态特征。灰度图训练的 mAP 如果和彩色图接近,说明模型已经在读形态;如果明显下降,说明它在依赖染色颜色,需要加强归一化。
5.4 标注框偏移造成 loss 无法收敛
现象:训练时 box loss 一开始降得很快,但到某个 epoch 后开始剧烈震荡,val loss 也在同一步骤跳高,降低学习率也没用。
原因:显微图像里细胞密度高的区域,人工标注时容易把框画偏、画大。当两个相邻细胞的框重叠超过一定比例时,训练过程中 target 分配不稳定,同一个 anchor 在不同 epoch 被分给不同的 ground truth,loss 自然震荡。
解决:训练前对标注框做一次统计检查,计算所有框的宽高分布和中心点分布,找出明显异常的值(比如宽高比超过 3 的、框中心点落在另一框内部的、框小到只有几个像素的)。把这些异常标注挑出来人工复核,或者直接用后处理脚本剔除掉宽高小于 5 像素的微小框。对密集区域,还要检查 NMS 后重叠度过高的框,一般用 IoU 大于 0.7 且类别不同的框作为怀疑对象,把这些框单独导出成图,优先让标注人员修这些区域。
5.5 显存不足时的工程处理误区
现象:想在 8GB 显存上训练 1280 分辨率的细胞检测模型,直接报 CUDA out of memory,于是把 batch 降到 2,结果训练完全无法收敛。
原因:batch 降到 2 意味着每个 batch 的统计量噪声极大,BN 层基本失效,而且梯度更新方向抖动严重,模型很难学到稳定的特征。显微图像本来就目标密集,小 batch 的梯度噪声比自然图像更明显。
解决:显存不够优先降imgsz而不是降 batch,把 1280 降到 640,通常显存占用直接缩到原来的四分之一;或者启用--amp混合精度训练,能省 30% 到 40% 显存;如果还不够,用梯度累积,batch=16 的等效效果用 batch=4 跑 4 步再更新梯度实现。最常见且稳定的做法是:imgsz 保持 640 不动,开 AMP,batch 控制在 8 到 16 之间,实测大多数 8GB 卡的场景都能扛住。
6. 进阶验证:用置信度曲线与混淆矩阵定位易混类别
模型训练完,最常见的问题不是「精度不够」,而是「不知道模型在哪类上犯的错」。分类准确率和 mAP 这些汇总指标掩盖了问题的真实分布,诊断模型缺陷需要更细粒度的观察视角。我惯用的诊断工具是两个:置信度分布曲线和按类别拆分的混淆矩阵。
置信度分布曲线的做法是,把测试集的推理结果按类别分组,画出每个类别预测框的置信度直方图。如果某个类别的置信度大量集中在 0.2 到 0.5 之间,说明模型对这个类别「没有把握」,它们在特征空间里离其他类别太近了。看直方图的分布形态比单纯看 mAP 更能提前预见线上推理时的问题——把置信度阈值从 0.25 提到 0.5,曲线形态在阈值附近陡峭的类别最容易受影响。画置信度分布图的脚本如下:
import json import numpy as np import matplotlib.pyplot as plt with open('cell_inference/predictions.json') as f: preds = json.load(f) conf_by_class = {} for pred in preds: cls = pred['class_id'] conf = pred['confidence'] conf_by_class.setdefault(cls, []).append(conf) fig, axes = plt.subplots(2, 2, figsize=(10, 8)) for idx, (cls, confs) in enumerate(conf_by_class.items()): ax = axes[idx // 2][idx % 2] ax.hist(confs, bins=30, range=(0, 1), color='steelblue') ax.set_title(f'class {idx} conf distribution') ax.set_xlabel('confidence') ax.set_ylabel('count') plt.tight_layout() plt.savefig('conf_distribution.png', dpi=150)这段脚本读入 JSON 格式的推理结果,按类别分组建置信度直方图。我一般会在训练完第一版模型后跑一次,如果发现某个类别的置信度分布是双峰的——一部分框置信度 0.8 以上、另一部分集中在 0.3 左右,基本能断定这个类别内部有两个形态子类,一个被模型充分学习了,另一个没有被学到。这种场景继续调参没用,正确的解法是把那个被忽略的子类找出来单独标注成新类别,或者在数据准备阶段补充这个子类的样本。
配合混淆矩阵一起看,能定位到具体的「难分对」组合。假如正常粒细胞和病变粒细胞的混淆程度很高,我会单独把这两类的误检图裁出来做模板匹配,观察它们在形态上到底有多大差异。做完这一步以后,我通常会根据诊断结果决定:是补数据、拆类别,还是退一步降低任务粒度只做二分类。从那以后我每次拿到新的细胞数据集,都会强制让训练流程先产出置信度分布图再谈指标,这个习惯帮我避开了至少三次「指标看着很高、上线就废」的面试级翻车。希望帮到你。
本文还有配套的精品资源,点击获取