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 等

架构关系说明

  1. PretrainedConfig → 定义模型配置(层数、维度等),被 PreTrainedModel 持有
  2. PreTrainedModel → 所有模型的基类,提供加载/保存/推理框架,各具体模型继承它
  3. AutoModel → 根据 config.json 中的 model_type 查找映射表,自动选择正确的模型类
  4. GenerationMixin → 混入 PreTrainedModel,提供 generate() 方法
  5. Trainer → 组合使用 PreTrainedModel + TrainingArguments + DataCollator 完成训练

2.2 PreTrainedModel 基类

PreTrainedModel(定义于 modeling_utils.py:1151)是所有模型的基类,继承了 nn.ModulePushToHubMixinPeftAdapterMixin 等:

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),核心流程如下:

  1. 下载/定位模型文件:通过 cached_file() 从 HuggingFace Hub 下载或从本地路径加载 config.json 和权重文件
  2. 加载配置:调用 PretrainedConfig.from_pretrained() 加载配置对象
  3. 确定模型类:如果是 AutoModel,根据 config.model_type 查找映射表确定具体的模型类
  4. 初始化模型:使用 config 初始化模型骨架(__init__
  5. 加载权重
    • 优先使用 safetensors 格式(load_state_dict() 函数,modeling_utils.py:333
    • 对于分片权重,使用 get_checkpoint_shard_files() 加载索引文件
    • 支持 bitsandbytes 量化加载
  6. 分配设备:根据 device_map 将模型各层分配到不同设备(通过 accelerate 库)
  7. 设置为评估模式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() 的逻辑

  1. 保存 config.json 到指定目录
  2. 将模型权重保存为 safetensors 格式(优先)或 pytorch bin 格式
  3. 对于大模型,自动分片保存
  4. 处理权重绑定(tied weights),避免重复保存

push_to_hub() 的逻辑

  1. 调用 save_pretrained() 将模型保存到临时目录
  2. 使用 huggingface_hub 库上传到 HuggingFace Hub
  3. 自动创建/更新 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_sizerms_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_NAMESMODEL_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") 时:

  1. 下载 config.json,读取 model_type 字段(如 "llama"
  2. MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 中查找 "llama""LlamaForCausalLM"
  3. 动态导入 transformers.models.llama.modeling_llama.LlamaForCausalLM
  4. 调用 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_projv_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 通过 PipelineRegistrybase.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.bfloat16torch.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_kTop-K 采样:只从概率最高的 K 个 token 中采样
do_sampleFalse 使用贪心解码;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.bfloat16float16 更稳定(无溢出风险)
  • 梯度检查点:设置 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_tokensmax_length 包含 prompt 长度容易出错

7.4 训练最佳实践

  • 学习率:全量微调通常 1e-55e-5;LoRA 微调可更高 1e-41e-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.weightembed_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.pyclass PreTrainedModel (L1151)
from_pretrained()modeling_utils.pydef from_pretrained() (L3766)
load_state_dict()modeling_utils.pydef load_state_dict() (L333)
PretrainedConfig 基类configuration_utils.pyclass PreTrainedConfig (L123)
AutoModel 映射表auto/modeling_auto.pyMODEL_MAPPING_NAMES
AutoModel 工厂auto/auto_factory.py_BaseAutoModelClass, _LazyAutoMapping
generate() 方法generation/utils.pydef generate() (L2153)
LogitsProcessorgeneration/logits_process.pyTemperatureLogitsWarper
Trainer 类trainer.pyclass Trainer (L255)
TrainingArgumentstraining_args.pyclass TrainingArguments
DataCollatorForLanguageModelingdata/data_collator.pyclass DataCollatorForLanguageModeling (L619)
DataCollatorForSeq2Seqdata/data_collator.pyclass DataCollatorForSeq2Seq (L487)
BitsAndBytesConfigutils/quantization_config.pyclass BitsAndBytesConfig (L389)
LlamaConfigmodels/llama/configuration_llama.pyclass LlamaConfig
LlamaForCausalLMmodels/llama/modeling_llama.pyclass LlamaForCausalLM
LlamaAttentionmodels/llama/modeling_llama.pyclass LlamaAttention
DynamicCachecache_utils.pyclass DynamicCache
Pipeline 基类pipelines/base.pyclass Pipeline (L739)