
简介这套面部表情检测数据集专为YOLO系列目标检测算法设计包含愤怒、悲哀、中立、幸福四类常见表情的200张标注图像适合用于课堂实践、算法对比或小型项目验证。全部数据已按训练与验证需求划分妥当并附带按YOLO官方格式编写的data.yaml配置文件可直接配合YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLOv11等主流版本进行训练与测试免去手动配置数据集的麻烦。压缩包共601个文件体积仅4.25MB其中200张jpg原图、200个txt标签和200个xml标签分别存储分别对应YOLO格式与VOC格式。txt标签使用 x_center y_center 的归一化坐标表示方便快速读取xml标签则完全符合VOC标准方便偏爱LabelImg等可视化工具的用户直接使用。目前已有136人学习下载对于刚接触YOLO目标检测的初学者来说是一份轻量且可直接运行的入门数据集也可作为模型效果对比的基准数据。 我拿到这个压缩包的时候第一反应是“又是个小打小闹的demo级数据”但解压完仔细翻了翻标签发现事情没那么简单。200张带标注的人脸表情图四个类别愤怒、悲哀、中立、幸福配上YOLO格式的txt标签这其实是一个非常典型的“用目标检测思路做表情识别”的小型实战项目。虽然体量不大但把这条链路完整跑通从数据校验、格式整理、模型训练到结果分析你能把这套流程迁移到任何你手头的小规模检测任务上。这篇就围绕这个数据集完整走一遍YOLO表情识别的流程把能踩的坑和能省的时间都告诉你。1. 先盘清楚这200张图到底能干什么1.1 标签结构与文件解析解压之后通常你会看到类似这样的目录├── images │ ├── train │ │ ├── angry_001.jpg │ │ ├── sad_001.jpg │ │ └── ... │ └── val │ ├── angry_010.jpg │ └── ... ├── labels │ ├── train │ │ ├── angry_001.txt │ │ ├── sad_001.txt │ │ └── ... │ └── val │ └── ... └── data.yaml每个txt标签的内容长这样0 0.4823 0.5217 0.3854 0.6112这就是YOLO格式的核心每行代表一个目标框五个数字分别是类别ID、归一化中心x、归一化中心y、归一化宽度、归一化高度。坐标全部除以图像宽高做了归一化所以不管原图是1920宽还是640宽标签文件都是0到1之间的小数。这个数据集里类别ID的映射可能是0: angry, 1: sad, 2: neutral, 3: happy具体看压缩包里的classes.txt或data.yaml。动手前务必先看这个文件别上来就训练类别顺序错了模型就白跑了。1.2 200张图的算力账与过拟合风险先泼盆冷水200张图训练YOLO从学术标准看是严重不够的。YOLO在COCO上可是用12万张图打底。但“不够”不等于“不能跑”得看你想要什么。如果目标是拿到一个能直接上线的表情识别接口那这200张图远远不够。但如果目标是练手、走通YOLO训练全流程、理解数据格式和训练机制那200张图完全够你跑出loss下降的曲线甚至能训出一个在小样本上mAP还不错的模型——只要你做对后面几件事。假设四类表情在200张图里分布均匀每类也就50张左右。五折交叉验证的话每次验证集只有10张图这个数据量下任何指标的波动都会非常大。所以我的建议很直接用留出法按8:2切分训练集和验证集不要用交叉验证。训练集160张验证集40张结果有参考性但只针对这个小数据集本身。模型用最小的YOLOv8n别用yolov8l或者x200张图喂大模型纯属浪费时间。提示小数据集的最大威胁是过拟合。模型不会去学“表情的共性特征”而会去背“这50张图的像素记忆”。后面我会说怎么通过数据增强和早停机制来缓解这个问题。2. 训练前先做三件事校验、划分、清洗2.1 把zip变成YOLO能吃的标准结构不管你这个zip包的内部结构是什么样的统一整理成下面这个标准结构最稳妥dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml写个小脚本一键整理别手动拖文件夹import os import shutil def organize_dataset(src_dir, dest_dir, split_ratio0.8, seed42): random.seed(seed) os.makedirs(f{dest_dir}/images/train, exist_okTrue) os.makedirs(f{dest_dir}/images/val, exist_okTrue) os.makedirs(f{dest_dir}/labels/train, exist_okTrue) os.makedirs(f{dest_dir}/labels/val, exist_okTrue) # 假设原始图片在 src_dir/images 下标签在 src_dir/labels 下 img_files [f for f in os.listdir(f{src_dir}/images) if f.endswith((.jpg, .png))] random.shuffle(img_files) train_files img_files[:int(len(img_files) * split_ratio)] val_files img_files[int(len(img_files) * split_ratio):] for f in train_files: shutil.copy(f{src_dir}/images/{f}, f{dest_dir}/images/train/{f}) label f.replace(.jpg, .txt).replace(.png, .txt) if os.path.exists(f{src_dir}/labels/{label}): shutil.copy(f{src_dir}/labels/{label}, f{dest_dir}/labels/train/{label}) for f in val_files: shutil.copy(f{src_dir}/images/{f}, f{dest_dir}/images/val/{f}) label f.replace(.jpg, .txt).replace(.png, .txt) if os.path.exists(f{src_dir}/labels/{label}): shutil.copy(f{src_dir}/labels/{label}, f{dest_dir}/labels/val/{label})2.2 三步检查保证标签没乱七八糟这是整个流程里最不起眼但最容易翻车的一步。压缩包的来源要么是爬虫抓的要么是手工标了一部分然后程序生成的质量参差不齐。我每次拿新数据都会跑这三步第一步查空标签。有些图片没有对应txt或者txt是空的。训练时这些图会被YOLO自动跳过但这意味着你的有效样本又少了。import os def check_empty_labels(label_dir): empty_files [] for f in os.listdir(label_dir): if f.endswith(.txt): filepath os.path.join(label_dir, f) if os.path.getsize(filepath) 0: empty_files.append(f) return empty_files empty check_empty_labels(dataset/labels/train) print(f空标签文件数量: {len(empty)})第二步查坐标越界。YOLO标签的五个数值都必须在0到1之间。如果出现负数或大于1的值YOLO会直接报错或者训练时崩掉。这通常是标注工具的bug或者标签格式转换时没归一化干净。def check_out_of_bounds(label_dir): bad_files [] for f in os.listdir(label_dir): if f.endswith(.txt): with open(os.path.join(label_dir, f)) as fp: for line in fp: parts line.strip().split() if len(parts) ! 5: bad_files.append(f{f} - 列数不对) break try: nums list(map(float, parts)) except: bad_files.append(f{f} - 有非数字字符) break if any(n 0 or n 1 for n in nums[1:]): bad_files.append(f{f} - 坐标越界) break return bad_files bad check_out_of_bounds(dataset/labels/train) for item in bad: print(item)第三步查类别ID是否超出预设。如果你的data.yaml定义了4个类别0到3但某个txt里出现了4 0.5 0.5 ...训练时会报index越界错误非常烦人。这三个查完再做一次shutil.rmtree把不合格的样本剔除掉保持图片和标签一一对应。很多新手栽在这里跑半天训练loss曲线是平的一看日志发现每张图都被当成背景样本了因为标签压根没被读进来。2.3 数据增强策略怎么把160张图“变”成几百张小数据集必须靠增强但增强要克制。YOLOv8自带的在线增强其实已经很强了你需要做的只是把某些参数调大一点。我自己测试下来针对这个小数据集比较合理的增强配置是参数推荐值说明hsv_h0.02色调轻微扰动即可别太重hsv_s0.5饱和度扰动模拟不同光照hsv_v0.5亮度扰动很重要degrees10小角度旋转人脸不会倒着出现translate0.1少量平移scale0.4缩放范围控制在40%以内fliplr0.5水平翻转注意这会让标签失效等等fliplr这里要注意水平翻转后的标签坐标不会自动调整吗YOLO做fliplr增强时标签的坐标也会同步做水平翻转具体来说就是中心x坐标变成1 - x_center。这个逻辑在ultralytics代码里已经处理好了你不需要自己管。但对表情识别来说水平翻转完全不影响类别语义——愤怒翻转后还是愤怒。不要盲目开mosaic1.0200张图做mosaic增强虽然能丰富背景但对人脸这个小目标来说四张图拼一起可能把脸切得面目全非反而引入噪音。实测下来mosaic0.5左右训练更稳定。3. 环境配置与YOLOv8训练实战3.1 环境准备显卡、CUDA、无GPU方案训练YOLO需要什么配置我现在的建议非常务实有NVIDIA显卡哪怕是GTX 1660就用GPU训练YOLOv8n在6GB显存下完全没压力。没有NVIDIA显卡用CPU也能跑只是慢。YOLOv8n在CPU上训练200张图epochs100大概需要20到40分钟完全能接受。AMD显卡用户别折腾ROCm了直接装CPU版PyTorch就行小样本场景差别不大。创建虚拟环境、装依赖conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics torch torchvision这就是全部依赖了。ultralytics这个pip包自带YOLOv8的所有实现不用另外clone仓库。提示如果你用CPU训练在pip install torch时选CPU版本能省几个GB的下载量。具体命令是pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu。3.2 编写data.yaml与训练命令data.yaml里面的内容很简单path: /absolute/path/to/dataset train: images/train val: images/val nc: 4 names: [angry, sad, neutral, happy]然后就可以启动了yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch8 \ patience20 \ projectexp \ namefer_exp \ pretrainedTrue \ optimizerAdamW \ lr00.001这里每个参数都有讲究pretrainedTrue必须用COCO预训练权重做迁移学习不要从零开始训。200张图从零训收敛速度和精度都是灾难。patience20早停机制。如果连续20个epoch验证集mAP没有提升就自动停止省时间。imgsz640YOLOv8默认输入尺寸。你数据集里的图如果小于640ultralytics会自动resize。我个人建议如果原始图像在500像素以下把imgsz降到416或480会更快。batch8显存不够就降到4CPU训练可以设到2或4。lr00.001很多人忽略学习率。用预训练权重做微调时学习率太大会破坏已经学好的底层特征0.001是微调场景的通用稳妥选择。3.3 训练过程解读这几条曲线要盯住训练开始之后不要干等。观察命令行输出的几个关键指标box_loss回归框损失。正常情况是稳步下降。cls_loss分类损失。你是在做表情分类这个值最重要。dfl_loss分布焦点损失。和box质量相关。mAP50、mAP50-95核心精度指标。mAP50是IoU阈值0.5下的平均精度这里重点关注它。如果一切正常你会看到loss曲线在前20个epoch快速下降然后趋于平缓。此时如果patience20模型大概率在50到70个epoch之间自动停止因为验证集精度已经饱和了。跑完以后在exp/fer_exp/weights/目录下会有两个文件best.pt验证集表现最好的权重用它做推理。last.pt最后一个epoch的权重一般用不上。4. 训练结果分析与推理实测4.1 四种表情模型到底学没学到特征200张图训出来的模型你拿着第一批验证集图去推理大概率能看到框画得还可以但置信度不会太高可能0.5到0.8之间飘。这正常小样本模型就是这样知道“脸在哪”但“表情”这个细粒度特征的辨识力有限。我用类似规模的数据实际跑过四种表情里“中立”和“悲哀”极易混淆因为这两个表情本身的特征边界就模糊。相反“幸福”的召回率通常最高笑脸的特征嘴角上扬在目标检测特征层面足够有辨识度。“愤怒”容易和“悲哀”混眉毛形态是关键区分点。如果验证集上某个类的AP特别低去翻一下训练准备阶段的数据平衡是不是这个类的样本数量明显少于其他类如果是可以给损失函数加类别权重或者简单粗暴复制几份该类的样本做离线增强让模型“多看几眼”这个类。4.2 推理实测用训练好的权重跑一张新图推理代码很简单from ultralytics import YOLO model YOLO(exp/fer_exp/weights/best.pt) results model.predict( sourcetest_images/angry_new.jpg, conf0.4, showTrue # 本地跑的时候用服务器上就别开了 ) for r in results: for box in r.boxes: cls_id int(box.cls[0].item()) conf float(box.conf[0].item()) xyxy box.xyxy[0].tolist() print(f类别ID: {cls_id}, 置信度: {conf:.3f}, 框: {xyxy})conf0.4是置信度阈值低于这个值的框会被过滤掉。小样本模型建议阈值调低到0.35甚至0.3否则可能什么都框不出来。4.3 训练过程常见报错与排查速查表实际训练中你会遇到的坑我整理成了一张速查表症状原因解决方案AssertionError: Labels not found路径配置错labels目录没找到检查data.yaml里的path是绝对路径CUDA out of memorybatch太大或显存不够调小batch到4或2imgsz降到480loss持续不降标签全被跳过、学习率过高检查是否空标签降低lr0到0.0001mAP一直为0验证集和训练集分布差异过大、样本太少重新检查数据划分确认测试样本质量推理结果全部是背景框置信度阈值太高模型输出置信度偏低conf调到0.3或检查类别映射训练时某个类完全没框出来这个类样本量太少或标签错误检查数据集中每类样本数量必要时做数据增强还有一些环境和依赖层面的坑AttributeError: NoneType object has no attribute shape通常是图片路径里有中文或特殊字符ultralytics读取失败。把整个数据集路径改成纯英文纯数字。训练进度条卡住不动有些版本的ultralytics搭配老版本PyTorch会出这个问题。升级或锁定版本pip install ultralytics8.2.0 torch2.1.0。CPU训练实在太慢显式指定只用一个进程yolo train ... workers2避免多进程数据加载在CPU上互相争抢资源。5. 从YOLOv8再往前一步这个项目的扩展路径200张图训出来的模型作为一个入门练手项目到这里已经闭环了。但如果你想把表情识别做得更靠谱一点我有几条实际经验分享数据永远是第一优先级。把200张扩到2000张比换任何模型结构都管用。你可以从公开的人脸表情数据集中挑一部分转成YOLO格式合进这个小数据集。格式转换的代码逻辑和上面那个标签检查脚本是同一套思路拿到原数据集的标注可能是VOC xml格式或COCO json格式解析后转成YOLO的txt。把检测和分类拆开。YOLO做端到端检测是有性能代价的它对“人脸在哪里”和“表情是什么”是一起学。更好的工程方案是先用一个成熟的人脸检测模型比如YOLOv8-face变体或retinaface框出人脸然后单独训练一个表情分类器ResNet18或者其他小分类网络对框出的人脸做分类。这种两步走方案在小样本下训练难度更低精度往往更高。注意类别定义的一致性。如果你从不同来源合并数据你会发现“中立”的标注标准在各数据集里差异很大。有的数据集把“轻微微笑”算中立有的算幸福。这种标注标准不一致会让模型学得精神分裂。合并数据前最好抽一批图手动校验一遍。6. 最后分享一点我的实际体会这个数据集跑完之后我最深的一个感受是小数据量训练最大的敌人不是模型不够强而是流程里的脏数据。200张图里我抽检了20张发现有两张标签的框明显偏了几十像素还有一张图片本身是镜像翻转过的。这些数据问题在COCO那个规模的数据集里可能不起眼但在200张的小样本里哪怕一张脏数据都能让验证集mAP掉好几个点。所以如果你手头的数据集也就几百张这个量级踏踏实实把数据清洗这步做扎实比花时间去调YOLO的anchor参数、改损失函数、试各种注意力模块要有用得多。这个流程跑通之后你可以把同一个套路原封不动套到其他小规模目标检测任务上货物空缺检测、二维码定位、桌面物品识别只要把标注数据整理成YOLO格式剩下的训练和推理代码一行都不用改。这正是YOLO这套工具链最香的地方。本文还有配套的精品资源点击获取