资讯动态

目标检测Python代码实现:从零跑通训练与推理全流程

发布时间:2026/10/19 12:19:08 来源:尧图企业网站定制
简介这份资源面向具备一定Python与深度学习基础、希望动手实现目标检测的开发者与学习者围绕YOLO、SSD、Faster R-CNN等主流检测器结合TensorFlow框架讲解从数据预处理、模型构建、训练到评估预测的完整流程帮助读者把目标检测理论落到可运行的代码实践中。压缩包共308个文件约4.05MB以210个py源码为主体配合26个proto、15个config、12个build等配置与构建文件另有16个md说明、9个sh脚本及少量图片、ipynb与pbtxt覆盖数据加载、网络定义、训练评估等模块目录结构清晰便于按环节检索与调试。目前已有3265人学习下载适合作为课程实验、项目练手或算法入门的参考代码读者可借此理解检测模型的搭建思路与工程组织方式并在此基础上迁移到自己的数据集与任务中。1. 目标检测 Python 代码实现从零跑通一套能用的检测流程如果你手头正好有一份深度学习目标检测的 Python 代码实现却不确定它能不能直接跑、依赖怎么装、参数怎么调那这篇笔记就是写给你的。目标检测和图像分类最大的区别在于分类只告诉你「图里有什么」检测还要告诉你「在哪里、有几个」。这就决定了它的代码链路更长数据标注格式、Anchor 设计、损失函数、后处理 NMS每一环都可能让结果从「能看」变成「玄学」。这份资源是一套基于 Python 的目标检测代码实现核心依赖深度学习框架和张量计算库覆盖从数据加载、模型搭建、训练到推理的完整流程。它适合两类人一是刚学完卷积网络、想动手跑通检测流程的新手二是需要快速搭一个检测 baseline、再往上改的从业者。下面我按「先立住原理、再动手复现、最后避坑」的顺序拆开讲每一步都落到能抄的代码和能改的参数上。2. 检测流程拆解数据、模型、损失三件事怎么串起来目标检测的代码看起来长本质就三件事把标注喂成模型能吃的格式、用一个网络同时预测类别和框、用损失把预测拉向真值。这一章先把这三件事的原理和选型理由讲清楚再给出可执行的代码骨架。2.1 数据加载与标注格式转换检测任务的数据集通常有两种标注形式一种是每张图对应一个 XML 或 JSON里面记录每个目标的类别和左上角、右下角坐标另一种是一个总的 CSV 或 TXT每行是「图片路径, xmin, ymin, xmax, ymax, class」。代码实现里一般会统一成后者因为读取快、便于打乱。常见做法是写一个 Dataset 类继承框架自带的基类在__getitem__里做三件事读图、把框坐标归一化、返回张量。下面是一个可复用的骨架import os import torch from torch.utils.data import Dataset from PIL import Image class DetectDataset(Dataset): def __init__(self, anno_file, img_dir, img_size416, transformNone): # anno_file 每行: 图片名,xmin,ymin,xmax,ymax,class_id self.img_dir img_dir self.img_size img_size self.transform transform self.samples [] with open(anno_file, r) as f: for line in f: parts line.strip().split(,) self.samples.append(parts) def __len__(self): return len(self.samples) def __getitem__(self, idx): parts self.samples[idx] img_path os.path.join(self.img_dir, parts[0]) img Image.open(img_path).convert(RGB) w, h img.size # 坐标归一化到 0~1避免不同分辨率带来的尺度问题 boxes [] for i in range(1, len(parts), 5): xmin float(parts[i]) / w ymin float(parts[i1]) / h xmax float(parts[i2]) / w ymax float(parts[i3]) / h cls int(parts[i4]) boxes.append([xmin, ymin, xmax, ymax, cls]) img img.resize((self.img_size, self.img_size)) if self.transform: img self.transform(img) return img, torch.tensor(boxes, dtypetorch.float32)逻辑说明__init__只做路径和标注的索引不读图避免内存爆掉__getitem__里先归一化再 resize顺序不能反否则坐标会错位。参数上img_size一般取 416 或 608取 416 是速度和精度的折中显存小于 6G 就老实用 416。transform里至少要有ToTensor和归一化均值方差用 ImageNet 的即可不必自己重算。提示如果标注里有宽或高为 0 的脏框归一化后会变成 NaN训练几个 step 就 loss 爆炸。加载时加一句过滤把xmaxxmin或ymaxymin的框直接丢掉。2.2 模型结构选型Anchor-based 还是 Anchor-free代码实现里最常见的是 Anchor-based 思路也就是在每个特征图格子上预设几种宽高比的框网络预测的是相对这些预设框的偏移量。选它的理由是收敛稳、对小目标友好缺点是 Anchor 的尺寸要靠聚类调调不好召回率上不去。另一种是 Anchor-free直接预测中心点和宽高省掉了聚类这一步代码更短但在密集小目标场景下容易漏。如果你这份代码实现里带的是 Anchor-based建议先别急着换把 Anchor 尺寸用 K-means 在自家数据上聚一遍往往比换结构涨点更明显。下面是一个极简的检测头展示 Anchor-based 的输出维度怎么来的import torch.nn as nn class DetectHead(nn.Module): def __init__(self, in_ch, num_anchors, num_classes): super().__init__() # 每个 anchor 预测: 4 个偏移 1 个前景置信度 num_classes 个类别 self.conv nn.Conv2d(in_ch, num_anchors * (5 num_classes), 1) def forward(self, x): # x: [B, in_ch, H, W] out self.conv(x) B, _, H, W out.shape # 重排成 [B, H*W*num_anchors, 5num_classes] out out.view(B, -1, 5 self.num_classes) return out逻辑说明1x1卷积不改变空间尺寸只把通道数映射到「每个 Anchor 需要的预测数」。num_anchors通常取 3对应三种宽高比。参数上num_classes不含背景背景由前景置信度那一维负责。这里没写解码逻辑解码时要把偏移量乘上 Anchor 的宽高再加上中心坐标顺序写反是新手最常见的翻车点。2.3 损失函数分类损失和回归损失怎么配比检测的损失是多项相加分类用交叉熵回归用 IoU 系列损失再加一个前景/背景的二分类损失。配比很关键回归损失权重给大了框准了但类别乱给小了类别对但框飘。常见做法是分类损失权重 1.0回归损失权重 5.0 左右前景损失 1.0。如果训练前期 loss 下降很慢先把回归权重降到 2.0 试试。代码里一般会用一个loss_dict把三项分开记录方便定位是哪一项不收敛。def compute_loss(pred_cls, pred_box, pred_obj, gt_cls, gt_box, gt_obj): cls_loss nn.functional.cross_entropy(pred_cls, gt_cls) box_loss nn.functional.smooth_l1_loss(pred_box, gt_box, reductionsum) obj_loss nn.functional.binary_cross_entropy_with_logits(pred_obj, gt_obj) # 回归损失按正样本数归一化避免 batch 内目标数波动导致 loss 抖动 num_pos gt_obj.sum().clamp(min1) total cls_loss 5.0 * box_loss / num_pos obj_loss return total, {cls: cls_loss.item(), box: box_loss.item(), obj: obj_loss.item()}逻辑说明smooth_l1_loss比 MSE 对离群框更稳reductionsum后除以正样本数是为了让不同 batch 的 loss 可比。参数上那个5.0就是回归权重是调参时第一个该动的地方。clamp(min1)是防止某张图没有目标时除零这个边界不处理训练到一半就会出 NaN。3. 训练与推理落地命令行、参数和验证指标原理立住之后这一章落到实际跑起来。训练脚本怎么调、推理怎么出框、指标怎么看每一步都给到能直接用的命令和参数含义。3.1 训练脚本的启动参数训练入口一般是一个train.py用argparse接参数。下面这条命令是我常用的起步配置python train.py \ --data ./data/anno.txt \ --img-dir ./data/images \ --epochs 100 \ --batch-size 8 \ --img-size 416 \ --lr 0.001 \ --weights ./weights/pretrain.pth \ --device cuda:0参数说明batch-size取 8 是 6G 显存下的安全值显存够可以上 16但学习率要按比例放大到 0.002 左右。lr用 0.001 配 Adam 起步如果 loss 在前 5 个 epoch 就震荡降到 0.0005。weights指向预训练权重检测任务从分类预训练权重起步比随机初始化收敛快一倍不止这一步别省。device写cuda:0而不是cuda多卡机器上能避免默认占满所有卡。训练过程中要盯三个数总 loss、验证集 mAP、学习率。总 loss 不降先查数据标注mAP 不涨但 loss 降多半是过拟合加数据增强或早停学习率如果用了余弦退火后期降到 1e-5 以下就可以停了。3.2 推理与 NMS 后处理推理阶段网络会输出成千上万个框必须用 NMS 去掉重叠的。下面是一个不依赖第三方库的 NMS 实现方便你直接嵌进代码import torch def nms(boxes, scores, iou_thres0.5): # boxes: [N, 4] 格式为 xmin,ymin,xmax,ymax keep [] order scores.argsort(descendingTrue) while order.numel() 0: i order[0].item() keep.append(i) if order.numel() 1: break # 计算当前框与剩余框的 IoU xx1 torch.clamp(boxes[order[1:], 0], minboxes[i, 0]) yy1 torch.clamp(boxes[order[1:], 1], minboxes[i, 1]) xx2 torch.clamp(boxes[order[1:], 2], maxboxes[i, 2]) yy2 torch.clamp(boxes[order[1:], 3], maxboxes[i, 3]) inter (xx2 - xx1).clamp(min0) * (yy2 - yy1).clamp(min0) area_i (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1]) area_o (boxes[order[1:], 2] - boxes[order[1:], 0]) * \ (boxes[order[1:], 3] - boxes[order[1:], 1]) iou inter / (area_i area_o - inter 1e-6) order order[1:][iou iou_thres] return keep逻辑说明按置信度从高到低排序每次取最高的留下然后把它和剩下框的 IoU 算出来超过阈值的丢掉。参数iou_thres取 0.5 是通用值如果同类目标挨得很近比如一排瓶子调到 0.6 到 0.7否则会把挨着的目标误删。1e-6是防除零别省。推理时还要设一个置信度阈值低于它的框直接不要。这个阈值取 0.3 到 0.5 之间取低了误检多取高了漏检多。验证集上画一条 precision-recall 曲线选 F1 最高的点对应的阈值比拍脑袋定靠谱。3.3 指标验证mAP 怎么算、怎么看mAP 是检测任务的核心指标计算逻辑是对每个类别把预测框按置信度排序逐个算 precision 和 recall画出 PR 曲线曲线下面积就是 AP所有类别取平均就是 mAP。IoU 阈值取 0.5 时记作 mAP0.5取 0.5 到 0.95 平均记作 mAP[.5:.95]后者更严格。验证脚本一般会输出一张表每个类别一行。看表时重点看两类AP 特别低的类别多半是样本太少或 Anchor 不匹配AP 高但实际效果差的类别可能是验证集和训练集分布太像过拟合了。常见做法是留出 10% 的数据不参与训练专门做验证别用训练集算 mAP那个数没有参考价值。注意mAP 计算时如果某个类别在验证集里一个目标都没有AP 会算成 0拉低整体均值。这种情况要么补数据要么在评估时跳过该类别让它污染指标。4. 避坑与排查训练不收敛、显存爆、框错位的真实原因这一章是我踩过的坑里最高频的几条每条按「现象 → 原因 → 解决」写你对着查能省不少时间。4.1 现象loss 从第一个 epoch 就是 NaN原因九成是标注里有非法框归一化后出现 NaN或者学习率太大导致梯度爆炸。先查数据再查学习率。解决在 Dataset 里加过滤把xmaxxmin或ymaxymin的框丢掉学习率从 0.001 降到 0.0001 再跑一遍。如果还 NaN在 loss 后面加torch.nn.utils.clip_grad_norm_(model.parameters(), 10)做梯度裁剪。4.2 现象训练 loss 正常降但验证 mAP 一直不动原因最常见的是数据增强太弱模型把训练集背下来了其次是 Anchor 尺寸和真实框分布差太远正样本匹配不上。解决加随机翻转、随机裁剪、色彩抖动增强力度以验证集 mAP 为准调。Anchor 用 K-means 在自家标注上聚一遍把聚类得到的宽高替换掉默认值这一步通常能涨 3 到 5 个点。4.3 现象显存够但一跑就 OOM原因不一定是 batch-size 大可能是验证阶段没加torch.no_grad()或者 NMS 在 CPU 上对几万个框做循环内存爆了。解决验证和推理全部包在with torch.no_grad():里NMS 前先按置信度阈值过滤掉大部分框再送进 NMS输入从几万降到几百内存和速度都会好很多。4.4 现象推理出的框整体偏移或大小不对原因解码时 Anchor 的中心坐标和宽高用错了顺序或者归一化和反归一化没对上。解决检查解码公式标准写法是x_center (sigmoid(tx) grid_x) / grid_ww anchor_w * exp(tw)。如果框整体偏左上多半是 grid 的索引从 0 开始还是从 1 开始搞混了。反归一化时乘回原图宽高别乘成 resize 后的尺寸。4.5 现象同一张图每次推理结果都不一样原因模型没切到 eval 模式BatchNorm 还在用 batch 统计量或者 Dropout 没关。解决推理前调model.eval()并用torch.no_grad()包住。如果还有随机性检查数据预处理里有没有随机增强没关掉验证和推理阶段的 transform 必须是确定性的。5. 进阶技巧把检测结果落到业务里的两个实用手段跑通训练只是第一步真正落地时还有两件事值得做一是把检测框和业务逻辑接起来二是用可视化快速定位问题。这一章给两个具体技巧都是我在实际项目里反复用的。5.1 用置信度分层做业务过滤业务场景里不同类别对误检的容忍度不一样。比如安全帽检测漏检一个没戴帽子的代价远大于误检一个戴了的。这时候不要用统一的置信度阈值而是按类别分层设。# 每个类别一个阈值漏检代价高的类别阈值调低 class_thres { 0: 0.3, # 高风险类别宁可误检不可漏检 1: 0.5, # 普通类别 2: 0.6, # 误检代价高的类别 } def filter_by_class(boxes, scores, labels): keep [] for i in range(len(boxes)): cls int(labels[i]) if scores[i] class_thres.get(cls, 0.5): keep.append(i) return keep逻辑说明class_thres按类别给不同阈值get的默认值 0.5 兜底。这个表要根据业务反馈调上线后收集误检和漏检的 case哪类问题多就动哪类的阈值。参数没有标准答案关键是别用一刀切的 0.5。5.2 可视化验证把框画回原图指标是数字画出来才直观。下面这段代码把预测框和真值框画在同一张图上一眼就能看出是漏检还是定位不准import cv2 def draw_boxes(img_path, pred_boxes, gt_boxesNone, save_pathvis.jpg): img cv2.imread(img_path) h, w img.shape[:2] for box in pred_boxes: x1, y1, x2, y2 [int(v) for v in box[:4]] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) # 绿色为预测 if gt_boxes is not None: for box in gt_boxes: x1, y1, x2, y2 [int(v) for v in box[:4]] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) # 红色为真值 cv2.imwrite(save_path, img)逻辑说明绿色画预测、红色画真值重叠好的框会呈现黄绿色偏了的框红绿分离一眼能看出来。参数上2是线宽图大就调粗。这个脚本我一般会在验证集里随机抽 20 张跑一遍比看 mAP 表快得多尤其是定位偏移这种指标反映不出来的问题。从那以后我每次改完 Anchor 或损失权重都强制走一遍「抽 20 张画框」的流程指标涨了但框画出来不对的情况遇到过不止一次。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