简介一套面向YOLOv7目标检测实战的打电话行为识别资源包适合刚接触PyTorch的算法学习者以及需要快速落地驾驶舱、工位等场景打电话检测原型的开发者。资源将日常打电话动作整理为带标注的数据集每张图片对应txt和xml两种标签格式分类存放便于在不同训练框架间切换同时提供训练好的模型权重加载后即可对图片或视频进行检测也可基于自有数据继续微调。压缩包共2000个文件主要包括jpg原始图像、txt/xml标注文件、py训练与推理脚本、yaml模型配置另含pt权重、ipynb演示、辅助工具和项目文档整体约703MB目录结构清晰便于按模块查阅。已有725人学习下载适合作为安全监控、驾驶行为分析等场景的基线方案结合配套博客的检测效果样例可快速评估模型表现投入二次开发。1. 为什么拿 YOLOv7 做打电话检测一份能直接跑的行为识别资源工地安全巡检、驾驶行为监测、考场纪律检查这些场景里高频出现的一个违规动作就是「打电话」。它的视觉特征非常明确手机贴近耳侧、手部姿态异常。但真正落地时你会发现传统图像处理方案根本扛不住复杂背景和光照变化拿通用目标检测模型又容易把「手拿手机」也判成「打电话」。YOLOv7 在这个任务上的优势很明显单阶段检测速度快推理延迟低配合一个标注质量靠谱的数据集就能在普通 GPU 甚至 CPU 上完成识别。这份资源的核心就是一套完整的 YOLOv7 打电话检测方案——训练好的权重、带 txt 和 xml 双格式标签的数据集、PyTorch 框架的完整训练代码拿过去改改路径就能直接跑。适合正在做行为识别方向毕业设计、或者要在实际监控场景里做试点验证的工程师也适合刚接触 YOLO 系列、想把「训练—验证—部署」全链路走一遍的新手。2. 从文件构成看懂这份资源模型代码、权重和双标签结构2.1 从文件清单反推项目构筑拿到资源先别急着跑训练把根目录的代码文件过一遍基本就能摸清这个项目是怎么组织的。这里出现的common.py、loss.py、yolo.py是 YOLOv7 官方仓库里模型结构定义的核心模块datasets.py负责数据加载train.py是常规训练入口train_aux.py是带辅助头训练的入口yolov7.pdf通常是网络结构图或者论文笔记LICENSE.md和README.md是项目说明。整套结构对应的是 YOLOv7 官方代码库的裁剪版本也就是说你后续要加模块、改损失函数、换数据增强都能直接在这个代码框架里操作不用另起炉灶。这份资源和纯官方仓库最大的区别一是多了一个训练好的打电话检测权重二是数据集里同时提供 txt 和 xml 两种标签格式。txt 是 YOLO 训练直接读的归一化格式xml 是 Pascal VOC 风格的像素坐标格式方便你在 LabelImg 里复查标注质量或者转成 COCO、JSON 等其他格式。两种格式双备份意味着不管你的下游工具链习惯用哪套都能直接对接不用自己写转换脚本。2.2 txt 与 xml 双标签两种格式逐字段对应先说 YOLO 格式的 txt 标签。文件里每一行对应一个目标框字段顺序是「类别编号、归一化中心 x、归一化中心 y、归一化宽度、归一化高度」。归一化指的是除以图片的宽和高所以值域在 0 到 1 之间训练时不管输入分辨率是 640 还是 1280框的坐标比例不变。举个例子一张 1280×720 的图上有一个打电话的人框的左上角在 (320, 180)右下角在 (640, 540)那么归一化后中心 x 是 0.375中心 y 是 0.5宽度是 0.25高度是 0.5txt 文件里就是一行0 0.375 0.5 0.25 0.5。类别编号从 0 开始所以如果数据集里只有「打电话」这一类那编号就是 0。再看 xml 标签它是 Pascal VOC 标准结构object节点下的bndbox直接记录像素坐标的xmin、ymin、xmax、ymax同时name节点记录类别名。两种格式对应关系如下信息项txt 字段xml 字段类别整数编号如 0类别名字符串如 phone中心坐标归一化 x、y—边界框归一化宽、高xmin, ymin, xmax, ymax坐标单位比例值像素值读取方datasets.py 直接读取LabelImg、转换脚本这里有个隐蔽的坑txt 里存的是类别编号xml 里存的是类别名字符串两者之间要靠一个「类别字典」对应起来。如果字典顺序错了训练时框就会张冠李戴。所以拿到数据集第一步不是赶着训练而是先摸清楚 xml 里到底有哪几个name再统一编号。后面第 3 章会给统计脚本。2.3 权重文件与推理链路训练好的模型怎么接训练好的权重文件是整个资源里最值钱的部分。它的标准用法是放在项目根目录下推理时通过--weights参数指定。YOLOv7 官方仓库的detect.py会自动加载权重文件里的模型结构不需要额外修改代码。如果你要在这个权重基础上继续微调训练命令里同样指定--weights 训练好的权重路径它就会作为预训练模型加载然后在你的新数据上接着训练迁移学习最常用的做法就是这样。值得注意的是YOLOv7 权重加载时会严格校验类别数。如果权重是在单类数据集上训练的而你非要改成多类加载时就会报Error(s) in loading state_dict除非你手动裁剪最后的检测头。这一点第 5 章避坑部分会细讲。另外提醒一句train.py和train_aux.py训练的模型结构略有差异aux 版本多了辅助检测头所以推理和继续微调时要保持权重与代码入口匹配拿 aux 权重去配普通模型结构也可能出现尺寸不匹配。3. 数据集预处理目录约定、标签体检和一套通用划分脚本3.1 images 与 labels 的目录约定YOLO 系列训练时对数据集目录有隐含约定datasets.py里的LoadImagesAndLabels类在读取标签时默认是拿图片路径做os.path.splitext然后把扩展名替换成.txt去对应目录里找标签文件。也就是说如果你的图片路径是data/train/images/img_001.jpg那么它会在data/train/labels/img_001.txt找标签这个路径是把images直接替换成labels。目录结构稍有偏差训练时就会提示找不到标签而且这类报错往往只在某个 batch 里出现特别难排查。常见的目录组织方式有两种。第一种是官方 COCO 风格datasets/phone/images/train/、datasets/phone/labels/train/第二种是交叉存放每个子集文件夹内同时放images和labels两个子目录。YOLOv7 对这两种都兼容关键是data yaml里train字段要指向图片目录而标签目录必须和图片目录同名、同层级只是最后一级文件夹叫labels。比如我们这次按第二种方式整理datasets/phone/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── phone.yamlphone.yaml里这样写train: datasets/phone/images/train val: datasets/phone/images/val nc: 1 names: [phone]这里names的列表顺序就是训练时类别编号的依据第一位对应编号 0。如果你的数据集里还区分了「正常行为」和「打电话」两个类别那nc改成 2names写成[normal, phone]。3.2 标签体检脚本先摸清家底再动手训练之前建议先跑一个体检脚本统计数据集里到底有哪些类别、哪些图片没有对应标签、哪些标签里出现了非法坐标。这类问题在从别处收集的数据集里非常常见尤其当别人裁剪过图片或者漏复制了标签文件时。我一般会写这么一段 Python 脚本从 xml 里提取类别清单再逐一核对 txt 是否存在import os import glob import xml.etree.ElementTree as ET img_dir datasets/phone/images/train label_dir datasets/phone/labels/train xml_dir datasets/phone/xmls/train # 如果单独存了xml # 1. 统计xml里的全部类别名 class_names set() for xml_path in glob.glob(os.path.join(xml_dir, *.xml)): tree ET.parse(xml_path) for obj in tree.findall(object): class_names.add(obj.find(name).text) print(xml中的全部类别:, class_names) # 2. 核对img和label是否一一对应 img_paths glob.glob(os.path.join(img_dir, *.jpg)) missing_txt [os.path.basename(p) for p in img_paths if not os.path.exists(os.path.join(label_dir, os.path.splitext(os.path.basename(p))[0] .txt))] print(缺少txt标签的图片数量:, len(missing_txt))这段脚本做了两件事第一段遍历所有 xml抓取name节点的文本内容汇总成类别集合用来确认数据集的类别定义第二段是逐个检查图片文件是否有同名的 txt 标签。逻辑说明的要点是YOLO 训练时如果某张图没有对应标签datasets.py里实际会把它当成纯背景图处理图片仍然参与训练但不会产生正样本损失如果你的训练集里大量图片缺失标签模型的召回率会明显偏低但 loss 曲线看起来正常。参数层面xml_dir的路径按你实际存放位置改扩展名.jpg也要根据数据集实际情况改成.png或.jpeg。3.3 数据集划分与两种标签格式的统一很多下载下来的数据集是整包一个目录没有划分 train 和 val需要自己动手按比例切分。写划分脚本时要注意一个原则同一张图片的 txt 和 xml 必须同步移动否则训练集和验证集内容对不上指标评估就是空中楼阁。下面这段脚本把图片和 txt 标签一起按 8:2 划分到对应目录xml 保留在原处作为复查用途import os import shutil import random from glob import glob random.seed(42) img_list glob(datasets/phone/all_images/*.jpg) random.shuffle(img_list) split_idx int(len(img_list) * 0.8) for split_name, part in zip([train, val], [img_list[:split_idx], img_list[split_idx:]]): os.makedirs(fdatasets/phone/images/{split_name}, exist_okTrue) os.makedirs(fdatasets/phone/labels/{split_name}, exist_okTrue) for img_path in part: base os.path.splitext(os.path.basename(img_path))[0] shutil.copy(img_path, fdatasets/phone/images/{split_name}/{base}.jpg) txt_path fdatasets/phone/all_labels/{base}.txt if os.path.exists(txt_path): shutil.copy(txt_path, fdatasets/phone/labels/{split_name}/{base}.txt)这个脚本的逻辑是先把图片列表随机打乱然后按 8:2 比例切分按基文件名去标签目录找对应 txt 并复制。random.seed(42)固定随机种子保证每次划分结果一致可复现。在实际训练中按 0.8/0.2 划分对于单类目标任务来说基本够用如果你手里的数据集偏小一两百张建议改成 0.9/0.1 甚至用 K 折交叉验证不要盲目套比例。脚本里我用的是copy而不是move为的是保留一份原始数据做备份万一划分后发现问题还有后悔药可吃。关于 txt 和 xml 两个版本训练时以 txt 为准xml 只在两个场景有用一是用 LabelImg 打开人工复查二是要转成 COCO 或其它格式时作为基准。如果实在需要把 txt 转回 xml可以按第 2.2 节表格里的公式反推把归一化坐标乘回图片宽高注意宽高信息要从图片本身读不能凭空猜。3.4 负样本该不该加打电话检测和普通目标检测有个本质区别它检测的不是「某个物体」而是「人与物体的交互状态」。数据集中如果全是正面样本模型学到的可能就是「有手机在脸旁就算」而不是「手机贴着耳朵才算」。常见做法是往数据集里掺入 10%~20% 的负样本也就是人拿着手机刷视频、人低头看手机、手机放在桌上但人在画面里这些图片不打标签或者打上「正常行为」的标签。YOLOv7 的datasets.py本身就支持图片没有标签时作为背景样本参与训练所以负样本只需放入图片目录、不写对应 txt 即可。这一招对抑制误报非常有效实测能让验证集上的误检率下降一截代价是训练时间略有增加。4. 训练自己的打电话检测模型train.py 与 train_aux.py 怎么选、超参怎么调4.1 两个训练入口的差异资源里同时给出了train.py和train_aux.py很多人不知道选哪个。官方仓库里train.py训练的是标准 YOLOv7 结构检测头只有一层train_aux.py训练的是带辅助检测头的版本中间的 feature map 会额外产出一组预测并参与损失计算辅助头在推理时会被丢弃只保留主检测头。辅助头的价值在于给浅层特征提供更强的梯度信号对检测小目标——比如监控画面里远处的人拿着手机——更有帮助。如果是第一版跑通流程建议直接用train.py结构和权重兼容性最稳妥。等训练稳定了再试train_aux.py看指标是否有提升。要注意的是train_aux.py需要配套的 yaml 配置文件里声明辅助头的结构参数加载预训练权重时也要对应 aux 版本混用会导致检测头输出维度不匹配的报错。4.2 data yaml 与模型 yaml 的修改清单训练前需要改两个 yaml 文件。一个是数据集描述文件也就是 3.1 节里的phone.yaml核心字段是train、val路径和nc、names。另一个是模型结构文件官方仓库里的cfg/training/yolov7.yaml核心改动是nc必须和数据集描述文件里的nc一致# yolov7.yaml 局部 nc: 1 depth_multiple: 1.0 width_multiple: 1.0 anchors: - [12,16, 19,36, 40,28] - [36,75, 76,55, 72,146] - [142,110, 192,243, 459,401]nc是类别数打电话检测单类场景就是1depth_multiple和width_multiple控制网络宽度和深度保持 1.0 就是原始 YOLOv7 大小如果算力紧张可以改成 0.33效果会下降但训练速度快很多。锚框anchors一般不需要手动调YOLOv7 在训练早期会自动进行锚框自适应计算没有特殊情况保持原样就行。4.3 训练命令与关键超参训练入口参数和官方train.py保持一致核心命令长这样python train.py \ --weights yolov7_training.pt \ --data datasets/phone/phone.yaml \ --cfg cfg/training/yolov7.yaml \ --batch-size 16 \ --img-size 640 \ --epochs 100 \ --device 0 \ --workers 4逐项说明一下。--weights指定预训练权重优先用这份资源自带的打电话权重如果要基于 COCO 预训练权重从零训就换成yolov7.pt。--img-size 640是训练分辨率显存不够可以降到 416但小目标检测能力会跟着下降。--batch-size这块有个玄学打电话检测的背景复杂度远高于 COCObatch 太小的话 BN 层统计不准loss 曲线会抖得厉害尽量至少 16。--device指定 GPU 编号没有 GPU 就用cpu但训练速度会慢一个量级后面避坑章细说。--workers是数据加载进程数Windows 上建议改成 0否则容易报DataLoader worker的错。其他值得调的参数--lr0默认 0.01迁移学习场景建议降到 0.001避免大步长把预训练权重冲坏--mosaic默认开启本意是提升小目标泛化能力但打电话检测的目标是整个人比例相对固定mosaic 带来的拼接边界伪影反而可能干扰训练遇到 loss 降不下去时可以尝试关掉。--label-smoothing 0.1可以加上对这类单类二分类任务有一定的正则化收益。4.4 训练产出与断点续训训练完成后runs/train/目录下会生成最后一轮权重last.pt和最优权重best.pt。best.pt取的是验证集上 mAP 最高的那一轮的权重推理和部署都该用它。如果要中断后续跑用同样的命令加--resume runs/train/exp/即可YOLOv7 会从last.pt继续优化器状态、学习率调度都会恢复。一个实用的建议训练时同时开启--save-period 10每 10 轮存一次检查点。打电话数据集通常不大训练速度快但如果中途电脑断电或者显存溢出崩了有检查点至少不用从头再来能省几个小时。我自己经历过训练到第 80 轮崩掉、从头再来的痛苦从那以后save-period是我必开的参数。5. 常见问题与避坑五个最容易翻车的点5.1 类别编号与类别名对不上现象训练指令刚跑起来日志里提示IndexError: index 1 is out of bounds for axis 0 with size 1或者 loss 震荡但 mAP 始终为 0。原因txt 标签里写的是类别编号模型 yaml 里nc1时编号只能是 0。如果数据集的 xml 里有两个类别名而你忽略了直接按单类训练某个目标的编号是 1就超界了。也可能是从 VOC 迁移过来的标注里把类别名顺序搞乱了。解决先用第 3.2 节的统计脚本跑一遍确定 xml 里实际出现的类别名数量再回头改nc和names。如果确实只有一类就把 txt 里所有非 0 的编号全部替换为 0# 把labels目录下所有txt中的非0类别编号统一改0 sed -i s/^[1-9]/0/ datasets/phone/labels/train/*.txt这条命令只改行首的第一个数字不影响后面的坐标值。注意在 macOS 上sed -i要加参数Linux 则直接可用。5.2 训练 loss 降得挺正常验证 mAP 却上不去现象训练曲线平滑下降看着一切正常但每轮结束的验证结果里 mAP0.5 一直卡在 0.1 以下。原因最常见的是训练集和验证集存在重复图片。如果你直接拿整包数据随机打乱了但没按「先按场景分组再划分」的原则同一个监控场景的连续帧可能同时进了 train 和 val模型在训练时已经见过验证图片了这不算过拟合但会虚高训练指标、掩盖真实泛化能力。另一可能是数据增强太强mosaic 拼出来的图片让模型学到了错误的上下文。解决重新按 3.3 节的脚本划分划分前先按场景或时间段分组。验证集最好挑和训练集不同时间段、不同摄像头角度的图片。另外在hyp.scratch.yaml里把mosaic从 1.0 降到 0.5看看指标变化。也可以通过对比「训练集 mAP」和「验证集 mAP」的差距来定位两者都低是数据或模型问题训练集高验证集低才是过拟合。5.3 把「手拿手机」误检成「打电话」现象推理时人举着手机在看屏幕模型也判定为打电话而且置信度还不低。原因数据标注本身不够精细。打电话行为的关键特征是手机贴近耳侧但如果标注时把「人拿着手机的全身框」都标成了正样本模型学到的是「人手机同框」这个粗粒度特征而不是「手机与头部的空间关系」这个细粒度特征。解决有两个方向。一是数据清洗把所有「打电话」标注框重新检查一遍确保只有手机位于耳侧附近的样本才保留标签模糊样本宁可删掉也别留着。二是引入后处理规则在推理结果之上加一道几何判断——计算检测框通常是人的框内人脸位置与手机位置的相对关系如果手机框的中心点距离人脸框的中心点超过一定阈值且不在耳朵附近就降低置信度。这类规则实现成本不高对误检的抑制非常有效。5.4 坐标错位txt 和 xml 混用导致标注框偏移现象在 LabelImg 里打开图片看标注位置是准的但训练出来的模型检测框整体偏移或者验证时框的位置不对。原因txt 是归一化坐标xml 是像素坐标如果某个环节把两种格式混着读——比如用 VOC 工具读取 txt 内容、或者拿 xml 的像素坐标直接喂给 YOLO——就会差一个尺度因子。图片是 1280 宽归一化 0.5 对应的像素是 640如果直接按 0.5 像素来画框自然就缩到左上角了。解决定好一条铁律——训练只用 txt人工检查只用 xml中间格式转换必须用脚本完成绝不手动改。如果要从 txt 转 xml记得从原图读取宽高再反算像素坐标不要用网络输入尺寸 640 去反算。另外每次转换完抽几张图用 LabelImg 打开核对框是否贴合目标。5.5 CPU 上训练慢到怀疑人生现象没有 GPU用--device cpu训练一个 epoch 跑了快一个小时照这速度一百轮得跑一周。原因YOLOv7 默认开启大量数据增强datasets.py在 CPU 上跑增强非常耗时加上--workers 4在 Windows 上还会反复拉起进程额外开销很大。解决实在要用 CPU 训练建议按这个配置降级--img-size 416 --batch-size 8 --workers 0 --epochs 50同时把hyp.scratch.yaml里mosaic关掉训练速度能提升几倍。但更务实的路线是拿这份资源自带的训练好的权重直接做推理验证最多在少量新数据上微调几十轮而不是从零训练。打电话检测场景下迁移学习 小数据微调是性价比最高的做法。6. 推理验证与部署从 detect.py 到 ONNX 导出训练完别急着收工先用验证集把指标跑一遍。YOLOv7 仓库的val.py会输出 Precision、Recall 和 mAP0.5打电话检测这类单类任务mAP0.5 在 0.9 以上才算合格。如果低于 0.85建议回头检查数据标注质量大概率是标签漏标了。推理命令很直接python detect.py \ --weights runs/train/exp/best.pt \ --source test_images/ \ --conf-thres 0.35 \ --iou-thres 0.45--conf-thres是置信度阈值宁可调高一点也别调低打电话检测的误报代价往往比漏报更高0.35 起步比较稳--iou-thres是 NMS 的 IoU 阈值0.45 是官方默认两个检测框重叠程度超过它就合并。检测结果图会保存到runs/detect/下建议每张图扫一眼重点看误检而不是只看有没有检出。如果要落地到实际业务比如接摄像头流或边缘盒子ONNX 导出是更靠谱的路线。YOLOv7 官方提供了export.pypython export.py --weights runs/train/exp/best.pt --img-size 640 --batch 1导出的.onnx可以用 ONNX Runtime 跑推理CPU 上单帧延迟能压到几十毫秒级别。导出时记住把输入尺寸固定成训练时的img-size动态尺寸虽然支持但在边缘设备上有兼容性风险。多说一句我的习惯每次拿到新的检测权重我都会固定跑一遍「五张典型图片」——正光、逆光、远处小目标、多人遮挡、手机和脸分离——肉眼过一遍再信指标。指标可以量化但误检类型只能靠看。这份资源我拆过之后又往自己的数据集上微调了三十轮导出 ONNX 放到 Jetson 上跑稳定性和帧率都能接受。希望帮到你训练顺利。提示数据集的 txt 和 xml 标签分别在两个文件夹中训练前请先核对目录结构不要自行合并。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?