☰
593张图像小样本YOLO训练:玩手机打电话行为检测落地实战
2026/10/1 10:50:20 网站建设 项目流程

简介:这是一份面向目标检测学习者的手机使用行为识别数据集,聚焦“玩手机”与“打电话”两类典型场景,适合正在入门或进阶YOLO系列算法的开发者用于模型训练与验证。数据集共593张图像,每张均配有标注,已按训练与验证需求划分完毕,可直接投入yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流框架使用。压缩包内共1780个文件,包含593个jpg图像、593个txt格式的YOLO标注、593个xml格式的VOC标注以及1个yaml配置文件,整体约20.46MB;两种标注格式分别存放,YOLO格式采用归一化中心点与宽高坐标,便于直接读取训练。目前已有191人学习下载。对于需要快速搭建检测流程的读者,可省去数据采集与标注环节,直接用于类别训练、精度对比与推理测试,是验证算法效果的实用素材。

1. 从 593 张“玩手机打电话”图像说起:小样本行为检测到底能不能落地

手上有一份 593 张图像、带标签的“玩手机 / 打电话”数据集,第一反应往往是:这么点数据,能训出能用的 YOLO 模型吗?我在工厂安全帽检测、加油站打电话识别这类项目里反复遇到过同样的疑问。结论先放这儿:593 张图像做通用检测确实偏少,但如果场景收敛、类别只有两三类、拍摄角度相对固定,它完全能训出一个可上线跑推理的模型,关键在于你怎么切分、怎么扩增、怎么选预训练权重。

这个标题拆开看是四件事:YOLO 算法、手机这个目标载体、玩手机与打电话两种行为、593 张带标签图像。它解决的是“特定区域内人员违规使用手机”的自动识别问题,典型落地场景是考场、加油站、驾驶舱、涉密车间。适合谁看?手上有类似小数据集、想跑通 YOLO 训练到部署全流程的算法工程师和嵌入式开发者。下面我按数据、训练、调参、踩坑、进阶的顺序,把这条链路讲透。

2. 593 张图像怎么变成能训的数据集:切分、清洗与标签校验

2.1 先搞清楚你的标签是“检测框”还是“行为类”

“玩手机”和“打电话”这两个类别,在标注层面有两种完全不同的做法。第一种是框住手机本身,类别写 phone,行为靠手机位置和人体姿态间接推断;第二种是框住人,类别直接写 play_phone、call。593 张这个量级,我一般推荐第二种,因为行为类直接对应业务语义,后处理简单,不用再写一套“手机框 + 人体框”的关联逻辑。

但第二种有个硬伤:打电话和玩手机在视觉上高度重叠,都是手举到头部或胸前。如果你的数据里这两类样本比例悬殊,模型会严重偏向多数类。先跑一遍统计:

import os from collections import Counter label_dir = "labels/train" counter = Counter() for f in os.listdir(label_dir): if not f.endswith(".txt"): continue with open(os.path.join(label_dir, f)) as fp: for line in fp: cls = line.strip().split()[0] counter[cls] += 1 print(counter) # 例如 Counter({'0': 412, '1': 181})

这段代码统计每个类别 ID 出现的次数。YOLO 标签格式是class_id x_center y_center width height,全部归一化到 0~1。如果发现某一类少于总框数的 15%,要么补数据,要么在训练时用类别权重补偿。参数上,class_id从 0 开始,别和背景类混淆——YOLO 没有独立背景类,背景由负样本隐式学习。

2.2 593 张的切分比例与“防泄漏”原则

小数据集最怕的是同一段视频抽帧后随机切分,导致训练集和验证集里出现几乎相同的画面,验证指标虚高。我一般这样做:先按拍摄来源(不同人、不同时间段、不同设备)分组,再按组切分,比例 8:1:1。593 张大概分成 474 训练、59 验证、60 测试。

# 假设原始图像和标签平铺在一个目录,先按来源前缀分组 python split_by_group.py --src images --ratio 0.8 0.1 0.1 --group-regex "^(.*?)_\d+"

--group-regex用来从文件名提取分组键,比如personA_001.jpg和personA_002.jpg归为同一组。这样切分后,验证集里的画面在训练集里不会出现“孪生兄弟”。如果数据来源单一、无法分组,那就退而求其次,用感知哈希去重,把相似度高于阈值的图像只保留一张。

2.3 标签校验:三个必查项

标注错误在小数据集里是致命的,593 张里错 20 张,mAP 能掉好几个点。我固定查三样:坐标越界、宽高为零、类别 ID 超范围。

