基于PyTorch与Torchvision的RetinaNet物体检测全流程指南

基于PyTorch与Torchvision的RetinaNet物体检测全流程指南

一、RetinaNet模型核心原理解析

RetinaNet作为单阶段目标检测的里程碑式模型,其核心创新在于引入Focal Loss解决类别不平衡问题。该模型由三部分构成:

  1. 特征金字塔网络(FPN):通过自顶向下和横向连接构建多尺度特征图,增强小目标检测能力。FPN在ResNet骨干网络后添加5个特征层(P3-P7),每个特征层对应不同空间分辨率(8×-256×下采样率)。
  2. 分类子网:采用4个3×3卷积层+ReLU激活,最终输出K×A维分类分数(K为类别数,A为锚框数量)。Torchvision实现中默认使用256个通道的卷积核。
  3. 回归子网:结构与分类子网平行,输出4×A维边界框偏移量。两个子网共享FPN特征但参数独立,避免特征耦合。

Focal Loss通过动态调整难易样本权重,将标准交叉熵损失改造为:
FL(pt) = -αt(1-pt)γlog(pt)
其中pt为预测概率,γ=2时可使易分类样本权重降低100倍,有效聚焦难样本。

二、Torchvision实现关键代码解析

1. 模型初始化

  1. import torchvision
  2. from torchvision.models.detection import retinanet_resnet50_fpn
  3. # 加载预训练模型(COCO数据集)
  4. model = retinanet_resnet50_fpn(pretrained=True)
  5. model.num_classes = 20 # 修改类别数(需与数据集匹配)

Torchvision提供了三种骨干网络变体:ResNet-50/101/152-FPN,预训练权重自动下载。模型包含两个关键组件:

  • backbone:由ResNet和FPN组成的特征提取器
  • head:包含分类和回归子网的检测头

2. 数据加载管道

  1. from torchvision import transforms as T
  2. from torchvision.datasets import CocoDetection
  3. transform = T.Compose([
  4. T.ToTensor(),
  5. T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
  6. ])
  7. dataset = CocoDetection(
  8. root='path/to/images',
  9. annFile='path/to/annotations.json',
  10. transform=transform
  11. )

需特别注意COCO格式标注文件的字段要求:images数组包含id/file_name/height/width,annotations数组包含image_id/category_id/bbox(需归一化到[0,1])。

3. 训练配置优化

  1. import torch.optim as optim
  2. from torch.optim.lr_scheduler import StepLR
  3. params = [p for p in model.parameters() if p.requires_grad]
  4. optimizer = optim.SGD(params, lr=0.01, momentum=0.9, weight_decay=0.0001)
  5. scheduler = StepLR(optimizer, step_size=3, gamma=0.1)

关键训练参数建议:

  • 初始学习率:0.01(ResNet-50)/0.005(ResNet-101)
  • 批量大小:根据GPU显存调整(建议4-8张/GPU)
  • 锚框配置:Torchvision默认使用[32,64,128,256,512]五种尺度,每种尺度3种长宽比(1:2,1:1,2:1)

三、完整训练流程实现

1. 训练循环实现

  1. def train_one_epoch(model, optimizer, data_loader, device, epoch, print_freq=10):
  2. model.train()
  3. metric_logger = utils.MetricLogger(delimiter=" ")
  4. header = f'Epoch: [{epoch}]'
  5. for images, targets in metric_logger.log_every(data_loader, print_freq, header):
  6. images = [img.to(device) for img in images]
  7. targets = [{k: v.to(device) for k, v in t.items()} for t in targets]
  8. loss_dict = model(images, targets)
  9. losses = sum(loss for loss in loss_dict.values())
  10. optimizer.zero_grad()
  11. losses.backward()
  12. optimizer.step()
  13. metric_logger.update(**loss_dict)
  14. return metric_logger.meters['loss_classifier'].global_avg

2. 评估指标计算

  1. def evaluate(model, data_loader, device):
  2. model.eval()
  3. cpu_device = torch.device("cpu")
  4. coco_evaluator = COCOEvaluator(data_loader.dataset, iou_types=['bbox'])
  5. metrics = coco_evaluator.evaluate()
  6. return metrics['bbox']['AP'] # 返回mAP@[IoU=0.50:0.95]

