2.1 模型结构
主流的大语言模型都采用了Transformer架构,它是一个基于多层Self-attention的神经网络模型。
原始的Transformer由编码器(Encoder)和解码器(Decoder)两个部分构成,同时,这两个部分也可以独立使用。
和 GPT 系列一样,LLaMA 模型也是 Decoder-only 架构,但结合前人的工作做了一些改进,比如:
- Pre-normalization [GPT3]。为了提高训练稳定性,LLaMA 对每个 transformer 子层的输入进行归一化,使用 RMSNorm 归一化函数,Pre-normalization 由Zhang和Sennrich(2019)引入。
- SwiGLU 激活函数 [PaLM]。将 ReLU 非线性替换为 SwiGLU 激活函数,且使用 2 / 3 ∗ 4 d 2/3 *4d 2/3∗4d 而不是 PaLM 论文中的 4d,SwiGLU 由 Shazeer(2020)引入以提高性能。
- Rotary Embeddings [GPTNeo]。模型的输入不再使用 positional embeddings,而是在网络的每一层添加了 positional embeddings (RoPE),RoPE 方法由Su等人(2021)引入。
2.2 模型超参数
不同模型的超参数详细信息在下表中给出:
2.3 SwiGLU
Feed Forward 层全称是 Position-wise Feed-Forward Networks(FPN),FFN 接收一个向量 x(序列中特定位置的隐藏表示),并将其通过两个可学习的线性变换(由矩阵 W1 和 W2 以及偏置向量 b1 和 b2 表示)进行处理,在两个线性变换之间应用修正线性(ReLU)激活函数。计算过程用数学公式可表达为:
在 T5 模型的实现中,使用是没有偏置 bias 的版本,数学公式表达如下:
后续的研究提出了用其他非线性激活函数替换ReLU,如高斯误差线性单元 (Gaussian Error Linear Units):
[Dauphin et al., 2016] 提出了门控线性单元(GLU),定义为输入的两个线性变换的逐元素乘积,其中一个经过了 sigmoid 激活。另外,他们还建议省略激活函数,称之为“双线性”(bilinear)层。
我们还可以使用其他激活函数定义 GLU 变体,如下所示:
在本论文中,作者提出了 Transformer FFN 层的其他变体,这些变体使用 GLU 或其变体代替第一个线性变换和激活函数。同样也省略了偏差项。
SwiGLU 激活函数是 Gated Linear Units (GLU) 变体之一,来源于论文 GLU Variants Improve Transformer。SwiGLU 数学表达式如下:
其中激活函数 Swish 的定义如下:
原始的的 FPN 层只有两个权重矩阵,但 F P N S w i G L U FPN_{SwiGLU} FPNSwiGLU 的线性变换层有三个权重矩阵。为了保持参数数量和计算量的恒定,需要将隐藏单元的数量 d_ff(W 和 V 的第二个维度以及 W2 的第一个维度)缩小 2/3。实现代码如下所示:
# -*- coding : utf-8 -*-
import torch
import torch.nn as nn
import torch.nn.functional as F
class FFNSwiGLU(nn.Module):
def __init__(self, input_dim: int, hidden_dim: int):
super().__init__()
hidden_dim = int(2 * hidden_dim / 3)
self.gate_proj = nn.Linear(input_dim, hidden_dim, bias=False)
self.down_proj = nn.Linear(hidden_dim, input_dim, bias=False)
self.up_proj = nn.Linear(input_dim, hidden_dim, bias=False)
def forward(self, x):
# LLaMA 官方提供的代码和模型默认是使用 F.silu() 激活函数,transformers 可通过配置指定
return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
layer = FFNSwiGLU(128, 256)
x = torch.randn(1, 128)
out = layer(x)
print(out.shape) # torch.Size([1, 128])