Keras+YOLO车辆检测实战:从训练到TensorRT实时推理

发布时间:2026/10/11 18:51:47
Keras+YOLO车辆检测实战:从训练到TensorRT实时推理
简介这份资源面向计算机视觉与深度学习方向的初学者及进阶开发者提供一套基于Keras框架与YOLO算法的车辆检测完整实战项目可用于智能交通、自动驾驶环境感知等场景的学习与复现。压缩包共15个文件约27.42MB包含2个Python脚本与1个Jupyter Notebook用于模型构建和训练推理7张jpg与2张png图片展示检测效果另有mp4演示视频、README说明文档及工具模块便于对照理解算法流程。目前已有74人学习下载。项目将YOLO的目标检测任务以回归方式实现涵盖网络结构搭建、模型训练、参数调整与效果评估等关键环节读者可借助源码与可视化结果掌握Keras构建检测网络的细节并参考utils工具代码与notebook逐步复现整个流程适合作为课程设计或入门实战的参考案例。1. 车辆检测项目拆解KerasYOLO 这套组合到底能跑出什么效果路上跑的每一辆车从监控画面里被框出来那一刻背后其实是一串很朴素的工程决策用什么框架搭、选哪版 YOLO、数据怎么标、训练多久、推理能不能实时。这个项目标题里的「车辆检测-基于KerasYOLO实现的车辆检测算法」说的就是用 Keras 作为深度学习前端框架把 YOLO 系列目标检测算法落地到车辆识别这个具体场景上再配一份能跑通的源码和效果展示。它解决的不是「从零发明检测算法」的问题而是「我手上有一批车辆图片或视频怎么用现成轮子快速训出一个能用的检测器」。适合谁适合刚入门 yolo 目标检测、想拿一个完整项目练手的学生和初级算法工程师也适合需要快速验证车辆检测可行性、不想一上来就啃 PyTorch 分布式训练的老手。Keras 的封装让网络搭建和训练循环写起来短YOLO 的回归式检测让推理速度够看两者叠在一起就是一条从数据到效果的短路径。2. Keras 与 YOLO 的选型逻辑为什么不是 PyTorch 也不是 SSD2.1 Keras 做检测框架的利与弊Keras 在目标检测项目里常被低估。很多人一提到检测就默认 PyTorch觉得 Keras 只能做分类。实际上 Keras 作为高层 API把层定义、损失函数、优化器、回调都收得很紧写一个 YOLO 的 backbone 加检测头代码量比原生 TensorFlow 少一大截。对于车辆检测这种输入尺寸固定、类别数少通常就 car、bus、truck 几类、不需要复杂自定义算子的任务Keras 的表达能力完全够用。但它的边界也很清楚。YOLO 的损失函数里包含坐标回归、置信度、类别三部分其中坐标部分常用 CIOU 或 GIOUKeras 原生没有这些需要自己写自定义损失。另外 YOLO 的 decode 过程涉及 anchor 匹配和网格偏移如果全用 Keras 的 Lambda 层塞进去模型保存和跨平台推理会变麻烦。我一般会建议训练阶段用 Keras 图个快导出阶段把 decode 逻辑挪到后处理脚本里模型只输出原始特征图。选 Keras 的另一个理由是生态。Keras 可以直接吃 TensorFlow 的 tf.data 管道车辆检测数据集动辄几万张图tf.data 做 shuffle、batch、prefetch 比手写 DataLoader 省心。而且 Keras 模型转 TensorFlow SavedModel 再转 TensorRT 的路径是通的后面要上边缘设备也有退路。2.2 YOLO 版本选择与车辆检测的匹配度YOLO 到现在已经迭代了很多版车辆检测项目里常见的是 YOLOv3、YOLOv4、YOLOv5 以及更新的 v8。这个项目标题没写具体版本但从 Keras 实现的角度看v3 和 v4 的 Keras 复现最多v5 官方是 PyTorchv8 也是 PyTorch 生态。所以如果坚持 Keras大概率是 v3 或 v4 的 Keras 实现。车辆检测对版本不敏感对输入分辨率和 anchor 设计更敏感。YOLOv3 用三个尺度输出13x13、26x26、52x52分别对应大、中、小目标。车辆在监控画面里通常占中等偏大26x26 和 13x13 两个头贡献最大。anchor 方面COCO 预训练的 anchor 直接拿来用也能跑但如果你的数据里车都是远景小目标最好用 k-means 重新聚类一遍自己的标注框。提示不要盲目追新版本。车辆检测任务里YOLOv3 加一个更好的 backbone比如 Darknet53 换 MobileNetV2往往比直接上 v8 更容易在 Keras 里调通。2.3 从标题到落地项目源码通常包含什么一个完整的「KerasYOLO 车辆检测」项目源码按我的经验目录结构一般长这样目录/文件作用是否必须train.py训练入口读配置、建模型、跑 fit必须detect.py推理脚本读图片/视频画框输出必须model/网络定义backbone head必须utils/anchor 解析、NMS、画框工具必须data/标注文件、类别名、样本图必须weights/预训练权重和训练后权重必须config.py超参、路径、输入尺寸建议有evaluate.pymAP 计算可选但推荐拿到源码后第一件事不是跑 train而是跑 detect用作者给的预训练权重先看效果。这一步能帮你确认环境对不对、后处理逻辑有没有问题。很多项目跑不起来不是模型错是路径和依赖版本对不上。3. 用 Keras 搭 YOLO 车辆检测的最小可跑流程3.1 环境准备与依赖版本锁定Keras 的版本坑比 YOLO 本身还多。TensorFlow 2.x 之后 Keras 被收编为 tf.keras但网上很多 Keras YOLO 代码是独立 Keras 时代写的直接混用会报AttributeError。我一般这样锁# 建议用 conda 建独立环境避免和系统 Python 打架 conda create -n vehicle_yolo python3.8 -y conda activate vehicle_yolo # TensorFlow 2.5~2.8 对 Keras YOLO 兼容性较好 pip install tensorflow2.8.0 pip install opencv-python4.5.5.64 pip install numpy1.21.6 pip install pillow9.0.1 pip install tqdm逻辑说明Python 3.8 是 TensorFlow 2.8 支持较好的版本numpy 锁在 1.21 是因为 1.24 之后np.float等别名被移除老代码会崩。opencv 用 4.5.x 是因为 4.6 之后部分cv2.dnn接口有变动虽然后处理不一定用到 dnn但画框和读视频稳定。参数说明如果你机器有 NVIDIA 显卡把tensorflow换成tensorflow-gpu2.8.0并确认 CUDA 11.2 和 cuDNN 8.1 已装。没有显卡也能跑只是训练慢推理单张图几秒。3.2 车辆数据集标注与 YOLO 格式转换车辆检测公开数据集不少但项目源码通常自带一份小样本。如果你要换自己的数据标注用 LabelImg 即可导出 YOLO 格式。YOLO 标注是每张图一个 txt每行class_id x_center y_center width height全部归一化到 0~1。转换脚本我一般这么写import os import xml.etree.ElementTree as ET # 把 VOC 的 xml 转成 YOLO 的 txt def voc_to_yolo(xml_path, classes, out_dir): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue cls_id classes.index(cls) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 归一化并转中心点格式 x_center (x1 x2) / 2.0 / w y_center (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) name os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, name .txt), w) as f: f.write(\n.join(lines)) classes [car, bus, truck] # 调用时遍历 xml 目录即可逻辑说明VOC 的坐标是左上角和右下角绝对像素YOLO 要的是中心点加宽高且归一化。除以 w 和 h 就是归一化中心点用 (x1x2)/2 算。类别 id 从 0 开始和训练时num_classes对应。参数说明classes列表顺序必须和训练配置里一致否则框对了类别错。out_dir要和图片目录同级YOLO 读数据时按文件名找同名 txt。3.3 模型构建backbone 与检测头的 Keras 写法YOLOv3 的 Keras 实现核心是 Darknet53 加三个尺度的检测头。下面是一个简化但可跑的检测头写法import tensorflow as tf from tensorflow.keras import layers, Model def yolo_head(feature, num_anchors, num_classes): # 每个 anchor 预测 5 num_classes 个值 # 5 tx, ty, tw, th, obj out layers.Conv2D(num_anchors * (5 num_classes), kernel_size1, paddingsame, kernel_initializerzeros)(feature) return out def build_yolo(input_shape(416, 416, 3), num_classes3): inputs layers.Input(shapeinput_shape) # 这里用 MobileNetV2 做 backbone比 Darknet53 轻适合车辆检测 backbone tf.keras.applications.MobileNetV2( input_tensorinputs, include_topFalse, weightsimagenet ) # 取两个中间层和一个末端做多尺度 c4 backbone.get_layer(block_6_expand_relu).output # 52x52 c5 backbone.get_layer(block_13_expand_relu).output # 26x26 c6 backbone.output # 13x13 # 三个尺度各接一个检测头anchor 数按 3 算 head_l yolo_head(c6, 3, num_classes) head_m yolo_head(c5, 3, num_classes) head_s yolo_head(c4, 3, num_classes) model Model(inputs, [head_l, head_m, head_s]) return model model build_yolo() model.summary()逻辑说明MobileNetV2 的block_6_expand_relu输出步长 8对应 52x52block_13_expand_relu步长 16对应 26x26末端步长 32对应 13x13。三个头分别负责小、中、大目标。每个头输出通道数是3 * (5 num_classes)3 是每个网格的 anchor 数。参数说明input_shape用 416x416 是 YOLO 的经典尺寸能被 32 整除。num_classes3对应 car、bus、truck。kernel_initializerzeros是 YOLO 的惯例避免初始预测偏移过大。如果你用 Darknet53把 backbone 换掉即可但要注意 Darknet53 在 Keras 里需要自己实现残差块。3.4 训练循环与损失函数的关键参数YOLO 的损失函数是三个头的损失相加每个头又分 obj、cls、box 三部分。Keras 里用model.compile加自定义 loss 最直接def yolo_loss(y_true, y_pred): # y_true 和 y_pred 形状都是 (batch, grid, grid, anchors, 5num_classes) # 这里简化写实际要按 mask 区分正负样本 obj_mask y_true[..., 4:5] noobj_mask 1.0 - obj_mask # 坐标损失用均方误差置信度用二值交叉熵 box_loss tf.reduce_sum(obj_mask * tf.square(y_true[..., :4] - y_pred[..., :4])) obj_loss tf.reduce_sum(obj_mask * tf.square(y_true[..., 4:5] - y_pred[..., 4:5])) noobj_loss tf.reduce_sum(noobj_mask * tf.square(y_true[..., 4:5] - y_pred[..., 4:5]) * 0.5) cls_loss tf.reduce_sum(obj_mask * tf.square(y_true[..., 5:] - y_pred[..., 5:])) return (box_loss obj_loss noobj_loss cls_loss) / tf.cast(tf.shape(y_true)[0], tf.float32) model.compile(optimizertf.keras.optimizers.Adam(1e-4), lossyolo_loss) model.fit(train_dataset, epochs50, validation_dataval_dataset)逻辑说明obj_mask标记哪些网格有目标有目标才算坐标和类别损失没目标的只算置信度损失且权重降一半防止负样本淹没正样本。除以 batch size 是为了让 loss 量级稳定。参数说明学习率 1e-4 是 Adam 的常用起点如果 loss 震荡降到 1e-5。epochs 50 是车辆检测小数据集的常见值数据多可以加到 100。batch size 看显存8G 显存用 8 或 16。注意上面是简化版 loss实际项目里还要处理 anchor 匹配和正负样本判定。如果源码里已经有完整 loss直接用源码的不要自己重写。4. 车辆检测训练与推理的避坑排查4.1 loss 不下降或变成 NaN现象训练几个 epoch 后 loss 突然变 nan或者一直卡在几百不降。原因最常见的是学习率太大或者标注文件里有宽高为 0 的框归一化后除零。另外如果 anchor 尺寸和实际目标差太远正样本匹配不上loss 也会僵住。解决先把学习率降到 1e-5 试。然后检查标注用脚本扫一遍所有 txt把width或height为 0 的行删掉。anchor 用 k-means 在自己的数据上重新聚一遍一般 9 个 anchor 分三组每组对应一个尺度。4.2 推理时框全挤在一起或类别错乱现象detect 脚本跑出来框密密麻麻叠在画面中间或者车被标成 bus。原因decode 阶段网格偏移算错或者类别 id 和训练时不一致。Keras 输出的是原始特征图需要 sigmoid 激活 tx、ty、obj、clstw、th 要 exp 再乘 anchor。如果忘了 sigmoid坐标会乱飞。解决检查后处理代码确认x (sigmoid(tx) grid_x) / grid_ww anchor_w * exp(tw) / input_w。类别错乱就核对classes列表顺序训练和推理必须一模一样。4.3 显存不够或训练速度极慢现象batch size 设 8 就 OOM或者一个 epoch 跑半小时。原因输入尺寸 416 其实不大OOM 多半是 tf.data 没设 prefetch 或者模型里中间层没释放。速度慢可能是没开 GPU或者数据管道在 Python 里做增强成了瓶颈。解决train_dataset train_dataset.prefetch(tf.data.AUTOTUNE)增强用 tf.image 的算子而不是 PIL。确认tf.config.list_physical_devices(GPU)有输出。如果还慢把输入降到 320x320车辆检测精度掉一点但速度翻倍。4.4 换自己的数据后 mAP 暴跌现象预训练权重跑 COCO 好好的换自己车辆数据训完 mAP 只有 0.2。原因数据分布差异大比如你的图都是夜间红外预训练权重是白天可见光。或者标注框太松把整个车身加背景都框进去了。解决先冻结 backbone 只训检测头 10 个 epoch再解冻全训。标注框紧贴车身不要留太多背景。如果数据量少于 2000 张做随机裁剪、亮度抖动、马赛克增强YOLO 的 mosaic 增强对小数据集提升明显。4.5 导出模型后推理结果和训练时不一致现象Keras 里model.predict结果正常转成 SavedModel 或 TensorRT 后框全错。原因Keras 模型里如果用了 Lambda 层做 decode导出时 Lambda 的 Python 逻辑不会被序列化导致输出含义变了。解决训练模型只输出原始特征图decode 放在 Python 后处理里。导出时用tf.saved_model.save推理时手动做 sigmoid 和 anchor 解码。这样跨平台最稳。5. 车辆检测进阶用 TensorRT 把 Keras YOLO 推到实时训练完只是第一步车辆检测真正落地要看推理速度。Keras 模型直接跑 TensorFlow 推理在 T4 上 640 分辨率大概能到 30~40 FPS但如果你要接多路 RTSP 监控视频拉流这个速度不够。常见做法是把 Keras 模型转成 ONNX再用 TensorRT 优化。转换路径我一般这么走import tensorflow as tf import tf2onnx # 假设 model 是训练好的 Keras YOLO输入 416x416 spec (tf.TensorSpec((None, 416, 416, 3), tf.float32, nameinput),) model.output_names [head_l, head_m, head_s] onnx_model, _ tf2onnx.convert.from_keras(model, input_signaturespec, opset13) with open(vehicle_yolo.onnx, wb) as f: f.write(onnx_model.SerializeToString())逻辑说明tf2onnx 把 Keras 图转成 ONNX 标准格式opset 13 对 YOLO 的 slice、concat 支持较好。输出三个头后处理在 Python 里做。参数说明input_signature的 batch 设 None 是为了支持动态 batchTensorRT 优化时可以固定成 1 或 8。opset 不要低于 11否则一些激活函数不支持。转完 ONNX 后用 TensorRT 的trtexec做 FP16 量化trtexec --onnxvehicle_yolo.onnx \ --saveEnginevehicle_yolo_fp16.engine \ --fp16 \ --workspace2048 \ --minShapesinput:1x416x416x3 \ --optShapesinput:8x416x416x3 \ --maxShapesinput:16x416x416x3逻辑说明--fp16开启半精度T4 对 FP16 有专门优化速度比 FP32 快近一倍精度掉不到 1 个点。--workspace是显存工作区2048MB 够用。min/opt/maxShapes 定义动态 batch 范围opt 是 TensorRT 调优时用的 batch。参数说明如果你的车辆检测场景是单路视频batch 设 1 就行延迟最低。多路 RTSP 拉流可以 batch 8 或 16吞吐高但延迟增加。640 分辨率下T4 跑 FP16 的 YOLO 车辆检测单路 1080p25 帧大概能支持 4~6 路具体看后处理开销。提示TensorRT 引擎和显卡架构绑定T4 上生成的 engine 不能直接拿到别的卡上用换卡要重新转。最后说个我自己的习惯每次训完车辆检测模型我都会留一个debug脚本随机抽 20 张验证集图片把预测框和真实框画在一起人眼过一遍。mAP 是给论文看的框有没有漏、有没有重眼睛最诚实。这套 KerasYOLO 的车辆检测方案从数据到实时推理坑基本都在版本、decode 和导出这三块把这三块理顺剩下的就是调参和堆数据。希望帮到你。本文还有配套的精品资源点击获取