模型加载与基础推理:从零跑通LLaMA

好,咱们今天进入实操环节。说实话,很多同学学大模型,理论背得滚瓜烂熟,一上手加载模型就报错。我当年第一次加载LLaMA时,光一个tokenizer就折腾了半小时——后来发现是路径写错了。嗯,这种坑咱们今天一并填上。

1. 用Transformers加载LLaMA模型

加载模型,说白了就两行代码的事。但这两行背后,Transformers库帮你做了大量脏活累活。

from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "meta-llama/Llama-2-7b-chat-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

你看,就这么简单。但我要提醒你——第一次跑的时候,确保网络通畅。模型文件动辄十几GB,下载时间够你喝两杯咖啡。

⚠️ 注意: 如果你在国内,建议设置镜像源,或者提前下载好模型文件到本地。我曾经在客户现场演示时,因为网络问题卡在下载环节,场面一度非常尴尬。

加载完成后,你可以用 model.device 看看模型在哪个设备上。默认是CPU,想用GPU得手动指定:

model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map="auto",      # 自动分配到GPU
    torch_dtype=torch.float16  # 半精度,省显存
)

我个人习惯用 device_map="auto",它会自动把模型各层分配到可用的GPU上。如果你的显卡只有8GB显存,跑7B模型有点吃力,可以考虑量化版本。

2. AutoModel与AutoTokenizer详解

这两个类,是Transformers库的「万能钥匙」。你想想看,不同模型的结构千差万别——BERT是编码器,GPT是解码器,LLaMA是纯解码器。如果每个模型都写一个加载类,代码得膨胀成什么样?

AutoModelAutoTokenizer 的作用,就是根据你传入的模型名称,自动匹配对应的类。

类名 作用 典型返回类型
AutoModel 加载模型本体(无LM头) LlamaModel
AutoModelForCausalLM 加载带因果语言模型头的版本 LlamaForCausalLM
AutoTokenizer 加载对应的分词器 LlamaTokenizer

这里有个细节:AutoModelForCausalLMAutoModel 的区别。前者在模型顶部加了一个线性层,用于预测下一个token。如果你只是想做特征提取,用AutoModel就够了;但要做文本生成,必须用带LM头的版本。

💡 小技巧: 你可以用 AutoModelForCausalLM.from_pretrained(..., output_hidden_states=True) 来获取每一层的隐藏状态,这在做模型分析时非常有用。

Tokenizer这边,我踩过一个坑:LLaMA的tokenizer默认没有pad_token。如果你要做batch推理,必须手动设置:

tokenizer.pad_token = tokenizer.eos_token
# 或者
tokenizer.add_special_tokens({'pad_token': '[PAD]'})

不设置的话,你会看到类似 ValueError: pad_token_id must be set 的错误。嗯,这个错误我至少见过五次。

3. 单次推理:让模型说第一句话

模型加载好了,咱们让它说句话试试。单次推理的流程很简单:

  1. 用tokenizer把文本转成token ID
  2. 喂给模型
  3. 把输出的token ID转回文本
input_text = "什么是人工智能?"
inputs = tokenizer(input_text, return_tensors="pt")

# 推理
outputs = model.generate(
    inputs.input_ids,
    max_new_tokens=50
)

# 解码
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(response)

你看,核心就三步。但这里有个容易忽略的点——return_tensors="pt" 表示返回PyTorch张量。如果你用TensorFlow,就改成 "tf"。

另外,skip_special_tokens=True 这个参数很重要。如果不加,你会看到一堆 <s></s> 之类的特殊token,影响阅读。

4. 生成参数:temperature、top_p、top_k

好,现在模型能说话了,但说的内容可能很「死板」。为什么?因为默认的生成策略是贪心搜索——每次都选概率最高的token。这会导致生成结果缺乏多样性。

这时候就需要调整生成参数了。我重点讲三个最常用的:

4.1 Temperature(温度)

Temperature控制的是概率分布的「尖锐程度」。值越小,模型越倾向于选高概率词;值越大,低概率词也有机会被选中。

  • temperature=0.1:几乎确定性的输出,适合事实性问答
  • temperature=0.7:平衡创造性和准确性,日常对话常用
  • temperature=1.5:输出很随机,适合创意写作
outputs = model.generate(
    inputs.input_ids,
    max_new_tokens=100,
    temperature=0.7,
    do_sample=True  # 必须开启,否则temperature无效
)
⚠️ 重要: 使用temperature时,必须设置 do_sample=True。我见过有人调了半天temperature没效果,最后发现忘了加这个参数。

4.2 Top-k 采样

Top-k的思路很简单:每次只从概率最高的k个token中采样。比如k=50,就只考虑前50个最可能的词。

这样做的好处是——避免选出概率极低的「离谱词」。我做过实验,不加top-k时,模型偶尔会蹦出一些完全不相关的词,比如在讨论天气时突然说「恐龙」。

outputs = model.generate(
    inputs.input_ids,
    max_new_tokens=100,
    do_sample=True,
    top_k=50
)

4.3 Top-p(核采样)

Top-p和top-k类似,但更灵活。它不是固定选前k个,而是选概率累积和超过p的最小集合。比如p=0.9,就选概率加起来超过90%的那些token。

我个人更偏爱top-p,因为它能动态调整候选集大小。当模型很确定时,候选集小;当模型不确定时,候选集大。

outputs = model.generate(
    inputs.input_ids,
    max_new_tokens=100,
    do_sample=True,
    top_p=0.9
)

4.4 组合使用

实际项目中,这三个参数经常组合使用。我常用的配置是:

outputs = model.generate(
    inputs.input_ids,
    max_new_tokens=200,
    do_sample=True,
    temperature=0.7,
    top_k=50,
    top_p=0.9
)

这个组合的效果是:先用top-k砍掉尾部低概率词,再用top-p动态调整候选集,最后用temperature微调概率分布。三层过滤下来,生成质量会稳定很多。

💡 经验之谈: 如果你做的是客服机器人,建议temperature设低一点(0.3左右),保证回答稳定;如果是写诗或故事,可以调到0.9以上。

5. 知识体系总览

为了让你更直观地理解本章的知识结构,我画了一张流程图:

模型加载与基础推理流程 1. 加载模型 2. Tokenizer分词 3. 模型推理 AutoModelForCausalLM device_map / 半精度 AutoTokenizer pad_token设置 model.generate() tokenizer.decode() 生成参数配置 Temperature 控制随机性 Top-k 固定候选集大小 Top-p 动态候选集 组合使用:三层过滤策略

这张图把整个流程串起来了。从加载模型开始,到分词、推理,再到生成参数的调优,每一步都有对应的代码和注意事项。

6. 避坑指南

最后,我总结几个实战中容易踩的坑:

  • 显存不足:7B模型用float16大概占14GB显存。如果你的卡只有8GB,考虑用4bit量化版本,或者用 device_map="sequential" 手动分配。
  • 生成速度慢:默认是CPU推理。记得用 model.to("cuda") 或者 device_map="auto"
  • 输出重复:可以设置 repetition_penalty=1.1 来惩罚重复的token。
  • 中文乱码:确保tokenizer支持中文。LLaMA原版对中文支持一般,建议用中文微调版本。

我曾经在部署一个对话系统时,发现模型总是重复「好的好的好的」,调了半天参数才发现是 repetition_penalty 没设置。嗯,这种细节往往最容易被忽略。

好了,模型加载和基础推理就讲到这里。你现在应该能自己跑通一个LLaMA模型,并且通过调整生成参数来控制输出风格了。下一章咱们会深入聊推理加速,那才是真正考验工程能力的地方。


专注资料整理