做AI推理硬件选型这几年,我背过的锅不算少。最常被问到的问题永远是“GPU还是NPU”,仿佛整个推理世界就只有这两个选项。但在一个边缘视频检测项目里,这两个答案都不合适:GPU太吃功耗和成本,NPU的算子支持又卡住了模型的脖子。折腾到最后,我们选了很少被第一考虑的那个答案——FPGA。别急着觉得我在炒冷饭或者偏门。这篇文章不是理论比较,是一个真实落地项目的完整复盘。我会说清楚为什么在这个项目里FPGA比GPU和NPU都合适,也会把定点量化、数据通路、任务流水线这些实操步骤一项项展开,最后把踩过的坑和排查思路也整理出来。如果你也在做边缘AI推理的选型,或者手里正好有一个对功耗、成本和灵活性都有要求的视觉项目,这篇内容应该能帮你省掉一小半试错时间。1. 项目缘起:为什么GPU和NPU都不够用1.1 核心场景:8路视频流的实时缺陷检测项目的背景其实很朴素:某生产现场的质检环节,要对8路工业相机视频流做实时检测,识别传送带上产品的外观缺陷。模型选的是经典的轻量目标检测网络,输入分辨率放在640x640,单帧推理目标延迟控制在30毫秒以内。这听起来不算难,GPU和NPU都能做到单帧30毫秒。但真正的约束在后面:整个检测单元要装进一个半封闭的机壳里,散热条件有限,整机功耗预算只有35瓦;同时因为产线会不定期换产品型号,检测模型里的网络结构、锚点参数、甚至输入分辨率都需要跟着改。也就是说,我需要一个“能变”的推理平台,而不是一个被固定好的黑盒。我们先评估了GPU方案。单看推理性能,一颗低功耗GPU确实能跑到8路并发、单帧30毫秒以内。但即便选低功耗型号,整板功耗也在80瓦以上,超出预算两倍多,散热风扇、供电模块都得跟着加,机壳尺寸直接失控。成本就更不用说了,一颗工业级GPU的单价、交期和后续产能波动,都是项目排期里的不稳定因素。NPU的情况刚好反过来。很多SoC里集成的NPU在功耗上非常漂亮,几瓦就能跑到几百TOPS的理论算力,而且专门的矩阵运算单元对卷积这类算子极其友好。但问题在于NPU的算子库是封闭的,它“擅长”什么是由编译器提前定好的。我们想部署的模型用了不少非标准算子,比如某个特殊的上采样方式,编译器不认,要么得重写网络结构,要么得手工拆成基础算子去蹭。试了两个型号之后发现,模型适配成本已经高到不如换个方案。1.2 选型困境的本质:既要灵活,又要功耗可控这个项目的需求本质,是一个“既要又要”的问题:既要有GPU那种“什么模型都能跑”的软件灵活性,又要有NPU那种能效比优势,还要在35瓦功耗墙内完成8路实时检测。当时我们内部开玩笑说,这是一个“三角需求”,传统硬件根本不可能同时满足。但也正是这个矛盾,让我开始认真审视从来不在第一梯队考虑的那个选项:FPGA。FPGA的硬件逻辑是可以用硬件描述语言或高层综合工具去“定制”的。换句话说,它没有一个被固定死的指令集架构,什么算子适合直接映射成硬件电路,我们就把它做成一个专门的数据通路。网络结构变了,重新编译烧写一下,硬件跟着变。这正好命中“灵活性”这个需求,而它的功耗模型又天然地贴近硬件本身,没有指令解码、缓存管理这些通用计算的开销,能效比和NPU同属一个量级。2. 第三个答案:FPGA在AI推理里的特殊位置2.1 FPGA为什么能做到软硬兼顾很多人对FPGA的刻板印象是“用来做接口逻辑和信号处理的”,跟AI推理八竿子打不着。但近几年的变化其实很大。FPGA厂商陆续推出了面向AI推理的工具链,支持把常见的深度学习算子(卷积、池化、归一化等)编译成硬件加速单元,同时保留了对非标准算子做自定义扩展的能力。这个“自定义扩展”恰恰是GPU和NPU都给不了的。拿我们遇到的特殊上采样算子来说。如果部署到NPU,需要考虑怎么用其他算子凑出同样的效果,这一步可能要好几天。但在FPGA上,我们直接把这个算子的数据流逻辑用高层语言实现,再映射成硬件模块,等同于给推理引擎单独加了一个专用电路。整个适配过程两天完成,而且硬件利用率非常高,没有多余的数据搬运。从原理上看,FPGA做推理时,每个算子对应一段物理电路,数据流过了这个电路就完成了计算。这没有指令取指、译码、乱序执行这些过程,所以延迟模型非常确定。对这个产线场景来说,确定性延迟和可控的功耗,比单纯刷高帧率重要得多。2.2 与GPU/NPU的对比:一张表说明白我在方案评审会上做了一张性能特征对照表,基本把三个方向的区别讲清楚了。这里直接放出来,可以当成选型参考:维度GPUNPUFPGA软件生态成熟度高,框架适配最快中,算子库决定上限中低,但工具链逐渐完善算子灵活性高,几乎什么都能跑低,非标算子很难搞高,可以自定义硬件算子能效比(同性能下功耗)一般优秀优秀确定性与实时性一般,受调度影响较好最好,延迟可控开发/适配成本低中中高,需要硬件思维生产成本(中等规模)高低中适用场景通用训练与批量推理稳定结构的端侧推理结构多变、功耗受限的嵌入式推理这张表里需要额外注意最后一行。FPGA的“适用场景”往往被低估,但恰恰是我们这次选型的关键依据:模型结构会变,功耗上限固定,部署环境又比较恶劣。这种情况下,FPGA反而是成本最优解,因为它一套硬件可以反复烧写适配不同模型,不需要像NPU那样选定型号后再遇到模型升级就只能换芯片。2.3 一个重要前提:先想清楚瓶颈在哪里选FPGA之前一定要想清楚一点:你的项目瓶颈到底是算力不够、功耗太高、还是模型适配成本太大?如果只是算力不够,GPU是最省心的答案;如果功耗敏感且模型结构非常稳定,NPU是更轻的选择;但如果模型结构会动,而且你对单帧延迟有硬性约束,FPGA才值得投入精力。我们之前有一个模拟项目X,就是因为没做这个前置判断,先上了NPU方案,结果模型迭代两版之后算子不在库里,前期的性能测试全部推倒重来。所以我在自己的团队里定了规矩:选型评估第一天,先列出未来一年内模型可能发生的变化点,再去比硬件参数。这个习惯在这次FPGA选型里帮了大忙。3. 实操过程:从算子分析到FPGA部署的完整链路3.1 第一步:模型裁剪与算子清单分析部署到FPGA的第一个动作不是写代码,而是把模型拆开看。我习惯用推理框架或者厂商工具先把网络结构导出来,生成一个算子清单和每个算子的数据流依赖关系,这是后面所有工作的基础。以我们用的目标检测模型为例,网络里主要有这几类算子:卷积(包括标准卷积和深度可分离卷积)、批归一化、激活函数(ReLU)、池化、上采样、以及最后的输出解码层(包括边界框回归和分类)。在FPGA上实现推理,并不是把整个网络当成一个黑盒去跑,而是把每个算子转成一个硬件加速模块,再把模块按数据流顺序串联起来。这一步的关键动作是层融合,也叫做算子融合。比如批归一化和激活函数,在数学上是可以合并到前面卷积层里的,合并之后不仅少了几次计算,也减少了中间结果写入DDR再读出来的时间。我在实际项目里做了一次层融合之后,整个网络的计算负载大概降了三成,这个收益不花一分钱,就是纯工程优化,我非常推荐先做这一步。另外要特别关注深度可分离卷积这类算子。它在通用GPU上不一定比普通卷积快多少,但在FPGA上如果能用硬件方式把深度卷积和逐点卷积拆成两条并行流水线,吞吐量提升会非常明显。我们最终在FPGA实现里把这条通路单独优化了一轮,效果比只做层融合又高了两成左右。3.2 第二步:INT8定点量化与精度校准GPU和NPU的部署链路里,量化通常是把浮点模型转成INT8或INT4格式,FPGA也一样。但FPGA上做量化有一个特点:你可以自主选择每个算子的定点位宽,而不是被迫用统一精度。这意味着我可以为模型里的“关键层”保留多一点比特位,而为不敏感的层削减位宽,最终在精度和资源占用之间找到更细的平衡点。这一部分是整个部署流程中最容易被低估的环节。很多人以为量化就是把浮点参数乘以一个缩放因子存起来,但实际操作时,一个很小的量化策略差异,就可能把mAP拉到不可用的水平。我们用的方法是先准备一个校准数据集,这个数据集不需要很大,几百张有代表性的图片就够了。把这些图片喂给浮点模型,统计每个激活值的分布范围,然后据此算出每一层的量化参数。重点是要看激活值里有没有特别大的离群点,如果有,直接用全局缩放会把大部分有效数值压得很低,精度损失会特别明显,必须针对离群的层做单独优化。初步量化之后的实测精度和浮点模型相比掉了约2个百分点,这在缺陷检测里不太能接受。为了解决这个问题,我们做了两件事:第一,把输出层之前的两个卷积层保留为更高精度定点格式;第二,对激活值分布特别不规律的层,量化时不用对称量化,改用非对称量化。这两项调整做完,精度差距缩小到0.5个百分点以内,缺陷漏检率回到了可接受范围,计算资源只多花了不到一成。这里补充一个非常实用的经验:不要只看总体的accuracy指标,一定要单独观察单类别的检测准确率。我们的缺陷类型里有一种细小划痕,它在量化前就偏低,量化后直接掉了一半。单独看总指标根本发现不了这个问题,只有按类别拆解之后才能精准定位。这个习惯后来也救过另一个项目的场,建议你们也加上。3.3 第三步:数据通路设计与DDR带宽估算模型层面的优化做完,接下来是硬件工程的核心:数据通路设计。FPGA上的算力其实经常不是瓶颈,反而数据搬运(也就是DDR带宽)才是最容易卡住吞吐量的地方。我们做了一个很简单的计算。假设输入视频流是8路,每路分辨率是1080p,经过网络前处理裁剪成640x640,从相机到FPGA侧的内存系统,每一帧原始数据量并不大,但推理过程中每个中间特征图都要写进DDR或者片上BRAM,这个搬运量才是大头。最开始的实现里,我们把上采样产生的中间特征图直接写到DDR,然后下一层再读回来。结果测量带宽后发现,整套流程的片外访存量非常高,推理延迟一度超过40毫秒,完全达不到目标。排查之后发现,上采样产生的数据量虽然不大,但写入和读出的突发长度太短,DDR控制器的效率被拉得很低,实际有效带宽只有理论值的一半左右。优化方法有两个方向。第一是尽可能把整个子网都放在片上RAM里完成,不碰DDR。我们把模型里的前几层网络做了内存子块划分,让一个帧的推理在一个连续的片上区域里完成,这一步直接减掉了大量的带宽消耗。第二是针对必须搬到DDR的数据,把多个小块的读写组合成一次大的突发传输,让DDR控制器的每笔访问都能跑满。两个方向的优化叠加之后,推理延迟从40毫秒降到了24毫秒左右,明显超过目标。3.4 第四步:N路并行与流水线设计单帧推理性能没问题之后,还要解决8路视频流的并发问题。最简单的方案是每一路分配一个完整的推理引擎,但这在FPGA上很不现实,逻辑资源很快就不够用了。我们用的方案是“时分复用加流水线拼接”。具体来说,8路视频流的数据按照固定的时间片轮转进入同一个推理引擎,但推理引擎内部的每一层(卷积、池化、上采样)都被设计成独立流水线阶段。当第1路数据在跑第3层的时候,第2路数据可以同时跑第2层,第1路的前一帧结果也正在做后处理。这样单个时钟周期内,硬件里的每一个阶段都在忙,不会出现“一整块资源等待另一块资源”的空闲状态。这种流水线设计在FPGA上实现有一定门槛,因为它要求每个算子模块都必须有独立的输入输出缓冲,而且各阶段的处理时间要大致匹配。如果一个卷积层要跑10微秒,上采样只要2微秒,上采样模块就会有大量空等时间。我们通过调整每一路输入的间隔(也就是流水线节拍),先把各阶段延迟测出来,再让调度器按最慢阶段做节拍对齐,总算把8路总吞吐跑到了目标水平,单帧延迟稳定在24到28毫秒之间。3.5 部署与上位机联调:别忽略CPU端的配合FPGA推理引擎本身做完了,整个系统还差最后一步:与CPU端控制程序的数据交互。我们在方案里没有让FPGA单独完成全部解析逻辑,而是把模型输出的中间结果通过底层接口直接搬回主控CPU,用常规的目标解码逻辑做边界框后处理。这个设计是因为FPGA更适合规整而重复的计算,而NMS这类动态逻辑在CPU上更成熟,也更方便后续调整筛选阈值。实际联调时遇到一个不大不小的坑:FPGA侧输出数据的排列原来是HWC格式,而CPU端推理框架默认的是CHW格式,两个格式不统一,导致前几次后处理结果全乱。最后我们在FPGA端的输出逻辑里增加了一个数据重排模块,格式对齐之后问题立刻消失。这个细节特别提醒大家注意,很多联调问题根本不在算法,而是数据结构约定没提前对齐。4. 常见问题与排查技巧实录4.1 问题一:量化后精度大幅下降,但找不到原因现象:FPGA部署完成后,同一张测试图,浮点模型的置信度是0.82,量化模型只有0.51,差距太大,无法上线。排查过程:第一步先确认是不是量化校准集选得不好,换了一个覆盖更多样本的校准集,置信度提升到0.61,仍然不够。第二步检查每个层的数据分布,发现网络倒数第二层的激活值存在一个异常大的离群点,远远偏离主体分布。全局缩放因子被这个离群点拉大,导致有效数值全被压缩。第三步采用混合精度策略,对这一层单独用更高位宽的定点表示,置信度恢复到了0.75左右。最终结论:量化精度问题,90%都是激活值分布没有“照顾”好。不要迷信全局校准,一定要敏感地处理离群点,该区别对待就区别对待。4.2 问题二:推理吞吐量上不去,但FPGA逻辑资源还有大量余量现象:单帧延迟已经达标,但8路并发时总吞吐只有5路水平,FPGA片上DSP和LUT的占用率却不高。排查过程:最开始怀疑是调度器的问题,但检查之后发现调度逻辑没有大问题。用硬件计数器分别统计卷积模块、上采样模块和DDR控制器的忙碌时间,发现上采样模块的空闲率非常高,数据总在等DDR返回。仔细看DDR访问模式,发现输入数据块太小,每次只读了一个很小的二维子块,无法跑满DDR突发长度。最后通过在模块输入端增加一个预取缓冲,把一个运算窗口所需的数据提前一次性搬入片上缓存,DDR访问的颗粒度变大了,带宽利用率显著上升。结论:在FPGA性能优化里,DDR带宽的利用率比算力更值得盯紧。只要DDR的突发访问没有跑到接近峰值,都是物理设计层面的浪费,不是资源不够。4.3 问题三:整个工程编译一次要两个小时,开发效率极低现象:版本迭代频繁,每次改一点点逻辑,重新走一遍综合和布局布线就要接近两个小时,多人协作时效率非常低。排查与处理:第一件事不是优化编译速度,而是把FPGA工程的构建流程做模块化拆分。我们按网络阶段分成多个IP核,每个模块独立编译,主工程只负责组装。修改某一模块时,只重新综合该模块,然后增量生成最终镜像,单次迭代时间从两小时缩短到半小时。另外把版本清单纳入代码化管理,每次编译前自动记录关键参数和版本号,防止多人协作时把旧配置烧到新的硬件上。结论:FPGA开发效率不能只看单次编译时间,而是要在工程结构上做好隔离,这个道理和大型软件工程里模块化设计完全一样。4.4 一些让人印象深刻的排错技巧这里的经验可能比较“土”,但真的有用:用FPGA片上的逻辑分析仪抓内部信号,不要只靠打印日志。有一次推理结果偶尔错误,跑CPU上根本看不出规律,后来在调试接口里嵌入了一个计数器和状态快照,抓到了因为某个使能信号时序差了一拍导致的偶发故障。对量化后的模型做一次“二次校准”。第一次量化后,在FPGA上跑采集到的真实视频,统计每一层的实际输出极值,用这些真实数据重新算量化参数,这么做可以把精度再挽回不少。在所有数据通路里加周期计数器,不同模块的开始与结束事件各打一个唯一标志,联调时可以快速定位是哪一段数据流阻塞了。这套打点技术排查起问题来非常高效,强烈推荐。5. 最终成果与选型之外的个人体会最终这套FPGA方案的成果可以量化成三组数字:整机功耗实测33瓦,在35瓦预算内;8路并发时单帧延迟平均26毫秒,满足30毫秒以内的目标;单位数量的检测精度和浮点模型相比,差距控制在了1个百分点以内。更重要的一点是,产线后来真的换了两次产品规格,模型结构跟着改了两版,我们只是重新做了量化和编译烧写,没有动任何硬件。我自己的体会是,做推理选型没有“永远正确”的答案,只有“当前场景下最合适”的答案。单纯的算力对比只是面试题里的答案,真实的工程决策永远要把功耗、成本、开发周期、模型演进风险全部放进来综合考量。如果你现在正在评估GPU和NPU,并且觉得两边都差口气,建议认真看一下FPGA这条路线,它不一定是最终答案,但大概率能拓展你对“硬件选型”这个问题的边界。最后分享一个小技巧:无论最后选了哪个方案,一定要在硬件定型之前先做一套“全流程模拟”测试,把模型量化后的计算数据、中间特征图的数值范围、DDR带宽的仿真曲线全都提前跑一遍。我在这个项目里最大的一次调整(从CHW改成HWC)就是靠这个模拟提前发现的,而不是等到硬件做好之后才面对返工的代价。这个习惯,比任何单点的优化技巧都更值钱。
做AI推理硬件选型这几年,我背过的锅不算少。最常被问到的问题永远是“GPU还是NPU”,仿佛整个推理世界就只有这两个选项。但在一个边缘视频检测项目里,这两个答案都不合适:GPU太吃功耗和成本,NPU的算子支持又卡住了模型的脖子。折腾到最后,我们选了很少被第一考虑的那个答案——FPGA。别急着觉得我在炒冷饭或者偏门。这篇文章不是理论比较,是一个真实落地项目的完整复盘。我会说清楚为什么在这个项目里FPGA比GPU和NPU都合适,也会把定点量化、数据通路、任务流水线这些实操步骤一项项展开,最后把踩过的坑和排查思路也整理出来。如果你也在做边缘AI推理的选型,或者手里正好有一个对功耗、成本和灵活性都有要求的视觉项目,这篇内容应该能帮你省掉一小半试错时间。1. 项目缘起:为什么GPU和NPU都不够用1.1 核心场景:8路视频流的实时缺陷检测项目的背景其实很朴素:某生产现场的质检环节,要对8路工业相机视频流做实时检测,识别传送带上产品的外观缺陷。模型选的是经典的轻量目标检测网络,输入分辨率放在640x640,单帧推理目标延迟控制在30毫秒以内。这听起来不算难,GPU和NPU都能做到单帧30毫秒。但真正的约束在后面:整个检测单元要装进一个半封闭的机壳里,散热条件有限,整机功耗预算只有35瓦;同时因为产线会不定期换产品型号,检测模型里的网络结构、锚点参数、甚至输入分辨率都需要跟着改。也就是说,我需要一个“能变”的推理平台,而不是一个被固定好的黑盒。我们先评估了GPU方案。单看推理性能,一颗低功耗GPU确实能跑到8路并发、单帧30毫秒以内。但即便选低功耗型号,整板功耗也在80瓦以上,超出预算两倍多,散热风扇、供电模块都得跟着加,机壳尺寸直接失控。成本就更不用说了,一颗工业级GPU的单价、交期和后续产能波动,都是项目排期里的不稳定因素。NPU的情况刚好反过来。很多SoC里集成的NPU在功耗上非常漂亮,几瓦就能跑到几百TOPS的理论算力,而且专门的矩阵运算单元对卷积这类算子极其友好。但问题在于NPU的算子库是封闭的,它“擅长”什么是由编译器提前定好的。我们想部署的模型用了不少非标准算子,比如某个特殊的上采样方式,编译器不认,要么得重写网络结构,要么得手工拆成基础算子去蹭。试了两个型号之后发现,模型适配成本已经高到不如换个方案。1.2 选型困境的本质:既要灵活,又要功耗可控这个项目的需求本质,是一个“既要又要”的问题:既要有GPU那种“什么模型都能跑”的软件灵活性,又要有NPU那种能效比优势,还要在35瓦功耗墙内完成8路实时检测。当时我们内部开玩笑说,这是一个“三角需求”,传统硬件根本不可能同时满足。但也正是这个矛盾,让我开始认真审视从来不在第一梯队考虑的那个选项:FPGA。FPGA的硬件逻辑是可以用硬件描述语言或高层综合工具去“定制”的。换句话说,它没有一个被固定死的指令集架构,什么算子适合直接映射成硬件电路,我们就把它做成一个专门的数据通路。网络结构变了,重新编译烧写一下,硬件跟着变。这正好命中“灵活性”这个需求,而它的功耗模型又天然地贴近硬件本身,没有指令解码、缓存管理这些通用计算的开销,能效比和NPU同属一个量级。2. 第三个答案:FPGA在AI推理里的特殊位置2.1 FPGA为什么能做到软硬兼顾很多人对FPGA的刻板印象是“用来做接口逻辑和信号处理的”,跟AI推理八竿子打不着。但近几年的变化其实很大。FPGA厂商陆续推出了面向AI推理的工具链,支持把常见的深度学习算子(卷积、池化、归一化等)编译成硬件加速单元,同时保留了对非标准算子做自定义扩展的能力。这个“自定义扩展”恰恰是GPU和NPU都给不了的。拿我们遇到的特殊上采样算子来说。如果部署到NPU,需要考虑怎么用其他算子凑出同样的效果,这一步可能要好几天。但在FPGA上,我们直接把这个算子的数据流逻辑用高层语言实现,再映射成硬件模块,等同于给推理引擎单独加了一个专用电路。整个适配过程两天完成,而且硬件利用率非常高,没有多余的数据搬运。从原理上看,FPGA做推理时,每个算子对应一段物理电路,数据流过了这个电路就完成了计算。这没有指令取指、译码、乱序执行这些过程,所以延迟模型非常确定。对这个产线场景来说,确定性延迟和可控的功耗,比单纯刷高帧率重要得多。2.2 与GPU/NPU的对比:一张表说明白我在方案评审会上做了一张性能特征对照表,基本把三个方向的区别讲清楚了。这里直接放出来,可以当成选型参考:维度GPUNPUFPGA软件生态成熟度高,框架适配最快中,算子库决定上限中低,但工具链逐渐完善算子灵活性高,几乎什么都能跑低,非标算子很难搞高,可以自定义硬件算子能效比(同性能下功耗)一般优秀优秀确定性与实时性一般,受调度影响较好最好,延迟可控开发/适配成本低中中高,需要硬件思维生产成本(中等规模)高低中适用场景通用训练与批量推理稳定结构的端侧推理结构多变、功耗受限的嵌入式推理这张表里需要额外注意最后一行。FPGA的“适用场景”往往被低估,但恰恰是我们这次选型的关键依据:模型结构会变,功耗上限固定,部署环境又比较恶劣。这种情况下,FPGA反而是成本最优解,因为它一套硬件可以反复烧写适配不同模型,不需要像NPU那样选定型号后再遇到模型升级就只能换芯片。2.3 一个重要前提:先想清楚瓶颈在哪里选FPGA之前一定要想清楚一点:你的项目瓶颈到底是算力不够、功耗太高、还是模型适配成本太大?如果只是算力不够,GPU是最省心的答案;如果功耗敏感且模型结构非常稳定,NPU是更轻的选择;但如果模型结构会动,而且你对单帧延迟有硬性约束,FPGA才值得投入精力。我们之前有一个模拟项目X,就是因为没做这个前置判断,先上了NPU方案,结果模型迭代两版之后算子不在库里,前期的性能测试全部推倒重来。所以我在自己的团队里定了规矩:选型评估第一天,先列出未来一年内模型可能发生的变化点,再去比硬件参数。这个习惯在这次FPGA选型里帮了大忙。3. 实操过程:从算子分析到FPGA部署的完整链路3.1 第一步:模型裁剪与算子清单分析部署到FPGA的第一个动作不是写代码,而是把模型拆开看。我习惯用推理框架或者厂商工具先把网络结构导出来,生成一个算子清单和每个算子的数据流依赖关系,这是后面所有工作的基础。以我们用的目标检测模型为例,网络里主要有这几类算子:卷积(包括标准卷积和深度可分离卷积)、批归一化、激活函数(ReLU)、池化、上采样、以及最后的输出解码层(包括边界框回归和分类)。在FPGA上实现推理,并不是把整个网络当成一个黑盒去跑,而是把每个算子转成一个硬件加速模块,再把模块按数据流顺序串联起来。这一步的关键动作是层融合,也叫做算子融合。比如批归一化和激活函数,在数学上是可以合并到前面卷积层里的,合并之后不仅少了几次计算,也减少了中间结果写入DDR再读出来的时间。我在实际项目里做了一次层融合之后,整个网络的计算负载大概降了三成,这个收益不花一分钱,就是纯工程优化,我非常推荐先做这一步。另外要特别关注深度可分离卷积这类算子。它在通用GPU上不一定比普通卷积快多少,但在FPGA上如果能用硬件方式把深度卷积和逐点卷积拆成两条并行流水线,吞吐量提升会非常明显。我们最终在FPGA实现里把这条通路单独优化了一轮,效果比只做层融合又高了两成左右。3.2 第二步:INT8定点量化与精度校准GPU和NPU的部署链路里,量化通常是把浮点模型转成INT8或INT4格式,FPGA也一样。但FPGA上做量化有一个特点:你可以自主选择每个算子的定点位宽,而不是被迫用统一精度。这意味着我可以为模型里的“关键层”保留多一点比特位,而为不敏感的层削减位宽,最终在精度和资源占用之间找到更细的平衡点。这一部分是整个部署流程中最容易被低估的环节。很多人以为量化就是把浮点参数乘以一个缩放因子存起来,但实际操作时,一个很小的量化策略差异,就可能把mAP拉到不可用的水平。我们用的方法是先准备一个校准数据集,这个数据集不需要很大,几百张有代表性的图片就够了。把这些图片喂给浮点模型,统计每个激活值的分布范围,然后据此算出每一层的量化参数。重点是要看激活值里有没有特别大的离群点,如果有,直接用全局缩放会把大部分有效数值压得很低,精度损失会特别明显,必须针对离群的层做单独优化。初步量化之后的实测精度和浮点模型相比掉了约2个百分点,这在缺陷检测里不太能接受。为了解决这个问题,我们做了两件事:第一,把输出层之前的两个卷积层保留为更高精度定点格式;第二,对激活值分布特别不规律的层,量化时不用对称量化,改用非对称量化。这两项调整做完,精度差距缩小到0.5个百分点以内,缺陷漏检率回到了可接受范围,计算资源只多花了不到一成。这里补充一个非常实用的经验:不要只看总体的accuracy指标,一定要单独观察单类别的检测准确率。我们的缺陷类型里有一种细小划痕,它在量化前就偏低,量化后直接掉了一半。单独看总指标根本发现不了这个问题,只有按类别拆解之后才能精准定位。这个习惯后来也救过另一个项目的场,建议你们也加上。3.3 第三步:数据通路设计与DDR带宽估算模型层面的优化做完,接下来是硬件工程的核心:数据通路设计。FPGA上的算力其实经常不是瓶颈,反而数据搬运(也就是DDR带宽)才是最容易卡住吞吐量的地方。我们做了一个很简单的计算。假设输入视频流是8路,每路分辨率是1080p,经过网络前处理裁剪成640x640,从相机到FPGA侧的内存系统,每一帧原始数据量并不大,但推理过程中每个中间特征图都要写进DDR或者片上BRAM,这个搬运量才是大头。最开始的实现里,我们把上采样产生的中间特征图直接写到DDR,然后下一层再读回来。结果测量带宽后发现,整套流程的片外访存量非常高,推理延迟一度超过40毫秒,完全达不到目标。排查之后发现,上采样产生的数据量虽然不大,但写入和读出的突发长度太短,DDR控制器的效率被拉得很低,实际有效带宽只有理论值的一半左右。优化方法有两个方向。第一是尽可能把整个子网都放在片上RAM里完成,不碰DDR。我们把模型里的前几层网络做了内存子块划分,让一个帧的推理在一个连续的片上区域里完成,这一步直接减掉了大量的带宽消耗。第二是针对必须搬到DDR的数据,把多个小块的读写组合成一次大的突发传输,让DDR控制器的每笔访问都能跑满。两个方向的优化叠加之后,推理延迟从40毫秒降到了24毫秒左右,明显超过目标。3.4 第四步:N路并行与流水线设计单帧推理性能没问题之后,还要解决8路视频流的并发问题。最简单的方案是每一路分配一个完整的推理引擎,但这在FPGA上很不现实,逻辑资源很快就不够用了。我们用的方案是“时分复用加流水线拼接”。具体来说,8路视频流的数据按照固定的时间片轮转进入同一个推理引擎,但推理引擎内部的每一层(卷积、池化、上采样)都被设计成独立流水线阶段。当第1路数据在跑第3层的时候,第2路数据可以同时跑第2层,第1路的前一帧结果也正在做后处理。这样单个时钟周期内,硬件里的每一个阶段都在忙,不会出现“一整块资源等待另一块资源”的空闲状态。这种流水线设计在FPGA上实现有一定门槛,因为它要求每个算子模块都必须有独立的输入输出缓冲,而且各阶段的处理时间要大致匹配。如果一个卷积层要跑10微秒,上采样只要2微秒,上采样模块就会有大量空等时间。我们通过调整每一路输入的间隔(也就是流水线节拍),先把各阶段延迟测出来,再让调度器按最慢阶段做节拍对齐,总算把8路总吞吐跑到了目标水平,单帧延迟稳定在24到28毫秒之间。3.5 部署与上位机联调:别忽略CPU端的配合FPGA推理引擎本身做完了,整个系统还差最后一步:与CPU端控制程序的数据交互。我们在方案里没有让FPGA单独完成全部解析逻辑,而是把模型输出的中间结果通过底层接口直接搬回主控CPU,用常规的目标解码逻辑做边界框后处理。这个设计是因为FPGA更适合规整而重复的计算,而NMS这类动态逻辑在CPU上更成熟,也更方便后续调整筛选阈值。实际联调时遇到一个不大不小的坑:FPGA侧输出数据的排列原来是HWC格式,而CPU端推理框架默认的是CHW格式,两个格式不统一,导致前几次后处理结果全乱。最后我们在FPGA端的输出逻辑里增加了一个数据重排模块,格式对齐之后问题立刻消失。这个细节特别提醒大家注意,很多联调问题根本不在算法,而是数据结构约定没提前对齐。4. 常见问题与排查技巧实录4.1 问题一:量化后精度大幅下降,但找不到原因现象:FPGA部署完成后,同一张测试图,浮点模型的置信度是0.82,量化模型只有0.51,差距太大,无法上线。排查过程:第一步先确认是不是量化校准集选得不好,换了一个覆盖更多样本的校准集,置信度提升到0.61,仍然不够。第二步检查每个层的数据分布,发现网络倒数第二层的激活值存在一个异常大的离群点,远远偏离主体分布。全局缩放因子被这个离群点拉大,导致有效数值全被压缩。第三步采用混合精度策略,对这一层单独用更高位宽的定点表示,置信度恢复到了0.75左右。最终结论:量化精度问题,90%都是激活值分布没有“照顾”好。不要迷信全局校准,一定要敏感地处理离群点,该区别对待就区别对待。4.2 问题二:推理吞吐量上不去,但FPGA逻辑资源还有大量余量现象:单帧延迟已经达标,但8路并发时总吞吐只有5路水平,FPGA片上DSP和LUT的占用率却不高。排查过程:最开始怀疑是调度器的问题,但检查之后发现调度逻辑没有大问题。用硬件计数器分别统计卷积模块、上采样模块和DDR控制器的忙碌时间,发现上采样模块的空闲率非常高,数据总在等DDR返回。仔细看DDR访问模式,发现输入数据块太小,每次只读了一个很小的二维子块,无法跑满DDR突发长度。最后通过在模块输入端增加一个预取缓冲,把一个运算窗口所需的数据提前一次性搬入片上缓存,DDR访问的颗粒度变大了,带宽利用率显著上升。结论:在FPGA性能优化里,DDR带宽的利用率比算力更值得盯紧。只要DDR的突发访问没有跑到接近峰值,都是物理设计层面的浪费,不是资源不够。4.3 问题三:整个工程编译一次要两个小时,开发效率极低现象:版本迭代频繁,每次改一点点逻辑,重新走一遍综合和布局布线就要接近两个小时,多人协作时效率非常低。排查与处理:第一件事不是优化编译速度,而是把FPGA工程的构建流程做模块化拆分。我们按网络阶段分成多个IP核,每个模块独立编译,主工程只负责组装。修改某一模块时,只重新综合该模块,然后增量生成最终镜像,单次迭代时间从两小时缩短到半小时。另外把版本清单纳入代码化管理,每次编译前自动记录关键参数和版本号,防止多人协作时把旧配置烧到新的硬件上。结论:FPGA开发效率不能只看单次编译时间,而是要在工程结构上做好隔离,这个道理和大型软件工程里模块化设计完全一样。4.4 一些让人印象深刻的排错技巧这里的经验可能比较“土”,但真的有用:用FPGA片上的逻辑分析仪抓内部信号,不要只靠打印日志。有一次推理结果偶尔错误,跑CPU上根本看不出规律,后来在调试接口里嵌入了一个计数器和状态快照,抓到了因为某个使能信号时序差了一拍导致的偶发故障。对量化后的模型做一次“二次校准”。第一次量化后,在FPGA上跑采集到的真实视频,统计每一层的实际输出极值,用这些真实数据重新算量化参数,这么做可以把精度再挽回不少。在所有数据通路里加周期计数器,不同模块的开始与结束事件各打一个唯一标志,联调时可以快速定位是哪一段数据流阻塞了。这套打点技术排查起问题来非常高效,强烈推荐。5. 最终成果与选型之外的个人体会最终这套FPGA方案的成果可以量化成三组数字:整机功耗实测33瓦,在35瓦预算内;8路并发时单帧延迟平均26毫秒,满足30毫秒以内的目标;单位数量的检测精度和浮点模型相比,差距控制在了1个百分点以内。更重要的一点是,产线后来真的换了两次产品规格,模型结构跟着改了两版,我们只是重新做了量化和编译烧写,没有动任何硬件。我自己的体会是,做推理选型没有“永远正确”的答案,只有“当前场景下最合适”的答案。单纯的算力对比只是面试题里的答案,真实的工程决策永远要把功耗、成本、开发周期、模型演进风险全部放进来综合考量。如果你现在正在评估GPU和NPU,并且觉得两边都差口气,建议认真看一下FPGA这条路线,它不一定是最终答案,但大概率能拓展你对“硬件选型”这个问题的边界。最后分享一个小技巧:无论最后选了哪个方案,一定要在硬件定型之前先做一套“全流程模拟”测试,把模型量化后的计算数据、中间特征图的数值范围、DDR带宽的仿真曲线全都提前跑一遍。我在这个项目里最大的一次调整(从CHW改成HWC)就是靠这个模拟提前发现的,而不是等到硬件做好之后才面对返工的代价。这个习惯,比任何单点的优化技巧都更值钱。