疲劳检测模型mAP上不去?先清理训练集里的无标注图片
简介这份疲劳驾驶监测数据集面向深度学习与图像识别方向的初学者及课程实践者用于搭建以打哈欠张嘴、闭眼状态为疲劳判定的入门级训练项目。压缩包共约2000个文件以2915张jpg图像和2914个xml标注文件为主图像用于模型输入xml提供目标框与类别信息整体约256.28MB可直接用于目标检测或分类任务的练习。需要留意的是原数据集中存在图片与标注不匹配的情况资源描述中附有一段Python脚本借助os与shutil遍历JPEGImages与Annotations目录按文件名比对后删除缺失对应xml的图片帮助使用者先完成数据清洗再投入训练。目前已有4742人学习下载适合希望快速上手疲劳状态识别、熟悉数据整理与标注校验流程的读者参考实践。1. 训练集里混进“无标注图片”为什么你的疲劳检测模型 mAP 死活上不去跑疲劳驾驶检测的新手项目最容易翻车的不是模型结构而是数据本身。你兴冲冲把Dataset/dataset/JPEGImages里的图片和Annotations里的 XML 一股脑喂给 YOLO 或 Faster R-CNN训练日志看着 loss 在降结果验证时 mAP 卡在 0.3 上不去可视化一看框全歪了——这种“玄学”十有八九是图片和标注对不上号。这个数据集里 1429.xml、894.xml、1062.xml 这些标注文件对应的图片和 JPEGImages 目录下的实际图片并不是一一对应的有相当一部分图片根本没有同名 XML。对做图像识别和深度学习的人来说这种脏数据不清理后面调参调到天亮都是白费。下面按“现象→根因→解决”的顺序把这类故障拆开讲清楚新手能照着命令跑熟手能看到排查思路。2. 图片与标注数量对不上从报错到定位的完整链路2.1 先看现象训练不报错但结果就是不对最迷惑人的地方在于图片和标注不匹配时大部分框架不会直接抛异常。你os.listdir两个目录打印长度发现 JPEGImages 有 2000 多张图Annotations 只有 1400 多个 XML差了 600 左右。但训练脚本照样能跑因为 DataLoader 只读它找到的标注找不到标注的图片要么被静默跳过要么被当成负样本。于是你看到的现象是训练集 loss 正常下降验证集指标却很低推理时有些明明有人的图检测不出框。更坑的是如果你用的是某些自动划分训练/验证集的脚本它按图片列表切分切完之后验证集里混进一堆无标注图评估结果直接失真。我一般会先做一次“对账”把两个目录的文件名去掉扩展名取交集和差集差集就是问题所在。这一步不需要任何深度学习框架纯 Python 就能跑。import os jpeg_dir Dataset/dataset/JPEGImages anno_dir Dataset/dataset/Annotations # 去掉扩展名只留文件名主体 jpeg_names {os.path.splitext(f)[0] for f in os.listdir(jpeg_dir)} anno_names {os.path.splitext(f)[0] for f in os.listdir(anno_dir)} only_jpeg jpeg_names - anno_names # 有图无标注 only_anno anno_names - jpeg_names # 有标注无图 print(f图片总数: {len(jpeg_names)}) print(f标注总数: {len(anno_names)}) print(f有图无标注: {len(only_jpeg)}) print(f有标注无图: {len(only_anno)}) print(前10个无标注图片:, list(only_jpeg)[:10])这段代码用集合差集做对账os.path.splitext去掉.jpg和.xml后缀保证比较的是同一维度的文件名。only_jpeg就是需要清理的目标only_anno则说明标注文件多余通常可以忽略但如果框架要求严格一一对应也要一并处理。跑完你会看到类似“有图无标注: 600”的输出这就是 mAP 上不去的直接原因。2.2 根因分析为什么数据集会缺标注这类数据集在采集和标注阶段通常是两条流水线图片从视频抽帧或爬取标注由另一批人用 labelImg 之类的工具画框。中间任何一环出问题都会导致不匹配。常见原因有三种一是抽帧后部分图片被人工筛掉模糊、重复、无人脸但标注文件没同步删除二是标注人员中途换工具或换命名规则导致文件名主体不一致比如图片叫1429.jpg标注却存成1429_1.xml三是数据集打包时只拷贝了部分 XML漏拷了。这个数据集属于第一种情况居多——原数据集中存在图片数据与标注数据不匹配的问题需要把不匹配的这部分数据删除。注意不要试图给无标注图片“补标注”。疲劳驾驶检测的标注成本很高闭眼、打哈欠这些状态需要专业判断补标注的代价远大于直接删除。而且如果图片本身质量差模糊、遮挡严重补了也是噪声。所以正确做法是以标注文件为准删除没有对应 XML 的图片。2.3 解决步骤用脚本批量清理无标注图片摘要里给出的参考代码思路是对的但有几个细节容易踩坑。先看原始代码import os, shutil jpeg Dataset/dataset/JPEGImages jpeg_list os.listdir(jpeg) anno Dataset/dataset/Annotations anno_list os.listdir(anno) for pic in jpeg_list: name pic.split(.)[0] anno_name name .xml print(anno_name) if anno_name not in anno_list: os.remove(os.path.join(jpeg, pic))这段代码能跑但直接os.remove是危险操作——一旦路径写错或判断逻辑有 bug图片就没了没有后悔药。我一般会改成“先移动到备份目录确认无误再删”。另外pic.split(.)[0]遇到文件名里带多个点的情况会出错比如1429.backup.jpg更稳妥的是os.path.splitext。改进版如下import os import shutil jpeg_dir Dataset/dataset/JPEGImages anno_dir Dataset/dataset/Annotations backup_dir Dataset/dataset/JPEGImages_orphan # 备份目录 os.makedirs(backup_dir, exist_okTrue) anno_set set(os.listdir(anno_dir)) moved 0 for pic in os.listdir(jpeg_dir): name, ext os.path.splitext(pic) if ext.lower() not in (.jpg, .jpeg, .png): continue anno_name name .xml if anno_name not in anno_set: src os.path.join(jpeg_dir, pic) dst os.path.join(backup_dir, pic) shutil.move(src, dst) # 先移走不直接删 moved 1 print(f共移出 {moved} 张无标注图片到 {backup_dir})参数说明anno_set用集合而不是列表查找复杂度从 O(n) 降到 O(1)几千张图差别不大但几万张时很明显。ext.lower()兼容.JPG大写后缀。shutil.move跨磁盘也能用比os.rename稳。跑完之后JPEGImages 里的图片数应该和 Annotations 的 XML 数一致或接近因为可能还有“有标注无图”的情况那部分不影响训练。确认训练正常后再手动删除备份目录。提示清理前先对整个Dataset目录打个压缩包或者至少确认备份目录可写。数据清理是不可逆操作别问我怎么知道的。3. 清理后训练仍报错标注文件本身格式与内容的坑3.1 XML 解析失败ParseError: not well-formed图片和标注数量对齐了不代表标注内容没问题。跑训练时可能遇到xml.etree.ElementTree.ParseError: not well-formed (invalid token)或者 labelImg 打开某个 XML 直接闪退。这类报错通常是 XML 文件在生成时被截断、编码不对或者手动改过导致标签不闭合。疲劳驾驶数据集的标注里object节点包含name如 closed_eye、open_mouth、bndbox等任何一个标签缺失都会让解析器罢工。定位方法是批量校验所有 XML 的可解析性import os import xml.etree.ElementTree as ET anno_dir Dataset/dataset/Annotations bad_files [] for f in os.listdir(anno_dir): if not f.endswith(.xml): continue path os.path.join(anno_dir, f) try: ET.parse(path) except ET.ParseError as e: bad_files.append((f, str(e))) print(f损坏的 XML 数量: {len(bad_files)}) for name, err in bad_files[:20]: print(name, -, err)ET.parse会完整读取并解析 XML遇到格式错误直接抛异常。把bad_files里的文件挑出来用文本编辑器看是不是标签没闭合或者用xmllint --noout file.xml检查。如果只是个别文件损坏直接删除对应图片和 XML如果大面积损坏说明数据集打包时就有问题需要重新获取。3.2 标注内容为空或框越界还有一种更隐蔽的情况XML 能解析但object数量为 0或者bndbox的坐标超出了图片实际尺寸。前者会让图片变成“背景图”后者会让训练时框到图像外面计算 loss 时产生异常梯度。我一般会加一段校验统计每张图的 object 数和框坐标范围import os import xml.etree.ElementTree as ET from PIL import Image anno_dir Dataset/dataset/Annotations jpeg_dir Dataset/dataset/JPEGImages empty_objs [] out_of_bound [] for f in os.listdir(anno_dir): if not f.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, f)) root tree.getroot() objs root.findall(object) if len(objs) 0: empty_objs.append(f) continue # 读取对应图片尺寸 img_name os.path.splitext(f)[0] .jpg img_path os.path.join(jpeg_dir, img_name) if not os.path.exists(img_path): continue w, h Image.open(img_path).size for obj in objs: bnd obj.find(bndbox) xmin int(bnd.find(xmin).text) ymin int(bnd.find(ymin).text) xmax int(bnd.find(xmax).text) ymax int(bnd.find(ymax).text) if xmin 0 or ymin 0 or xmax w or ymax h: out_of_bound.append((f, xmin, ymin, xmax, ymax, w, h)) break print(空标注文件:, len(empty_objs)) print(框越界文件:, len(out_of_bound))empty_objs里的文件建议连同图片一起删掉因为无目标的图对检测训练没有正贡献。out_of_bound里的框需要修正或删除该 object如果越界严重比如 xmax 是图片宽度的两倍说明标注时用错了坐标系整份标注都不可信。3.3 类别名不统一closed_eye和close_eye混用疲劳驾驶检测通常把“闭眼”“打哈欠”作为疲劳标准但标注人员可能写成closed_eye、close_eye、eyes_closed等多种形式。训练时框架会按name文本聚类导致同一类被拆成多个类模型学不到东西。用一条命令统计所有类别名import os import xml.etree.ElementTree as ET from collections import Counter anno_dir Dataset/dataset/Annotations counter Counter() for f in os.listdir(anno_dir): if not f.endswith(.xml): continue root ET.parse(os.path.join(anno_dir, f)).getroot() for obj in root.findall(object): name obj.find(name).text.strip() counter[name] 1 for name, cnt in counter.most_common(): print(f{name}: {cnt})如果看到closed_eye和close_eye同时出现就需要统一替换。替换前先备份然后用脚本批量改 XML 文本或者用 labelImg 的“更改类别名”功能。统一后的类别建议控制在 2~3 个closed_eye、open_mouth必要时加normal。4. 避坑与常见问题数据清理阶段最容易翻车的 5 个操作4.1 直接os.remove没有备份现象脚本跑完发现删多了或者路径变量写错把整个 JPEGImages 清空。原因os.remove不可逆且没有二次确认。解决一律用shutil.move移到备份目录训练验证通过后再手动删。备份目录不要放在 JPEGImages 内部避免被后续os.listdir再次扫到。4.2 用split(.)[0]处理文件名现象文件名1429.v2.jpg被截成1429和1429.xml误匹配导致该删的没删。原因split(.)按所有点分割取第一段。解决用os.path.splitext只去掉最后一个扩展名得到1429.v2再拼.xml比较。4.3 忽略大小写和扩展名差异现象图片是.JPG大写标注是.xml小写脚本判断1429.JPG对应1429.xml时因为扩展名没统一而误判。原因os.listdir返回原始文件名大小写敏感。解决比较前统一lower()并且只处理图片扩展名白名单。4.4 清理后没有重新划分训练/验证集现象清理前按图片列表划分了 train/val清理后验证集里还有已删除图片的引用训练时报FileNotFoundError。原因划分文件是静态的数据变了没同步。解决清理完成后重新跑一遍划分脚本或者用符号链接方式让划分动态读取当前文件列表。4.5 把“有标注无图”的 XML 也删了现象发现only_anno有几百个一激动全删了结果其中一部分是图片扩展名不是.jpg而是.png被误判为“无图”。原因只按.jpg找对应图片。解决检查图片目录时把所有图片扩展名都纳入.jpg、.jpeg、.png、.bmp都要试一遍确认真的没有对应图片再删 XML。5. 快速定位数据问题的排查顺序与验证方法遇到疲劳驾驶检测训练效果差不要先调模型按下面这个顺序过一遍数据八成问题在前三步就能暴露。排查步骤命令/操作判断标准数量对账集合差集统计图片数 ≈ XML 数差值超过 5% 就要清理XML 可解析ET.parse批量校验损坏文件数为 0空标注/越界遍历 object 和 bndbox空标注文件删除越界框修正类别统一Counter统计 name类别名唯一无同义混用图片可读Image.open批量打开无UnidentifiedImageError这个顺序的逻辑是先保证“有图有标注”再保证“标注能用”最后保证“图片能读”。每一步都有对应的脚本跑一遍几分钟比盲目调参高效得多。验证清理是否彻底最直接的方法是重新跑一次训练看第一个 epoch 的 loss 是否比清理前更低、更平稳。如果清理前 loss 震荡严重清理后明显收敛说明无标注图片确实在干扰。另一个验证点是推理可视化随便挑 20 张验证集图片画上预测框看有没有“该有框的地方没框”。如果清理前有大量漏检清理后漏检减少说明数据对账起了作用。从那以后我每次拿到新数据集第一件事不是写模型而是先跑一遍数量对账和 XML 校验把数据问题挡在训练之前。这个习惯帮我省下了无数个调参的夜晚。希望这份排查思路能帮到你数据集里的清理脚本和校验代码可以直接拿去用照着跑一遍你的疲劳检测项目就能少走一段弯路。本文还有配套的精品资源点击获取

