AI-深度学习

从零搭建视觉模型 18:轻量模型与计算成本

展开合集目录
  1. 从零搭建视觉模型 01:图片进入模型后发生了什么
  2. 从零搭建视觉模型 02:Python 起步,写给 C/C++ 开发者
  3. 从零搭建视觉模型 03:从像素到张量的基础概念
  4. 从零搭建视觉模型 04:图片如何变成张量
  5. 从零搭建视觉模型 05:MLP 入门,从手算到图片输入
  6. 从零搭建视觉模型 06:亲手搭建一个小型 CNN
  7. 从零搭建视觉模型 07:模型怎样预测类别:分数、概率与决策
  8. 从零搭建视觉模型 08:让模型开始学习,损失、梯度与训练循环
  9. 从零搭建视觉模型 09:数据集、标签与数据划分
  10. 从零搭建视觉模型 10:损失函数与优化器:怎样把错误变成更新
  11. 从零搭建视觉模型 11:批次、训练轮次与学习率
  12. 从零搭建视觉模型 12:训练第一个真实图片分类器
  13. 从零搭建视觉模型 13:过拟合、数据增强与正则化
  14. 从零搭建视觉模型 14:验证、测试与错误分析
  15. 从零搭建视觉模型 15:保存模型、恢复训练与独立推理
  16. 从零搭建视觉模型 · 综合实践:实现 MLP,从损失计算、训练到 ONNX 推理
  17. 从零搭建视觉模型 16:ResNet 与残差连接
  18. 从零搭建视觉模型 17:预训练、迁移学习与微调
  19. 从零搭建视觉模型 18:轻量模型与计算成本
  20. 从零搭建视觉模型 19:目标检测与 YOLO:从类别到目标框
  21. 从零搭建视觉模型 20:图像分割与 U-Net:给每个像素一个判断
  22. 从零搭建视觉模型 21:注意力与视觉 Transformer
  23. 从零搭建视觉模型 22:从训练模型到 ONNX
  24. 从零搭建视觉模型 23:量化与精度对比
  25. 从零搭建视觉模型 24:从摄像头输入到部署验收
  26. 从零搭建视觉模型 25:发展历史、主流分类与当前格局
从零搭建视觉模型 18:轻量模型与计算成本的封面

学习初稿 · 第 18 课。 本篇已开放网页阅读,用于先建立完整认识。文中手算值和示意记录不代表真实训练成绩;真实数据训练、预训练权重和板端测试以本篇具体说明为准,本篇已附代码练习,实际验证范围见文末与包内记录。

模型更小,为什么不一定更快

参数数量告诉我们需要存储多少可学习数值,不能独立决定推理延迟。中间激活、内存访问、算子实现、并行能力和输入分辨率都影响执行。一个参数很少但包含许多小操作的模型,可能因调度和数据搬移而变慢。

因此,轻量化首先要明确约束:内存不够、单帧延迟过高、功耗过大,还是吞吐不够?它们需要的优化未必相同。

手算一个普通卷积

忽略偏置,卷积核大小 K、输入通道 Cin、输出通道 Cout,对应参数数为 K²×Cin×Cout。每个输出位置都应用这些运算,乘加次数约为 Hout×Wout×K²×Cin×Cout。

将一次乘法加一次加法记为一次 MAC;某些资料把它记成两次 FLOPs,比较前先统一统计口径。以下例子使用 3×3、Cin=16、Cout=32、输出 32×32:

k, cin, cout, h, w = 3, 16, 32, 32, 32
regular = k*k*cin*cout
depthwise_pointwise = k*k*cin + cin*cout
print('parameters:', regular, depthwise_pointwise)
print('MACs:', h*w*regular, h*w*depthwise_pointwise)
assert regular == 4608
assert depthwise_pointwise == 656

深度可分离卷积先在每个输入通道内做空间卷积,再用 1×1 卷积混合通道。这里得到 656 个参数,相比 4608 显著减少。但它改变了结构和表达方式,不能把已训练普通卷积随意替换后就期待输出不变。MobileNet 原论文

