详细介绍:Qwen2模型架构

    Qwen2.5是阿里云最新推出的开源大型语言模型系列,相比Qwen2,新版本在多个方面实现了显著提升,包括知识掌握、编程能力、数学能力以及指令执行等。
在这里插入图片描述


1. Qwen2 整体结构(左侧部分)

1. 文本输入 → Tokenizer → input_ids

  • 输入是一串文本
  • Tokenizer 转成整数:
input_ids: (batch, seq_len)

2. input_ids → Embedding 层

将 token id 查表得到向量:

hidden_states: (batch, seq_len, hidden_size)

这是 Transformer 第 0 层的初始特征。


3. hidden_states 传入多个 Transformer Layers

每层结构:

RMSNorm
→ Self-Attention
→ 残差连接
→ RMSNorm
→ FFN(MLP)
→ 残差连接

每层都更新 hidden_states:

hidden_states = Layer_i(hidden_states)

4. Transformer 最后一层输出 → Final RMSNorm

Qwen2(LLaMA 系)在所有层结束后会再加一次最终 RMSNorm:

hidden_states = FinalRMSNorm(hidden_states)

作用:
让最后阶段的数值更加稳定(作为 logits 的输入)。


5. Final hidden_states → 线性投影(LM Head)

最后一层的 hidden_states 通过一个线性层(权重与 embedding 通常共享):

logits = hidden_states @ W_vocab^T

形状:

logits: (batch, seq_len, vocab_size)

例如 vocab_size = 151,000(Qwen2 实际大小)。


6. logits → Softmax → 预测下一个 token

取所有batch的最后一个token在 vocab 上的所有概率 logits。:

probs  = softmax(logits[:, -1, :])
next_id = argmax(probs)

(伪代码版)

input_ids
   ↓
token_embedding
   ↓
hidden_states (layer0)
for layer in transformer_layers:
    hidden_states = RMSNorm(hidden_states)
    hidden_states = hidden_states + Attention(hidden_states)
    hidden_states = hidden_states + FFN(RMSNorm(hidden_states))
# 最后一层输出:
decoder_hidden = hidden_states
logits = decoder_hidden @ token_embedding.weight.T  # shape: (batch, seq_len, vocab_size)
# 取最后一个 token logits
last_token_logits = logits[:, -1, :]  # shape: (batch, vocab_size)
# Softmax → next token
probs = softmax(last_token_logits, axis=-1)
next_id = argmax(probs, axis=-1)  # shape: (batch,)

2. Decoder Layer 结构

  • Hidden_states 经过 RMSNorm 正则化后,传递给 Attention 模块。
  • 在 Attention 模块内,输入分为 Query、Key 和 Value,用于注意力机制的计算。
  • 经过 Attention 后的输出再经过另一个 RMSNorm 处理,并通过 MLP(多层感知器)进一步处理。
  • 每一步都有残差连接(Residual),保证信息流动不会丢失。

在这里插入图片描述

(伪代码版)

hidden = hidden
hidden = hidden + Attention(RMSNorm(hidden))
hidden = hidden + MLP(RMSNorm(hidden))

换成一句话解释:

Decoder Layer 做两件事:

  • Attention:让 token “互相看见”
  • MLP:非线性建模能力
    并且每一步前面都有 RMSNorm、每一步后面都有残差。

3.Attention机制

  • Hidden_states 分别生成 Query、Key 和 Value,Q和K应用 Rotary positional embedding
    进行位置编码。
  • 这些编码后的 Query、Key 和 Value 经过注意力计算,生成 Attention_weight。
  • Attention_weight 通过 Softmax 归一化,并与 Value 相乘(Matmul),最后输出为 O_Linear。
    在这里插入图片描述
    Qwen2 使用 GQA 通过减少 K/V 头数来节省显存并加速推理,同时保持 Query 头数不变,用更少的代价获得接近 MHA 的性能。
方案Query headsKV heads显存/速度性能
MHA3232显存最大、最慢最强
MQA321显存最小、最快有性能损失
GQA328节省 4 倍 KV 缓存接近 MHA 性能

4.MLP

Qwen 的 MLP 是一种 门控前馈网络(Gated MLP / SwiGLU MLP),结构如下:

(hidden_size → intermediate_size → hidden_size)

在这里插入图片描述
包含三层 Linear 和一个激活函数:

  • gate_proj:Linear(hidden → intermediate)
  • up_proj:Linear(hidden → intermediate)
  • act_fn:SiLU(或其他 activation)
  • down_proj:Linear(intermediate → hidden)

