开源模型调用
使用 Hugging Face 的 transformers 库可以比较方便地调用开源大语言模型。为了先把调用链路跑通,这里尽量选择一个小模型:
Qwen/Qwen2.5-0.5B-Instruct
这个模型只有 0.5B 参数,适合本地学习和调试。它的能力不能和 7B、14B 甚至更大的模型相比,但胜在下载快、显存占用低、启动成本小。
为什么先选小模型
刚开始学习开源模型调用时,不建议一上来就跑很大的模型,原因是:
- 大模型对显存要求更高,环境问题会掩盖调用逻辑本身。
- 小模型启动快,适合反复调试 prompt、tokenizer 和采样参数。
- 如果只是学习
tokenizer -> model.generate -> decode 这条链路,0.5B 或 1.5B 已经足够。
- 小模型可以在很多普通 GPU 甚至 CPU 上跑通,虽然 CPU 会慢一些。
模型权重大致显存可以这样估算:
$$
\text{显存} \approx \text{参数量} \times \text{每个参数占用字节数}
$$
以 0.5B 参数为例:
| 精度 |
每个参数 |
权重大致占用 |
| fp32 |
4 bytes |
约 2GB |
| fp16 / bf16 |
2 bytes |
约 1GB |
| int8 |
1 byte |
约 0.5GB |
| int4 |
0.5 byte |
约 0.25GB |
实际推理时还会有 KV cache、中间激活和框架开销,所以真实占用会比权重本身更高。
环境准备
建议新建一个单独的 conda 环境:
1 2
| conda create -n llm-call python=3.10 conda activate llm-call
|
安装依赖:
1
| pip install torch transformers accelerate sentencepiece
|
如果使用 GPU,需要安装和 CUDA 版本匹配的 PyTorch。第一次运行时,模型会自动从 Hugging Face 下载到本地缓存。
最小调用示例
下面是一个最小可运行版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import torch from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "Qwen/Qwen2.5-0.5B-Instruct" device = "cuda" if torch.cuda.is_available() else "cpu"
tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16 if device == "cuda" else torch.float32, ).to(device)
messages = [ {"role": "system", "content": "你是一个简洁、准确的中文助手。"}, {"role": "user", "content": "用三句话解释什么是 Transformer。"}, ]
text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, )
inputs = tokenizer([text], return_tensors="pt").to(device)
outputs = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, )
generated_ids = outputs[0][inputs.input_ids.shape[-1]:] response = tokenizer.decode(generated_ids, skip_special_tokens=True)
print(response)
|
这段代码的核心流程是:
1 2 3 4 5 6
| messages -> apply_chat_template -> tokenizer 编码 -> model.generate 生成 -> tokenizer 解码 -> 文本回答
|
chat template 的作用
指令模型通常不是直接把用户输入拼接给模型,而是有固定的对话格式。例如:
1 2 3 4
| messages = [ {"role": "system", "content": "你是一个有帮助的助手。"}, {"role": "user", "content": "请解释什么是 KV Cache。"}, ]
|
apply_chat_template 会把这种结构化对话转换成模型训练时熟悉的文本格式。不同模型的模板可能不同,所以一般不要手写模板,优先使用 tokenizer 自带的模板:
1 2 3 4 5
| text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, )
|
其中 add_generation_prompt=True 表示前面是上下文,接下来应该轮到 assistant 生成回答。
generate 常用参数
generate() 里的参数会直接影响输出风格:
| 参数 |
含义 |
常见设置 |
max_new_tokens |
最多生成多少个新 token |
128、256、512 |
do_sample |
是否采样 |
创作类用 True,确定性任务用 False |
temperature |
控制随机性 |
0.3 更稳定,0.7 较自然,1.0 更发散 |
top_p |
nucleus sampling |
常用 0.8 到 0.95 |
repetition_penalty |
抑制重复 |
常用 1.05 到 1.2 |
如果希望输出更稳定:
1 2 3 4 5
| outputs = model.generate( **inputs, max_new_tokens=256, do_sample=False, )
|
如果希望输出更有创造性:
1 2 3 4 5 6 7
| outputs = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.8, top_p=0.9, )
|
如果模型容易重复,可以加入:
1 2 3 4 5 6 7 8
| outputs = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, repetition_penalty=1.1, )
|
封装成一个类
实际使用时可以把加载模型和生成逻辑封装起来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import torch from transformers import AutoModelForCausalLM, AutoTokenizer
class LocalLLM: def __init__(self, model_name: str = "Qwen/Qwen2.5-0.5B-Instruct"): self.device = "cuda" if torch.cuda.is_available() else "cpu" self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16 if self.device == "cuda" else torch.float32, ).to(self.device)
def chat(self, prompt: str, system_prompt: str = "你是一个简洁、准确的中文助手。") -> str: messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ]
text = self.tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, )
inputs = self.tokenizer([text], return_tensors="pt").to(self.device) outputs = self.model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, )
generated_ids = outputs[0][inputs.input_ids.shape[-1]:] return self.tokenizer.decode(generated_ids, skip_special_tokens=True)
if __name__ == "__main__": llm = LocalLLM() print(llm.chat("什么是大语言模型的推理?"))
|
这样后续想更换模型时,只需要修改 model_name。
使用 pipeline 快速调用
pipeline 写法更短,适合快速验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from transformers import pipeline
pipe = pipeline( "text-generation", model="Qwen/Qwen2.5-0.5B-Instruct", device_map="auto", )
messages = [ {"role": "user", "content": "请用一段话解释什么是 tokenizer。"}, ]
result = pipe( messages, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, )
print(result[0]["generated_text"][-1]["content"])
|
pipeline 的优点是简单,缺点是很多细节被封装起来了。如果目的是理解推理流程,建议先掌握 AutoTokenizer + AutoModelForCausalLM 的写法。
写成命令行聊天脚本
可以新建一个 chat.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| from transformers import AutoModelForCausalLM, AutoTokenizer import torch
model_name = "Qwen/Qwen2.5-0.5B-Instruct" device = "cuda" if torch.cuda.is_available() else "cpu"
tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16 if device == "cuda" else torch.float32, ).to(device)
def generate(prompt: str) -> str: messages = [ {"role": "system", "content": "你是一个简洁、准确的中文助手。"}, {"role": "user", "content": prompt}, ] text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, ) inputs = tokenizer([text], return_tensors="pt").to(device) outputs = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, ) generated_ids = outputs[0][inputs.input_ids.shape[-1]:] return tokenizer.decode(generated_ids, skip_special_tokens=True)
while True: prompt = input("\nUser: ") if prompt.lower() in {"exit", "quit"}: break print("Assistant:", generate(prompt))
|
运行:
本地服务化调用
如果希望其他程序通过 HTTP 调用本地模型,可以用 FastAPI 包一层。
安装:
1
| pip install fastapi uvicorn
|
示例 server.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| from fastapi import FastAPI from pydantic import BaseModel from transformers import AutoModelForCausalLM, AutoTokenizer import torch
class ChatRequest(BaseModel): prompt: str
app = FastAPI()
model_name = "Qwen/Qwen2.5-0.5B-Instruct" device = "cuda" if torch.cuda.is_available() else "cpu"
tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained( model_name, torch_dtype=torch.float16 if device == "cuda" else torch.float32, ).to(device)
@app.post("/chat") def chat(req: ChatRequest): messages = [ {"role": "system", "content": "你是一个简洁、准确的中文助手。"}, {"role": "user", "content": req.prompt}, ]
text = tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True, )
inputs = tokenizer([text], return_tensors="pt").to(device) outputs = model.generate( **inputs, max_new_tokens=256, do_sample=True, temperature=0.7, top_p=0.9, )
generated_ids = outputs[0][inputs.input_ids.shape[-1]:] response = tokenizer.decode(generated_ids, skip_special_tokens=True) return {"response": response}
|
启动:
1
| uvicorn server:app --host 127.0.0.1 --port 8000
|
调用:
1 2 3
| curl -X POST http://127.0.0.1:8000/chat \ -H "Content-Type: application/json" \ -d "{\"prompt\":\"解释一下什么是注意力机制\"}"
|
常见问题
下载模型很慢
第一次运行会下载模型权重。如果 Hugging Face 访问慢,可以配置镜像,或者提前手动下载模型到本地。
显存不够
可以按这个顺序处理:
- 换更小的模型。
- 减小
max_new_tokens。
- 尝试量化模型。
- 先用 CPU 跑通流程。
输出重复
可以尝试:
也可以降低 temperature。
输出不符合预期
优先检查:
- 是否使用了正确的 chat template。
system prompt 是否太模糊。
max_new_tokens 是否太小。
- 采样参数是否过于发散。
小结
开源模型调用的核心流程可以概括为:
1 2 3 4 5 6
| 文本输入 -> chat template -> tokenizer 编码 -> model.generate 自回归生成 -> tokenizer 解码 -> 文本输出
|
刚开始建议用 Qwen/Qwen2.5-0.5B-Instruct 这样的小模型跑通完整流程。理解 tokenizer、prompt、采样参数和显存占用之后,再切换到 1.5B、3B、7B 等更大的模型。