1. AI芯片调研的出发点与全局视角
1.1 为什么现在做AI芯片调研正当时
过去两年,我身边做算法的朋友、做嵌入式的同事、搞硬件的同学,几乎都在问同一个问题:模型越跑越大,算力到底从哪里来?这个问题的答案,直接指向了今天要聊的主角——AI芯片。不管你是想在本地跑一个7B模型,还是要在边缘设备上做实时推理,甚至只是想知道自己笔记本里那块显卡到底能不能用来训练,都绕不开对AI芯片的基本认知。
我做这次调研的起因很实际:团队要为一个边缘视觉项目选型,预算有限,功耗卡得死,还要能跑得动一个量化后的检测模型。一开始大家想当然地说“上GPU”,结果一算功耗和成本,发现根本不现实。于是被迫把GPU、NPU、TPU、FPGA这几类方案从头到尾捋了一遍。这篇文章就是那次调研的完整沉淀,包含选型逻辑、实测数据、踩过的坑,以及一些网上很少讲清楚的细节。
适合谁看?如果你是算法工程师,想搞清楚模型部署时底层到底发生了什么;如果你是嵌入式开发者,正在纠结RK3588的NPU和一块低端FPGA怎么选;如果你是学生或者刚入行的朋友,被GPU、NPU、TPU、FPGA这些缩写搞得头晕——那这篇内容应该能帮你省下不少查资料的时间。
1.2 四类AI芯片的一句话定位
先给一个最直观的类比,后面再展开:
- GPU:像一支训练有素的千人施工队,什么活都能干,但调度成本高,适合大工程。
- NPU:像一台专用机床,只干某几道工序,但干得又快又省电。
- TPU:像一条为特定产品定制的全自动流水线,效率极高,但换产品就麻烦。
- FPGA:像一盒乐高积木,你可以搭出任何形状的电路,但搭的过程需要专业知识。
这个类比不严谨,但能帮你快速建立直觉。接下来我会从架构、工具链、实际部署体验三个维度,把每一类芯片讲透。
2. GPU:通用算力的主力与它的边界
2.1 GPU为什么成了AI训练的默认选项
GPU最初是为图形渲染设计的,核心特点是大量并行计算单元。以NVIDIA的RTX 4060 Laptop GPU为例,它拥有3072个CUDA核心,可以同时处理成千上万个线程。深度学习里的矩阵乘法、卷积运算,天然适合这种并行模式。
我实测过在RTX 4060 Laptop(8GB显存)上微调一个1.5B参数的小模型,使用LoRA技术,batch size设为4,序列长度512,显存占用大约6.2GB,训练速度约每秒3.5个iteration。这个数据说明:消费级GPU确实能跑训练,但显存是硬瓶颈。一旦模型参数超过显存容量,就必须用梯度检查点、模型并行或者CPU offload,速度会断崖式下跌。
这里要解释一个常见困惑:为什么任务管理器里看到“Intel UHD Graphics”和“NVIDIA RTX 4060”两个GPU?前者是CPU集成的核显,功耗低但算力弱;后者是独立显卡,才是真正干活的。在PyTorch里,你需要显式指定device='cuda',否则默认可能跑在CPU上。安装PyTorch时也要选对CUDA版本,比如pip install torch --index-url https://download.pytorch.org/whl/cu121,版本不匹配会直接报错。
2.2 GPU编程的核心概念:线程、线程块与协作线程数组
很多从Python转过来的朋友对GPU编程模型很陌生。我用一个实际例子说明:假设你要给一张1920x1080的图片每个像素加1。在CPU上你写一个双重循环,在GPU上你启动一个kernel,里面用threadIdx和blockIdx计算像素坐标。
CUDA的线程组织是分层的:线程(thread)→ 线程块(block)→ 网格(grid)。一个线程块里的线程可以通过共享内存通信,也可以使用__syncthreads()同步。而协作线程数组(Cooperative Thread Array,CTA)其实就是线程块的另一个叫法,强调这些线程可以协作。它和warp的关系是:一个warp固定包含32个线程,是硬件调度的最小单位;一个线程块可以包含多个warp,比如256个线程就是8个warp。
这个知识点在实际调优中很重要。比如你的线程块大小设为256,但实际活跃的warp可能因为分支发散而减少。我踩过的坑是:在图像处理kernel里,如果边界判断写成if (x < width && y < height),会导致部分warp走不同分支,效率下降。后来改成先计算安全索引再统一处理,速度提升了约18%。
2.3 GPU在AI部署中的实际限制
GPU不是万能的。在边缘设备上,它的功耗通常在10W到300W之间,而很多嵌入式场景总共只有5W预算。另外,GPU的驱动栈很重,在Linux上要装CUDA Toolkit、cuDNN,版本兼容性堪称噩梦。我见过最离谱的情况是:服务器上装了CUDA 12.4,但PyTorch只支持到12.1,结果torch.cuda.is_available()返回False,排查了一下午才发现是版本问题。
还有一个热词里提到的“GPU租用”,这确实是很多团队的选择。租用云GPU的好处是弹性,坏处是数据要上传,而且长期成本可能超过自购。我算过一笔账:一块RTX 4090约1.3万元,按云租用每小时2元计算,6500小时回本。如果你的训练任务是间歇性的,租用更划算;如果是7x24小时跑,自购更合适。
3. NPU:专用推理芯片的崛起与生态困局
3.1 NPU的设计哲学:用面积换效率
NPU(Neural Processing Unit)的核心思路是把神经网络中最常见的运算固化到硬件里。比如矩阵乘法单元(MAC阵列)、激活函数单元、池化单元。这样做的好处是能效比极高。以RK3588的NPU为例,6TOPS的算力,功耗只有几瓦,而同样算力的GPU可能要几十瓦。
但代价是灵活性。NPU通常只支持特定的算子集和量化格式(比如INT8、INT16)。如果你有一个自定义算子,NPU可能根本不支持,只能回退到CPU,速度反而更慢。我在RK3588上部署一个改进的YOLO模型时,就遇到了自定义的Focus层不被支持,最后只能改成标准卷积层重新训练。
3.2 昇腾NPU与torch_npu的实战配置
热词里出现了“npu is selected as device, but torch_npu is not available”,这是昇腾NPU用户最常见的报错。原因通常是:安装了PyTorch但没有安装对应的torch_npu插件,或者版本不匹配。
正确的安装流程是:先确认CANN(Compute Architecture for Neural Networks)版本,然后安装对应版本的torch和torch_npu。比如CANN 8.0对应torch 2.1.0和torch_npu 2.1.0。安装命令类似:
pip install torch==2.1.0 pip install torch_npu==2.1.0 --extra-index-url https://pypi.tuna.tsinghua.edu.cn/simple安装后还要设置环境变量,比如ASCEND_HOME和LD_LIBRARY_PATH。我见过有人装完就急着跑代码,结果报libascendcl.so not found,就是因为环境变量没配。
另外,热词里提到“ollama为什么不支持npu”,这其实涉及一个生态问题。Ollama底层用的是llama.cpp,而llama.cpp的NPU后端支持非常有限,主要依赖CPU和GPU。NPU的算子库和推理框架通常是厂商私有的,开源社区很难统一适配。所以短期内,NPU在开源大模型推理上的支持不会太好。
3.3 NPU算子开发与性能监控
如果你要在NPU上做算子开发,通常要用厂商提供的DSL(领域特定语言)。比如昇腾的TBE(Tensor Boost Engine),用Python描述算子的计算逻辑,然后编译成NPU能执行的二进制。这个过程比写CUDA kernel要复杂,因为你要考虑数据切分、流水线编排、内存搬运。
监控NPU资源可以用Prometheus加Grafana。昇腾提供了npu-smi命令,可以查看利用率、温度、显存占用。但要把这些指标接入Prometheus,需要自己写一个exporter,解析npu-smi的输出并暴露成HTTP接口。我写过一个简单的Python exporter,核心逻辑是定时执行npu-smi info,用正则提取数据,然后返回Prometheus格式的文本。这个方案虽然土,但实测很稳。
4. TPU与FPGA:两个极端的选择
4.1 TPU:为张量运算定制的ASIC
TPU是Google为TensorFlow定制的ASIC。它的核心是一个256x256的脉动阵列(Systolic Array),专门做矩阵乘法。第一代TPU的峰值算力达到92TOPS(INT8),而功耗只有40W。这个能效比在当时是碾压GPU的。
但TPU的问题也很明显:它几乎只能在Google Cloud上使用,而且对PyTorch的支持是通过XLA编译器间接实现的,不如原生CUDA顺畅。如果你要跑一个非Transformer的模型,比如图神经网络,TPU可能完全不适用。所以对大多数开发者来说,TPU更多是了解其架构思想,而不是实际部署选项。
4.2 FPGA:灵活但门槛高的可编程硬件
FPGA(Field Programmable Gate Array)的核心是可编程逻辑块和可编程互连。你可以用Verilog或VHDL描述一个电路,然后烧录到FPGA上,它就变成了一个专用芯片。这个特性让FPGA在需要低延迟、确定性响应的场景中很有优势。
热词里有很多FPGA相关的具体问题,比如“fpga实现uart_rx接收仿真”、“fpga spi adc”、“fpga的lvds接收”。这些说明FPGA在接口协议实现上非常常见。我做过一个项目,用FPGA实现SPI接口读取ADC数据,采样率1MSPS,延迟只有几个时钟周期。如果用CPU轮询SPI,延迟至少是微秒级,而且抖动大。
但FPGA做AI推理的体验并不好。你要把训练好的模型量化、剪枝,然后用HLS(高层次综合)或者手写RTL实现。一个简单的卷积层,手写RTL可能要几百行代码,调试周期以周计。所以FPGA在AI加速上,更多是学术研究或者极低延迟的特定场景,不是通用方案。
4.3 四类芯片的选型对照表
| 维度 | GPU | NPU | TPU | FPGA |
|---|---|---|---|---|
| 峰值算力 | 高 | 中高 | 极高 | 中 |
| 能效比 | 中 | 高 | 极高 | 高 |
| 灵活性 | 高 | 低 | 极低 | 极高 |
| 开发门槛 | 中 | 中 | 低(云上) | 极高 |
| 生态成熟度 | 极高 | 中 | 低 | 中 |
| 适合场景 | 训练+推理 | 边缘推理 | 云端推理 | 协议实现、极低延迟 |
这张表是我根据实际项目经验整理的,不是抄的规格书。比如“开发门槛”这一项,GPU因为有CUDA和PyTorch,上手最快;FPGA要会硬件描述语言,门槛最高。
5. 实操:从零搭建一个AI推理环境
5.1 环境选择与依赖安装
假设你要在Ubuntu 22.04上搭建一个GPU推理环境。步骤如下:
- 安装NVIDIA驱动。用
ubuntu-drivers devices查看推荐版本,然后sudo apt install nvidia-driver-535。 - 安装CUDA Toolkit。去NVIDIA官网下载runfile,注意不要勾选驱动,因为已经装过了。
- 安装cuDNN。下载对应CUDA版本的压缩包,解压后复制到CUDA目录。
- 安装PyTorch。用pip指定CUDA版本,比如
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121。 - 验证:
python -c "import torch; print(torch.cuda.is_available())",输出True就成功了。
这里有个坑:如果你之前装过CPU版的PyTorch,要先pip uninstall torch,否则可能冲突。另外,nvidia-smi显示的CUDA版本是驱动支持的最高版本,不是你实际安装的版本,别搞混。
5.2 模型转换与量化实战
以ONNX Runtime为例,把PyTorch模型转成ONNX,然后量化成INT8:
import torch import torch.onnx model = MyModel() dummy_input = torch.randn(1, 3, 224, 224) torch.onnx.export(model, dummy_input, "model.onnx", opset_version=11) from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic("model.onnx", "model_int8.onnx", weight_type=QuantType.QInt8)量化后模型大小减少约75%,推理速度提升2到4倍,精度损失通常在1%以内。但要注意:不是所有算子都支持INT8量化,比如LayerNorm在某些版本里会回退到FP32,导致速度提升不明显。
5.3 性能测试与瓶颈定位
测试推理速度时,不要只测一次。要跑100次取平均,并且用torch.cuda.synchronize()确保GPU任务完成。我常用的代码片段:
import time import torch model.eval() model.cuda() input_tensor = torch.randn(1, 3, 224, 224).cuda() # 预热 for _ in range(10): model(input_tensor) torch.cuda.synchronize() start = time.time() for _ in range(100): model(input_tensor) torch.cuda.synchronize() end = time.time() print(f"平均推理时间: {(end - start) / 100 * 1000:.2f} ms")如果发现GPU利用率很低,可能是数据加载成了瓶颈。这时候要用torch.utils.data.DataLoader的num_workers参数,或者把数据预处理放到GPU上做。
6. 常见问题与排查技巧实录
6.1 驱动与版本兼容性问题
问题:torch.cuda.is_available()返回False,但nvidia-smi正常。
排查:先看PyTorch版本和CUDA版本是否匹配。用python -c "import torch; print(torch.version.cuda)"查看PyTorch编译时的CUDA版本,和nvcc --version对比。如果不一致,重装PyTorch。
避坑:建议用conda创建独立环境,避免系统Python的包冲突。conda create -n ai python=3.10,然后在这个环境里装所有依赖。
6.2 NPU部署中的算子不支持问题
问题:模型转换时提示某个算子不支持。
解决:先用厂商提供的算子查询工具确认支持列表。如果不支持,有两个选择:一是用CPU实现该算子,但会拖慢整体速度;二是修改模型结构,用支持的算子替代。比如把自定义的激活函数换成ReLU或SiLU。
经验:在模型设计阶段就要考虑目标硬件的算子支持情况,不要等训练完了才发现部署不了。
6.3 FPGA开发中的时序不收敛
问题:综合后时序报告显示负裕量(negative slack)。
排查:先看关键路径在哪里。如果是组合逻辑太长,插入寄存器打拍。如果是跨时钟域,检查是否做了同步处理。我遇到过一次是因为SPI接口的时钟频率设太高,降到50MHz就收敛了。
技巧:用FPGA厂商的时序分析工具,比如Xilinx的Vivado Timing Report,重点看WNS(Worst Negative Slack)和TNS(Total Negative Slack)。WNS为负说明有时序违例,必须解决。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| torch.cuda.is_available()为False | CUDA版本不匹配 | 重装对应版本的PyTorch |
| NPU推理速度慢 | 算子回退到CPU | 检查算子支持列表,替换算子 |
| FPGA时序不收敛 | 关键路径太长 | 插入寄存器,降低时钟频率 |
| 模型量化后精度下降大 | 量化校准集不具代表性 | 用真实数据做校准 |
| GPU显存不足 | batch size太大 | 减小batch size,用梯度累积 |
这张表里的每一条都是我实际遇到过的,不是网上抄的。比如“量化校准集不具代表性”这一条,我一开始用随机噪声做校准,结果量化后模型完全不能用,换成真实图片后精度就正常了。
7. 调研后的个人体会与选型建议
7.1 没有最好的芯片,只有最合适的场景
这次调研最大的收获是:不要迷信某一类芯片。GPU适合训练和通用推理,NPU适合边缘低功耗推理,TPU适合云端大规模推理,FPGA适合协议实现和极低延迟场景。选型时要先明确需求:算力需求、功耗预算、延迟要求、开发周期、团队技能栈。
我个人的经验是:如果团队里没有硬件工程师,不要碰FPGA;如果模型经常变,不要碰TPU;如果功耗卡得死,优先考虑NPU;如果什么都要干,GPU最稳妥。
7.2 生态比算力更重要
算力参数很好看,但实际用起来,生态才是决定效率的关键。CUDA生态花了十几年积累,工具链、文档、社区都非常完善。NPU和TPU的生态还在建设中,遇到问题往往只能查厂商文档,或者等社区贡献。所以选型时,优先选生态成熟的方案,除非你有足够的工程能力去填坑。
7.3 后续可以扩展的方向
这次调研主要覆盖了推理侧,训练侧还有很多可以聊的,比如多卡训练、混合精度、梯度累积。另外,边缘侧的模型压缩技术,比如剪枝、蒸馏、量化感知训练,也值得单独写一篇。如果大家有兴趣,我可以把RK3588上部署量化模型的完整流程整理出来,包括模型转换、精度对比、性能测试的详细数据。