Forward 计算流程

MLP 的 forward 逻辑是:

hidden = act(gate_proj(x)) * up_proj(x)
output = down_proj(hidden)

也就是:

  1. gate_proj(x) 送入激活函数(如 SiLU)
  2. up_proj(x)逐元素相乘(门控)
  3. 再送入 down_proj 回到原维度

核心思想

  • 不是普通 MLP
    不是常规的 Linear → Act → Linear,而是使用 GLU 结构(Gated Linear Unit)。

  • 两个上投影:gate_proj & up_proj
    其中 gate_proj 控制信息的“通过量”,相当于一个门(gate)。

  • 更强的表达能力,但参数几乎不变
    Gated MLP(SwiGLU)在 LLM 中效果显著更好,因此被 LLaMA、Qwen、GPT-NeoX 等广泛使用。


5.输出层

Qwen2 输出层 = RMSNorm(稳定 hidden_states)→ Linear(映射到词表)→ Softmax(得到概率),最终生成 logits,用于预测下一个 token。
在这里插入图片描述

模块作用输入形状输出形状
RMSNorm对 hidden_states 做尺度归一化(batch, seq_len, hidden_size)(batch, seq_len, hidden_size)
Linear (lm_head)映射到词表维度(batch, seq_len, hidden_size)(batch, seq_len, vocab_size)
Softmax生成概率分布(batch, seq_len, vocab_size)(batch, seq_len, vocab_size)

2.Qwen2-7B的配置参数

{
"architectures": [
"Qwen2ForCausalLM"
],
"attention_dropout": 0.0,
"bos_token_id": 151643,
"eos_token_id": 151645,
"hidden_act": "silu",
"hidden_size": 3584,
"initializer_range": 0.02,
"intermediate_size": 18944,
"max_position_embeddings": 32768,
"max_window_layers": 28,
"model_type": "qwen2",
"num_attention_heads": 28,
"num_hidden_layers": 28,
"num_key_value_heads": 4,
"rms_norm_eps": 1e-06,
"rope_theta": 1000000.0,
"sliding_window": 131072,
"tie_word_embeddings": false,
"torch_dtype": "bfloat16",
"transformers_version": "4.43.1",
"use_cache": true,
"use_sliding_window": false,
"vocab_size": 152064
}
参数说明
architectures[“Qwen2ForCausalLM”]模型类,Qwen2 的自回归语言模型
model_type“qwen2”模型类型标识,用于 Transformers 框架
bos_token_id151643句首 token ID(Begin of Sentence)
eos_token_id151645句尾 token ID(End of Sentence)
vocab_size152064模型词表大小,也决定输出 logits 维度
tie_word_embeddingsfalse输入 embedding 和输出 lm_head 是否共享权重
hidden_size3584Transformer 隐层维度 d_model
intermediate_size18944MLP 中间层维度(FeedForward hidden size)
hidden_act“silu”MLP 激活函数(SiLU / SwiGLU 类型)
num_hidden_layers28Transformer 层数
num_attention_heads28Query 头数
num_key_value_heads4Key/Value 头数(GQA,Query 多头共享少量 KV)
attention_dropout0.0注意力权重 Dropout 概率
rms_norm_eps1e-06RMSNorm 的 epsilon 防止除零
rope_theta1000000.0RoPE(旋转位置编码)频率缩放参数
max_position_embeddings32768模型最大支持 token 长度
max_window_layers28滑动窗口/长序列允许参与的最大层数
sliding_window131072滑动窗口总长度(长序列生成时使用)
use_sliding_windowfalse是否开启滑动窗口上下文
initializer_range0.02权重初始化范围
torch_dtype“bfloat16”PyTorch 数据类型,用 bfloat16 节省显存
use_cachetrue是否在推理时缓存 KV 提升自回归生成速度
transformers_version“4.43.1”Transformers 框架版本,用于兼容性

3.Qwen源码

1.RMS归一化类

1️⃣ 稳定训练

  • RMSNorm 可以 保持每个样本的特征向量在一个稳定的尺度上,避免梯度在深层网络中爆炸或消失。
  • 与传统的 LayerNorm 不同,它 不减均值,只归一化均方根,计算更简单,训练更稳定。

