
简介面向交通场景车辆与行人的目标检测需求这套资源基于YOLOv7和PyTorch搭建了完整可用的训练与推理方案。模型在超过一万张真实道路图像上完成训练mAP达到百分之九十以上目标类别聚焦person和car并附带PR曲线、loss曲线和训练日志便于直观评估与调优。随包另提供五千余张行人车辆检测数据集标注文件同时输出txt和xml两种格式且分别存放在不同文件夹中训练、验证或格式转换都较为省心。整个资源包共一百五十八个文件涵盖python脚本、yaml配置文件、ipynb示例、pt权重、jpg样例图、xml标注和pdf说明包体大小约851MB另含dockerfile、shell脚本、pyc缓存等便于在不同环境下快速复现实验或进行tensorrt、onnxruntime等部署尝试。目前已有2489人学习下载适合自动驾驶、安防监控、智慧交通等方向的研究者或工程开发者参考尤其适合需要现成权重和数据集快速启动项目的人群。1. 从 mAP 90% 的交通场景模型说起如果你接过一个路口车流统计的活儿摄像头架在六米灯杆上画面里同时出现七八辆车和十几个行人遮拦、逆光、远处小目标全凑齐了你就会明白一个现成可用的车辆行人检测权重有多值钱。自己标注两千张图至少搭进去两个人一周时间而这套资源直接把这条链路缩短了YOLOv7 训练好的 person/car 双类别检测模型基于一万多张交通场景数据训练mAP 在 90% 以上附带 5000 多张已标注数据集标签同时给了 txt 和 xml 两种格式。适合做交通监控、园区安防、车流统计的同学拿到之后先用现成权重跑视频流看它的失误模式能否接受再决定要不要在自采数据上微调。2. YOLOv7 的 E-ELAN 与重参数化双类别检测为什么选它车辆行人检测的场景特殊之处在于目标尺度跨度极大远处行人可能只占 20 个像素近处公交车几乎充满画面。所以选骨干网络时多尺度特征融合能力比单纯堆参数量更重要。YOLOv7 正好在这件事上做了针对性的结构设计这也是它即使放到今天依然适合垂直场景微调的原因。2.1 E-ELAN用扩展、打乱、合并提升特征复用E-ELANExtended Efficient Layer Aggregation Network解决的核心问题是传统堆叠式特征融合会让不同层学到重复信息参数效率不高。它的做法概括成三步expand、shuffle、merge cardinality。Expand 先把通道分组扩展每组独立计算Shuffle 在组间做特征重排Merge 再把多组结果合并送到下一阶段。这样设计的效果是每一组卷积学到的特征在语义上有区分度不会做了三组计算却学到同一件事。实际训练中你会发现一个直观现象用同样的显卡、同样的 batch sizeYOLOv7 在车辆行人这类目标数量较多的画面里收敛后的小目标召回通常比同参数量的 YOLOv5 高几个点。原因是 E-ELAN 让浅层细节特征和深层语义特征在 PANet 路径上融合得更好行人这种小目标吃到了更多底层纹理信息。2.2 RepConv 重参数化训练结构和推理结构是两回事RepConv 是 YOLOv7 里另一个对部署友好的设计。训练阶段每个 RepConv 块内部是 3x3 卷积、1x1 卷积和 BN 层三路相加的结构相当于让网络在训练时拥有更强的表达能力。推理阶段这三路可以融合成一个 3x3 卷积计算量直接降下来精度不掉。这解释了为什么同样的权重用 PyTorch 直接推理和导出 ONNX 后推理速度会有明显差异——后者已经完成了结构重参数化。需要留意的是如果你自己动手改网络结构不要动 RepConv 的融合逻辑。常见错误是在自定义 backbone 时把 RepConv 换成普通卷积结果训练收敛变慢、精度掉两三个点。垂直场景微调时最忌讳大改主干。2.3 辅助训练头与动态标签分配容易被忽略的训练细节YOLOv7 训练时存在两个检测头Lead Head 和 Aux Head。Aux Head 只在训练阶段生效推理时完全不参与计算。它的作用是给浅层特征额外的梯度监督尤其是训练早期网络还分不清行人和背景的时候Aux Head 用更宽松的标签分配策略稳住梯度方向。配合动态标签分配每个头拿到的正样本质量和数量是不同的Lead Head 要求更严格的 IoUAux Head 相对宽松。我在微调时踩过一个坑为了省显存有人会把 aux loss 权重直接设成 0结果训练前期 loss 掉得很慢最后 mAP 也上不去。正确做法是保持默认的 aux 权重和标签分配策略优先通过 batch size 和输入尺寸去适配显存。下表是 YOLOv7 与当时主流选择的对比对比维度YOLOv5m6YOLOv7特征聚合方式CSPDarknet PANetE-ELAN 扩展 PAN训练检测头单检测头Lead Head Aux Head部署检测头直接导出RepConv 融合后导出标签分配静态 IoU 阈值动态从粗到细分配迁移学习生态成熟与 YOLOv5 相近工具齐全语义分割、OCR 等任务通常不会选 YOLOv7但在车辆行人这种目标尺度变化大、类别少的纯检测场景它的结构和部署生态是平衡得最好的选择。3. 训练自己的 person/car 检测模型目录、配置与命令拿到项目源码后先别急着跑训练把目录结构和数据格式对齐能省掉后面八成的问题。这套资源的目录里包含yolov7-main项目本体、训练日志文件events.out.tfevents.*、Dockerfile以及几个 Notebook其中compare_YOLOv7_vs_YOLOv5m6_half.ipynb这类对比实验脚本值得打开翻一翻能直观看到作者在不同模型之间做的取舍依据。3.1 工程目录结构与数据摆放训练前先确认你的数据目录是否满足 YOLOv7 的默认读取逻辑。常见做法是把图片和标签放在同一个数据集根目录下通过 train.txt 和 val.txt 指定图片路径列表datasets/ ├── vehicle_person/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ ├── train.txt └── val.txttrain.txt 每一行是一张训练图片的绝对路径YOLOv7 会依据路径自动找同名的 txt 标签文件。注意图片和标签必须同名扩展名可以不同但主文件名要一致例如img_001.jpg对应img_001.txt。如果你拿到的数据集是 xml 标签需要先转成 txt转换脚本在下一章给出。3.2 数据配置文件与类别映射在yolov7-main/data/下新建一个vehicle_person.yaml内容如下train: /absolute/path/to/datasets/train.txt val: /absolute/path/to/datasets/val.txt nc: 2 names: [person, car]nc是类别数这里必须是 2。names列表的顺序就是类别 ID 的映射关系索引从 0 开始person 对应 ID 0car 对应 ID 1。标签文件里每行开头的数字只能是 0 或 1。注意如果原数据集的 xml 里类名是Person或Car转换时全部统一小写否则会被当成未知类别丢弃。3.3 训练命令与超参数选择在 yolov7-main 根目录执行训练python train.py \ --workers 8 \ --device 0 \ --batch-size 16 \ --data data/vehicle_person.yaml \ --img 640 640 \ --cfg cfg/training/yolov7.yaml \ --weights yolov7_training.pt \ --name vp_det \ --hyp data/hyp.scratch.p5.yaml \ --epochs 150参数含义拆开说。--workers 8是数据加载线程数机械硬盘或网络盘环境下建议降到 4避免 IO 阻塞--batch-size 16在 24G 显存、640 输入尺寸下比较稳妥显存不够就降到 8 同时配合--cache-images缓解磁盘读取压力--img 640 640是训练尺寸交通场景建议保持 640不要盲目上 1280显存开销是四倍而对 mAP 的提升在这类数据上往往没有想象的显著。--weights换预训练权重时要注意默认yolov7_training.pt是在 COCO 上多轮训练得到的迁移到车辆行人场景效果好于直接用yolov7.pt。如果你打算在 5000 张数据上从零开始训那我明确建议不要这么做收敛慢且最终精度大概率不如微调。期望的 mAP 90% 以上成绩靠的是「COCO 预训练 一万多张交通数据微调」这个组合。3.4 训练中断恢复与超参数调整节奏训练跑一半机器重启是常态YOLOv7 支持断点续训python train.py --resume runs/train/vp_det/checkpoint/last.pt它会自动读取之前的 epoch、优化器状态和超参数配置无需重新传入 data 和 cfg。我一般会在训练到 100 epoch 左右看一眼 loss 曲线如果 val 的 box_loss 不再下降甚至回升说明开始过拟合这时候把--epochs截断在 120 附近而不是硬跑满 150。另一个节省时间的做法是在最后 10 个 epoch 关闭 Mosaic 增强让模型在接近真实分布的数据上做精细调整这在车辆行人检测里对最终 mAP 有肉眼可见的提升。训练日志会写入runs/train/vp_det/目录其中包含events.out.tfevents.*文件用 TensorBoard 启动tensorboard --logdir runs/train/vp_det浏览器打开 6006 端口重点看 box_loss、obj_loss、cls_loss 三条曲线的 val 分支走向。4. 5000 张数据集的标签工程txt/xml 双格式统一这份资源里数据集的特色是同时提供 txt 和 xml 两种标签格式且分别存放在两个文件夹中。好处是你可以直接对比两种格式的标注是否一致坏处是训练只能认一种必须先做格式统一。标签质量直接决定 mAP 上限这一章把格式差异、转换脚本和检查手段一次讲清。4.1 两种标签格式的差异txt 是 YOLO 系列原生格式每行五个字段类别 ID、归一化中心点 x、归一化中心点 y、归一化宽、归一化高。xml 是 VOC 风格包含对象名、坐标、图片尺寸等结构化信息。两者对比如下对比维度txt 格式xml 格式每行内容class_id, cx, cy, w, hobject 节点内嵌 bndbox坐标表示归一化浮点数绝对像素整数可读性差需脚本查看好可直接打开阅读YOLOv7 训练支持直接支持需转换后支持4.2 xml 批量转 txt 脚本在数据集根目录放一个xml2yolo.py内容如下import xml.etree.ElementTree as ET from pathlib import Path CLASSES [person, car] # 顺序必须与训练 yaml 的 names 一致 def convert_xml_to_yolo(xml_path: Path, out_dir: Path) - None: tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text.strip().lower() if cls_name not in CLASSES: continue cls_id CLASSES.index(cls_name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 四角坐标转中心点加宽高再除以图片宽高做归一化 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h bw (xmax - xmin) / img_w bh (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_dir.mkdir(parentsTrue, exist_okTrue) out_path out_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines)) def batch_convert(xml_dir: Path, out_dir: Path) - None: for xml_file in xml_dir.glob(*.xml): convert_xml_to_yolo(xml_file, out_dir) if __name__ __main__: batch_convert(Path(xml_labels), Path(labels))这段脚本的逻辑是遍历所有 xml 文件读取每个 object 节点的类别名和 bndbox 四点坐标先算中心点和宽高再分别除以图片宽高完成归一化。CLASSES列表的顺序就是训练时的类别 ID 映射这一点务必和vehicle_person.yaml保持一致。另外把cls_name做了.strip().lower()处理就是为了防止源数据里出现Person、CAR这类大小写和空格不一致的情况。4.3 划分训练集与验证集资源自带的数据集可能没有划分 train/val这时候不建议手动拖文件写个简单脚本按比例切分import random from pathlib import Path def split_dataset(img_dir: Path, val_ratio: float 0.15, seed: int 42): imgs sorted([p for p in img_dir.iterdir() if p.suffix in (.jpg, .jpeg, .png)]) random.seed(seed) random.shuffle(imgs) n_val int(len(imgs) * val_ratio) val_list, train_list imgs[:n_val], imgs[n_val:] with open(train.txt, w) as f: f.write(\n.join(str(p.resolve()) for p in train_list)) with open(val.txt, w) as f: f.write(\n.join(str(p.resolve()) for p in val_list)) print(ftrain{len(train_list)} val{len(val_list)}) if __name__ __main__: split_dataset(Path(images))5000 张图按 15% 划分就是 750 张验证集、4250 张训练集这个比例对 mAP 评估足够稳定。划分时确保每张图片都有对应的标签文件否则训练时会报找不到标签的警告虽然不中断进程但会白白浪费计算资源。划分逻辑关键在于先把图片路径全部收集后做 shuffle再按比例切分避免序列前部全是同一场景的连拍图否则验证集会丧失代表性。4.4 标签一致性检查拿到手的数据集如果 txt 和 xml 分别存放第一个动作就是做完整性校验。我常用的检查脚本很短from pathlib import Path def check_consistency(img_dir: Path, txt_dir: Path, xml_dir: Path): imgs {p.stem for p in img_dir.iterdir() if p.suffix in (.jpg, .jpeg, .png)} txts {p.stem for p in txt_dir.glob(*.txt)} xmls {p.stem for p in xml_dir.glob(*.xml)} print(fimages{len(imgs)} txt{len(txts)} xml{len(xmls)}) print(missing txt:, imgs - txts or none) print(missing xml:, imgs - xmls or none)如果missing txt或missing xml不为空说明原数据集存在标注缺失后续训练时要注意对应图片是否要保留。还有一种常见情况是两张不同图片用了相同主文件名但扩展名不同这不会触发上面的报警但会导致标签错配排查时需要对比文件名加扩展名的完整字符串。5. 用曲线判断模型健康度再做 ONNX/TensorRT 部署训练完成后模型的runs/train/vp_det/目录里会留下PR_curve.png、loss_curve.png等结果图取到 weights 后建议先复现一次推理再做部署优化。在 5000 张数据规模下读取 PR 曲线的正确姿势是先看 AP0.5 和 AP0.5:0.95 的差值。差值在 8 个点以内说明模型对检测框定位比较精准如果 AP0.5 高但 AP0.5:0.95 明显掉下去说明框的 IoU 精度不足典型原因是训练时iou_thres设置偏高导致回归不够精细。loss 曲线方面重点观察val/obj_loss车辆行人这类多目标场景里obj loss 是最敏感的指标它上升通常先于 box_loss 反映过拟合。部署分两步走。先导出 ONNXpython export.py \ --weights runs/train/vp_det/weights/best.pt \ --img 640 640 \ --batch 1 \ --dynamic \ --include onnx--dynamic让导出的 ONNX 支持动态 batch方便服务端按请求量调整 batch。项目仓库里的YOLOv7-Dynamic-Batch-ONNXRUNTIME.ipynb就是配合这套导出方式做的运行时测试建议对照着跑一遍。想要更高吞吐走 TensorRT。注意仓库里提供的是YOLOv7-Dynamic-Batch-TENSORRT.ipynb这个 Notebook 核心是动态 batch 的 engine 构建适合视频流这种单帧、多帧混合请求的场景。一个实用的技巧是TensorRT 的 engine 构建时要指定显存工作空间大小默认值偏保守你可以在构建参数里把workspace调到显存上限的 70%比如 24G 显卡填 16G能明显提高 engine 执行时的算子融合程度。另一个更隐蔽的坑是 END本文还有配套的精品资源点击获取