
简介这份资源面向计算机视觉初学者与需要开展图像分类实践的学生、开发者提供了一套已划分完毕的小狗情绪识别数据集可用于训练与验证四类情绪模型包括angry、happy、relaxed、sad适合作为YOLOv5分类任务或自定义卷积网络实验的数据基础。压缩包内共2000个文件以1998张jpg图像为主体另含1个Python可视化脚本和1个JSON类别字典文件整体约113.95MB。data目录下分为train训练集与val验证集分别存放2800与1200张同类图片目录结构清晰便于直接接入训练流程。配套的py脚本支持随机传入4张图片进行展示并自动保存到当前目录方便快速检查数据质量与类别分布。目前已有60人学习下载适合希望省去数据采集与划分环节、专注模型改进与调参的读者参考使用。1. 小狗情绪图像分类数据集从 4000 张图到可复现训练管线拿到一个图像分类数据集最怕的不是图片少而是目录结构混乱、类别映射对不上、可视化脚本跑不起来。这套小狗情绪图像识别分类数据集把这三件事都提前做完了data 目录下分 train 和 val 两个子目录train 共 2800 张、val 共 1200 张按同一类别归入各自文件夹类别字典文件直接给出 0 到 3 的映射关系——angry、happy、relaxed、sad 四类情绪。它适合想快速验证图像分类算法、跑通 yolov5 分类头、或者做迁移学习对比实验的从业者。你不需要再花半天写划分脚本拿到就能直接喂给 DataLoader可视化脚本随机抽 4 张图就能确认数据分布是否合理。下面按「结构拆解 → 训练接入 → 可视化验证 → 踩坑排查 → 进阶技巧」的顺序把这份资源拆成能直接抄作业的实战笔记。2. 目录结构与类别字典先搞清楚数据怎么组织的2.1 train/val 划分逻辑与类别映射这份数据集的核心结构非常直白根目录下 data 文件夹里面 train 和 val 两个子目录每个子目录下再按类别名建文件夹。train 总数 2800val 总数 1200合计 4000 张。四类情绪 angry、happy、relaxed、sad 对应的类别字典是{ 0: angry, 1: happy, 2: relaxed, 3: sad }这个字典文件的作用是给标签编码提供权威映射。很多分类数据集只给文件夹名训练时靠os.listdir排序后隐式生成类别索引一旦文件夹命名有大小写差异或排序变化标签就对不上了。这里显式给出 JSON 字典训练脚本里直接加载能避免「训练时 accuracy 很高、推理时全错」的玄学问题。常见做法是把这个字典存成class_dict.json训练和推理共用同一份。如果你要接 YOLOv5 的分类模块YOLOv5 的classify模式要求数据集按 ImageNet 风格组织也就是train/class_name/xxx.jpg和val/class_name/xxx.jpg这份数据的结构刚好吻合不需要额外转换。2.2 用 Python 校验数据完整性在正式训练前我一般会先跑一段校验脚本确认每个类别的图片数量、文件格式、是否有损坏文件。这一步花不了两分钟但能省掉训练到一半才发现某类只有几十张图的后悔药。import os import json from PIL import Image DATA_ROOT ./data CLASS_DICT {0: angry, 1: happy, 2: relaxed, 3: sad} SPLITS [train, val] for split in SPLITS: split_path os.path.join(DATA_ROOT, split) print(f {split} ) total 0 for cls_id, cls_name in CLASS_DICT.items(): cls_dir os.path.join(split_path, cls_name) if not os.path.isdir(cls_dir): print(f [缺失] {cls_name}) continue imgs [f for f in os.listdir(cls_dir) if f.lower().endswith((.jpg, .jpeg, .png))] # 抽查前 5 张能否正常打开 bad 0 for fname in imgs[:5]: try: Image.open(os.path.join(cls_dir, fname)).verify() except Exception: bad 1 print(f {cls_name}: {len(imgs)} 张, 抽查异常 {bad} 张) total len(imgs) print(f 合计: {total})这段脚本做三件事按类别字典遍历 train 和 val、统计每类图片数量、用 PIL 的verify()抽查前 5 张是否可正常解码。参数上DATA_ROOT指向数据集根目录CLASS_DICT直接内联了类别映射。如果你拿到的数据里类别文件夹名和字典不一致脚本会打印「缺失」这时候就要回头检查是不是解压时目录层级多了一层。提示verify()只能检测文件头是否合法不能保证图片内容完整。如果训练时出现OSError: image file is truncated需要在 DataLoader 的__getitem__里加ImageFile.LOAD_TRUNCATED_IMAGES True或者提前用Image.open().convert(RGB)重新保存一遍。3. 接入 YOLOv5 分类训练从目录到可跑通的命令3.1 YOLOv5 classify 模式的数据要求YOLOv5 从 v6.0 开始支持分类任务入口是classify/train.py。它对数据集的硬性要求只有一条train和val目录下按类别名建子文件夹每个子文件夹里放对应图片。这份数据集的结构完全满足不需要写自定义 Dataset。但有几个参数容易翻车。第一--data参数在分类模式下不是指向 data.yaml而是指向数据集根目录也就是包含 train 和 val 的那一层。第二--epochs默认 300小数据集上很容易过拟合建议先跑 50 到 100 轮看曲线。第三--img-size默认 224如果原图分辨率远大于这个值建议先统一 resize 到 256 或 320减少 CPU 解码瓶颈。3.2 训练命令与关键参数假设你已经 clone 了 YOLOv5 仓库并装好依赖数据放在./data训练命令如下python classify/train.py \ --data ./data \ --model yolov5s-cls.pt \ --epochs 80 \ --batch-size 32 \ --img-size 224 \ --lr0 0.001 \ --project runs/train-cls \ --name dog_emotion_v1逐项说明--data指向包含 train/val 的根目录--model用预训练的 yolov5s-cls 权重做迁移学习四类任务上比从头训收敛快很多--batch-size 32在 8GB 显存上跑 224 分辨率基本安全如果 OOM 就降到 16--lr0 0.001是初始学习率分类任务上比检测任务要小一档--project和--name控制输出目录方便多组实验对比。训练过程中重点看两个指标val/loss是否持续下降以及metrics/accuracy_top1是否在验证集上稳定爬升。如果 train loss 降但 val loss 从第 20 轮开始反弹基本就是过拟合可以加--dropout 0.2或者用--label-smoothing 0.1。3.3 用类别字典做推理后处理训练完之后推理脚本输出的 logits 是 0 到 3 的索引需要映射回情绪名称。这时候类别字典文件就派上用场了import json import torch from PIL import Image from torchvision import transforms class_dict json.load(open(class_dict.json, r)) idx_to_name {int(k): v for k, v in class_dict.items()} model torch.load(runs/train-cls/dog_emotion_v1/weights/best.pt, map_locationcpu) model.eval() transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) img Image.open(test_dog.jpg).convert(RGB) tensor transform(img).unsqueeze(0) with torch.no_grad(): logits model(tensor) pred_idx logits.argmax(dim1).item() print(f预测情绪: {idx_to_name[pred_idx]})这里的关键点是idx_to_name的构建JSON 的 key 是字符串必须转成 int 才能和 argmax 的输出对齐。Normalize 的均值和标准差用的是 ImageNet 的统计值因为预训练权重是在 ImageNet 上训的推理时必须保持一致否则精度会掉几个点。4. 可视化脚本随机抽 4 张图确认数据分布4.1 可视化脚本的输入输出设计数据集自带的 Python 可视化脚本功能是随机传入 4 张图片并展示同时把结果保存在当前目录。这个脚本的价值在于快速确认三件事图片能否正常读取、类别标签是否和视觉内容一致、四类情绪在视觉上是否有区分度。我一般会把脚本改成从 train 和 val 各抽 2 张并且把类别名标在标题上这样一眼就能看出标签有没有错位。下面是一个可直接用的版本import os import random import matplotlib.pyplot as plt from PIL import Image DATA_ROOT ./data CLASS_NAMES [angry, happy, relaxed, sad] SPLITS [train, val] fig, axes plt.subplots(2, 2, figsize(10, 10)) axes axes.flatten() for i, split in enumerate(SPLITS): for j in range(2): cls random.choice(CLASS_NAMES) cls_dir os.path.join(DATA_ROOT, split, cls) fname random.choice(os.listdir(cls_dir)) img Image.open(os.path.join(cls_dir, fname)).convert(RGB) ax axes[i * 2 j] ax.imshow(img) ax.set_title(f{split} / {cls}) ax.axis(off) plt.tight_layout() plt.savefig(sample_grid.png, dpi120) plt.show()参数说明figsize(10, 10)控制输出图大小dpi120控制保存分辨率random.choice每次运行结果不同方便多次抽样。保存的文件名sample_grid.png落在当前工作目录和数据集自带的脚本行为一致。4.2 从可视化结果判断数据质量跑完可视化后重点看几个信号。如果四类图片在毛色、背景、拍摄角度上差异很大说明数据多样性够训练出的模型泛化能力会好一些。如果某一类全是室内近景、另一类全是室外远景模型可能会学到背景而不是情绪特征这时候需要考虑做数据增强或者重新采样。另一个常见问题是类别不平衡。虽然 train 总数 2800、val 总数 1200但四类未必均分。如果某一类只有 300 张而另一类有 900 张训练时 loss 会被多数类主导。解决办法是在 DataLoader 里用WeightedRandomSampler给少数类更高的采样权重。具体做法是先统计每类数量然后按1/count计算权重传给 sampler。注意可视化脚本只做抽样展示不能替代完整的分布统计。正式训练前建议用 2.2 节的校验脚本跑一遍全量统计确认每类数量后再决定是否需要重采样。5. 避坑与排查四类情绪分类里最容易翻车的五个点5.1 现象训练 accuracy 很高但推理全错原因类别索引映射不一致。训练时os.listdir返回的文件夹顺序是 angry、happy、relaxed、sad但推理时用了另一套排序导致 argmax 出来的 0 被解释成了别的类别。解决训练和推理共用同一份class_dict.json并且在 Dataset 的__getitem__里显式用字典查表不要依赖os.listdir的隐式排序。5.2 现象val loss 从第 15 轮开始持续上升原因过拟合。4000 张图对四分类任务来说不算多如果模型参数量大或者训练轮数过多很容易记住训练集。解决加数据增强随机翻转、颜色抖动、随机裁剪把--epochs降到 50 到 80加--dropout 0.2或者用--label-smoothing 0.1。如果还不行换更小的模型比如 yolov5n-cls。5.3 现象DataLoader 报OSError: image file is truncated原因部分 JPEG 文件在传输或解压时损坏PIL 默认会抛异常。解决在 Dataset 文件顶部加from PIL import ImageFile; ImageFile.LOAD_TRUNCATED_IMAGES True或者在__getitem__里用 try/except 跳过坏图并打印文件名后续手动清理。5.4 现象训练速度极慢GPU 利用率不到 30%原因图片分辨率过大CPU 解码成为瓶颈。如果原图是 2000x2000 以上每个 batch 都要在 CPU 上 resize 到 224耗时远超 GPU 前向传播。解决提前用脚本把所有图片统一 resize 到 256x256 或 320x320 并重新保存训练时只做随机裁剪到 224。这样 CPU 只需要解码小图速度能提升 3 到 5 倍。5.5 现象某一类验证集 accuracy 始终为 0原因该类在验证集中样本极少或者标签在划分时被错误地全部放进了训练集。解决用 2.2 节的校验脚本统计 val 每类数量如果某类少于 50 张考虑从训练集里 stratified split 一部分补充或者用类别权重损失函数给该类更高权重。6. 进阶技巧用混淆矩阵和 TTA 把四分类精度再提一档训练跑通之后真正决定模型能不能上线的是你能不能定位它到底在哪两类之间犯错。四类情绪里relaxed 和 happy 在视觉上最容易混angry 和 sad 也有一定重叠。这时候光看 top1 accuracy 不够得画混淆矩阵。import numpy as np import matplotlib.pyplot as plt from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay # 假设 y_true 和 y_pred 是验证集上的真实标签和预测标签 y_true [...] # 从 val 集收集 y_pred [...] # 模型预测结果 cm confusion_matrix(y_true, y_pred, labels[0, 1, 2, 3]) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labels[angry, happy, relaxed, sad]) disp.plot(cmapBlues, values_formatd) plt.title(Dog Emotion Confusion Matrix) plt.savefig(confusion_matrix.png, dpi120) plt.show()拿到混淆矩阵后重点看非对角线上的大数字。如果 relaxed 被大量预测成 happy说明这两类的特征在模型眼里太接近。解决办法有两个一是对这两类做针对性数据增强比如对 relaxed 类加更多的水平翻转和亮度变化二是用 TTATest Time Augmentation推理时对同一张图做原图、水平翻转、轻微缩放三个版本取平均 logits 再 argmax。TTA 的实现不复杂在推理循环里加几行就行def predict_with_tta(model, img_tensor): model.eval() with torch.no_grad(): logits_1 model(img_tensor) logits_2 model(torch.flip(img_tensor, dims[3])) logits_3 model(torch.nn.functional.interpolate(img_tensor, scale_factor1.1, modebilinear, align_cornersFalse)[:, :, :224, :224]) avg_logits (logits_1 logits_2 logits_3) / 3 return avg_logits.argmax(dim1)这段代码对每张图做三次前向原图、水平翻转、放大 10% 后中心裁剪回 224。三次 logits 取平均再 argmax。代价是推理时间变成三倍但在验证集上通常能涨 1 到 3 个点。如果线上对延迟敏感可以只保留原图和水平翻转两个版本性价比最高。还有一个容易被忽略的点验证集上的类别分布要和真实场景一致。如果实际业务里 angry 的图片占 60%但验证集里四类均分那 accuracy 再高也不代表线上表现好。我一般会额外构造一个按真实分布采样的验证子集单独跑一遍评估这个数字才是有参考价值的。从那以后我每次拿到新的分类数据集都强制走一遍「校验脚本 → 可视化抽样 → 混淆矩阵 → TTA 对比」这四步哪怕数据集看起来再干净也不跳过。希望帮到你。本文还有配套的精品资源点击获取