1. 项目概述与方案选型
1.1 这项目到底在做什么
飞腾腾锐D3000,这是飞腾新一代的桌面级处理器,采用ARM架构,主频、核心数、内存通道这些指标相比前代都有了明显提升。过去这两年,我在国产CPU平台上折腾过不少东西,从简单的Web服务到数据库、中间件都跑过,但真正把大模型推理跑起来,这还是第一次比较完整的实践。
文心大模型是百度自研的生成式大语言模型,常见的有ERNIE系列,包含不同参数规模的版本。普通的做法是直接调用云端API,但现在很多场景要求数据不出内网、完全离线,就必须自己部署一套推理环境。飞腾D3000所在的这种国产化硬件平台,恰恰是这类需求高发的地方——政务、金融、能源这些行业,采购的终端或者服务器就是飞腾、鲲鹏、龙芯这一类的CPU,上面要跑AI应用,正好需要这套流程。
这个项目做的事情,就是在一台基于飞腾腾锐D3000的机器上,从零开始搭建文心大模型的推理服务。不是调用云端接口,而是把模型权重下载到本地,通过推理框架加载起来,对外提供接口,让应用可以调用。这篇分享面向的是自己正在折腾或者计划折腾国产平台大模型部署的工程师,尤其是搞信创适配、内网AI应用落地的朋友,能少踩不少坑。
1.2 方案选型:为什么不是GPU,也不直接调用API
先说一个很多新手搞不清楚的认知问题。大家习惯了NVIDIA GPU上跑大模型,到了飞腾D3000这种纯CPU、还未必有独立显卡的平台上,第一反应往往是"这能跑吗"。实际上能跑,只是速度和体量上有天花板。
飞腾D3000没有CUDA生态,你装不了CUDA、cuDNN,也用不了PyTorch的GPU后端。但这个平台上可以用的方案并不少:
| 方案 | 原理 | 适合场景 | 我的评价 |
|---|---|---|---|
| PaddlePaddle + Paddle Inference | 百度自家框架,对文心系列原生支持最好 | 飞腾/鲲鹏等ARM平台、信创环境 | 首选,踩坑最少 |
| ONNX Runtime + DNNL/OpenBLAS | 把模型导出为ONNX,CPU推理 | 需要跨框架迁移的情况 | 可行,但转换麻烦 |
| llama.cpp / Ollama | 量化GGUF模型,CPU友好 | 资源受限、追求轻量 | 需要模型本身有GGUF版 |
| 云端API | 不部署,直接调用 | 无数据合规要求的场景 | 和"部署"无关,不在讨论范围 |
没有选择云端API,原因很直白:很多内网环境根本出不了网,数据也不能出域。要解决的就是"模型必须在本地跑"这件事。没有选择llama.cpp作为主方案,是因为文心系列在飞腾平台的社区优化主要是基于Paddle生态展开的,用官方框架配合官方推理引擎,兼容性和性能都更有保障。llama.cpp后续可以作为性能对比的备选方案去尝试,但主推理链路我建议固定在Paddle上。
还有一个容易忽略的选型细节是操作系统。飞腾D3000的开发板自带固件通常对银河麒麟V10适配最好,UOS也可以,但需要额外装内核头文件。我这次用的是银河麒麟V10 SP1,内核版本5.10以上,ARM64架构。如果你手上是其他发行版,比如Ubuntu或者Debian,只要内核支持ARMv8指令集,流程基本一致,差别只在软件包的安装命令上。
1.3 硬件配置与性能预期管理
先管理好预期:CPU推理大模型,速度不会像GPU那么快。飞腾D3000即使多核并发,跑一个7B参数量的量化模型,单次生成速度通常也就是每秒几个token到十几个token的量级,约等于人阅读速度的三分之一到一半。但这不代表没有实用价值,很多场景比如文档分类、关键词抽取、结构化信息整理,对响应速度的敏感度没那么高,几百毫秒到几秒的等待完全可以接受。
我的硬件配置供参考:
- 处理器:飞腾腾锐D3000(8核心,ARMv8.2+指令集)
- 内存:64GB DDR4 ECC(大模型推理对内存容量非常敏感,建议至少32GB起步)
- 硬盘:512GB NVMe SSD(模型文件体积大,IO快慢影响加载速度)
- 操作系统:银河麒麟V10 SP1(ARM64)
这里要强调内存的算法。以7B参数量的模型为例,FP16精度下权重文件约14GB,推理过程中还需要KV Cache和激活值的内存开销,通常需要模型权重体积的1.5倍才能跑得舒服。也就是说14GB权重的模型,16GB内存的机器就基本到极限了。如果想跑更大的模型,要么上64GB内存,要么对模型做量化压缩到INT8甚至INT4。我自己测下来的经验是,同样一个7B模型,INT8量化后权重约7GB,内存占用大幅下降,速度还有小幅提升,效果损失基本可控。
2. 飞腾ARM平台部署文心大模型的关键前置准备
2.1 操作系统基础环境检查
拿到机器后,不要急着装东西,先把底子摸清楚。我习惯按下面这个顺序做一轮检查,每一个命令都有它要回答的具体问题。
# 确认CPU架构 uname -m # 输出应该是 aarch64 或 arm64 # 确认内核版本,最好5.10以上 uname -r # 确认操作系统发行版 cat /etc/os-release # 检查内存和交换分区 free -h如果uname -m输出的不是aarch64,那基本可以断定你拿到的不是ARM版系统镜像,后面装的软件包会各种不兼容。内核版本太低的话,部分向量指令集特性无法启用,Paddle的Kernel优化模块可能直接拒绝加载。
内存检查这里有个实操技巧。第一次部署时我不小心在只有16GB内存的机器上试图加载7B模型,结果加载到一半进程就被OOM Killer杀掉了,日志里连个报错都没留下,只有Killed三个字母。所以强烈建议部署前先确认内存充足,或者预先把交换分区加大。虽然交换分区对推理性能没有帮助,但至少能避免进程在加载阶段被直接干掉。
2.2 ARM架构下的Python环境搭建
飞腾平台上的Python环境有它的特殊性。不要直接用系统自带的Python 3.6或者3.7,太老,部分新版依赖库已经不再支持。也不要盲目下载x86的安装包,架构不对装不上。我测试下来Python 3.10是比较稳妥的选择。
# 安装编译工具链,后面有依赖包需要现场编译 sudo apt update sudo apt install -y gcc g++ gfortran make cmake # 安装Python 3.10(如果系统默认版本不是3.10) sudo apt install -y python3.10 python3.10-dev python3.10-venv # 创建虚拟环境,避免污染系统Python python3.10 -m venv wenzhang_env source wenzhang_env/bin/activate # 升级pip和基础工具 pip install --upgrade pip setuptools wheel虚拟环境这一步强烈建议不要跳过。部署大模型过程中会装大量依赖包,不同项目之间的版本冲突非常常见。我见过有同事图省事直接装在系统Python里,后面跑另一个项目时发现numpy版本冲突,系统里一堆服务起不来,苦不堪言。
2.3 飞腾平台PaddlePaddle安装技巧
文心大模型在飞腾上的部署目前最顺滑的路径是PaddlePaddle框架。但PaddlePaddle的官方安装命令默认是基于x86架构写的,直接跑会下载到不匹配的安装包。必须在安装时通过--platform参数指定ARM64版本。
# 验证当前Python平台信息 python -c "import platform; print(platform.platform())" # 确认显示的是 aarch64 / Linux # 在虚拟环境中安装ARM64版PaddlePaddle pip install paddlepaddle==2.6.1 --platform manylinux2014_aarch64 --only-binary=:all: --no-deps为什么用--no-deps?因为ARM架构下部分依赖包如果让pip自动解析,它会尝试去下载x86的编译版本,直接安装失败。正确的做法是手动安装依赖,然后再装Paddle本体。
# 先手动安装numpy、protobuf这些核心依赖 pip install numpy==1.24.4 protobuf==3.20.3 # 再安装paddlepaddle,此时不解析依赖 pip install paddlepaddle==2.6.1 --platform manylinux2014_aarch64 --only-binary=:all: --no-deps # 安装完成后验证 python -c "import paddle; paddle.utils.run_check()"补充:常见的安装依赖包列表
| 依赖包 | 版本建议 | 说明 |
|---|---|---|
| numpy | 1.24.4 | 太新版本的numpy在部分ARM平台上会触发GLIBC不兼容 |
| protobuf | 3.20.3 | 版本过高会与paddle内部通信模块冲突 |
| onnxruntime | 1.16.3 | 如果走ONNX路线,这个版本对ARM支持较完善 |
| openblas | 0.3.21+ | 需要从源码编译,Paddle的CPU算子底层依赖 |
| libstdc++ | 随gcc 9以上,系统自带 | 老版本gcc会导致加载so库时符号找不到 |
注意:PaddlePaddle在飞腾平台上的安装包体积约150MB左右,下载速度取决于网络环境。建议提前准备好离线安装包,或者在网络状况好的时段操作。我在内网环境下用了本地代理仓库才解决下载问题。
2.4 模型权重文件获取与格式确认
文心大模型的权重文件可以从百度开源的渠道获取。当前比较常见的开源版本是ERNIE系列,参数规模有不同的档位。部署前一定要确认清楚拿到的权重格式:
- Paddle格式:通常是一个
model.pdmodel文件和若干个model.pdiparams文件,直接用Paddle Inference加载。 - HuggingFace格式:包含
config.json、pytorch_model.bin等文件,需要先做转换,再给Paddle加载。 - GGUF格式:供llama.cpp/Ollama使用,CPU推理友好,但文心系模型的GGUF转换工作要看社区是否已经有人完成。
# 假设已下载Paddle格式权重到models/目录,目录结构如下 models/ ├── ernie_3.0_tiny_chinese/ │ ├── model.pdmodel │ ├── model.pdiparams │ └── tokenizer.json如果拿到的是PyTorch权重,需要先用PaddlePaddle的模型转换工具转成Paddle格式。这个过程不难,但有几个细节容易出错,比如embedding层和layernorm层的参数名称映射、position_ids的形状差异。我当时的做法是参考官方转换脚本,把一个中文文本分类模型从HuggingFace格式转成了Paddle格式,在飞腾上跑通后,才决定把对话生成模型也走同样的路线。
经验:在国产平台上做模型部署,格式转换往往是最大的时间黑洞。强烈建议优先找官方提供的Paddle格式权重,省掉这一步。如果非要转换,就用Docker启动一个x86机器来做转换再拷贝到飞腾上,比在ARM上现场转换快得多。
3. 手把手实操:在飞腾D3000上把文心大模型跑起来
3.1 部署管理服务与推理API搭建
如果你之前了解过Dify、Ollama这类部署管理工具,会发现它们简化了模型服务的启动过程。在飞腾平台上,也有类似思路:用一个轻量级的API服务把模型包起来,调用方不关心底层推理细节,只需要发HTTP请求即可。
这里我用Paddle Serving作为推理服务层,它是Paddle生态自带的模型服务化组件,支持RESTful API,也可以直接和Paddle Inference配套使用。在飞腾上安装:
pip install paddle-serving-server==0.9.0 --platform manylinux2014_aarch64 --only-binary=:all: --no-deps安装完成后,写一个简单的服务端启动脚本:
# serve_ernie.py from paddle_serving_server import ServingServer from paddle_serving_client import Client # 配置模型路径和推理参数 model_config = { "model_path": "models/ernie_3.0_tiny_chinese", "port": 8080, "device": "cpu", "max_body_size": 64 * 1024 * 1024 } # 启动服务 server = ServingServer() server.set_model_config(model_config) server.start()# 启动服务 python serve_ernie.py启动日志里如果看到Server started successfully就说明服务已经就绪。
3.2 模型加载与推理验证
服务起来后,先用一个最小化的测试脚本验证模型能不能正常推理:
# test_infer.py import json import urllib.request url = "http://127.0.0.1:8080/ernie/predict" payload = { "text": "飞腾D3000是一款什么架构的处理器?", "max_length": 128, "temperature": 0.7 } req = urllib.request.Request( url, data=json.dumps(payload).encode("utf-8"), headers={"Content-Type": "application/json"} ) with urllib.request.urlopen(req) as resp: result = json.loads(resp.read().decode("utf-8")) print(result["result"])正常情况下的输出是一段中文文本。如果报错,通常集中在两个地方:
- 模型路径错误:报
Model not found,检查路径是否写对、权限是否可读。 - Kernel不匹配:报
No operator found或Operator ... is not implemented,说明PaddlePaddle的ARM算子库没有涵盖这个模型中的某些算子,通常是版本不匹配,升级Paddle版本即可。
跑通一次推理后,建议用time命令统计一下耗时。比如:
time python test_infer.py如果响应时间在几秒内,这个规模对于内部工具类应用完全可以接受。如果想要更稳定的秒级返回,可以把模型切成INT8或者使用更小的模型。比如用ERNIE 3.0 Tiny(大概0.5B参数量),飞腾D3000上单次推理大约能控制在几百毫秒到一秒左右。
3.3 量化与推理加速:榨干CPU性能
CPU推理要提速,核心不外乎两点:量化压缩模型体积、开启ARM平台的计算库优化。逐个说。
模型量化:从FP32到INT8,通常可以把模型体积缩小到原来的1/4,推理速度提升1.5到3倍不等,效果损失在可接受范围内。Paddle提供了量化工具,可以直接在部署时做在线量化:
# 安装量化工具 pip install paddle-quantization # 运行量化脚本,输入模型路径,输出量化后模型 python -m paddle_quantization.quantize \ --model_dir models/ernie_3.0_tiny_chinese \ --output_dir models/ernie_3.0_tiny_chinese_int8 \ --quant_type post_training \ --batch_size 8开启ARM优化:编译PaddlePaddle时如果指定了-DWITH_ARM=ON,推理时会自动使用NEON指令集加速。另一个关键是OpenBLAS。飞腾的CPU上有自己的向量指令集实现,OpenBLAS针对ARM平台做了深度优化,在矩阵乘法上有明显优势。如果安装时没有用OpenBLAS,推理时每条样本的延迟可能会差到3到5倍。
验证是否真的用上了OpenBLAS:
python -c "from paddle.base import core; print(core.get_build_version())"如果返回的输出中包含OpenBLAS字样,说明已经启用。
实际操作中,我还调整了Paddle Inference的线程数。飞腾D3000是8核心,默认线程数可能是4。用满8线程通常能带来额外20%-30%的吞吐提升:
import paddle import paddle.inference as paddle_infer config = paddle_infer.Config("model.pdmodel", "model.pdiparams") config.enable_mkldnn() config.set_cpu_math_library_num_threads(8) config.disable_gpu() predictor = paddle_infer.create_predictor(config) predictor.run()这里的enable_mkldnn()是开启Intel MKL-DNN优化。对ARM平台它不一定生效,但我实测在飞腾上开启它后,部分算子会自动切换到Paddle自己的ARM实现,兼容性没问题,性能略有浮动。如果你担心不兼容,可以不加这一行。
3.4 彻底离线:建立本地模型仓库
前面所有操作都建立在能联网下载依赖包的前提下。但实际部署环境经常是完全离线内网。这时候需要一个本地依赖仓库,把Paddle、numpy、protobuf、模型权重文件全部放进去,然后用pip的--find-links参数从本地安装。
创建目录结构建议:
offline_repo/ ├── wheels/ │ ├── paddlepaddle-2.6.1-cp310-cp310-linux_aarch64.whl │ ├── numpy-1.24.4-cp310-cp310-linux_aarch64.whl │ └── ... ├── models/ │ └── ernie_3.0_tiny_chinese/ └── install.shinstall.sh里可以写一个循环:
#!/bin/bash pip install --no-index --find-links=wheels/ wheels/*.whl这样在断网环境中也能实现一键安装。模型权重文件直接拷贝过去,不需要走git lfs。
注意:离线部署的一个常见坑是Paddle框架依赖系统的
libgomp、libstdc++库,这些属于系统级依赖,pip不管。如果内网机器连这些库都没有,需要预先装好gcc-gfortran或者对应的runtime包,否则启动时会提示GLIBCXX_3.4.29 not found。
4. 部署过程中的典型问题与性能调优
4.1 问题排查速查表
部署过程我踩了不少坑,把典型的整理成下面这个表,方便大家直接对照排查。
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
Illegal instruction (core dumped) | CPU指令集不兼容,某个库针对更新指令集编译 | 查看cat /proc/cpuinfo确认支持的指令集,重装对应版本库,或换低版本 |
GLIBCXX_3.4.29 not found | 系统libstdc++库太老 | sudo apt install libstdc++6升级,或手动拷贝新版库到/usr/lib/aarch64-linux-gnu/ |
加载模型时Killed | 内存不足,被OOM Killer杀掉 | 增加交换分区sudo fallocate -l 16G /swapfile,或用INT8量化模型 |
No operator found | Paddle版本不识别该模型中的某些算子 | 升级PaddlePaddle到更高版本,确保算子库覆盖 |
| 启动服务端口占用 | 8080端口被其他进程占用 | lsof -i:8080查看到占用进程,改端口或杀进程 |
Cannot load library: libopenblas.so.0 | OpenBLAS未装或路径不对 | find / -name "libopenblas*"查找路径,设置LD_LIBRARY_PATH |
| 中文乱码或输出空白 | tokenizer文件缺失或编码问题 | 确认tokenizer.json在模型目录下,UTF-8编码正常 |
4.2 从"能跑"到"跑得更好"的调优记录
很多人在CPU平台部署大模型,试验跑通了就认为任务完成。但实际用起来会发现"能跑"和"能满足业务需求"之间还有距离。我从几个维度做了优化,效果明显。
第一轮优化:启动加载时间
未优化前,每次启动模型服务需要加载权重文件,7B FP16的模型加载大约要30-60秒。这个时间对于服务的一次性启动可以接受,但如果频繁重启模型服务,体验就很差。我的处理方式是:把模型加载放到常驻内存,作为一个常驻进程管理,配合systemd设置服务自启,避免反复加载。
第二轮优化:请求排队机制
CPU推理速度有限,如果同时来多个请求,不加以控制就会互相争抢资源,导致每个请求都变得很慢。Paddle Serving本身支持设置最大并发数,可以把同一时间处理的请求数限制在2个,其余请求排队等待。这样单请求延迟更稳定,系统整体吞吐也更可控。
# serving_config.yml worker_num: 2 max_body_size: 67108864 batching_enable: true max_batch_size: 8开启batching_enable之后,多个短请求可以在同一个batch里执行,CPU核心利用率明显提高。这个改动对文档批量处理类场景提升最大,单个请求的在线对话场景收益有限。
第三轮优化:精度与速度的折中
同一个7B模型,FP32、FP16、INT8、INT4四种精度下,速度和内存占用差异很大。我在飞腾上分别做了测试:
| 精度 | 权重大小 | 相对速度 | 内存占用 | 效果损失 |
|---|---|---|---|---|
| FP32 | 28GB | 1x(最慢) | 很高 | 无 |
| FP16 | 14GB | 1.3x | 较高 | 几乎无 |
| INT8 | 7GB | 2x | 中 | 较小,可接受 |
| INT4 | 3.5GB | 2.5x | 低 | 有可感知的退化 |
我的建议是:业务场景对生成质量要求高、机器内存充足,用FP16;资源紧张、追求响应速度,用INT8。INT4虽然可以在8GB内存的机器上跑7B模型,但中文场景下生成质量下降比较明显,除非万不得已不推荐。
4.3 扩展阅读:从单机推理到服务化上线
如果你做的项目不止是个人实验,而是要把模型能力接入业务系统,那还需要考虑几个额外的东西。
一是API接口的鉴权。直接裸奔的模型服务放在内网里,一旦网络环境稍有暴露就很危险。至少加一个简单的Token鉴权,可以在Nginx层做,也可以在服务代码里做一个中间件拦截,实现成本都很低。
二是监控。模型服务运行期间,需要关注请求量、响应时间、错误率、CPU和内存占用。飞腾D3000上的部署我通常配合Prometheus + Grafana来监控,相关的exporter可以直接跑在这个平台上面。监控做好后,可以设置告警规则:比如CPU跑到90%以上持续5分钟,或者响应时间超过10秒,就触发通知。这样可以尽早发现问题,而不是等业务方来投诉。
三是模型版本管理。如果之后要换新版本模型,建议把版本号放进模型目录名里,而不是直接覆盖同名文件,方便回滚。
5. 写在最后
这篇文章我尽量把飞腾腾锐D3000上部署文心大模型的全过程拆开来讲,但真正动手做和看文章是两回事。你部署过程中会遇到各种各样预想不到的问题,有的是系统库版本不对,有的是模型转换链路卡住,还有的是性能远远达不到预期。这些都很正常,也是这类项目的乐趣所在。
就我个人的体会来说,从x86平台迁移到ARM平台,最大的一个感受是很多"图上画着容易"的步骤实际做起来要复杂得多。比如PaddlePaddle在飞腾上安装,官方文档里有现成命令,但直接复制粘贴大概率会报错,因为平台的架构和依赖版本都会影响安装结果。真正要保证部署顺利,还是得靠自己在虚拟环境里逐一把依赖理清,耐心建好离线仓库,这样后续无论是换机器还是扩展模型都有了一套可复制的方法。
另外,在CPU平台上跑大模型,务必事先控制好预期。不可能指望着跑出GPU那样的速度,但对于内部工具型应用来说,秒级返回已经完全够用。如果后续你们有更高的性能要求,也有两条路可以探索:一是通过模型蒸馏、剪枝缩小模型规模;二是如果业务允许,把推理任务切分到多台飞腾机器上做分布式。分布式那条路复杂度高不少,但在企业场景里往往是最终方向。
最后再分享一个小技巧:部署完成后,记得把模型推理进程注册成systemd服务,设置开机自启,这样断电重启之后模型服务能自动恢复,不用每次都到机器前面敲命令。在国产平台上做开发,很多不起眼的细节,攒多了就成了经验,也希望这篇能帮你在自己的部署之路上少走几段弯路。