相关新闻

边缘智能数据库sfsDb:时序数据与AI推理融合的实践解析

边缘智能数据库sfsDb:时序数据与AI推理融合的实践解析

sfsDb这个名字,很多人第一次听以为又是某个嵌入式KV存储的变种。但如果我们把视角拉到边缘智能这个赛道里,就会发现它想做的事情其实更大:它瞄的不是某一个具体设备上的数据管理问题,而是整个边缘计算体系下面,从传感器…

2026/10/19 0:41:06 阅读更多 →
REA实战:从截图到前端代码的AI逆向工程全解析

REA实战:从截图到前端代码的AI逆向工程全解析

前段时间社区里冒出一类很火的项目,名字都挺直白:REA,全称 Reverse Engineer Anything,翻译过来就是“逆向工程一切”。说人话:你把一张界面截图丢进去,它能给你吐出来一整套可编辑的前端代码。这个方向我关…

2026/10/19 0:41:06 阅读更多 →
过程和函数:用 TaoToken 统一 Key 调试存储过程与游标

过程和函数:用 TaoToken 统一 Key 调试存储过程与游标

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:40:06 阅读更多 →

最新新闻

用UiPath自动化禁用Windows更新:批量节点策略治理与踩坑实录

用UiPath自动化禁用Windows更新:批量节点策略治理与踩坑实录

