🚀 核心目标
在保持 mAP(平均精度)下降不超过 1-2% 的前提下,将 YOLOv5 模型体积缩小 4-8 倍,推理速度提升 2-4 倍

适用场景

  • 移动端/嵌入式:Android/iOS (TFLite), Raspberry Pi, Jetson Nano, 无人机。
  • 边缘计算:安防摄像头、工业质检机。
  • 高并发服务:降低服务器 GPU 显存占用,提升吞吐量。

技术路线

  1. 稀疏训练 (Sparse Training):在训练中引入 L1 正则化,让不重要的通道权重趋近于 0。
  2. 通道剪枝 (Channel Pruning):根据缩放因子(Scale Factor)切除冗余通道。
  3. 微调 (Fine-tuning):恢复剪枝后的精度。
  4. 量化 (Quantization):将 FP32 转为 INT8,进一步压缩并加速。

一、环境准备与工具链

本实战基于 YOLOv5 v7.0+ (官方仓库) 和 Ultralytics 生态。

# 1. 克隆仓库
git clone https://github.com/ultralytics/yolov5
cd yolov5

# 2. 安装依赖 (2026版 Python 3.10+)
pip install -r requirements.txt

# 3. 安装额外工具
pip install thop # 用于计算 FLOPs 和参数量
pip install onnx onnxruntime
pip install tensorflow-lite-support # 如果最终部署到 TFLite

二、第一阶段:稀疏训练 (Sparse Training)

原理:普通训练只优化权重 WWW。稀疏训练同时优化权重 WWW 和 BN 层的缩放因子 γ\gammaγ (Scale)。通过 L1 正则化惩罚 γ\gammaγ,迫使不重要的通道 γ→0\gamma \to 0γ0

1. 修改训练命令

在标准训练命令基础上,增加 --hyp 参数使用稀疏超参数,或直接设置 --sparsity

官方推荐方式 (使用 hyp.scratch.yaml 的修改版)
YOLOv5 官方 data/hyps/hyp.scratch.yaml 中通常没有 sparsity 项,我们需要创建一个 hyp.finetune_sparse.yaml

# hyp.finetune_sparse.yaml
lr0: 0.01
lrf: 0.1
momentum: 0.937
weight_decay: 0.0005
warmup_epochs: 3.0
warmup_momentum: 0.8
warmup_bias_lr: 0.1
box: 0.05
cls: 0.5
cls_pw: 1.0
obj: 1.0
obj_pw: 1.0
iou_t: 0.2
anchor_t: 4.0
fl_gamma: 0.0
hsv_h: 0.015
hsv_s: 0.7
hsv_v: 0.4
degrees: 0.0
translate: 0.1
scale: 0.5
shear: 0.0
perspective: 0.0
flipud: 0.0
fliplr: 0.5
mosaic: 1.0
mixup: 0.1
copy_paste: 0.0
# 关键参数:BN 层权重的 L1 正则化系数
# 越大越稀疏,但可能影响精度。推荐 0.0001 - 0.001
lambda_bn: 0.0001 

开始稀疏训练

python train.py \
    --img 640 \
    --batch 16 \
    --epochs 100 \
    --data coco128.yaml \
    --weights yolov5s.pt \
    --cfg models/yolov5s.yaml \
    --hyp hyp.finetune_sparse.yaml \
    --project runs/sparse_train \
    --name yolov5s_sparse

注意:如果你使用的是较新版本的 YOLOv5,可以直接在 train.py 中通过 --sparsity 参数开启(如果代码已集成),或者手动修改 models/yolo.py 中的 Model 类,在 loss 计算部分加入 loss += lambda_bn * sum(torch.abs(m.weight)) for m in model.modules() if isinstance(m, nn.BatchNorm2d)

简便方案:Ultralytics 社区通常提供补丁。若不想改代码,可使用第三方库 yolov5-pruning 或手动在 compute_loss 函数中添加 BN 权重的 L1 惩罚。

2. 分析稀疏结果

训练结束后,查看 results.csvconfusion_matrix.png

  • 预期:mAP 可能与正常训练持平或略低(0.5% 以内),但 BN 层的 γ\gammaγ 值分布会出现明显的“双峰”:大量接近 0,少量较大。

三、第二阶段:通道剪枝 (Channel Pruning)

利用上一步得到的稀疏模型,切除 γ\gammaγ 值小的通道。

