
简介YOLOv8算法人脸表情识别训练权重与配套数据集组成一套可直接上手的目标检测训练资源面向计算机视觉开发者和算法学习者适用于人机交互、课堂专注度分析、驾乘安全监测等需要判断人物情绪状态的场景也适合作为相关课题演示和毕业设计的基础材料。数据集已划分train、val、test三部分附带data.yaml配置文件包含anger、happy、sad、surprise四类标签类别定义明确除YOLOv8外YOLOv5、YOLOv7、YOLOv9等算法均可直接加载训练配合txt格式标注能显著减少数据整理与格式转换的时间成本。压缩包共2000个文件、约114.27MB其中含678个jpg图像、602个txt标签、58个yaml文件、134个Python脚本以及C推理示例、PDF说明和HTML预览等文档整体目录层级清晰可按训练、验证、测试和工具模块快速定位。目前已有1434人学习下载既可基于训练权重直接进行表情推理也能借助脚本复现训练流程、微调模型参数在本地环境即可快速验证模型效果是入门和实践YOLO系列表情识别项目的高性价比资料。1. 训练 YOLOv8 算法的人脸表情识别模型最费时间的不是算法而是数据集训练一个 YOLOv8 算法的人脸表情识别模型最费时间的从来不是模型本身而是数据准备。表情图片要筛、标签要一张张核对、train/val/test 比例要重切路径配错一次整个训练流程都得从头跑。这套资源把大部分重复劳动提前做完了四类表情 anger、happy、sad、surprise 的图片和 txt 标签已经按标准 YOLO 格式分好data.yaml 也配好另外包含训练权重和可供参考的推理示例。你拿到手不是看个演示而是可以直接把训练跑起来。它适合刚入门目标检测、不想在数据整理上耗半个月的人。2. 先摸透数据集和 data.yaml三个路径问题决定训练能不能开始我拿到任何下载包第一件事不是解压以后立刻复制到项目里跑而是先看目录和配置文件。这份资源里最关键的其实不是权重而是那份 data.yaml 和它背后的标签目录。如果 data.yaml 里的路径没有处理好后面报错会让你误以为模型或权重出了问题实际上只是路径没对上。2.1 资源包里的文件构成解压之后你会看到一堆代码文件和页面文件我先挑几个和训练直接相关的说。biaoqing_data/ # 我习惯把数据集单独放一个根目录 ├── data.yaml ├── dataset/ │ ├── train/ │ │ ├── images/ │ │ └── labels/ │ ├── valid/ │ │ ├── images/ │ │ └── labels/ │ └── test/ │ ├── images/ │ └── labels/ ├── inference.cpp ├── inference.h ├── main.cpp ├── comments.html ├── source-file.html └── style.cssinference.cpp、inference.h、main.cpp是 C 推理的参考实现骨架常见的做法是加载训练好的权重把图片或视频帧送进去然后拿到类别框和置信度。comments.html、source-file.html和style.css属于展示层内容大概率是结果可视化页面对训练本身不是必需。在某个 labels 目录下可能还会看到一个labels.cache文件这是 ultralytics 在首次读取数据集时自动生成的缓存作用是记录图片和标签的路径让下一次启动速度快一点。如果你调整了数据集内容缓存会失效训练时会重新生成也可以手动删掉这点后面避坑章详细说。2.2 data.yaml 的原始配置和它的坑原始 data.yaml 里写的是上传者机器的 Windows 绝对路径典型长这样train: E:\python_code\dataset\biaoqing_detect_data\train/images val: E:\python_code\dataset\biaoqing_detect_data\valid/images test: E:\python_code\dataset\biaoqing_detect_data\test/images nc: 4 names: - anger - happy - sad - surprise这份配置本身没有问题问题在于换一台机器之后绝对路径几乎不可能还成立。你下载包放在 D 盘它写 E 盘你放在服务器上的/data目录它写 Windows 路径。拷贝到别的机器后直接跑训练几乎必然报train: ... does not exist。我一般会改成相对路径方案让 YOLO 自己基于当前目录解析# 把 path 指向数据集的根目录train/val/test 用相对路径 path: ./dataset train: train/images val: valid/images test: test/images nc: 4 names: 0: anger 1: happy 2: sad 3: surprise注意两件事这里的path是 ultralytics 支持的基础路径字段相对路径就是相对于这个根目录解析的。train 目录写的是train/images因为标签和图片集是分开的val 对应的是valid/images不是val/images两者目录名不一样别想当然去改。另外names我建议写成带索引的字典形式不要用列表。YOLO 后续输出类别 id 时需要靠列表顺序映射到names一旦顺序被改动模型输出的类别语义就全乱了。用字典显式指定0: anger和1: happy可以避免无意中改错顺序。2.3 训练前花五分钟扫描标签txt 标签是 YOLO 的归一化格式每行表示一个目标框0 0.485 0.620 0.292 0.410 1 0.388 0.251 0.185 0.202从左到右分别是 class_id、中心点 x、中心点 y、框宽度、框高度后四项都是 0 到 1 之间的相对坐标。比如第一行表示一个类别为 0 的 anger 表情中心点位于图片宽度 48.5%、高度 62% 的位置框宽占整张图片宽度的 29.2%高占 41%。为了保证数据没有明显问题我会在正式训练前先跑一个小扫描import os from collections import Counter label_dir dataset/train/labels classes [anger, happy, sad, surprise] counts Counter() errors 0 for name in os.listdir(label_dir): if not name.endswith(.txt): continue with open(os.path.join(label_dir, name), r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式错误 {name}: {line.strip()}) errors 1 continue cls int(parts[0]) if cls len(classes): print(f类别越界 {name}: {cls}) errors 1 continue counts[cls] 1 for i, cls_name in enumerate(classes): print(f{cls_name}: {counts[i]} 个框) print(f共发现 {errors} 个异常标签)这段脚本的作用有两个一是确认每个 txt 文件里都是 5 个有效字段二是检查类别编号是否落在 data.yaml 声明的 4 个类之内。经常出现的隐藏问题是某一张图片的标签编号写成了 5而 nc 只有 4训练时会被当成非法类别轻则该图不参与损失计算重则影响最终的 mAP。扫描完没报错再进训练阶段心里就有底了。3. 用 YOLOv8 训练四类表情命令、参数和训练曲线怎么看数据集没问题之后训练环节反而简单因为 YOLOv8 已经把所有繁琐的训练逻辑封装好了。你需要做的只是选择合适的模型大小、把数据路径传进去、启动训练然后观察几条关键曲线是否健康。3.1 环境安装和模型选型环境上我只装一个ultralytics包就够了它会连带把 PyTorch 和必要的依赖装好。推荐用 Python 3.9 到 3.11 之间的版本显存紧张时选 CPU 版 PyTorch 意义不大因为训练阶段还是要靠 GPU 提速。pip install ultralytics接着确认 GPU 和版本python -c import torch; print(torch.cuda.is_available(), torch.__version__)输出True说明当前机器可以走 CUDA 训练如果输出False后面device0会直接报错需要改成devicecpu或者去排查驱动。模型尺寸我建议首选yolov8n.pt。不要觉得 nano 小就效果差对于四类表情识别这种类别较少、目标集中在小尺寸人脸框的任务nano 在 640 输入下的表现通常已经够用。如果标签数量和样本量足够大再换yolov8s.pt或者yolov8m.pt也不迟。wider face 或者自采数据标注的表情图片和 COCO 预训练权重中的泛化特征有很强相关性加载预训练权重能明显加快收敛。3.2 命令行启动训练ultralytics 提供了 CLI 入口最短的命令是这样yolo detect train datadata.yaml modelyolov8n.pt \ epochs100 imgsz640 batch16 device0这里每个参数都有实际意义。data指向你的 data.yaml 路径model使用预训练权重作为初始化epochs100不代表必须跑满默认的早停机制会在连续多个 epoch 验证指标不再提升时自动终止imgsz640是所有 YOLOv8 默认预训练最适应的输入尺寸batch16要结合显存来设如果显卡只有 8G可以直接写成batch-1让 ultralytics 自动探测最大可用 batch。device0指使用第一块 GPU。第一次运行时如果本地没有yolov8n.pt会在联网状态下自动下载。这只影响第一次后续会走缓存。权重会保存到runs/detect/train/weights/best.pt和last.pt。命令行适合快速验证如果需要在训练过程中调整更多细节我更喜欢用 Python 脚本。3.3 用 Python 脚本训练并监控指标from ultralytics import YOLO # 加载预训练权重 model YOLO(yolov8n.pt) # 训练 model.train( datadata.yaml, epochs100, imgsz640, batch16, device0, patience20, projectruns/detect, nameemotion_exp, exist_okTrue, ) # 训练结束后直接跑一次验证 model.val(datadata.yaml, splitval)代码逻辑很直白YOLO对象加载初始权重train传数据集配置和超参数。patience20表示连续 20 个 epoch 验证指标不提升就提前结束这对未知数据集很友好省时间也能避免过拟合。project和name是两个刚入门的同学容易忽略的参数它们决定训练结果保存目录不设置的话会在默认路径上叠一串时间戳让人分不清哪次是哪次。exist_okTrue的作用是允许覆盖同名目录避免报错。训练过程中控制台每一轮会输出box_loss、cls_loss、dfl_loss、precision、recall、mAP50、mAP50-95这几列。前三个是损失值理想情况是逐渐下降后走平后面几个是评价指标整体趋势应该缓慢上升。如果损失在暴跌后又快速反弹大概率是学习率设置过高或者数据有脏标签如果损失一直不降先怀疑数据路径是否配错、标签是不是全空。mAP50和mAP50-95有区别mAP50 是 IoU 阈值固定为 0.5 时的平均精度mAP50-95 是多个 IoU 阈值的平均后者更严格也更看框的定位精度。表情识别场景下mAP50 更实用因为人脸框不需要像医学影像那样精细到小数点后三位。4. 把 best.pt 用起来Python 推理和 C 对接的常用链路训练完成后真正的落地才刚开始。runs/detect/emotion_exp/weights/best.pt是我们需要的东西。PyTorch 权重可以直接被 Python 调用但在 C 工程里通常要先导出成 ONNX 格式再接入推理框架。4.1 先写一个最小可跑的 Python 推理脚本from ultralytics import YOLO model YOLO(runs/detect/emotion_exp/weights/best.pt) results model.predict( sourcetest/images/sample.jpg, conf0.25, iou0.5, saveTrue, ) for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(类别:, model.names[cls_id], 置信度:, conf, 坐标:, xyxy)这段代码中的conf0.25是置信度过滤阈值低于这个值的框会被丢掉iou0.5是 NMS 去重阈值两个框重叠度超过 0.5 就保留置信度更高的那个。saveTrue会把画好框的图片保存到本轮 result 目录里方便确认效果。输出的xyxy是绝对像素坐标单位是原图分辨率比如 1920 宽的图返回的就是 1920 以内的整数。4.2 导出 ONNX给 C 端准备通用输入资源包里既然带了inference.cpp和inference.h说明代码参考方向是 C 而非纯 Python。常见做法是先用 ultralytics 导出 ONNX再在 C 里通过 ONNX Runtime 或 OpenCV DNN 加载。导出命令yolo export modelruns/detect/emotion_exp/weights/best.pt formatonnx imgsz640 opset12导出时注意opset不能太低太低可能缺少某些算子imgsz必须和训练时的输入尺寸保持一致否则你后面预处理要额外做一次缩放精度也会受影响。导出完成后会得到best.onnx。C 里的推理流程无非三步读图、预处理、模型推理。一个简化版 ONNX Runtime 调用骨架大致长这样#include opencv2/opencv.hpp #include onnxruntime_cxx_api.h void run_inference(Ort::Session session, const cv::Mat image) { // 1. 转换通道顺序OpenCV 是 BGR模型期望 RGB cv::Mat rgb; cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB); // 2. 缩放到 640x640归一化到 0-1转成 CHW 排列 cv::Mat resized; cv::resize(rgb, resized, cv::Size(640, 640)); std::vectorfloat input(1 * 3 * 640 * 640); // 这里需要按 channel 顺序拷贝像素并除以 255 // 3. 执行 session.Run 得到输出张量 // 输出维度一般是 1 x (4 nc) x 8400 // 8400 是 80x80 40x40 20x20 三个尺度下候选框的总数 // 4. 把 4 个类的置信度做一次 NMS得到最终框 }C 端最容易摔跤的是张量布局。PyTorch 里图片是NCHW排布而 OpenCV 的Mat是HWC排布直接整块拷贝数据是错乱的。我一般会写一个两层循环按通道搬数据确保内存里是 R 通道全部像素、G 通道全部像素、B 通道全部像素的顺序。这个坑不解决推理结果会出现大量莫名其妙的高置信度误检。4.3 输出解析和类别顺序必须和 names 保持一致ONNX 模型输出形状通常是[1, 8, 8400]因为 4 个类别加上每个框的 4 个坐标正好 8。前 4 行是cx, cy, w, h的后处理信息后 4 行是 anger、happy、sad、surprise 四个类别的置信度。解析时最容易出现的问题是死记硬背输出顺序。有人看到输出维度是 8 就默认前 4 是坐标完全不看 names 里的顺序。如果 data.yaml 里的 names 结构和你解析后映射的数组顺序不一致比如你把第 5 行解读成 surprise模型实际输出的是 anger画面检测结果就会全部错位。所有用过这份资源的开发者都应该养成一个习惯把模型自带的model.names打印出来确认顺序后再解析输出。5. 常见问题与排查五个实际会碰到的训练翻车现场这里列的五个问题都是我在这类资源包上踩过或者帮别人排过的高频问题。每一个都有明显的现象、原因和对应解法。5.1 路径问题文件明明存在训练却报找不到数据集现象执行训练命令后日志输出类似train: ... does not exist然后中断。检查文件夹发现图片和标签都在。原因data.yaml 里写的是资源作者的绝对路径比如E:\python_code\dataset\biaoqing_detect_data\train/images。你把它放在自己的目录下后路径自然失效。另一类原因是路径里带中文或空格某些环境下会解析失败。解决把 data.yaml 改成基于path字段的相对路径写法确认 train、val、test 都指向以images结尾的实际目录。改完之后用python -c import yaml; print(yaml.safe_load(open(data.yaml)))检查一次加载结果确认无误再跑训练。5.2 labels.cache 损坏或与当前数据集不匹配现象首次启动时出现corrupted cache警告或者上下文中显示使用了旧的 cache 文件但标签显然已经改动过。原因ultralytics 为加速数据集读取生成了labels.cache文件每次启动通过文件列表哈希判断是否有效。如果你用文本编辑器改过标签内容或者增删了图片旧缓存不会自动同步读取阶段就会报错。解决直接删除labels.cache文件再重新训练。它会按当前目录重新生成。我在每次改完标签后都会先删一遍缓存这一步零成本能省掉一堆诡异报错。如果你用脚本批量刷过数据记得把删除缓存写进数据预处理流程里。5.3 模型只预测某一个类其他类全部漏检现象训练时 loss 下降正常mAP50 也不低但实际推理永远只输出 happy 或者只输出 anger其他类几乎不出现。原因最常见的不是数据量失衡而是类和标签索引错位。比如 txt 标签里第一列写的是 0但 data.yaml 中names列表的顺序和后处理代码里的类别清单不一致解析端把 0 映射成了 surprise而实际训练端把 0 当成 anger。训练和解析两套顺序一打架输出就混乱。解决先写脚本扫描一个标签文件确认第一列最大值不超过 nc-1再打印model.names确保和 data.yaml 完全一致最后把输出解析时使用的类别数组也调整成同一顺序。表情识别只有 4 类错位后比类别多的场景更难找因为看起来每个框都有合理语义。5.4 显存溢出训练跑到一半 OOM现象epoch 刚开始时进度条没走几步直接抛torch.OutOfMemoryError或提示CUDA out of memory。原因batch size 设得太大输入分辨率太高或者同时开了多个消融实验抢显存。yolov8n 在 640 输入下显存占用不算高但很多人把 batch 直接设成 32 或 64在消费级显卡上就会爆。解决把batch改成-1让 ultralytics 自动测一批能放下的最大 batch或者手动降到 8、16。如果显存低于 6G还可以把imgsz降到 480记得推理时也要用同样的尺寸。训练脚本里不要开workers2以上的 Windows Dataloader高 worker 也会吃内存改workers0反而更稳定。5.5 CPU 上训练慢到像卡死进度条半小时不动现象没有 GPU 的机器上训练一个 epoch 跑了 20 分钟还停在 1%或者看起来完全没动静。原因训练任务被派到了 CPU 上而且没有指定deviceultralytics 默认情况下能用 GPU 就用 GPU只有没识别到 GPU 时才落到 CPU。另一方面人脸图分辨率不低640 输入在 CPU 上预处理和后处理都很吃力。解决先确认机器确实没有可用 GPU然后接受 CPU 训练的事实把小模型和低分辨率作为权宜之计modelyolov8n.pt, imgsz416, epochs50。或者干脆先用这份数据生成一个轻量 demo确认 loss 能下降几个小时再找 GPU 机器正式跑长训练。与其让 CPU 硬扛 100 epoch不如减少一半训练量先验证数据可用性。6. 进阶用验证集 F1 曲线给置信度阈值做一次体检训练完权重只是第一步真正接入摄像头或图片服务后最常见的反馈是表情误报。默认conf0.25在很多场景下并不是最优阈值。你可以用验证集跑一遍指标找到 F1 最高的置信度点把它写进推理参数。from ultralytics import YOLO model YOLO(runs/detect/emotion_exp/weights/best.pt) model.val(datadata.yaml, splitval)验证完成后ultralytics 会在结果目录里生成F1_curve.png横轴是置信度阈值纵轴是 F1。图像上会标出最高点对应的 conf 值。比如最高点落在 0.42说明推理时用 0.42 比用默认 0.25 更平衡既不放过真框也不引入大量误检。我自己的习惯是把这一步做成固定动作。有一次我把 best.pt 直接接进实时预览窗口surprise 表情在普通画面里不停乱跳原因就是 conf 太低只要有人稍微张嘴就会被判定为惊讶。后来打开 F1 曲线一看这个数据集在 0.45 附近才是 F1 峰值把推理参数改掉后误报立刻减少翻车现场才算按住。从那以后我每换一个数据集都会把“训练-验证-F1 阈值检查”作为一个整体流程跑完再决定是不是要上线而不是训练完就任权重裸奔。希望帮到你。本文还有配套的精品资源点击获取