☰
用YOLO构建医疗疼痛检测数据集:从标注到训练的实战记录
2026/10/2 10:23:35 网站建设 项目流程

做了大半年的医疗健康场景视觉项目后,我最大的感触是:AI落地的难点往往不在模型,而在"数据到底怎么定义"。就拿疼痛检测来说,临床上大家都在说NRS评分、面部表情量表,但真正能丢进YOLO训练的数据集少之又少。这篇东西我想把最近整理的2200张疼痛检测数据集的思路、标注规则、训练参数和踩坑过程完整记下来,给想在这个方向入手的同行一点参考。适合正在做医疗健康视觉、护理监护、康复评估相关产品,或者想用YOLO做表情/状态识别但卡在数据构建阶段的人。

1. 为什么要做疼痛检测数据集:医疗场景里"疼不疼"是个硬需求

先交代背景。我们团队在做住院患者的日常监护系统,摄像头装到病房里之后,管理层提了一个需求:能不能在护士查房间隙,自动判断病人是不是正处于疼痛状态?这个需求听着简单,但实际操作里特别棘手。目前绝大多数医院的疼痛评估靠护士定时问诊或者患者自己按呼叫铃,主观性强、频率低,而且夜间和术后恢复期最容易漏掉状况。

我当时先在公开渠道翻了一遍现成的疼痛数据集。国内能找到的基本都是学术性质的人脸表情数据集,里面的疼痛评估标准和临床常用的NRS评分对不上,而且很多是实验室环境光线、正面机位、摆拍表情,换到病房场景基本是废的。于是我们决定自己攒一个医疗健康场景的疼痛检测数据集,模型选定YOLO系列,因为后续要部署到病房的边缘设备上,实时性和轻量性是硬指标。

1.1 疼痛检测到底检测什么

很多人一开始会走偏,以为疼痛检测是"给整张图像算一个疼痛分数"。我们的做法是把任务定义成目标检测+分类:先在画面里找出人脸框,然后给每个人脸框一个疼痛等级标签。这样有几个好处:

  • 多人病房场景下,一个画面里可能有多位患者或者家属,逐人输出结果比整图打标签更实用;
  • 检测框天然提供了定位信息,医护系统可以在监控画面上把有重度疼痛风险的人脸框标红;
  • 后续如果要接质检、报警、追溯,有框才有可解释性。

疼痛等级我们参考临床习惯做了三档:no_pain(无痛)、mild_pain(轻度疼痛)、severe_pain(重度疼痛)。为什么不直接用NRS的0-10分?因为目标检测本质是离散类别输出,把10个等级做成一堆类别,同类之间的特征差异可能比跨类的还小,模型会学得非常痛苦。三档划分既和临床处置动作挂钩(一般NRS大于4分才需要药物干预),又不会让标注变得不可操作。

注意:如果你的场景需要连续分数,更合理的做法是用YOLO做人脸检测,再拿检测框去切图,接一个回归头输出NRS值。这个我在后面"落地场景"里还会详细说。

1.2 为什么选YOLO而不是其他方案

我们内部其实对比过三套技术路线。第一套是人脸关键点方案,比如用68点关键点算嘴巴、眼睛的开合度,再喂给分类器。优势是可解释性强,但关键点的标注成本大约是框标注的3倍,而且被遮挡时关键点直接崩。第二套是基于Transformer的时序模型,学术效果确实好,可护理站的工控机跑不动,延迟一上来就失去了"持续无感监护"的意义。第三套就是YOLO,理由很直接:

  • YOLOv8的n/s型号在边缘设备上能做到30fps以上,满足实时要求;
  • txt格式的标注文件和LabelImg、Roboflow、Ultralytics生态完全打通,标注团队上手快;
  • 单阶段检测器对人脸这种尺度稳定、外观差异不算大的目标,效果和速度的平衡点掌握得好。

还有一个隐性原因:YOLO的生态允许同一个模型同时做人脸检测和疼痛分类,不用像"检测+识别"两段式那样维护两个模型,对医疗现场的部署复杂度是很大的缓解。

2. 2200张数据是怎么攒出来的:数据来源、清洗与标注规范

数据集的构建是整个项目里耗时最长的部分,前后花了大约六周。很多人以为攒图片就是"下载-打标签-开训",实际上每一步都要做决策,而且里面不少坑是网上教程不会写的。

