简介一份用于淋巴细胞目标检测的医学图像数据集包含训练集580张、验证集290张、测试集282张共1152张病理HE染色图像标注由医学专家校验采用YOLO格式的边界框与类别标签。资源面向医疗AI开发者、病理辅助诊断系统研究者及医学教育场景可训练YOLOv5/v7/v8等算法支持细胞计数、密度分析等下游任务。包内含2000个文件以jpg图像和txt标注文件为主另有yaml配置和docx说明文档压缩后大小67.68MB便于直接用于模型训练与推理。针对淋巴细胞簇等密集分布场景进行了专门适配有助于提升模型在真实病理图像中的泛化能力可直接用于癌症病理分析、免疫状态评估等临床前研究。目前已有60人学习下载适合需要快速获取规范标注数据开展医学目标检测实验的读者。1. 拿到淋巴细胞目标检测数据集.zip先别急着训练拿到一个带“数据集”三个字的 zip并不等于你手里已经有了一份能直接喂给训练脚本的输入——这是我在医学图像方向碰过几回壁之后最真实的体会。“淋巴细胞目标检测数据集.zip”这类压缩包常见于病理切片或细胞涂片场景要检测的淋巴细胞直径只有几微米到十几微米落到图像里往往只是几十个像素的小目标还经常和炎症细胞、坏死碎片挤在一起背景很脏。这个 zip 解决的问题很直接把一批已标注的淋巴细胞图像集中交付省掉你从零找数据、手工标注的漫长周期。它适合正在做细胞检测、肿瘤微环境分析以及一切想用 YOLO 系模型在显微图像上做目标检测的从业者。但别急着把 zip 拖进训练代码先按下面的流程把这份家底盘明白。2. 解压与盘库先确认数据格式再谈训练淋巴细胞模型2.1 解压这一步就藏着坑中文路径、伪加密和损坏包在 Linux 服务器上拿到 zip最常见的处理方式自然是 unzip。但直接 unzip 在遇到中文文件名时大概率会乱码因为 Windows 下压缩时默认用 GBK 编码文件名而 Linux 的 unzip 默认按 UTF-8 去解。表现是解压出来一堆乱码目录名后续 Python 的 os.listdir 能读到但路径一旦拼接或打印就出问题。我一般分三步走。第一步先用 unzip -l 看压缩包列表确认顶层目录名和文件数量而不是盲目解压unzip -l 淋巴细胞目标检测数据集.zip | head -50 unzip -l 淋巴细胞目标检测数据集.zip | tail -5head 看开头目录结构tail 看末尾统计的总文件数。如果发现文件名乱码用 -O 参数指定原始编码unzip -O CP936 淋巴细胞目标检测数据集.zip -d ./lympho_data-CP936 是让 unzip 用 GBK 解码文件名再转成 UTF-8 落地。如果没有 -O 选项部分精简版 unzip 不支持改用 7z 也能解7z 对中文编码的容错更好7z x 淋巴细胞目标检测数据集.zip -o./lympho_data提示7z 解压后建议用 ls -la 检查目录权限。如果数据从 U 盘或 Windows 分区拷过来常见问题是脚本只有读权限后面 YOLO 训练写缓存文件时会报 Permission denied。还有一个容易被忽略的点是 zip“伪加密”。有的 zip 文件设置了伪加密标志位unzip 会要求输入密码但实际数据并没有加密7z 或者 Python 的 zipfile 模块仍然可以正常读取。这不是让你去破解密码密码保护的数据务必走正规授权途径确认密码伪加密只是在排查“解压不了”时值得知道的现象。解压之前还要做完整性检查这一步很多人跳过跳过的代价是训练到一半报“图片损坏”或者某个 batch 读不出来。unzip -t 能快速校验每个文件的 CRCunzip -t 淋巴细胞目标检测数据集.zip如果输出里有 bad CRC 或者 mismatch 字样说明这个包在传输过程中损坏过优先找交付方重新打包。别信“就一张图坏了删掉继续训练”这种侥幸医学数据一张坏图背后往往意味着整个包的不完整交付。2.2 目录结构阅读三类典型布局与标注格式的大致判断解压完成之后先摸清目录长什么样。常见的目标检测数据集打包方式大概有三种images/ annotations/图像是一批标注是另一批。annotations 里可能是 XML、JSON 或 txt。JPEGImages/ Annotations/ ImageSets/VOC 时代留下的经典布局ImageSets/Main 里会有 train.txt、val.txt。images/ labels/ classes.txt已经处理好的 YOLO 格式labels 里每个 txt 对应一张同名的图像。不管原始数据来自哪一款目标检测常用标注工具导出格式无非下面几种。判断标注格式主要看文件扩展名再打开首行内容确认标注格式典型文件位置坐标形式读取方式VOC XMLannotations/*.xmlxmin/ymin/xmax/ymax绝对像素xml.etree.ElementTreeCOCO JSONannotations/instances_*.jsonbbox 为 x,y,w,h绝对像素json pycocotoolsYOLO TXTlabels/*.txtclass, x_center, y_center, w, h归一化到 0-1纯文本LabelMe JSON每张图一个 .jsonpolygon 点集json做检测需先转矩形框VOC XML 的根节点是 annotationobject 里有 bndbox坐标是绝对的像素值COCO 则是一个大 JSONimages 和 annotations 通过 image_id 关联YOLO txt 每行五个数字第一个是类别 ID后四个是归一化坐标。LabelMe 的 JSON 在分割任务里常见但如果这个 zip 的目标检测数据集是用它标注的那就意味着你要先从多边形里算出外接矩形多一层转换工作。2.3 先写脚本盘库存图像数、标注数、类别平衡不做任何格式转换之前先写一个小的 Python 脚本把所有关键数字统计出来。这一步能帮你判断这个数据集是否值得投入时间清洗也能在后续训练时作为 baseline 参照。import os import xml.etree.ElementTree as ET from collections import Counter DATA_ROOT ./lympho_data IMG_DIR os.path.join(DATA_ROOT, images) ANN_DIR os.path.join(DATA_ROOT, annotations) img_exts (.jpg, .jpeg, .png, .bmp, .tif, .tiff) imgs [f for f in os.listdir(IMG_DIR) if f.lower().endswith(img_exts)] anns [f for f in os.listdir(ANN_DIR) if f.lower().endswith(.xml)] class_counter Counter() no_ann [] for img in imgs: xml_name os.path.splitext(img)[0] .xml xml_path os.path.join(ANN_DIR, xml_name) if not os.path.exists(xml_path): no_ann.append(img) continue tree ET.parse(xml_path) for obj in tree.findall(object): cls obj.findtext(name, unknown).strip() class_counter[cls] 1 print(f图像总数: {len(imgs)}) print(fXML 标注总数: {len(anns)}) print(f缺失标注的图像: {len(no_ann)} {no_ann[:5]}) print(f类别分布: {dict(class_counter)})逻辑说明这里以“同名 XML 是否存在”作为标注完整性的判断依据并统计每个类别的目标数量。import 里用了 ElementTree 解析 VOC XML如果你的包是 COCO JSON 或 YOLO txt把解析段替换成 json.load 或直接读文本即可。参数说明IMG_DIR 和 ANN_DIR 要跟你的实际目录一致img_exts 覆盖了医学图像常见的 .tif/.tiff很多病理切片导出图是 TIFF 格式这一点和自然图像数据集不太一样。class_counter 帮你一眼看出类别是否平衡如果某类目标只有几十个后面训练就要考虑给这类加权重或做复制粘贴增强。这一轮盘库之后你手里应该有三张表图像总数、标注总数、类别分布以及一份缺标注的坏图名单。接下来才轮到格式转换。3. 把 VOC 标注转成 YOLO txt转换脚本与四个边界坑3.1 为什么要统一成 YOLO txt不论原始包是 VOC XML 还是 COCO JSON我最终都会统一转成 YOLO txt 再喂训练。原因有三第一YOLO 系列的训练脚本默认读 txt目录只要 images 和 labels 两套配一个 data.yaml 就能跑省掉大量胶水代码第二txt 是归一化坐标和图像分辨率解耦训练时无论缩放到 640 还是 1024标注都不需要跟着换算第三txt 的排查成本最低任何文本编辑器都能打开看格式错一眼能发现。3.2 VOC XML 转 YOLO txt完整脚本这是一个我在多个细胞检测数据上验证过的转换脚本直接复制改路径就能用。import os import xml.etree.ElementTree as ET ANN_DIR ./lympho_data/annotations # VOC XML 所在目录 LABEL_DIR ./lympho_data/labels # 输出的 YOLO txt 目录 os.makedirs(LABEL_DIR, exist_okTrue) # 类别映射表必须和后面 data.yaml 里的 names 顺序完全一致 CLASS_MAP { lymphocyte: 0, plasma_cell: 1, neutrophil: 2, } def convert_xml(xml_path, label_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.findtext(width)) img_h int(size.findtext(height)) lines [] for obj in root.findall(object): name obj.findtext(name, ).strip() if name not in CLASS_MAP: continue # VOC 里 difficult 和 truncated 的目标医学数据里一般忽略掉 if obj.findtext(difficult, 0) 1: continue bndbox obj.find(bndbox) xmin float(bndbox.findtext(xmin)) ymin float(bndbox.findtext(ymin)) xmax float(bndbox.findtext(xmax)) ymax float(bndbox.findtext(ymax)) # 过滤掉宽高小于 1 像素的退化框通常是标注误操作产生的 if xmax - xmin 1 or ymax - ymin 1: continue # YOLO 格式归一化的中心点坐标 归一化宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 浮点坐标裁到 [0, 1] 区间防止标注越界引发的训练报错 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) cls_id CLASS_MAP[name] lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) if lines: with open(label_path, w, encodingutf-8) as f: f.write(\n.join(lines)) for xml_name in os.listdir(ANN_DIR): if not xml_name.lower().endswith(.xml): continue stem os.path.splitext(xml_name)[0] convert_xml( os.path.join(ANN_DIR, xml_name), os.path.join(LABEL_DIR, stem .txt), )逻辑说明脚本的核心是把绝对像素坐标换算成归一化坐标——中心点就是 (xminxmax)/2 和 (yminymax)/2 各自除以宽高宽高则是 (xmax-xmin) 和 (ymax-ymin) 各自除以宽高。每个目标输出 5 个数字类别 ID、中心 x、中心 y、宽、高。文本里的换行符用 \n不要用 Windows 的 \r\nLinux 和容器里读起来更干净。参数说明这里有三个边界坑每一个我都踩过值得单独说。第一CLASS_MAP 的顺序决定了训练时的类别 ID后续 data.yaml 里的 names 必须和它一一对应。顺序错了模型不会报错但预测结果张冠李戴验证集上看起来 mAP 不错实际部署全错。第二difficult 标记在 VOC 里表示“难以辨认的目标”医学数据里经常出现在边缘不清的淋巴细胞上常规检测训练建议直接跳过否则模型会被不确定的标注干扰训练损失来回震荡。第三clip 操作只管住越界到 1.0 的浮点误差如果原标注本身框就在图像外这种数据要回到标注工具里修不能靠 clip 糊弄过去因为框的中心已经偏移了。3.3 转换后的校验反投影画框眼见为实转换完不要直接开训练先把 txt 反投影回图像上画框用眼睛确认一遍。我吃过这个亏有一版脚本把 ymin 和 ymax 的除法写反了训练了两天 loss 一直在降最后可视化才发现框全部上下颠倒。坐标转换的 bug 不会让训练崩掉只是让模型学一个错误的映射。import cv2 IMG_DIR ./lympho_data/images LABEL_DIR ./lympho_data/labels VIS_DIR ./check_vis os.makedirs(VIS_DIR, exist_okTrue) for txt_name in os.listdir(LABEL_DIR): stem os.path.splitext(txt_name)[0] img_path os.path.join(IMG_DIR, stem .jpg) img cv2.imread(img_path) if img is None: print(f[warn] 读不到图像: {img_path}) continue h, w img.shape[:2] with open(os.path.join(LABEL_DIR, txt_name), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id, xc, yc, bw, bh map(float, parts) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, str(int(cls_id)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) out_path os.path.join(VIS_DIR, stem _check.jpg) cv2.imwrite(out_path, img)逻辑说明从 txt 读归一化坐标反向乘回图像宽高换算成像素框的左上角和右下角然后用 OpenCV 画红色矩形和类别 ID输出到 check_vis 目录。参数说明cls_id 显示为 int 类型方便你对类别名。每张图抽查右下角是否有越界框——如果矩形画到了图像边缘外部说明原始标注就有问题这类图单独记录下来不要混进训练集。挑 5 到 10 张肉眼检查重点看细胞密集区域框是否贴合细胞膜边缘、是否出现一个细胞被两个框套住、整张图的框是否整体偏移。这一步是把数据质量问题暴露在训练之前的最后机会也是整个目标检测数据集处理流程里最值得花时间的步骤。4. 划分数据集与 YOLOv8 训练参数怎么设才不翻车4.1 划分数据按图像源分组避免同源泄露数据划分最忌讳的是随手 random.shuffle。淋巴细胞图像经常来自同一个病人的多张切片或者同一张切片的不同视野截图如果这些高度相似的图一部分进了训练集一部分进了验证集验证指标会虚高到离谱换到新病人数据上直接崩掉。正确做法是先看文件名前缀或者目录名是否包含病例 ID有的话按病例 ID 分组再把组划分成训练、验证、测试。import os import random import shutil random.seed(42) IMG_DIR ./lympho_data/images LABEL_DIR ./lympho_data/labels SPLIT_DIR ./lympho_data/split TRAIN_RATIO, VAL_RATIO 0.8, 0.1 os.makedirs(f{SPLIT_DIR}/images/train, exist_okTrue) os.makedirs(f{SPLIT_DIR}/images/val, exist_okTrue) os.makedirs(f{SPLIT_DIR}/images/test, exist_okTrue) os.makedirs(f{SPLIT_DIR}/labels/train, exist_okTrue) os.makedirs(f{SPLIT_DIR}/labels/val, exist_okTrue) os.makedirs(f{SPLIT_DIR}/labels/test, exist_okTrue) # 按文件名下划线前缀分组假设前缀是病例 ID如 case03_slide01_001.jpg groups {} for img_name in os.listdir(IMG_DIR): stem os.path.splitext(img_name)[0] group_key stem.split(_)[0] groups.setdefault(group_key, []).append(stem) group_keys list(groups.keys()) random.shuffle(group_keys) n_train int(len(group_keys) * TRAIN_RATIO) n_val int(len(group_keys) * VAL_RATIO) for i, key in enumerate(group_keys): split train if i n_train else val if i n_train n_val else test for stem in groups[key]: src_img os.path.join(IMG_DIR, stem .jpg) src_lbl os.path.join(LABEL_DIR, stem .txt) if os.path.exists(src_img): shutil.copy(src_img, f{SPLIT_DIR}/images/{split}/{stem}.jpg) if os.path.exists(src_lbl): shutil.copy(src_lbl, f{SPLIT_DIR}/labels/{split}/{stem}.txt) print(fgroup 总数: {len(group_keys)}) print(ftrain groups: {n_train}, val groups: {n_val}, test groups: {len(group_keys) - n_train - n_val})逻辑说明先按文件名前缀聚合病例再在“病例组”级别随机打乱和划分而不是在单张图像级别打乱。这样同一病例的图像绝不会同时出现在训练和验证里对医学检测任务这是评估可信度的底线。参数说明TRAIN_RATIO 取 0.8、VAL_RATIO 取 0.1剩下 0.1 做测试。样本量小的时候可以改成 0.7/0.15/0.15但测试集无论如何要留一部分全程不参与训练过程只在最后评估用一次。seed 固定为 42是为了复现实验时划分结果完全一致。如果你的数据集文件名是纯数字编号看不出分组关系那就需要回到原始采集记录里找切片编号这个功夫不能省。4.2 data.yaml 与训练超参数先从 yolov8n 跑 baselineYOLOv8 训练自己的数据集写对 data.yaml 是所有后续动作的入口path: /data/lympho_data/split train: images/train val: images/val test: images/test names: 0: lymphocyte 1: plasma_cell 2: neutrophil参数说明path 必须是绝对路径YOLO 会把 train/val 里的相对路径拼到它后面。names 的索引顺序必须和转换脚本的 CLASS_MAP 一致这是整条链路里最容易出错的地方我在第 3 章和第 5 章都会提到。test 这一项在训练时不参与val 才是验证集。训练我一般从轻量模型开始跑命令如下yolo detect train \ modelyolov8n.pt \ data/data/lympho_data/split/data.yaml \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20逻辑说明yolov8n.pt 是预训练权重加载它能显著加速收敛在标注数量不富裕的医学数据上尤其重要。模型不会从头学起而是在 ImageNet 和 COCO 的通用特征基础上微调淋巴细胞的细胞核纹理、边缘信息属于底层视觉特征预训练权重能帮上忙。参数说明这里每一个参数都值得讲清楚。imgsz输入分辨率。淋巴细胞的典型框可能只有 30×30 像素640 是下限显存允许就上 960 甚至 1024。imgsz 的改动对目标检测的小目标场景影响比换模型结构还大后面第 6 章专门说。batch显存不够就减半但 batch 太小低于 8时 BatchNorm 统计不稳定loss 会抖得厉害。这时优先考虑梯度累积而不是硬扛一个小 batch。lr0预训练模型微调用 0.01 起步是安全的。如果从头训练不加载 .pt要用 0.001否则前几个 epoch 很容易数值爆炸。patience医学数据集小训练后期 val 指标经常震荡patience 调大到 30 或 40 更稳否则容易在模型还没收敛时就停掉。数据增强YOLOv8 默认开启 mosaic1.0对小目标检测有帮助但 mosaic 会拼接四张图标注框在拼接处会被裁剪。如果训练后期 loss 反复横跳把 mosaic 降到 0.5 或关闭再试。4.3 训练过程中看什么别只盯 mAP训练命令跑起来之后runs/detect/train 目录会生成一系列图表。我最常盯的几张是confusion_matrix.png看淋巴细胞是否被系统性地误判成别的类别这比单个 mAP 数字信息量大得多。PR_curve.png如果曲线在 recall 接近 1 的位置明显下坠说明模型对遮挡和密集细胞场景处理不好这是淋巴细胞数据集的典型痛点。val 阶段的 box_loss 和 cls_loss两个损失如果在最后 20 个 epoch 还在持续下降说明 patience 可能不够模型还没收敛就早停了。训练完成后用最佳权重在 test 集上跑一次验证观察指标相对 val 是否明显下跌yolo detect val \ modelruns/detect/train/weights/best.pt \ data/data/lympho_data/split/data.yaml \ splittest输出里重点看每一类的 mAP50 和 recall而不是只看平均。哪一类 recall 低就去难例里找原因。5. 淋巴细胞数据集避坑实录5 个高频问题与排查路径5.1 图像路径或文件名带中文训练一启动就报 FileNotFoundError现象数据在本地 Windows 上跑得好好的传到 Linux 服务器后yolo detect train 刚启动就报 FileNotFoundError提示找不到某张训练图但 ls 明明能看到文件。原因Windows 压缩时使用 GBK 编码的中文目录名Linux 端默认 UTF-8 解码后目录名变成乱码YOLO 在读取 train.txt 时按乱码路径去找文件自然找不到。或者反过来解压时文件名是好的但脚本里用了硬编码的中文路径而服务器 locale 不是 UTF-8。解决解压时统一用 unzip -O CP936 转成 UTF-8就是第 2.1 节的命令用脚本把所有文件名批量重命名为小写字母和数字组合训练路径永远只用 ASCII。这一步在医学项目交付时尤其该做很多合作方的导出数据文件名都很随意别把风险留到训练阶段。5.2 训练 loss 正常下降但画出来的框全部偏移现象训练了 50 个 epochloss 曲线看起来完全正常打开验证集的可视化结果却发现每个框都偏在细胞一侧或者整张图所有框同时偏移一个固定距离。原因这是坐标换算错位的典型表现。最常见的是 VOC 的 xmin/ymin 被直接当成中心点用或者除以图像尺寸时把宽和高搞反。YOLO 的归一化坐标即便错位loss 依然能通过调整其他参数拟合一部分所以不会出现 loss 不下降的明显报错这也是这类 bug 极具迷惑性的原因。解决回到第 3.3 节的反投影脚本把每个 txt 的框画回原图人工检查重点看 y 方向是否出现系统性偏差因为高宽比不一致的图最容易暴露这个问题。修复转换脚本后重新走一遍转换和反投影再开始训练。这个教训我记了很多年任何坐标转换代码哪怕只改了一行都要重新可视化一批图再放心训练。5.3 类别 ID 从 1 开始或不连续训练不报错但结果全错现象训练正常结束weights 也正常保存但验证集上模型预测的类别和实际标注对不上混淆矩阵对角线全是稀碎的数值。原因标注工具导出的类别名经常是手动填的字符比如 1-lymphocyte、2-lymphocyte或者原始包里的 classes.txt 本身就是 1-based。转换脚本如果不做严格映射只把类别字符串 int() 一下就会得到从 1 开始的 ID。YOLO 的类别 ID 必须从 0 开始且连续否则模型输出层的类别索引和 names 之间会出现整体偏移。解决在转换脚本里统一维护 CLASS_MAP 字典把原始类别名显式映射到从 0 开始的 ID杜绝任何“自动分配类别号”的逻辑转换完成后扫描所有 txt 文件统计出现过的类别 ID 集合确认它是 {0, 1, 2} 而不是 {1, 2, 3}。这一步一分钟就能跑完但能省掉一整天排错时间。5.4 验证指标虚高 10 个点新数据上打回原形现象val 的 mAP50 高得让人惊喜但把训练好的模型拿到新的病理切片上跑效果明显差一截。原因数据划分时在单张图像层面做了随机打乱同一病例或同一视野的多个截图同时进入了训练集和验证集。模型相当于见过验证集图像的“兄弟版本”指标自然虚高。医学图像和自然图像的这点差别非常关键自然图像的类间差异大同源泄露的影响有限显微图像里同一个病人的细胞形态高度相似泄露带来的虚高会非常明显。解决按第 4.1 节的方法以病例组为单位划分数据集。划分之后还要做一个动作从训练集第 1 张和验证集第 1 张的文件夹路径上确认两份数据确实来自不同的病例组而不是同一组的两个子目录。这种错误在多人协作交付的数据集里尤其容易发生。5.5 目标检测模型微调崩了怎么办第一个 epoch loss 直接 NaN现象使用 yolov8n.pt 微调第一个 epoch 的 loss 就输出 nan之后一直 nan终端没有其他报错。原因最常碰见的有三种一是数据里有空标签 txt模型在某个 batch 上找不到正样本损失函数对空预测求导出了问题二是学习率相对于当前 batch 尺寸过大数值溢出三是图像里存在全黑或全白的异常通道输入张量的数值范围异常。解决先写个脚本统计 labels 目录里每个 txt 的目标数量和图像数组的数值范围min/max 是否全 0把空标签文件和可疑的全黑图移出训练目录再把 lr0 降到 0.001 重试。如果还不行用最小数据集比如只留 10 张图跑 1 个 epoch逐步加回来定位是数据的问题还是超参的问题。另外老显卡驱动下 AMP 混合精度偶尔会产出 NaN命令里加 ampFalse 强制单精度重跑一次成本很低。6. 小目标检测场景的增量手段从输入分辨率到切图推理淋巴细胞在常规 640 分辨率下经常只有 20 到 40 像素属于标准的小目标检测问题。我拿到一个新数据集时第一件事不是调模型而是统计标注框的像素尺寸分布把第 3 章反投影脚本里的 w 和 h 乘回图像分辨率画一张框宽高的分布直方图。如果 70% 以上的框小于 32×32那后续所有优化都围绕“小目标”展开。第一个手段是提高输入分辨率。imgsz 从 640 提到 960 或 1024相当于直接放大目标在输入张量里的占比。代价是显存和训练速度batch 可能要减半如果显存只有 8G 左右基本跑不动 1024 的 batch 16这时可以把 mosaic 降一点、关掉部分增强来换训练稳定性。另一个手段是推理侧切图也就是 SAHI 的思路把一张大图切成若干有重叠的窗口每个窗口独立推理再把结果映射回原图坐标做融合。切图窗口大小一般取训练用 imgsz 的 1.5 到 2 倍重叠率 20% 到 30%。它对病理大图尤其好用一张 2000×2000 的切片直接推理小目标看不清切图之后每个细胞都变成了“大目标”模型的检测能力立刻被释放。不过切图也有成本一个大图边长翻倍推理次数翻四倍批量跑上百张切片时耗时会显著拉长要在实际交付前先算清楚性价比。评估指标上我习惯额外看两类数字一是 recall50 在每一类上的表现尤其是淋巴细胞这一类因为漏检一个细胞比错检一个细胞对后续统计的影响更大二是按目标尺寸分层的 AP比如 ap_small看优化前后小目标 AP 的变化这比整体 mAP 的变化更有说服力。如果优化后 ap_small 涨了 2 个点而整体 mAP 波动不大说明方向是对的。我自己的习惯是任何优化都只动一个变量先跑一个不做任何增强处理的 baseline把 val 和 test 的数字记下来然后每次只改一个参数训练完跟 baseline 比绝不一次调三个参数。这样虽然慢但最后写交付报告时能说清楚“每个分数是怎么涨起来的”。这个习惯是从第一次调参翻车后学来的——那次我同时改了 imgsz、mosaic 和优化器结果指标涨了却说不清是谁的功劳。希望帮到你。本文还有配套的精品资源点击获取
