3. 轻量化设计原则:计算瓶颈与端侧硬件特性
做端侧AI ISP,说白了就是在“螺蛳壳里做道场”。你模型设计得再花哨,跑不动就是白搭。我个人习惯,在动手写第一行代码前,先搞清楚两个事:计算瓶颈在哪,以及目标硬件是什么脾气。
3.1 计算瓶颈分析:MACs vs 内存带宽
很多刚入行的同学喜欢只看参数量,觉得参数少就快。其实这是个误区。我见过一个项目,模型参数量压到了1M以内,结果在NPU上跑起来比2M的模型还慢。为什么?因为内存访问瓶颈卡死了。
端侧推理的瓶颈,通常分两类:
- 计算密集型(Compute-bound):MACs(乘加运算)是短板。典型如大卷积核、全连接层。
- 内存密集型(Memory-bound):数据搬运是短板。典型如Depthwise卷积、逐元素操作、通道shuffle。
核心公式:
计算强度 = MACs / 内存访问量 (Bytes)
当计算强度 < 硬件算力/带宽比 时,就是内存瓶颈。
举个例子。一个3x3的标准卷积,假设输入输出都是64通道,特征图32x32。它的计算强度大概在几十左右。但换成3x3的Depthwise卷积,计算强度可能直接掉到个位数。这意味着,DWConv在大多数端侧芯片上都是访存受限的。
| 操作类型 | 计算强度(相对值) | 瓶颈类型 | 典型场景 |
|---|---|---|---|
| 标准Conv (3x3) | 高 | 计算密集 | 特征提取主干 |
| Depthwise Conv | 极低 | 内存密集 | 轻量化骨干 |
| Pointwise Conv (1x1) | 中 | 两者之间 | 通道变换 |
| Add / Concat | 接近0 | 纯内存密集 | 残差连接 |
我的经验:在量化之前,先用profiler跑一遍模型的内存访问量。如果某个层的MACs占比不到10%,但内存访问占比超过30%,那它就是优化重点。我曾经把一个模型的Add操作从5次合并成2次,推理速度直接快了15%。
3.2 端侧硬件特性:DSP / NPU / GPU
不同硬件,脾气完全不同。你想想看,用GPU的思路去优化NPU,或者用CPU的思维去写DSP代码,结果往往很惨。我踩过这个坑,所以今天重点说说这三者的区别。
3.2.1 DSP(数字信号处理器)
DSP的特点是低功耗、定长计算。它擅长流水线作业,但对不规则的内存访问非常敏感。
- 优势:极低功耗,适合Always-on场景(比如预览流)。
- 劣势:通常只支持INT8,对激活函数支持有限(很多DSP连ReLU都要用查表模拟)。
- 优化要点:尽量使用连续内存访问,避免随机索引。我建议把网络设计成“流式”结构,减少中间结果的DDR回写。
3.2.2 NPU(神经网络处理器)
NPU是专门为MACs设计的。它内部有大量的乘加阵列,算力很高。但NPU有个通病:对模型结构敏感。
- 优势:高算力,高能效比,支持混合精度(INT8/INT16)。
- 劣势:对非4的倍数通道不友好,对不规则张量操作(如Gather)支持差。
- 优化要点:通道数尽量对齐到4或8的倍数。避免使用Channel Shuffle这类操作,NPU跑起来效率极低。
避坑指南:我曾经把一个ShuffleNetV2移植到某款NPU上,结果速度比MobileNetV3还慢。后来发现,NPU对Channel Shuffle的实现是靠CPU回退的。从那以后,我设计网络时都会先查一下目标NPU的算子支持列表。
3.2.3 GPU(移动端GPU)
移动端GPU(如Adreno、Mali)和桌面GPU不太一样。它更强调并行度和数据复用。
- 优势:支持FP16,对激活函数支持好,适合复杂网络结构。
- 劣势:功耗相对较高,不适合长时间跑在最高频率。
- 优化要点:尽量使用GPU擅长的密集计算,减少小算子(如逐元素乘)的调用次数。我个人习惯把多个小算子融合成一个kernel。
3.3 硬件特性对比总表
| 特性 | DSP | NPU | GPU |
|---|---|---|---|
| 典型功耗 | 极低(< 100mW) | 低(100-500mW) | 中(500mW-2W) |
| 典型算力 | 0.1-1 TOPS | 1-10 TOPS | 0.5-2 TFLOPS |
| 精度支持 | INT8为主 | INT8/INT16 | FP16/FP32 |
| 算子灵活性 | 低 | 中 | 高 |
| 内存带宽敏感度 | 高 | 中 | 低 |
| 最佳场景 | Always-on、低功耗 | 高吞吐推理 | 复杂模型、训练 |
3.4 轻量化设计原则总结
基于上面的分析,我总结了几条设计原则:
- 先定硬件,再定网络:不要先设计一个通用网络,再去找硬件。反过来,先搞清楚目标芯片的算力、带宽、算子支持列表。
- 计算强度要匹配:如果硬件带宽有限,就多用计算密集的操作(如标准Conv),少用Depthwise Conv。反之亦然。
- 通道对齐:NPU和DSP对通道数对齐有要求。我建议统一用8的倍数,省心。
- 减少内存搬运:能融合的算子就融合,能复用的数据就复用。端侧芯片的DDR带宽往往是真正的瓶颈。
- 量化感知设计:在设计网络时就要考虑量化后的精度损失。比如,避免使用对量化敏感的激活函数(如Sigmoid、Swish)。
一句话总结:轻量化不是简单的“减少参数”,而是让模型的计算模式和内存访问模式,去适配目标硬件的“舒适区”。
3.5 端侧AI ISP轻量化设计流程
下面这张图,是我在实际项目中总结出来的设计流程。每次开始一个新项目,我都会按这个流程走一遍。
个人习惯:在步骤3(瓶颈分析)和步骤4(网络设计)之间,我通常会做一个快速原型验证。用PyTorch搭一个简化版网络,直接在目标硬件上跑一下profiling。这一步花不了半天时间,但能避免后面走大弯路。