2️⃣ 加速收敛

  • 归一化后,网络的输入特征分布更均匀,使得优化器更容易更新参数。
  • 在 Transformer 类模型中,RMSNorm 可以让模型更快收敛,尤其是在大模型(如 Qwen2)中。

在这里插入图片描述

class Qwen2RMSNorm(nn.Module):
def __init__(self, hidden_size, eps=1e-6):
"""
Qwen2RMSNorm is equivalent to T5LayerNorm
"""
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.variance_epsilon = eps
def forward(self, hidden_states):
input_dtype = hidden_states.dtype
hidden_states = hidden_states.to(torch.float32)
variance = hidden_states.pow(2).mean(-1, keepdim=True)
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
return self.weight * hidden_states.to(input_dtype)

2.Attention

def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
"""
This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
"""
batch, num_key_value_heads, slen, head_dim = hidden_states.shape
if n_rep == 1:
return hidden_states
hidden_states = hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)

1️⃣ 功能

  • 作用:将多组 key/value 张量(num_key_value_heads)扩展到多头注意力所需的总头数(num_attention_heads)。

  • 输入

    • hidden_states:形状 (batch, num_key_value_heads, seq_len, head_dim)
    • n_rep:每个 key/value head 需要重复的次数
  • 输出

    • 形状 (batch, num_key_value_heads * n_rep, seq_len, head_dim)
    • 逻辑上把每个 key/value head 重复 n_rep

2️⃣ 核心逻辑

hidden_states[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
  • 新增维度None:在 key/value head 后增加一个维度,用于重复
  • expand:逻辑上重复 n_rep不占用额外显存,共享原始数据
.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
  • reshape:把 (num_key_value_heads, n_rep) 展平为总头数 num_key_value_heads * n_rep,方便矩阵乘法计算

3️⃣ 特点

  1. 高效:使用 expand + reshape,不会立刻增加显存
  2. 逻辑复制:每个 key/value head 对应多个 query head(分组注意力)
  3. 通用性:适用于多头注意力中 key/value head 少于 query head 的情况

4️⃣ 举例

import torch
x = torch.arange(2*2*3*4).reshape(2, 2, 3, 4)  # batch=2, kv_heads=2, seq_len=3, head_dim=4
y = repeat_kv(x, 3)  # 每个 head 复制3次
print(y.shape)  # (2, 6, 3, 4)
  • 每个 key/value head 被逻辑上重复 3 次,总头数变为 6
  • 底层内存仍然是原来的 2 个 key/value head,没有占用 3 倍显存

总结一句话

repeat_kvexpand + reshape 将少量 key/value head 逻辑上复制到多头注意力所需的总头数,高效共享内存,便于分组注意力计算。


def eager_attention_forward(
module: nn.Module,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attention_mask: Optional[torch.Tensor],
scaling: float,
dropout: float = 0.0,
**kwargs,
):
key_states = repeat_kv(key, module.num_key_value_groups)
value_states = repeat_kv(value, module.num_key_value_groups)
attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
if attention_mask is not None:
causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
attn_weights = attn_weights + causal_mask
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
attn_output = torch.matmul(attn_weights, value_states)
attn_output = attn_output.transpose(1, 2).contiguous()
return attn_output, attn_weights

经典的GQA,无需多说。

3.MLP

import torch
import torch.nn as nn
# 定义激活函数映射
ACT2FN = {
"relu": nn.ReLU(),
"gelu": nn.GELU(),
"silu": nn.SiLU(),  # Swish 激活
}
# 配置类
class Config:
def __init__(self, hidden_size=8, intermediate_size=16, hidden_act="silu"):
self.hidden_size = hidden_size
self.intermediate_size = intermediate_size
self.hidden_act = hidden_act
# Qwen2MLP 模块
class Qwen2MLP(nn.Module):
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):
# GLU 前向传播
return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
# 测试函数
def test_qwen2mlp():
# 使用 config 创建模型
config = Config(hidden_size=8, intermediate_size=16, hidden_act="silu")
mlp = Qwen2MLP(config)
# 随机输入: batch_size=2, seq_len=4, hidden_size=8
x = torch.randn(2, 4, config.hidden_size)
# 前向传播
output = mlp(x)
print("输入形状:", x.shape)
print("输出形状:", output.shape)
print("输出示例:", output)
# 运行测试
if __name__ == "__main__":
test_qwen2mlp()
posted @ 2026-01-07 21:16  gccbuaa  阅读(5)  评论(0)    收藏  举报