新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于YOLOv5与TT100K的交通标志识别实战:从数据转换到部署

发布时间:2026/10/1 13:58:24来源:尧图网络
基于YOLOv5与TT100K的交通标志识别实战:从数据转换到部署
简介这是一套基于YOLOv5与TT100K数据集的交通标志牌识别完整项目面向人工智能、计算机视觉方向学生及开发者可直接用于毕业设计、课程设计或目标检测入门进阶。项目源码为高分开源成果代码经过运行验证并配有详细文档和训练配置覆盖从数据准备、模型训练到检测推理的主要环节。资源包共149个文件压缩后约1.12MB其中包含56个Python脚本训练与检测逻辑、49个YAML和12个YML配置文件、6个Shell脚本、6个Markdown说明文档、3个Jupyter Notebook演示以及Dockerfile等容器化部署文件目录结构清晰便于按需查阅。目前已有81人浏览学习。使用者可基于该代码快速复现交通标志牌检测效果也可修改类别与数据以适配其他目标检测任务对于正在准备毕设或课设的同学这套源码、文档、配置文件一体化的资源既能支撑项目答辩也能作为学习YOLOv5实战的起步材料。1. 交通标志牌识别为什么要选 YOLOv5 TT100K先说结论再做项目很多人搜“基于yolov5的交通标志牌识别项目”时心态很明确要么是毕业设计需要一个能跑通的目标检测系统要么是想快速上手真实数据集。标题里的组合很经典yolov5 负责检测tt100k 提供中国真实街景交通标志图加上源码和文档覆盖了数据清洗、训练、推理、部署的完整闭环。它能解决的实际问题很具体输入一张街景图或一段视频输出交通标志的位置框和类别。但它绝不是开箱即用TT100K 标注是 JSONYOLOv5 要 txt小目标多、类别长尾环境配置和标签转换才是真门槛。我会按实操顺序展开适合有 Python 基础、想亲手复现的人。2. 从环境到数据TT100K 标注转 YOLO 格式是第一个深坑2.1 yolov5 环境配置用 conda 锁住 Python 和依赖版本我见过太多人拿到项目包第一件事就是双击 train.py结果报错一屏接一屏。yolov5 环境配置本身不难难在 PyTorch 和 CUDA 版本互相打架。我的习惯是新建独立 conda 环境把 Python、PyTorch、CUDA 工具链一次锁死避免把系统 Python 弄得一团糟。conda create -n yolo_tt100k python3.9 -y conda activate yolo_tt100k conda install pytorch1.13.1 torchvision0.14.1 cudatoolkit11.7 -c pytorch -y git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt逻辑说明Python 3.9 对 yolov5 各版本兼容性都比较好不建议追新。PyTorch 版本要和你显卡驱动匹配conda 安装的 cudatoolkit 不需要你手动装驱动。克隆官方仓库后pip 会把 numpy、opencv、matplotlib 等依赖一起装上。参数说明pytorch1.13.1是常见稳定组合之一你可以在 PyTorch 官网找到对应 CUDA 11.7 的安装命令如果装的是 CPU 版后面训练会慢到你怀疑人生。装完先做一次环境检查python -c import torch; print(torch.__version__, torch.cuda.is_available())输出True说明 GPU 可用。如果输出False说明 torch 不是 CUDA 版需要卸载重装。还有一个高频坑pip install -r requirements.txt时 opencv 依赖 libGLUbuntu 服务器上常常报ImportError: libGL.so.1: cannot open shared object file执行sudo apt install libgl1 libglib2.0-0再装就能解决。这些环境问题一次梳理清楚后边能省出大量时间。2.2 TT100K 数据集的目录结构和 json 标注先看再动手TT100K 下载解压后通常有 train 和 test 两个图片目录外加一个 annotations.json。如果你不知道里面长什么样就急着转换后面大概率要返工。我的做法是写个几行脚本先看前两条标注结构import json with open(annotations.json, r, encodingutf-8) as f: ann json.load(f) for img_name, info in list(ann.items())[:2]: objs info.get(objects, []) print(img_name, len(objs)) if objs: print(objs[0][category], objs[0][bbox])逻辑说明tt100k 的标注是按图片名索引的每张图名下挂着 objects 列表。每个 object 包含 category、bbox 等字段bbox 是xmin, ymin, xmax, ymax的字典格式用的是原图像素坐标。特别提醒这个格式和 COCO 的 bbox 不一样。COCO 给的是x, y, width, heightTT100K 给的是左上角和右下角两个点。很多人转换时直接拿 xmax-xmin 当 x 坐标用结果训练出来的框全偏了这是最容易踩的标签坑。类别字段是p100、i5、w1这类字符串分别对应禁令、指示、警告等不同组别的标志。看起来统一实际含义要靠官方表格去查不建议自己猜。还有一点值得注意TT100K 里大量图片中的交通标志非常小有的只有十几像素类别分布也很不均匀一小部分类别占了绝大多数样本长尾效应明显。如果直接把全类别丢给 YOLOv5 训练很多罕见类根本学不到。常见的做法是先统计类别频率再决定保留哪些类别或者做类别合并这一步会直接影响 mAP。2.3 把 JSON 转成 YOLO txt 标注完整的转换脚本yolov5 训练自己的数据集时标签目录默认叫 labels与图片目录对应每个图片一个 txt每行格式是class_id x_center y_center width height所有值都归一化到 0-1。我一般写一个独立转换脚本顺便做类别过滤和坐标保护import json import os import cv2 from collections import Counter IMG_DIR data/tt100k/train JSON_FILE data/tt100k/annotations.json LABEL_DIR data/tt100k/labels/train CLASS_FILE data/tt100k/classes.txt with open(JSON_FILE, r, encodingutf-8) as f: ann json.load(f) counter Counter() for img_name, info in ann.items(): for obj in info.get(objects, []): counter[obj[category]] 1 common [c for c, _ in counter.most_common(45)] with open(CLASS_FILE, w, encodingutf-8) as f: f.write(\n.join(common) \n) cls2idx {c: i for i, c in enumerate(common)} os.makedirs(LABEL_DIR, exist_okTrue) for img_name, info in ann.items(): img_path os.path.join(IMG_DIR, os.path.basename(img_name)) if not os.path.exists(img_path): continue img cv2.imread(img_path) if img is None: print(skip broken image:, img_path) continue h, w img.shape[:2] lines [] for obj in info.get(objects, []): cat obj[category] if cat not in cls2idx: continue b obj[bbox] x1 max(0, min(int(b[xmin]), w - 1)) y1 max(0, min(int(b[ymin]), h - 1)) x2 max(0, min(int(b[xmax]), w - 1)) y2 max(0, min(int(b[ymax]), h - 1)) if x2 - x1 1 or y2 - y1 1: continue xc (x1 x2) / 2 / w yc (y1 y2) / 2 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{cls2idx[cat]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) if lines: txt_path os.path.join(LABEL_DIR, os.path.basename(img_name).replace(.jpg, .txt)) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明common取出现频率最高的 45 个类别是为了避开长尾噪声。如果你想要更细的类别或减少类别数可以改成自己维护一个 category 白名单然后按白名单映射。cls2idx 的字典顺序会写进 classes.txt后续 data.yaml 里的 names 顺序必须和它完全一致否则预测出来的标签全是错位。参数说明bbox 做了越界钳制避免标注边界超出图像宽高导致归一化数值小于 0 或大于 1空标注图片不生成 txtYOLOv5 训练时会自动跳过这些图片。如果某些图片本身损坏cv2.imread 会返回 None脚本会跳过并打印提示不会中断整个转换。转换完不能直接开训我习惯随机抽出几张图把 txt 里的坐标画回去验证。这一步能发现绝大多数低级错误import cv2 import os import random LABEL_DIR data/tt100k/labels/train IMG_DIR data/tt100k/train with open(data/tt100k/classes.txt, r) as f: names [x.strip() for x in f] txt_list [x for x in os.listdir(LABEL_DIR) if x.endswith(.txt)] txt_path os.path.join(LABEL_DIR, random.choice(txt_list)) img cv2.imread(os.path.join(IMG_DIR, txt_path.replace(.txt, .jpg))) for line in open(txt_path): cid, xc, yc, bw, bh map(float, line.split()) h, w img.shape[:2] x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, names[int(cid)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imwrite(check.jpg, img)看到框和标志贴合说明转换脚本正确。如果框偏移、大小不对问题基本都出在坐标归一化计算上优先检查宽度高度有没有除以原图尺寸。3. 模型训练yolov5 超参数和 TT100K 类别不平衡问题3.1 数据配置文件和类别名data.yaml 是训练的入口数据转换完成后下一步是写 data.yaml。它是训练脚本读取数据集的唯一入口路径、类别数量、类别名称全在这里。很多从 yolov5 源码包白嫖来的项目跑不起来一半是因为 yaml 里 paths 写的是别人电脑的绝对路径。# data/traffic.yaml train: data/tt100k/train.txt val: data/tt100k/val.txt nc: 45 names: 0: p100 1: i5 2: p11 3: p26 4: p27 # 后续类别必须与 classes.txt 顺序一致逻辑说明train 和 val 指向的是图片路径清单文件。yolov5 会读取 train.txt 里的每一行图片路径然后自动去同级的 labels 目录里找同名 txt。所以 train.txt 必须写成真实存在的图片路径不能只写目录名。参数说明nc 是类别总数必须和 names 的长度一致也要和 classes.txt 里的行数一致。names 的索引从 0 开始顺序就是转换脚本里 cls2idx 的顺序。这里错一个标点训练出来的模型标注就全乱。生成 train.txt 我一般用find $PWD/data/tt100k/train -name *.jpg data/tt100k/train.txt find $PWD/data/tt100k/val -name *.jpg data/tt100k/val.txt如果你没有单独的 val 数据集可以按 9:1 从 train 里划分再分别生成两个 txt。注意划分之前最好先按类别分层抽样否则某些类别只在训练集出现验证集上 AP 恒为 0最后 mAP 看起来很难看。还有一个方向是类别合并。TT100K 里很多类只是限速数字不同比如限速 30、限速 50如果它们样本不均衡模型很容易把样本少的那一类学丢。做实训项目时我一般会把所有限速牌合并成一个 speed_limit 类把警告牌合并成 warning类别数能从几十压到十以内mAP 会好看很多也更贴近真实工程上的使用习惯。3.2 训练命令与关键超参数epochs、batch-size、imgsz、mosaic数据配置就绪后训练命令反而很简单。常见做法是用 yolov5s 做预训练权重它体积小、速度快交通标志这种类别不算复杂的任务s 模型已经足够。python train.py \ --data data/traffic.yaml \ --weights yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --imgsz 640 \ --device 0 \ --workers 4逻辑说明--weights yolov5s.pt会下载官方 COCO 预训练权重用它能显著加快收敛。--imgsz 640是训练输入尺寸TT100K 的小目标多我不是很建议再往下调。--device 0指定 GPU 编号CPU 训练也能跑但 100 epoch 可能要跑十几个小时。参数说明如果显存不够先减--batch-size不要优先减 imgsz。TT100K 的目标本身就小输入分辨率再低小标志几乎等于消失。--workers 4可以根据 CPU 核数适当调大但 Windows 下 worker 过大可能报 DataLoader 相关错误一般 2 到 4 够用。训练中断了不用从头再来yolov5 支持断点续训python train.py --resume runs/train/exp/weights/last.pt另外yolov5 的超参数都写在 data/hyps 下面的 yaml 文件里比如hyp.scratch-low.yaml。里面能看到lr0、mosaic、mixup这些项。第一次跑我建议用一个默认 hyp 文件跑通后续再按照训练曲线微调。如果 loss 一直上下震荡把lr0从 0.01 调到 0.001 立竿见影如果类别不平衡明显把mixup从 0 调到 0.2 可以在训练时做样本混合缓解少数类过拟合。不要上来就大改先跑通再调参。3.3 训练日志和结果文件如何判断模型真的收敛了yolov5 会把每次训练输出到runs/train/exp里面最重要的是weights/best.pt和last.pt。best.pt 是验证集 mAP 最高时保存的权重last.pt 是最后一个 epoch 的权重。推理部署一定要用 best.pt这是很多新手的误区。结果文件results.csv记录了每个 epoch 的 train loss、val loss、mAP50 等指标。我一般用一段小脚本直接看最后一行import csv with open(runs/train/exp/results.csv, r) as f: rows list(csv.DictReader(f)) last rows[-1] print(epoch:, last[epoch]) print(mAP50:, last[metrics/mAP_0.5]) print(mAP50-95:, last[metrics/mAP_0.5:0.95])逻辑说明mAP50 是 IoU 阈值 0.5 时的平均精度mAP50-95 是多个 IoU 阈值的平均值后者更严格。交通标志这种单目标检测任务mAP50 更容易刷高但真正能说明问题的是 mAP50-95。判断模型是否收敛不能只看 train loss。如果 train loss 持续下降但 val loss 不再下降甚至回升说明已经开始过拟合。这个时候要么减少 epoch要么增强数据增强要么换更轻的模型。还有一个容易被忽视的点训练启动时 yolov5 会自动计算数据集的 anchor日志里会打印每个尺度的 anchor 大小。如果 TT100K 的目标普遍很小但生成的 anchor 很大可以尝试调大--imgsz到 960。不要手动乱改 yaml 里的 anchor先看日志里的 autoanchor 输出再做判断。4. 推理与部署从 detect.py 到 ONNX 导出4.1 用 detect.py 跑图片和视频参数与输出目录训练完成后最简单的验证方式是直接用 detect.py 跑测试集。它的 input 很灵活可以是图片、文件夹、视频文件甚至摄像头序号。python detect.py \ --weights runs/train/exp/weights/best.pt \ --source data/tt100k/test \ --conf-thres 0.5 \ --iou-thres 0.45 \ --save-txt \ --save-conf \ --project runs/detect逻辑说明--source指向测试图片目录detect.py 会扫描目录下所有图片逐个推理。--save-txt会把每个检测框保存成 YOLO 格式的 txt--save-conf会在 txt 里额外追加一个置信度字段。--project指定输出根目录结果默认放在runs/detect/exp下面如果重复运行会生成 exp2、exp3。参数说明conf-thres是置信度阈值默认 0.25我习惯在测试时设 0.5避免输出一堆低置信度误检。iou-thres是 NMS 的 IoU 阈值0.45 是常见值如果检测目标彼此靠得很近可以适当调低到 0.3。除了命令行我偶尔也会在 Jupyter 里直接加载模型看单张图效果import torch model torch.hub.load(ultralytics/yolov5, custom, pathruns/train/exp/weights/best.pt, force_reloadTrue) model.conf 0.4 model.iou 0.45 results model(data/tt100k/test/320.jpg) results.print() results.save(runs/detect/hub)参数说明force_reloadTrue会忽略缓存重新加载权重换模型时一定要加。model.conf和model.iou可以直接赋值这比每次推理传参更省事。注意 torch.hub 第一次会从 GitHub 拉取模型结构代码网络受限时会失败所以离线环境我更推荐直接调 detect.py。4.2 把权重导出成 ONNX 并用 onnxruntime 推理如果要把模型集成到 C、Java 或者边缘设备上第一步通常是导出 ONNX。yolov5 官方提供了 export.py。python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --imgsz 640 \ --opset 11逻辑说明导出成功的best.onnx文件会出现在权重同目录。--opset 11是 ONNX 算子集版本兼容性较好如果部署环境要求新算子集可以改成 13 或 17。导出后用 onnxruntime 跑推理输入是一张标准化到 0-1 的 RGB 图shape 是 NCHW。以下是一段最小推理代码import cv2 import numpy as np import onnxruntime as ort sess ort.InferenceSession(runs/train/exp/weights/best.onnx) img cv2.imread(test.jpg) img_resized cv2.resize(img, (640, 640)) x img_resized[:, :, ::-1].transpose(2, 0, 1)[None].astype(np.float32) / 255.0 out sess.run(None, {sess.get_inputs()[0].name: x})[0] print(out.shape) # 1, 25200, 5 nc逻辑说明img_resized[:, :, ::-1]是把 BGR 转成 RGBtranspose(2, 0, 1)是把 HWC 转成 CHW/ 255.0是归一化。输出 shape 里 25200 是根据输入 640x640 和 anchor 组合算出来的候选框数量第三维是 5 加类别数如果 nc45就是 50。参数说明这里的输入尺寸必须和导出时的--imgsz一致否则输出形状对不上。如果是动态 batch 导出sess.run的输入还需要严格用 numpy 的 uint8 或 float32不要用 list。4.3 yolov5 后处理解码、NMS 与置信度过滤是怎么配合的很多人第一次拿 ONNX 输出时会懵一堆浮点数不知道该怎么变成检测框。YOLOv5 的输出每个候选框是[x, y, w, h, objectness, class_scores...]这里的 x、y、w、h 已经解码到输入图坐标不再需要 anchor 解码但还需要做三件事置信度过滤、坐标转换、NMS。先看置信度过滤和坐标转换def decode_pred(pred, num_classes45, conf_thres0.3): obj_conf pred[:, 4] cls_conf pred[:, 5:5 num_classes].max(axis1) scores obj_conf * cls_conf mask scores conf_thres boxes pred[mask, :4] scores scores[mask] cls_ids pred[mask, 5:5 num_classes].argmax(axis1) x, y, w, h boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3] x1, y1 x - w / 2, y - h / 2 x2, y2 x w / 2, y h / 2 boxes np.stack([x1, y1, x2, y2], axis1) return boxes, scores, cls_ids逻辑说明obj_conf表示这个框含目标的概率cls_conf表示每个类别里最高的概率两者相乘才是这个框最终属于某类的置信度。乘出来的分数低于conf_thres的直接丢。过滤完还需要做 NMS去除同一目标上的重复框。下面是一个常用的纯 NumPy NMS 函数def nms(boxes, scores, iou_thr0.45): x1 boxes[:, 0]; y1 boxes[:, 1] x2 boxes[:, 2]; y2 boxes[:, 3] areas (x2 - x1) * (y2 - y1) order scores.argsort()[::-1] keep [] while order.size 0: i order[0] keep.append(i) xx1 np.maximum(x1[i], x1[order[1:]]) yy1 np.maximum(y1[i], y1[order[1:]]) xx2 np.minimum(x2[i], x2[order[1:]]) yy2 np.minimum(y2[i], y2[order[1:]]) inter np.maximum(0.0, xx2 - xx1) * np.maximum(0.0, yy2 - yy1) iou inter / (areas[i] areas[order[1:]] - inter 1e-6) order order[(iou iou_thr).nonzero()[0] 1] return np.array(keep)参数说明这个 NMS 是通用写法加了1e-6防止除零iou_thr是 0.45。如果你用的是多类别检测建议对每个类别单独做 NMS否则两个不同类别的目标重叠较大时低置信度那个会被误删。最后别忘了坐标还原如果推理前把原图 resize 到了 640x640ONNX 输出框是基于 640 坐标系的。要显示到原图或保存 JSON 结果必须把框坐标乘上原图宽/640和原图高/640。这一步漏掉检测框看起来会错位。如果在树莓派 5 上部署自己训练的 yolov5 模型可以把脚本精简到只用 ONNX Runtime 加这组后处理去掉 torch 依赖然后考虑是否量化到 FP16。树莓派 CPU 跑 640x640 输入到 s 模型速度不会太快建议用轻量的 yolov5n 或降低输出帧率不要对实时性抱太高期待。5. 避坑指南TT100K 训练 YOLOv5 的 5 个翻车现场5.1 坑一训练开始 Loss 就变 NaN现象训练前几个 epoch 的 loss 直接打出 nan之后模型输出的框全是乱飞。原因最常见的可能是学习率过高尤其在使用某些预训练权重时默认lr00.01遇到结构性强的小数据集容易震荡也有可能是数据里混入了全黑图片、损坏图片或者某个标签的坐标出现 NaN 值。解决先跑一遍数据检查脚本确认每张图 cv2.imread 都能读到确认 txt 文件里没有 nan 字符。然后把学习率降到 0.001重新训练。如果还在 nan检查是不是 CPU/GPU 混合精度在老旧显卡上有问题在 train.py 里加--no-amp关闭自动混合精度很多老卡能救回来。5.2 坑二Loss 正常但 mAP 接近 0数据检查比调模型更优先现象训练曲线看着很顺train loss 稳定下降但 val mAP 一直在 0.05 左右徘徊和没学一样。原因这个场景十有八九是标签映射错位。比如 classes.txt 里第 0 行是 p100但 data.yaml 的 names 里第 0 行写成了 i5模型学到的语义和验证标签对不上mAP 自然崩。还有一种情况是转换脚本的 txt 文件名写错导致 YOLOv5 读取标签时拿到的全是空文件。解决用前面提到的画框脚本随机画 20 张训练图片肉眼确认标签框和类别文字是否正确。再打开 data.yaml 核对 names 顺序与 classes.txt 一致。如果还需要精确验证可以把验证集的预测结果打印成 JSON看 detected class 名称是不是和目标类名错位。这类问题调模型参数没用纯数据活。5.3 坑三训练启动直接报错 No labels in ... cache现象执行 train.py 后还没有进入训练循环就报AssertionError: train: No labels in ...或类似找不到标签的错误。原因yolov5 默认会从图片路径推导标签路径。如果图片放在data/tt100k/train/images它期望标签在data/tt100k/train/labels而我们的转换脚本把标签放在data/tt100k/labels/train目录层级不一样就会找不到。解决调整目录结构让 images 和 labels 处于同级且 labels 目录直接对应图片根目录。即图片是train/xxx.jpg标签是train/xxx.txt。如果不想移动数据也可以在 yaml 里增加path字段把路径指到它们的共同父目录。还有一种情况是 train.txt 里写的是.png路径但标签转换时替换的是.jpg文件后缀不匹配也要一起检查。5.4 坑四远处小标志全部漏检大标志却好好的现象测试集里近处的标志框得很准远处几十像素大小的标志几乎全部漏掉甚至很多还被低置信度过滤掉。原因TT100K 是从真实街景中截取的高清大图远处交通标志在原图里只占很小一块。训练时 imgsz 如果只有 640小标志在缩放后可能只有 10 个像素不到卷积特征已经把它磨没了。另一个推手是 mosaic 增强四张图拼成一张后目标平均尺寸进一步缩小。解决先把--imgsz提高到 960batch-size 相应减半。如果显存不够可以考虑裁剪图片训练把大图切块后只保留含标志的 patch。推理阶段也可以做切图推理把原图按 640x640 滑窗裁开分别检测再把检测框合并回原坐标做一次全局 NMS。这个方法对小目标非常有效代价是推理耗时成倍增长。另外不要为了速度把conf-thres设太高小目标得分普遍偏低设到 0.25 甚至 0.1 会更稳。5.5 坑五TT100K 类别不平衡罕见类别 AP 一直为 0现象统计 mAP 时样本量大的类别 AP 都有 0.7 以上但某些低频类别的 AP 始终是 0甚至从来没被正确预测过。原因数据集本身长尾严重模型在训练时对低频类没见过足够多样的样本决策面完全被高频类带偏。YOLOv5 默认的 BCE loss 对所有类别一视同仁低频类在总 loss 中占比太小。解决最简单的做法是类别重映射把所有低频类合并到一个“其他标志”类或者直接筛掉让模型专注学习样本量足够的类别。更进阶一点可以在训练时用复制粘贴增强把低频目标从一张图复制到多张背景图上人为增加样本数。如果一定要保留细粒度类别可以尝试在损失函数里给低频类加权重但改完需要好好验证容易把高频类的 mAP 拉下来。做实训项目或毕设时我更推荐先做类别合并理由很简单工程落地上“能识别出这是限速标志”比“精确到限速 30 还是 50”更刚需而且合并之后模型稳定性好很多。6. 进阶优化小目标切图推理、训练数据增强与最终验收技巧6.1 小目标切图推理用 patch 合并提升 TT100K 召回率如果测试集里大量小标志漏检切图推理是我最推荐的手段。思路很简单推理时用滑动窗口把原图切成多个 patch每个 patch 分别送入模型得到局部检测结果后再映射回原图坐标最后做一次全局 NMS。def patch_inference(model, img, patch640, stride320, conf0.25): h, w img.shape[:2] boxes [] for y in range(0, max(1, h - patch 1), stride): for x in range(0, max(1, w - patch 1), stride): crop img[y:y patch, x:x patch] res model(crop, sizepatch).pandas().xyxy[0] for _, r in res.iterrows(): boxes.append((x r[xmin], y r[ymin], x r[xmax], y r[ymax], r[confidence], r[class])) return boxes逻辑说明stride 通常取 patch 的一半保证相邻 patch 有 50% 重叠避免目标正好被切在边界上。得到所有候选框后再调用第 4.3 节的全局 NMS按置信度排序去重。参数说明patch大小可以按数据集目标尺寸调整TT100K 一般 640 合适。conf在切图推理时可以设低一点因为小目标在 patch 里被放大置信度会提高很多。如果显存或内存紧张可以牺牲一些重叠率把 stride 调到 patch 的 0.75速度会更快但漏检率略增。6.2 训练增强策略mosaic 与复制粘贴的取舍yolov5 的 mosaic 增强默认开启它把四张图拼成一张再训练对小目标来说是把双刃剑一方面增加了目标数量和多样性另一方面目标被缩小。如果你发现训练集的小目标经过 mosaic 后几乎不可见可以把 hyp 文件里的mosaic从 1.0 降到 0.5或者只在训练前半段开启。复制粘贴增强在 TT100K 这种长尾数据集上更有针对性。思路是统计类别频率找到低频类目标把它们从原图抠出来随机粘贴到其他图片的合理位置再同时生成标签。这个增强方式比单纯翻转、缩放更直接地解决样本不足问题。需要注意的是粘贴位置不能随意放在天空或者完全无关的区域否则模型学到的是“低频目标都长在奇怪位置”泛化会变差。6.3 最终验收别只盯着 mAP画混淆矩阵和典型错误训练跑完很多人看一眼总体 mAP 就开始写报告。实际上我更建议先做一轮标准评估用 val.py 生成细化指标python val.py \ --data data/traffic.yaml \ --weights runs/train/exp/weights/best.pt \ --task val \ --conf-thres 0.001 \ --save-json参数说明--conf-thres 0.001不是为了让输出好看而是为了画 PR 曲线时覆盖完整的置信度区间。val.py 会输出每类 AP、混淆矩阵图片和 PR 曲线。看混淆矩阵时重点看两个地方一是对角线上的数值是否足够高二是哪些类别频繁被预测成背景。TT100K 里的“其他标志”和某些禁令标志长得像误检会在混淆矩阵里非常直观。看到问题后再回到测试集里挑出几十张典型错误图片把预测框和真实框一起画出来。很多高分项目的 mAP 是用低 IoU 阈值刷出来的框实际上偏大偏小都算对这在答辩时经不起追问。我习惯每次跑完都保存一批对比图既能验证模型也能在写文档时直接当素材。这套流程走完你手里才真正有一套可以复现、有依据、敢拿出手的结果。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Sealos Devbox 基础教程:把 Cursor Base URL 改到 TaoToken 从零开发 Python 项目 2026/10/1 15:22:02

Sealos Devbox 基础教程:把 Cursor Base URL 改到 TaoToken 从零开发 Python 项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
游戏AI Agent Harness:行为逻辑与规则管控的工程化落地 2026/10/1 15:22:02

游戏AI Agent Harness:行为逻辑与规则管控的工程化落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
软件适配认证全流程指南:从概念到落地,投标必备 2026/10/1 15:21:49

软件适配认证全流程指南:从概念到落地,投标必备

说实话,第一次被问到“软件适配认证”时,我也是一愣——明明产品功能测试都过了,用户用得也好好的,为什么招标方偏偏要一份特定环境下的认证证书?后来自己做了一遍这个流程才明白:适配认证不是产品“能不能…

阅读更多 →
2026年擅长RAG向量化的GEO优化服务商行业现状与选型指南 2026/10/1 15:21:43

2026年擅长RAG向量化的GEO优化服务商行业现状与选型指南

现在市场上做GEO优化的服务商越来越多,很多采购负责人选型时都会被几个问题卡住,我们整理了三个行业最受关注的问题,逐一给大家拆解分析。Q1:现在国内擅长RAG向量化的GEO优化服务商行业现状是什么样的?Generative Engine Optimiz…

阅读更多 →
Strata 实验性速度投影(ESP):拒绝方向投影的原理、收益与安全风险 2026/10/1 15:21:43

Strata 实验性速度投影(ESP):拒绝方向投影的原理、收益与安全风险

Strata 实验性速度投影(ESP):拒绝方向投影的原理、收益与安全风险 【免费下载链接】Strata Qwen3.8-Flash-Next on any consumer hardware: one-click install for Windows / Linux. Strata inference engine, OpenAI/Anthropic API on local…

阅读更多 →
MF308C Openlinux 文档说明 2026/10/1 15:21:43

MF308C Openlinux 文档说明

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