ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

VOC格式打电话检测数据集转YOLOv8训练全流程指南

VOC格式打电话检测数据集转YOLOv8训练全流程指南 简介一套专门用于打电话行为检测的Pascal VOC格式数据集面向需要使用YOLO等目标检测模型进行训练与评估的研究者、算法工程师。数据集共包含5364张jpg图片、5364份xml标注和1份txt使用说明合计10729个文件压缩包约414MB图片与xml同名一一对应能直接接入常规VOC格式训练管线省去格式转换和筛选时间标签文件采用标准xml结构便于二次处理与统计。标注由labelImg完成类别为phone与head分别有5045个和415个矩形框。标注规则经过严格设计手机贴近耳朵通话时将手机、头部或耳朵及手部合并成一个框以捕捉完整打电话动作同时借助head类别辅助抑制误检。相比网上很多把玩手机也算作打电话的粗放标注这套数据明确区分“打电话”与“玩手机”更贴近监控、驾驶等真实落地场景。目前已有1600人浏览学习适合作为行为识别、目标检测等项目的训练集或评测集。1. 5364张VOC格式打电话检测数据集先确认它适合谁、能干什么监控安防和车载安全场景里打电话检测是识别模型的刚性需求。这套5364张的VOC格式打电话检测数据集涵盖手持接听、贴近耳旁、车内驾驶员、办公区域等多种姿态对YOLO系检测器来说正好卡在“够用又不冗余”的区间。VOC格式保留了object级别的bbox框、类别名、截断与难例标记既能当迁移学习的底座也能转成YOLO训练要的txt标注再继续补数据。用途建议先框成三个手头没有干净标注集、想练熟YOLO训练流程的初学者拿来即用省下最枯燥的标注时间做边缘盒子和摄像头端算法验证的工程师先确认模型在这个数据分布上能收敛再收集自己现场的图片做增量训练需要快速出演示效果的售前或方案岗位用这套数据跑一个可展示的检测demo比从零标注快太多。有一点要提前说清楚这套数据不是VOC 20类而是“电话使用”单类目标转换标签时不要套用PASCAL VOC的20类映射表。目录结构倒是规范的VOC布局——JPEGImages存图片、Annotations存XML标注、ImageSets/Main放划分文件但图片尺寸不统一有1920×1080的监控大图也有640×480的中等画面这个变量在训练时要专门处理。下面直接从格式转换开始按我实际跑通这条链路的方式来写。2. VOC转成YOLO能直接用的txt标注解析XML到归一化坐标的完整脚本VOC格式和YOLO训练格式之间最直接的差别就在标注文件上。VOC的标注是XML每个目标对应一个object节点里面写类别名、截断标志、难例标志和一个bndbox四角坐标YOLO训练的标注则是和图片同名的txt文件每行五个数字class_id x_center y_center width height坐标全部归一化到0到1之间。直接把XML文件夹路径塞给YOLO训练是不行的第一步必须是写转换脚本。但我不建议照抄网上的通用脚本跑完就进训练先把标注内容打开看一遍确认干净再转换。这样坐标越界、标签名拼写不一致、空标注这类问题能提前被拦下来而不是等到loss曲线变成一条直线时才回头查数据。2.1 VOC标注里最容易忽略的字段size、difficult与name拼写先打开一个典型XML看看结构。下面这段是数据集中某张驾驶场景图片的标注节选文件字段和这套数据完全一致annotation folderJPEGImages/folder filenamedriver_phone_00235.jpg/filename size width1920/width height1080/height depth3/depth /size object namephone_call/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin732/xmin ymin215/ymin xmax826/xmax ymax466/ymax /bndbox /object /annotation三个字段必须单独提醒。size块里的width和height是整张图像的像素尺寸不是标注框的尺寸。所有归一化计算都要拿它当分母。一旦有人误用了标注框自身的宽高做分母生成的txt坐标会整体错位训练出来的模型预测框全部漂在目标旁边。转换脚本里应当从XML的size块读取尺寸不要依赖外部传入的宽高参数。truncated和difficult表示目标在画面边缘被截断或者小到难以辨认。打电话检测里很常见的情况是手机被方向盘挡了一半或驾驶员侧过身只露出半个屏幕这类样本如果difficult1我默认先过滤掉单独留一个文件夹做困难样本挖掘。不过滤的话模型会把“半截手机”学成强特征误检率会明显上升。name的拼写一致性也要查。真实标注过程里同一个类别在不同XML里可能被写成phone_call和phonecall甚至中英文混用。转换前对所有XML的name字段做一次去重统计能自动暴露这类问题。2.2 用Python脚本把VOC的XML转成YOLO的txt转换逻辑的核心是四个换算把xmin和xmax换算成中心点x_centerymin和ymax换算成中心点y_center再整体除以图像宽高完成归一化。下面是完整可跑的脚本。import os import glob import xml.etree.ElementTree as ET # 类别映射表VOC标注里的类名 - YOLO类别ID # 这个数据集只有 phone_call 一个正类ID从0开始 CLASS_MAP {phone_call: 0} def voc_to_yolo(xml_path, output_dir): tree ET.parse(xml_path) root tree.getroot() # 图像尺寸从XML的size块读取不要靠外部传入 size root.find(size) width int(size.find(width).text) height int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASS_MAP: # 遇到映射表里没有的类就跳过不报错方便后续扩展多类 continue difficult 0 if obj.find(difficult) is not None: difficult int(obj.find(difficult).text) if difficult 1: continue # 难例单独留作困难样本挖掘不进入常规训练 bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化中心点与宽高分母是整图尺寸 x_center ((xmin xmax) / 2) / width y_center ((ymin ymax) / 2) / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height lines.append( f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f} ) txt_name os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(output_dir, txt_name), w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: xml_dir Annotations txt_dir labels_for_yolo os.makedirs(txt_dir, exist_okTrue) for xml_file in glob.glob(os.path.join(xml_dir, *.xml)): voc_to_yolo(xml_file, txt_dir)脚本运行结果是把每张图片的标注写进同名txt没有有效标注的图片会生成空文件。CLASS_MAP目前只把phone_call映射到0如果这套数据以后要合并进头盔佩戴、分心驾驶等多类检测往字典里继续加键值就行。difficult1在这里直接跳过后面做困难样本挖掘时可以把它们单独导出、单独训练。写完后务必抽查随机取十张图用OpenCV读出实际宽高再对照txt里的归一化坐标乘以宽高还原出的整数坐标和XML里bndbox是否能对上。多数坐标漂移问题在这一步就能抓出来。2.3 数据划分按采集片段切train/val/test而不是随机切txt转换完成后的下一步是划分三个集合。很多教程只教随机打乱但在打电话检测这种场景下随机切分等于给验证集喂答案。这套数据里有相当一部分图片是从监控视频里逐帧抽取的。同一段十几秒的视频相邻帧之间可能只有手臂抬高几个像素的差异。如果随机切分同一视频的帧会同时出现在train和val里模型等于提前见过验证画面的构图mAP虚高。等模型上了真实摄像头画面漏检和误检立刻打回原形。我习惯按采集片段分组整段划进同一个集合。import random from collections import defaultdict from pathlib import Path image_dir Path(images) train_ratio, val_ratio 0.8, 0.15 # 按文件名前缀聚类同一采集片段不跨集合 groups defaultdict(list) for img in sorted(image_dir.glob(*.jpg)): stem img.name.split(_)[0] # driver_phone_00235.jpg - driver groups[stem].append(img.stem) all_stems [] for imgs in groups.values(): all_stems.extend(imgs) random.seed(42) random.shuffle(all_stems) total len(all_stems) train_set set(all_stems[:int(total * train_ratio)]) val_set set(all_stems[int(total * train_ratio):int(total * (train_ratio val_ratio))]) test_set set(all_stems[int(total * (train_ratio val_ratio)):]) lists { train: train_set, val: val_set, test: test_set, } for name, stems in lists.items(): with open(f{name}.txt, w) as f: for stem in sorted(stems): f.write(f{image_dir / (stem .jpg)}\n)脚本按文件名首个下划线前的字段聚类比如driver_、office_、street_把同一段采集来源的视频帧捆成一组再切分。random.seed(42)保证不同机器上划分结果一致。test_set先留在那里不用等第一版模型训完后做最终验收不要过早消费它。这样分组的前提是文件名前缀能代表采集片段。如果某个前缀混入了不同批次的图片分组就不准。折中方案是按“前缀 图片拍摄时间戳”双重聚类脚本会多几十行但更稳。2.4 转换与划分后的文件清单核对进训练之前花两分钟核对一遍省得训练一个小时后才发现数据路径是错的。核对项整理成下表核对项命令预期结果标签与图片数量一致ls labels_for_yolo | wc -l和ls images/*.jpg | wc -l两边数量相等空标签文件数量find labels_for_yolo -name *.txt -empty | wc -l允许少量但要知道是哪些坐标范围是否越界awk {if ($30 || $31) print} labels_for_yolo/*.txt无输出train与val是否有交集comm -12 train.txt val.txt无输出空txt文件通常就是difficult过滤太狠导致的这类图片训练时会被YOLO跳过样本会少几个不影响流程但要知道原因。坐标越界的检查建议直接在转换脚本里做发现越界就打印对应的XML文件名回头去查标注源。到这里VOC标注已经变成YOLO能直接吃的txttrain/val/test按采集片段隔离开路径清单也核对过了。下一步是真正把训练跑起来。3. 用5364张图跑通YOLOv8训练data.yaml、训练命令与收敛判断3.1 先把环境和data.yaml配好相对路径、类别数、图片目录环境这边Ultralytics YOLOv8依赖Python 3.8以上和PyTorch 1.8以上有NVIDIA显卡就装CUDA版PyTorch。我一般在conda环境里一次性装好conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics torch torchvision如果只用CPU训练这套5364张图也不是不能跑但一个epoch要好几分钟一百个epoch下来时间成本太高。我建议至少有一张8GB显存的卡哪怕是老款的GTX 1080Ti都行训练速度能快出一个数量级而且后面调小目标分辨率时显存大是硬道理。数据配置文件是训练的第一步。在数据集根目录放一个phone_call.yaml# 数据集根路径建议写绝对路径避免yolo进程的工作目录不同导致图片找不到 path: /home/user/phone_call_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: phone_calltrain和val字段写的是图片目录Ultralytics会到对应目录旁找同名的txt标签。如果你用的是上一节生成的txt列表文件也可以直接写train: train.txt效果一样。nc: 1和names里的ID必须和转换脚本里的CLASS_MAP对齐一旦这里写成两个类模型输出维度就多一维训练会变慢且类别编号对不上。写错了会在第一个epoch报维度不匹配即使没报错后面mAP也是乱的。3.2 训练命令逐条拆解从模型选型到超参设置我常用的一条训练命令长这样yolo detect train \ dataphone_call.yaml \ modelyolov8n.pt \ epochs100 \ imgsz1280 \ batch8 \ lr00.01 \ cos_lrTrue \ patience30 \ cacheTrue \ projectruns/detect \ namephone_call_v1逐条说明参数含义。modelyolov8n.pt会加载YOLOv8n在COCO上的预训练权重做迁移学习起点。显存紧就保持nano不变12GB以上显存可以换yolov8s.pt或yolov8m.pt精度会涨但训练时间翻倍。打电话检测属于目标小、数量少的任务nano在1280分辨率下也能跑出不错的mAP没有必要一上来就上最大模型。epochs100配patience30意思是连续30个epoch验证指标没刷新就早停不会傻跑满100轮。单类数据集的mAP50-95波动比多类大patience设太小容易误杀训练所以我不建议用默认的15。imgsz这里直接设了1280而不是640原因在下一章专门讲核心是手机目标太小。8GB显存下1280配batch8是极限再往上就会OOM。lr00.01是初始学习率对加载预训练权重的迁移学习来说常规如果是从零训练我一般降到0.001。cos_lrTrue开启余弦退火让学习率在训练后期平滑降低小数据集上收敛更稳。cacheTrue把图片提前缓存进内存。5364张图大概占几百MB内存够就开着能省掉大量磁盘IO等待时间。另外提一个容易被忽略的参数Mosaic数据增强。YOLOv8默认开启把四张图拼成一张训练。如果训练时发现目标被缩得太小、模型老漏检不要把Mosaic整个关掉而是调成mosaic0.5让拼接图和原图各占一半降低尺度畸变。3.3 损失函数曲线怎么看DFL、CIoU与分类损失别只看一个数YOLOv8的损失由三部分组成分类的BCE损失、回归的CIoU损失以及DFL分布聚焦损失。Ultralytics训练时会打印box_loss、cls_loss、dfl_loss三列很多人只盯着总loss看这是不对的。我的观察方法是拆开看。第一box_loss和dfl_loss如果平稳下降说明框的位置在学对。第二cls_loss在单类数据集上通常降得最快因为只有“电话”和“背景”两个选择分类本身简单。第三如果三个loss都在降但val的mAP不涨问题基本不在损失函数而是验证集和训练集的图片分布差异太大回头检查第2章的采集片段分组切分。训练结束后重点看mAP50和mAP50-95两个指标。mAP50是IoU阈值取0.5时的平均精度只关心“大致框中了没有”mAP50-95是从0.5到0.95每隔0.05取一次IoU再平均对框的精确度要求高得多。打电话检测这类告警场景我以mAP50为主因为系统更关心目标是否出现框精不精确可以靠后处理修正。mAP50在0.85以上、mAP50-95在0.5以上模型就可以进入验证阶段。补充一个早停细节。patience对应的验证指标默认是mAP50-95这个指标在单类小目标上的波动比mAP50大很容易触发“看似不涨、其实在涨”的误杀。除了把patience调大训练命令里加save_period10定期存checkpoint防止中间断了没存下模型。Ultralytics每个epoch结束都会保存last.ptbest.pt会自动跟踪最优权重中断续训用resumeTrue加载last.pt就行。训练结束后Ultralytics会在runs/detect/phone_call_v1/下自动生成混淆矩阵和PR曲线。第一次训完如果发现PR曲线在低召回率时就掉头先别急着调参回去看训练集里有没有大量模糊标注——大部分模型不收敛的情况根因在数据质量而不是网络结构。这个在避坑章节会继续说。4. 打电话场景的三个调参点小目标、单目标与迁移学习第3章把流程跑通了但基线mAP50可能只有0.6到0.7。这套数据想在指标上拉到0.9以上有三个点必须处理小目标怎么应对、单类任务怎么压制背景误检、迁移学习怎么冻结层才不浪费预训练权重。4.1 手机在画面里只有几十像素先统计bbox尺寸再决定分辨率训练分辨率怎么定不是拍脑袋先把标注框尺寸分布统计出来。用转换好的txt做一个快速统计import glob import numpy as np widths, heights [], [] for txt in glob.glob(labels_for_yolo/*.txt): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) 5: continue # 第4、5列是归一化宽高这里按常见监控分辨率1920x1080还原成像素 widths.append(float(parts[3]) * 1920) heights.append(float(parts[4]) * 1080) widths np.array(widths) heights np.array(heights) print(中位宽:, np.median(widths), 90分位宽:, np.percentile(widths, 90)) print(中位高:, np.median(heights), 90分位高:, np.percentile(heights, 90))打电话检测场景里驾驶监控1920×1080画幅中手部区域往往只有100×100像素公交车前门摄像头拍到的乘客手机可能只有40×60像素。40×60的目标在640×640输入下被压缩成13×20经过YOLOv8的五次下采样特征图上只剩一两个像素点检测基本靠蒙。这也是很多人在这个数据集上把imgsz调到1280后mAP50突然涨五六个点的原因。不过imgsz1280的显存开销不能忽略。我的经验8GB显存用imgsz960、batch812GB以上用1280、batch8。训练时间大约翻倍但对小目标的收益非常直接。还有一个辅助手段是调整Mosaic比例。Mosaic把四张图拼接后再缩放小目标被缩得更小容易让模型学到错误的尺度感。把mosaic0.5试一下保留一半原图不做拼接模型对小目标的尺度感知会稳很多。4.2 单类别训练时的背景误检压制单类任务看起来简单实际翻车点集中在背景误检。负样本没有显式标注模型只能靠“图里没有标注框”来隐式学习背景。当背景里出现和手机颜色、纹理接近的物品时比如深色对讲机、方向盘上的按键、白色茶杯模型会持续输出高置信度框。我一般从两个方向压制。第一是往训练集里补负样本单独建一个background/目录放两百张不含打电话行为的监控截图对应txt留空。空标签文件不会产生反向传播信号但图片本身参与了背景分布的学习误检能压下来不少。第二是推理阶段的阈值策略训练时无所谓部署时把conf0.3提到conf0.5宁可漏几个小目标也不要一个画面里冒三四个假框。压完背景误检后一定要回头看一眼PR曲线尾部。如果召回率到0.9附近精度掉得很猛说明大量低置信度预测是误检。这时候加负样本比调阈值更根本。4.3 迁移学习冻结层与学习率策略这套数据质量整体在线但只有5364张不算大。迁移学习的策略直接决定训练效率。我习惯第一版加载yolov8n.pt后先冻结backbone跑20个epoch让neck和head先适应单类任务再解冻所有层把学习率降到之前的十分之一继续跑完剩余epoch。冻结backbone在Ultralytics里很轻量训练命令加一个freeze10就行yolo detect train \ dataphone_call.yaml \ modelyolov8n.pt \ epochs100 \ imgsz1280 \ batch8 \ freeze10 \ lr00.01freeze10代表冻结前10层也就是backbone的绝大部分训练时这部分参数不动。冻结层不参与反向传播单卡显存能再省一点。学习率方面冻结阶段用lr00.01没问题解冻之后如果发现val_loss震荡明显重新加载best.pt把lr00.001再续训一轮通常能稳下来。迁移学习还要注意一点预训练权重和模型结构必须匹配。yolov8n.pt对应YOLOv8n结构如果换成了yolov8s.pt但配置里还是n的yaml加载时要么报shape不匹配要么模型前向输出错乱却不报错。这是特别常见的新手翻车点。我在团队里要求所有训练脚本的model参数只写权重文件名不额外改yaml就是这个原因。5. 训练翻车避坑这些坑我替你踩过按现象对号入座5.1 坐标越界xmax比图像宽还大loss曲线直接飞掉现象训练第一个epoch还没跑完loss出现NaN终端里刷满坐标相关的warning。原因原始XML里部分xmax、ymax超出了图像尺寸转换脚本没做裁剪归一化坐标出现了大于1的值。YOLO在计算IoU时遇到非法框梯度直接爆炸。另一个常见诱因是标注员把框拉到了画布外面保存的坐标带负值。解决转换脚本里加一个clip操作把所有坐标限制在图像范围内同时把越界情况打印出来人工确认。补丁式写法如下xmin max(0, float(bndbox.find(xmin).text)) ymin max(0, float(bndbox.find(ymin).text)) xmax min(width, float(bndbox.find(xmax).text)) ymax min(height, float(bndbox.find(ymax).text))clip之后再判断一次如果框宽或框高小于5像素说明这个框已经没有训练价值直接跳过。clip对边缘露出的半截手机会稍微压小框的大小但比让训练彻底崩溃好得多。5.2 batch size太小导致BN层不稳验证集指标像锯齿现象loss正常下降但每个epoch的验证mAP波动巨大val_loss曲线像锯齿。更诡异的是同一个模型在同一张图上重复推理两次结果不完全一致。原因batch size设成2甚至1时BatchNorm层的统计量在每步之间剧烈变化。训练阶段BN的滑动均值不收敛验证时就出现这种“玄学”抖动。8GB显存上调大imgsz后很容易被迫用小batch。解决优先保证batch不小于8。显存不够就先降imgsz再不行就换nano模型不要牺牲batch。如果4是极限可以考虑用累积梯度模拟更大batch等效于加大batch size但显存不变。机器显存拿不准时Ultralytics里可以用batch-1自动探测最大安全batch我一般先用这个值跑一轮再手动调小一档留余量。5.3 验证集和训练集没隔离mAP虚高得离谱现象训练集mAP50到0.92验证集mAP50也0.88看着非常完美。但把模型拿到现场相机帧上测试漏检率和误检率都高得吓人。原因用了随机切分同一段视频的相邻帧同时进了train和val模型相当于背了答案。监控视频抽帧的数据尤其致命相邻帧的构图差异太小验证指标完全失真。解决重新按采集片段分组切分严格保证同一前缀的图片全部落到同一个集合。切完以后用文件名stem做一次交集检查确认train和val没有任何重复样本。这个检查脚本只有三行我每次准备新数据都会跑一遍已经成为固定动作。5.4 预训练权重结构错位loss不降先别急着调学习率现象加载权重后前10个epoch的box_loss纹丝不动mAP一直是0换学习率也没用。原因权重文件与模型结构不匹配。比如用yolov8s.pt的权重配了nano的模型配置某些层的shape对不上。PyTorch加载时有时不报错但模型前向传播的输出已经错乱训练自然不收敛。解决模型结构和权重来源必须统一训练命令里modelyolov8n.pt时不要再改模型yaml。从其他框架转换过来的权重先打印state_dict的key前缀和shape逐层核对。另外loss完全不动也有可能是学习率设太低比如lr00.0001。区分这两类问题有个简单办法打印第一个epoch的梯度范数梯度范数接近0说明是学习率问题梯度正常但loss不降才是结构问题。6. 验证模型不是在测试集上跑一遍混淆矩阵、PR曲线与TensorRT导出6.1 单类别模型的正确验证姿势单类数据集上mAP数字不是唯一标准。我验证模型时会单独跑一次test集生成混淆矩阵和PR曲线然后看三个位置background列的误检率、召回率0.8附近精度是否掉头、1080p视频里小目标区域的目检结果。yolo detect val \ dataphone_call.yaml \ modelruns/detect/phone_call_v1/weights/best.pt \ imgsz1280 \ conf0.3 \ plotsTrueplotsTrue会生成混淆矩阵、F1曲线和PR曲线。如果conf0.3下背景误检偏高部署时把阈值提到0.5。这不是调模型是调运营策略阈值和模型是两件事。6.2 导出ONNX与TensorRT量化注意与NMS处理确认模型可用后导出TensorRT我习惯走两条腿先导出ONNX再用trtexec转engine。这样中途能检查出量化问题。yolo export modelbest.pt formatonnx imgsz1280 opset17 trtexec --onnxbest.onnx --saveEnginebest.engine --fp16这个流程里最要注意的就是导出尺寸必须与训练一致。1280训练的模型导出时不要用640否则特征图尺度不同输出会整体错乱。用INT8量化时TRT需要校准集直接从训练集抽500张做校准表即可但校准集的图片分布要贴近现场光照。校准集和现场环境差太远INT8精度下降可能超过两个百分点这在打电话检测场景里会直接造成边缘设备漏报属于生产事故级别的问题。我自己每次部署前都有一个固定动作把现场的1080p视频切出十段不重复的片段用导出的engine跑一遍统计误检和漏检的帧编号。然后对比FP16和INT8在同一段视频上的结果FP16掉点不明显就量产明显就继续用FP16。这个习惯替我挡掉了好几个项目的返工也希望帮到你。本文还有配套的精品资源点击获取
返回列表