尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv3三类别车辆检测实战:从VOC格式转换到模型训练

YOLOv3三类别车辆检测实战:从VOC格式转换到模型训练 简介面向车辆检测任务的YOLOv3训练数据集覆盖car、bus、truck三个类别适用于自动驾驶、交通监控、智能安防等场景的模型训练与算法验证。共5380个文件包含1793张JPG图片及对应的1794个TXT标注与1793个XML标注两种标签格式便于接入不同训练流程TXT简洁易解析XML遵循PASCAL VOC标准含更丰富元数据。包体约603.55MB作为中等规模数据集可支撑YOLOv3等目标检测模型的训练与调优。已有1011人学习下载。通过该数据集可快速构建车辆检测训练集省去自行采集与标注的时间成本对照图片与边界框标签还能深入理解YOLOv3的网格检测原理及多类别定位逻辑适合目标检测入门者及需要车辆样本的开发者使用。1. YOLOv3车辆检测数据集三类别先看它的标注长什么样车辆检测是安防、交通流量统计、自动驾驶感知里的基础任务三类别车辆检测数据集对应的是轿车、巴士、货车或类似划分比单个car类更贴近真实路口场景。很多人在拿到一个YOLOv3车辆检测数据集的RAR压缩包后第一反应是解压后直接扔进训练脚本结果Loss不降、mAP上不去问题往往出在没搞清标注格式与类别映射。我一般会先把标注文件、类别文件、图片三者的对应关系列清楚再决定是直接训练还是先做格式转换。三类别的主要意义在于YOLOv3的每个网格单元只能预测固定数量的边界框类别越多目标框的语义区分压力越大。只做三类别既能保证模型在嵌入式设备上的推理速度又能在车辆细分类别之间保持足够的区分度。适合的人包括刚接触目标检测的工程师、需要快速搭建交通场景原型的产品团队以及想评估YOLOv3在车辆检测基线效果的研究者。下面直接拆解这个数据集在训练前最需要处理的几个环节。2. 数据格式整理从RAR解压到VOC格式与YOLO训练格式的转换2.1 解压后的目录图片、标注文件与类别映射拿到三类别.rar这类车辆检测数据集常见目录结构是JPEGImages原图、AnnotationsXML标注、ImageSets/Maintrain、val、trainval文件列表也有直接给TXT标注的版本。如果是VOC风格每张图片对应一个同名的XML文件里面保存了object节点每个节点包含name标签名和bndbox坐标。要先确认name到底写了什么值可能是英文也有可能是中文比如car、bus、truck也可能是sedan、suv、van。不要想当然直接用下面几条命令看一眼。unrar x 车辆检测数据集-三类别.rar ls 车辆检测数据集/JPEGImages | head -10 cat 车辆检测数据集/Annotations/000001.xml | grep -E name|xmin|ymin|xmax|ymax以上命令先解压再列出图片目录前10个文件最后查看一张标注XML的标签名和坐标。如果grep输出里只有name没有坐标说明XML结构可能用了VOC之外的字段需要进一步用xmllint或Python解析。查看类别时建议把所有XML里出现的name值去重统计防止数据集里混入了四类、五类的脏标注。2.2 用Python脚本把VOC标签转成YOLO格式YOLOv3训练时用的不是XML而是每张图片对应一个TXT文件每一行是class_id x_center y_center width height坐标全部归一化到0到1之间。转换脚本我一般这样写import os import xml.etree.ElementTree as ET classes [car, bus, truck] # 按类别文件顺序排列 def convert_annotation(xml_file, out_dir): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) yolo_lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue cls_id classes.index(name) bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) base os.path.splitext(os.path.basename(xml_file))[0] with open(os.path.join(out_dir, base .txt), w) as f: f.write(\n.join(yolo_lines)) os.makedirs(yolo_labels, exist_okTrue) for xml in os.listdir(Annotations): if xml.endswith(.xml): convert_annotation(os.path.join(Annotations, xml), yolo_labels)这段脚本先确定类别列表顺序然后在读取每个对象时跳过不在列表里的类别把XML里的绝对坐标除以图片宽高得到归一化中心点坐标和宽高。注意classes列表的顺序必须与后续data文件里的names路径对应否则训练时类别标签会整体错位。转换完成后随机抽样检查几个TXT确保每行的五个数值都在0到1之间同时与图片尺寸匹配。如果数据集本身已经带YOLO格式的TXT也需要检查坐标是否归一化过。有的旧数据集直接给像素坐标YOLOv3训练时Loss会异常大或者NAN概率就是你跳过了这一步。2.3 划分训练集与验证集并生成文件列表YOLOv3训练需要两个txt文件分别记录训练和验证图片的绝对路径。在ImageSets/Main里如果已有train.txt和val.txt可以直接用没有的话用Python按8:2比例随机划分。划分后一定要检查验证集里各类别都有样例尤其三类别样本数量不平衡时概率性划分会让验证集里某种车一辆都没有。paste -d (sed s|^|/data/voc/images/| train.txt) (sed s|^|/data/voc/labels/| train.txt) train_pair.txt实际上只用图片路径列表就够了Darknet会在训练时自动根据图片路径找到同名的标签TXT。我这里习惯把划分好的图片路径写入train.txt和val.txt然后每行都必须是绝对路径相对路径会让读取失败。图片与标签文件名必须严格一致扩展名可以不统一但文件名主体必须相同。生成完后用wc -l确认数量再用一个小循环统计标签文件数量和图片数量发现对不上就先补标注不要带着脏数据进训练。3. YOLOv3训练配置从零跑通三类别车辆检测3.1 准备Darknet环境与预训练权重常见做法是克隆AlexeyAB版的Darknet它在Windows和Linux下都支持得很好。先编译再下载darknet53.conv.74预训练权重这个权重是在ImageNet上分类训练好的backbone不包含检测头。三类别数据集通常量级在几千到几万张从头训练很难收敛而从预训练权重继续训练可以在较短时间达到可用精度。git clone https://github.com/AlexeyAB/darknet.git cd darknet sed -i s/OPENCV0/OPENCV1/ Makefile sed -i s/CUDNN0/CUDNN1/ Makefile make -j8 wget https://pjreddie.com/media/files/darknet53.conv.74上面命令打开OpenCV和CUDNN开关然后编译并下载backbone权重。如果你没有GPU环境也可以只用CPU训练但要把batch和subdivisions调到更小训练速度会慢很多。建议哪怕是单卡GTX 1060级别三类别、输入416x416也可以接受。3.2 修改cfg文件类别数、filters与数据集路径YOLOv3的cfg文件里必须修改两处关键数字每个[yolo]层上面的filters3*(classes5)以及每个[yolo]层的classes。三类别时filters3*(35)24。在cfg里搜索filters时前面的卷积层也有filters只有那些紧挨着[yolo]的卷积层才需要改。确认方法是看[convolutional]层的size1且stride1然后向下一个[yolo]输出。搜索classes也会出现在别的位置但检测头中的classes要改为3。[convolutional] size1 stride1 pad1 filters24 activationlinear [yolo] mask 0,1,2 anchors 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 classes3上面这段是yolov3.cfg末尾检测头的关键配置。filters就是由classes计算出来的改classes不动filters训练时会直接报维度不匹配。与此同时要新建obj.names和obj.dataclasses 3 train /data/vehicle/train.txt valid /data/vehicle/val.txt names /data/vehicle/obj.names backup /data/vehicle/backup/obj.names里三行分别是三个类别名称顺序必须与前面转换脚本里的classes列表一致。backup目录会被自动创建但提前建好可以避免权限问题。路径不要用相对路径Darknet对相对路径的解析基于当前工作目录稍不注意就会报Cant open file。3.3 启动训练并看懂日志里的关键信号训练命令很简单./darknet detector train obj.data yolov3.cfg darknet53.conv.74 -gpus 0,1。这里-gpus 0,1指定多卡单卡可以不写。启动后观察前几百轮v3 (iou loss, Normalizer: (iou: 0.75, obj: 1.00, cls: 1.00) Region 82 Avg IOU: 0.73, Class: 0.92, Obj: 0.65, No Obj: 0.21, .5R: 0.84, .75R: 0.56, count: 14Avg IOU表示预测框与真实框的平均交并比开始训练时在0.3到0.5之间正常随着训练会逐渐升到0.7以上。Class表示类别预测置信度三类别数据如果这个值始终低于0.7要检查标注框是否太小或类别不均衡。count: 14代表当前批次的真实目标数如果频繁出现count: 0说明这个batch里没有目标需要调大batch或检查数据加载路径。训练日志里Loss下降速度会从几十逐渐降到个位数并不需要死等Loss到0只要看到mAP指标提升就可以停止。3.4 训练中断后的续训训练过程经常会因为断电、显存溢出等问题中断。Darknet会在backup目录下保存yolov3_last.weights每10000轮保存一个yolov3_10000.weights。续训时直接把这个权重当作初始权重来加载但要保持cfg和data文件完全一致。我曾经遇到过续训后Loss比中断前高很多的情况后来发现是改了learning_rate或batch导致学习率调度器重置。续训的规则是尽量保持命令行参数和cfg不变只把第一个权重参数换成last权重。4. 关键训练参数调整与三类别数据集扩充策略4.1 与YOLOv3车辆检测强相关的参数设置YOLOv3的训练参数主要在cfg文件开头几行三类别车辆数据集的参数设置与通用目标检测略有不同。下面给出一张常用参考表基于输入尺寸416x416、单卡12GB显存写出的建议值。参数推荐值说明batch64总batch多卡时会按卡数拆分subdivisions16把batch分成16份载入显存不足时增大该值width / height608 / 416416速度更快608小目标精度更高max_batches12000一般取类别数乘以4000防止过拟合的同时给足收敛空间steps9600, 10800max_batches的80%和90%位置衰减学习率learning_rate0.001微调时用0.0001更保险subdivisions每增大一倍单次送入GPU的图片数就减少一半显存压力变小但因为梯度累计的关系训练时间变化不大。车辆检测数据集里的目标普遍比COCO里的目标更大416输入通常够用不需要盲目拉到608。如果数据集里有大量远距离小目标优先考虑608或增加random1来多尺度训练。4.2 类别不均衡的调整技巧三类别车辆数据集里最常见的问题是轿车太多、巴士很少或者只有货车和轿车的图片导致模型对少数类召回率极低。第一层次的做法是重复少数类样本简单但容易过拟合。第二层次是做数据增强YOLOv3本身在cfg里用angle、saturation、exposure、hue控制随机扰动但也可以通过离线方式增加少数类的镜像、亮度变化。angle0 saturation 1.5 exposure 1.5 hue.1angle0表示不做随机旋转因为车辆检测场景里倒置和旋转90度的车辆没有实际意义开着旋转反而会误导模型。saturation和exposure的取值范围在0到2之间1.5能让模型对逆光和阴影更鲁棒。hue不宜过大0.1已经足够。对三类别数据我还常用max_batches配合类别数来做自动学习率调整steps9600,10800对应max_batches12000的80%和90%这是Darknet官方建议的通用策略。对三类别这种简单任务12000轮足够加到15000轮反而容易在验证集上过拟合。4.3 迁移学习冻结backbone训练检测头当数据集数量和标签质量都不够好时直接用预训练权重训练容易让backbone的特征漂移。常见做法是先冻结前75层只训练检测头训练3000轮后再放开全网络微调。Darknet里没有直接的freeze参数但可以通过修改cfg的stopbackward实现或者简单一点用一个小学习率直接全网络微调。我一般会直接全网络训练因为车辆检测数据集的域与ImageNet相差不大预训练权重里的通用特征对车辆边缘、车轮、车窗依旧有效。在obj.data里还可以设置max_chart_loss来触发Early Stopping比如设成50Loss连续多次超过50就提前结束防止梯度爆炸。对三类别简单任务Loss如果一开始就冲到几千优先检查标签坐标归一化。这一步很多人忽略结果把像素坐标当成归一化坐标训练出的模型预测框全跑到图片角落。5. 模型评估、误检分析与训练中的常见坑5.1 用Darknet自带的mAP命令逐类评估三类别训练结束后用验证集计算mAP是验证模型质量的必要步骤。Darknet提供了一个内置计算命令要求在obj.data里配置好valid对应的图片路径文件。./darknet detector map obj.data yolov3.cfg backup/yolov3_final.weights输出结果会按类别分别列出Precision、Recall和AP值最后给出一行mean average precision (mAP0.50) 0.8920。对三类别车辆检测我的经验是mAP0.5至少应该到0.8才算基本可用0.9以上可以投放比较严格的道路场景。逐类看AP如果某个类别AP明显低多半是该类样本数量不足。此时不要急着改网络结构先在数据集中补加该类别、且目标尺寸不同的样本比增加anchor数量更有效。5.2 预测时的NMS阈值与置信度阈值调节训练出的权重在推理时还需要指定-thresh参数控制置信度阈值以及cfg里nms_iou_thresh控制NMS的交并比阈值。这两个参数的调节效果差别很大。./darknet detector test obj.data yolov3.cfg backup/yolov3_final.weights -ext_output -thresh 0.25 data/test.jpg-thresh越低漏检越少但误检越多车辆检测场景里适合先设0.25做定性和定量分析。如果追求精度而允许少量漏检调到0.45以上。nms_iou_thresh过大会让同一辆车输出多个框过小又会让重叠的车辆只保留一个。三类别车辆数据集中轿车和货车并排停放时会大量重叠nms_iou_thresh0.45是相对稳妥的起点。5.3 训练集与验证集划分导致的误检很多人在设计车辆检测实验时把同一个视频连续帧的图片同时放进了训练集和验证集导致mAP虚高部署到新场景后跌掉十几个点。正确做法是让训练集和验证集来自不同视频或不同时间段。如果数据集本身只有一个连续道路监控视频可以按地点名称划分为两个集合而不是随机按帧划分。我在处理三类别车辆数据时还遇到过一类问题验证集里有一辆模型没见过的红色双层巴士模型把它的车窗识别成了多个car。这不是模型学习能力问题而是数据集中巴士样本太少。此时可以计算每张图片的目标数量把包含巴士样本的所有图片全部放入训练集验证集里只保证其他两类有足够样本这样更容易判断模型是否真的学到了巴士特征。6. 把训练好的三类别模型接入车辆检测业务的验证技巧6.1 用视频流快速验证的脚本三类别模型交付前至少要用一段没参与训练的道路视频做端到端验证。我通常用Darknet的darknet_video.py或者直接改成批量图片测试。下面这段代码用OpenCV读入视频帧逐帧调用训练好的网络并在画面上叠加类别标签和置信度。import cv2 from darknet import Darknet cfg yolov3.cfg weights backup/yolov3_final.weights data obj.data net Darknet(cfg) net.load_weights(weights) cap cv2.VideoCapture(test_road.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break detections net.detect(frame, thresh0.3, nms_thresh0.45) for label, conf, bbox in detections: x, y, w, h bbox cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(frame, f{label} {conf:.2f}, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) cv2.imshow(vehicle detection, frame) if cv2.waitKey(1) 27: break cap.release() cv2.destroyAllWindows()这段脚本直接复用了Python版Darknet接口简单有效。注意如果检测到的bbox是(center_x, center_y, width, height)格式cv2.rectangle画出来会偏。确认一下net.detect返回的坐标格式再决定是否需要转换成左上角坐标否则画框位置会跟车辆对不齐。6.2 三个快速判断模型是否可用的指标接入业务前我会在视频里截取200帧并做人工标注然后看三个指标车流方向是否正确、重叠车辆是否被合并、夜间或逆光下是否有漏检。三类别模型只识别车辆和类别不跟踪车辆所以如果车流方向统计需求强烈要在检测结果上接一个简单的IoU时间序列关联。若夜间效果差优先对测试图像做自适应直方图均衡化而不是重新训练。类别名称可以按自己的业务需求改成car、bus、truck外的名称但训练时obj.names和预测脚本里的label显示要一起改否则会在预测代码里出现类别标签错位。部署时如果帧率达不到要求先降输入分辨率而不是换模型YOLOv3在416下跑单卡GPU一般能达到实时CPU环境下再考虑TensorRT或剪枝那才需要动网络结构。本文还有配套的精品资源点击获取
返回列表