ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLO医疗健康数据集实战:2200张图片构建疼痛检测模型的完整复盘

YOLO医疗健康数据集实战:2200张图片构建疼痛检测模型的完整复盘 把疼这件事交给机器来判断听起来有点反直觉。疼痛是纯主观体验患者说疼就是疼可临床上就是存在大量患者说不清疼的场景——重症监护里的镇痛需求评估、术后苏醒期的躁动判断、婴幼儿和认知障碍患者的疼痛识别这些场景根本没法靠一张量表解决。于是自动疼痛检测成了医疗健康AI里一个很务实的细分方向而支撑它的第一步就是一套靠谱的疼痛检测数据集。这篇文章我会完整复盘一个围绕2200张YOLO医疗健康数据集落地的实战项目从数据怎么采集、标注方案怎么定、YOLO系列怎么选到训练评估阶段那些文档里不会写的坑一次说清楚。适合想用YOLO做医疗视觉检测的开发者也适合正准备自建小规模标注数据集的团队参考。1. 疼痛检测到底在检测什么先明确任务边界再选模型1.1 从主观量表到客观视觉指标医院的疼痛评估基本还是靠量表VAS视觉模拟评分、NRS数字评分让患者自己画线、自己报数字。这套流程在门诊偶尔用还行但放到重症、术后、新生儿场景就彻底失灵了——患者要么镇静中要么表达不清要么根本没有表达能力。这也是过去二十多年研究者一直在做客观疼痛评估的原因核心思路是捕捉疼痛引起的可测量行为信号其中最成熟、最稳定的就是面部表情。面部在不同疼痛程度下会触发特定的表情动作单元比如皱眉、眯眼、鼻翼扩张、嘴角拉扯。这些动作的组合和疼痛强度有统计学相关性所以疼痛检测数据集普遍以人脸图像为主体。机器要做的事就是把这组面部视觉特征映射到有没有疼或者疼到几级的输出上。我构建的这个数据集2200张图片不是随手收集的疼脸图而是围绕任务边界专门设计的检测主目标是人脸区域类别体系是疼痛事件的有/无以及0到3级的程度分级。四个类别的比例在采集时故意做了控制没有平均分配因为真实病房里轻度不适远多于重度疼痛这种先验分布必须体现在数据里不然后面训练出来的模型会严重偏向高频类。1.2 YOLO在疼痛检测任务里的真实定位这里先泼盆冷水用YOLO做疼痛检测通常不是让模型直接一眼识别疼痛等级而是把它放在一个更大的处理链路里。YOLO是目标检测器强项是快速定位目标区域并给出类别置信度它最适合承担的是把脸框出来、把手术区域框出来这类前置定位工作而不是做细粒度等级判断。我当时采用的方案是两级结构第一级用YOLO检测图片或视频帧中的人脸输出脸部bbox和粗类别第二级把裁切后的脸部区域送进一个轻量分类网络做细粒度疼痛等级打分。也有人直接用YOLO的多类输出完成疼痛/非疼痛的粗分类实测效果也能用但一旦涉及0到3级这样的分级任务我建议还是交给专门的分支处理更稳。这种检测定位分类识别的分工直接决定了标注阶段必须同时关注两个质量维度bbox是否紧贴目标类别标签是否准确。很多初学者在这里会用通用目标检测的思路标注觉得大概框住就行结果训练时会暴露严重的domain bias推理时遇到头部倾斜、手部遮挡就疯狂漏框。这个问题我在下一章展开讲。2. 2200张数据怎么攒出来采集、清洗、标注的全链路2.1 数据来源与合规前提医疗健康数据集的构建第一关不是技术是合规。人脸在绝大多数国家都被视为敏感生物识别信息项目启动前就必须确认数据有没有脱敏有没有授权协议能不能开放二次加工。以这个数据集为例来源分三块一是有公开许可的学术疼痛表情数据允许研究用途进行二次加工二是合作医疗场景中采集并完成去标识化的病房图片三是为了补充特定角度和光照条件由志愿者在签署知情同意书的前提下拍摄的模拟疼痛表情。三块数据的价值权重也有讲究。真实病房数据最宝贵但样本量少、场景单一公开数据规模大但分辨率、光照、年龄分布可能跟部署环境不匹配模拟表情能补足角度和多样性但表演疼痛和真实疼痛在微表情动态上存在差距。最终2200张的构成我按真实数据约700张、公开来源约1000张、模拟扩充约500张来控制这个比例是在真实度和覆盖度之间取的平衡。2.2 标注方案设计检测框和疼痛等级分开标疼痛检测数据集的标注关键问题是检测目标到底是什么。我采用的是两阶段标注方案先标人脸bbox再给每个人脸打上疼痛程度标签。因为后面要接分类分支K类标签必须保持一致这里我设计了4类0代表无明显疼痛1代表轻度轻微皱眉等响应2代表中度表情变化明显、伴随身体紧张3代表重度明显的面部扭曲或发声表现。标注工具用的是CVAT原因是多人协同标注时它能做任务分片和复核比单机版LabelImg高效很多。这个环节有个特别容易被忽略的点一致性校验。疼痛等级本质上是主观评分两个人可能对同一张脸给出不同等级。所以每个样本至少要有两位标注者独立标注遇到分歧再由第三人仲裁。我当时的Kappa一致性系数做到了0.82以上才敢把数据送进训练。没有这步校验后面所有模型指标都是虚的。2.3 清洗、增强与数据划分标注完成后要做的第一件事是清洗。按优先级排查四类问题模糊对焦不准或运动模糊、过曝医疗场景白平衡经常出问题、遮挡口罩、设备、手部遮挡超过40%、标签错误。清洗防的是脏数据污染梯度宁可少100张干净图也别多留500张错误图。数据增强方面我用了水平翻转、小角度旋转、随机亮度对比度扰动以及轻微的HSV色域增强。这里特别提醒一句不要对医疗人脸数据做太夸张的几何增强。切边、大角度旋转、弹性形变虽然能在验证集上刷高指标但会破坏临床图像的真实形态模型在真实病房里会表现为能识别训练分布里的脸认不出真实环境里的脸。增强是为了模拟真实变化不是为了造出玄幻图片。划分比例我自己的习惯是train:val:test7:1.5:1.5对应1540张训练、330张验证、330张测试。验证集和测试集必须严格同分布测试集是留到最后才碰的那一部分这个纪律直接决定最终评估的可信度。3. 从标注文件到YOLO训练格式格式转换与模型选型3.1 标注格式转换的细节坑标注完成后CVAT导出的是COCO JSON格式而YOLO训练流程用的是txt格式。很多人第一次跑会报no labels found之类的错就是因为格式没转。转换逻辑本身不复杂把COCO的像素XYWH转成归一化的中心点坐标类别ID映射到YOLO连续整数编号。示意代码可以这样写import json from pathlib import Path def coco_to_yolo_txt(json_path, img_dir, out_dir, categories): with open(json_path) as f: data json.load(f) for img in data[images]: img_w, img_h img[width], img[height] anns [a for a in data[annotations] if a[image_id] img[id]] lines [] for a in anns: cls_id categories[a[category_id]] x, y, w, h a[bbox] x_center (x w / 2) / img_w y_center (y h / 2) / img_h w_norm w / img_w h_norm h / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}) out_file out_dir / (Path(img[file_name]).stem .txt) out_file.write_text(\n.join(lines))这里有一个很常见的坑COCO的bbox是左上角原点的像素坐标YOLO要的是归一化中心坐标。有些人想当然地除以图片宽高就完事结果忘了把x,y先转成中心点训练出来的loss全程不降还要怀疑是模型的问题。转换完一定要抽查十张图把txt里的坐标反算回图片上画框验证一遍。目录结构也要按ultralytics的习惯放好dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/3.2 YOLO系列选型小数据量场景做什么决策2200张的小规模医疗数据集我最推荐YOLOv8n或YOLOv8s。原因有三第一模型容量和数据量匹配n版本约300万参数不容易把训练集学穿第二ultralytics生态对迁移学习、训练曲线、导出部署支持最完整省去大量自造轮子的时间第三医疗场景往往要求部署到低成本设备或移动端轻量模型在推理延迟上的收益远大于几个百分点的mAP提升。这里多说一句社区里很火的efficient head轻量检测头思路。在小数据集背景下检测头的复杂度和训练稳定性成负相关——头越复杂在有限样本上越容易过拟合也越容易出梯度异常。想做模型改进的话优先在检测头轻量化、特征融合简化上做文章别一上来就堆注意力模块否则训练稳定性会让你怀疑人生。训练前确认预训练权重。从COCO预训练权重开始迁移是当前性价比最高的做法。COCO上的检测特征已经包含人脸轮廓、边缘、纹理这类底层视觉模式先验我们要做的只是微调高层语义。这个选择能直接节约60%左右的训练轮次让模型更早进入有效收敛状态。类别映射表也要提前固定好比如0代表pain_none1代表pain_mild2代表pain_moderate3代表pain_severe这个表一旦训练开始就不能改。4. 训练阶段最常踩的四个坑过拟合、BN崩溃、样本不均衡与损失不收敛4.1 过拟合2200张数据的第一杀手一个300万参数的检测模型放到2200张图上过拟合是大概率事件。我的判断标准很简单训练集loss持续下降验证集mAP从某个epoch开始不再上升甚至下跌train和val的loss曲线逐渐劈叉。解决方案除了前面讲到的数据增强最有效的是分期冻结训练。先用预训练权重冻结骨干层freeze10只训练检测头让模型先快速适应疼痛数据集的标签分布跑50个epoch之后解冻骨干用小学习率微调整个网络学习率大约设成原来的十分之一。这样能防止模型一上来就把大量参数砸向训练集的噪声细节尤其在医疗图像噪声偏高的情况下这个操作几乎等于保命。4.2 BN崩溃小batch size下的隐形雷yolo训练中bn崩溃是我在社区里看到的高频词一开始没当回事直到某次训练loss直接变成NaN才重视起来。BN崩溃的典型诱因是batch size太小导致BatchNorm层在计算均值和方差时统计震荡剧烈医疗图像又经常有大面积暗部单张图内部像素分布不均匀BN统计量更容易失控。我的应对经验batch size尽量不低于8。如果显存不够优先降输入分辨率640降到416也不要硬把batch压到4数据加载时对所有图片做统一的均值和方差归一化能明显降低BN统计量的抖动。训练一旦出现NaN立刻回滚到崩溃前最近的正常检查点然后把当前batch里的异常样本捞出来看别盲目调学习率多半是数据或者归一化的问题。4.3 样本不均衡重度疼痛样本太少怎么补前面提到真实场景四个疼痛等级天然不均衡。我在YOLO训练时发现模型对重度疼痛的recall明显偏低因为第3级样本只有一百多张。处理方法是两步并行一是对少数类做过采样复制时配合增强变化二是在损失函数层面给少数类更大权重。ultralytics支持设置class weights参数我按[1, 1.2, 1.5, 2.0]做递增序列让模型在梯度更新时更关注重度样本。但这个操作有个副作用模型会变得过度激进把轻度疼痛也判成中度。所以调完权重之后必须回看验证集混淆矩阵确认提升的是少数类的召回率而不是整体乱猜。样本不均衡的解法本质是个杠杆方向对了能撬动指标方向错了会摧毁精确率。4.4 损失曲线观察与超参数参考YOLOv8的训练曲线一般看box_loss、cls_loss、dfl_loss三条。正常节奏是前10个epoch三条loss快速下降中间50个epoch缓慢收敛后期小幅波动。如果cls_loss在中途突然反弹多半是学习率过大或者数据里有标签冲突样本。前者调低lr后者回头查标注往往能找到两张同一张脸被标成两个等级的问题图。我实测下来一组稳定的基线参数供参考参数推荐值modelYOLOv8nepochs100imgsz640batch16optimizerSGDlr00.01weight_decay0.0005freeze10epoch 60前冻结骨干conf_thres0.5iou_thres0.45这组参数下2200张数据大约40到60个epoch就能让mAP50摸到0.85以上。再往后训练大概率过拟合学会收手比学会硬训练更重要。5. 别只盯着mAP医疗场景下的评估指标与部署落地5.1 看混淆矩阵更要看错误方向目标检测社区习惯用mAP作为主要指标但在医疗AI场景里mAP远远不够。mAP是平均值的艺术它会把把轻痛判成无痛和把无痛判成重痛一视同仁地算进误差而临床上这两类错误的影响完全不同。对疼痛检测来说漏报意味着患者得不到镇痛干预虚报顶多是护士多跑一趟。怕的是漏报不是虚报。我的评估习惯是三步走先看mAP50和mAP50:95确认模型整体可用再打开混淆矩阵统计每个疼痛等级之间的错误迁移方向最后专门算一个临床显著漏报率——即实际等级大于等于二级但被预测成0级或1级的样本占比。这个自定义指标比mAP更能反映模型在真实场景里有没有用。第一次拿到YOLO混淆矩阵的人可能会疑惑为什么行和列的总数对不上。这是NMS和置信度阈值过滤导致的低于阈值的框不进入最终输出所以矩阵里没有对应条目不代表程序算错了。5.2 从训练指标到实际部署验证集指标只是第一步部署环境才是真正的考场。我当时把YOLOv8n导出ONNX再用TensorRT做FP16加速在病房常见的低功耗主机上测试推理单帧约35毫秒1080p视频勉强能做到20FPS以上够用但不算宽裕。部署阶段有几个细节值得记下来。第一训练时的输入尺寸和部署时的推理尺寸要保持一致换个resize方式都会导致定位偏移第二实际摄像头视角和训练数据差别通常很大必须准备一批现场采集图做二次校准第三医疗设备的推理结果要保守置信度阈值宁高勿低我用0.5宁可漏掉一些边界case也不让误报干扰医护判断。部署不是模型训练完就结束的事情多跑现场才能看到训练时看不到的问题。6. 复盘2200张数据集的价值不在数量而在形成闭环6.1 小数据集的意义不在于全而在于可用如果非要给这次项目定个性我会说2200张YOLO医疗健康数据集本身不该被当成大而全的资源它更像一个高质量验证底座用来证明在有限样本下客观疼痛检测这条路可以跑通。小数据集加迁移学习加严格清洗往往比大数据集加粗糙标注更能快速验证产品假设。我个人的感受是医疗AI的数据迭代一定要小步快跑。每次补充几百张真实临床样本做增量训练比一次性追求几万张图靠谱得多。数据质量的控制是一个持续过程模型指标其实是对数据质量的回应。6.2 后续扩展方向与一个小技巧模型结构上不必追新YOLOv8n这种成熟轻量模型在现有数据规模下已经够用。真正值得投入精力的方向是把公开数据和真实场景数据做更精细的domain mix以及在评估体系里加上更多面向临床的指标口径。如果你也要做类似的小规模医疗检测数据集我建议在训练时引入少量合成样本加较强增强的组合把公开数据里的人物姿态迁移到不同背景上这可以在不大幅增加标注成本的前提下提高泛化性。最后分享一个我自己养成的小习惯每个版本的模型训练完我都会把验证集里预测错的样本导出成一张拼图按错误类型分组定期回看。这比任何loss曲线都更能提醒自己——数据里还有哪些长得像的坑没填平下一批补充数据应该优先采什么场景。整个过程踩过不少坑但把数据、标注、训练、评估这条链路完整走通之后迁移到其他医疗检测任务基本就是复制经验的事了。
返回列表