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|>assistant

pipeline:最省事的用法,一行完成分词、推理、解码。

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 上的模型格式。

参考资料

相关