Transformers 模型库(结合源码)
1. 库的简介
Transformers 是由 HuggingFace 开发的开源库,是当前大语言模型(LLM)开发领域最核心的基础库之一。它提供了统一的 API 来访问和使用数千个预训练模型,覆盖了 NLP、计算机视觉、音频等多种模态。在 LLM 开发中,Transformers 扮演着”模型基础设施”的角色——无论是加载预训练权重、进行推理、微调训练,还是模型量化与分布式部署,Transformers 都是不可或缺的底层依赖。
Transformers 的核心价值:
- 统一的模型接口:所有模型遵循
PreTrainedModel基类,提供一致的from_pretrained()、save_pretrained()、generate()等 API - AutoModel 自动加载机制:根据
config.json中的model_type自动选择正确的模型类 - 丰富的训练支持:
Trainer+TrainingArguments封装了完整的训练循环 - 生态集成:与 DeepSpeed、bitsandbytes、PEFT、accelerate 等深度集成
2. 源码架构分析
2.1 整体架构图
src/transformers/
├── configuration_utils.py # PretrainedConfig 基类:配置管理
├── modeling_utils.py # PreTrainedModel 基类:模型加载/保存/推理
├── tokenization_utils.py # PreTrainedTokenizer 基类:分词器
├── cache_utils.py # KV Cache 实现(DynamicCache, StaticCache等)
├── modeling_flash_attention_utils.py # FlashAttention 集成
├── trainer.py # Trainer 训练类
├── training_args.py # TrainingArguments 训练参数
├── modeling_rope_utils.py # RoPE 旋转位置编码工具
├── masking_utils.py # 注意力掩码生成
├── modeling_outputs.py # 模型输出数据类
├── modeling_layers.py # 通用模型层(分类头等)
├── loss/ # 损失函数
├── data/
│ └── data_collator.py # DataCollator 数据整理器
├── generation/
│ ├── utils.py # generate() 方法核心逻辑
│ ├── logits_process.py # LogitsProcessor 采样处理
│ ├── stopping_criteria.py # 停止条件
│ └── configuration_utils.py # GenerationConfig
├── models/
│ ├── auto/ # AutoModel 自动加载机制
│ │ ├── modeling_auto.py # AutoModel, AutoModelForCausalLM 等
│ │ ├── tokenization_auto.py # AutoTokenizer
│ │ ├── auto_factory.py # 自动类工厂
│ │ └── configuration_auto.py # AutoConfig
│ ├── llama/ # LLaMA 模型实现
│ │ ├── configuration_llama.py # LlamaConfig
│ │ ├── modeling_llama.py # LlamaModel, LlamaForCausalLM 等
│ │ └── tokenization_llama.py # LlamaTokenizer
│ ├── bert/ # BERT 模型实现
│ ├── gpt2/ # GPT-2 模型实现
│ ├── qwen2/ # Qwen2 模型实现
│ ├── mistral/ # Mistral 模型实现
│ └── ... # 200+ 其他模型
├── pipelines/ # 高级推理管道
│ ├── base.py # Pipeline 基类与 PipelineRegistry
│ ├── text_generation.py # 文本生成管道
│ └── ... # 其他任务管道
├── integrations/ # 外部框架集成
│ ├── deepspeed.py # DeepSpeed 集成
│ ├── peft.py # PEFT/LoRA 集成
│ ├── flash_attention.py # FlashAttention 集成
│ └── ...
├── quantizers/ # 量化支持
│ └── auto.py # 自动量化器选择
└── utils/
└── quantization_config.py # BitsAndBytesConfig 等
架构关系说明:
- PretrainedConfig → 定义模型配置(层数、维度等),被
PreTrainedModel持有 - PreTrainedModel → 所有模型的基类,提供加载/保存/推理框架,各具体模型继承它
- AutoModel → 根据
config.json中的model_type查找映射表,自动选择正确的模型类 - GenerationMixin → 混入
PreTrainedModel,提供generate()方法 - Trainer → 组合使用
PreTrainedModel+TrainingArguments+DataCollator完成训练
2.2 PreTrainedModel 基类
PreTrainedModel(定义于 modeling_utils.py:1151)是所有模型的基类,继承了 nn.Module、PushToHubMixin、PeftAdapterMixin 等:
class PreTrainedModel(nn.Module, EmbeddingAccessMixin, ModuleUtilsMixin, PushToHubMixin, PeftAdapterMixin):
config_class: type[PreTrainedConfig] | None = None # 关联的配置类
base_model_prefix: str = "" # 基础模型前缀,如 "model"
main_input_name: str = "input_ids" # 主输入名
_no_split_modules: set[str] | list[str] | None = None # 设备映射时不可分割的模块
_supports_sdpa: bool = False # 是否支持 SDPA 注意力
_supports_flash_attn: bool = False # 是否支持 FlashAttention
supports_gradient_checkpointing: bool = False # 是否支持梯度检查点
from_pretrained() 的源码逻辑
from_pretrained() 是类方法(modeling_utils.py:3766),核心流程如下:
- 下载/定位模型文件:通过
cached_file()从 HuggingFace Hub 下载或从本地路径加载config.json和权重文件 - 加载配置:调用
PretrainedConfig.from_pretrained()加载配置对象 - 确定模型类:如果是 AutoModel,根据
config.model_type查找映射表确定具体的模型类 - 初始化模型:使用
config初始化模型骨架(__init__) - 加载权重:
- 优先使用 safetensors 格式(
load_state_dict()函数,modeling_utils.py:333) - 对于分片权重,使用
get_checkpoint_shard_files()加载索引文件 - 支持 bitsandbytes 量化加载
- 优先使用 safetensors 格式(
- 分配设备:根据
device_map将模型各层分配到不同设备(通过accelerate库) - 设置为评估模式:
model.eval()
关键参数:
def from_pretrained(
cls,
pretrained_model_name_or_path, # 模型ID或本地路径
config=None, # 预加载的配置对象
cache_dir=None, # 缓存目录
force_download=False, # 强制重新下载
local_files_only=False, # 仅使用本地文件
token=None, # HuggingFace 访问令牌
revision="main", # 模型版本(分支/标签/commit)
dtype="auto", # 数据类型,如 torch.bfloat16
device_map=None, # 设备映射,如 "auto"
quantization_config=None, # 量化配置,如 BitsAndBytesConfig
attn_implementation=None, # 注意力实现:"eager"/"sdpa"/"flash_attention_2"
trust_remote_code=False, # 是否信任远程代码
...
)
save_pretrained() 的逻辑
- 保存
config.json到指定目录 - 将模型权重保存为 safetensors 格式(优先)或 pytorch bin 格式
- 对于大模型,自动分片保存
- 处理权重绑定(tied weights),避免重复保存
push_to_hub() 的逻辑
- 调用
save_pretrained()将模型保存到临时目录 - 使用
huggingface_hub库上传到 HuggingFace Hub - 自动创建/更新 Model Card
2.3 PretrainedConfig:配置管理机制
PretrainedConfig(定义于 configuration_utils.py:123)是基于 Python dataclass 的配置基类:
@dataclass_transform(kw_only_default=True)
@strict(accept_kwargs=True)
@dataclass(repr=False)
class PreTrainedConfig(PushToHubMixin, RotaryEmbeddingConfigMixin):
model_type: str # 模型类型标识,如 "llama"
vocab_size: int # 词表大小
hidden_size: int # 隐藏层维度
num_attention_heads: int # 注意力头数
num_hidden_layers: int # Transformer 层数
...
配置管理的关键设计:
- 序列化:
config.to_json_string()将配置序列化为 JSON 字符串,保存为config.json - 反序列化:
PretrainedConfig.from_pretrained()从 JSON 文件加载配置 - 验证:
@strict装饰器确保配置参数类型正确,validate_architecture()方法验证架构合法性 - 子类化:每个模型定义自己的 Config 类,如
LlamaConfig添加了intermediate_size、rms_norm_eps等特有参数
2.4 AutoModel 系列:自动模型加载的注册机制
AutoModel 的核心是 映射表 + 懒加载 机制,定义在 models/auto/ 目录下。
映射表
modeling_auto.py 定义了多个 OrderedDict 映射表,将 model_type 字符串映射到具体模型类名:
# modeling_auto.py 中的映射表(部分)
MODEL_MAPPING_NAMES = OrderedDict([
("llama", "LlamaModel"),
("bert", "BertModel"),
("gpt2", "GPT2Model"),
...
])
MODEL_FOR_CAUSAL_LM_MAPPING_NAMES = OrderedDict([
("llama", "LlamaForCausalLM"),
("bert", "BertForMaskedLM"),
("gpt2", "GPT2LMHeadModel"),
...
])
还有 MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES、MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES 等多种任务的映射表。
自动类工厂
auto_factory.py 中的 _BaseAutoModelClass 是 AutoModel 的元类,_LazyAutoMapping 实现了延迟加载——只有真正使用模型时才导入对应的模型模块:
class _LazyAutoMapping(OrderedDict):
def __getitem__(self, key):
value = super().__getitem__(key)
# 延迟导入:当访问映射值时,才动态导入对应的模型类
module_name = model_type_to_module_name(key)
return dynamic_import(module_name, value)
from_pretrained 的自动路由
当调用 AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-hf") 时:
- 下载
config.json,读取model_type字段(如"llama") - 在
MODEL_FOR_CAUSAL_LM_MAPPING_NAMES中查找"llama"→"LlamaForCausalLM" - 动态导入
transformers.models.llama.modeling_llama.LlamaForCausalLM - 调用
LlamaForCausalLM.from_pretrained(...)完成加载
2.5 模型实现:以 LLaMA 为例
LLaMA 模型的文件组织:
models/llama/
├── __init__.py # 导出 LlamaConfig, LlamaModel, LlamaForCausalLM 等
├── configuration_llama.py # LlamaConfig 配置类
├── modeling_llama.py # 模型实现
└── tokenization_llama.py # 分词器
LlamaConfig
# configuration_llama.py
@strict
class LlamaConfig(PreTrainedConfig):
model_type = "llama"
vocab_size: int = 32000 # 词表大小
hidden_size: int = 4096 # 隐藏层维度
intermediate_size: int = 11008 # FFN 中间层维度
num_hidden_layers: int = 32 # Transformer 层数
num_attention_heads: int = 32 # 注意力头数
num_key_value_heads: int | None = None # GQA 的 KV 头数
hidden_act: str = "silu" # 激活函数
max_position_embeddings: int = 2048 # 最大位置编码长度
rms_norm_eps: float = 1e-6 # RMSNorm epsilon
rope_parameters: RopeParameters | dict | None = None # RoPE 参数
attention_bias: bool = False # 注意力是否使用偏置
mlp_bias: bool = False # MLP 是否使用偏置
def __post_init__(self, **kwargs):
if self.head_dim is None:
self.head_dim = self.hidden_size // self.num_attention_heads
if self.num_key_value_heads is None:
self.num_key_value_heads = self.num_attention_heads # 默认 MHA
LlamaModel(基座模型)
# modeling_llama.py
class LlamaModel(LlamaPreTrainedModel):
def __init__(self, config: LlamaConfig):
super().__init__(config)
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
self.layers = nn.ModuleList(
[LlamaDecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
)
self.norm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
self.rotary_emb = LlamaRotaryEmbedding(config=config)
LlamaForCausalLM(因果语言模型)
class LlamaForCausalLM(LlamaPreTrainedModel, GenerationMixin):
_tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"} # 权重绑定
def __init__(self, config):
super().__init__(config)
self.model = LlamaModel(config) # 基座模型
self.vocab_size = config.vocab_size
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) # 语言模型头
LlamaAttention(注意力层)
class LlamaAttention(nn.Module):
def __init__(self, config: LlamaConfig, layer_idx: int):
self.head_dim = config.hidden_size // config.num_attention_heads
self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads # GQA 分组
self.scaling = self.head_dim ** -0.5 # 缩放因子
self.q_proj = nn.Linear(config.hidden_size, config.num_attention_heads * self.head_dim, bias=False)
self.k_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
self.v_proj = nn.Linear(config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False)
self.o_proj = nn.Linear(config.num_attention_heads * self.head_dim, config.hidden_size, bias=False)
注意 GQA(Grouped-Query Attention)的实现:k_proj 和 v_proj 的输出维度是 num_key_value_heads * head_dim,通过 repeat_kv() 函数将 KV 头复制到与 Q 头数匹配。
LlamaDecoderLayer(解码器层)
class LlamaDecoderLayer(GradientCheckpointingLayer):
def __init__(self, config: LlamaConfig, layer_idx: int):
self.self_attn = LlamaAttention(config=config, layer_idx=layer_idx)
self.mlp = LlamaMLP(config)
self.input_layernorm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
self.post_attention_layernorm = LlamaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
def forward(self, hidden_states, attention_mask, ...):
residual = hidden_states
hidden_states = self.input_layernorm(hidden_states) # Pre-Norm
hidden_states, _ = self.self_attn(hidden_states, ...) # Self-Attention
hidden_states = residual + hidden_states # 残差连接
residual = hidden_states
hidden_states = self.post_attention_layernorm(hidden_states) # Pre-Norm
hidden_states = self.mlp(hidden_states) # FFN
hidden_states = residual + hidden_states # 残差连接
2.6 Pipeline:高级推理管道
Pipeline(定义于 pipelines/base.py:739)提供了端到端的推理接口,将预处理、模型推理、后处理封装为一个调用:
class Pipeline(_ScikitCompat, PushToHubMixin):
"""Pipeline 基类,封装了完整的推理流程"""
Transformers 提供了丰富的 Pipeline 实现:
text-generation:文本生成text-classification:文本分类token-classification:令牌分类(NER)fill-mask:掩码填充automatic-speech-recognition:语音识别image-text-to-text:图文到文本- 等 30+ 种任务
Pipeline 通过 PipelineRegistry(base.py:1323)进行注册和管理。
3. 核心类/函数详细说明
3.1 AutoModelForCausalLM.from_pretrained()
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
pretrained_model_name_or_path, # 模型ID(如 "meta-llama/Llama-2-7b-hf")或本地路径
config=None, # PreTrainedConfig 对象,覆盖自动加载的配置
cache_dir=None, # 模型缓存目录
force_download=False, # 强制重新下载权重
local_files_only=False, # 仅使用本地缓存,不联网
token=None, # HuggingFace 访问令牌(用于私有模型)
revision="main", # Git 版本(分支名、标签或 commit ID)
torch_dtype="auto", # 权重数据类型
device_map=None, # 设备映射策略
trust_remote_code=False, # 是否执行远程自定义代码
quantization_config=None, # 量化配置(BitsAndBytesConfig等)
attn_implementation=None, # 注意力实现方式
low_cpu_mem_usage=False, # 低CPU内存模式
)
关键参数详解:
| 参数 | 说明 |
|---|---|
torch_dtype | 模型权重数据类型。"auto" 自动检测保存时的 dtype;也可指定 torch.bfloat16、torch.float16 等 |
device_map | 设备映射策略。"auto" 由 accelerate 自动分配;"cuda:0" 全部放 GPU 0;也可传入 dict 手动指定每层设备 |
trust_remote_code | 对于使用自定义代码的模型(如 Qwen、ChatGLM),需设为 True 才能加载 |
quantization_config | 传入 BitsAndBytesConfig 对象进行量化加载 |
attn_implementation | 注意力实现:"eager"(手动实现)、"sdpa"(PyTorch 原生)、"flash_attention_2"(FlashAttention) |
3.2 AutoTokenizer.from_pretrained()
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(
pretrained_model_name_or_path, # 模型ID或本地路径
use_fast=True, # 是否使用 Rust 实现的快速分词器
padding_side=None, # 填充方向:"right" 或 "left"
truncation_side=None, # 截断方向:"right" 或 "left"
trust_remote_code=False, # 是否信任远程代码
token=None, # 访问令牌
revision="main", # 版本
add_prefix_space=None, # 某些分词器需要在文本前加空格
)
关键参数详解:
| 参数 | 说明 |
|---|---|
use_fast | 优先使用基于 Tokenizers 库的 Rust 快速分词器。某些模型只有慢速分词器 |
padding_side | 对于因果语言模型推理,通常设为 "left"(左填充),确保生成从最右侧开始 |
truncation_side | 默认 "right",截断超长文本的右侧 |
3.3 model.generate()
outputs = model.generate(
inputs=None, # 输入张量(input_ids)
generation_config=None, # GenerationConfig 对象
max_new_tokens=128, # 生成的新 token 最大数量
max_length=None, # 输入+输出的总最大长度
temperature=1.0, # 采样温度
top_p=1.0, # 核采样概率阈值
top_k=50, # Top-K 采样的 K 值
do_sample=False, # 是否启用随机采样
repetition_penalty=1.0, # 重复惩罚系数
num_beams=1, # 束搜索的束数
num_return_sequences=1, # 返回序列数
no_repeat_ngram_size=0, # 禁止重复的 n-gram 大小
early_stopping=False, # 束搜索是否提前停止
streamer=None, # 流式输出对象(TextStreamer等)
assistant_model=None, # 辅助模型(用于推测解码)
pad_token_id=None, # 填充 token ID
eos_token_id=None, # 结束 token ID
)
关键参数详解:
| 参数 | 说明 |
|---|---|
max_new_tokens | 生成的新 token 数量上限(不包含输入 prompt) |
temperature | 控制随机性。< 1.0 更确定,> 1.0 更随机,0 等价于贪心 |
top_p | 核采样:从概率累积达 top_p 的最小 token 集中采样 |
top_k | Top-K 采样:只从概率最高的 K 个 token 中采样 |
do_sample | False 使用贪心解码;True 启用随机采样 |
repetition_penalty | > 1.0 惩罚重复 token,1.0 无惩罚 |
num_beams | > 1 启用束搜索,提高生成质量但降低速度 |
3.4 Trainer
from transformers import Trainer
trainer = Trainer(
model=None, # PreTrainedModel 模型
args=None, # TrainingArguments 训练参数
data_collator=None, # DataCollator 数据整理器
train_dataset=None, # 训练数据集
eval_dataset=None, # 评估数据集
processing_class=None, # 分词器或处理器
model_init=None, # 模型初始化函数
compute_metrics=None, # 评估指标计算函数
callbacks=None, # 训练回调列表
optimizers=(None, None), # (优化器, 学习率调度器)
preprocess_logits_for_metrics=None, # 指标计算的 logits 预处理
)
核心方法:
| 方法 | 说明 |
|---|---|
trainer.train() | 启动训练循环 |
trainer.evaluate() | 在 eval_dataset 上评估 |
trainer.predict(test_dataset) | 在测试集上预测 |
trainer.save_model(output_dir) | 保存模型到指定目录 |
trainer.push_to_hub() | 推送模型到 HuggingFace Hub |
3.5 TrainingArguments
from transformers import TrainingArguments
training_args = TrainingArguments(
output_dir="./results", # 输出目录
num_train_epochs=3, # 训练轮数
per_device_train_batch_size=8, # 每设备训练批次大小
per_device_eval_batch_size=8, # 每设备评估批次大小
gradient_accumulation_steps=1, # 梯度累积步数
learning_rate=5e-5, # 学习率
weight_decay=0.01, # 权重衰减
warmup_steps=0, # 学习率预热步数
warmup_ratio=0.0, # 学习率预热比例
lr_scheduler_type="linear", # 学习率调度类型
fp16=False, # 是否使用 FP16 混合精度
bf16=False, # 是否使用 BF16 混合精度
logging_steps=500, # 日志记录步数间隔
eval_strategy="no", # 评估策略:"no"/"steps"/"epoch"
eval_steps=None, # 评估步数间隔
save_strategy="steps", # 保存策略:"no"/"steps"/"epoch"
save_steps=500, # 保存步数间隔
save_total_limit=None, # 最多保存的检查点数
load_best_model_at_end=False, # 训练结束时加载最佳模型
metric_for_best_model=None, # 最佳模型的评估指标
greater_is_better=None, # 指标是否越大越好
gradient_checkpointing=False, # 是否启用梯度检查点
dataloader_num_workers=0, # DataLoader 工作进程数
remove_unused_columns=True, # 是否移除模型不需要的列
report_to=None, # 实验跟踪后端:"wandb"/"tensorboard"等
seed=42, # 随机种子
data_seed=None, # 数据加载随机种子
push_to_hub=False, # 训练结束后推送模型
)
3.6 DataCollator
DataCollatorForLanguageModeling
用于因果语言模型(CLM)和掩码语言模型(MLM)训练:
from transformers import DataCollatorForLanguageModeling
collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm=False, # False=因果语言模型, True=掩码语言模型
mlm_probability=0.15, # MLM 掩码概率(仅 mlm=True 时有效)
pad_to_multiple_of=None, # 填充到某数的倍数(如 8,用于 Tensor Core 加速)
)
当 mlm=False 时,labels 等于 input_ids,padding 位置设为 -100(忽略)。这是 LLM 微调的常用配置。
DataCollatorForSeq2Seq
用于序列到序列(Seq2Seq)模型训练:
from transformers import DataCollatorForSeq2Seq
collator = DataCollatorForSeq2Seq(
tokenizer=tokenizer,
model=None, # 模型(用于准备 decoder_input_ids)
padding=True, # 填充策略
max_length=None, # 最大长度
pad_to_multiple_of=None, # 填充到某数的倍数
label_pad_token_id=-100, # 标签填充值(-100 在损失计算中被忽略)
return_tensors="pt", # 返回张量类型
)
与 DataCollatorForLanguageModeling 的关键区别:同时对 labels 进行填充。
3.7 BitsAndBytesConfig
from transformers import BitsAndBytesConfig
# 4-bit 量化配置(最常用)
bnb_config_4bit = BitsAndBytesConfig(
load_in_4bit=True, # 启用 4-bit 量化
bnb_4bit_quant_type="nf4", # 量化类型:"fp4" 或 "nf4"(推荐)
bnb_4bit_compute_dtype=torch.bfloat16, # 计算时的数据类型
bnb_4bit_use_double_quant=True, # 是否使用双重量化(量化常量再次量化)
bnb_4bit_quant_storage=torch.uint8, # 量化参数的存储类型
)
# 8-bit 量化配置
bnb_config_8bit = BitsAndBytesConfig(
load_in_8bit=True, # 启用 8-bit 量化
llm_int8_threshold=6.0, # 离群值检测阈值
llm_int8_skip_modules=None, # 跳过量化的模块列表
llm_int8_enable_fp32_cpu_offload=False, # 是否启用 FP32 CPU 卸载
llm_int8_has_fp16_weight=False, # 是否使用 FP16 主权重(微调时有用)
)
NF4(NormalFloat4)量化的优势:假设权重服从正态分布,使用信息论最优的量化级别,在 4-bit 下保持更好的精度。双重量化可以进一步节省约 0.4 bit/param 的显存。
4. 数学原理
4.1 Transformer 架构:Self-Attention 公式
Self-Attention 是 Transformer 的核心操作:
\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V\]其中:
- $Q$(Query)、$K$(Key)、$V$(Value)分别是输入经线性变换得到的矩阵
- $d_k$ 是 Key 的维度,除以 $\sqrt{d_k}$ 防止点积值过大导致 softmax 梯度消失
- softmax 沿最后一个维度计算,使注意力权重和为 1
多头注意力(Multi-Head Attention):
\[\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O\] \[\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)\]分组查询注意力(GQA):LLaMA 2/3 使用 GQA,多个 Q 头共享一组 K/V 头。设 GQA 分组数为 $g$:
\[\text{head}_i^{K} = \text{head}_{\lfloor i/g \rfloor}^{K}, \quad \text{head}_i^{V} = \text{head}_{\lfloor i/g \rfloor}^{V}\]这显著减少了 KV Cache 的显存占用。
4.2 因果语言模型的训练目标
因果语言模型(Causal LM)使用自回归交叉熵损失:
\[L(\theta) = -\sum_{t=1}^{T} \log P(x_t \mid x_{<t}; \theta)\]其中:
- $x_t$ 是第 $t$ 个 token
- $x_{<t}$ 是第 $t$ 个 token 之前的所有 token
- $\theta$ 是模型参数
- 训练目标是最大化训练数据的对数似然
在源码中(LlamaForCausalLM.forward()),损失通过 self.loss_function() 计算,标签中 label == -100 的位置被忽略(通常是 padding token)。
4.3 KV Cache 原理
在自回归生成中,每生成一个新 token,都需要计算它与之前所有 token 的注意力。如果不使用 KV Cache,每次都需要重新计算前面所有 token 的 K 和 V,这会导致 $O(n^2)$ 的重复计算。
KV Cache 的核心思想:缓存已经计算过的 Key 和 Value,每步只需计算新 token 的 Q/K/V,然后将新的 K/V 追加到缓存中:
第 t 步:
1. 计算新 token 的 q_t, k_t, v_t
2. 将 k_t, v_t 追加到 Cache: K = [k_1, ..., k_t], V = [v_1, ..., v_t]
3. 计算 attention: attn_t = softmax(q_t @ K^T / sqrt(d_k)) @ V
Transformers 中的 Cache 实现(cache_utils.py):
- DynamicCache:动态增长的缓存,支持任意序列长度
- StaticCache:预分配固定大小的缓存,适合编译优化
- QuantizedCache:对缓存进行量化以减少显存
- EncoderDecoderCache:编码器-解码器模型的缓存
在源码中(LlamaAttention.forward()),KV Cache 通过 past_key_values.update(key_states, value_states, layer_idx) 更新:
if past_key_values is not None:
key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)
KV Cache 的显存占用公式:2 × num_layers × batch_size × seq_len × num_kv_heads × head_dim × dtype_size
5. 代码原理
5.1 from_pretrained 的加载流程
from_pretrained(model_id)
│
├─ 1. 解析路径/模型ID
│ cached_file() → 下载 config.json
│
├─ 2. 加载配置
│ config = PretrainedConfig.from_pretrained(model_id)
│ → 从 config.json 反序列化为 Config 对象
│
├─ 3. 确定模型类(AutoModel 场景)
│ model_type = config.model_type # 如 "llama"
│ → 查找 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES["llama"] = "LlamaForCausalLM"
│ → 动态导入 transformers.models.llama.modeling_llama.LlamaForCausalLM
│
├─ 4. 初始化模型骨架
│ model = LlamaForCausalLM(config)
│ → __init__() 创建所有层(embed_tokens, decoder_layers, lm_head)
│ → post_init() 初始化权重
│
├─ 5. 加载权重
│ ├─ 查找权重文件:优先 safetensors,回退 pytorch_model.bin
│ ├─ 分片加载:如果存在 model.safetensors.index.json
│ │ → get_checkpoint_shard_files() 按分片加载
│ ├─ load_state_dict() → 使用 safetensors.safe_open() 或 torch.load()
│ └─ model.load_state_dict(state_dict) → 将权重注入模型
│
├─ 6. 处理量化
│ if quantization_config:
│ → get_hf_quantizer() 获取量化器
│ → 量化器在加载时替换 Linear 层为量化层
│
├─ 7. 处理设备映射
│ if device_map:
│ → _get_device_map() 计算最优设备映射
│ → accelerate_dispatch() 将各层分配到不同设备
│
└─ 8. 设置为评估模式
model.eval()
→ 关闭 Dropout、BatchNorm 使用运行统计
5.2 generate 的解码流程
model.generate(input_ids, **kwargs)
│
├─ 1. 准备生成配置
│ generation_config = _prepare_generation_config(...)
│ → 合并默认配置、模型配置和用户参数
│
├─ 2. 确定生成模式
│ → 根据 do_sample, num_beams 等确定模式:
│ GREEDY_SEARCH / SAMPLE / BEAM_SEARCH / BEAM_SAMPLE / ASSISTED_GENERATION
│
├─ 3. 准备输入
│ → 编码输入(如果需要)
│ → 创建 attention_mask
│ → 初始化 KV Cache: DynamicCache()
│
├─ 4. 准备 LogitsProcessor 和 StoppingCriteria
│ → TemperatureLogitsWarper (temperature)
│ → TopPLogitsWarper (top_p)
│ → TopKLogitsWarper (top_k)
│ → RepetitionPenaltyLogitsProcessor (repetition_penalty)
│ → MinLengthLogitsProcessor, MinNewTokensLengthLogitsProcessor
│ → MaxLengthCriteria, EosTokenCriteria
│
├─ 5. 解码循环
│ while not stopping_criteria:
│ │
│ ├─ 前向传播: model.forward(input_ids, past_key_values=cache)
│ │ → 返回 logits 和更新的 KV Cache
│ │
│ ├─ Logits 处理: logits_processor(input_ids, logits)
│ │ → 应用 temperature, top_p, top_k 等
│ │
│ ├─ 采样下一个 token:
│ │ ├─ 贪心: next_token = argmax(logits)
│ │ ├─ 采样: next_token = multinomial(softmax(logits))
│ │ └─ 束搜索: 更新多个候选序列
│ │
│ ├─ 追加 token: input_ids = cat([input_ids, next_token])
│ │
│ └─ 检查停止条件:
│ ├─ 达到 max_new_tokens
│ ├─ 生成 eos_token_id
│ └─ 达到最大时间限制
│
└─ 6. 返回结果
→ GenerateDecoderOnlyOutput (return_dict_in_generate=True)
→ 或 torch.LongTensor (默认)
5.3 Trainer 的训练循环
trainer.train()
│
├─ 1. 初始化
│ ├─ 准备模型: model = self._wrap_model(self.model)
│ ├─ 准备优化器: optimizer = self.create_optimizer()
│ ├─ 准备调度器: lr_scheduler = self.create_scheduler()
│ └─ accelerate 加速器设置: self.accelerator.prepare(model, optimizer, ...)
│
├─ 2. 数据加载
│ ├─ train_dataloader = self.get_train_dataloader()
│ │ → DataLoader(train_dataset, batch_size, collate_fn=data_collator, ...)
│ └─ eval_dataloader = self.get_eval_dataloader()
│
├─ 3. 训练循环(每个 epoch)
│ for epoch in range(num_train_epochs):
│ │
│ ├─ 前向传播:
│ │ outputs = model(**inputs)
│ │ loss = outputs.loss # 模型内部计算交叉熵
│ │
│ ├─ 梯度累积控制:
│ │ loss = loss / gradient_accumulation_steps
│ │
│ ├─ 反向传播:
│ │ self.accelerator.backward(loss) # 支持混合精度
│ │
│ ├─ 梯度裁剪:
│ │ self.accelerator.clip_grad_norm_(model, max_grad_norm)
│ │
│ ├─ 优化器步骤(每 gradient_accumulation_steps 步):
│ │ optimizer.step()
│ │ lr_scheduler.step() # 更新学习率
│ │ optimizer.zero_grad()
│ │
│ ├─ 日志记录:
│ │ logging_loss, 训练速度, 学习率等
│ │
│ ├─ 评估(按策略):
│ │ if should_eval: trainer.evaluate()
│ │
│ └─ 保存检查点(按策略):
│ if should_save: self._save_checkpoint()
│
└─ 4. 训练结束
├─ 加载最佳模型(如果 load_best_model_at_end=True)
└─ 推送到 Hub(如果 push_to_hub=True)
6. 在 LLM 开发中的典型使用场景和代码示例
6.1 加载预训练模型进行推理
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# 加载模型和分词器
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16, # 使用 BF16 精度
device_map="auto", # 自动分配到可用 GPU
)
# 准备输入
prompt = "The future of artificial intelligence is"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
# 生成
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=128, # 最多生成 128 个新 token
temperature=0.7, # 较低温度,更确定的输出
top_p=0.9, # 核采样
do_sample=True, # 启用采样
repetition_penalty=1.1, # 轻微重复惩罚
)
# 解码输出
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(response)
6.2 使用 Trainer 微调模型
import torch
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
Trainer,
DataCollatorForLanguageModeling,
)
from datasets import load_dataset
# 加载模型和分词器
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token # 设置 pad_token
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
)
# 准备数据集
dataset = load_dataset("tatsu-lab/alpaca", split="train")
def tokenize_function(examples):
# 将指令和输出拼接为训练文本
texts = [f"### Instruction:\n{inst}\n### Response:\n{out}"
for inst, out in zip(examples["instruction"], examples["output"])]
return tokenizer(texts, truncation=True, max_length=512)
tokenized_dataset = dataset.map(tokenize_function, batched=True, remove_columns=dataset.column_names)
# 数据整理器(因果语言模型模式)
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
# 训练参数
training_args = TrainingArguments(
output_dir="./llama-finetuned",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # 等效 batch_size=16
learning_rate=2e-5,
lr_scheduler_type="cosine", # 余弦退火
warmup_ratio=0.03, # 预热 3% 的步数
bf16=True, # BF16 混合精度训练
logging_steps=10,
save_strategy="epoch",
gradient_checkpointing=True, # 节省显存
report_to="tensorboard",
)
# 创建 Trainer
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=data_collator,
)
# 开始训练
trainer.train()
# 保存模型
trainer.save_model("./llama-finetuned/final")
6.3 使用 Pipeline 快速推理
from transformers import pipeline
# 创建文本生成管道
generator = pipeline(
"text-generation",
model="meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
device_map="auto",
)
# 单条推理
result = generator(
"Explain quantum computing in simple terms:",
max_new_tokens=200,
temperature=0.7,
do_sample=True,
)
print(result[0]["generated_text"])
# 批量推理
prompts = [
"What is machine learning?",
"Explain the concept of neural networks.",
"What are transformers in AI?",
]
results = generator(prompts, max_new_tokens=100, batch_size=3)
6.4 量化加载大模型
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
# 4-bit 量化配置(NF4 + 双重量化)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4", # NormalFloat4 量化
bnb_4bit_compute_dtype=torch.bfloat16, # 计算时使用 BF16
bnb_4bit_use_double_quant=True, # 双重量化节省显存
)
# 加载模型
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
)
# 显存占用:7B 模型从 ~14GB (FP16) 降至 ~4GB (4-bit)
# 推理
inputs = tokenizer("Hello, how are you?", return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
# 8-bit 量化(更高精度,更多显存)
bnb_config_8bit = BitsAndBytesConfig(load_in_8bit=True)
model_8bit = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config_8bit,
device_map="auto",
)
# 显存占用:~7GB
6.5 分布式训练(device_map=”auto”)
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
# 自动设备映射:accelerate 根据可用 GPU 显存自动分配模型层
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-13b-hf",
torch_dtype=torch.bfloat16,
device_map="auto", # 自动跨 GPU 分配
max_memory={ # 可选:指定每设备最大显存
0: "24GiB",
1: "24GiB",
"cpu": "30GiB",
},
)
# 查看设备映射
print(model.hf_device_map)
# 输出示例: {'model.embed_tokens': 0, 'model.layers.0-15': 0, 'model.layers.16-31': 1, ...}
# 手动指定设备映射
device_map = {
"model.embed_tokens": 0,
"model.layers": 1,
"model.norm": 1,
"lm_head": 1,
}
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
device_map=device_map,
)
# 张量并行(需要 torchrun 启动)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-hf",
torch_dtype=torch.bfloat16,
tp_plan="auto", # 使用预定义的张量并行计划
tp_size=2, # 2 路张量并行
)
# 启动: torchrun --nproc_per_node=2 script.py
7. 常见注意事项和最佳实践
7.1 显存管理
- 优先使用 BF16:在 Ampere+ GPU(A100、H100、RTX 30/40 系列)上,
torch_dtype=torch.bfloat16比float16更稳定(无溢出风险) - 梯度检查点:设置
gradient_checkpointing=True以时间换空间,可减少约 60-70% 的训练显存 - 梯度累积:当单卡 batch_size 受限时,使用
gradient_accumulation_steps模拟大 batch_size - 量化微调:4-bit 量化 + LoRA(PEFT)可以在单卡上微调 70B 模型
7.2 模型加载
- 优先使用 safetensors:比
pytorch_model.bin更安全(无 pickle 风险)且加载更快 - 始终指定
torch_dtype:默认 FP32 会浪费显存,大多数 LLM 训练时使用 BF16/FP16 trust_remote_code的风险:仅在可信来源设为True,它会执行 Hub 上的自定义 Python 代码low_cpu_mem_usage=True:大模型加载时减少 CPU 内存峰值
7.3 生成策略
- 贪心 vs 采样:事实性任务用贪心(
do_sample=False),创意性任务用采样(do_sample=True) - temperature 设置:
0.1-0.3适合代码/事实,0.7-1.0适合对话/创意 - 重复惩罚:
repetition_penalty=1.0-1.2适度惩罚,过高会导致输出不自然 - max_new_tokens vs max_length:优先使用
max_new_tokens,max_length包含 prompt 长度容易出错
7.4 训练最佳实践
- 学习率:全量微调通常
1e-5到5e-5;LoRA 微调可更高1e-4到1e-3 - 学习率调度:
cosine配合warmup_ratio=0.03-0.1是常用组合 - 数据预处理:确保
pad_token已设置;对于因果 LM,使用DataCollatorForLanguageModeling(mlm=False) - 保存策略:使用
save_total_limit=2-3避免磁盘被检查点占满 - 评估策略:设置
eval_strategy="steps"+eval_steps+load_best_model_at_end=True
7.5 注意力实现选择
- SDPA(默认):PyTorch 2.0+ 的
F.scaled_dot_product_attention,无需额外安装,性能好 - FlashAttention 2:需要安装
flash-attn,在长序列上性能最优,但仅支持 Ampere+ GPU - Eager:手动实现,速度最慢,但兼容性最好,适合调试
7.6 常见陷阱
- 忘记设置
model.eval():from_pretrained()返回的模型已在 eval 模式,但手动创建的模型需要调用 - tokenizer 的 padding_side:生成任务应设为
"left",否则右填充会导致输出包含 pad token - labels 中的 -100:在计算损失时,
-100对应的位置会被忽略,用于 mask padding - 权重绑定:如 LLaMA 的
lm_head.weight与embed_tokens.weight共享,保存时只保存一份 - device_map 与多 GPU 训练:
device_map="auto"用于推理,训练时应使用 FSDP 或 DeepSpeed
7.7 与 PEFT/LoRA 配合
from peft import LoraConfig, get_peft_model, TaskType
# LoRA 配置
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16, # LoRA 秩
lora_alpha=32, # LoRA 缩放因子
lora_dropout=0.05, # LoRA Dropout
target_modules=["q_proj", "v_proj"], # 应用 LoRA 的目标模块
)
# 加载模型并应用 LoRA
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
quantization_config=bnb_config, # 可配合 4-bit 量化
)
model = get_peft_model(model, lora_config)
print(f"可训练参数: {model.print_trainable_parameters()}")
# 可训练参数通常仅占全部参数的 0.1%-1%
7.8 流式生成
from transformers import TextStreamer
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
outputs = model.generate(
**inputs,
max_new_tokens=256,
streamer=streamer, # 流式输出
temperature=0.7,
do_sample=True,
)
# 输出会实时逐 token 打印
附录:关键源码位置索引
| 功能 | 文件 | 行号/类名 |
|---|---|---|
| PreTrainedModel 基类 | modeling_utils.py | class PreTrainedModel (L1151) |
| from_pretrained() | modeling_utils.py | def from_pretrained() (L3766) |
| load_state_dict() | modeling_utils.py | def load_state_dict() (L333) |
| PretrainedConfig 基类 | configuration_utils.py | class PreTrainedConfig (L123) |
| AutoModel 映射表 | auto/modeling_auto.py | MODEL_MAPPING_NAMES 等 |
| AutoModel 工厂 | auto/auto_factory.py | _BaseAutoModelClass, _LazyAutoMapping |
| generate() 方法 | generation/utils.py | def generate() (L2153) |
| LogitsProcessor | generation/logits_process.py | TemperatureLogitsWarper 等 |
| Trainer 类 | trainer.py | class Trainer (L255) |
| TrainingArguments | training_args.py | class TrainingArguments |
| DataCollatorForLanguageModeling | data/data_collator.py | class DataCollatorForLanguageModeling (L619) |
| DataCollatorForSeq2Seq | data/data_collator.py | class DataCollatorForSeq2Seq (L487) |
| BitsAndBytesConfig | utils/quantization_config.py | class BitsAndBytesConfig (L389) |
| LlamaConfig | models/llama/configuration_llama.py | class LlamaConfig |
| LlamaForCausalLM | models/llama/modeling_llama.py | class LlamaForCausalLM |
| LlamaAttention | models/llama/modeling_llama.py | class LlamaAttention |
| DynamicCache | cache_utils.py | class DynamicCache |
| Pipeline 基类 | pipelines/base.py | class Pipeline (L739) |