1. 运行剪枝脚本

YOLOv5 官方仓库通常包含 prune.py (或在 utils/prune.py)。如果没有,可以使用以下逻辑(基于社区通用实现):

python prune.py \
    --weights runs/sparse_train/yolov5s_sparse/weights/best.pt \
    --output weights/yolov5s_pruned.pt \
    --sensitivity 0.01 \  # 敏感度,保留多少比例的通道 (0.01 表示保留最显著的 1%? 不,通常是阈值)
    --normal-thr 0.001    # 阈值:gamma < 0.001 的通道将被剪掉

如果没有现成脚本,手动剪枝逻辑如下

  1. 加载模型。
  2. 遍历所有 BatchNorm2d 层。
  3. 收集所有 bn.weight (即 γ\gammaγ)。
  4. 设定阈值(如全局百分位数的 10%),低于该阈值的索引标记为“待删除”。
  5. 构建新模型,复制未被标记的通道权重。
  6. 保存为新 .pt 文件。

💡 经验值

  • normal-thr = 0.0001: 轻微剪枝,精度几乎无损。
  • normal-thr = 0.001: 中度剪枝,速度提升明显,精度微降。
  • normal-thr = 0.01: 激进剪枝,体积小,但需强力微调。

2. 验证剪枝效果

使用 thop 对比剪枝前后的参数量和计算量:

import torch
from models.yolo import Model
from thop import profile

# 加载剪枝后模型
model = torch.load('weights/yolov5s_pruned.pt', map_location='cpu')['model'].float()
model.eval()

# 模拟输入
img = torch.zeros(1, 3, 640, 640)

# 计算 FLOPs 和 Params
flops, params = profile(model, inputs=(img,), verbose=False)
print(f"Params: {params/1e6:.2f} M")
print(f"FLOPs: {flops/1e9:.2f} G")

预期结果:Params 减少 30%-50%,FLOPs 相应减少。


四、第三阶段:微调 (Fine-tuning)

剪枝后的模型结构变了,精度通常会下降(例如 mAP 从 0.35 降到 0.30)。必须通过微调恢复精度。

关键点

  • 冻结骨干:前几层可以冻结,只训练头部和剪枝层附近的参数(可选)。
  • 学习率:使用较小的学习率(如 0.001 或 0.0001)。
  • ** epochs**:不需要从头训练,30-50 个 epoch 通常足够。
python train.py \
    --img 640 \
    --batch 16 \
    --epochs 50 \
    --data coco128.yaml \
    --weights weights/yolov5s_pruned.pt \  # 加载剪枝后的权重
    --hyp hyp.finetune.yaml \               # 使用普通微调超参,不再需要稀疏正则
    --project runs/finetune \
    --name yolov5s_pruned_finetuned \
    --cache

目标:mAP 恢复到原始模型的 98%-99%


五、第四阶段:量化 (Quantization)

剪枝减少了通道数,量化减少了数值精度(FP32 -> INT8)。两者结合可实现极致压缩。

方案 A:训练后量化 (PTQ) - 推荐,简单快速

无需重新训练,只需少量校准数据。

1. 导出 ONNX
python export.py \
    --weights runs/finetune/yolov5s_pruned_finetuned/weights/best.pt \
    --include onnx \
    --imgsz 640 \
    --dynamic \
    --simplify
2. 使用 ONNX Runtime 或 TFLite 进行 INT8 量化

针对 TFLite (移动端首选)
你需要一个校准数据集(约 100-500 张代表性图片)。

import tensorflow as tf
import numpy as np
import cv2
import os

# 1. 加载 ONNX 模型并转换为 SavedModel (中间步骤,或使用直接转换工具)
# 这里假设你已经有了 .tflite 的 float 版本,或者直接用 TFLiteConverter 从 SavedModel 转
# 最简单路径:PyTorch -> TorchScript -> TFLite (通过 ONNX)

# 使用 ultralytics 导出 tflite 时直接开启量化
# 需要准备一个 representative dataset 生成器

def representative_data_gen():
    # 替换为你的校准图片路径
    dataset_path = 'calibration_images/' 
    images = os.listdir(dataset_path)[:100] # 取100张
    
    for img_file in images:
        img = cv2.imread(os.path.join(dataset_path, img_file))
        img = cv2.resize(img, (640, 640))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        img = img.astype(np.float32) / 255.0
        img = np.expand_dims(img, axis=0)
        yield [img]

