【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
标题
221、【AI】【模型部署】跑起第一个模型(下):Notebook 里首次推理
背景
上篇 blog 【AI】【模型部署】跑起第一个模型(上):装 CPU 环境与下载 Qwen2.5-0.5B 完成了"准备":在 venv 里装好 CPU 版 torch(2.14.0+cpu)、transformers 5.16.1、modelscope 1.40.0,并从 ModelScope 下载了 Qwen2.5-0.5B-Instruct(11 文件、约 1GB、缓存于 ~/.cache/modelscope/models/Qwen–Qwen2.5-0.5B-Instruct/snapshots/master),还验算了权重体积 ≈ 参数量 × 每参数字节数。本篇把这堆文件加载起来、第一次真正生成文本——在 Notebook 里逐格跑,复用 215 介绍过的交互式工作流,全程只在本机 CPU
模型部署
环境与模型都已就位,缺的只是"把 model.safetensors 变成能对话的大脑"那几行代码。本篇在一个新建的 Notebook 里逐格完成:加载分词器 → 加载模型 → 组装对话 → 生成 → 看输出。
🔍 开场前提:让 Notebook 找到模型
Notebook 里不需要联网——from_pretrained 直接指向本地缓存目录即可:
MODEL = "/home/adminpc/.cache/modelscope/models/Qwen–Qwen2.5-0.5B-Instruct/snapshots/master"

这一格没有"输出",只定义一个变量——正是 215 说的"跨格共享状态":后面所有格都能直接用 MODEL,不用重复粘贴长路径。
🧩 格 1:加载分词器与模型(约 0.4 秒)
import time
from transformers import AutoModelForCausalLM, AutoTokenizer
t0 = time.time()
tok = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForCausalLM.from_pretrained(MODEL)
print(f"加载合计 {time.time()–t0:.1f}s")

实测合计 0.4s(其中分词器约 0.2s、模型约 0.2s)就把 5 亿参数装进内存。为什么这么快:from_pretrained 从本地目录按 config.json 里的架构声明(qwen2、24 层、hidden 896)现场搭起网络结构,再把 safetensors 里的权重灌进去——不是"下载",而是本地文件的即时加载。变量 tok/model 留在内核里,后续每格都能直接用(跨格共享状态)。
🧩 格 2:组装一句对话
模型要按它训练的格式说话,模板由分词器负责渲染:
msg = [{"role": "user", "content": "用一句话介绍什么是 Transformer 模型。"}]
inputs = tok.apply_chat_template(msg, tokenize=True,
return_tensors="pt", return_dict=True)
print("prompt tokens:", inputs["input_ids"].shape[1])

apply_chat_template 把"角色消息列表"渲染成模型认识的完整文本——开头加上 system 设定、句间插入 <|im_start|>user 这类特殊 token;实测这句话被切成 35 个 token。返回值里 input_ids 就是喂给模型的数字序列。
🧩 格 3:生成 52 个词并计时
import time
t0 = time.time()
out = model.generate(**inputs, max_new_tokens=64, do_sample=False)
dt = time.time() – t0
gen_n = out.shape[1] – inputs["input_ids"].shape[1]
print(f"生成 {gen_n} tokens | 耗时 {dt:.1f}s | {gen_n/dt:.1f} tok/s")
text = tok.decode(out[0], skip_special_tokens=True)
print(text)

实测结果:生成 52 tokens、耗时 3.1 秒、约 16.9 tok/s。max_new_tokens=64 限定最多新增 64 个词(本次 52 个即遇到结束符);do_sample=False 表示贪心解码,每步取概率最高的词。贪心还给调试带来可复现性:同一 prompt 每次运行结果一致,便于核对问题出在输入还是模型。
🧩 格 3 的真实输出:长什么样
同一格打印出的完整文本(节选):
system
You are Qwen, created by Alibaba Cloud. You are a helpful assistant.
user
用一句话介绍什么是 Transformer 模型。
user
Transformer 模型是一种深度学习模型,它通过自注意力机制…
两个值得注意的点:
- 文本里出现 system/user 字样的角色标记,因为 apply_chat_template 已经把整段对话拼进输入,decode 时这些标记连答复一起回显——想只看"纯答复"可在生成后从 out[0][prompt_len:] 截取生成段再解码;
- 最前面的 system 提示语(“You are Qwen, created by Alibaba Cloud…”)每次都出现,说明系统提示是模板自动加的——这也是"套壳聊天产品"注入系统提示的底层位置;
- 答复末尾又冒出一个 user,是 0.5B 小模型的常见毛病——生成时偶尔把对话模板再回显一段。模型小≠完美,跑通流程的目的已达成。
🧩 同样的问法,换个解码参数:输出会不同
上面用的是 do_sample=False(贪心,每步取概率最高的词,输出稳定可复现)。改成 do_sample=True 引入随机采样后,同样的问题会得到措辞不同的回答。实测同句的采样输出(节选):
Transformer 是一种基于自注意力机制的神经机器学习模型,
通过分词、编码和解码等步骤实现了大规模语言处理任务…

参数的工程含义:产品里要稳定就开贪心或把 temperature 调低(temperature=0.9 左右);要多样、发散(写文案、头脑风暴)就开采样。这个开关在后续做推理服务时是必配的请求参数。
🧩 顺带懂一个点:为什么 35 个 token
一句 20 多个字的中文被切成 35 个 token,既不是"一字一 token"也不是"整句一个"。现代分词器按 BPE 子词切分:常见词/字组会合并成更大的单元,生僻内容拆得更碎,最后映射成词表里的数字 ID。Qwen2.5 词表有 151936 个 token(config.json 里的 vocab_size)。input_ids 里的每个数,就是这些 token 的 ID——模型看到的从来不是文字,而是一串数字。
📊 这次 Notebook 会话在做什么
| 准备 | 定义 MODEL 本地路径 | 变量跨格共享 |
| 格 1 | 加载 tokenizer + model | 0.4s,本地即时加载 |
| 格 2 | 组装消息 → 35 tokens | apply_chat_template |
| 格 3 | generate 生成 | 52 tok / 3.1s / 16.9 tok/s |
速度换算直观感受:16.9 tok/s 意味着写 512 个词要约 30 秒——CPU 上能"跑通"但不算快,这正是后来要把推理放到 GPU 服务(DSW/EAS)上的现实动机之一。
📌 一句话记忆
Notebook 里用三格跑通第一个模型:from_pretrained(本地目录) 加载 tokenizer 与模型(0.4s,config.json 定结构、safetensors 灌权重)→ apply_chat_template 把消息渲染成 35 tokens 的对话 → model.generate(max_new_tokens=64) 贪心生成,实测 52 tokens / 3.1s / 16.9 tok/s;小模型会回显角色标记是正常现象,CPU 能跑通但慢——为后续上 GPU 服务埋下动机。
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog 【AI】【模型部署】模型的影子:解剖 Qwen2.5-0.5B 模型目录
网硕互联帮助中心



评论前必须登录!
注册