2.1 数据构成与授权处理

先说数据来源。我们用了三类素材叠加,避免单一来源导致的模型偏见:

  • 公开表情库中的疼痛表情子集:主要是实验室条件下通过冷压实验、电刺激等方式诱发疼痛表情的公开人脸影像,约730张。这类素材胜在疼痛表情"标准",但背景干净、光线均匀、机位固定,和真实病房差距大;
  • 自建模拟疼痛表情:找志愿者在病房环境下按标准表情表演的轻度和重度疼痛,约920张。这部分用来拉近数据分布和真实场景的距离;
  • 实际临床视频截帧:在获得患者知情同意后,从术后恢复期监控视频中截取关键帧,约550张,并做了严格脱敏处理。

这里必须多说一句:医疗数据不是普通数据,来源和授权是红线。公开表情库要看清楚授权协议是否允许商用和二次标注,自建部分要过伦理审查,临床截帧绝不允许出现患者身份信息。我当时因为图省事差点直接用爬虫抓网络图片,被组里做合规的同事拦住了。用未经授权的面部图像训练并发布,风险极高,尤其这数据集还带"医疗健康"属性,一旦出问题不是删库能解决的。

2.2 标注规范的制定

标注规范是决定数据集质量上限的东西。我们最开始只给标注员发了一张示例图,结果第一周回来一看,同一个"轻度疼痛"的框,三个人画得千差万别。后来我整理了一份详细标注指南,核心几条:

  • 目标定义:只有正脸或半侧面可见且能判断眼睛和嘴巴区域的人脸才标注,纯侧脸和背面不标;
  • 框的贴合度:框必须包住额头到下巴、左耳到右耳的可见范围,不裁剪头发;
  • 疼痛等级判定:以"眉间收缩+眼睑紧绷+上唇提拉+嘴角横向拉扯"等面部动作单元为参考,出现1-2个算轻度,3个以上且有明显痛苦状算重度;
  • 多人场景:画面中有几个符合条件的人脸就标几个框;
  • 不确定就弃标:无法判断等级的样本不要硬标,宁可少一张也不要制造噪声。

这里我特别想强调"弃标"这条规则。很多项目为了凑数据量,让标注员在模棱两可的图上硬选一个标签。事实证明这些图在训练里全变成了噪音,轻度的标准被拉得乱七八糟。我们最终2200张图里有大概300多张是标注后又被复核踢掉的。

2.3 清洗与去重

标注完之后还有一道清洗流程,千万别省。我们做了四件事:

  1. 感知哈希去重:病房监控视频截帧里常有大量相似度极高的帧,几张几乎一样的重复图会让验证集的评估虚高,所以必须按哈希值去重;
  2. 清晰度过滤:用拉普拉斯算子计算图像方差,低于阈值的模糊帧直接删;
  3. 小目标过滤:人脸框面积小于32×32像素的样本废弃,因为这种分辨率下YOLO根本学不到疼痛特征;
  4. 分布复查:按拍摄场景、性别、年龄段统计分布,防止某个文件夹的图特别多、把模型带偏。

清洗后,最终数据集为2200张图像、2981个人脸标注框,其中no_pain约1030框、mild_pain约890框、severe_pain约1061框,整体类别接近均衡。按约8:1:1切分,得到train 1760张、val 220张、test 220张。

2.4 目录结构与YOLO格式

先看最终的目录结构:

pain-dataset/ ├── images/ │ ├── train/ # 1760张 │ ├── val/ # 220张 │ └── test/ # 220张 ├── labels/ │ ├── train/ # 对应的txt标注 │ ├── val/ │ └── test/ ├── dataset.yaml # 训练配置文件 └── classes.txt # no_pain, mild_pain, severe_pain

classes.txt内容:

no_pain mild_pain severe_pain

每张图的标注txt格式长这样:

2 0.5123 0.4187 0.2234 0.3365 0 0.6812 0.5245 0.2012 0.3189

每行依次是:类别id、归一化中心点x、归一化中心点y、归一化框宽、归一化框高。dataset.yaml内容:

path: /path/to/pain-dataset train: images/train val: images/val test: images/test names: 0: no_pain 1: mild_pain 2: severe_pain

