3. 模型评估与性能分析:用数据说话

模型压缩完了,效果怎么样?不能光靠感觉。我见过太多同学,压缩完模型一看 mAP 掉了 0.5% 就慌了,其实根本没搞清楚瓶颈在哪。这一章,我们就来聊聊怎么用 FLOPs、Params、FPS、mAP 这些硬指标,给模型做一次全面体检。

核心观点:评估不是跑个数字就完事,而是为了定位瓶颈。是计算量太大?还是参数量冗余?或者是推理框架没优化好?每个指标背后都有故事。

3.1 四大核心指标,各司其职

先看一张总览表,把这四个指标的关系理清楚:

指标 全称 衡量什么 单位 压缩后趋势
FLOPs 浮点运算次数 计算复杂度 GFLOPs ↓ 减少
Params 参数量 模型大小 M (百万) ↓ 减少
FPS 每秒帧数 推理速度 fps ↑ 提升
mAP 平均精度均值 检测精度 % ↓ 轻微下降

说白了,FLOPs 和 Params 是模型本身的属性,FPS 是运行时的表现,mAP 是最终效果。四者之间没有的线性关系,这也是评估的难点所在。

3.2 FLOPs 与 Params:理论复杂度分析

我个人习惯,拿到一个压缩后的模型,第一件事就是算 FLOPs 和 Params。这两个指标能帮你快速判断压缩方法是否有效。

3.2.1 怎么算?用工具

别手算,用现成的库。PyTorch 生态里,我常用 thop 或者 ptflops。举个例子:

# 安装:pip install thop
import torch
from thop import profile, clever_format

# 加载你的 YOLOv11 模型(假设已经定义好)
model = YOLOv11()  # 你的模型实例
input = torch.randn(1, 3, 640, 640)  # 标准输入

flops, params = profile(model, inputs=(input,))
flops, params = clever_format([flops, params], "%.3f")

print(f"FLOPs: {flops}, Params: {params}")
# 输出示例:FLOPs: 128.456G, Params: 25.300M
小技巧:注意输入尺寸要和实际部署时一致。YOLOv11 默认是 640x640,如果你部署时用了 320x320,FLOPs 会大幅下降,但 mAP 也会掉。这个 trade-off 要心里有数。

3.2.2 压缩前后的对比

假设我们对 YOLOv11 做了剪枝和量化,结果可能像这样:

模型版本 FLOPs (G) Params (M) 压缩率
原始 YOLOv11 128.5 25.3 -
剪枝 30% 89.9 17.7 30%
剪枝 + INT8 量化 22.5 4.4 82%

你看,剪枝主要降参数量,量化主要降 FLOPs。两者结合效果最好。但注意,FLOPs 降了 82%,FPS 不一定能提升 5 倍,因为还有内存带宽、算子优化等瓶颈。

3.3 FPS:真实推理速度

FLOPs 再低,跑不快也是白搭。FPS 才是用户能感知到的指标。我曾在项目中遇到一个坑:模型在 PC 上跑 120 FPS,换到 Jetson Orin 上只有 15 FPS。为什么?因为没考虑硬件特性。

3.3.1 怎么测 FPS?

测 FPS 要模拟真实部署环境。别用 PyTorch 的 eager 模式测,要用 TensorRT 或 ONNX Runtime。下面是一个标准流程:

  1. 模型导出:将 PyTorch 模型转成 ONNX 或 TensorRT 引擎。
  2. 预热:跑 100 次推理,让 GPU 进入稳定状态。
  3. 正式测试:连续跑 1000 次,记录总时间。
  4. 计算 FPS:1000 / 总时间(秒)。
import time
import numpy as np
import tensorrt as trt

# 假设已经加载了 TensorRT 引擎
engine = load_engine("yolov11.trt")
context = engine.create_execution_context()

# 预热
for _ in range(100):
    context.execute_v2(bindings)

# 正式测试
times = []
for _ in range(1000):
    start = time.time()
    context.execute_v2(bindings)
    end = time.time()
    times.append(end - start)