需安装pycocotools库进行标准COCO指标评估,关键指标包括:

  • AP(平均精度)
  • AP50(IoU=0.5时的精度)
  • AP75(IoU=0.75时的精度)
  • APs/APm/APl(小/中/大目标精度)

四、性能优化实战技巧

1. 数据增强策略

  1. from torchvision import transforms as T
  2. augmentation = T.Compose([
  3. T.RandomHorizontalFlip(0.5),
  4. T.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
  5. T.RandomApply([T.GaussianBlur(kernel_size=3, sigma=(0.1, 2.0))], p=0.1),
  6. T.ToTensor(),
  7. T.Normalize(...)
  8. ])

建议组合使用:

  • 几何变换:随机缩放(0.8-1.2倍)、随机裁剪
  • 色彩变换:亮度/对比度/饱和度调整
  • 高级增强:MixUp/CutMix(需自定义数据加载器)

2. 模型微调策略

  1. 骨干网络冻结:前3个阶段冻结,仅训练FPN和检测头
    1. for name, param in model.backbone.named_parameters():
    2. if 'layer4' not in name and 'fpn' not in name:
    3. param.requires_grad = False
  2. 学习率热身:前500步线性增加学习率
    1. def warmup_lr_scheduler(optimizer, warmup_iters, warmup_factor):
    2. def f(x):
    3. if x >= warmup_iters:
    4. return 1
    5. alpha = float(x) / warmup_iters
    6. return warmup_factor * (1 - alpha) + alpha
    7. return optim.lr_scheduler.LambdaLR(optimizer, f)

五、部署应用实战

1. 模型导出为TorchScript

  1. input_tensor = torch.rand(1, 3, 800, 800)
  2. traced_model = torch.jit.trace(model, input_tensor)
  3. traced_model.save("retinanet.pt")

2. ONNX格式转换

  1. torch.onnx.export(
  2. model,
  3. input_tensor,
  4. "retinanet.onnx",
  5. input_names=["images"],
  6. output_names=["boxes", "labels", "scores"],
  7. dynamic_axes={"images": {0: "batch"}, "boxes": {0: "batch"}}
  8. )

3. C++推理示例

  1. #include <torch/script.h>
  2. #include <opencv2/opencv.hpp>
  3. auto model = torch::jit::load("retinanet.pt");
  4. cv::Mat img = cv::imread("test.jpg");
  5. cv::Mat tensor_img;
  6. cv::resize(img, tensor_img, cv::Size(800, 800));
  7. tensor_img.convertTo(tensor_img, CV_32F, 1.0/255);
  8. auto input = torch::from_blob(
  9. tensor_img.data,
  10. {1, tensor_img.rows, tensor_img.cols, 3}
  11. ).permute({0, 3, 1, 2}).to(torch::kCUDA);
  12. auto outputs = model.forward({input}).toTuple()->elements();
  13. auto boxes = outputs[0].toTensor();
  14. auto labels = outputs[1].toTensor();
  15. auto scores = outputs[2].toTensor();

六、常见问题解决方案

  1. 训练不收敛

    • 检查锚框匹配逻辑,确保正负样本比例合理
    • 降低初始学习率至0.005
    • 增加训练轮次至20+epoch
  2. 小目标检测差

    • 在FPN顶层添加P2特征层(下采样率4×)
    • 增加小尺度锚框(如16×16)
    • 使用更高分辨率输入(如1024×1024)
  3. 推理速度慢

    • 启用TensorRT加速(FP16精度可提速2-3倍)
    • 量化感知训练(QAT)减少模型体积
    • 使用TorchScript优化执行图

通过系统掌握上述技术要点,开发者可在PyTorch生态中高效实现工业级RetinaNet物体检测系统。实际项目数据显示,在COCO数据集上微调后的RetinaNet-ResNet50模型可达38.5mAP,推理速度在V100 GPU上达到45FPS(800×800输入)。