大模型原理:实现具有GELU激活函数的前馈神经网络

GELU激活函数

详见 链接

实现

这里为了理解,用手动实现,实际 pytorch 内置了实现类

import torch
import torch.nn as nn
import matplotlib.pyplot as plt


class GELU(nn.Module):

    def __init__(self):
        super().__init__()

    def forward(self, x):
        return 0.5 * x * (1 + torch.tanh(
            torch.sqrt(torch.tensor(2.0 / torch.pi)) * (x + 0.044715 * torch.pow(x, 3))
        ))

绘制图像

为了直观地比较GELU函数与ReLU函数,将它们并排绘制出来

gelu = GELU()
relu = nn.ReLU()
x = torch.linspace(-3, 3, 100)
print("x.shape:", x.shape)
print("x:\n", x)
y_gelu = gelu(x)
y_relu = relu(x)
plt.figure(figsize=(8, 3))
for i, (y, label) in enumerate(zip([y_gelu, y_relu], ["GELU", "ReLU"]), 1):
    plt.subplot(1, 2,i)
    plt.plot(x, y)
    plt.title(f"{label} activation function")
    plt.xlabel("x")
    plt.ylabel(f"{label}(x)")
    plt.grid(True)
plt.tight_layout()
plt.show()

执行结果,x 轴表示函数输入值,y 轴表示函数输出值:

从上图可以看到:

  • ReLU(右)是一个分段线性函数,当输入为正数时直接输出输入值,否则输出0

  • GELU(左)则是一个平滑的非线性函数,它近似ReLU,但在几乎所有负值(除了在 x 约等于 -0.75 的位置外)上都有非零梯度

GELU优点

GELU的平滑特性可以在训练过程中带来更好的优化效果,因为它允许模型参数进行 更细微的调整。

相比之下,ReLU在零点处有一个 尖锐的拐角(参见上图的右图),有时会使得优化过程更加困难,特别是在深度或复杂的网络结构中。

此外,ReLU对 负输入 的输出为 0 ,而GELU对 负输入 会输出一个 小的非零值。这意味着在训练过程中,接收到 负输入 的神经元仍然可以 参与学习,只是贡献程度不如正输入大。

前馈神经网络

详见 前馈神经网络(feedforward neural network,FNN)

实现小型神经网络

将使用GELU函数来实现小型神经网络模块 FeedForward,该模块将在大语言模型的Transformer块中使用

# 实现小型神经网络
print("\n实现小型神经网络------")
class FeedForward(nn.Module):
    def __init__(self, cfg):
        """
        由两个线性层和一个GELU激活函数组成
        :param cfg:
        """
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
            GELU(),
            nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
        )

    def forward(self, x):
        return self.layers(x)

解释

FeedForward 类是一个小型神经网络,由 两个线性层 和 一个GELU激活函数 组成。
在参数量为1.24亿的GPT模型中,该模块通过 GPT_CONFIG_124M 字典接收输入批次,其中每个词元的嵌入维度为 768,即 GPT_CONFIG_124M["emb_dim"] = 768。

下图展示了当输入传递给这个小型前馈神经网络时,嵌入维度是如何被操作的:

执行

创建一个词元嵌入维度为 768 的 FeedForward 对象,然后将一个包含 2个样本 且 每个样本有3个词元 的批次输入提供给它:

GPT_CONFIG_124M = {
    "vocab_size": 50257,     # 词汇表大小
    "context_length": 1024,  # 上下文长度
    "emb_dim": 768,          # 嵌入维度
    "n_heads": 12,           # 注意力头的数量
    "n_layers": 12,          # 层数
    "drop_rate": 0.1,        # dropout率
    "qkv_bias": False        # 查询-键-值偏置
}

ffn = FeedForward(GPT_CONFIG_124M)
x = torch.rand(2, 3, 768)
out = ffn(x)
print("神经网络输出结果的shape:", out.shape)
print("神经网络输出结果:\n", out)

执行结果:

torch.Size([2, 3, 768])

说明

FeedForward 模块在提升模型学习和泛化能力方面非常关键。该模块的 输入和输出维度保持一致,执行过程:

  1. 通过第一个线性层将嵌入维度 扩展到更高的维度,从768维扩展到3072维

  2. 扩展之后,应用 非线性GELU激活函数

  3. 然后通过 第二个线性变换 将维度 缩回原始大小,即:将3072维压缩回768维

好处

  • 这种设计允许模型探索更丰富的表示空间
  • 输入维度和输出维度的一致性 简化了架构,在后续堆叠多个层时 无须调整维度,从而增强了模型的扩展能力

总结

已经实现了大部分大语言模型的构建块。接下来,将介绍插入在神经网络不同层之间的快捷连接的概念

其中黑色勾选标记表示已经构建完成的部分


原文出处:/show_1GW2eUXqs4Of.html