# 转换
converter = tf.lite.TFLiteConverter.from_saved_model('best_saved_model')
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.representative_dataset = representative_data_gen
# 强制整型量化 (Full Integer Quantization)
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.uint8 # 或 tf.int8
converter.inference_output_type = tf.uint8

tflite_quant_model = converter.convert()

with open('yolov5s_pruned_int8.tflite', 'wb') as f:
    f.write(tflite_quant_model)

针对 TensorRT (NVIDIA Jetson/Server)

# 使用 trtexec 工具
trtexec --onnx=yolov5s_pruned.onnx \
        --saveEngine=yolov5s_pruned_int8.engine \
        --int8 \
        --calib=calibration_cache.bin \
        --buildOnly

方案 B:量化感知训练 (QAT) - 精度最高,成本高

如果在 PTQ 后精度下降超过 2%,则需进行 QAT。这需要在训练过程中模拟量化噪声。

  • 需要使用 PyTorch Quantization API (torch.quantization)。
  • 流程:插入 FakeQuantize 节点 -> 训练几个 Epoch -> 转换导出。
  • 注:YOLOv5 原生对 QAT 支持一般,通常 PTQ 已足够。

六、性能对比实测 (模拟数据)

YOLOv5s (COCO 数据集) 为例,在不同阶段的性能表现:

阶段 模型文件 参数量 (M) FLOPs (G) mAP@0.5 推理速度 (Jetson Nano) 推理速度 (Snapdragon 865) 体积 (MB)
原始 yolov5s.pt 7.2 16.5 0.350 4.5 FPS 22 FPS 14.4
剪枝后 yolov5s_pruned.pt 3.8 (-47%) 8.2 (-50%) 0.335 (-1.5%) 8.2 FPS (+82%) 38 FPS (+72%) 7.6
剪枝 + 微调 yolov5s_ft.pt 3.8 8.2 0.348 (-0.2%) 8.2 FPS 38 FPS 7.6
剪枝 + INT8 yolov5s_int8.tflite 3.8 8.2 0.345 (-0.5%) 14.5 FPS (+220%) 65 FPS (+195%) 3.8

结论

  • 体积缩小 3.8 倍
  • 速度提升 3 倍 (移动端)。
  • 精度损失仅 0.5% (几乎不可感知)。

七、避坑指南与最佳实践

  1. 稀疏系数选择

    • lambda_bn 太大(>0.001)会导致模型难以收敛,mAP 暴跌。
    • lambda_bn 太小(<0.00001)剪枝效果不明显。
    • 策略:先小范围实验(如 0.0001),观察 gamma 分布直方图。
  2. 剪枝阈值设定

    • 不要一刀切。不同层的敏感度不同。
    • 高级玩法:分层设定阈值,深层网络可以剪更多,浅层(特征提取)保留更多。
  3. 量化校准集

    • 校准集必须具有代表性。如果只用白天的图片校准,晚上检测效果会变差。
    • 数量不用多,100-200 张覆盖各种场景即可。
  4. 算子支持

    • 剪枝后的模型结构可能包含非常规的切片操作,导出 ONNX 时报错。
    • 解决:确保使用最新版的 onnxonnx-simplifier (--simplify)。
  5. 硬件兼容性

    • INT8 模型在某些老旧 GPU 或 CPU 上可能反而变慢(因为需要反量化)。
    • 测试:务必在目标设备上实测 FPS。对于不支持 INT8 指令集的设备,回退到 FP16。

八、总结

YOLOv5 轻量化三部曲

  1. 稀疏训练:让模型学会“偷懒”,把不用的神经元权重压到 0。
  2. 手术剪枝:切掉那些权重为 0 的神经元,物理减小模型体积。
  3. 量化压缩:把剩下的神经元从“高精度浮点”变成“低精度整数”,进一步压榨速度。

这套组合拳是 2026 年边缘 AI 部署的标准工业流程。虽然 YOLOv8/v10 原生支持更好的导出,但 YOLOv5 的这套手动控制流程能让你更深刻地理解模型结构,并在极端资源受限的场景下(如 MCU、超低端手机)挖掘出最后的性能潜力。

最后建议
如果你的项目时间紧迫,优先尝试 PTQ (INT8 量化),通常能直接获得 2 倍加速且无需修改训练流程。只有在对体积极度敏感(<5MB)时,再上 剪枝 大招。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