跳到主要内容

数据截至 (上游 commit b6c0bfe04c82)

Transformers — 架构与原理

30 秒导读: Hugging Face Transformers 是 transformer 模型时代的「标准图书馆 + 运行时」:507 个架构目录各装一份能单独读懂的参考实现(models/<name>/modeling_<name>.py),上面架着共享的三根梁——PreTrainedModel.from_pretrained 负责把 Hub 上的权重正确装进模型、generate() 负责自回归解码、Trainer 负责训练——再用 AutoModel 一行代码按 config.json 里的 model_type 把 507 选 1 的路由做掉。整个开源 LLM 生态的 checkpoint 格式(config.json + safetensors)就是从这个库长出来的事实标准。本文基于 v5.16.0.dev0(v5 大版本,含不兼容重构)。


1. 这是什么(零基础也能懂)

一句话定义

Transformers 是一个 PyTorch 模型库 + 配套运行时:它把论文里的每个 transformer 架构用统一风格的 Python 类实现出来,并给所有模型同一套「加载预训练权重 → 前向/生成 → 微调 → 保存回 Hub」的工作流。

它要解决谁的什么问题

设想你拿到一篇新论文(比如一个新 MoE 架构),或者拿到 Hub 上别人训好的 70B 权重:

  • 你想直接用:得有正确实现 + 正确的权重加载,权重键名、dtype、分片、量化一个都不能错。
  • 你想改着用:得读得懂这份实现,最好能拷出来改,不牵一发动全身。
  • 你想训着用:得有训练回路,处理分布式、混精度、梯度累积、checkpoint 恢复。

这三件事的难度都在「每换一个架构就要重来一遍」。Transformers 把重复的部分抽成基类,把不同的部分留在每个模型自己的文件里。

它能做什么

能力具体形态
架构参考实现src/transformers/models/ 下 507 个目录,每目录一个(基本)自包含的 modeling_*.py
权重加载/保存from_pretrained / save_pretrained,safetensors 分片、量化、device_map、张量并行
文本生成generate():贪心、采样、beam search、投机解码,带 KV cache
训练Trainer + TrainingArguments,底层委托 accelerate,支持 FSDP/DeepSpeed
任务封装pipeline("text-generation", model=...) 三行跑通一个任务
生态契约config.json / generation_config.json / safetensors 分片格式,被 vLLM、PEFT 等全线读取

用起来什么样

# 生态里最经典的三行(示意,非源码)
from transformers import AutoModelForCausalLM, AutoTokenizer

tok = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B", device_map="auto")
out = model.generate(**tok("你好", return_tensors="pt"), max_new_tokens=64)

AutoTokenizerAutoModelForCausalLM 都不知道 Llama 是什么——它们读 config.json 里的 model_type: "llama",查表路由到 LlamaForCausalLM(见第 6 章)。训练侧对应的最小形态是:包一个 Trainer(model, args, train_dataset=...) 然后 trainer.train()(见第 5 章)。

一句话直觉

把它想成「硬件行业的 ATX 标准 + 主板说明书库」。 config.json/safetensors 是接口标准(谁都能读写),每个 modeling_*.py 是一块主板的说明书(单独可读),PreTrainedModel/generate/Trainer 是所有主板共享的电源、总线和 BIOS。


2. 顶层全景(它大概怎么转)

2.1 分层结构

库内部可以看成五层,上层只依赖下层的公共接口:

┌─────────────────────────────────────────────────────────────┐
│ 任务层 pipeline("text-generation", ...) │
│ pipelines/base.py:753 (Pipeline) │
├─────────────────────────────────────────────────────────────┤
│ 路由层 AutoModelForCausalLM / AutoConfig / AutoTokenizer │
│ models/auto/ (按 model_type 查表,懒加载) │
├──────────────────┬───────────────────────┬──────────────────┤
│ 推理引擎 │ 权重装载 │ 训练引擎 │
│ GenerationMixin │ PreTrainedModel │ Trainer │
│ generation/utils │ modeling_utils.py │ trainer.py │
│ .generate() │ .from_pretrained() │ .train() │
├──────────────────┴───────────────────────┴──────────────────┤
│ 模型层 507 个 models/<arch>/modeling_<arch>.py │
│ 每个文件自带 Attention/MLP/DecoderLayer/Model 全套 │
├─────────────────────────────────────────────────────────────┤
│ 基设层 KV cache 抽象(cache_utils.py) · 注意力后端注册表 │
│ (modeling_utils.py ALL_ATTENTION_FUNCTIONS) · 掩码 │
│ 工具(masking_utils.py) · 配置基类(configuration_utils)│
└─────────────────────────────────────────────────────────────┘

怎么读这张图: 垂直方向是依赖方向(下不依赖上);中间三个引擎是并列的,都坐在同一批模型类上。Trainergenerate 不互相调用——训练走 forward,生成走 GenerationMixin

2.2 部件职责

部件干什么在哪个文件
Pipeline任务级封装:preprocess → forward → postprocesspipelines/base.py:753(run_single:1297)
AutoModelForCausalLM按 config 的 model_type 选模型类,再调它的 from_pretrainedmodels/auto/auto_factory.py:194:261
AutoConfig读 config.json → 实例化对应 PreTrainedConfig 子类models/auto/configuration_auto.py:303
PreTrainedModel所有模型的基类:加载、保存、权重绑定、device_map、量化钩子modeling_utils.py:1180
GenerationMixin混入模型类的生成能力:generate() 与各种解码循环generation/utils.py:359
Cache / DynamicCacheKV cache 容器:一个模型一个 Cache,每层一个 Layercache_utils.py:1269:1737
ALL_ATTENTION_FUNCTIONS注意力后端注册表:eager/sdpa/flash/flex 按名取函数modeling_utils.py:5185
Trainer训练编排:数据加载、梯度累积、checkpoint、评测、回调trainer.py:258
TrainingArguments训练的全部超参(dataclass,几百个字段)training_args.py:180
PreTrainedConfig所有配置的基类,序列化为 config.jsonconfiguration_utils.py:148
modular_*.py「继承式建模」源文件:用 Python 继承从别的架构 diff 出新架构models/qwen3/modular_qwen3.py(287 个)

2.3 主线走一遍(推理)

一次 model.generate(**inputs, max_new_tokens=64) 经过的部件,高层不进代码:

① 路由 AutoModelForCausalLM.from_pretrained 已把模型类选好、权重装好(第 2、6 章)

② 定模式 GenerationConfig.get_generation_mode():看 num_beams/do_sample
决定走采样、beam 还是投机解码(generation/configuration_utils.py:534)

③ 组装处理器 _get_logits_processor():temperature/top_p/重复惩罚…
每个参数变成一个 LogitsProcessor(generation/utils.py:1123)

④ prefill _prefill():整段 prompt 一次 forward,KV 全部进 cache

⑤ 逐 token 解码循环 _sample():
forward 1 个 token → 取最后一位 logits → processor 加工
→ softmax/argmax 或 multinomial 选 token → 拼到 input_ids 尾部
→ stopping_criteria 判断每条序列是否该停(EOS/长度/时间)

⑥ 收尾 按 return_dict_in_generate 决定返回裸 token 还是结构化输出

第 ③~⑤ 步的全部细节见第 3 章,cache 在循环里怎么滚动见第 4 章。

2.4 主线走一遍(训练)

trainer.train() 的高层轮廓(_inner_training_loop,trainer.py:1465):

初始化 set_initial_training_values → _init_training_state(可恢复)
→ _prepare_for_training(建 optimizer/scheduler,accelerator.prepare 包模型)

epoch 循环 for epoch in range(epochs_trained, num_train_epochs) (trainer.py:1538)

step 循环 get_batch_samples(数出 num_items_in_batch)
→ training_step():compute_loss → accelerator.backward(loss)
→ 攒够梯度累积步数 → optimizer.step + scheduler.step
→ CallbackHandler 一路发 on_step_end/on_log/on_save 事件
→ control.should_training_stop? 退出

收尾 _finalize_training:评测、存最终 checkpoint

关键认知:Trainer 自己不做分布式、不做混精度——这些都委托给它内部持有的 accelerate.Accelerator(trainer.py:829 创建)。Trainer 只做「训练流程编排 + 回调事件」。细节见第 5 章。


3. 阅读地图(建议顺序)

六章由浅入深。只想抓要害:读 01 → 03 → 06 就能明白「为什么这个库长这样」。

顺序章节讲什么适合谁
101-model-file-anatomy.md一个 modeling_*.py 的内部结构、modular 继承式生成、注意力注册表想读懂/新增一个架构的人,必读
202-model-loading.mdfrom_pretrained 全流程、v5 权重转换系统、tie_weights被 missing/unexpected keys 警告折磨过的人
303-generation.mdgenerate() 总控与三种解码循环、logits processor 链要改采样行为、加约束解码的人
404-kv-cache.mdCache/Layer 二级抽象与各 variant、offloading关心推理显存与吞吐的人
505-trainer.mdTrainer 的编排哲学、训练循环、compute_loss 细节微调模型的、要自定义 loss 的人
606-auto-config.mdAuto 路由、懒加载映射、trust_remote_code、config.json 契约想理解生态互操作格式的人

4. 巧妙之处(可借鉴的技术)

