AI

深入理解大模型推理过程(3):一个LLM请求的推理过程实现-模型加载

2026年9月12日 阅读(0)

1.transformers代码

from transformers import AutoModelForCausalLM
path = "./models/Qwen2.5-0.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(path)

2.transformers/PreTrainedModel 机制

2.1 定位

PreTrainedModel 是”模型骨架的标准化包装盒”,定义了”模型长什么样、怎么存、怎么取”的统一规范,但不包含任何具体网络结构的实现。作为Hugging Face 生态的”模型标准化协议”——它通过 config.json(结构蓝图)+ state_dict(权重数据)两层松耦合设计,实现了”同一份权重,用任何框架、在任何上下文、从任何来源加载”的终极目标。

PreTrainedModel (抽象基类)
    │
    ├── 提供统一的 保存/加载/配置管理 接口
    │   ├── .save_pretrained(save_directory)
    │   ├── .from_pretrained(model_path)
    │   └── .save_to_cache() / .from_cache()
    │
    ├── 提供统一的 配置管理 接口
    │   ├── .config → PretrainedConfig 实例
    │   └── .save_config(config, save_directory)
    │
    ├── 提供统一的 设备/精度管理 接口
    │   ├── .to(device) / .cuda() / .cpu() / .half() / .bfloat16()
    │   └── .float() / .double()
    │
    ├── 提供统一的 梯度检查点 / 编译 支持
    │   ├── .gradient_checkpointing_enable()
    │   └── .compile()
    │
    └── 派生为各种具体模型类
        ├── PreTrainedModel → Qwen2Model (基座模型,无 LM Head)
        │                         └── Qwen2ForCausalLM (加了 LM Head)
        │                         └── Qwen2ForSequenceClassification (加分类头)
        ├── PreTrainedModel → BertModel (基座)
        │                         └── BertForSequenceClassification
        │                         └── BertForQuestionAnswering
        └── PreTrainedModel → LlamaForCausalLM
                                  └── MistralForCausalLM
                                  └── Qwen2ForCausalLM

2.2 模型目录结构

典型目录结构

(AI) peile.duan@U-7K6V5HCV-0153 AI % ls models/Qwen3.8-27B-4bit/
README.md				model-00001-of-00003.safetensors	processor_config.json
chat_template.jinja			model-00002-of-00003.safetensors	tokenizer.json
config.json				model-00003-of-00003.safetensors	tokenizer_config.json
configuration.json			model.safetensors.index.json		video_preprocessor_config.json
generation_config.json			preprocessor_config.json		vocab.json

from_pretrained 能精准还原一个模型,依赖两大文件的协同存储。PreTrainedModel 的职责就是:读取 config.json 用代码把骨架搭起来,再把 model.safetensors 里的肉填进去。

2.3 结构描述文件 config.json

(AI) peile.duan@U-7K6V5HCV-0153 AI % cat models/Qwen3.8-27B-4bit/config.json
{
    "architectures": [
        "Qwen3_5ForConditionalGeneration"
    ],
    "do_sample": true,
    "eos_token_id": [
        248046,
        248044
    ],
    "generation_config": {
        "bos_token_id": 248044,
        "do_sample": true,
        "eos_token_id": [
            248046,
            248044
        ],
        "pad_token_id": 248044,
        "temperature": 1.0,
        "top_k": 20,
        "top_p": 0.95
    },
    "image_token_id": 248056,
    "language_model_only": false,
    "model_type": "qwen3_5",
    "quantization": {
        "group_size": 64,
        "bits": 4,
        "mode": "affine"
    },
    "quantization_config": {
        "group_size": 64,
        "bits": 4,
        "mode": "affine"
    },
    "temperature": 1.0,
    "text_config": {
        "attention_bias": false,
        "attention_dropout": 0.0,
        "attn_output_gate": true,
        "bos_token_id": 248044,
        "dtype": "bfloat16",
        "eos_token_id": 248044,
        "full_attention_interval": 4,
        "head_dim": 256,
        "hidden_act": "silu",
        "hidden_size": 5120,
        "initializer_range": 0.02,
        "intermediate_size": 17408,
        "layer_types": [
            "linear_attention",
            "linear_attention",
            "linear_attention",
            "linear_attention",
            ......
            "full_attention"
        ],
        "linear_conv_kernel_dim": 4,
        "linear_key_head_dim": 128,
        "linear_num_key_heads": 16,
        "linear_num_value_heads": 48,
        "linear_value_head_dim": 128,
        "mamba_ssm_dtype": "float32",
        "max_position_embeddings": 262144,
        "model_type": "qwen3_5_text",
        "mtp_num_hidden_layers": 1,
        "mtp_use_dedicated_embeddings": false,
        "num_attention_heads": 24,
        "num_hidden_layers": 64,
        "num_key_value_heads": 4,
        "output_gate_type": "swish",
        "pad_token_id": null,
        "partial_rotary_factor": 0.25,
        "rms_norm_eps": 1e-06,
        "rope_parameters": {
            "mrope_interleaved": true,
            "mrope_section": [
                11,
                11,
                10
            ],
            "partial_rotary_factor": 0.25,
            "rope_theta": 10000000,
            "rope_type": "default"
        },
        "tie_word_embeddings": false,
        "use_cache": true,
        "vocab_size": 248320
    },
    "tie_word_embeddings": false,
    "top_k": 20,
    "top_p": 0.95,
    "transformers_version": "5.8.0.dev0",
    "video_token_id": 248057,
    "vision_config": {
        "deepstack_visual_indexes": [],
        "depth": 27,
        "hidden_act": "gelu_pytorch_tanh",
        "hidden_size": 1152,
        "in_channels": 3,
        "initializer_range": 0.02,
        "intermediate_size": 4304,
        "model_type": "qwen3_5",
        "num_heads": 16,
        "num_position_embeddings": 2304,
        "out_hidden_size": 5120,
        "patch_size": 16,
        "spatial_merge_size": 2,
        "temporal_patch_size": 2
    },
    "vision_end_token_id": 248054,
    "vision_start_token_id": 248053
}%

2.3.1 顶级架构声明

字段

含义

architectures

["Qwen3_5ForConditionalGeneration"]

顶层模型类名。ForConditionalGeneration表明它是多模态编码器-解码器风格的生成模型(处理图像→文本),而非纯 ForCausalLM

model_type

"qwen3_5"

HuggingFace 路由机制的匹配键。告诉 AutoModel

去哪个映射表找对应的 Python 类

transformers_version

"5.8.0.dev0"

极新。目前主流 transformers 是 4.x,5.8.0.dev0 意味着需要未来版本/自编译版本才能原生支持

language_model_only

false

声明此模型不是纯语言模型,必须加载视觉分支

do_sample

/ temperature

/ top_k

/ top_p

见 generation_config

顶层的冗余采样参数,供旧版库读取

2.3.2 text_config:语言模型核心架构

这是整个配置中最关键、信息量最大的部分。它揭示了一个非常前卫的混合注意力架构

1.基础维度

字段

含义

vocab_size

248320

词表大小 ≈ 248K。超大词表(LLaMA-3 是 128K),支持多语言高压缩率

hidden_size

5120

隐藏层维度 = 5120(约 5B 参数级别的标准宽度,对应 DeepSeek-V2 级别)

intermediate_size

17408

FFN 中间维度(SwiGLU:intermediate = hidden × 3.4,比传统 4× 略窄)

num_hidden_layers

64