def validate_label(path, num_classes=2): errors = [] with open(path) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: errors.append(f"line {i}: 字段数不对") continue c, x, y, w, h = int(parts[0]), *map(float, parts[1:]) if c >= num_classes: errors.append(f"line {i}: 类别 {c} 越界") if not (0 <= x <= 1 and 0 <= y <= 1): errors.append(f"line {i}: 中心点越界") if w <= 0 or h <= 0 or w > 1 or h > 1: errors.append(f"line {i}: 宽高异常") return errors

坐标越界通常来自标注工具导出时的坐标系不一致,宽高为零多半是误触。类别 ID 越界在合并多个数据集时特别常见,比如你把一个只有 phone 类的数据集和一个有 play_phone/call 的数据集混在一起,ID 就冲突了。校验完把有问题的图像和标签一起移出训练集,别只删标签。

3. 用 YOLOv8 在 593 张图上跑通训练:配置、命令与参数含义

3.1 选 v8 还是 v5:小数据集的预训练权重差异

热词里“yolov8训练自己的数据集”出现频率很高,我自己的选择也是 v8 起步。原因不是 v8 一定比 v5 准,而是 v8 的默认增强策略对小数据集更友好,且ultralytics这套接口把训练、验证、导出串成了一条命令。593 张这个量级,我建议从yolov8n.pt或yolov8s.pt开始,n 更快、s 更稳,别一上来就上 m 或 l,参数量大了在小数据上过拟合风险陡增。

预训练权重的作用是提供通用边缘、纹理特征,让模型不用从零学“什么是手机轮廓”。如果你的场景里手机都是特定型号、特定角度,预训练权重的收益会打折,但依然比随机初始化强。常见做法是先用 COCO 预训练权重训 50 轮,观察验证集曲线,再决定要不要换更大的骨干。

3.2 一份能直接抄的 data.yaml 与训练命令

YOLO 训练靠一个 YAML 描述数据路径和类别:

# data.yaml path: /home/user/phone_dataset train: images/train val: images/val test: images/test nc: 2 names: 0: play_phone 1: call

path是数据集根目录,train/val/test是相对路径。nc是类别数,必须和标签里的最大 class_id + 1 一致。names的顺序就是 class_id 的顺序,写反了模型会把打电话认成玩手机。

训练命令:

yolo detect train \ model=yolov8s.pt \ data=data.yaml \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ patience=30 \ augment=True \ cache=True \ project=runs/phone \ name=exp1

逐项说:epochs=150对小数据集够用,配合patience=30早停,验证集 30 轮不涨就停。imgsz=640是默认值,如果你的图像分辨率远高于此,可以提到 960,但显存和速度要权衡。batch=16在 8G 显存上跑 yolov8s 基本安全,爆显存就降到 8。lr0=0.01是初始学习率,小数据集我有时会降到 0.005 减少震荡。lrf=0.01是最终学习率因子,配合余弦退火。cache=True把图像缓存到内存,593 张完全放得下,能明显加快每轮速度。

3.3 训练过程中该盯哪几条曲线

runs/phone/exp1下会生成results.csv和一堆曲线图。我重点看三条:train/box_loss是否持续下降、val/box_loss是否在某个点后反弹、metrics/mAP50-95是否还在爬。如果 train loss 降但 val loss 早早反弹,就是过拟合,该加增强或减模型容量。如果两条 loss 都不降,先查学习率是不是太大,或者标签是不是根本没对上图像。

# 快速看最后 10 轮指标 tail -n 10 runs/phone/exp1/results.csv

results.csv的列名包含epoch, train/box_loss, val/box_loss, metrics/mAP50, metrics/mAP50-95等。mAP50 到 0.8 以上、mAP50-95 到 0.5 以上,在 593 张这个量级就算不错了。如果 mAP50 高但 mAP50-95 低,说明框的位置不够准,可以检查标注框是否贴合目标边缘。

4. 小数据集训练的避坑清单:从过拟合到类别混淆

4.1 现象:验证集 mAP 很高,一上测试集就崩

原因:切分时没有按来源分组,验证集和训练集画面高度相似,模型记住了背景而不是目标。解决:回到 2.2 节的分组切分,或者用感知哈希去重后重新切。我踩过这个坑,验证集 0.92、测试集 0.61,排查半天才发现是同一段视频的相邻帧被分到了两边。

4.2 现象:打电话被大量误判成玩手机

原因:两类视觉特征重叠,且训练样本里 play_phone 远多于 call,模型倾向于把模糊样本判给多数类。解决:先做类别平衡,对 call 类做针对性扩增(旋转、亮度扰动、局部裁剪),再在损失里给 call 类更高权重。YOLOv8 不直接暴露类别权重参数,可以通过复制 call 类样本、或在data.yaml里用过采样实现。

4.3 现象:训练 loss 震荡剧烈,mAP 忽高忽低