fps = 1000 / sum(times)
print(f"FPS: {fps:.2f}")
注意:不要用单次推理时间算 FPS,误差太大。我曾经犯过这个错,测出来 200 FPS,实际部署只有 80。一定要用批量测试取平均。

3.3.2 影响 FPS 的隐藏因素

  • Batch Size:batch size 越大,吞吐量越高,但延迟也会增加。边缘设备通常用 batch=1。
  • 内存带宽:量化后的模型虽然计算量小了,但如果内存带宽不够,数据搬运会成为瓶颈。
  • 算子支持:某些自定义算子(如 SiLU)在 TensorRT 上可能没有高效实现,导致降速。

3.4 mAP:精度不能丢

压缩的代价通常是精度下降。mAP 就是用来衡量这个代价的。YOLOv11 官方用的是 COCO 数据集上的 mAP@0.5:0.95,但实际项目中,我更关注 mAP@0.5,因为很多场景下 IoU=0.5 就够用了。

3.4.1 精度与速度的权衡

我习惯画一张 精度-速度曲线,横轴是 FPS,纵轴是 mAP。不同压缩策略会形成不同的曲线。比如:

  • 剪枝:mAP 下降平缓,FPS 提升线性。
  • 量化:mAP 可能突然掉点(尤其是 INT4),但 FPS 提升显著。
  • 蒸馏:mAP 保持最好,但 FPS 提升有限。

你的任务就是找到那个“甜点”——mAP 下降可接受,FPS 满足需求。

3.5 定位性能瓶颈:一张图看懂

下面这张 SVG 图,是我总结的瓶颈定位流程。每次评估完,我都会走一遍这个逻辑:

模型性能瓶颈定位流程图 模型性能瓶颈定位流程 获取压缩后模型 ① 计算 FLOPs / Params 压缩率达标? 是 ② 测 FPS 否 调整压缩策略 FPS 达标? 是 ③ 测 mAP 否 优化推理框架 mAP 达标? 是 ✅ 模型部署就绪 否 回退/微调模型 说明 • 先看理论指标 • 再看实际速度 • 最后验证精度 • 逐层定位瓶颈

这个流程我用了很多项目,屡试不爽。核心思路是:先看理论,再看实际,最后验证效果。每一步都有明确的判断标准,不会盲目优化。

3.6 实战案例:一个完整的评估报告

假设我们做了一个剪枝 + 量化的 YOLOv11 模型,下面是完整的评估数据:

指标 原始模型 压缩后模型 变化 是否达标
FLOPs 128.5 G 22.5 G ↓ 82.5% ✅ 是
Params 25.3 M 4.4 M ↓ 82.6% ✅ 是
FPS (TensorRT) 45 fps 210 fps ↑ 4.7x ✅ 是
mAP@0.5 72.3% 69.8% ↓ 2.5% ⚠️ 临界
mAP@0.5:0.95 51.2% 47.6% ↓ 3.6% ❌ 否
分析结论:FLOPs 和 FPS 都达标了,但 mAP@0.5:0.95 掉了 3.6 个百分点。如果项目要求严格,这个精度损失可能不可接受。这时候,我会建议回退到剪枝 20% 的方案,或者用知识蒸馏来补偿精度。

3.7 避坑指南

最后,分享几个我踩过的坑:

  • 别信单次测试:FPS 至少测 3 次取平均,每次 1000 帧以上。系统负载波动会影响结果。
  • 注意输入尺寸:评估 FLOPs 时用的输入尺寸,必须和部署时一致。我见过有人用 640x640 评估,部署时用 320x320,结果 FPS 翻倍了,但 mAP 也掉了。
  • 量化后的 mAP 波动:INT8 量化后,mAP 可能上下波动 0.5%。这不是 bug,是量化噪声。多测几次取平均。
  • 硬件差异:同一个模型,在 A100 上跑 500 FPS,在 Jetson Nano 上可能只有 10 FPS。评估一定要在目标硬件上进行。
我的习惯:每次压缩完,我都会生成一份类似上面的评估报告,然后和团队一起 review。数据摆在那里,哪里有问题一目了然,省去了很多无意义的争论。