Hugging Face 是目前开源模型最主要的发布和使用平台。它包括一个托管模型与数据集的网站(Hub),以及一组 Python 库。学大语言模型时,下载模型、推理、微调,基本都绕不开这套工具。
| 组件 | 定位 |
|---|---|
| Hub | 托管模型、数据集和演示应用的网站,每个模型有一个 ID,如 gpt2、Qwen/Qwen2.5-0.5B-Instruct |
transformers | 加载和运行预训练模型的核心库 |
tokenizers | 高性能的分词器实现,transformers 在底层使用它 |
datasets | 下载、处理数据集 |
peft | 参数高效微调,如 LoRA |
trl | 大语言模型的后训练:监督微调、偏好优化、强化学习 |
accelerate | 让同一份训练代码跑在单卡、多卡、多机上 |
安装时先按硬件选择合适的 PyTorch,再安装 transformers datasets peft trl accelerate。这些库独立发布版本,应固定并记录一组兼容的版本;不能仅凭 transformers 的版本保证 PEFT、TRL 示例也兼容。下文按官方文档核对接口,涉及下载模型和后训练的片段需要在对应环境中实际验证。
transformers:加载模型与推理
AutoTokenizer 和 AutoModelForCausalLM 会根据模型 ID 自动选择正确的类。第一次运行时会从 Hub 下载权重,之后使用本地缓存。
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
model_id = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.float32)dtype 指定加载精度,旧版本常用参数名 torch_dtype。dtype="auto" 让库根据配置或检查点确定精度;为便于复现,示例显式设置这个参数,避免依赖不同版本的默认值。
分词:把文本变成 token ID,再变回来。
enc = tokenizer("Hello, world!")
enc["input_ids"] # [15496, 11, 995, 0]
tokenizer.convert_ids_to_tokens(enc["input_ids"]) # ['Hello', ',', 'Ġworld', '!']
tokenizer.decode(enc["input_ids"]) # 'Hello, world!'Ġ 表示前面有一个空格,这是 GPT-2 字节级 BPE 的记法,见分词。
前向计算:模型输出每个位置上、整个词表的 logits,最后一个位置的 logits 就是对下一个 token 的预测。
inputs = tokenizer("The capital of France is", return_tensors="pt")
with torch.no_grad():
out = model(**inputs)
out.logits.shape # torch.Size([1, 5, 50257]):批次、序列长度、词表大小
tokenizer.decode(out.logits[0, -1].argmax()) # ' the'传入 labels 时,模型会自动计算语言模型的交叉熵损失(内部会把标签错开一位,用第 t 个位置预测第 t+1 个 token):
inputs = tokenizer("The quick brown fox jumps over the lazy dog.", return_tensors="pt")
out = model(**inputs, labels=inputs["input_ids"])
out.loss # 约 5.09
torch.exp(out.loss) # 困惑度,约 162原理见语言模型。
生成文本:generate 把「预测下一个 token、拼回输入」这个过程循环执行。
ids = model.generate(
**inputs,
max_new_tokens=20, # 最多生成多少个新 token
do_sample=True, # 采样;False 且 num_beams=1 时才是贪心解码
temperature=0.8, # 温度越低,输出越确定
top_p=0.95, # 核采样:只在累计概率前 95% 的 token 中采样
pad_token_id=tokenizer.eos_token_id,
)
print(tokenizer.decode(ids[0], skip_special_tokens=True))对话模型:指令微调过的模型要求输入符合它训练时的对话格式,用 apply_chat_template 自动拼接,不要手写:
model_id = "Qwen/Qwen2.5-0.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, dtype="auto", device_map="auto")
messages = [
{"role": "system", "content": "你是一个乐于助人的助手。"},
{"role": "user", "content": "用一句话解释什么是梯度下降。"},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, add_special_tokens=False, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=60)
# 只解码新生成的部分
print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))device_map="auto" 属于 Accelerate 的大模型推理分发:优先使用加速器显存,必要时可能卸载到 CPU 或磁盘,并不保证整个模型都在 GPU 上,也不能直接当成分布式训练方案。模板已加入对话特殊 token,再分词时设置 add_special_tokens=False,避免重复添加。打印 text 可以看到 Qwen 使用的对话格式:
<|im_start|>system
你是一个乐于助人的助手。<|im_end|>
<|im_start|>user
用一句话解释什么是梯度下降。<|im_end|>
<|im_start|>assistantpipeline:最省事的用法,一行完成分词、推理、解码。
from transformers import pipeline
gen = pipeline("text-generation", model="gpt2")
gen("Once upon a time", max_new_tokens=15)
# [{'generated_text': 'Once upon a time ...'}]批量输入与填充:一个批次里的句子长短不一,需要填充(padding)到同样长度,attention_mask 标出哪些位置是真实 token。GPT-2 没有专门的填充 token,通常用结束符代替。
tokenizer = AutoTokenizer.from_pretrained("gpt2") # 本节重新使用 GPT-2
tokenizer.pad_token = tokenizer.eos_token
batch = tokenizer(["short", "a much longer sentence here"], padding=True, return_tensors="pt")
batch["attention_mask"]
# tensor([[1, 0, 0, 0, 0],
# [1, 1, 1, 1, 1]])用仅解码器模型批量生成时,要设置 tokenizer.padding_side = "left",把填充放在左边,让每条序列的末尾都紧接着新生成的 token。
datasets:加载与处理数据
from datasets import load_dataset
ds = load_dataset("stanfordnlp/imdb", split="train[:100]") # 只取训练集前 100 条
ds[0]["label"] # 0
def tok(batch):
return tokenizer(batch["text"], truncation=True, max_length=128)
ds = ds.map(tok, batched=True, remove_columns=["text"]) # 批量分词,新增 input_ids 等列
ds = ds.filter(lambda e: e["label"] == 0) # 按条件过滤常规 Dataset 可通过 Apache Arrow 和内存映射避免把整个数据集一次性读入内存,但预处理批次、多进程、转成 Python 对象等仍可能占用大量内存。split="train[:100]" 限制返回的样本范围,并不保证只下载或预处理 100 条;若要流式读取,可使用 streaming=True 并按需取样。
tokenizers:独立使用分词器
transformers 的许多 fast tokenizer 使用 Rust 实现的 tokenizers 库,也存在 Python、SentencePiece 等其他实现。通常只有训练或定制分词器时,才需要直接操作这个底层库。
from tokenizers import Tokenizer
t = Tokenizer.from_pretrained("gpt2")
e = t.encode("Hello, world!")
e.ids, e.tokens # ([15496, 11, 995, 0], ['Hello', ',', 'Ġworld', '!'])peft:LoRA 微调
全参数微调要为每个参数保存梯度和优化器状态,显存开销很大。LoRA 冻结原模型,只训练插入的小型低秩矩阵:
from peft import LoraConfig, get_peft_model
# c_attn 是 GPT-2 的模块名,不能沿用前面对话示例中的 Qwen 模型
model = AutoModelForCausalLM.from_pretrained("gpt2", dtype=torch.float32)
config = LoraConfig(
r=8, # 低秩矩阵的秩
lora_alpha=16, # 缩放系数
lora_dropout=0.05,
target_modules=["c_attn"], # GPT-2 的注意力投影层
fan_in_fan_out=True, # GPT-2 Conv1D 的权重按 (fan_in, fan_out) 存储
task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
model.print_trainable_parameters()
# trainable params: 294,912 || all params: 124,734,720 || trainable%: 0.2364只需训练 0.24% 的参数。原理见指令微调与偏好优化。
trl:监督微调与偏好优化
TRL 提供了 SFTTrainer(监督微调)、DPOTrainer(直接偏好优化)、GRPOTrainer 等训练器,把训练循环、数据处理、对话模板都封装好了。
from datasets import Dataset
from peft import LoraConfig
from trl import SFTConfig, SFTTrainer
# 对话格式的数据:每条是一个 messages 列表,会自动套用模型的对话模板
train_dataset = Dataset.from_list([
{"messages": [
{"role": "user", "content": "hi"},
{"role": "assistant", "content": "hello"},
]},
] * 8)
trainer = SFTTrainer(
model="Qwen/Qwen2.5-0.5B-Instruct", # 可以直接传模型 ID
train_dataset=train_dataset,
args=SFTConfig(
output_dir="qwen-sft",
per_device_train_batch_size=2,
max_steps=2, # 仅演示流程,重复八条问候语不能训练出通用能力
bf16=False,
fp16=False,
eos_token="<|im_end|>", # 与 Qwen 对话结束标记一致
report_to="none",
),
peft_config=LoraConfig(r=8, lora_alpha=16, task_type="CAUSAL_LM"), # 可选:配合 LoRA
)
trainer.train()SFTConfig 继承自 transformers 的 TrainingArguments。这里显式关闭 BF16/FP16,便于在不支持它们的设备上演示;实际训练应按硬件配置精度。默认情况下,这类 messages 数据通常对整段序列计算损失,并不自动只训练 assistant 部分。若要设置 assistant_only_loss=True,还需确认对话模板能提供 assistant token 掩码。
accelerate:分布式训练
accelerate 让一份普通的 PyTorch 训练循环只改几行,就能跑在多卡、多机或混合精度上:
from accelerate import Accelerator
accelerator = Accelerator()
model, optimizer, loader = accelerator.prepare(model, optimizer, loader)
for x, y in loader: # 不用再手动 .to(device)
loss = loss_fn(model(x), y)
optimizer.zero_grad()
accelerator.backward(loss) # 代替 loss.backward()
optimizer.step()用 accelerate config 配置一次硬件环境,再用 accelerate launch train.py 启动。transformers 的 Trainer 和 TRL 的训练器在底层都使用 accelerate。
在 AI 中的作用
- 绝大多数开源大模型(Llama、Qwen、DeepSeek、Mistral 等)都在 Hub 上发布,用
from_pretrained一行加载。 - 从推理、数据处理到微调、偏好优化,这套库覆盖了个人和小团队使用大模型的完整流程。
- 生产环境的高吞吐推理通常改用 vLLM、SGLang 等专门的推理引擎,但它们同样直接读取 Hub 上的模型格式。
参考资料
相关
- PyTorch模型训练:这些高层工具封装的底层训练循环。
- Transformer:
transformers库所实现的模型结构。 - 指令微调与偏好优化:PEFT 与 TRL 对应的训练方法。
- 分词:分词器的原理。
- 开源框架生态:训练、后训练、评测、推理框架的选型。