原因:学习率偏大,或 batch 太小导致梯度噪声大。解决:把lr0降到 0.005,batch尽量提到显存允许的最大值,同时开cos_lr=True让学习率平滑衰减。593 张图用 batch=16 时,一个 epoch 只有约 30 个 iteration,梯度噪声本来就大,学习率保守一点更稳。

4.4 现象:模型把“手持手机但没在玩”也判成玩手机

原因:标注时只框了人,没有区分“手持”和“贴耳/低头看屏”的姿态差异。解决:要么在标注规范里明确“玩手机”必须包含低头看屏或手指操作的动作,要么引入关键点辅助判断。纯检测框很难区分“拿着”和“在用”,这是行为检测的固有边界,必要时上姿态估计做二次过滤。

4.5 现象:导出 ONNX 后推理结果和 PyTorch 不一致

原因:预处理不一致,比如 PyTorch 推理时用了 letterbox,导出后忘了同步;或者归一化参数不同。解决:导出时用yolo export model=best.pt format=onnx imgsz=640,推理端严格按 letterbox 缩放并保持 0~1 归一化。我一般会拿同一张图分别跑 PyTorch 和 ONNX,比对输出框的坐标,误差在 1 像素内才算通过。

5. 从 593 张到可部署模型:增强策略、量化与推理验证

5.1 小数据集的增强组合:别开太猛

YOLOv8 默认开了 mosaic、HSV 扰动、翻转等。593 张这个量级,mosaic 能显著提升泛化,但也会让单张图里的目标变小、变密,如果原始场景里人本来就小,mosaic 后可能小到看不清。我的做法是:前 100 轮开 mosaic,后 50 轮关掉,让模型在接近真实分布的图像上收尾。关闭方式是在训练命令里加close_mosaic=50,表示最后 50 轮关闭。

yolo detect train model=yolov8s.pt data=data.yaml epochs=150 close_mosaic=50 ...

HSV 扰动里,hsv_h=0.015, hsv_s=0.7, hsv_v=0.4是默认值。如果场景光照稳定,可以把hsv_v降到 0.2,避免模型对亮度过度敏感。翻转方面,fliplr=0.5默认开,但“打电话”这个动作左右手有语义差异吗?一般没有,可以保留。flipud默认关,因为上下翻转不符合真实拍摄。

5.2 用验证集做阈值扫描,别用默认 0.25

YOLO 推理默认置信度阈值 0.25、NMS IoU 0.45。这两个值在行为检测里往往不是最优。我一般拿验证集跑一遍,扫 0.1 到 0.6 的置信度阈值,看哪个点的 F1 最高。

from ultralytics import YOLO import numpy as np model = YOLO("runs/phone/exp1/weights/best.pt") results = model.val(data="data.yaml", conf=0.001, iou=0.6) # 低阈值多召回 # 再根据 results 里的 precision-recall 曲线选 F1 最大点

conf=0.001是为了让验证时保留尽可能多的预测框,方便后续画 PR 曲线。实际部署时用选出来的阈值,比如 0.35。如果业务对误报容忍度低(比如自动处罚),阈值往高调;如果只是提醒,阈值可以低一点保召回。

5.3 量化与边缘部署:INT8 能省多少

如果目标是手机端或边缘盒子,导出 INT8 量化模型能显著降延迟。YOLOv8 支持导出 OpenVINO、TensorRT、TFLite 等格式。以 TFLite INT8 为例:

yolo export model=best.pt format=tflite int8=True data=data.yaml imgsz=640

int8=True需要提供校准数据,data=data.yaml就是校准集来源。量化后模型大小约为 FP32 的四分之一,推理速度在支持 INT8 的硬件上能快 2~3 倍。代价是 mAP 通常掉 1~3 个点,593 张训出来的模型本身精度就不算高,量化前先确认 FP32 的 mAP 有足够余量。我一般要求量化后 mAP50 不低于 0.75 才上线。

5.4 一个具体的验证习惯:拿“最难的 10 张”做回归

每次改完增强、阈值或量化,我都会固定拿 10 张“最难”的图跑一遍——这些图通常是遮挡严重、光照极端、两类行为模糊的样本。把它们单独放一个目录,写个脚本批量推理并保存可视化结果:

yolo detect predict model=best.pt source=hard_cases/ save=True conf=0.35

结果存在runs/detect/predict下,我逐张看框的位置和类别。如果这 10 张里错超过 2 张,说明这次改动引入了退化,回滚。这个习惯帮我避免了好几次“指标涨了但实际更差”的翻车。593 张的数据集,模型容量有限,任何改动都可能牵一发动全身,固定难例回归是最省事的后悔药。

希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询