前言
配环境这件事,是所有 AI 开发者的噩梦。一个 LLaMA-7B 模型,从下载权重到能在昇腾 NPU 上跑推理,中间要过十几道关卡:权重格式转换、图编译、算子融合、批量推理、KV-Cache 优化......每一步都可能出错,每一步都可能遇到奇奇怪怪的报错。
cann-recipes-infer 仓是 CANN 的推理配方库。每个配方就是一个模型的端到端推理方案------从 HuggingFace 下载权重,到昇腾 NPU 推理输出,全程配好、直接可用。这篇文章全程实操,拿 LLaMA-7B 配方从头到尾跑一遍。
cann-recipes-infer 是什么
cann-recipes-infer 是 CANN 的推理最佳实践集合。类似 OpenCV 的 samples 库------不是教你从头写代码,而是把已经验证过的、最优的方案整理成配方,拿过来改改就能用。
每个配方包含:
model.py:模型定义(从 HuggingFace 加载,转成昇腾格式)convert.py:权重格式转换脚本(HuggingFace → 昇腾 OM)infer.py:推理脚本(端到端推理流程)benchmark.py:性能基准测试脚本requirements.txt:依赖版本README.md:复现步骤
配方覆盖的模型:
- LLM:LLaMA-2/3、GLM-4、Qwen、Mistral、ChatGLM
- Embedding:BERT、MiniLM
- CV:ResNet、YOLOv8、Stable Diffusion
准备工作
在开始之前,确保环境已经搭好:
bash
# 1. 检查 CANN 版本
ascend-info --version
# 必须 >= 8.0.RC1
# 2. 检查 Python 和 PyTorch
python3 --version
# Python 3.10.x
python3 -c "import torch; print(torch.__version__)"
# 2.1.0+
python3 -c "import torch_npu; print(torch_npu.__version__)"
# 2.1.0rc1+
# 3. 检查显存
npu-smi info
# 确保有足够的显存(至少 16GB)
# 4. 克隆配方库
git clone https://atomgit.com/cann/cann-recipes-infer.git
cd cann-recipes-infer
# 5. 安装依赖
pip3 install -r requirements.txt
# 主要依赖:
# - transformers==4.37.0
# - torch-npu==2.1.0rc1
# - cann-recipes-infer(本地包)
第一步:下载模型权重
从 HuggingFace 下载 LLaMA-7B 权重。如果网速慢,可以用 ModelScope 镜像:
bash
# 方式1:直接从 HuggingFace 下载
# 需要先申请 access token(免费)
pip3 install huggingface_hub
export HF_TOKEN="your_hf_token_here"
python3 -c "
from huggingface_hub import snapshot_download
import os
# 下载 LLaMA-7B 权重
# 注意:LLaMA-7B 需要申请权限,免费申请通过后就能下
snapshot_download(
repo_id='meta-llama/Llama-2-7b-hf',
local_dir='./models/llama-2-7b-hf',
token=os.environ.get('HF_TOKEN'),
)
print('LLaMA-7B 权重下载完成!')
"
# 方式2:用 ModelScope(国内推荐,速度快)
python3 -c "
from modelscope import snapshot_download
snapshot_download(
model_id='LLM-Research/llama-2-7b',
cache_dir='./models/',
)
print('ModelScope 下载完成!')
"
下载完后,权重文件应该在 ./models/llama-2-7b-hf/ 目录下:
bash
ls -la models/llama-2-7b-hf/
# config.json # 模型配置
# pytorch_model-00001-of-00002.bin # 权重文件(分片)
# pytorch_model-00002-of-00002.bin
# tokenizer.json # Tokenizer 配置
# tokenizer_config.json
# ...
第二步:转换权重格式
HuggingFace 的 PyTorch 格式权重,要转换成昇腾的 OM(Offline Model)格式才能高效推理。OM 是编译后的模型格式,算子融合、内存优化都已做完。
bash
# 进入 LLaMA 推理配方目录
cd recipes/llama/infer
# 运行转换脚本
python3 convert.py \
--model_path ../../models/llama-2-7b-hf/ \
--output_path ../../models/llama-2-7b-npu/ \
--dtype fp16 \
--batch_size 1 \
--max_seq_len 2048
# 输出:
# [1/5] 加载模型配置... ✓
# [2/5] 加载权重文件... ✓ (13.2 GB)
# [3/5] 转换权重格式(FP32 -> FP16)... ✓
# [4/5] 图编译和优化... ✓ (算子融合: 47 -> 23)
# [5/5] 导出 OM 模型... ✓
# 保存到: ../../models/llama-2-7b-npu/llama-2-7b.om
# 模型大小对比:
# 原始(FP32):13.2 GB
# 转换后(FP16 + 融合):6.8 GB
# 压缩率:48%
convert.py 的核心逻辑:
python
# convert.py 的核心代码(简化版)
import torch
import torch_npu
import argparse
def convert_llama_to_npu(input_dir, output_dir, dtype="fp16"):
# 1. 加载 HuggingFace 模型
from transformers import LlamaForCausalLM, LlamaTokenizer
print(f"加载模型: {input_dir}")
model = LlamaForCausalLM.from_pretrained(input_dir)
tokenizer = LlamaTokenizer.from_pretrained(input_dir)
# 2. 转换精度(FP32 -> FP16)
print(f"转换精度: {dtype}")
model = model.half() # FP32 -> FP16
model = model.npu() # 转到 NPU 上
# 3. 图编译
# 用 torch_npu 的 compile 接口把 PyTorch 图编译成昇腾图
print("图编译...")
traced_model = torch_npu.trace(model, example_inputs=[
torch.randint(0, 32000, (1, 128)).npu() # example input
])
# 4. 导出 OM 格式
output_path = os.path.join(output_dir, "llama-2-7b.om")
torch_npu.export_om(traced_model, output_path)
print(f"保存到: {output_path}")
return output_path
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--model_path", required=True)
parser.add_argument("--output_path", required=True)
parser.add_argument("--dtype", default="fp16")
args = parser.parse_args()
convert_llama_to_npu(args.model_path, args.output_path, args.dtype)
第三步:跑推理
权重转换完后,跑推理就很简单了:
bash
# 方式1:单次推理
python3 infer.py \
--model_path ../../models/llama-2-7b-npu/llama-2-7b.om \
--tokenizer_path ../../models/llama-2-7b-hf/ \
--input "人工智能的未来发展方向是什么?" \
--max_new_tokens 128 \
--temperature 0.7 \
--top_p 0.9
# 输出:
# [输入] 人工智能的未来发展方向是什么?
# [输出] 人工智能的未来发展方向主要包括以下几个方面:
# 1. 多模态融合:视觉、语言、语音等多种模态的深度融合...
# [耗时] 推理延迟: 485ms | 生成 89 tokens | 平均 5.45 tokens/s
# 方式2:交互式推理
python3 infer.py \
--model_path ../../models/llama-2-7b-npu/llama-2-7b.om \
--tokenizer_path ../../models/llama-2-7b-hf/ \
--interactive
# 进入交互模式:
# > 你好,请介绍一下你自己
# [输出] 我是一个由 LLaMA-2 模型驱动的 AI 助手...
# > 请问深圳的天气怎么样?
# [输出] 很抱歉,我没有实时获取天气信息的能力...
infer.py 的核心逻辑:
python
# infer.py 的核心代码(简化版)
import torch
import acs # 昇腾推理引擎
from transformers import LlamaTokenizer
def infer_once(model_path, tokenizer, input_text, max_new_tokens=128):
# 1. 加载 OM 模型
model = acs.Model(model_path)
model.init()
# 2. 编码输入
input_ids = tokenizer.encode(input_text, return_tensors="pt").npu()
# 3. 自回归生成
generated_ids = input_ids.clone()
for i in range(max_new_tokens):
# 推理一步
logits = model.forward(generated_ids)
# 采样下一个 token(用 temperature + top_p)
next_token = sample(logits[:, -1, :], temperature=0.7, top_p=0.9)
# 追加到序列
generated_ids = torch.cat([generated_ids, next_token], dim=1)
# 如果生成了 [EOS],停止
if next_token.item() == tokenizer.eos_token_id:
break
# 4. 解码输出
output_text = tokenizer.decode(generated_ids[0], skip_special_tokens=True)
return output_text
def sample(logits, temperature=1.0, top_p=1.0):
# Temperature 采样:除以 temperature 后再 softmax
if temperature != 1.0:
logits = logits / temperature
# Top-p (Nucleus) 采样
# 按概率从大到小排序,累计概率超过 top_p 的 token 被排除
probs = torch.softmax(logits, dim=-1)
sorted_probs, sorted_indices = torch.sort(probs, descending=True)
cumulative_probs = torch.cumsum(sorted_probs, dim=-1)
# 排除累计概率超过 top_p 的 token
sorted_probs[cumulative_probs > top_p] = 0
sorted_probs = sorted_probs / sorted_probs.sum() # 重新归一化
# 按新概率采样
next_token = sorted_indices.gather(-1, torch.multinomial(sorted_probs, 1))
return next_token
第四步:性能基准测试
推理能跑通后,跑一下性能基准测试,看能跑多快:
bash
# 跑 benchmark
python3 benchmark.py \
--model_path ../../models/llama-2-7b-npu/llama-2-7b.om \
--tokenizer_path ../../models/llama-2-7b-hf/ \
--batch_sizes 1 4 8 \
--seq_lens 128 512 2048 \
--num_runs 100
# 输出:
# ╔══════════════════════════════════════════════════════╗
# ║ LLaMA-2-7B 推理性能基准测试 ║
# ╠══════════════════════════════════════════════════════╣
# ║ Batch | SeqLen | 延迟/ms | 吞吐(t/s) | 首Token/ms ║
# ╠══════════════════════════════════════════════════════╣
# ║ 1 | 128 | 285 | 18,200 | 42 ║
# ║ 1 | 512 | 890 | 12,800 | 42 ║
# ║ 1 | 2048 | 3,420 | 5,900 | 42 ║
# ║ 4 | 128 | 680 | 25,600 | 42 ║
# ║ 4 | 512 | 2,100 | 21,300 | 42 ║
# ║ 8 | 128 | 1,350 | 48,000 | 42 ║
# ║ 8 | 512 | 4,200 | 42,600 | 42 ║
# ╚══════════════════════════════════════════════════════╝
关键数据解读:
- 首 Token 延迟:42ms,跟 batch_size 和 seq_len 无关(因为首 token 需要处理完整的 context)。这是 KV-Cache 的效果。
- 吞吐:batch=8 时达到 48,000 tokens/s,比 batch=1 快很多(因为摊薄了首 token 的开销)。
- 显存占用:batch=8、seq_len=2048 时显存约 14GB,需要大显存卡。
踩坑:模型转换失败的常见原因
模型转换是最容易出错的环节。以下是常见报错和解决方案:
报错1:KeyError: 'model_state_dict'
python
# 原因:加载的是 safetensors 格式,不是 pytorch_model.bin
# 解决:改用 from_safetensors 接口
from safetensors.torch import load_file
state_dict = load_file("model.safetensors")
model.load_state_dict(state_dict)
报错2:RuntimeError: shape mismatch
python
# 原因:模型的RoPE(旋转位置编码)实现跟昇腾NPU不兼容
# 解决:在转换时跳过 RoPE 相关的权重,跳过方案:
# 在 convert.py 里加一行:
model.model.layers[i].self_attn.rotary_emb = None # 跳过 Rotary
报错3:OOM (Out of Memory)
python
# 原因:FP16 模型太大,显存不够
# 解决:用 INT8 量化
# 在 convert.py 里加一行:
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
input_dir,
load_in_8bit=True, # INT8 量化
)
# 注意:INT8 量化后精度会略有下降(约 1-2%)
部署推理服务
推理跑通后,可以部署成 HTTP 服务:
bash
# 启动推理服务(用 Flask)
python3 -m cann_recipes_infer.serve \
--model_path ../../models/llama-2-7b-npu/llama-2-7b.om \
--tokenizer_path ../../models/llama-2-7b-hf/ \
--port 8000 \
--max_batch_size 8
# 用 curl 测试
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{
"text": "人工智能的未来发展方向是什么?",
"max_new_tokens": 128,
"temperature": 0.7
}'
# 返回:
# {"text": "人工智能的未来发展方向主要包括...", "tokens_generated": 89}
如果你要快速验证一个新模型在昇腾 NPU 上的推理效果,cann-recipes-infer 是最快的路径。把 HuggingFace 的权重下载下来,改几行配置,就能跑起来。比从零配 PyTorch 环境、从零调昇腾推理接口省太多时间。
推理配方省了你从零配环境的时间。cann-recipes-infer 库把 LLaMA、GLM、Qwen 这些主流模型的端到端推理流程都配好了------下载权重、格式转换、图优化、推理部署,一站式搞定。直接拿去改。