3. YOLO训练实战:模型选型、超参设置与效果复盘

数据准备好之后,训练过程反而是最顺的部分。这里我把完整的配置和调整思路写出来,包括为什么要这么设。

3.1 训练配置与命令

我们最终用YOLOv8s作为主模型,训练命令如下:

yolo detect train \ data=./dataset.yaml \ model=yolov8s.pt \ imgsz=640 \ epochs=300 \ batch=16 \ lr0=0.01 \ device=0 \ patience=50 \ project=runs/pain \ name=exp_pain_yolov8s

几个关键取舍:

  • 选YOLOv8s而不是n:n在边缘设备上确实更快,但我们早期用n在验证集上的mAP50只有0.73不到,加一档到s后提升到0.81,推理速度依然能跑30fps,这档性价比最高。如果你部署的是CPU盒子,可能还得退回n或者用TensorRT量化,可以在s训练好后做剪枝蒸馏,而不是直接拿n从零训;
  • imgsz用640:原始图像里有不少病房全景,人脸占比不大,如果缩到416,小脸区域的疼痛细节会丢很多。640是目前速度和精度的平衡点;
  • epochs设300,patience=50:医疗数据样本量不大,模型通常到200-250轮才会充分收敛。设早停是防止过拟合。

3.2 训练中额外做的两件事

我在训练时还额外开了两个开关。第一是mosaic和mixup增强,虽然外界对YOLOv8自带增强的争议不少,但在样本量只有2200张的情况下,不做增强过拟合会非常快。第二是给mild_pain类别单独加了class weights,因为轻度疼痛是三者里最难学的类别,也是标注一致性最差的类别,让它多受点监督信号,能显著压制"中度样本学成wave"的情况。

另外我强烈建议在第一次训练时只跑50轮,用一小批数据走通整个pipeline,检查loss曲线和标注格式有没有问题。我们就有一次labels目录里混入了空txt,导致训练时loss反复横跳,排查了两个小时。小步快跑能省很多这种事。

3.3 测试集上的效果复盘

在保持测试集完全隔离的前提下,YOLOv8s训练300轮的结果如下:

类别PrecisionRecallmAP50mAP50-95
no_pain0.910.900.940.68
mild_pain0.620.570.650.41
severe_pain0.840.820.890.62
全部0.790.760.810.55

这个结果基本符合预期:severe_pain和no_pain好识别,mild_pain是最难啃的骨头。重度疼痛的面部动作幅度大,特征明显;无痛的样本通常表情放松,也好学。轻度疼痛介于两者之间,很多样本在"有点不舒服"和"还挺疼"之间摇摆,模型和人的判断都容易犹豫。

4. 疼痛检测的难点和踩坑记录:几个把模型搞崩的细节

这一节全是实战里踩出来的,我也想重点提醒在这个方向做数据集的同行:训练指标好看不代表模型能用,真实场景里各种"非典型疼痛"才是坑。

4.1 疼痛表情和日常表情的边界模糊

痛的表情在面部动作上与"眯眼、皱眉、用力、憋笑"非常接近。尤其是术后恢复期的患者,躺久了脸上是"僵住"的状态,肌肉收紧但未必在疼,模型很容易打成轻度疼痛。我们做了个小实验:拿一批风吹日晒的户外劳动者照片(长期眯眼导致眼周细纹)去测试,误报率高得离谱。

解决这种问题没有捷径,只能在数据里增加"容易混淆"的负样本。我后期专门让标注员从病房视频里截取皱眉但未疼的帧,给它们打上no_pain。这些困难负样本对抑制误报的作用比单纯加普通no_pain大得多。

4.2 机位角度和遮挡带来的分布漂移

实验室数据几乎都是正面平视视角,但病房摄像头通常装在床头斜上方,病人躺卧时会形成俯视角度,甚至半张脸压进枕头,眼睛区域完全被挡。第一次用测试集以外的新病房视频验证时,mAP掉了十几个点,全是因为机位变化。

我后面重新补充采集了俯视、侧卧、部分遮挡三类场景的数据,数量大概占新增数据的35%。这条经验后来成了团队铁律:任何采集都要覆盖至少3种视角、2种光照条件,否则模型只在固定机位上"认得疼痛"。

4.3 标注者之间的不一致比想象中严重

我们对两个资深标注员的同一批200张图做了交叉验证,发现mild_pain标签的一致率只有68%,而no_pain和severe_pain的一致率都在85%以上。也就是说,模型在mild_pain上的低分,有一部分根本不是模型弱,而是真值本身就飘。

应对办法是建立三方仲裁机制:两个标注员结果不一致的图,交给临床顾问做最终裁决;如果顾问也拿不准,就弃标。同时每周抽30张图做标注员间的Kappa一致性检查,低于阈值的图打回重标。这套管理流程虽然增加工时,但数据集的置信度明显变高。

4.4 真实临床数据泄露隐私的伦理坑

这个坑和模型无关,但处理不当整个项目会被叫停。我们一开始从医院拿了一批监控视频用来"内部测试",后来评审发现这一批视频既没有走伦理流程、也没有在数据集里标明使用范围。血液检测不让用,表情影像当然也不能随便用。必须提醒所有做医疗健康数据集的人:人脸图像属于敏感个人信息,哪怕做了模糊处理,也要签署明确授权协议,开源数据集时要标注授权的边界,例如"仅限非商业研究"或"禁止再识别"。

5. 2200张数据集的落地场景与扩展方向

有了这个数据集和训练好的模型,后面能做的事情其实还挺多的,这里给几个我觉得比较实际的落地方向和扩展思路。

5.1 直接能做的三个场景

  • 住院患者疼痛自动预警:对术后病房实时画面做检测,当某人脸上出现重度疼痛标签且持续时间超过10秒,推送提醒给护士站,让值班护士优先处理;
  • 康复训练强度反馈:在理疗师指导下,用YOLO实时识别患者训练时的疼痛表情,辅助判断当前动作强度是否过大,这样理疗师不用一直盯着患者的脸,能把注意力放在动作标准性上;
  • 远程问诊辅助评估:线上问诊时患者开摄像头描述病情,系统可以辅助记录其表情变化,给医生一个客观参考——当然这只能辅助,不能替代医生判断。

我自己比较看好的是康复训练场景,因为它的机位可控、监控对象配合度高、不存在隐私争议问题,反而比病房这个严肃场景更容易先落地。

5.2 从静态单帧走向时序判断

单帧检测有一个先天不足:疼痛是一个连续过程,而且表情会出现瞬间的"夸张表情"假阳性。比如患者突然咳嗽,脸部会瞬间扭曲,单帧检测会当成重度疼痛报警。

我验证过两套增强方案。第一套是YOLO检测后接时序投票器:连续15帧中,如果重度疼痛被判定的帧数超过一半才发警报。这套方案零训练成本,误报率能降40%左右。第二套是用YOLO框作为输入特征,接一个LSTM或TemporalConvolution做序列分类:效果上限更高,但训练数据需要换成视频序列形式,标注成本直线上升。对大多数团队,我建议先用投票器,跑稳了再上序列模型。

5.3 下一步规划:补弱项、扩标签

这个数据集目前最大的短板有两个。其一是真实临床场景的样本比例还是偏低,模拟表演出来的疼痛和真实疼痛之间存在"表演痕迹",下一步计划在更多合作医院采集真实术后疼痛帧。其二是只有面部信息,没有结合语音、姿态等辅助信号,其实疼痛在蜷缩身体、托扶患处这些动作上表现得比面部更明显。我们准备先在现有数据集上补充"身体疼痛姿态"类别,把检测目标从人脸上扩到"人的上半身+表情"两个维度。

另外计划把疼痛分级从三档扩展到五档,对应NRS评分的0-2、3-4、5-6、7-10区间,让模型在临床上更有指导价值。当然这必然带来标注成本的提升,具体怎么平衡,还在等新数据到位后做一轮评估。

回到最开始的问题:"AI能不能自动判断病人疼不疼?"用2200张YOLO数据集训练出的模型已经能给出一个不错的参考。它替代不了护士的床边问诊,也替代不了患者自己的主观描述,但它可以做一件人做不到的事——每时每刻都在看,每分每秒都在判断。这大概就是医疗健康数据集的真正价值所在。对正在做类似项目的人,我的建议就一句话:先把标注规范和授权协议搞明白,再谈模型精度,这两件事做好了,后面的YOLO训练反而是最简单的一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询