陷阱二:BEV特征图尺寸与NPU内存对齐的“隐形墙”
好,咱们接着聊。上一章讲了量化,这一章我带你碰一个更隐蔽的坑——内存对齐。
说实话,我第一次踩这个坑的时候,整整调了两天。模型在PC上跑得飞起,一上NPU,要么跑出来的结果全是乱的,要么干脆跑不起来。后来一查,问题出在特征图的尺寸上。
你可能会问:“尺寸不对,大不了补零嘛,能有多大事?”
嗯,还真不是补零那么简单。NPU的内存访问有它自己的“脾气”。
NPU为什么需要内存对齐?
先讲个底层原理。NPU处理数据,不是按“一个像素”来的。
它一次读一大块数据。比如32字节、64字节,甚至128字节。
如果特征图的宽度不是这些对齐单位的整数倍,就会出问题。
举个例子。假设NPU一次读32字节。你的特征图宽度是17像素,每个像素4字节(float32)。
17 × 4 = 68字节。68除以32,余4。
这就尴尬了。NPU读第一行,需要读3次(32+32+4)。但第三次只用了4字节,剩下的28字节怎么办?
有些NPU会直接报错。有些NPU会读脏数据。还有些NPU,嗯,它“假装”没看见,但结果就是错的。
核心结论:BEV特征图的宽度和高度,必须满足NPU的对齐要求。通常是16、32或64的整数倍。
我在项目中遇到的真实案例
我记得有一次部署一个BEV模型。输入图像是608×608,经过backbone后,特征图尺寸是19×19×256。
19?你想想看,19能被32整除吗?显然不能。
结果就是,NPU在计算时,把19当成了32来处理。多出来的13个位置,全是随机数。
最终输出的BEV鸟瞰图,边缘全是噪点。我当时还以为是模型没训练好,折腾了好久。
后来怎么解决的?
我把输入改成640×640。特征图变成20×20。20虽然也不是32的倍数,但至少是4的倍数,很多NPU能处理。
更稳妥的做法,是改成32的倍数,比如608改成640,或者干脆用672。
注意:不要以为只有宽度需要对齐。高度也一样。有些NPU对通道数也有对齐要求,比如通道数必须是8或16的倍数。
常见的对齐要求一览
不同NPU的对齐要求不一样。我整理了一个表格,方便你对照:
| NPU类型 | 宽度/高度对齐 | 通道对齐 | 备注 |
|---|---|---|---|
| 瑞芯微RK3588 | 16像素 | 8通道 | 部分算子要求32对齐 |
| 地平线J5 | 32像素 | 16通道 | BEV特征图建议64对齐 |
| 寒武纪MLU220 | 64像素 | 32通道 | 要求比较严格 |
| 高通SNPE | 16像素 | 4通道 | 相对宽松 |
这个表只是参考。具体到你的芯片,一定要查手册。我个人习惯是,拿到新芯片先跑一个对齐测试。
如何检测对齐问题?
说实话,对齐问题很难肉眼发现。因为模型在PC上跑是对的,上板子就错。
我教你三个方法:
- 打印特征图尺寸:在模型转换时,把每一层的输出尺寸都打印出来。看有没有“非对齐”的数字。
- 对比输出:用同一张图,在PC上跑一次,在NPU上跑一次。逐像素对比。如果差异很大,大概率是对齐问题。
- 补零测试:把特征图尺寸强行补到对齐。如果补零后结果变好了,那就是对齐的锅。
小技巧:我写了一个脚本,自动检测模型所有层的尺寸是否对齐。脚本逻辑很简单:遍历所有层的输出shape,检查每个维度是否能被对齐单位整除。不能整除的,标红报警。
解决方案:补零与裁剪
发现问题后,怎么修?
主要有两种做法:
- 补零(Padding):在特征图右侧和下方补零,让尺寸变成对齐的倍数。比如19×19补成32×32。注意,补零后要记录原始尺寸,后续处理要裁剪回来。
- 调整输入:直接修改输入图像的尺寸,让整个pipeline的输出都对齐。比如输入从608改成640。这是最干净的做法。
我个人更推荐第二种。因为补零会引入额外的计算量,而且有些NPU对补零的处理并不高效。
我曾经在一个项目里,为了省事用了补零。结果NPU把补零的区域也算了,白白浪费了30%的算力。后来改成调整输入尺寸,性能直接提升了一截。
避坑指南
最后,给你几条我踩坑踩出来的经验:
- 设计模型时就考虑对齐:不要等到部署了才发现。在模型设计阶段,就把特征图尺寸设成32或64的倍数。
- 不要相信“自动对齐”:有些模型转换工具声称会自动对齐。但据我观察,它们经常对齐得不对。还是自己手动检查最靠谱。
- 注意BEV特有的尺寸:BEV模型的输出通常是H×W×C,比如200×200×64。200能被32整除吗?可以。但如果是180×180呢?180除以32,余20。不行。
- 多尺度特征图要统一对齐:BEV模型经常用FPN,多个尺度的特征图融合。每个尺度的尺寸都要对齐,否则融合时会出问题。
一句话总结:内存对齐是NPU部署的“隐形墙”。你撞上去之前,根本感觉不到它的存在。但撞一次,就记住了。
好了,这一章就到这里。下一章我们聊聊BEV模型里最让人头疼的“动态尺寸”问题。到时候见。