某天上午,我打开一批测试节点的事件日志,看到整齐划一的"Windows更新已完成安装,正在重启"。前一天夜里,跑批任务刚跑到一半,机器就重启了,监控平台黄了一串。值班的同事说,这已经不是…

2026/10/19 5:16:15 阅读更多 →
云服务器上部署OpenClaw,打造7×24小时在线的AI私人助手

云服务器上部署OpenClaw,打造7×24小时在线的AI私人助手

你有没有过这种时刻:临时想查个东西,手机解锁、打开App、输入问题、等回复,一整套流程下来,自己都忘了刚才想干什么。我折腾OpenClaw之前,总觉得大模型挺好用,但每次都缺一个随手能喊出来的入口。后来我换了…

2026/10/19 5:16:15 阅读更多 →
人形机器人专利池的精准补位战略:从技术地图到产业卡位

人形机器人专利池的精准补位战略:从技术地图到产业卡位

这些年做机器人硬件和智能算法的朋友应该都有一个共同感受:人形机器人这个词,已经从实验室里的概念验证,变成了产业界真金白银下注的确定性赛道。上个月我在某产业论坛待了三天,被问到最多的问题不是“能不能造出来”,…

2026/10/19 5:16:15 阅读更多 →
AirVLA技术拆解:端到端VLA模型如何实现桌面机械臂空中抓取与导航

