
做医疗视觉项目两年多最头疼的往往不是模型结构选哪个而是数据从哪来、标注怎么做、格式怎么转。尤其是疼痛检测这种偏冷门的方向开源数据集少得可怜公开的又大多是国外人脸数据库直接拿来训练场景不对、肤色有偏、标注口径也不统一。这篇就专门聊聊我最近在整理的一套疼痛检测数据集2200张图像、YOLO格式、面向医疗健康场景从数据设计思路到训练踩坑完整走一遍流程给同样在碰这类项目的朋友一个可以直接抄作业的参考。先说我自己的定位这套数据不是那种“下载完就开训”的现成包而是我按YOLO训练需求重新清洗、标注、划分的一套实用数据。原始数据来自公开医疗场景图像库我做了图像筛选、身份去重、标注框修正、格式统一和训练集/验证集划分这几道工序。适用对象很明确想跑通“YOLO做疼痛表情/疼痛行为检测”这个方向的算法工程师、研究生或者做护理监测系统、康复评估系统的团队。你不需要它有十万张图2200张对YOLO这种小模型来说配合预训练权重和合理增强足以在限定场景下把baseline打出来。1. 项目整体设计为什么用YOLO做疼痛检测数据怎么定1.1 疼痛检测到底是什么任务数据从哪来先讲清楚任务本身。疼痛检测在医疗健康场景里常见有两种落法一种是基于视频流的时序模型检测“疼痛程度随时间的变化”另一种是静态图像的疼痛线索识别比如抓拍一帧判断画面里的人是否处于疼痛状态、疼痛等级大概是多少。这背后有个很现实的问题很多患者尤其是术后病人、重症监护室病人、婴幼儿没法主动描述自己的疼痛只能靠医护人员周期性观察评分比如FLACC量表、面部表情量表。这种人工评估主观性强、劳动强度大才催生了“自动疼痛识别”的需求。数据层面原始图像主要来自医疗场景下的公开研究图像和患者授权使用的图像库。整理成数据集的时候我做了严格的脱敏处理所有面部图像都去掉了患者信息部分图像还做了关键区域裁剪。这里多说一句医疗图像做数据集合规永远是第一位的不要自己拿手机去病房拍也不要使用来源不明的爬取图像。我用的都是明确标明可用于学术研究的公开库涉及隐私信息的一律不保留。1.2 为什么选YOLO而不是分类网络疼痛检测如果只做“疼/不疼”二分类ImageNet预训练的ResNet就行没必要上目标检测。但实际项目里疼痛检测的需求往往是多重的你要知道画面里有没有人、人在哪个位置、哪个部位出现了疼痛相关的动作特征比如捂肚子、面部表情区域在哪。这时候检测网络就比分类网络合适——它天然是“定位分类”的结构输出标注框和类别直接给下游业务提供结构化信息。YOLO系列在这类任务上的优势一是速度快一张图在GPU上毫秒级推理护理监测系统对实时性要求很高二是生态成熟从标注到训练到部署全链路资料多、踩坑的解决办法也好找。相比之下两阶段检测器比如Faster R-CNN精度可能更稳但部署成本和工程复杂度高一个量级。2200张的数据量YOLO完全够用Faster R-CNN反而容易因为数据不足导致收敛慢。1.3 标注类别和标签体系怎么定这是最纠结的一步。疼痛检测的标注体系主流有两类一类是离散标签比如标“轻度疼痛”“中度疼痛”“重度疼痛”“无痛”另一类是行为/动作标签比如“捂头”“捂腹”“面部扭曲”“蜷缩”。我最终采用的是混合策略主类别pain只要有明显疼痛表现的人体目标框细分类别pain_mild、pain_moderate、pain_severe根据面部表情强度和动作特征细分辅助类别caregiver医护人员用来让模型学会区分患者和医护人员避免误检有人说“疼痛等级”这东西本身就很主观不同标注员一致性差怎么办。我的做法是给标注手册写清楚每条判据比如“轻度眉毛轻微收紧、嘴角下拉但幅度小中度明显皱眉、眼睑收紧重度面部扭曲、伴随护痛动作”然后所有图像由3个标注者独立标注用多数投票决定最终标签。这样至少能把主观性压到可控范围。如果你的项目只需要一个类别那直接用pain单类即可数据处理流程完全一样。2. 数据集结构与YOLO格式转换的完整实操2.1 目录结构与图像分配训练集、验证集、测试集的划分我没有按默认的随机划分走而是先按患者身份去重同一个人的图像只出现在一个集合里避免模型“记住人脸”造成验证集虚高。比例上训练集1760张、验证集280张、测试集160张大约8:1:1。最终目录结构长这样pain_dataset/ ├── images/ │ ├── train/ # 1760张 │ ├── val/ # 280张 │ └── test/ # 160张 ├── labels/ │ ├── train/ # 每张图对应一个同名txt │ ├── val/ │ └── test/ ├── data.yaml ├── label_mapping.json └── README.md这里是纯文件目录说明但很多人会忽略的一点是YOLO的label文件和image文件名必须完全一致包括扩展名前的部分否则训练时对应不上。我的文件名统一用pain_0001.jpg这种连续编号顺序打乱后写入不同集合避免同类图片排在一起影响训练批次多样性。2.2 YOLO标注格式与坐标归一化转换YOLO格式的标注文件是纯文本每行一个目标结构是class_id x_center y_center width height注意这里x_center、y_center、width、height都是归一化到[0,1]的值不是像素坐标。我在实际转换过程中最常遇到两类错误一是忘了除以图像宽高二是类别id从0开始编号不是从1开始。这里直接给一段我用Python做的转换脚本核心逻辑import os def voc_to_yolo(x1, y1, x2, y2, img_w, img_h): # x1,y1左上角; x2,y2右下角; 全部为像素坐标 dw 1.0 / img_w dh 1.0 / img_h x_center (x1 x2) / 2.0 y_center (y1 y2) / 2.0 w x2 - x1 h y2 - y1 return x_center * dw, y_center * dh, w * dw, h * dh如果你手里的原始标注是Labelme的JSON或者COCO的JSON先统一转成中间格式每行一个目标再加class_id这样最不容易出错。2.3 数据YAML配置与标签映射YOLOv8的训练入口是一个data.yaml文件。我这份数据的配置是这样的path: /path/to/pain_dataset train: images/train val: images/val test: images/test names: 0: pain 1: pain_mild 2: pain_moderate 3: pain_severe 4: caregiver写这个文件的时候有个细节path尽量写绝对路径。如果写相对路径YOLO会基于当前工作目录拼接而你后续如果换一台机器或者放到别处跑很容易报找不到数据集。另外names这个字段的顺序必须和标注文件里的class_id一致否则类别就错位了。我自己就吃过这个亏标注脚本里id从1开始yaml里names也从1开始结果训练时YOLO自动把0类当成背景模型输出直接全乱套。3. 用YOLOv8训练疼痛检测模型的完整流水线3.1 环境配置与预训练权重选择我的环境配置如下供参考Python 3.10PyTorch 2.1.0 CUDA 11.8ultralytics 8.2.xGPU一张V10016G或RTX 3090/4090都可以显存不够的话用yolov8s也完全跑得动数据集只有2200张不建议从随机初始化开始训。我在实际训练时优先加载官方提供的COCO预训练权重yolov8n.pt、yolov8s.pt。有人觉得COCO和医疗疼痛场景差太多预训练没用这个观点不完全对。YOLO浅层学到的是边缘、纹理、色彩这类通用特征这些对医疗图像同样成立真正需要从头学的是高层语义特征。用预训练权重等于让模型带着“眼睛”来学而不是盲人摸象。下载方法也很简单ultralytics库会自动下载如果网络不行就手动去官方GitHub的release里取。3.2 训练命令与关键参数说明我用的是yolov8s作为baseline命令大概长这样yolo train modelyolov8s.pt datapain_dataset/data.yaml \ epochs200 imgsz640 batch16 patience30 \ device0 workers8 \ projectruns/pain_detect nameexp_baseline逐个拆解这几个参数的作用epochs2002200张图batch16每epoch约110步200轮不算多。但如果训练集再小比如1000张我建议epochs提到300因为小数据下模型收敛慢。imgsz640YOLOv8默认就是640。如果你的图像里目标框很小比如离远拍摄的疼苦表情可以考虑768但显存占用会变大训练时间也增加。patience30连续30轮验证集mAP不提升就早停。这个参数很实用小数据集经常到100轮附近就过拟合了硬训到200轮纯属浪费时间。workers8数据加载线程数。Windows上如果报错改成2或4。在损失函数方面YOLOv8默认的cls_loss是BCEbox_loss是CIoUdfl_loss是Distribution Focal Loss。这些默认配置我建议不改除非你有明确理由。很多人问“用不用换损失函数解决小目标”说实话在医疗行为检测这种“目标不算特别小”的场景下默认损失完全够用优先调数据比调损失函数效率高得多。3.3 训练过程中的指标监控要点训练日志里除了loss我最关注三个指标mAP50、mAP50-95、precision。对小数据集mAP50达到0.7以上算可用precision要看误检率是否影响护理系统告警。我在训练时每5个epoch存一次验证集预测图直接肉眼检查典型的假阳性和漏检。这里有个容易犯的错误只盯着mAP看不看PR曲线。疼痛检测场景里假阳性的代价比假阴性小多告警一次医护可以复核所以我会把confidence阈值调低到0.2~0.25换取更高recall。但如果你做的是自动评分系统不想频繁打扰医护那阈值可以设到0.5以上。阈值没有标准答案完全看业务怎么权衡。4. 小样本训练疼痛检测的典型问题与排查手册4.1 过拟合训练loss降了验证mAP不涨反跌这是2200张规模数据集的头号问题。症状是训练loss很漂亮验证集mAP在某个点之后开始掉或者震荡。我的排查路径先检查train augmentation是否开太大。YOLOv8默认的增强里有随机翻转、缩放、颜色扰动如果图像里有医护人员的蓝色手术衣颜色扰动会让模型把“蓝色”和“患者”搞混。我用的增强策略是翻转0.5、scale 0.4、hsv_h 0.01、hsv_s 0.5、hsv_v 0.4然后关掉mosaicmosaic0。为什么关mosaic小数据集下mosaic会让模型看到大量拼接后的“假图”虽然能增强泛化但对疼痛细节特征的破坏很大容易让模型学不到细微表情变化。再查是否用了dropout或weight decay。YOLOv8默认weight_decay0.0005如果loss还是压不住可以尝试加到0.001效果有时很直接。如果上面两步都做了还过拟合那就老老实实加数据。2200张确实少可以再做一次基于相似度筛选的扩充把原始图像做小角度旋转(±15度)、水平翻转、亮度抖动扩充到4000张左右。但这只是线性涨数据真正质的提升还是靠更多独立样本。4.2 类别不均衡pain_severe样本太少模型完全学不到我的标注统计里pain_severe大概只有总目标的8%训练时模型对这个类几乎不预测。解决办法我用了两层一是给loss加类别权重。ultralytics支持在data.yaml里通过weight字段配权重但实际操作中我更喜欢在数据层面做处理把严重疼痛的图像做更多增强副本比如旋转、裁剪缩放这样既不改损失函数逻辑又变相增加了该类别的出现频率。二是用“按需采样”替代纯随机采样。我写了个简单的数据集子类每次迭代时根据当前batch里的类别分布动态抽取样本保证每个batch里至少有两个severe样本。代码不复杂但对小数据集非常有效。如果你不想改代码最简单粗暴的做法是把severe类的图像复制几份放进训练集虽然不优雅但实测能让模型“看到”更多该类的变化。4.3 训练中loss出现NaN或BN崩溃热搜词里有个“yolo训练中bn崩溃”确实存在。我跑这个数据集时也遇到过前几个epoch loss正常到第70轮左右突然loss变成NaN。排查出来的原因是我把batch size设成64但训练图像里有几张是超宽全景图做了resize之后某些格子里目标严重变形导致梯度爆炸。解决方法是三选一降低batch size到16或32最省事显式设置workers不要太高避免数据加载时的偶发错误清除异常数据——我把所有宽高比大于3:1的图像单独拎出来做过一遍标注复查发现确实有少数图像因裁剪不当导致边界框超出图像范围这种框会让loss计算出现无穷值。还有一点训练中途BN崩溃经常是因为学习率设置过高。YOLOv8默认lr00.01对微调预训练模型我一般降到0.005稳定很多。4.4 验证集mAP虚高部署后效果暴跌这是最坑的一个问题。训练时mAP50有0.85一到真实场景完全不行。十有八九是数据划分不严格导致的泄漏。我举一个自己踩过的例子早期版本我直接在原图像库上随机划分数据集结果同一个人的不同帧图像同时进了训练集和验证集模型等于见过照片上的人验证自然漂亮。后来我改成按“人物身份”划分同一个受试者的所有图像要么全部进训练集要么全部进验证集mAP立刻掉到0.72但测试结果反而更真实。另一个泄漏来源是重复图像公开图像库里有时同一张图被不同来源反复上传清洗阶段没做感知哈希去重很可能悄悄进了两个集合。我的做法是对全部图像算一次pHash相似度大于0.95的直接只保留一张再做划分。这一步花不了多长时间但对可靠评估帮助巨大。5. 数据增强策略、部署要点与可复现建议5.1 小数据集增强策略的最终配置参考我最终跑下来比较稳的增强配置是这样参数值说明flipud0.0不启用上下翻转医学图像里人物方向固定fliplr0.5水平翻转增强左右对称特征scale0.4沿入截图尺度防止模型只认识一个尺寸hsv_h0.01色调轻微扰动避免色彩过拟合hsv_s0.5饱和度扰动适可而止hsv_v0.4亮度扰动模拟不同环境光照mosaic0.0关掉保留疼痛细节mixup0.0默认不用小数据下效果不稳定这套配置不是最优解但它是“稳定不容易崩”的起点你完全可以在它的基础上加大scale和fliplr的比重做对比实验。5.2 训练完的模型导出与部署注意训练完成后要落地到实际系统比如病房监测端我一般做这几步yolo export modelruns/pain_detect/exp_baseline/weights/best.pt formatonnx opset12 yolo export modelruns/pain_detect/exp_baseline/weights/best.pt formattflite imgsz640导出ONNX后用onnxruntime或者TensorRT跑推理。实际部署我强烈建议做一次int8量化因为在边缘设备上FP32推理速度往往不够。但量化要注意校准数据集要用训练集里的子集不要用验证集否则精度损失评估会失真。还有一个部署细节YOLO的输出坐标是归一化的在使用时需要还原到原始分辨率坐标再去和业务逻辑对接。很多工程事故都出在忘了做这一步直接把0到1的值当成像素坐标去画框或计算距离。5.3 数据合规与实验复现建议做医疗场景数据集有几条红线必须反复强调不可以使用含个人身份信息的原始图像所有图像必须脱敏处理如果数据来自第三方库仔细阅读授权协议确认是否允许二次分发和商用标注过程中如果涉及多人协作标注者之间要有统一的标注手册并且定期做一致性校验比如每50张图抽取一张由不同标注者复标计算Cohens Kappa整个数据处理的脚本、参数、增强配置建议用DVC或简单的git-lfs记录方便实验复现。我自己的习惯是每轮实验除了记录yaml还把训练命令、数据版本号写进一个experiment.log这样以后想看哪个版本跑出来的结果直接回溯。这套疼痛检测数据集整理下来我最深的感受是2200张图放在医疗AI领域不算多但如果你把“数据质量”这一关把住了——身份维度去重、标注一致性、格式严谨、增强克制——YOLO跑出来的指标是完全够做个像样的demo和研究基线的。我实际拿这套数据训出的yolov8s模型在内部采集的20段病房验证视频上疼痛事件的检出率大约在八成左右误报率大约每半小时两次。对初步的护理辅助监测来说这个水平已经可以拿去和临床团队聊需求了。下一步我计划在数据里加入更多夜间低光照场景的样本再把标注细化为“疼痛面部动作单元”AU4皱眉、AU7眼睑收紧等让模型不仅能检测疼痛等级还能解释是哪个部位的线索触发了判断。这个方向如果你也在做欢迎拿这套流程去试试有问题咱们评论区碰。