总层数 64 层。比 Qwen2.5-72B 的 80 层少,但比 27B 级别(如 Gemma-2-27B 的 46 层)深得多

head_dim

256

注意力头维度 256。异常大(传统 128),配合 num_attention_heads=24,得到 Q 总维度 = 24×256 = 6144(实际用 partial_rotary 只旋转部分)

2.GQA(分组查询注意力)配置

字段

含义

num_attention_heads

24

Query 头数量 = 24

num_key_value_heads

4

KV 头数量 = 4

GQA 比率

6:1

每 6 个 Query 头共享 1 组 KV。这是 DeepSeek-V3(8:1)和 LLaMA-3-70B(8:1)级别的激进压缩

3.layer_types:混合注意力层(最核心创新)

"layer_types": [
    "linear_attention",   ×N 层
    ...
    "full_attention"      若干层
]

Layer 类型

功能

特点

linear_attention

通过线性注意力(类似Mamba/Gated Linear Attention)处理长序列

时间复杂度 O(L),显存不随序列长度增长

full_attention

标准 Softmax 自注意力

时间/显存 O(L²),但表达能力更强

full_attention_interval: 4 的含义:

layer_types 的实际排布:
  第 0~2 层:linear_attention
  第 3 层:  full_attention     ← 每 4 层插入 1 个全注意力
  第 4~6 层:linear_attention
  第 7 层:  full_attention
  ...
  • 大部分层用线性注意力处理 256K 长上下文,计算量可控
  • 每 4 层插入一个全注意力层,让模型定期”全局回顾”——这是 Qwen3.5 架构的首创策略

4.线性注意力/SSM 细节

字段

含义

linear_conv_kernel_dim

4

线性注意力中的卷积核维度(类似 Mamba 的卷积 short-conv)

linear_key_head_dim

128

线性注意力 K 的维度

linear_num_key_heads

16

线性注意力的 K 头数量

linear_value_head_dim

128

线性注意力 V 的维度

linear_num_value_heads

48

线性注意力的 V 头数量

mamba_ssm_dtype

"float32"

SSM 内部计算用 FP32 保持稳定(外部 BF16)

解读:KV 头数不对称(16K vs 48V),说明这是 GLA(Gated Linear Attention) 或自研变体,而非原始 Mamba。linear_conv_kernel_dim=4 说明引入了局部卷积增强位置感知。

5.RoPE 位置编码

字段

含义

rope_theta

10000000

RoPE 基频 = 10M(远超 LLaMA-3 的 500K),支持更长上下文

partial_rotary_factor

0.25

部分旋转:只旋转 25% 的 head_dim,其余 75% 不旋转。这是 DeepSeek-V3 的优化,保留绝对位置信息

rope_type

"default"

标准 RoPE(无 NTK/YaRN 变体)

mrope_interleaved

true

多模态 RoPE(MRoPE)交织模式,视觉 token 使用 3D 空间坐标旋转

mrope_section

[11, 11, 10]

MRoPE 将 head_dim=256 切分为三段:[t×11, h×11, w×10]

,对应时间/高度/宽度三个维度的位置编码

🧠 架构提示mrope_interleaved + partial_rotary_factor + rope_theta=1e+7 这套组合拳,是典型的 LongRoPE / NTK-aware RoPE 的现代实现。它使得模型既能处理 256K 的超长上下文,又避免了长文本中的位置编码混乱(token 间的关系衰减过慢)

6.其他关键机制

字段

含义

attn_output_gate

true

在注意力输出后增加一个门控机制(类似 DeepSeek-V3 的 head gate),用 Swish 函数调节信息流

output_gate_type

"swish"

上述门控的激活函数 = Swish(SiLU)

mtp_num_hidden_layers

1

MTP(Multi-Token Prediction):额外的 1 层预测头,用于同时预测下一个和再下一个 token(DeepSeek-V3 的训练技巧)

mtp_use_dedicated_embeddings

false

MTP 不额外学习独立的 embedding,复用主 embedding

tie_word_embeddings

false

不绑定权重:输入 embedding 和输出 LM Head 独立

max_position_embeddings

262144

最大上下文长度 = 256K tokens

rms_norm_eps

1e-06

RMSNorm 的数值稳定常数

use_cache

true

推理时使用 KV Cache

2.3.3 vision_config:视觉编码器

字段

含义

model_type

"qwen3_5"

视觉编码器也标记为 qwen3_5

patch_size

16

图像 patch 大小 = 16×16 像素

in_channels

3

RGB 三通道

hidden_size

1152

ViT 内部隐藏维度

intermediate_size

4304

ViT FFN 中间维度

depth

27

ViT 共 27 层

num_heads

16

ViT 注意力头数

num_position_embeddings

2304

ViT 最大 patch 位置数(对应 2304 个 patch = 约 768×768 分辨率)

out_hidden_size

5120

投影输出维度 = 语言模型的 hidden_size。通过 MLP Projector 将 1152 映射到 5120

spatial_merge_size

2

空间合并:2×2 patch 合并为 1 个 token(降低视觉 token 数量)

temporal_patch_size

2

视频处理:时间维度每 2 帧取 1 帧

deepstack_visual_indexes

[]

空数组:不使用 DeepStack 视觉融合(Qwen2-VL 的机制,将视觉特征注入不同 LLM 层)

2.3.4 量化配置(4-bit)

"quantization_config": {
    "group_size": 64,
    "bits": 4,
    "mode": "affine"
}

含义:明示该模型不是 FP16 还是 FP32,而是经过 4-bit 量化的。

字段

含义

bits

4

权重用 INT4 存储

group_size

64

每 64 个权重共享一个 scale/zero-point

mode

"affine"

仿射量化:r = S(q - Z),有偏移量。比对称量化精度更高

两个重复字段 quantization quantization_config

  • quantization:顶层冗余声明,供 MLX/llama.cpp 等非 HF 引擎读取
  • quantization_config:HuggingFace bitsandbytes/AutoGPTQ 的标准格式

2.3.5 generation_config:推理采样参数

这是生成器默认值的清单。当你只写 model.generate(**inputs) 而不加任何额外参数时,它会拿这里的值当作 baseline。

字段

含义

bos_token_id

/ eos_token_id

/ pad_token_id

248044

复用同一个 token 作为 BOS/EOS/PAD(ChatML 语系常见做法)

eos_token_id

[248046, 248044]

两个 EOS:<|im_end|><|endoftext|>,任一触发停止

temperature

1.0

温度 1.0,不做温度缩放

top_k

20

只保留概率最高的 20 个 token 采样

top_p

0.95

累积概率 95% 截断

do_sample

true

随机采样(关闭则贪心)

注意temperature=1.0 + top_k=20 + top_p=0.95 组合意味着采样偏保守(top_k 限制了候选池)。