AirVLA技术拆解:端到端VLA模型如何实现桌面机械臂空中抓取与导航

最近具身智能圈子里放出了一个叫 AirVLA 的方案,名字听起来很“大模型”,但核心一句话就能讲明白:它让桌面机械臂的视觉-语言-行动(VLA)模型从“只会平面抓取”升级到了“能在三维空间里完成空中…

2026/10/19 5:16:15 阅读更多 →
线程回收、分离属性、互斥与同步:NEXT 2项目实战拆解

线程回收、分离属性、互斥与同步:NEXT 2项目实战拆解

接手项目NEXT 2的时候,我第一反应是:这不就是一个普通的并发任务模块吗?无非开几个线程、跑几个队列。真正动手才发现,线程回收、分离属性、互斥、同步这四件事,任何一个环节没想清楚,程序就会以各种奇怪的…

2026/10/19 5:16:15 阅读更多 →
gPINN求解多陡峭界面Allen-Cahn方程:Python实现与训练策略

gPINN求解多陡峭界面Allen-Cahn方程:Python实现与训练策略

我先从一个很现实的场景起头:对一个 ε 已经小到 0.02 的一维 Allen-Cahn 方程,初始条件里同时存在两个甚至四个陡峭过渡层,标准 PINN 训练到中途,损失曲线虽然还在降,但画出来的解已经不像界面,更像一段被…

2026/10/19 5:15:15 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →