
简介一套YOLO目标检测配套资源面向血红细胞检测任务适合计算机、电子信息工程、数学等专业学生用于课程设计、期末大作业或毕业设计也适合目标检测初学者快速上手。压缩包共包含1399个文件以709张血红细胞图像和686个XML标注文件为主体可直接用于YOLO系列模型训练与验证少量TXT和PKL文件负责类别配置与数据划分整体大小仅14.56MB下载和部署都非常轻量。目前已有453人学习下载。资源省去了手动标注的繁琐步骤结合参数化编程和清晰注释使用者可方便调整训练参数快速理解检测流程从预览看图像与同名XML标注一一对应便于直接开展实验和结果比对是完成课程项目或算法验证的实用数据包。1. 血红细胞数据集到手先别急着用 YOLO 训练很多朋友从压缩包里解压出几百张BloodImage_*.jpg和同名.txt第一反应就是直接扔进 YOLO 开训。我见过不少因此翻车的案例训练一个晚上 loss 不降验证集 mAP 几乎为零最后发现标签的类别 id 和配置文件里的names对不上或者坐标没有归一化。实际上这个数据集的质量还不错但“已标注”不代表“已就绪”中间还差一次格式校验。下面把标签校验、目录划分、训练配置、损失观察和粘连场景调参完整走一遍免得把时间浪费在 Debug 数据上。2. 标签格式校验与 YOLO 坐标换算很多人以为 YOLO 标签就是“一行五个数”随便拿个脚本都能读。但血红细胞这类目标分布密集标注文件里一旦出现比例换算错误肉眼很难发现。在训练之前我认为最值得花 20 分钟做的是三件事检查文件映射关系、校验坐标是否越界、把标签画回原图。2.1 文件映射关系与常见目录结构解压后通常看到两类文件BloodImage_00000.jpg和BloodImage_00000.txt。.txt就是 YOLO 格式标注每一行对应一个目标格式为class_id x_center y_center width height这里x_center y_center width height全部是相对图像宽高的比例值范围在 0~1 之间。class_id是一个从 0 开始的整数。训练前要把图片和标签放在同一个数据集目录下常见结构如下blood_cell/ ├── images/ │ ├── train/ │ │ └── BloodImage_00000.jpg │ └── val/ │ └── BloodImage_00100.jpg └── labels/ ├── train/ │ └── BloodImage_00000.txt └── val/ └── BloodImage_00100.txt我一般会先写一段小脚本检查是否有jpg缺少对应txt或者txt是空文件。空标签文件在 YOLO 中表示“该图无目标”但如果你把无目标和漏标混在一起训练时会引入噪声。2.2 用 Python 校验坐标是否越界接下来就是逐行解析标签文件。下面这段代码会把所有标签文件读一遍报告字段数错误、非数值内容以及越界坐标from pathlib import Path label_dir Path(labels) errors [] for txt in label_dir.glob(*.txt): for line_no, line in enumerate(txt.read_text().strip().splitlines(), 1): parts line.split() if len(parts) ! 5: errors.append((txt.stem, line_no, 字段数不是5)) continue try: cls_id, cx, cy, w, h map(float, parts) except ValueError: errors.append((txt.stem, line_no, 包含非数字)) continue # 检查中心坐标是否归一化到 [0,1] if not (0 cx 1 and 0 cy 1): errors.append((txt.stem, line_no, 中心点越界)) # 宽高超出 [0,1] 说明可能是未归一化的像素值 if w 0 or h 0 or w 1 or h 1: errors.append((txt.stem, line_no, 宽高不在0~1)) print(f发现 {len(errors)} 个问题) for e in errors[:20]: print(e)这段代码的逻辑是先把txt按行拆分再用map(float, parts)转成数值。如果某一步出现异常说明标签文件被写坏。越界判断放在最后因为只要数值能转成浮点数再判断它是否在合理范围内。实际处理中我发现不少老旧数据集把像素坐标直接写入 YOLO 标签这种文件在这里会被识别为“中心点越界”需要重新做归一化。2.3 将 YOLO 标注还原为像素框并可视化校验通过后一定要把标签画回原图看一眼。下面代码读取一张图和一个标签把归一化坐标转成左上角和右下角像素坐标并画框import cv2 from pathlib import Path img_path Path(images/BloodImage_00000.jpg) label_path Path(labels/BloodImage_00000.txt) img cv2.imread(str(img_path)) H, W img.shape[:2] for line in label_path.read_text().strip().splitlines(): cls_id, cx, cy, w, h map(float, line.split()) # 注意cx,cy 是中心点需要减半宽高得到左上角和右下角 x1 int((cx - w / 2) * W) y1 int((cy - h / 2) * H) x2 int((cx w / 2) * W) y2 int((cy h / 2) * H) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(check_00000.jpg, img)这里有一个容易踩的坑YOLO 标签里的cx, cy是目标中心点不是左上角。很多人用cx, cy直接当x1, y1画出来的框整体向右下偏移。正确做法是像上面这样用cx - w/2和cy - h/2换算。对血红细胞这种高密度小目标错一点就导致框和细胞边缘贴合不上后续训练时样本噪声会非常大。下面这张表列出我常遇到的标签问题供快速定位现象可能原因处理建议训练 loss 不降坐标未归一化用脚本统一除以原始图宽高可视化框整体偏移到右下把中心点当成了左上角改用(cx - w/2, cy - h/2)所有框类别都是 0类别映射没同步检查data.yaml的names顺序某些图片没有框标签为空或文件名不一致建立jpg与txt的映射检查3. 训练集划分与 data.yaml 配置数据校验只是第一步真正让 YOLO 跑起来还需要把数据组织成框架能识别的目录结构并正确编写配置文件。这里以 YOLOv8 为例讲清楚目录划分、类别声明和训练命令参数。3.1 目录结构与数据划分脚本YOLOv8 期望图片路径写在data.yaml的train和val字段里所以我们要先把数据集切成训练集和验证集。比较稳妥的做法是 8:2 随机划分并固定随机种子。import random from pathlib import Path import shutil random.seed(42) src_images Path(images) src_labels Path(labels) out Path(blood_cell) for split in [train, val]: (out / images / split).mkdir(parentsTrue, exist_okTrue) (out / labels / split).mkdir(parentsTrue, exist_okTrue) all_files list(src_images.glob(*.jpg)) random.shuffle(all_files) val_count int(len(all_files) * 0.2) for i, img_path in enumerate(all_files): split val if i val_count else train label_path src_labels / (img_path.stem .txt) if not label_path.exists(): print(f跳过缺少标签的 {img_path.name}) continue shutil.copy(img_path, out / images / split / img_path.name) shutil.copy(label_path, out / labels / split / label_path.name) print(划分完成)这段脚本把 20% 的图片作为验证集剩余 80% 作为训练集。采用copy而不是move是怕原始 zip 还需要保留。shutil.copy会保留原文件适合后续反复实验。注意划分之前一定要确保每个jpg都有同名txt否则 YOLO 在训练时会警告“图片无标签”并可能影响训练收敛。3.2 编写 data.yaml 并核对类别顺序在blood_cell目录下新建data.yaml内容如下path: ./blood_cell train: images/train val: images/val names: 0: red_blood_cell如果数据集里不止红细胞一类比如还有白细胞和血小板那么names必须按照标签文件里的class_id顺序列出不能只写一类。常见错误是标签里类别是 1但names只有 0 一项框架会报“class index out of range”。另外path推荐写相对路径如果和 YOLO 工作目录不同容易出现找不到图片的诡异问题。你也可以用绝对路径但这样项目换机器后又要改配置。3.3 训练命令与关键参数选择目录和配置文件准备好后就可以启动训练。以 YOLOv8n 为例yolo detect train \ datablood_cell/data.yaml \ modelyolov8n.pt \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectruns \ nameblood_rbc解释一下这里的参数modelyolov8n.ptn 代表 nano速度和显存都最友好适合先跑通流程。epochs100血红细胞检测任务目标不大100 轮通常够用早停可以提前退出。batch16根据显存调整。16GB 显存跑 640 精度时这个值比较稳如果显示 OOM先降到 8。imgsz640默认分辨率。血红细胞直径一般在几十像素640 下已经能覆盖。patience20验证集指标连续 20 轮不提升就停避免无效等待。训练过程中会看到每个 epoch 的box_loss、cls_loss、dfl_loss以及验证集的precision、recall、mAP50等指标。下一章专门讲这些数值该怎么盯。4. YOLO 损失函数曲线与训练过程观察训练过程中最常被问到的问题是“loss 降到多少才算好”。对 YOLOv8 来说光看总 loss 不够因为它由多个部分叠加而成。你需要分别关注 box、cls、dfl 三个分量才能判断模型到底卡在哪里。4.1 box_loss / cls_loss / dfl_loss 分别代表什么YOLOv8 的损失函数由三部分组成下面这张表总结它们的作用和典型下降趋势损失项全称作用常见问题box_lossBounding Box Regression Loss衡量预测框和真实框的位置误差不降说明定位没学准cls_lossClassification Loss衡量目标类别判断误差单类数据集通常很小dfl_lossDistribution Focal Loss优化边界框边缘分布让框更贴合目标过高会导致框偏大或偏小训练日志里box_loss一般从 1.x 降到 0.05 以下cls_loss对于单类检测很快降到 0.01 量级dfl_loss会缓慢下降。如果某个损失项在训练后期还在剧烈波动常见原因是学习率过大或标签噪声太高。血红细胞数据集里如果存在边缘模糊的细胞box_loss可能会有小幅波动这是正常的不要一看到波动就疯狂调参。4.2 训练日志里值得盯的三个信号在训练到中段时我会重点看三个信号验证集precision和recall是否同步上升。如果precision高而recall低说明模型倾向保守宁可漏检也不误检反之则说明模型输出大量框需要检查 NMS 阈值。mAP50是否达到 0.8 以上。红细胞目标大且数量多通常很容易到 0.85 以上如果你的数据低于 0.6先回看可视化标注结果。早停后的 best epoch 出现在哪个位置。如果 best epoch 是最后一轮说明训练可能没收敛可以把epochs加长如果早停发生在 30 轮以内说明学习率偏大或数据量太小。举个例子我曾经用这个数据集训练时发现cls_loss一直是 0原因是names里只写了一个类别而标签里实际有 0 和 1 两类模型强行把两个类合并成一个类去学训练指标看起来很漂亮但推理时会把白细胞也识别成红细胞。最后重新整理data.yaml把类别列表补全后才恢复正常。4.3 显存不足时的常用参数调整常见的 OOM 提示是CUDA out of memory。我的应对顺序是先降 batch再降 imgsz最后才换更小的模型。比如yolo detect train \ datablood_cell/data.yaml \ modelyolov8n.pt \ batch8 \ imgsz480 \ device0imgsz从 640 降到 480显存占用会大幅降低但分辨率太低可能导致小目标漏检。如果还想恢复 batch16 的统计稳定性可以手动把有效 batch 提高Ultralytics 框架内部会使用nbs参数做梯度累积但对 BatchNorm 统计量有一定影响。对血红细胞这类目标尺寸比较均匀的数据集imgsz512是一个不错的折中。5. 评估结果与错检样本的排查训练结束后runs/detect/blood_rbc/weights/下会生成best.pt和last.pt。很多人直接拿best.pt去推理却忽略了验证集上还有大量信息可以用来定位问题。这一章讲如何跑验证、看混淆矩阵以及从失败样本反推数据集问题。5.1 用 val 脚本计算 mAP 和 PR 曲线在 YOLOv8 中一条命令就能完成验证yolo detect val \ datablood_cell/data.yaml \ modelruns/detect/blood_rbc/weights/best.pt \ imgsz640 \ conf0.001 \ iou0.6这里把conf设成 0.001 是为了让验证阶段尽量保留所有预测框方便计算完整的 PR 曲线iou0.6是计算 mAP 时对预测框和真实框的匹配阈值。验证完成后在runs/detect/val下会生成PR_curve.png和confusion_matrix.png。对红细胞检测来说confusion_matrix.png中背景类background如果有较多预测落入说明模型存在大量低置信度误检此时可以调高推理时的conf阈值。5.2 红细胞粘连导致的漏检原因分析红细胞在显微镜图像中经常粘连成串默认 NMS 参数很容易把两个相邻细胞当做一个目标。这时如果只看整体 mAP可能只比正常情况低 0.02但实际视觉感受会差很多。我一般会手动挑出验证集里几组粘连明显的图片运行下面的推理并保存结果yolo detect predict \ modelruns/detect/blood_rbc/weights/best.pt \ sourcepath/to/sticky_images \ conf0.25 \ iou0.6 \ saveTrueiou0.6是 NMS 的 IoU 阈值值越小对重叠框的抑制越严格。对于粘连细胞我会把iou调低到 0.3 再试一次可以看到更多细胞被保留。注意 NMS 调低会带来重复框需要在下一步用置信度排序和宽度约束过滤。5.3 用小 Batch 复现问题与 Debug 方式如果你怀疑是数据划分导致的偶然偏差一个简单做法是把训练集缩小到 1/10 再训 50 轮看训练 loss 能不能降到接近 0。如果小数据集上 loss 都降不下去说明标签噪声或格式问题依旧存在。另一种 Debug 方式是直接读取训练日志里的results.csv画一张训练曲线import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/blood_rbc/results.csv) # 对比训练和验证的 box_loss观察是否过拟合 plt.plot(df[epoch], df[train/box_loss], labeltrain box_loss) plt.plot(df[epoch], df[val/box_loss], labelval box_loss) plt.legend() plt.savefig(loss_curve.png)这段代码把训练和验证的 box_loss 画在一起能直观看出是不是在过拟合。如果 val box_loss 在某个 epoch 后开始反弹而 train box_loss 仍在下降就说明模型开始记住训练集中的噪声此时应恢复更早的best.pt或调大patience提前停止。6. 粘连场景 NMS 调参与批量推理脚本血红细胞检测的最终目的往往是计数或统计形态这就需要在推理阶段输出每个细胞的框坐标和置信度。默认参数在粘连场景下会丢框下面给出一个可直接用的调参思路和批量导出脚本。6.1 调低 IoU 阈值保留粘连目标YOLO 推理时的iou参数控制 NMS 对重叠框的抑制强度。默认iou0.45在红细胞粘连严重的图上会把相邻的框合并成一个。我通常先试iou0.25同时把conf从 0.25 提到 0.35用置信度过滤掉 NMS 带来的重复框yolo detect predict \ modelruns/detect/blood_rbc/weights/best.pt \ sourceimages/val \ conf0.35 \ iou0.25 \ save_txtTrue调低iou后同一目标可能被输出两个框此时优先保留置信度高的那个。如果还是双框可以在后处理里计算框中心距合并距离小于 5 像素的框。这里的关键是记住iou是 NMS 阈值conf是置信度阈值两者配合使用不要只调一个。6.2 把预测结果导出成 CSV 供下游统计当数据量达到几百张图时手动翻可视化图片不现实。我一般会把结果写到 CSV后续用 Pandas 按图像统计细胞数量、平均面积等from ultralytics import YOLO import csv model YOLO(runs/detect/blood_rbc/weights/best.pt) results model(images/val, conf0.35, iou0.25) with open(rbc_boxes.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, class, x1, y1, x2, y2, conf]) for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() cls int(box.cls[0]) conf float(box.conf[0]) writer.writerow([r.path, cls, int(x1), int(y1), int(x2), int(y2), round(conf, 4)])这段脚本把验证集里每张图的预测框都写到rbc_boxes.csv。r.path是输入图片路径box.xyxy是像素坐标的左上角和右下角box.cls是类别 id。注意box.conf已经被conf参数过滤过所以 CSV 里不会再出现置信度低于 0.35 的框。统计时可以直接按 image 分组数出每张图的红细胞数量再和标签中的真实数量对比不一致的样本回到 CSV 里检查x1/y1/x2/y2的坐标范围通常能定位是 NMS 丢框还是标签漏标。本文还有配套的精品资源点击获取