ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PyTorch RetinaNet源码解析:从Focal Loss原理到目标检测实战

PyTorch RetinaNet源码解析:从Focal Loss原理到目标检测实战 简介本资源是一个基于PyTorch实现的RetinaNet目标检测模型开源项目面向计算机视觉初学者与中级开发者聚焦小目标检测与类别不平衡等实际难题提供可复现、可迁移的完整训练框架。压缩包共22个文件968KB含12个核心Python脚本如model.py、losses.py、dataloader.py、train.py、7张示例图像用于可视化验证、1份README说明、1份LICENSE授权文件及1个.gitignore配置文件覆盖模型定义、Focal Loss实现、FPN特征金字塔构建、COCO/CSV数据加载、评估与推理全流程。已有600人学习下载代码结构清晰、模块职责分明特别适合快速上手自定义数据集训练——无需从零搭建可直接修改数据路径、调整anchor参数或替换backbone配合内置的visualize.py和coco_eval.py即能完成端到端调试与性能分析。1. 项目概述一个经典的PyTorch目标检测实现如果你正在寻找一个清晰、可运行、且能让你深入理解单阶段目标检测器核心原理的PyTorch项目那么“pytorch-retinanet-master”这个仓库大概率会出现在你的搜索结果前列。它不是一个追求最新SOTA指标的复杂框架而是一个将RetinaNet论文《Focal Loss for Dense Object Detection》从理论转化为可执行代码的经典实现。对于从Faster R-CNN等两阶段检测器入门想要切入单阶段检测领域或者希望亲手搭建一个完整检测pipeline的学习者来说这个项目提供了一个绝佳的“解剖样本”。RetinaNet本身在目标检测发展史上是一个里程碑。在它之前单阶段检测器如YOLO、SSD虽然在速度上占优但精度往往落后于两阶段方法。RetinaNet的核心贡献——Focal Loss优雅地解决了训练过程中前景与背景类别极度不平衡简单负样本过多的问题从而让单阶段检测器在保持速度优势的同时首次在精度上追平甚至超越了当时的两阶段检测器。因此理解RetinaNet不仅是学习一个模型更是理解如何通过损失函数的设计来攻克一个关键学术与工程难题。这个“pytorch-retinanet-master”项目通常实现了包括Backbone如ResNet、特征金字塔网络FPN、分类与回归子网络Head、以及核心的Focal Loss在内的完整架构。它的价值在于“透明性”代码结构通常比较直接数据加载、模型构建、训练循环、评估脚本一目了然你可以很容易地跟踪数据流观察特征图的变化并亲手调试每一个模块。这正是“源码”二字的价值——它不只是一个黑盒工具更是一份可以逐行研读的学习资料。2. 核心架构与代码深度解析2.1 骨架网络Backbone与特征金字塔FPN的实现项目中的Backbone通常基于PyTorch官方torchvision.models中的ResNet进行改造。这里的关键不是简单地加载预训练模型而是如何从中提取多尺度特征图。以ResNet50为例我们会截取来自不同深度的特征层例如C3, C4, C5它们分别具有不同的空间分辨率下采样率如8倍、16倍、32倍和通道数如512, 1024, 2048。import torch.nn as nn import torchvision.models as models class ResNetFPN(nn.Module): def __init__(self, backbone_nameresnet50): super().__init__() # 加载预训练ResNet并获取中间层输出 backbone getattr(models, backbone_name)(pretrainedTrue) self.layer1 nn.Sequential(backbone.conv1, backbone.bn1, backbone.relu, backbone.maxpool, backbone.layer1) # C2 self.layer2 backbone.layer2 # C3 self.layer3 backbone.layer3 # C4 self.layer4 backbone.layer4 # C5 # FPN 自顶向下及横向连接 # 对C5进行1x1卷积得到P5 self.toplayer nn.Conv2d(2048, 256, kernel_size1, stride1, padding0) # 用于融合的1x1卷积横向连接 self.latlayer1 nn.Conv2d(1024, 256, kernel_size1, stride1, padding0) self.latlayer2 nn.Conv2d(512, 256, kernel_size1, stride1, padding0) # 用于输出的3x3卷积平滑 self.smooth1 nn.Conv2d(256, 256, kernel_size3, stride1, padding1) self.smooth2 nn.Conv2d(256, 256, kernel_size3, stride1, padding1) def _upsample_add(self, x, y): 上采样并相加实现自顶向下路径 _, _, H, W y.size() return nn.functional.interpolate(x, size(H, W), modenearest) y def forward(self, x): # 前向传播获取多尺度特征 c2 self.layer1(x) c3 self.layer2(c2) c4 self.layer3(c3) c5 self.layer4(c4) # FPN 构建 p5 self.toplayer(c5) p4 self._upsample_add(p5, self.latlayer1(c4)) p3 self._upsample_add(p4, self.latlayer2(c3)) # 平滑处理 p4 self.smooth1(p4) p3 self.smooth2(p3) # 有时也会从P5生成P6和P7用于检测更大物体 p6 nn.functional.max_pool2d(p5, kernel_size1, stride2) p7 nn.functional.max_pool2d(p6, kernel_size1, stride2) return [p3, p4, p5, p6, p7] # 返回多尺度特征金字塔为什么是FPN在目标检测中大物体需要深层的、语义丰富的特征来识别其类别小物体则需要浅层的、细节丰富的特征来定位其位置。FPN通过自顶向下将高层语义特征上采样和横向连接融合同尺度的浅层特征的结构为每一层都构建了兼具高语义和高分辨率的特征图。这使得RetinaNet能在单一网络的不同层级上有效地检测不同尺度的目标。实操心得Backbone的选择与预训练权重项目中默认使用ResNet50这是一个精度与速度的平衡点。如果你想提升精度可以换用ResNet101或ResNeXt但要注意显存消耗和速度下降。务必使用在ImageNet上预训练的权重进行初始化这是加速收敛、提升模型性能的关键。直接随机初始化训练检测网络效果会差很多且训练时间大幅增加。加载预训练权重时要注意骨干网络输出层的名称可能与state_dict中的键名不完全匹配需要灵活处理。2.2 锚点Anchor机制与预测头Head设计FPN输出的每一层特征图如P3到P7都负责检测特定尺度范围的物体。为了覆盖该层负责的尺度我们需要在特征图的每一个像素点上设置多个不同大小和长宽比的“锚点”Anchor。这些锚点本质上是预先定义好的一组候选框。RetinaNet通常在每个位置设置9个锚点3种尺度×3种长宽比如[0.5, 1, 2]。分类与回归头是两个并行的轻量子网络它们共享输入的特征图但各自进行独立的卷积运算。class RetinaNetHead(nn.Module): def __init__(self, in_channels256, num_anchors9, num_classes80, prior_prob0.01): super().__init__() self.num_classes num_classes self.num_anchors num_anchors # 分类子网络预测每个锚点属于各个类别的概率 self.cls_head nn.Sequential( nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, num_anchors * num_classes, kernel_size3, padding1) ) # 回归子网络预测每个锚点的位置偏移量Δx, Δy, Δw, Δh self.reg_head nn.Sequential( nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, in_channels, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(in_channels, num_anchors * 4, kernel_size3, padding1) ) # 初始化技巧将分类卷积最后一层的偏置b初始化为 -log((1-pi)/pi) # 使得初始预测概率接近 prior_prob如0.01防止初始损失过大 bias_value -math.log((1 - prior_prob) / prior_prob) self.cls_head[-1].bias.data.fill_(bias_value) def forward(self, x): cls_logits self.cls_head(x) reg_preds self.reg_head(x) # 调整形状: [batch, channels, H, W] - [batch, H*W*num_anchors, num_classes或4] batch_size x.size(0) cls_logits cls_logits.permute(0, 2, 3, 1).contiguous().view(batch_size, -1, self.num_classes) reg_preds reg_preds.permute(0, 2, 3, 1).contiguous().view(batch_size, -1, 4) return cls_logits, reg_preds锚点匹配策略在训练前需要将成千上万的锚点与真实标注框GT进行匹配。通常采用基于IoU交并比的匹配规则一个GT框会分配给与其IoU最大的锚点同时任何IoU超过某个高阈值如0.5的锚点也会被分配为该GT的正样本IoU低于某个低阈值如0.4的锚点则被标记为负样本背景介于两者之间的锚点被忽略不参与损失计算。这一步是检测任务中非常关键且容易出错的环节。注意事项锚点参数调优锚点的尺度sizes和长宽比ratios需要根据你的数据集目标大小分布进行微调。例如如果你的数据集中包含大量细长形的物体如行人那么增加长宽比为0.25或3的锚点可能会有帮助。你可以通过统计数据集中所有标注框的宽高分布来设计更合适的锚点组合。不匹配的锚点设计会导致模型难以收敛或检测性能下降。2.3 损失函数核心Focal Loss的代码实现与原理Focal Loss是RetinaNet的灵魂它的提出是为了解决单阶段检测器中正负样本前景与背景极度不平衡的问题。在密集锚点中只有极少部分是包含物体的正样本绝大部分都是容易分类的背景负样本。这些大量的、容易分类的负样本会产生虽然单个损失很小但总量巨大的梯度淹没掉来自正样本和难分类负样本的梯度信号。标准的交叉熵损失CE为CE(p, y) -log(p)其中p是模型预测为目标类别的概率对于正样本或为背景的概率对于负样本。Focal Loss在CE的基础上增加了两个调制因子平衡因子 α用于平衡正负样本的重要性。通常为前景类设置一个较小的α如0.25为背景类设置较大的α如0.75以降低大量背景样本的权重。聚焦因子 (1-p)^γ用于降低易分类样本p很大的损失权重让模型更专注于难分类的样本p较小。γ是一个大于0的超参数通常取2。因此Focal Loss的公式为FL(p) -α * (1-p)^γ * log(p)。import torch import torch.nn as nn import torch.nn.functional as F class FocalLoss(nn.Module): def __init__(self, alpha0.25, gamma2.0, reductionmean): super(FocalLoss, self).__init__() self.alpha alpha self.gamma gamma self.reduction reduction def forward(self, inputs, targets): inputs: 模型输出的分类logits形状为 [N, num_anchors, num_classes] targets: 标签形状为 [N, num_anchors]值为类别索引0为背景 # 将logits转换为概率 probs F.softmax(inputs, dim-1) # [N, num_anchors, num_classes] # 获取每个锚点对应目标类别的概率 gathering_idx targets.unsqueeze(-1) # [N, num_anchors, 1] # 注意对于背景targets0我们取的是第0类的概率即背景概率 class_probs torch.gather(probs, -1, gathering_idx).squeeze(-1) # [N, num_anchors] # 计算平衡因子 α_t # 对于正样本α_t alpha对于负样本背景α_t 1 - alpha alpha_factor torch.ones_like(targets) * (1 - self.alpha) # 默认负样本权重 alpha_factor torch.where(targets 0, self.alpha, alpha_factor) # 正样本替换为alpha # 计算调制因子 (1 - p_t)^gamma modulating_factor (1 - class_probs).pow(self.gamma) # 计算标准交叉熵损失 ce_loss F.cross_entropy(inputs.view(-1, inputs.size(-1)), targets.view(-1), reductionnone) ce_loss ce_loss.view_as(targets) # 计算最终的Focal Loss focal_loss alpha_factor * modulating_factor * ce_loss if self.reduction mean: return focal_loss.mean() elif self.reduction sum: return focal_loss.sum() else: return focal_loss为什么Focal Loss有效通过(1-p)^γ项当一个样本被很好地分类p接近1时该项接近0损失被大幅降低。反之当一个样本被错误分类或分类置信度低p较小时该项接近1损失基本保留。这样训练过程就自动地将重心从海量的简单负样本转移到了那些对模型性能提升更有价值的困难样本上。α因子则进一步手动调整了正负样本之间的权重平衡。实操心得Focal Loss的调参alpha和gamma是Focal Loss的关键超参数。原论文中alpha0.25, gamma2.0在COCO数据集上效果很好但这并非金科玉律。在你的数据集上可能需要微调。如果数据集中正样本非常稀少可以尝试增大alpha如0.5来给予正样本更多关注。如果模型在简单样本上过度自信导致难样本学习不足可以尝试增大gamma如3.0。调整后务必在验证集上观察AP平均精度的变化。2.4 边框回归与损失计算分类头告诉我们“是什么”回归头则告诉我们“在哪里”。回归头预测的是锚点框到其匹配的真实框之间的偏移量Δx, Δy, Δw, Δh。通常采用基于对数空间的变换使其回归目标更平滑易于优化。def encode_boxes(anchors, gt_boxes): 计算回归目标将GT框编码为相对于锚点的偏移量。 anchors: [N, 4] (x1, y1, x2, y2) gt_boxes: [N, 4] (x1, y1, x2, y2) returns: targets [N, 4] (dx, dy, dw, dh) anchors_wh anchors[:, 2:] - anchors[:, :2] anchors_xy anchors[:, :2] anchors_wh / 2 gt_wh gt_boxes[:, 2:] - gt_boxes[:, :2] gt_xy gt_boxes[:, :2] gt_wh / 2 dxdy (gt_xy - anchors_xy) / anchors_wh dwdh torch.log(gt_wh / anchors_wh) return torch.cat([dxdy, dwdh], dim-1) def decode_boxes(preds, anchors): 将预测的偏移量解码为最终的检测框。 preds: [N, 4] (dx, dy, dw, dh) anchors: [N, 4] (x1, y1, x2, y2) returns: boxes [N, 4] (x1, y1, x2, y2) anchors_wh anchors[:, 2:] - anchors[:, :2] anchors_xy anchors[:, :2] anchors_wh / 2 pred_xy preds[:, :2] * anchors_wh anchors_xy pred_wh torch.exp(preds[:, 2:]) * anchors_wh boxes torch.zeros_like(preds) boxes[:, :2] pred_xy - pred_wh / 2 boxes[:, 2:] pred_xy pred_wh / 2 return boxes回归损失通常使用平滑L1损失Smooth L1 Loss它对离群值异常大的偏移的敏感性低于L2损失训练更稳定。reg_loss F.smooth_l1_loss(pred_offsets, target_offsets, reductionnone, beta1.0/9.0) # 只对正样本计算回归损失 reg_loss reg_loss.sum(dim-1) # [N] reg_loss reg_loss[pos_indices].sum() / max(1.0, num_pos_anchors.float())总损失是分类Focal Loss和回归Smooth L1 Loss的加权和通常回归损失会乘以一个权重如1.0或2.0以平衡两项损失的尺度。3. 项目环境搭建与训练流程实操3.1 环境配置与依赖安装拿到源码后第一件事是搭建一个可复现的环境。项目通常会提供一个requirements.txt或environment.yml文件。# 1. 创建并激活conda环境推荐 conda create -n retinanet python3.8 -y conda activate retinanet # 2. 安装PyTorch根据你的CUDA版本 # 访问 https://pytorch.org/get-started/locally/ 获取最新命令 # 例如对于CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 3. 安装项目其他依赖 pip install opencv-python pillow matplotlib scipy pycocotools tensorboard # 如果项目有requirements.txt pip install -r requirements.txt踩坑记录PyTorch与CUDA版本匹配这是新手最容易出错的地方。务必使用nvidia-smi查看你的驱动支持的最高CUDA版本然后去PyTorch官网选择对应版本的安装命令。安装后在Python中运行torch.cuda.is_available()验证是否成功。如果失败大概率是版本不匹配需要卸载重装。另一个常见问题是pycocotools在Windows下的安装可能需要从https://github.com/philferriere/cocoapi下载源码手动修改setup.py中的extra_compile_args去掉-Wno-cpp再进行python setup.py build_ext install。3.2 数据准备与数据集类编写RetinaNet项目通常支持COCO或VOC格式的数据集。你需要将你的数据组织成相应的格式。COCO格式需要一个标注文件如instances_train2017.json和对应的图片文件夹。标注文件是包含images,annotations,categories三个主要字段的JSON。VOC格式每张图片对应一个XML标注文件存放在Annotations文件夹图片在JPEGImages文件夹。你需要编写一个继承自torch.utils.data.Dataset的类。这个类的核心是__getitem__方法它需要返回图像张量、目标框张量、以及对应的标签。from torch.utils.data import Dataset import cv2 import torch from pycocotools.coco import COCO class CocoDataset(Dataset): def __init__(self, root_dir, annotation_file, transformNone): self.root_dir root_dir self.coco COCO(annotation_file) self.image_ids list(self.coco.imgs.keys()) self.transform transform # 加载类别映射 self.categories {cat[id]: i for i, cat in enumerate(self.coco.loadCats(self.coco.getCatIds()))} def __getitem__(self, idx): image_id self.image_ids[idx] image_info self.coco.loadImgs(image_id)[0] image_path os.path.join(self.root_dir, image_info[file_name]) image cv2.imread(image_path) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # OpenCV默认BGR转为RGB ann_ids self.coco.getAnnIds(imgIdsimage_id) annotations self.coco.loadAnns(ann_ids) boxes [] labels [] for ann in annotations: # COCO标注是[x, y, width, height] x, y, w, h ann[bbox] # 转为 [x1, y1, x2, y2] boxes.append([x, y, xw, yh]) labels.append(self.categories[ann[category_id]]) target {} target[boxes] torch.as_tensor(boxes, dtypetorch.float32) target[labels] torch.as_tensor(labels, dtypetorch.int64) target[image_id] torch.tensor([image_id]) if self.transform: image, target self.transform(image, target) return image, target数据增强Transform是提升模型泛化能力的关键。训练时通常包括随机水平翻转、颜色抖动、随机缩放裁剪等。可以使用torchvision.transforms或albumentations库。注意对图像进行空间变换时标注框也需要同步变换。3.3 训练循环与关键参数配置训练脚本是项目的核心。你需要构建数据加载器、模型、优化器和学习率调度器。import torch from torch.utils.data import DataLoader from torch.optim import SGD from torch.optim.lr_scheduler import MultiStepLR, CosineAnnealingLR # 1. 初始化模型、移至GPU model RetinaNet(num_classes80).cuda() # 2. 构建优化器通常使用带动量的SGD optimizer SGD(model.parameters(), lr0.01, momentum0.9, weight_decay1e-4) # 3. 学习率调度器在指定epoch衰减或使用Cosine退火 scheduler MultiStepLR(optimizer, milestones[30, 40], gamma0.1) # scheduler CosineAnnealingLR(optimizer, T_maxepochs) # 4. 构建数据加载器 train_loader DataLoader(train_dataset, batch_size8, shuffleTrue, num_workers4, collate_fncollate_fn) # collate_fn用于处理同一batch中图片大小不一致的情况通常将它们填充到相同尺寸。 for epoch in range(total_epochs): model.train() for images, targets in train_loader: images [img.cuda() for img in images] # 假设images是list of tensors (可变尺寸) targets [{k: v.cuda() for k, v in t.items()} for t in targets] optimizer.zero_grad() # 模型前向传播计算损失 loss_dict model(images, targets) losses sum(loss for loss in loss_dict.values()) losses.backward() # 梯度裁剪防止梯度爆炸 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm10.0) optimizer.step() scheduler.step() # 每个epoch后在验证集上评估 evaluate(model, val_loader)关键参数解析学习率lr初始学习率是关键。太大容易震荡不收敛太小收敛慢。对于ImageNet预训练模型常用初始lr在0.01到0.001之间。使用学习率预热Warmup策略可以提升稳定性。批大小batch_size受限于GPU显存。更大的batch_size通常使训练更稳定但可能降低泛化能力。如果显存不足可以累积梯度gradient accumulation即多次前向传播后再更新一次参数模拟大batch效果。权重衰减weight_decayL2正则化系数防止过拟合常用1e-4。调度器schedulerMultiStepLR在固定epoch衰减简单有效CosineAnnealingLR能让学习率平滑下降至0有时效果更好。3.4 模型评估与指标解读目标检测的评估比分类复杂得多核心指标是平均精度Average Precision, AP尤其是COCO AP。在验证集上评估时流程如下前向推理模型输出所有预测框的坐标、类别置信度和类别。后处理置信度过滤去掉置信度低于阈值如0.05的预测。非极大值抑制NMS对每个类别单独进行。NMS会移除那些与更高置信度预测框IoU超过阈值如0.5的重叠框。这是消除重复检测的关键步骤。与真实标注匹配将处理后的预测框与GT框进行匹配通常基于IoU如0.5计算真阳性TP、假阳性FP、假阴性FN。计算PR曲线与AP根据不同的置信度阈值计算精确率Precision和召回率Recall绘制PR曲线曲线下的面积即为该类别的AP。对所有类别的AP取平均得到mAPmean Average Precision。COCO AP进一步细分为AP[0.5:0.95]在IoU阈值从0.5到0.95步长0.05上计算的平均AP这是主要评价指标。AP0.5即PASCAL VOC的mAPIoU阈值为0.5。AP0.75更严格的IoU阈值0.75。AP_s, AP_m, AP_l针对小、中、大不同尺度目标的AP。使用pycocotools可以方便地计算这些指标from pycocotools.cocoeval import COCOeval # 假设coco_gt是COCO标注对象results是符合COCO结果格式的预测列表 coco_dt coco_gt.loadRes(results) coco_eval COCOeval(coco_gt, coco_dt, bbox) coco_eval.evaluate() coco_eval.accumulate() coco_eval.summarize() # 打印AP指标实操心得NMS的参数调优NMS的IoU阈值nms_threshold对最终结果影响很大。默认0.5对于一般场景可以。如果预测结果中同一个物体周围存在大量重叠框可以适当降低阈值如0.4来保留更多框如果同一个物体被重复检测多次可以适当提高阈值如0.6来抑制重复。此外在NMS前进行的置信度过滤阈值score_threshold也需要平衡设得太高如0.5会漏检太低如0.01则计算量剧增且引入大量噪声。通常先设一个较低的值如0.05经过NMS后再根据输出框的最终分数进行二次过滤。4. 常见问题排查与进阶优化技巧4.1 训练过程中的典型问题与解决方案问题1损失Loss不下降或为NaN。可能原因1学习率过高。这是最常见的原因。尝试将学习率降低一个数量级如从0.01到0.001并使用学习率预热。可能原因2数据或标注有问题。检查数据加载环节确保图像能正常读取标注框坐标是有效的x2 x1, y2 y1且在图像范围内。可以写一个脚本可视化几个batch的图片和标注框。可能原因3梯度爆炸。加入梯度裁剪torch.nn.utils.clip_grad_norm_。检查网络初始化特别是新增的层。可能原因4FPN或Head输出有NaN。检查网络中是否有除零或log(0)的操作。在回归目标计算中确保锚点的宽高不为零。问题2验证集mAP始终为0或极低但训练损失正常下降。可能原因1过拟合。训练集性能很好验证集很差。增加数据增强如随机裁剪、MixUp、CutMix或使用更强的正则化如Dropout增大weight_decay。可能原因2评估代码有误。确认评估时代码处于model.eval()模式并且关闭了梯度计算with torch.no_grad():。检查NMS和后处理逻辑是否正确。可能原因3训练集和验证集分布差异大。检查两个数据集是否来自同一分布预处理如归一化是否一致。问题3训练速度慢。可能原因1数据加载是瓶颈。使用DataLoader的num_workers参数通常设为CPU核心数并使用pin_memoryTrue加速GPU传输。考虑将数据集预处理成更快的格式如LMDB。可能原因2模型太大或输入分辨率太高。考虑使用更小的Backbone如ResNet34或降低输入图像尺寸。可能原因3频繁的CPU-GPU数据交换。确保整个batch的数据在送入模型前已在GPU上。4.2 模型性能优化与部署考量1. 混合精度训练AMP使用自动混合精度可以大幅减少显存占用并加快训练速度几乎不影响精度。from torch.cuda.amp import autocast, GradScaler scaler GradScaler() for data in train_loader: optimizer.zero_grad() with autocast(): loss model(data) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()2. 模型剪枝与量化训练完成后如果想部署到资源受限的边缘设备可以考虑剪枝移除网络中不重要的权重或通道。量化将FP32的权重和激活转换为INT8大幅减少模型体积和推理延迟。PyTorch提供了torch.quantization工具。3. 转换为ONNX或TorchScript为了跨平台部署可以将模型导出为ONNX格式或TorchScript。# 导出为ONNX (示例需根据实际输入调整) dummy_input torch.randn(1, 3, 800, 600).cuda() torch.onnx.export(model, dummy_input, retinanet.onnx, opset_version11) # 导出为TorchScript scripted_model torch.jit.script(model) # 或 torch.jit.trace scripted_model.save(retinanet.pt)4.3 在自己的数据集上微调这是最常见的需求。步骤通常如下准备数据将你的数据标注转换为COCO或VOC格式。修改类别数在模型定义中将num_classes参数改为你的类别数记得背景也算一类所以num_classes 你的类别数 1。调整锚点分析你数据集中目标框的宽高分布可能需要调整FPN各层锚点的尺度和长宽比使其更匹配你的数据。加载预训练权重强烈建议在COCO等大型数据集上预训练的模型上微调而不是从头训练。注意处理分类头最后一层由于类别数不同而导致的权重不匹配问题。通常的策略是加载除分类头最后一层外的所有权重最后一层随机初始化。调整超参数由于是微调学习率可以设得比从头训练小如0.001或0.0001。可以冻结Backbone的前几层只训练后面的层以防止小数据上的过拟合。一个实用的微调策略先冻结Backbone只训练FPN和Head训练几个epoch让网络适应新数据的尺度分布然后解冻Backbone的后面几层用更小的学习率进行联合微调。通过这个“pytorch-retinanet-master”项目你不仅能跑通一个目标检测模型更能深入到网络设计、损失函数、训练技巧和问题排查的每一个细节。这种从源码级理解的能力是日后应对更复杂模型和任务的最坚实基础。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进