大模型原理:构建GPT-2大语言模型-理解层归一化原理

提出问题

由于 梯度消失 或 梯度爆炸 等问题,会导致训练过程不稳定,使网络 难以有效地调整权重,从而使学习过程难以找到一组 最小化损失函数的参数(权重)。

换句话说,网络难以学习数据中的潜在模式,从而无法进行准确预测或决策。

解决

使用归一化,将数据转为 均值为0且方差(单位方差)为1 的标准正太分布

介绍

实现 层归一化,以提高神经网络训练的 稳定性 和 效率。

原理

调整神经网络层的激活(输出),使其 均值为0且方差(单位方差)为1。

这种调整有助于加速权重的有效收敛,并确保训练过程的一致性和可靠性。

详见:

执行位置

在GPT-2和当前的Transformer架构中,层归一化 通常在 多头注意力模块 的 前后进行。

同时,层归一化还应用于 最终输出层之前。

下图是 层归一化 的图示,其中层的 6个输出(激活值)被调整为 均值为0且方差为1

例子

实现了一个具有 5个输入 和 6个输出 的神经网络层,并将其应用于两个输入示例:

import torch
import torch.nn as nn

torch.manual_seed(123)
# 生成2行5列张量,作为两个训练样本,每个样本包含5个维度(特征)
batch_example = torch.randn(2, 5)
# 神经网络层包括一个线性层和一个非线性激活函数ReLU
layer = nn.Sequential(nn.Linear(5, 6), nn.ReLU())
# 执行计算
out = layer(batch_example)
print(out)
代码解释:

编写的神经网络层包括 一个线性层 和 一个非线性激活函数ReLU

ReLU 是神经网络中的一种标准激活函数,详见 链接。简单理解:只是简单地将负输入值设为0,从而确保层的输出值都是正值,这也解释了为什么结果层的输出中 不包含负值

执行结果
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>)
Mean:
 tensor([[0.1324],
        [0.2170]], grad_fn=<MeanBackward1>)
Variance:
 tensor([[0.0231],
        [0.0398]], grad_fn=<VarBackward0>)

输出结果中:

  • 第1行显示了第一个输入的层输出
  • 第2行显示了第二个输入的层输出

查看输出结果的均值和方差

在对这些输出应用层归一化之前,先检查一下均值和方差:

print("查看输出结果的均值、方差--------")
# 沿着最后一个维度(列)计算均值,且输出维度与输入维度相同
mean = out.mean(dim=-1, keepdim=True)
# 沿着最后一个维度(列)计算方差,且输出维度与输入维度相同
var = out.var(dim=-1, keepdim=True)
print("均值:\n", mean)
print("方差:\n", var)
执行结果:
查看输出结果的均值、方差--------
均值:
 tensor([[0.1324],
        [0.2170]], grad_fn=<MeanBackward1>)
方差:
 tensor([[0.0231],
        [0.0398]], grad_fn=<VarBackward0>)

输出结果中:

  • 第1行是第一个输入行的均值
  • 第2行是第二个输入行的均值

对输出进行层归一化操作

具体方法是 减去均值,并将结果 除以方差的平方根(也就是标准差):

print("对输出进行层归一化操作--------")
#  减去均值,并将结果 除以方差的平方根(也就是标准差)
out_norm = (out - mean) / torch.sqrt(var)
# 沿着最后一个维度(列)计算均值,且输出维度与输入维度相同
mean = out_norm.mean(dim=-1, keepdim=True)
# 沿着最后一个维度(列)计算方差,且输出维度与输入维度相同
var = out_norm.var(dim=-1, keepdim=True)
print("归一化结果:\n", out_norm)
print("均值:\n", mean)
print("方差:\n", var)
执行结果:
对输出进行层归一化操作--------
归一化结果:
 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>)
均值:
 tensor([[9.9341e-09],
        [0.0000e+00]], grad_fn=<MeanBackward1>)
方差:
 tensor([[1.0000],
        [1.0000]], grad_fn=<VarBackward0>)

从结果可以看出,归一化后的层输出现在也 包含负值,其均值为 0,方差为1

提示: 均值 9.9341e-09是用科学记数法来表示,由于计算机表示数值的有限精度存在一些小的数值误差,非常接近0,但不是完全为0

关闭科学记数法

为了提高可读性,可以通过将 sci_mode 设置为 False 来关闭科学记数法,从而在打印张量值时避免使用科学记数法:

# 关闭科学记数法
torch.set_printoptions(sci_mode=False)
print("均值:\n", mean)
print("方差:\n", var)

执行结果:

均值:
 tensor([[0.0000],
        [0.0000]], grad_fn=<MeanBackward1>)
方差:
 tensor([[1.0000],
        [1.0000]], grad_fn=<VarBackward0>)

原文出处:/show_1GW2e9tQZMtc.html