
简介面向医疗AI与病理图像分析研究者的淋巴细胞目标检测数据集采用YOLO格式标注适用于训练细胞检测模型可支撑病理辅助诊断、免疫微环境评估及癌症相关研究。数据集共含1152张医学图像其中训练集580张、验证集290张、测试集282张覆盖淋巴细胞密集分布与多样形态场景图像均来自病理数据库涵盖典型分布形态。压缩包内共2000个文件以jpg病理图像和txt边界框标注为主体另附yaml配置文件与docx说明文档整体大小约67.68MB结构清晰可直接接入YOLOv5/v7/v8等主流框架训练。标注经医学专家校验定位精准适配复杂病理图像可用于细胞计数、密度分析等下游任务也可辅助医学教育与药物研发中的免疫细胞评估。目前已有60人学习适合需要高质量医疗检测数据集的算法工程师与科研人员。1. 淋巴细胞目标检测数据集显微镜图像里的找细胞问题医学图像里最磨人的任务之一是从密密麻麻的血细胞背景中把淋巴细胞一个个框出来而不是简单地看一张图。这类数据通常以 zip 压缩包下发一批血涂片或骨髓涂片的显微图像配上 VOC 风格 XML 或 COCO 风格 JSON 标注标注目标是视野内的淋巴细胞有时还带中性粒细胞、单核细胞等附加类别。它解决的核心问题很朴素——让目标检测模型在显微场景下学会定位和分类单个细胞。适合两类人想用真实标注数据跑通第一个检测模型的新手以及做细胞学辅助分析预研的从业者。接下来我把解包→格式转换→训练→验证→排错的完整路径过一遍命令、参数和坑都按一线调试经验来写。2. 拿到淋巴细胞标注先做三件事格式确认、类别核对、划分策略2.1 数据集结构与标注格式先认清 VOC 还是 COCO解压后常见两种布局一种是images/加annotations/标注是 Pascal VOC 风格的 XML另一种是images/加单个 JSON 文件属于 COCO 风格。淋巴细胞数据集的 XML 里name一般是lymphocyte有的版本会带上neutrophil、monocyte、eosinophilCOCO JSON 的categories字段同理。很多人第一个坑就是没做格式确认就套用别人的训练脚本——数据明明是 COCO却按 VOC 路径去读结果类别对不上、坐标全乱。我在动手前一定会先跑一段统计脚本把类别分布、目标面积分布打出来。这一步决定了后面按单类训练还是多类训练也决定imgsz的初始选择。实际数据里淋巴细胞标注框常常只有 20×20 到 80×80 像素属于典型小目标如果某张整片扫描图是 4000×3000细胞框普遍小于 40 像素训练时直接缩到 640 会把细胞缩成糊点这时要提前定好是否切 patch的策略。别急着进入训练先让统计数字说话。import xml.etree.ElementTree as ET import os from collections import Counter ann_dir annotations # 解压后存放 XML 的目录 cat_counter Counter() area_list [] for xml_file in os.listdir(ann_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(ann_dir, xml_file)) for obj in tree.getroot().iter(object): name obj.findtext(name).strip() cat_counter[name] 1 box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) area_list.append((xmax - xmin) * (ymax - ymin)) # 计算框面积像素为单位 total sum(cat_counter.values()) print(类别分布, dict(cat_counter)) print(目标总数, total) print(平均框面积%.1f 像素² % (sum(area_list) / len(area_list)))这段脚本只做一件事摸清标注的家底。cat_counter统计每个类别有多少个目标area_list记录每个框的像素面积。输出里如果某一类占比超过 90%训练时就要考虑类别权重如果平均框面积只有几百像素说明该数据集中小目标占主导后面调imgsz和mosaic时要格外小心。2.2 把 XML 标注转成 YOLO 训练格式脚本与四个边界坑YOLO 训练自己的数据集需要 txt 标注每行格式是class_id x_center y_center width height坐标相对于整张图的宽高做归一化。常见做法是写个一次性转换脚本把 VOC XML 批量转成 YOLO txt。转换本身不难难在边界处理坐标越界、框退化成点、类别顺序错位、出现标注里没见过的类别名。这四个问题任何一个没处理训练时会以奇怪的方式翻车——比如 loss 一直不降或者验证时框全偏到图像角落。import xml.etree.ElementTree as ET CLASSES [lymphocyte, neutrophil, monocyte, eosinophil] # 这个顺序必须与后续 data.yaml 的 names 完全一致否则训练类别错位 def convert_voc_to_yolo(xml_path, img_w, img_h): tree ET.parse(xml_path) lines [] for obj in tree.getroot().iter(object): name obj.findtext(name).strip() if name not in CLASSES: continue # 不在映射表里的类别直接跳过 cls_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 越界坐标先裁剪回图像内部避免训练时出现负数宽高 xmin max(0, min(xmin, img_w)) ymin max(0, min(ymin, img_h)) xmax max(0, min(xmax, img_w)) ymax max(0, min(ymax, img_h)) if xmax - xmin 1 or ymax - ymin 1: continue # 退化成一两个像素的框没有学习价值 x_c ((xmin xmax) / 2) / img_w y_c ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_c:.6f} {y_c:.6f} {w:.6f} {h:.6f}) return lines调用时遍历images目录用 PIL 或 OpenCV 读每张图的宽高作为img_w、img_h然后同名写入labels目录。这里有几个细节CLASSES顺序一旦定下来就不要改。训练时data.yaml里的names按同样的顺序排列如果中途加类别旧标注必须重新生成。坐标越界是标注软件常留的坑不裁剪会导致 YOLO 输出负的宽高loss 直接变 NaN。面积小于 1 像素的框多半是标注时误操作留下的噪点过滤掉比保留更安全。如果 XML 里出现CLASSES之外的类别宁可先跳过也不要自动改名塞进现有类别否则类别语义会混乱。2.3 按玻片或病人划分数据集别让评估数字骗你划分 train/val 最常见的错误做法是random.sample直接按文件打散。显微镜图像的特殊性在于相邻视野来自同一张玻片内容高度重叠随机切分会让验证集里混入训练集的近亲mAP 虚高一到新样本上就现原形。正确做法是先按 slide_id 分组再把组切分保证验证集里出现的玻片在训练时一次也没见过。import os import random from collections import defaultdict src_dir images # 原始图像目录 test_ratio 0.2 random.seed(42) # 固定随机种子保证划分可复现 groups defaultdict(list) for f in os.listdir(src_dir): if not f.endswith(.jpg): continue parts f.split(_) slide_id parts[0] _ parts[1] # 文件名形如 slide_001_region_12.jpg groups[slide_id].append(f) slides list(groups.keys()) random.shuffle(slides) val_slides set(slides[: int(len(slides) * test_ratio)]) train_files, val_files [], [] for slide, files in groups.items(): for f in files: if slide in val_slides: val_files.append(f) else: train_files.append(f) print(f训练图像 {len(train_files)} 张验证图像 {len(val_files)} 张) print(验证玻片, sorted(val_slides))这段脚本按文件名前缀提取玻片编号把同一玻片的所有视野放进同一个分组里。划分后验证集和训练集在玻片层面完全隔离评估数字才接近真实场景。少数情况下某个玻片的图像量特别大简单按玻片数切分会导致训练/验证比例失衡这时改成按图像数加权切分保证 val 图像量稳定在 15%20%。这套思路对所有显微镜类数据都适用不管是细胞、组织切片还是鸟类目标检测数据集只要图像之间有物理来源关联就不能纯随机切。3. 用 YOLOv8 把淋巴细胞检测跑通最小命令与三个必调参数3.1 环境配置与目录整理Ultralytics 从零起步环境配置不用折腾太多Ultralytics 的开源库把训练、验证、推理封装成了统一的命令行入口。我一般直接建一个干净环境来装避免和本来的 PyTorch 版本打架conda create -n cell_detect python3.10 -y conda activate cell_detect pip install ultralytics装完后用yolo命令验证一下是否可用。数据目录建议按下面的结构整理Ultralytics 对这套目录约定有原生支持不需要额外写 Dataset 类datasets/cell_detect/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml是训练入口内容很简单path: /absolute/path/to/datasets/cell_detect train: images/train val: images/val names: 0: lymphocyte 1: neutrophil 2: monocyte需要提醒的是path要写绝对路径相对路径在不同机器上容易出错names的索引和上一节CLASSES的顺序严格对应。目录整理完先看一眼labels/train里有没有和images/train同名的 txt 文件数量对不上就回头查转换脚本别带着残缺标注进训练。3.2 训练、验证、推理一条龙命令与参数含义训练命令一行就能跑起来但参数不能全按默认。以下是我在淋巴细胞这类小目标场景下常用的最小启动命令yolo detect train \ modelyolov8n.pt \ datadatasets/cell_detect/data.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ patience20 yolo detect val \ modelruns/detect/train/weights/best.pt \ datadatasets/cell_detect/data.yaml \ imgsz640 yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcedatasets/cell_detect/images/val \ conf0.25三个必调参数值得单独说。第一个是imgsz。细胞框普遍小imgsz640在多数数据上只是起步。先训练一版看结果如果小细胞漏检多把imgsz提到 960 甚至 1280。代价是显存和训练时间上升但对细胞检测来说输入分辨率往往是比模型大小更敏感的因素。第二个是patience。早停的耐心值默认可能偏长我习惯设 20让它在验证指标连续 20 轮不涨时自动停。训练初期 loss 波动大千万别把 patience 设成 5否则模型还没收敛就停了。第三个是device。有 GPU 就写device0没有就devicecpu。CPU 训练时把batch降到 48imgsz降到 640先跑通流程再谈精度。不同模型规模的选型我一般按这个思路来具体指标取决于显卡和训练时间预算模型适合场景选择理由yolov8n快速试跑、CPU推理参数量最小先验证数据流程是否通畅yolov8s科研基线精度和速度均衡多数淋巴细胞数据从这档起步yolov8m/l对漏检敏感的医学场景细胞小且密集更大的模型容量对边界模糊的细胞更友好训练结束后runs/detect/train/下会生成weights/best.pt和weights/last.pt。验证命令会打印 mAP50、mAP50-95 等指标推理命令默认把带框结果存到runs/detect/predict/。先把整个流程跑通再回头调参这是最稳的顺序。3.3 训练结果怎么读数混淆矩阵、PR 曲线与特征热力图训练完别只看一个 mAP。runs/detect/train/里有confusion_matrix.png、PR_curve.png、results.png这几张图比终端里那行数字信息量大得多。confusion_matrix.png要重点看淋巴细胞被误分类到背景的比例如果假阴性很高说明模型没学到充分特征如果和中性粒细胞互相混淆说明这两类在标注里的形态边界本身就模糊得回头检查标注图像。results.png里有 box_loss、cls_loss、dfl_loss 三条曲线训练后期 loss 反复震荡时先调低学习率而不是加 epochs。另外一个值得尝试的方向是目标检测特征图和热力图的可视化。常见做法是用 Grad-CAM 思路把网络关注区域叠回原图看模型到底是根据细胞核轮廓判断还是根据染色伪影判断。淋巴细胞数据最怕的就是模型学会看颜色而不是看形态——不同染色批次下颜色一变模型立刻失效。热力图能直观地暴露这个问题如果高亮区域集中在细胞边缘的染色环上而不是细胞核内就要考虑增强数据多样性或做颜色归一化。4. 淋巴细胞数据集避坑五条血泪经验4.1 小目标漏检指标不错实际图片里漏掉一串小细胞现象mAP50 有 0.85但打开推理结果一看图像边缘和细胞密集区域漏检严重漏掉的都是那些只有二三十像素的小细胞。原因输入分辨率不够加上 mosaic 增强把图像切碎后小细胞被进一步缩小模型根本没有足够的像素去学细胞轮廓。解决先提升imgsz到 960 或 1280再看漏检是否缓解同时把mosaic关闭或调到 0.5避免小目标在拼接过程中被裁掉一半。另一个管用的做法是切 patch——把大图切成 512×512 的 tile 训练推理时同样切块再合并结果。4.2 类别不均衡淋巴细胞占比过高模型学成单类检测器现象验证集上淋巴细胞检测得很好中性粒细胞几乎全漏混淆矩阵里中性粒细胞那一行几乎全是背景。原因数据集中淋巴细胞目标数量远超其他类别模型在训练中天然偏向多数类或者标注里其他类别的框质量本身就不高模型学到的是别管它。解决先做类别统计如果比例超过 10:1优先考虑对少数类做过采样或复制增强其次在训练配置里给少数类更高的cls权重具体数值用验证集上的 F1 来调。多类细胞检测不要一上来就追求类别数量先把淋巴细胞这一主类做扎实再逐步加类。4.3 染色差异导致误检换一批染色图像直接崩现象训练集来自同一台显微镜同一批染色模型在训练集上表现很好换一个医院的图像误检率和漏检率同时飙升。原因模型把染色的颜色分布当成了判别特征而不是细胞本身的形态特征。这是医学图像里最常见的域偏移问题。解决在数据准备阶段加入染色归一化预处理把不同批次的图像颜色分布对齐训练时用较大的hsv_h、hsv_s增强让模型对颜色变化不那么敏感。更彻底的做法是收集多个来源的数据让训练集本身覆盖染色差异。4.4 标注框质量参差框不贴边模型输出也跟着飘现象训练 loss 正常收敛但预测框总比细胞大一圈或者斜着包住了旁边另一个细胞。原因原始标注里一部分框标得松把细胞周围的少量背景也包了进去另一部分框则只框住了细胞核没框住整个细胞。混杂的标注尺度让模型学了一个平均框。解决训练前用脚本统计标注框宽高比和面积分布把明显偏离中位数的框筛出来人工复核训练后用预测结果的 IoU 分布来辅助找问题——如果大量预测框的置信度高但 IoU 只有 0.5多半是标注框本身就不准。标注清洗是这类数据集里最花时间但回报最高的一步。4.5 划分泄漏导致指标虚高验证集里混进了训练集的近亲现象本地验证 mAP 0.9一到新样本上掉到 0.6怀疑模型没学好但找不到原因。原因数据划分是纯随机的同一玻片的相邻视野一部分进了训练集一部分进了验证集。重叠内容被模型记住了验证集自然好看。解决回到第 2.3 节的分组划分方案按玻片或病人做 group split。评估医学检测模型时分组划分不是可选优化而是必须项否则你只是在验证模型的记忆力而不是泛化能力。5. 进阶验证自建负样本与难例挖掘把模型逼到更稳基础训练跑通后最常见的困境是指标够好但不敢用。我建议把精力放在两个方向上负样本和难例挖掘。负样本指的是那些不包含淋巴细胞、但长得像细胞的区域。淋巴细胞的检测本质上是在像细胞的东西里做筛选如果训练集里全是带标注的正样本模型就缺少这不是淋巴细胞的反馈。做法很简单从无标注的整片扫描图里随机裁背景块或者把训练集里被预测成假阳性的区域收集起来存成没有标注的负样本目录和原数据集一起训练。Ultralytics 支持在train目录下放置没有对应 txt 的图片这些图会被当作纯背景参与训练模型会在这些区域学会压低置信度。难例挖掘适合标注量不足的场景。常见做法是用开放词汇目标检测模型先跑一遍所有训练图像把置信度在 0.30.6 之间的可疑细胞自动粗标出来再人工复核修正。这一步相当于用开放词汇模型当助手把人工标注的精力集中在最难判断的样本上。我习惯把每轮训练后 val 集预测结果里置信度最高的假阳性图存档每隔 50 轮翻一次看模型犯的错误是不是在收敛——这个习惯比单看 loss 曲线可靠得多。验证方法上推荐按玻片分组的 K-Fold 交叉验证。用第 2.3 节的划分逻辑把数据切成 5 折训练 5 个模型统计 mAP50、mAP50-95 和 F1 的均值和方差。方差大说明模型对特定玻片敏感需要补数据均值低说明基础特征没学好回去调imgsz或模型容量。细胞计数类任务如果要做浓度估计单张 2D 检测只是第一步后续往三维目标检测和体视学方向走才能解决细胞重叠和厚度问题但那是另一个项目的起点了。我自己最深的教训是拿到新数据集先花一天做统计、划分和标注清洗而不是直接开训练。这个前置时间省下来的调试时间通常是三倍以上。希望帮到你。本文还有配套的精品资源点击获取