Before we start… 下文都是基于 Qwen/Qwen3-4B 模型开展的代码分析,其他模型可能存在配置差异。
输入 - 嵌入层
分词 / Tokenization
这一层其实应该还带有前置的文本 - 向量转化过程,但是这个过程比较显而易见。用库来写大概是这样的:
1 2 3 4
| text = "Hello, world!" input_ids = tokenizer(text, return_tensors="pt")
|
如果上面的 return_tensors=”pt” 没有加上,那么得到的 input_ids 是一个 list,而非 tensor。
嵌入层 / Embedding
从输入到嵌入的过程就是按照 token 在表中查找每个 token 对应的向量,得到一个二维矩阵 E[seq_len * embedding_dim]
1 2 3 4 5 6 7 8 9 10 11
| print(model.embed_tokens) embeddings = model.embed_tokens(input_ids["input_ids"])
|
位置嵌入 / Positional Embedding
位置嵌入是这样一个向量,其表示了一个 token 在序列中的位置。对于 Qwen3 来说,位置嵌入是通过旋转位置编码(Rotary Position Embedding)实现的。其核心思想是将位置编码与输入的 token 嵌入进行旋转操作,从而使模型能够捕捉到序列中 token 的相对位置信息。
计算结果是一个与输入嵌入维度相同的向量(对于这个例子来讲,就是 torch.Size([1, 4, 2560]))表示了每个 token 在序列中的位置。这个位置嵌入会与输入嵌入结合。
输入 - 嵌入层对应代码:
modular_qwen3.py#L110 定义了 class Qwen3ForCausalLM(Qwen2ForCausalLM),完全复用父类的 forward 方法;
modeling_qwen2.py#417 定义 class Qwen2ForCausalLM(Qwen2PreTrainedModel, ...),其 forward 方法中调用了 self.model(...);对应 modeling_qwen2.py#L353 ,对 embedding 的计算在 L366:
1 2
| if inputs_embeds is None: inputs_embeds = self.embed_tokens(input_ids)
|
modeling_qwen2.py#L396 涉及位置嵌入的计算:position_embeddings = self.rotary_emb(hidden_states, position_ids),其中,hidden_states 现在就是 inputs_embeds,position_ids 计算在L#372: position_ids = torch.arange(inputs_embeds.shape[1], device=inputs_embeds.device) + past_seen_tokens; position_ids = position_ids.unsqueeze(0) 也就是一个从 0 到 seq_len-1 的向量。
self.rotary_emb 定义在 L#343 -> L#51。其 forward 方法定义在 #L102
WIP
Qwen3/Qwen3-4B 有 36 个 Transformer 层,定义在 modeling_qwen2.py#L340:self.layers = nn.ModuleList([Qwen2DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)])
每层结构定义在 #L207 或者 modeling_qwen3.py#L294
1 2 3 4 5 6 7 8 9
| def __init__(self, config: Qwen3Config, layer_idx: int): super().__init__() self.hidden_size = config.hidden_size
self.self_attn = Qwen3Attention(config=config, layer_idx=layer_idx)
self.mlp = Qwen3MLP(config) self.input_layernorm = Qwen3RMSNorm(config.hidden_size, eps=config.rms_norm_eps) self.post_attention_layernorm = Qwen3RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
Qwen3Attention 定义在 modeling_qwen3.py#L222
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| def __init__(self, config: Qwen3Config, layer_idx: int): super().__init__() self.layer_type = config.layer_types[layer_idx] if hasattr(config, "layer_types") else None self.config = config self.layer_idx = layer_idx self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads) self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads self.scaling = self.head_dim**-0.5 self.attention_dropout = config.attention_dropout self.is_causal = True
self.q_proj = nn.Linear( config.hidden_size, config.num_attention_heads * self.head_dim, bias=config.attention_bias ) self.k_proj = nn.Linear( config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias ) self.v_proj = nn.Linear( config.hidden_size, config.num_key_value_heads * self.head_dim, bias=config.attention_bias ) self.o_proj = nn.Linear( config.num_attention_heads * self.head_dim, config.hidden_size, bias=config.attention_bias ) self.q_norm = Qwen3RMSNorm(self.head_dim, eps=config.rms_norm_eps) self.k_norm = Qwen3RMSNorm(self.head_dim, eps=config.rms_norm_eps) self.sliding_window = config.sliding_window if self.layer_type == "sliding_attention" else None
|
Qwen3MLP 定义在 modeling_qwen3.py#L70
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| def __init__(self, config): super().__init__() self.config = config self.hidden_size = config.hidden_size self.intermediate_size = config.intermediate_size self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False) self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False) self.act_fn = ACT2FN[config.hidden_act]
def forward(self, x): down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x)) return down_proj
|