2.3.6 多模态 token 映射(桥梁配置

字段

数值

含义

image_token_id

248056

告诉 tokenizer:看到 ID 248056 时,这是一个图像 patch 占位符。

video_token_id

248057

视频占位符。

vision_start_token_id

248053

视觉序列开始标记(在 text 中插入 [V] [X]... [X])。

vision_end_token_id

248054

视觉序列结束标记。

2.3.7 多模态工作流程

  1. Tokenizer 渲染对话:<|im_start|>user <tokens_vision_start> <|image_pad|> ... 描述这张图<|im_end|>
  2. Tokenizer 识别到 image_token_id (248056)。
  3. LLM 在生成序列后,前向传播。Vision Encoder (vision_config) 计算出 27 层的图像特征,形状是 (batch, num_patches, 1152)
  4. Embedding 替换: LLM 装配器(通常是 Processor 的一部分,而非 PreTrainedModel 本身)在运行前知道:“喂,这个词表 ID 248056 的位置,应该用 Vision Encoder 输出的 1152 维向量去替换掉文本的 Embedding。”
  5. 输入 LLM (text_config)。文本 Embedding 和 Visual Embedding 组装成一个长序列,包含所有文本 token + 图像 token + 文档。
  6. Attention。所有这些 token 在 text_configlinear_attentionfull_attention 机制中被融合在一起。

2.3.8 架构总结

Qwen3.8-27B-4bit 架构总览
│
├── 输入层
│   ├── Tokenizer (248K 词表, ChatML 格式, 多模态占位)
│   └── Embedding (5120 维)
│
├── 视觉分支
│   ├── ViT Encoder (27层, 1152维, patch=16)
│   ├── Spatial Merge (2×2)
│   └── MLP Projector (1152 → 5120)
│
├── 语言模型主体 (64层混合注意力)
│   ├── Layer 0,1,2: Linear Attention (Mamba-like, GLA)
│   ├── Layer 3:     Full Attention (GQA 24/4, head_dim=256)
│   ├── Layer 4,5,6: Linear Attention
│   ├── Layer 7:     Full Attention
│   └── ... (每4层1个全注意力)
│
├── 位置编码
│   ├── RoPE θ=10M, partial_rotary=0.25
│   ├── Full Attention 层用标准 RoPE
│   └── 视觉 token 用 MRoPE (3D 交织)
│
├── 输出层
│   ├── LM Head (不绑定权重)
│   └── MTP 额外预测头 (1层)
│
└── 量化
    └── INT4, group_size=64, 仿射量化

核心定位:这是一个面向 256K 长上下文多模态场景的 27B 模型。通过混合注意力(线性+全注意力)平衡长序列效率与表达能力,通过 MRoPE 处理视觉-文本位置对齐,通过激进 GQA (6:1) 和 4-bit 量化控制部署成本。

2.4 权重文件(.safetensors.bin

作用:存储所有可学习参数的实际数值。

两种格式对比

维度

.bin

(PyTorch Pickle)

.safetensors

(Safetensors)

安全性

⚠️ 任意代码执行风险

纯数据,无代码执行

加载速度

慢(反序列化无索引)

快3-5倍(mmap + zero-copy)

分片支持

手动

自动

跨框架

仅 PyTorch

PyTorch / TF / JAX / MLX 通用

2.4.1 SafeTensors格式介绍

https://github.com/safetensors/safetensors

SafeTensors 是 HuggingFace 在 2022 年提出的一种张量序列化标准格式。它不是新的神经网络架构,而是一种安全、高效、零拷贝的模型权重存储协议,现已成为 LLM 时代的通用标准。

SafeTensors 当前已成为AI 基础设施领域的”HTTP 协议”——简单、安全、通用、高性能。它把 PyTorch 繁琐复杂的模型加载过程,降维成 3 步:读 8 字节头 → 解析 JSON → mmap 数据。配合 config.json 中的 Python 代码骨架,构成了当前 LLM 生态的存储与传输标准。

从 OpenAI 的 GLM 到 DeepSeek 的 FP8 训练,从 3090 上的 7B 量化版到超大规模集群的 405B MoE,这个不起眼的 JSON 头格式正在背后支撑着整个大模型时代的 reproducibility。

2.4.2 设计目标

目标

实现手段

安全性

完全移除 Python 对象机制,只存纯数值

零拷贝

直接内存映射(mmap)加载张量,不建 Python 对象

跨框架

与语言无关的二进制规范,Rust/C++/Go 均可读写

高性能

加载速度比 PyTorch bin 快 10-20 倍

精度保持

支持任意 dtype(BF16/FP16/INT4),无损保存

2.4.3 格式规范:文件内部长什么样?

SafeTensors 是一个极简的二进制容器,与通常的认知不同——它不包含任何模型架构信息,只保存张量数据和元数据。模型存在于两个平行世界:

世界一:模型 "骨架"(Python 代码 + JSON 配置)
  └── config.json: {"model_type":"qwen2", "hidden_size":896, ...}
  └── qwen2.py: class Qwen2ForCausalLM(nn.Module) { ... }
  └── 这是有逻辑、可执行的代码

世界二:模型"肌肉"(纯数值矩阵)
  └── model.safetensors: {"model.embed_tokens.weight": [151936×896], ...}
  └── 这是一堆没有灵魂的浮点数

具体文件布局如下:

SafeTensors 文件布局:
┌──────────────────────────────────────────┐
│ Header 长度 (8字节, 大端序 uint64)       │  ← 告诉你后面有多少字节是 JSON Header
├──────────────────────────────────────────┤
│ JSON Header (UTF-8, 变长)               │  ← 所有张量的名称、形状、dtype、偏移
├──────────────────────────────────────────┤
│ 对齐的空余空间 (可选)                    │  ← 将后续数据对齐到内存页边界
├──────────────────────────────────────────┤
│ 张量数值数据块 (Raw binary)              │  ← 按 header 声明的偏移顺序存储
├── ... 更多 tensor ...                   │
└──────────────────────────────────────────┘

2.4.4 JSON Header 详解

{
  "metadata": { "__version__": "4" },
  "model.embed_tokens.weight": {
    "dtype": "BF16",
    "shape": [151936, 896],
    "data_offsets": [0, 272322528]
  },
  "model.layers.0.self_attn.q_proj.weight": {
    "dtype": "BF16",
    "shape": [896, 896],
    "data_offsets": [272322528, 273374976]
  },
  ...
}

关键字段含义

字段

类型

含义

dtype

字符串(如 "BF16", "F16", "F32", "I64", "U8"

数据类型,HF 扩展的 NumPy 类型

shape

List[int]

张量各维大小

data_offsets

[start, end]

该张量在整个文件后面的绝对字节偏移区间

核心设计:

  • 字典键在 safetensors 的 header 里,张量名本身就是 JSON 对象的 key,而不是作为 value 里的某个字段保存。
  • 地址与数据解耦:元数据只记录偏移量,实际数据在文件后半段连续存放。
  • dtype 字符串规范:SafeTensors 定义了严格的对照表(BF16 bfloat16,I64 torch.int64 等),避免 Python-ism。

2.4.5 大文件分片

大型模型的分片方式(例如 Qwen2.5-72B):
├── model-00001-of-00009.safetensors  (第 1-7 层)
├── model-00002-of-00009.safetensors  (第 8-14 层)
├── model-00003-of-00009.safetensors
├── ...
└── model.safetensors.index.json       ← 全局分片索引

Index 文件结构:

{
  "metadata": {"total_size": 144897451008},
  "weight_map": {
    "model.embed_tokens.weight": "model-00001-of-00009.safetensors",
    "model.layers.0.self_attn.q_proj.weight": "model-00001-of-00009.safetensors",
    "model.layers.24.self_attn.v_proj.weight": "model-00002-of-00009.safetensors",
    ...
  }
}

加载逻辑:HuggingFace 加载器读 index.json,找到每个 tensor 在哪个分片文件中,然后按需 mmap 对应文件。

3.PreTrainedModel初始化

PreTrainedModel 是 Hugging Face transformers 库的核心基类,所有模型类(如 Qwen2ForCausalLMBertModelLlamaForCausalLM)都继承自它。它是整个生态”一次保存、随处加载“能力的支柱。

model = AutoModelForCausalLM.from_pretrained(path)   

该函数对应的完整调用链如下所示:

AutoModelForCausalLM.from_pretrained("/path/to/qwen2")
│
│  ┌──── 第一层: 工厂分发 ──────────────────────────────────┐
│  │ [modeling_auto.py:2187] AutoModelForCausalLM            │
│  │  → super().from_pretrained()                             │
│  │                                                          │
│  │ [auto_factory.py:261] _BaseAutoModelClass.from_pretrained│
│  │  ├─ A. 提取参数 (config, trust_remote_code, hub_kwargs) │
│  │  ├─ B. cached_file(path, "config.json") → commit_hash   │
│  │  ├─ C. PEFT 适配器处理                                   │
│  │  ├─ D. AutoConfig.from_pretrained(path)                 │
│  │  │     → 读 config.json → model_type="qwen2"            │
│  │  │     → Qwen2Config 实例                                │
│  │  ├─ E. 判断: has_local_code=True (在 MODEL_MAPPING 中)  │
│  │  └─ F. _get_model_class(config, MODEL_FOR_CAUSAL_LM_MAPPING)│
│  │       │  [auto_factory.py:178]                           │
│  │       ├─ model_mapping[Qwen2Config]                      │
│  │       │  → _LazyAutoMapping.__getitem__                  │
│  │       │     ├─ _reverse_config_mapping["Qwen2Config"] → "qwen2"│
│  │       │     ├─ _model_mapping["qwen2"] → "Qwen2ForCausalLM"│
│  │       │     └─ _load_attr_from_module("qwen2", "Qwen2ForCausalLM")│
│  │       │        ├─ model_type_to_module_name → "qwen2"    │
│  │       │        ├─ importlib.import_module(".qwen2", "transformers.models")│
│  │       │        └─ getattr → Qwen2ForCausalLM 类          │
│  │       │                                                  │
│  │       └─ model_class.from_pretrained(path, config=config)│
│  └──────────────────────────────────────────────────────────┘
│
│  ┌──── 第二层: PreTrainedModel.from_pretrained ────────────┐
│  │ [modeling_utils.py:3859]                                  │
│  │                                                           │
│  │ 1. 参数提取: dtype, device_map, quantization_config, ...│
│  │    torch_dtype → dtype; dtype=None → "auto"              │
│  │                                                           │
│  │ 2. check_and_set_device_map(device_map)                  │
│  │                                                           │
│  │ 3. (如未传入) config_class.from_pretrained()             │
│  │                                                           │
│  │ 4. get_hf_quantizer(config, quantization_config, ...)    │
│  │    → hf_quantizer (或 None)                              │
│  │    → validate_environment() + update_device_map()         │
│  │                                                           │
│  │ 5. _get_resolved_checkpoint_files(path, ...)             │
│  │    → checkpoint_files = ["model.safetensors"] (可能多个) │
│  │    → sharded_metadata (分片索引)                         │
│  │                                                           │
│  │ 6. _get_dtype("auto", checkpoint_files, config, ...)    │
│  │    → config.dtype / sharded_metadata / state_dict 推断  │
│  │                                                           │
│  │ 7. ★ 模型实例化 (meta device) ★                          │
│  │    get_init_context(dtype, ...) → 上下文管理器列表:      │
│  │    ├─ local_torch_dtype(dtype)                            │
│  │    ├─ init.no_tie_weights()                              │
│  │    ├─ torch.device("meta")  ← 零内存初始化               │
│  │    └─ init.meta_device_safe_creation_ops()               │
│  │                                                           │
│  │    with ContextManagers(init_context):                   │
│  │        model = cls(config)  ← Qwen2ForCausalLM(config)   │
│  │        if hf_quantizer:                                   │
│  │            hf_quantizer.preprocess_model(model, ...)     │
│  │            (替换 nn.Linear → Linear4bit 等)              │
│  │                                                           │
│  │ 8. get_model_conversion_mapping(model, key_mapping, ...)│
│  │    → WeightRenaming / WeightConverter 列表              │
│  │                                                           │
│  │ 9. ★ 权重加载 ★                                          │
│  │    _load_pretrained_model(model, state_dict,             │
│  │        checkpoint_files, load_config)                    │
│  │    [modeling_utils.py:4391]                              │
│  │    ├─ expected_keys = model.state_dict().keys()          │
│  │    ├─ caching_allocator_warmup()  ← GPU 内存预热         │
│  │    ├─ 加载 state_dict:                                    │
│  │    │   safe_open(file) → get_slice(k)  ← 惰性切片       │
│  │    └─ convert_and_load_state_dict_in_model()             │
│  │       [core_model_loading.py:1465]                       │
│  │       │                                                  │
│  │       ├─ 阶段1: 收集 (遍历 state_dict)                   │
│  │       │   ├─ rename_source_key() → checkpoint key 映射  │
│  │       │   ├─ 判断量化需求                                │
│  │       │   ├─ 确定 dtype                                  │
│  │       │   ├─ TP/DTensor 分片                             │
│  │       │   └─ spawn_materialize() → Future/Callable      │
│  │       │      (异步线程池物化 tensor)                     │
│  │       │                                                  │
│  │       └─ 阶段2: 转换和写入                                │
│  │           ├─ mapping.convert() → 执行 WeightTransform    │
│  │           │   (Chunk/Concatenate/量化反序列化...)        │
│  │           └─ set_param_for_module()                      │
│  │               ├─ param._is_hf_initialized = True         │
│  │               └─ setattr(module, param_name, param_value)│
│  │                                                          │
│  │ 10. 后处理: _finalize_model_loading()                    │
│  │     ├─ mark_tied_weights_as_initialized()                │
│  │     ├─ _move_missing_keys_from_meta_to_device()          │
│  │     ├─ _initialize_missing_keys()                        │
│  │     ├─ tie_weights()                                     │
│  │     └─ _adjust_missing_and_unexpected_keys()             │
│  │                                                         │
│  │ 11. model.eval()                                        │
│  │ 12. set_use_kernels() (内核替换)                         │
│  │ 13. adjust_generation_fn() (如果 can_generate)          │
│  │ 14. accelerate_dispatch() (多设备 hook, 如果需要)        │
│  │ 15. hf_quantizer.postprocess_model() (量化后处理)        │
│  └─────────────────────────────────────────────────────────┘
│
= 返回 model (Qwen2ForCausalLM 实例,权重已加载)
  model.device → 实际设备
  model.config → Qwen2Config
  model.can_generate() → True

3.1 AutoModelForCausalLM类定义

类定义位于 modeling_auto.py 第 2187-2199 行。AutoModelForCausalLM 继承自 _BaseAutoModelClass,本身不包含任何加载逻辑:

class AutoModelForCausalLM(_BaseAutoModelClass):
    _model_mapping = MODEL_FOR_CAUSAL_LM_MAPPING

    @classmethod
    def from_pretrained(cls, pretrained_model_name_or_path, *model_args, **kwargs):
        return super().from_pretrained(pretrained_model_name_or_path, *model_args, **kwargs)

3.1.1 _BaseAutoModelClass.from_pretrained

位于 auto_factory.py 第 261-408 行。这是工厂模式的核心,核心职责是类型推断——它不做任何权重加载,只负责”根据 config 找到正确的模型类,然后把控制权交给该类的 from_pretrained“。真正的模型实例化和权重加载发生在下一层 PreTrainedModel.from_pretrained 中。

from_pretrained(path, **kwargs)
  │
  ├─ A. 参数提取 → config, trust_remote_code, hub_kwargs, commit_hash
  ├─ B. 获取 commit_hash → cached_file("config.json") → extract_commit_hash
  ├─ C. PEFT 适配器 → find_adapter_config_file → base_model_name_or_path
  ├─ D. 加载配置 → AutoConfig.from_pretrained → config.json → model_type → Config 子类
  ├─ E. 判断代码来源
  │   ├─ has_remote_code? (config.auto_map 中有 cls.__name__)
  │   ├─ has_local_code? (type(config) in _model_mapping)
  │   └─ resolve_trust_remote_code()
  │
  └─ F. 委托
      ├─ 远程代码 → get_class_from_dynamic_module → register → model_class.from_pretrained
      └─ 本地代码 → _get_model_class(config, mapping)
                     │
                     ├─ mapping[type(config)]
                     │   → _LazyAutoMapping.__getitem__
                     │     ├─ reverse_config_mapping → model_type
                     │     ├─ model_mapping → model_name
                     │     └─ _load_attr_from_module → importlib + getattr
                     │
                     ├─ tuple/list? → config.architectures 精确匹配
                     └─ model_class.from_pretrained(path, config=config)

3.1.2 _get_model_class 函数

位于 auto_factory.py 第 178-191 行:

def _get_model_class(config, model_mapping):
    supported_models = model_mapping[type(config)]   # 触发 _LazyAutoMapping.__getitem__
    if not isinstance(supported_models, (list, tuple)):
        return supported_models
    name_to_model = {model.__name__: model for model in supported_models}
    architectures = getattr(config, "architectures", [])
    for arch in architectures:
        if arch in name_to_model:
            return name_to_model[arch]
    return supported_models[0]

与 AutoTokenizer 的关键区别:模型映射的值可以是 tuple/list(一个 Config 类对应多个模型类,如 Qwen2Config 可能对应 Qwen2ForCausalLMQwen2ForSequenceClassification)。此时用 config.architectures 精确匹配,无匹配时取第一个(默认)。

3.1.3 映射表与懒加载

MODEL_FOR_CAUSAL_LM_MAPPING_NAMESmodeling_auto.py 第 678 行起)是 OrderedDict,典型条目:

("qwen2", "Qwen2ForCausalLM"),
("qwen3", "Qwen3ForCausalLM"),
("llama", "LlamaForCausalLM"),
("gemma", "GemmaForCausalLM"),

MODEL_FOR_CAUSAL_LM_MAPPING = _LazyAutoMapping(CONFIG_MAPPING_NAMES, MODEL_FOR_CAUSAL_LM_MAPPING_NAMES)(第 2032 行),使用与 AutoTokenizer 完全相同的 _LazyAutoMapping 机制:

Qwen2Config → _reverse_config_mapping → "qwen2"
           → _model_mapping → "Qwen2ForCausalLM"
           → model_type_to_module_name("qwen2") → "qwen2"
           → importlib.import_module(".qwen2", "transformers.models")
           → getattr(module, "Qwen2ForCausalLM") → 具体模型类

3.2 权重加载层

选定具体模型类后(如 Qwen2ForCausalLM),调用其继承的 PreTrainedModel.from_pretrained,位于 modeling_utils.py 第 3859-4388 行。这是最复杂的部分,具体调用链如下:

PreTrainedModel.from_pretrained(path, *model_args, config=config, **kwargs)
│  [modeling_utils.py:3859]
│
├─ 1. 参数提取                                         [:4101-4131]
│   pop: dtype, torch_dtype(→dtype), device_map, quantization_config,
│        offload_folder, gguf_file, key_mapping, use_kernels, ...
│   移除废弃参数: low_cpu_mem_usage, _fast_init, from_tf, from_flax
│   dtype 为 None → 设为 "auto"
│
├─ 2. 适配器加载 + device_map 规范化                     [:4176-4181]
│   maybe_load_adapters()
│   check_and_set_device_map(device_map)  # → {"": device} 或保留 "auto"
│
├─ 3. 加载配置 (如果上层未传入)                          [:4188-4210]
│   config_class.from_pretrained(path, return_unused_kwargs=True)
│
├─ 4. 量化器初始化                                      [:4225-4227]
│   get_hf_quantizer(config, quantization_config, device_map, ...)
│   ├─ 读 config.quantization_config (预量化) 或用户传入 (即时量化)
│   ├─ AutoHfQuantizer.from_config() → hf_quantizer
│   ├─ validate_environment() + update_device_map() + update_tp_plan()
│   └─ 返回 hf_quantizer (或 None)
│
├─ 5. 解析权重文件                                     [:4248-4258]
│   _get_resolved_checkpoint_files(path, variant, ...)
│   优先级: safetensors > .bin > .gguf, 支持分片索引
│   → checkpoint_files (文件路径列表), sharded_metadata
│
├─ 6. 确定 dtype                                       [:4263-4265]
│   _get_dtype(dtype, checkpoint_files, config, ...)
│   "auto" → config.dtype / sharded_metadata / state_dict 推断
│   量化器: hf_quantizer.update_dtype(dtype)
│
├─ 7. ★ 模型实例化 (meta device, 零内存) ★             [:4301-4315]
│   init_context = cls.get_init_context(dtype, is_quantized, ...)
│   │  [modeling_utils.py:3769]
│   │  ├─ local_torch_dtype(dtype)        # 临时设 torch 默认 dtype
│   │  ├─ init.no_tie_weights()           # 禁止 __init__ 时 tie weights
│   │  ├─ torch.device("meta")            # ← meta 设备, 不分配内存
│   │  └─ init.meta_device_safe_creation_ops()
│   │
│   with ContextManagers(init_context):
│       model = cls(config, *model_args, **model_kwargs)
│       │  → 如 Qwen2ForCausalLM(config) 在 meta 上构建网络结构
│       │
│       if hf_quantizer is not None:
│           hf_quantizer.preprocess_model(model, ...)
│           # 替换 nn.Linear → Linear4bit 等 (不碰权重值)
│
├─ 8. 获取权重转换映射                                  [:4326-4330]
│   weight_conversions = get_model_conversion_mapping(
│       model, key_mapping, hf_quantizer
│   )
│   → WeightRenaming (键名重命名) / WeightConverter (tensor 变换)
│
├─ 9. ★ 权重加载 ★                                     [:4331-4348]
│   load_config = LoadStateDictConfig(
│       device_map=device_map, dtype=dtype, dtype_plan=...,
│       hf_quantizer=hf_quantizer, weight_mapping=weight_conversions, ...
│   )
│   loading_info, disk_offload_index = cls._load_pretrained_model(
│       model, state_dict, checkpoint_files, load_config
│   )
│   │  [modeling_utils.py:4391-4494]
│   │
│   ├─ expected_keys = model.state_dict().keys()  # meta 模型参数名
│   ├─ 磁盘 offload 准备 (如果 device_map 含 "disk")
│   ├─ caching_allocator_warmup()  # GPU 一次性 cudaMalloc 预热
│   │
│   ├─ 加载 state_dict:
│   │   ├─ safetensors: safe_open() + get_slice(k)  ← 惰性切片
│   │   ├─ .bin: torch.load() 全量加载
│   │   └─ 分片: 逐文件 safe_open() 合并
│   │
│   └─ convert_and_load_state_dict_in_model(model, state_dict, load_config, ...)
│      [core_model_loading.py:1465-1756]
│      │
│      ├─ 阶段 1: 收集 (遍历 state_dict)
│      │   ├─ rename_source_key() → checkpoint key → 模型 key
│      │   ├─ 判断量化需求: hf_quantizer.param_needs_quantization()
│      │   ├─ 确定 dtype: dtype_plan / empty_param.dtype
│      │   ├─ TP/DTensor 分片处理
│      │   └─ spawn_materialize() → 异步线程池物化 tensor (Future)
│      │
│      └─ 阶段 2: 转换和写入
│          ├─ mapping.convert() → 执行 WeightTransform
│          │   (Chunk / Concatenate / 量化反序列化 ...)
│          └─ set_param_for_module()
│              ├─ param._is_hf_initialized = True  # 阻止重新初始化
│              └─ setattr(module, param_name, param_value) → 写入模型
│
├─ 10. 后处理: _finalize_model_loading()               [:4349]
│   [modeling_utils.py:4497-4533]
│   ├─ mark_tied_weights_as_initialized()
│   ├─ _move_missing_keys_from_meta_to_device()
│   ├─ _initialize_missing_keys()  # 用正确分布初始化缺失权重
│   ├─ tie_weights()  # 绑定共享权重 (如 embedding ↔ lm_head)
│   └─ _adjust_missing_and_unexpected_keys()
│
├─ 11. model.eval()  # 切换评估模式                    [:4358]
├─ 12. set_use_kernels(use_kernels, kernel_config)      [:4362]
├─ 13. adjust_generation_fn() (如果 can_generate)       [:4366]
├─ 14. accelerate_dispatch() (多设备时安装 forward hooks)[:4370]
├─ 15. hf_quantizer.postprocess_model() (量化后处理)     [:4372]
│
└─ 返回 model

核心要点:模型先在 meta device 零内存初始化(第 7 步),然后通过惰性 safetensors 切片 + 异步线程池逐参数加载真实权重(第 9 步),最后通过 tie_weights 和 missing key 初始化完成收尾(第 10 步)。整个过程的关键设计是“先建结构(meta),再填数据(权重加载),最后修补(finalize)”的三阶段模式。

3.3 关键设计决策

  1. 懒加载映射_LazyAutoMapping 通过 importlib.import_module 按需导入,避免 200+ 模型模块拖慢 import transformers。模块缓存避免重复导入。与 AutoTokenizer 使用完全相同的机制。
  2. Meta Device 初始化 + 分离式权重加载:V5 核心架构变化。模型先在 torch.device("meta") 上零内存初始化,然后通过 convert_and_load_state_dict_in_model 逐参数加载真实权重。替代了 V4 的 low_cpu_mem_usage 参数(已废弃)。
  3. WeightTransform 声明式权重转换:V5 引入 WeightRenaming WeightConverter,将权重转换从硬编码的 if-else 变为声明式模式匹配系统。支持 Chunk/Concatenate/MergeModulelist/TorchaoDeserialize 等操作,使不同 checkpoint 格式可统一加载。
  4. 惰性 safetensors 切片 + 异步加载:使用 get_slice(k) 而非 get_tensor(k),仅在 spawn_materialize 阶段才读取 tensor 数据。结合 ThreadPoolExecutor 异步加载,极大降低峰值内存和加载时间。
  5. GPU 缓存分配器预热:加载前根据参数总量一次性 cudaMalloc,避免逐参数分配导致的内存碎片。可将 70B 模型加载从数分钟降至约 13 秒。
  6. 统一量化集成:V5 废弃 load_in_4bit/load_in_8bit/bnb_config,统一通过 quantization_config 传入。三步集成:preprocess_model(替换模块)→ 即时量化(权重加载时)→ postprocess_model(清理)。量化器还能调整 device_map 和 TP plan。
  7. _is_hf_initialized 标记:加载的参数设置此标记,阻止模型 __init__ 中的 _init_weights 重新初始化已加载的权重——避免覆盖正确值。
  8. auto_class_update 装饰器:用 copy_func 复制方法,为每个 Auto 类生成独立的文档字符串(包含支持的模型列表),但共享同一份代码逻辑。

4.模型特化实现

模型特化实现保存在models目录下,以qwen3_5为例,下面包含如下文件

(AI) peile.duan@U-7K6V5HCV-0153 transformers %    ls models/qwen3_5
__init__.py			configuration_qwen3_5.py	modular_qwen3_5.py
__pycache__			modeling_qwen3_5.py

qwen3_5/ 目录共 5 个文件,3086 行代码:

文件

行数

职责

__init__.py

28

模块入口,惰性加载导出

configuration_qwen3_5.py

200

配置类(自动生成)

modeling_qwen3_5.py

2026

完整模型实现(自动生成)

modular_qwen3_5.py

738

模块化源文件(唯一手工维护)

tokenization_qwen3_5.py

94

BPE 分词器

注意:没有 image_processing_*.pyprocessing_*.py——Qwen3.5 复用了 Qwen3VL 的处理器,在 auto/processing_auto.py 中注册为 ("qwen3_5", "Qwen3VLProcessor")

4.1 configuration_qwen3_5.py

定义”模型长什么样”,包含三个配置类,全部继承 PreTrainedConfig

model_type

职责

Qwen3_5TextConfig

"qwen3_5_text"

文本模型参数(vocab_size、hidden_size、layer_types 等)

Qwen3_5VisionConfig

"qwen3_5_vision"

视觉编码器参数

Qwen3_5Config

"qwen3_5"

多模态顶层配置,包含 text_config + vision_config 子配置

配置类解决的核心问题

  • 声明模型所有超参数及其默认值
  • 设置 model_type 标识符,供 AutoConfig 自动映射
  • 配置张量并行计划(base_model_tp_plan:每个线性层指定 colwise/rowwise 切分策略)
  • 多模态配置的父子关系(sub_configs + base_config_key
  • __post_init__ 中的自动生成逻辑——Qwen3.5 的 layer_types 为 None 时自动生成交替层类型(默认每 4 层一个 full_attention)

4.2 modeling_qwen3_5.py

Qwen3.5 的架构创新在于混合注意力——大部分层(默认 3/4)使用 Qwen3_5GatedDeltaNet(线性注意力,O(L) 复杂度,通过门控 Delta 规则维护递归状态),少量层(默认 1/4)使用 Qwen3_5Attention(标准 softmax 注意力,O(L²) 复杂度,门控查询)。这种设计在保持长序列建模能力的同时大幅降低了计算开销。

它是模型的”建筑图纸”:它定义了完整的神经网络计算图,网络由哪些层组成、层与层怎么连接、前向传播怎么计算。但它不包含任何具体的参数数值——参数数值在 model.safetensors 里。

Qwen3.5 的核心设计思想:

1. "大部分层用线性注意力处理长序列,每隔 N 层插入全注意力做全局回顾"
   → 对应 layer_types: ["linear_attention", ..., "full_attention"]

2. "视觉和文本共享同一套 Transformer 主干,但视觉 token 用 3D MRoPE"
   → 对应 mrope_section: [11, 11, 10](时间/高度/宽度)

3. "门控无处不在"
   → RMSNormGated, attn_output_gate, SwiGLU, output_gate_type="swish"

4. "梯度检查点是标配,不是可选"
   → 所有 DecoderLayer 继承 GradientCheckpointingLayer

4.2.1 类说明

nn.Module
├── Qwen3_5VisionRotaryEmbedding      ← 视觉 1D RoPE
├── Qwen3_5TextRotaryEmbedding        ← 文本 3D MRoPE (交错)
├── Qwen3_5RMSNormGated              ← 带门控 RMSNorm (线性注意力用)
├── Qwen3_5RMSNorm                   ← 标准 RMSNorm (1+weight)
├── Qwen3_5GatedDeltaNet             ← 线性注意力层 (O(L))
├── Qwen3_5Attention                 ← 全注意力层 (O(L²), 门控查询)
├── Qwen3_5MLP                       ← SwiGLU FFN
├── Qwen3_5VisionMLP                ← 视觉 FFN
├── Qwen3_5VisionPatchEmbed          ← 3D 卷积 patch embedding
├── Qwen3_5VisionPatchMerger         ← 2×2 空间合并
└── Qwen3_5VisionAttention           ← 视觉注意力 (变长序列)

GradientCheckpointingLayer
├── Qwen3_5DecoderLayer              ← 混合架构路由层
└── Qwen3_5VisionBlock               ← 视觉 Transformer 块

PreTrainedModel
└── Qwen3_5PreTrainedModel           ← 基类 (权重初始化 + 能力声明)
    ├── Qwen3_5VisionModel           ← 完整视觉编码器
    ├── Qwen3_5TextModel             ← 纯文本主干
    ├── Qwen3_5Model                 ← 多模态主干 (视觉+文本)
    ├── Qwen3_5ForCausalLM           ← 纯文本因果LM
    │   └── + GenerationMixin
    └── Qwen3_5ForConditionalGeneration  ← 多模态条件生成
        └── + GenerationMixin

GenericForTokenClassification + Qwen3_5PreTrainedModel
└── Qwen3_5ForTokenClassification

GenericForSequenceClassification + Qwen3_5PreTrainedModel
├── Qwen3_5TextForSequenceClassification   ← 纯文本序列分类
└── Qwen3_5ForSequenceClassification       ← 多模态序列分类

BaseModelOutputWithPast
└── Qwen3_5ModelOutputWithPast      ← 输出容器 (+rope_deltas)

1.基础组件

定义模型会反复用到的底层模块:

  • RMSNorm / Gated RMSNorm
  • RoPE / MRoPE 位置编码
  • MLP / VisionMLP
  • 一些辅助的门控、卷积、状态更新单元

这些类解决的是“一个小积木怎么计算”的问题。

2.核心注意力模块

Qwen3.5 不是纯 Transformer,而是线性注意力 + 全注意力混合架构

Qwen3_5GatedDeltaNet

    • 线性注意力/状态空间风格模块
    • 复杂度近似 O(L)
    • 负责长上下文高效建模
  • Qwen3_5Attention
    • 标准全注意力
    • 复杂度 O(L²)
    • 负责精确的全局信息交互

3.视觉编码器相关模块

为多模态能力服务:

  • Qwen3_5VisionPatchEmbed:把图像/视频切成 patch 并映射成向量
  • Qwen3_5VisionAttention:视觉分支内部的注意力
  • Qwen3_5VisionMLP
  • Qwen3_5VisionPatchMerger:把 patch 特征合并压缩
  • Qwen3_5VisionBlock:视觉 Transformer block

这部分负责把图片/视频先编码成可供语言模型消费的视觉 token 表示。

4.ecoder 层 / Block 级组装

  • Qwen3_5DecoderLayer
    • 是文本主干的单层 block
    • 内部会根据配置路由到:
      • 线性注意力层,或
      • 全注意力层
    • 再接 MLP 和残差、Norm

这一层体现了 Qwen3.5 的核心设计:不同层不是同构的,而是混合排布的。

5.完整模型主干

在更高一层把所有 block 拼成完整网络,这部分解决的是“整台模型机器怎么装起来”。

  • Qwen3_5VisionModel:完整视觉编码器
  • Qwen3_5TextModel:纯文本主干
  • Qwen3_5Model:多模态主干(视觉 + 文本融合)

6.任务头(Head)

在主干上再加不同任务输出层,同一个底座可以支持不同任务。

  • Qwen3_5ForCausalLM
    • 纯文本生成模型
    • 用于自回归 next-token prediction
  • Qwen3_5ForConditionalGeneration
    • 多模态条件生成模型
    • 输入图像/视频/文本,输出文本
  • Qwen3_5ForTokenClassification
    • token 级分类任务

7.Transformers 集成基类

它是连接 config.json、权重文件和模型代码的桥梁。

  • Qwen3_5PreTrainedModel
    • 提供和 Transformers 框架对接所需的统一能力
    • 比如:
      • 权重初始化
      • from_pretrained
      • save_pretrained
      • gradient checkpointing 支持
      • generation 支持声明

8.跨模块调用关系

用户输入: "描述这张图" + [图片]
    │
    ▼
┌─ Qwen3_5VisionPatchEmbed ─────────────────────┐
│  Conv3d(3→1152, kernel=(2,16,16))              │
│  图片 → [B, num_patches, 1152]                 │
└─────────────────────┬──────────────────────────┘
                      ▼
┌─ Qwen3_5VisionBlock × 27 ─────────────────────┐
│  RMSNorm → VisionAttention → RMSNorm → VisionMLP│
│  2D RoPE 位置编码                               │
└─────────────────────┬──────────────────────────┘
                      ▼
┌─ Qwen3_5VisionPatchMerger ─────────────────────┐
│  2×2 空间合并 → 投影到 5120 维                  │
│  [B, num_patches, 1152] → [B, num_merged, 5120]│
└─────────────────────┬──────────────────────────┘
                      │ vision_features
                      ▼
┌─ Token 替换 ───────────────────────────────────┐
│  input_ids 中的 <|image_pad|> 位置              │
│  替换为 vision_features                          │
│  构造 3D MRoPE position_ids:                    │
│    文本 token → (t, 0, 0)                       │
│    视觉 token → (t, h, w)                       │
└─────────────────────┬──────────────────────────┘
                      ▼
┌─ Qwen3_5DecoderLayer × 64 ─────────────────────┐
│                                                  │
│  Layer 0-2: GatedDeltaNet (线性注意力)           │
│    Conv1d(4) → QKV → Delta Rule → GatedNorm     │
│    O(L) 复杂度,处理长视觉序列高效                │
│                                                  │
│  Layer 3: Full Attention (全注意力)              │
│    GQA(24:4) → MRoPE → Swish Gate → O(L²)      │
│    全局回顾,建立跨模态关联                       │
│                                                  │
│  Layer 4-6: GatedDeltaNet                       │
│  Layer 7: Full Attention                        │
│  ...                                             │
│  Layer 63: Full Attention (最后一层总是全注意力) │
│                                                  │
│  每层后接 SwiGLU MLP                            │
└─────────────────────┬──────────────────────────┘
                      ▼
┌─ Qwen3_5RMSNorm → lm_head ─────────────────────┐
│  [B, L, 5120] → [B, L, 248320]                 │
│  → softmax → next token prediction              │
└──────────────────────────────────────────────────┘

4.2.2 forward实现

Qwen3_5ForConditionalGeneration.forward()
  │
  ├─ self.model (Qwen3_5Model).forward()
  │   │
  │   ├─ self.visual (Qwen3_5VisionModel).forward()
  │   │   ├─ Qwen3_5VisionPatchEmbed    — patch 切分
  │   │   ├─ Qwen3_5VisionRotaryEmbedding — 视觉 RoPE
  │   │   ├─ [Qwen3_5VisionBlock] × N
  │   │   │   ├─ Qwen3_5VisionAttention  — 视觉自注意力
  │   │   │   └─ Qwen3_5VisionMLP        — 视觉 FFN
  │   │   └─ Qwen3_5VisionPatchMerger    — 空间合并
  │   │
  │   ├─ 替换占位 token 的 embedding → 视觉特征注入
  │   ├─ compute_3d_position_ids → 4D position_ids
  │   │
  │   └─ self.language_model (Qwen3_5TextModel).forward()
  │       ├─ embed_tokens → input embeddings
  │       ├─ Qwen3_5TextRotaryEmbedding — 交错 MRoPE
  │       │
  │       └─ [Qwen3_5DecoderLayer] × N
  │           ├─ if layer_types[i] == "linear_attention":
  │           │   └─ Qwen3_5GatedDeltaNet   — 线性注意力 (O(L))
  │           │       ├─ torch_chunk/recursive_gated_delta_rule
  │           │       ├─ causal_conv1d_fn/update
  │           │       └─ Qwen3_5RMSNormGated
  │           │
  │           ├─ if layer_types[i] == "full_attention":
  │           │   └─ Qwen3_5Attention       — 全注意力 (O(L²))
  │           │       └─ apply_rotary_pos_emb + eager/SDPA/Flash
  │           │
  │           ├─ Qwen3_5RMSNorm (input_layernorm)
  │           └─ Qwen3_5MLP + Qwen3_5RMSNorm (post_attention)
  │
  └─ self.lm_head → logits

4.2.3 与config.json的关系

config.json(施工参数)          模型 py(施工图纸)
─────────────────────          ────────────────────
hidden_size: 5120        →     nn.Linear(5120, ...)
num_hidden_layers: 64    →     for i in range(64): DecoderLayer()
num_attention_heads: 24  →     q_proj = Linear(5120, 24*head_dim)
num_key_value_heads: 4   →     k_proj = Linear(5120, 4*head_dim)
intermediate_size: 17408 →     gate_proj = Linear(5120, 17408)
vocab_size: 248320       →     Embedding(248320, 5120)
hidden_act: "silu"       →     act_fn = SiLU()
rms_norm_eps: 1e-6       →     RMSNorm(eps=1e-6)
rope_theta: 10000000     →     RotaryEmbedding(theta=10000000)

4.2.4 config.json vs modeling_*.py vs model.safetensors

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│  config.json          modeling_*.py          model.safetensors  │
│  ───────────          ─────────────          ─────────────────  │
│  "造多大"             "怎么造"               "填什么值"         │
│                                                                 │
│  hidden_size=5120     nn.Linear(5120,...)    weight=[5120×...]  │
│  num_layers=64        for i in range(64)     layers.0.q_proj    │
│  vocab_size=248320    Embedding(248320,5120) embed.weight       │
│                                                                 │
│  三者缺一不可:                                                   │
│  • 没有 config → 不知道建多大的结构                              │
│  • 没有模型代码 → 不知道怎么连接                                  │
│  • 没有权重 → 结构是空壳,输出是随机数                            │
│                                                                 │
│  调用入口:AutoModelForCausalLM.from_pretrained(path)            │
│  路由桥梁:config.json 中的 model_type 字段                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.3 modular_qwen3_5.py

Hugging Face Transformers 库在 v4.39+ 版本引入的一项重大工程重构:Modular Transformers(模块化架构)。简单来说,modeling_qwen3_5.py 不再是人类手写和维护的源码,而是由构建工具根据 modular_qwen3_5.py 自动编译/生成的产物。

4.3.1 核心痛点

在 Modular 机制出现之前,Transformers 库里的每个模型都是完全独立的“孤岛”。这导致了严重的代码重复与维护灾难

  • 复制粘贴泛滥:Qwen2 是从 Llama 复制来的,Qwen2.5 又是从 Qwen2 复制来的。RMSNorm、RoPE、SwiGLU MLP 等基础组件在几百个模型文件中被重复定义了上万次。
  • 牵一发而动全身:如果修复了 RoPE 的一个 bug,或者为 FlashAttention-2 增加了一种新优化,开发者需要手动去修改几百个 modeling_*.py 文件,极易遗漏。
  • Diff 不可读:PR 里充斥着大量无意义的重复代码变更,Code Review 极其痛苦。

4.3.2 Modular 机制

  1. 声明依赖与继承:在 modular_qwen3_5.py 中,开发者不再重写所有类,而是通过 import 引入基础模型(如 Qwen2 或 Llama),然后只写差异部分python
# modular_qwen3_5.py (人类编写的源码)
from ..qwen2.modeling_qwen2 import Qwen2ForCausalLM, Qwen2Model

class Qwen3_5Model(Qwen2Model):
    # 只重写有变化的方法或属性
    def __init__(self, config):
        super().__init__(config)
        # 添加 Qwen3.5 特有的 MTP Head
        if hasattr(config, "mtp_num_hidden_layers"):
            self.mtp_head = MTPHead(config)

# 没有重写的类(如 RMSNorm, MLP)会自动从父类继承
  1. AST 解析与代码展开:当你运行 make style 或 CI 流水线时,Transformers 内部的脚本(utils/modular_model_converter.py)会使用 Python AST(抽象语法树)解析这个文件。
  2. 生成扁平化代码:脚本将所有的继承关系、import 引用进行静态展开(Flattening),把零散的增量代码合并成一个完整的、自包含的、没有任何外部模型依赖的传统 modeling_qwen3_5.py 文件。
  3. 写入文件并校验:生成的代码会覆盖目标文件,并通过 Ruff/Black 进行格式化。最终提交到 Git 仓库中的依然是完整的 modeling_*.py

4.3.3 为什么还要保留生成的 modeling_qwen3_5.py

既然有了模块化的源码,为什么不直接在运行时动态组装,而是要生成一个物理文件并提交到仓库?

  • 向后兼容:数以万计的第三方推理引擎(vLLM、SGLang、TGI)和用户脚本都硬编码了 from transformers.models.qwen3_5.modeling_qwen3_5 import ...。保留生成的完整文件,保证了整个生态无需任何修改即可无缝工作。
  • 可读性与调试:生成的文件是标准的 Python 代码。当用户在本地 debug 断点时,看到的是完整的逻辑,而不是复杂的动态 mixin 或元类魔法。
  • 零运行时开销:代码展开发生在开发/构建阶段,运行时没有任何额外的反射或动态加载成本。

4.3.4 对开发者和用户的影响

角色

影响与注意事项

模型贡献者

禁止直接修改 modeling_qwen3_5.py。任何手动修改都会在下次 CI 运行时被覆盖。必须修改 modular_qwen3_5.py,然后运行 make fix-copies重新生成。

框架维护者

修复底层 Bug(如 Attention Mask 逻辑)时,只需修改基础模型(如 Llama/Qwen2),所有继承它的衍生模型会在下次构建时自动获得修复。

普通用户

使用体验完全不变。AutoModel.from_pretrained()依然正常工作,只是底层的代码组织方式变了。

下游引擎开发者

如果你通过正则表达式或 AST 解析 modeling_*.py来提取模型结构,可能会发现不同模型的代码风格变得高度一致且标准化,这反而让自动化解析变得更稳定了。

You Might Also Like