文章目录
- 4 Implementing a GPT model from Scratch To Generate Text
- 4.2 Normalizing activations with layer normalization
4 Implementing a GPT model from Scratch To Generate Text
4.2 Normalizing activations with layer normalization
通过层归一化(Layer Normalization)对激活值进行归一化处理。
-
Layer normalization (LayerNorm):将激活值中心化到均值为 0,归一化方差为 1,稳定训练并加速收敛。
应用位置:
-
transformer block 中的 multi-head attention module 前后。
-
最终输出层之前。
下图提供了LayerNormalization的直观概述
从一个小例子看看LayerNormalization发生了什么
torch.manual_seed(123)batch_example = torch.randn(2, 5) layer = nn.Sequential(nn.Linear(5, 6), nn.ReLU()) out = layer(batch_example) print(out) print(out.shape)# 计算均值和方差 mean = out.mean(dim=-1, keepdim=True) var = out.var(dim=-1, keepdim=True)print("Mean:\n", mean) print("Variance:\n", var)out_norm = (out - mean) / torch.sqrt(var) print("Normalized layer outputs:\n", out_norm)mean = out_norm.mean(dim=-1, keepdim=True) var = out_norm.var(dim=-1, keepdim=True) print("Mean:\n", mean) print("Variance:\n", var)"""输出""" tensor([[0.2260, 0.3470, 0.0000, 0.2216, 0.0000, 0.0000],[0.2133, 0.2394, 0.0000, 0.5198, 0.3297, 0.0000]],grad_fn=<ReluBackward0>)torch.Size([2, 6])Mean:tensor([[0.1324],[0.2170]], grad_fn=<MeanBackward1>)Variance:tensor([[0.0231],[0.0398]], grad_fn=<VarBackward0>)Normalized layer outputs:tensor([[ 0.6159, 1.4126, -0.8719, 0.5872, -0.8719, -0.8719],[-0.0189, 0.1121, -1.0876, 1.5173, 0.5647, -1.0876]],grad_fn=<DivBackward0>)Mean:tensor([[9.9341e-09],[0.0000e+00]], grad_fn=<MeanBackward1>) Variance:tensor([[1.0000],[1.0000]], grad_fn=<VarBackward0>)
归一化会独立应用于两个输入(行)中的每一个;使用
dim=-1
表示在最后一个维度(在本例中为特征维度)上进行计算,而不是在行维度上进行计算。
关闭科学计数法
torch.set_printoptions(sci_mode=False) #关闭科学计数法 print("Mean:\n", mean) print("Variance:\n", var)"""输出""" Mean:tensor([[ 0.0000],[ 0.0000]], grad_fn=<MeanBackward1>) Variance:tensor([[1.0000],[1.0000]], grad_fn=<VarBackward0>)
-
-
LayerNorm
类实现:基于归一化思路,实现一个LayerNorm
类,稍后我们可以在 GPT 模型中使用它class LayerNorm(nn.Module):def __init__(self, emb_dim):super().__init__()self.eps = 1e-5self.scale = nn.Parameter(torch.ones(emb_dim))self.shift = nn.Parameter(torch.zeros(emb_dim))def forward(self, x):mean = x.mean(dim=-1, keepdim=True)var = x.var(dim=-1, keepdim=True, unbiased=False)norm_x = (x - mean) / torch.sqrt(var + self.eps)return self.scale * norm_x + self.shift
层归一化公式(上面的例子中 γ = 1 \gamma = 1 γ=1 、 β = 0 \beta=0 β=0、 ϵ = 0 \epsilon = 0 ϵ=0)
L a y e r N o r m ( x i ) = γ ⋅ x i − μ σ 2 + ϵ + β LayerNorm(x_i) = \gamma \cdot \frac{x_i-\mu}{\sqrt{\sigma^2 + \epsilon}} + \beta LayerNorm(xi)=γ⋅σ2+ϵxi−μ+β
其中-
μ 、 σ 2 \mu 、 \sigma^2 μ、σ2 分别x在layer维度上的均值和方差
-
γ 、 β \gamma 、\beta γ、β 是可学习的缩放平移参数
-
ϵ \epsilon ϵ 是一个小常数,用于防止除零错误。
scale
和shift
:可训练参数,用于在归一化后调整数据的缩放和偏移。有偏方差:在上述方差计算中,设置
unbiased=False
,意味着使用公式 ∑ i ( x − x ‾ ) n \frac{\sum_i(x- \overline x)}{n} n∑i(x−x),不包含贝塞尔校正。其中 n 是样本大小(此处为特征或列的数量);该公式不包含贝塞尔校正(即在分母中使用n-1
),因此提供的是方差的有偏估计。(对于 LLMs,嵌入维度n
非常大,使用 n 和n-1
之间的差异可以忽略不计,GPT-2 是在归一化层中使用有偏方差进行训练的,因此为了与后续章节中加载的预训练权重兼容,我们也采用了这一设置。)ln = LayerNorm(emb_dim=5) out_ln = ln(batch_example) mean = out_ln.mean(dim=-1, keepdim=True) var = out_ln.var(dim=-1, unbiased=False, keepdim=True)print("Mean:\n", mean) print("Variance:\n", var)"""输出""" Mean:tensor([[ -0.0000],[ 0.0000]], grad_fn=<MeanBackward1>) Variance:tensor([[1.0000],[1.0000]], grad_fn=<VarBackward0>)
-
-
所以、本节至此,我们介绍了实现GPT架构所需的构建块之一,如下图中打勾的部分