最近在折腾边缘侧的AI落地,正好把手上几块板子(RK3588、Jetson Orin Nano)的部署流程又重新捋了一遍。说句实在话,很多人在芯片选型和部署阶段反复踩坑,本质问题不是模型调得不好,而是没把"AI芯片的软硬件设计"这件事当成一个整体来看。芯片选型、硬件架构理解、软件栈适配、算子映射、量化校准、性能调优,这是一条完整的链路,任何一个环节掉链子,都会在最后的部署环节变成难啃的骨头。这篇文章作为"AI芯片的软硬件设计"系列的第二篇,我想把这条链路上真正重要的部分拿出来聊一聊,顺带把我在实际项目中踩过的坑和总结的排查思路一并写出来,希望对正在做AI落地、边缘计算、嵌入式视觉的你有参考价值。
AI芯片不是单纯把硬件堆起来就完事,也不是拿到一套SDK就能高枕无忧。真正的难点在于让硬件算力被软件充分"喂饱",同时让软件算法能在硬件约束下跑出该有的性能。这篇文章适合三类人:一是准备做边缘AI产品选型的工程师,二是正在学嵌入式AI、想搞懂芯片内部逻辑的学生,三是已经在用NPU/GPU做推理加速但总被性能瓶颈卡住的开发者。后面讲的内容不会停留在PPT层面,都是可以直接落地的思路和操作。
1. 全局视角:为什么AI芯片必须软硬件一起看
1.1 芯片算力只是起点,不是终点
很多人在选型时第一眼看的就是TOPS算力,觉得算力大就万事大吉。但实际上,AI芯片的算力指标只是理想状态下的峰值,真正能跑出来的有效算力往往只有峰值的30%到60%。原因是多方面的,比如数据搬运消耗了太多时钟周期,比如算子没有被充分映射到硬件资源上,再比如量化精度掉点导致不得不回退到FP16、FP32去做混合精度推理。
我举个例子:一款宣称8TOPS算力的芯片,实际部署YOLOv5s时,如果batch size设为1,帧率可能只有20到30FPS,远达不到算力数字给人的预期。为什么会这样?因为NPU在计算卷积时,需要把输入图像、权重、中间特征图全部搬运到片上缓存,卷积计算本身很快,但片外DRAM带宽成了瓶颈。带宽不够,算力再高也只能等着数据喂进来。
这就要引出软硬件协同设计的第一个核心理念:计算与访存的平衡。芯片设计者在硬件层面做的事情,很大一部分是在优化数据通路,减少片外搬运;而软件开发者在软件层面要做的事情,是配合这种硬件结构,合理安排内存布局、算子执行顺序、数据复用策略。两边不对齐,算力就是纸面数字。
1.2 软硬件协同实则是"两岸搭桥"的逻辑
软件和硬件之间的关系,像是一个人用一门外语和另一人交流,需要翻译。这层"翻译"在AI芯片里就是编译器、工具链、算子库。芯片厂商把硬件抽象成一套指令集或运行时接口,上层框架(TensorFlow、PyTorch、ONNX Runtime)把模型表达成算子图,工具链再把这些算子图翻译成芯片能高效执行的底层指令。
所以,"AI芯片软硬件设计"的核心工作,是把算法结构映射到硬件拓扑,让卷积、矩阵乘这些高频算子踩在硬件计算单元的最优工作点上。这种映射不是天生的,而是一层层设计出来的。理解这一点,你就能明白为什么同算力不同芯片之间的实际性能可能差异巨大,也就能理解为什么必须在选型阶段把软件工具链的成熟度作为一个核心维度去评估。
1.3 从设计流程看,为什么架构优先于写代码
我在硬件设计这个方向上有一个很深的体会:在芯片架构还未定型、SoC整体规划还没完成的时候,决定指令集抽象层级和存储层次结构,是最关键、也最不可逆的决策。如果指令集抽象得太粗,上层灵活度不够,若干新算子出现时只能干瞪眼;如果抽象得太细,芯片设计复杂度加大,验证周期拉长,很可能错过窗口期。
AI芯片的软硬件协同设计,本质是"折中"的艺术。计算精度(INT8、FP16、BF16)、存储层次(SRAM容量、带宽)、数据流方式(权重固定、激活固定、输出固定)、可编程性(完全硬核化还是留可编程区域),这四个大问题必须在硬件设计阶段就做出取舍,软件栈则必须围绕这些取舍去适配。顺序反了,也就是先做软件再定硬件,在真实芯片项目中基本等于返工。
2. 硬件侧:AI芯片的基本盘是如何搭起来的
2.1 三大流派:GPU、ASIC、NPU,各自的算盘
目前市面上的AI芯片大致可以分为三大流派:通用GPU、ASIC(专用集成电路)、以及广泛意义上的NPU(神经网络处理器)。GPU的强项是通用性和极高的计算密度,适合训练和通用推理;ASIC是彻底为特定算法定制的,比如比特币矿机芯片、某些专用的语音识别芯片,极快但几乎不可编程;NPU则是介于两者之间,它针对神经网络算子做了硬件优化,但仍保留一定的可编程能力。
在我们边侧、嵌入式场景里,NPU是主流选择。原因很朴素:NPU的能效比通常远高于GPU,单位瓦特算力可以做得非常突出。但NPU的挑战也就在这里,它不像GPU那样什么算子都能跑得飞快,它有一个"算子支持列表",不在列表里的算子要么跑在CPU fallback,要么干脆不支持,这会带来性能断层和兼容性麻烦。
2.2 设计核心:乘累加阵列、片上存储与数据流
在NPU内部,你会看到一个高度规律的结构。最核心的是乘累加阵列,也就是MAC阵列。MAC(Multiply-Accumulate)操作是卷积和全连接层的根基,一乘一加算一次运算。芯片设计时,MAC阵列的规模直接决定了芯片的并行度。例如,一个16x16的MAC阵列,理论上每时钟周期可以执行256次乘加操作,若芯片主频1GHz,纸面算力就是256 GOPS(如果做INT8,再乘以2得到TOPS也行)。
光有MAC阵列还不够,必须有配套的片上存储和数据流设计。常见的数据流有三种:权重固定(Weight Stationary)、激活固定(Activation Stationary)、输出固定(Output Stationary)。实际芯片通常是混合数据流。比如某个算子连续做多次卷积,如果权重固定方案,权重从片外搬运一次之后就一直在片上复用,这能显著减少带宽压力。
数据流的选择为什么对性能影响这么大?因为MAC阵列一次要"吃"大量数据,而这些数据不可能全部来自片外DRAM,否则功耗和延迟都不可接受。片上SRAM的容量有限,比如常说几MB到几十MB,数据必须分块(tiling)搬运。软件工具链要做的事,就是决定这些数据块切多大、轮转什么顺序,才能尽量让计算和搬运重叠,减少等待。
2.3 存储层次:从片外DRAM到片上SRAM的"物流"
把存储层次理解成物流系统会清晰得多。片外DRAM(LPDDR4/5)是中央仓库,容量大但"取货"速度相对慢,延迟上百纳秒;片上SRAM是离计算单元最近的缓存,速度极快,几乎是单时钟周期访问,但容量小得可怜。MAC阵列像是一个加工流水线,它需要每分钟消耗大量原料(激活值、权重),如果物流调度不好,流水线就空转。
所以一张好的NPU设计,一定会花大量面积在SRAM和NoC(片上网络)上,目的就是把这套"物流系统"打通。实际部署中,你如果发现性能低于预期,第一个要查的就是"数据是不是在反复来回搬运"。很多时候,把算子融合一下,比如把卷积和后面的ReLU、BatchNorm合并成一个融合算子,让数据在片上多走几步而不是流回DRAM,性能能提升30%以上。
2.4 典型AI芯片的参数对比:怎么从纸面看出实战能力
随便找几款边缘端常见的芯片来看,道理就更直观了:
| 芯片 | 算力(INT8) | 片上内存 | 内存带宽 | 主要应用场景 |
|---|---|---|---|---|
| RK3588(NPU) | 6 TOPS | 未知,依赖内部架构 | LPDDR4/5,约17GB/s | 边缘盒子、NVR、智能座舱 |
| Jetson Orin Nano 8GB | 20 TOPS(稀疏) | 8GB LPDDR5 | 约 68GB/s | 机器人、多路视觉、边缘大模型推理 |
| 部分手机SoC NPU | 15-30 TOPS | 数MB SRAM | LPDDR5 / LPDDR5X | 端侧AIGC、影像算力 |
| 高端训练GPU(如H系列) | 数百TOPS(INT8) | 数十MB L2/SRAM | HBM带宽可达3TB/s以上 | 模型训练、大规模推理 |
你会发现,真正决定实战能力的,不在于峰值TOPS,而在于"内存带宽÷单次操作所需数据量"能够支撑多大算力。算力再高,带宽跟不上就是花瓶。这个"算术带宽"的概念,建议每个做AI部署的人都先算一下再选型。
3. 软件栈:让硬件从"能用"变成"好用"的关键一环
3.1 工具链到底做了什么:从模型到芯片指令的旅程
硬件就像一台复杂的乐器,能不能弹出好听的曲子,还得看软件——工具链。拿一个典型的部署流程来说,你训练好的PyTorch模型,先是转成ONNX格式,然后是模型解析、图优化、算子映射、内存规划、指令生成,最终变成NPU上跑的模型包或可执行程序。每一步都可能影响最终性能。
比如图优化阶段,工具链会把BatchNorm和前面的卷积二合一,这样推理时省掉一次完整的数据遍历;会把连续的小卷积核合并成大卷积;会把长尾的reshape、transpose尽量消除。另一个重点是算子映射。工具链会把模型里的Conv算子分解成MAC阵列能够执行的形式,比如用隐式GEMM把卷积转化成矩阵乘,加载到MAC阵列中运算。
3.2 算子映射:一个算子在不同芯片上的"变脸"
算子映射是软硬件设计中最好玩的部分。同一个卷积算子,在GPU上可能走cuDNN的某种隐式GEMM算法,在NPU上可能走直接卷积的滑动窗口模式,在CPU上则走im2col+GEMM。不同硬件拓扑决定了不同打法。
举个例子。在Jetson Orin Nano上,TensorRT会把普通卷积层融合掉,同时启用tactics自动调优去搜索针对该GPU的最佳kernel配置;在RK3588的RKNN工具链上,则会把模型转换成特制的.rknn格式,工具链内部按NPU的MAC阵列尺寸对卷积做分块,选择最合适的"数据在片上流动"方式。开发者在乎的"算子支持列表"在底层就是这张映射表,映射不了的,就会自动落到CPU去执行,性能自然摆烂。
3.3 量化:从FP32到INT8的"硬压缩"
量化是让模型跑在NPU上拿高算力的前提。FP32在通用GPU上没太大问题,但在NPU上不做INT8量化,基本等于自废武功,因为NPU的MAC阵列就是按INT8设计的。量化的核心是用整数运算模拟浮点运算,同时尽量少丢精度。
实际做法是校准(calibration)。你准备一批有代表性的输入数据,喂给模型,记录每一层激活值的分布范围(即min/max),然后据此算出量化比例系数(scale)和零点(zero point)。这步做得好不好,直接决定INT8模型是否掉点。
我常用的流程是:先做感知量化训练(QAT),或者先试着做训练后量化(PTQ)。QAT时间成本高,但精度通常更好;PTQ通常几分钟搞定,但会有掉点风险。实在不行,可以混合精度——关键层保留FP16,其余层走INT8,性能下降不多,精度能保住。
3.4 推理框架:TensorRT、RKNN、OpenVINO等,选型看什么
推理框架是工具链的一个上层封装。选推理框架时,我一般看三个维度。
第一是算子覆盖度。你的模型里用到的算子在不在支持列表里?有些框架对Transformer结构支持得极好,但对某些奇葩的自定义算子就很头疼。第二是性能上限。即便是同一框架,不同版本的优化差距可以很大,比如TensorRT 10对比8.5可以说性能翻了倍。第三是部署生态。是否便于做动态形状输入,是否支持多路并发,是否方便和业务代码集成,这些都比网上吹的那些benchmark数字重要得多。
4. 软硬件协同的关键细节:从架构到部署全链路打通
4.1 CPU、NPU、GPU协作:异构计算的真正意义
在实际场景中,不会只有NPU在干活。一个SoC里,通常CPU负责调度和通用逻辑,NPU负责神经网络加速,GPU负责图形、显示或一些并行计算。异构计算框架会把任务按性质拆开,分别扔给合适的处理器。
我在实际项目中遇到过分工不当导致性能大幅下降的案例。打个比方,某个功能的预处理用了大量OpenCV算子跑在CPU上,而NPU这边的模型推理太快,结果整个系统吞吐被CPU预处理卡住了。解决办法是尽可能把预处理也丢到GPU或者用NPU的算子替代,或者改成流水线作业,CPU预取下一帧,NPU推理当前帧,两者并行,吞吐翻倍。这个思路在嵌入式多路视频分析里非常常见。
4.2 从设计到部署:时钟、功耗、电源与散热层面的现实约束
软硬件协同设计可不只是软件层面的事。你做的算法跑在芯片上,芯片的功耗、发热、供电都是有预算的。如果你的模型要求满负载运行,那么散热设计能不能把温升压住?如果电源模块的纹波控制做不好,NPU供电不稳,推理性能会剧烈波动,甚至出现随机崩溃。
特别是在做便携式、车载类产品时,散热约束非常苛刻。我通常会用功耗分析工具去统计每路算子的能耗,尽量在精度不变的前提下降低模型计算量(FLOPs)和内存消耗,从而把平均功耗压下来,让芯片在一个健康的热范围内工作。
4.3 项目实战准备:一个边缘AI视觉检测项目的软硬件分工
把前面的理论落进水里面,我们来看一个实际的边缘AI视觉检测项目。假设我们要做一套流水线上的质量检测系统,识别缺陷零件,要求在15W功耗以内,单路1080p视频实时处理,延迟小于50ms。
硬件选型可以定为RK3588,因为它的NPU算力够用,而且CPU/GPU/NPU异构协同比较灵活,配套软件也比较成熟。软件层面分成几块:视频解码走硬件MIPI CSI接口和GPU,预处理走CPU并行优化,模型推理走NPU,后处理走CPU或者GPU并行,这样让每个处理器都做自己最擅长的事。
基于这样的分工,延迟和吞吐才能同时达标。反过来说,如果全都往NPU塞,遇到NPU不善长的算子就崩;如果全都放在CPU,推理帧率上不去,这个协同分配就是决定成败细节。
5. 实操篇:在RK3588上部署YOLOv5s并跑满NPU算力
5.1 环境准备与官方工具链配置
先交代一下基础环境:一台装有Ubuntu的PC用于模型转换,一块RK3588开发板,系统刷的是官方Ubuntu镜像。PC端安装RKNN-Toolkit2,板端要运行对应的RKNN-Toolkit-Lite运行时,或者通过Python和C接口跨平台调用。
安装时特别建议用conda单独建一个虚拟环境,Python版本按官方文档要求(通常是3.8到3.10之间)。我踩过最大的坑是依赖冲突,rknn-toolkit2依赖的onnx、numpy、opencv版本和本地其他项目不一致,导致模型转换过程莫名其妙报错。所以环境隔离一定要做,宁可多花十分钟,也不要和现有环境搅在一起。
5.2 从PyTorch模型到RKNN格式的转换流程
第一步把YOLOv5s导出为ONNX。官方YOLOv5仓库里有现成的export.py脚本,导出时要注意opset版本,RKNN工具链对ONNX Opset的支持范围有限,我一般用opset 12到17之间的版本,太高反而容易出问题。导出之后先用onnx-simplifier做一下简化,把部分冗余节点合并掉,这不仅让模型更小,也让后面转换更快、更少报错。
第二步用RKNN-Toolkit2做转换。初始化配置里把目标平台设成rk3588,开启量化。量化数据集建议选取50到200张有代表性的图像,尤其是包含你真正要检测的目标种类的样本。校准数据集如果和真实场景相差太远,量化后掉点会很严重。
转换日志里我特别关注哪些算子被放在了CPU上执行。理想状态下,YOLOv5s这类模型应该全模型都映射到NPU,如果看到某些算子走了CPU,要回到模型优化环节去处理,比如把自定义激活函数替换成标准SiLU,把部分上采样操作改成工具链支持的形式。
5.3 性能调优的四个实用手段
模型调优可以从四个方向入手。
第一是输入分辨率:YOLOv5s原本默认输入是640x640,如果检测目标不是很小,可以把输入降到512x512甚至480x480,推理延迟能明显下降。这个改动对mAP有损耗,但很多时候完全可以接受。
第二是batch size和多路并发:如果产品是多路视频流,不要只跑单路batch=1,应尝试NPU单次推理处理多张图,比如把四路画面拼成一个batch或者做tiling处理,利用率会大幅提高。
第三是算子融合与重写:把模型中的一些冗余处理放到预处理里完成,比如归一化(除以255),有些工具链支持通过配置把归一化融入模型首层,省一次全图数据扫描。
第四是CPU与NPU的流水线重叠:在代码层面采用多线程双缓冲──线程A在读取下一帧并做预处理,线程B在将当前帧交给NPU推理,这样预处理时间被推理时间覆盖掉,总吞吐自然上升。
5.4 我这个项目里最终跑出来的实际数据
以我手头这个项目为例,YOLOv5s通过上述优化后在RK3588上单路1080p视频流能达到约45FPS的推理速度,整体端到端延迟约35ms。CPU占用率约35%,NPU占用率约80%,这个状态算是把芯片刨得比较干净了。
有一说一,第一次转换时连20FPS都不到,主要原因是初始模型输入是640x640,算子也没有在转换前做精简,另外当时预处理一直放在CPU上没做流水线。经过三个方向的调整,最终帧率提升了一倍多。这个过程本身就很好地说明了:软硬件协同的收益,光靠堆硬件是拿不到的。
6. 调试笔记:常见问题与排查思路实录
6.1 性能不达标,先别怀疑芯片,顺序排查
我在调试时一般按这个顺序走。第一个查数据通路,看看是不是数据反复在片内片外搬运;第二个查算子映射,打开profiling工具看哪些算子耗时最长,是否存在CPU fallback;第三个查量化配置,是不是有些层被量化后计算量虽然小了,但引入了额外反量化操作;最后查调度,看看多路并发时是不是存在线程同步等待。
性能问题如果按这个顺序排查,基本能覆盖90%以上的情况。很多时候不是芯片不给力,而是工具的配置参数没吃透。比如RKNN的NPU和CPU之间的内存copy模式,选择零拷贝模式和普通模式性能差一大截。
6.2 量化掉点的几大元凶
量化掉点是边缘部署最折磨人的问题。常见原因有三个。第一个是校准数据集太单一,没有覆盖真实场景的分布,特别是光照变化和噪声等级。第二个是模型中存在动态范围特别大的层,比如某些检测头的输出,这种层激活值分布很宽,INT8量化容易爆掉,这种情况我会把这层单独保留为FP16或者做拆分。第三个是预处理差异,PyTorch训练时用了ImageNet均值和方差归一化,但部署时如果输入没有做同样的归一化,量化误差会非常大。这点听着简单,但实际错的人特别多。
6.3 一些值得记录的工具链细节
工具链的版本差异绝对比你想的大。RKNN-Toolkit2在1.5版本到2.0版本之间,算子支持和性能优化发生了天翻地覆的变化,你的模型在旧版本上转换不通过,换一个新版本可能直接顺利。TensorRT也是同理,8.x的小版本之间性能差异能有15%以上。所以确定了一个正式版本后,不要频繁升级,否则你的调优经验就全部作废了。
另外建议打开每个工具链自带的profiling工具,比如RKNN官方提供了耗时统计接口,TensorRT有Nsight Systems。这是让软硬件协同"可观测"的关键。没有性能剖析,一切优化都是盲人摸象。
6.4 速查表:边缘AI芯片部署常见问题一览
| 现象 | 大概率原因 | 快速排查方法 |
|---|---|---|
| 推理速度远低于预期 | 算子CPU fallback、数据搬运过多、未开启零拷贝 | 查看日志中CPU算子和profiling数据 |
| 模型转换失败 | 算子不兼容、opset版本不对 | 换opset版本、算子替换、查官方支持列表 |
| INT8精度掉得离谱 | 校准集不够、归一化没对齐、量化层选择不当 | 换校准集、混合精度、拆分动态范围过大的层 |
| 多路并发时偶发卡顿 | 线程同步等待、内存竞争 | 开启双缓冲、调整线程优先级、增加延迟容忍 |
| 芯片温度高、降频严重 | 模型计算量过大、散热不足 | 降分辨率、减少batch、关闭部分CPU核心 |
7. 延伸思考:大模型时代,AI芯片软硬件设计的新变化
7.1 Transformer结构带来的新挑战
过去AI芯片主要优化的对象是CNN,卷积算子占大头。但Transformer结构已经成为NLP、多模态乃至视觉模型的主流。Transformer的核心算子是矩阵乘法、Softmax、LayerNorm、自注意力,这些算子的内存访问模式和CNN完全不同。注意力矩阵的计算量不大,但中间的K、Q、V矩阵以及注意力矩阵都需要大块存储空间,这对NPU的片上存储和带宽提出了新的要求。
有些新一代NPU开始加入对矩阵乘法的特别优化、对Softmax的硬件支持,甚至加入类似Transformer引擎的模块,以实现在硬件层面直接支持注意力机制。这说明芯片架构必须跟着算法趋势走,软件栈也得跟着硬件更新。软硬件协同设计在这种快速迭代的前沿算法面前,节奏越来越快,压力也越来越大。
7.2 边缘端大模型推理:硬件压缩与软件裁剪的组合拳
现在还流行在边缘端跑大模型。说实话,单靠芯片算力硬跑LLM是不现实的,因为模型参数都太大了,动辄几十上百GB。边缘端做推理通常要叠加模型压缩,比如结构化剪枝、蒸馏、4bit量化。同时芯片本身也在变化,比如支持更宽的位宽、更高效的稀疏计算、更大的片外带宽。软硬件两条腿一起走,才能在功耗和成本可控的范围内把大模型塞进边缘设备。
我在测试Jetson Orin Nano跑量化后的7B模型时,最深的体会是:模型权重已经远远超出片上SRAM容量,每次token生成都要搬运大块权重到计算单元,整个系统的瓶颈就是权重搬运带宽。这也解释了为什么HBM、LPDDR5X这类高带宽存储对新一代AI芯片如此重要——算力堆得再高,喂不饱就是白搭。
7.3 对从业者的启示:四条腿走路的适应力
现阶段做AI芯片软硬件设计,需要同时盯住算法演进、芯片架构、编译器工具链、应用场景四个方向。算法变了,旧的硬件优化思路可能就失效了;编译器工具链跟不上,芯片的算力再强也发挥不出来;应用场景不同,对功耗、延迟、吞吐的取舍也完全不同。整体来看,软硬件协同的能力逐渐成为衡量AI工程师水平分水岭的一个标志:能不能在芯片约束下把模型的性能和精度平衡好,比单纯会训练模型要值钱得多。
8. 最后再分享一些个人积累的心得
总结的话不多说了,直接分享几个我在多个项目中反复验证过的经验。
第一件是选芯片前先画数据流图。不要只看TOPS,先把你的算法推理过程拆开,统计每一层的数据量,估一下带宽需求,算一下batch size和延迟目标,再拿着这些数据和芯片资料对照。这个工作花半天时间,换来的是省掉后面几个月的返工。
第二件是工具链版本必须锁定。AI工具链更新频繁,每一个大版本都可能有算子行为差异和性能变化。在一个项目里,一旦确认了某个版本稳定,就把它固化下来,尽量不在中途升级。文档和记录也要按版本保留,避免换一个人接手就不知道该看哪个版本的手册。
第三件是性能剖析永远先于优化。任何性能问题,先打开profiling拿到数据,再动手改。没有数据就凭感觉调参,大概率会陷入"调一个参数跑一次测试"的无底洞。做软硬件协同,说白了就是和数据打交道,和硬件资源打交道,每一步都要有度量。
第四件是保留一条纯CPU推理的途径作为保底。即使NPU部署再顺利,我也会在代码里保留一套CPU推理的逻辑,用于在异常情况下降级运行或者做精度对齐测试。NPU的量化误差说不清是算子本身还是工具链问题的时候,拿CPU的FP32结果一对比就清楚了。这个方法帮我避过很多次因为量化误差导致的疑难Bug。
AI芯片的软硬件设计,说到底是一门权衡的艺术。真正有价值的能力,不在于会用一个芯片、跑通一个模型,而在于能理解硬件为什么这样设计、软件为什么这样调度,然后在两者之间找到最优解。这篇博文写到这里,把我目前最想分享的内容基本都倒出来了,如果你也在做类似的事情,欢迎带着实际数据和问题来交流,我们可以把这个话题继续往深了聊。