激活可能比权重更占地方

形状 [1,32,224,224] 的 float32 特征图约占 1×32×224×224×4 字节,即约 6.42 MB 十进制存储。一次计算可能同时保留多个中间张量,训练还要保存反向传播需要的信息。

输入宽高各缩小一半,空间位置减少到四分之一,但目标细节也可能丢失。对于小目标,盲目降低分辨率可能使任务本身无法完成,所以资源优化必须连同质量评估进行。

减少参数、剪枝、蒸馏和量化分别作用于不同方面。非结构化零权重若没有稀疏执行支持,未必减少实际耗时;剪掉整个通道会改变结构,需要正确重建后续层并评估质量。

延迟与吞吐要分开测

单张输入的端到端延迟包括预处理、传输、模型和后处理。批量吞吐关注单位时间完成多少张,可能通过增大 batch 提升,但也增加排队和内存,不一定适合实时摄像头。

测量前固定输入尺寸、数值精度、batch、线程数、设备模式和功耗策略。先预热再记录多次耗时,报告中位数与高分位,并注明是否包含模型加载。异步加速后端需要使用其同步或事件计时机制,否则计到的可能只是提交任务的时间。

还应观察长时间运行:温度上升后降频、内存碎片、输入队列堆积都可能改变表现。桌面 CPU 上的排序不能直接推导 NPU 上的排序,尤其当某些算子回退到 CPU 时。

如何选择一个候选模型

先以现有小 CNN 建立基线,再选择一个轻量结构做相同任务对照。比较验证质量、峰值内存、单帧延迟和实际算子支持。预训练模型与从零训练模型需要分别标明,避免把训练条件变化隐藏在“网络更轻”之后。

本课的手算只说明理论计算差异,没有声称某个板卡达到特定 FPS。设备测试放到最后的部署验收课,那里会明确输入、后端和时间边界。

练习

  1. FP32 权重 100 万个,仅权重理想大小是多少?
  2. batch=8 的吞吐提高,是否表示单个请求延迟必然降低?
  3. 为什么深度可分离卷积理论 MAC 更少,仍需在目标设备测量?
参考答案

约 4 MB 十进制,不含元数据与激活。吞吐和延迟不同,批处理可能带来等待。硬件内核、内存带宽、调度及回退会影响实际速度,理论计数没有覆盖这些成本。

下一课开始从整图分类走向目标检测:同一张图里可能有多个目标,输出也随之改变。

配套代码练习

实验:普通卷积与深度可分离卷积成本

查看完整代码:main.py
"""普通卷积与深度可分离卷积成本。运行方式与练习见 README.md。"""
cin,cout,k,h,w = 32,64,3,32,32
regular = cin*cout*k*k
separable = cin*k*k+cin*cout
print('不含 bias 参数:',regular,separable)
print('MACs:',regular*h*w,separable*h*w)
print('参数比例:',separable/regular)
print('FP32 输出激活字节:',cout*h*w*4)
assert regular == 18432 and separable == 2336
assert regular*h*w == 18874368
# MAC 一次乘加按一个单位;若将乘与加各算一次,FLOPs 约为两倍。
下载代码

在项目根目录执行;下载包解压后,在解压目录执行 python3 main.py。

cd 'content/posts/AI-深度学习/从零搭建视觉模型/18-轻量模型与计算成本/code' && python3 main.py

本篇已附独立实验包,在代码卡片中查看源码、下载或复制运行命令。解压后执行 python3 main.py;依赖安装见包内 README.md。

观察目标: 普通/可分离参数为 18432/2336;32×32 输出下普通卷积为 18874368 MACs。

动手修改: 保持通道不变将分辨率翻倍,再将通道翻倍,分别观察参数量和 MACs;解释为何这还不能预测设备延迟。

所有输入均在本篇代码中构造;运行范围和验证记录见包内 verification.md。


完整阅读目录 · 上一篇 · 下一篇