挑四条最有迁移价值的;各章末尾还有更多。

  1. 「单文件自读」是政策,不是巧合。 卡片里写得很白:模型文件间的刻意重复是设计决策——每个 modeling_*.py 要能单独读懂、单独拷走。为了既保可读又消重复,v5 用 modular 继承系统:作者在 modular_qwen3.py 里写 class Qwen3RMSNorm(Qwen2RMSNorm): pass 这样的 Python 继承 diff,CI 再把它展开生成自包含的 modeling_qwen3.py(文件头第 2 行明示 "automatically generated from ... modular_qwen3.py")。源是 diff,产物是全量——兼得两者。见第 1 章。

  2. 权重加载在 v5 被建成一套「转换算子代数」。 旧 checkpoint 键名不同?QKV 三块要合并?不再是模型里一坨 if,而是声明式的 WeightTransform 算子(Chunk/Concatenate/Transpose/PermuteForRope…,core_model_loading.py:81 起),加载时对 checkpoint 键做模式匹配后统一执行。生态十年积累的「权重格式漂移」被收敛成一个可扩展的算子库。见第 2 章。

  3. 注意力实现是「名字 → 函数」的全局注册表。 模型代码里只有一句 ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward)(models/llama/modeling_llama.py:264-266);eager/sdpa/flash/flex 的切换发生在查表,不在模型代码里。新架构默认获得全部后端,新后端默认覆盖全部架构。 见第 1 章。

  4. 解码行为全部由「组装处理器链」决定,而不是 if-else。 generate() 把 temperature、top_p、重复惩罚等参数各自变成一个 LogitsProcessor,把 EOS/最大长度变成 StoppingCriteria,主循环 _sample 只剩三步:forward → 加工 logits → 选 token(generation/utils.py:2916-2922)。用户想加约束,往链上挂一个处理器即可。见第 3 章。


5. 边界与局限

诚实清单,按「会咬人的程度」排序:

  • 单文件自读 = 海量刻意重复。 507 个目录意味着一个横跨所有模型的 bug 要修几百个文件(或用 modular 重新生成)。读一个文件很舒服,改全库很痛苦。这是明知的取舍。
  • 不是推理引擎。 generate() 追求通用与可读,吞吐被 vLLM/SGLang 这类带连续批处理、paged attention 的服务化引擎碾压(库里有 generation/continuous_batching/,但主线仍以单请求 decode 为主)。要上线服务请出门左转 vllm
  • trust_remote_code 是真执行远端代码。 Auto 体系允许 Hub 仓库自带 Python 建模代码并在你机器上执行(models/auto/configuration_auto.py:389-408 的分支)。方便,但本质是 eval 别人的仓库,只对信得过的源开启。
  • 版本移动极快。 本 commit 是 v5.16.0.dev0,根目录就有 MIGRATION_GUIDE_V5.md;引用行为务必对 commit,跨版本行号必漂。
  • Trainer 是「单机为主、分布式靠 accelerate」。 超大模型的 3D 并行(TP/PP 手工切)不是 Trainer 的主场;它接 DeepSpeed/FSDP 是通过配置透传,控制力不如专用框架。

6. 横向对比

同书架兄弟库与 transformers 的关系,多数是「读它的格式」或「替它干活」:

关系不同取舍
vllm读 HF checkpoint/config 格式,替 generate 干活为吞吐牺牲通用性:连续批处理、paged KV、CUDA graph;transformers 为「任何架构都能跑」牺牲速度
accelerate被 Trainer 持有,替它干分布式的活accelerate 只做分布式原语,不含训练逻辑;Trainer 只做编排,不碰 NCCL
peft寄生在 PreTrainedModel 上改权重结构LoRA 等只加小参数,冻结基座;from_pretrained 还原生支持 adapter 路径探测(models/auto/auto_factory.py:298-318)
trl拿 Trainer 当底座写 RLHF/DPOTRL 的 SFTTrainer/DPOTrainer 都是 Trainer 子类,重写 compute_loss 等钩子
tokenizers分词的 Rust 引擎,被 PreTrainedTokenizerFast 包装tokenizers 只管文本↔id;transformers 的 tokenizer 层管特殊 token、chat 模板、与模型配置的耦合

一句话:transformers 是这个生态的「格式与基类提供者」——别家或消费它的格式,或继承它的基类,或补上它刻意不做的性能/分布式纵深。


7. 代码地图(入口级)

每章末尾有细粒度地图。这里只列从零开始读源码的六个入口:

主题文件路径符号名
一个架构的完整形态(教科书)src/transformers/models/llama/modeling_llama.pyLlamaAttentionLlamaDecoderLayerLlamaModelLlamaForCausalLM
模型基类与权重加载src/transformers/modeling_utils.pyPreTrainedModelfrom_pretrainedtie_weights
v5 权重转换系统src/transformers/core_model_loading.pyWeightTransformWeightConverterconvert_and_load_state_dict_in_model
生成总控与解码循环src/transformers/generation/utils.pyGenerationMixin.generate_sample_beam_search
KV cachesrc/transformers/cache_utils.pyCacheDynamicCacheDynamicLayerStaticLayer
训练回路src/transformers/trainer.pyTrainer.train_inner_training_looptraining_stepcompute_loss
Auto 路由src/transformers/models/auto/auto_factory.pyconfiguration_auto.py_BaseAutoModelClass.from_pretrainedAutoConfig.from_pretrained