MiniMind(2)模型架构
目录
- MiniMind-Dense
- Decoder-Only与Encoder-Decoder
- GQA
- 位置编码
- MiniMind-MoE
- MoE
MiniMind-Dense
MiniMind-Dense使用了Decoder-Only架构,跟GPT-3的区别在于:在每个Transformer子层的输入上进行RMSNorm归一化,用SwiGLU代替ReLU,去掉绝对位置嵌入,改用旋转位置嵌入(RoPE),以处理超出训练长度的推理。
Decoder-Only与Encoder-Decoder
Decoder-Only与Encoder-Decoder架构具有本质差别:
- Encoder-Decoder输入与输出解耦,Encoder处理源序列(如英文句子),Decoder生成目标序列(如中文翻译),二者独立。
- Encoder-Decoder需要显式对齐,通过交叉注意力(Cross-Attention)连接两端信息。
# Encoder-Decoder
encoder_output = encoder(source_text) # 编码源序列
decoder_output = decoder(target_prefix, encoder_output) # 解码目标序列# Decoder-Only
for token in output_sequence:next_token = model(input_sequence) # input_sequence是已生成的部分input_sequence.append(next_token) # 自回归扩展输入
在Encoder-Decoder中,Encoder层无掩码且自注意力是双向的,Decoder层必须掩码未来位置。Decoder-Only的自注意力是掩码的。
交叉注意力
Encoder-Decoder架构通过交叉注意力(Cross-Attention)实现源序列(Encoder输出)和目标序列(Decoder输入)之间的信息交互
下面以机器翻译作为例子说明:
GQA
对于注意力层,使用了GQA(Group Query Attention),这是对Multi-Head Attention(MHA)和Multi-Query Attention(MQA)的扩展。GQA把查询头分成G个组,每个组共享一个公共的K和V投影。因此,GQA有三种变体:
- GQA-1:一个单独的组,等同于 Multi-Query Attention (MQA)。
- GQA-H:组数等于头数,基本上与 Multi-Head Attention (MHA) 相同。
- GQA-G:一个中间配置,具有G个组,平衡了效率和表达能力。
- 多头注意力有H个query,key,value头,Multi-Query Attention有H个query头,一个key和value头。使用G个组可以减少存储每个头的键和值所需的内存开销,特别是在具有大的上下文窗口或批次大小的情况下。
位置编码
位置编码可以提供序列顺序信息:自注意力机制(Self-Attention)本身无法感知输入元素的顺序(即排列不变性),位置编码通过为每个位置生成独特的编码向量,使模型能够区分序列中不同位置的元素。
在位置编码的基本流程中,有一段长度为 L L L的序列 X = [ x 1 , . . . , x L ] X=[x_{1},...,x_{L}] X=[x1,...,xL](词嵌入向量),为每个位置 i i i生成一个与词嵌入同维度的向量 P i P_{i} Pi,组成位置编码 P ∈ R L × d P\in\mathbb{R}^{L\times d} P∈RL×d,将位置编码与词嵌入相加: X e n c o d e d = X + P X_{encoded}=X+P Xencoded=X+P。
常见位置编码方法有三种
正弦余弦编码(Transformer原始方案)
该位置编码的特点是:可以处理任意长度序列,并且通过正弦波的波长变化,捕获不同粒度的位置关系(高频捕捉局部位置,低频捕捉全局位置)。
可以看到Transformer的位置编码使用了三角函数,周期形式的设计动机可以参考下图:
可以看到以列为单位,不同位置上的数字都会出现0、1的交替变化。比如第0列(右面数第一列)是每1次都切换,第1列(右面数第二列)是每两次切换。由于在浮点数的世界中使用二进制值是对空间的浪费,所以我们可以用正弦函数代替。事实上,正弦函数也能表示出二进制那样的交替。随着波长的变长,带来的是数据变化的变慢,就如同上面的提到的。
所以,位置编码的每个维度都对应于一个正弦曲线。即每个维度都是波长不同的正弦波,波长范围是2π到10000·2π,选用10000这个比较大的数是因为三角函数式有周期的,在这个范围基本上,就不会出现波长一样的情况了。
下图可以比较清晰的表现这过程(如黑色的(pos,i)所示,行代表维度,列代表token id)。相同颜色表示的是同一个token的词向量,为了方便展示,波形图只使用sin,而且波形图只是展示使用,并不是实际波形图:
可以看到,每个维度(即每一行)都是sin波形,随着维度增加,波长越来越长。可视化出来如下,维度为512:
可学习的位置编码
直接随机初始化一个位置嵌入矩阵 P P P,随着模型训练进行更新,优点是灵活,但是无法处理超出训练最大长度的序列,代表的工作比如scGPT和scTranslator,把模态,扰动等元信息均作为位置编码。
相对位置编码(T5,DeBERTa)
编码相对举例,而非绝对位置(如相邻词距离为1,隔一个词为2),设序列长度为 L L L,相对位置偏置矩阵 B ∈ R L × L B\in\mathbb{R}^{L\times L} B∈RL×L中的每个元素 B i , j B_{i,j} Bi,j表示位置 i i i和位置 j j j之间的相对位置偏置。
以T5为例,首先定义相对距离嵌入表,初始化一个可学习的嵌入表 E ∈ R ( 2 k + 1 ) × h E\in\mathbb{R}^{(2k+1)\times h} E∈R(2k+1)×h,其中 2 k + 1 2k+1 2k+1是可能的相对距离范围 − k -k −k到 k k k, h h h是注意力头的维度,然后,计算相对距离 δ = i − j \delta=i-j δ=i−j,截断超出范围的 δ \delta δ:
查表获取偏置标量:
收集所有 B i , j B_{i,j} Bi,j后,把偏置矩阵合并到注意力分数中:
相对位置编码不仅能学习,还能处理超出训练最大长度的序列。
旋转位置编码
旋转位置编码通过对query和key施加旋转变换来编码位置信息,兼具绝对位置的区分能力和相对位置的外推性。对于二维向量 v = [ x , y ] v=[x,y] v=[x,y],旋转角度 θ \theta θ的变换为:
扩展到高维,将 d d d维向量分为 d / 2 d/2 d/2个子空间,对每对分量 ( x m , x m + 1 ) (x_{m},x_{m+1}) (xm,xm+1)独立旋转,旋转角度:位置 i i i的旋转角度为 i θ m i\theta_{m} iθm,其中 θ m = 1000 0 − 2 m / d \theta_{m}=10000^{-2m/d} θm=10000−2m/d(频率递减),第 m m m组旋转为:
下面是一个数值演示,给定一个位置 i = 2 i=2 i=2的query向量,对其施加位置编码:
可以看到,旋转位置编码(RoPE)的核心创新正是通过隐式的旋转变换编码位置信息,而无需显式生成位置编码向量(如Transformer的绝对位置编码)。
在Attention中,对query向量 q i \textbf{q}_{i} qi和key向量 k j \textbf{k}_{j} kj分别旋转得到:
其中, R i \textbf{R}_{i} Ri是位置 i i i对应的旋转矩阵。再计算注意力分数:
可以看到,结果仅依赖相对位置(通过旋转矩阵 R i − j \textbf{R}_{i-j} Ri−j体现)
正弦函数编码通过设置很大的100000来扩展位置,其实不能处理无限长度,RoPE可以,且RoPE不存储位置编码矩阵,并且保证Attention(i,j)不等于Attention(j,i)(因为 R i − j \textbf{R}_{i-j} Ri−j不等于 R j − i \textbf{R}_{j-i} Rj−i),适合方向敏感任务。
RoPE保留了绝对位置的区分能力:
- 每个绝对位置对应唯一的旋转角度 i θ i\theta iθ
- 所以旋转后的向量隐含了绝对位置信息
RoPE中的 θ \theta θ控制旋转角的衰减速率,对于较大的 θ \theta θ,在高维度中,旋转速度慢,用于捕获全局位置关系,在低维度,旋转速度快,捕获局部位置,如果 θ \theta θ较小,则旋转速度趋于一致,缺乏多样性:
MiniMind-MoE
在Dense中把FFN换成MoE,采用MixFFN混合专家模块:
MoE参考:https://huggingface.co/blog/zh/moe
MoE
模型规模是提升模型性能的关键因素之一。在有限的计算资源预算下,用更少的训练步数训练一个更大的模型,往往比用更多的步数训练一个较小的模型效果更佳。
MoE的一个显著优势是它们能够在远少于稠密模型所需的计算资源下进行有效的预训练。这意味着在相同的计算预算条件下,您可以显著扩大模型或数据集的规模。特别是在预训练阶段,与稠密模型相比,混合专家模型通常能够更快地达到相同的质量水平。
MoE包含两部分:
- 稀疏MoE层:这些层代替了传统Transformer中的FFN。MoE 层包含若干“专家”(例如 4 个),每个专家本身是一个独立的神经网络。
- 门控网络或路由:这个部分用于决定哪些token被发送到哪个专家。例如,在下图中,“More”这个令牌可能被发送到第二个专家,而“Parameters”这个令牌被发送到第一个专家。有时,一个令牌甚至可以被发送到多个专家。令牌的路由方式是 MoE 使用中的一个关键点,因为路由器由学习的参数组成,并且与网络的其他部分一同进行预训练。
MoE存在以下挑战:
- 训练挑战: 虽然 MoE 能够实现更高效的计算预训练,但它们在微调阶段往往面临泛化能力不足的问题,长期以来易于引发过拟合现象。
- 推理挑战: MoE 模型虽然可能拥有大量参数,但在推理过程中只使用其中的一部分,这使得它们的推理速度快于具有相同数量参数的稠密模型。然而,这种模型需要将所有参数加载到内存中,因此对内存的需求非常高。以 Mixtral 8x7B 这样的 MoE 为例,需要足够的 VRAM 来容纳一个 47B 参数的稠密模型。之所以是 47B 而不是 8 x 7B = 56B,是因为在 MoE 模型中,只有 FFN 层被视为独立的专家,而模型的其他参数是共享的。
MoE微调技巧
- 一种可行的微调策略是尝试冻结所有非专家层的权重。实践中,这会导致性能大幅下降,但这符合我们的预期,因为混合专家模型 (MoE) 层占据了网络的主要部分。我们可以尝试相反的方法: 仅冻结 MoE 层的参数。实验结果显示,这种方法几乎与更新所有参数的效果相当。这种做法可以加速微调过程,并降低显存需求。
- 额外的经验:稀疏模型往往更适合使用较小的批量大小和较高的学习率,这样可以获得更好的训练效果。