在这里插入图片描述
1.数据预处理:原始数据进行词元化,以及通过,依据词汇表生成ID编号,对ID编号随机生成嵌入向量,特别注意的是嵌入向量事实上也是一个权重,通过反向传播进行更新。

2.掩码多头注意力机制:增加掩码多头注意力机制,其中增加掩码机制是为了确定序列的输出的因果关系,多头注意力机制是为了对不同路径的来源数据进行上下文联系。

3.LLM架构transform块:进行层归一化,GELU激活函数,前馈神经网络,快捷连接。

1.大语言模型整体架构

在这里插入图片描述
数据预处理,掩码多头注意力机制已经学过了,接下来进行最重要的LLM架构的transform块的学习。
#1 Transform块总体概述
整个transform块包含以下内容,层归一化,GELU激活函数,前馈神经网络,快捷连接等等。
在这里插入图片描述

2 大语言的Transformer模块

2.1 层归一化

层归一化是为了调整神经网络的输出,让其均值为0,方差为1。目的是为了神经网络更加稳定。具体做法是依据以下的数学公式,对emb_dim维度上进行 μ = x − μ σ \mu=\frac{x-\mu}{\sigma} μ=σxμ,最终能够对该维度嵌入向量进行归一化,最后再加上动态可训练参数缩放和平移。

#%% 层归一化
import torch #导入torch包 类似于一个目录,包含了__init__.py以及其他模块的py文件或者其他子包
import torch.nn as nn # 导入torch包中的nn模块 一个模块就是一个.py文件,一个py文件下有函数以及类
torch.manual_seed(123) #固定随机种子
batch_example = torch.randn(2,5) #随机生成两行五列的张量,然后每行是均值为0,方差为1的正态分布
print("batch_example,随机生成的输入为:",batch_example)
layer = nn.Sequential(nn.Linear(5,6),nn.ReLU()) # 容器类装有神经网络类,并通过序列连接,输入连输出,输出也是一个神经网络模块
out = layer(batch_example)
print("自定义的神经网络结构的输出为:",out)
mean = out.mean(dim=-1,keepdim=True) #按照列的方向进行求均值,同时保持维度不变
var = out.var(dim=-1,keepdim=True) #按照行的方向进行求均值,同时保持维度不变
print("未归一化的输出的均值为:",mean,"未归一化的输出的方差为:",var)
#接下来进行归一化
out_norm = (out- mean)/torch.sqrt(var)
torch.set_printoptions(sci_mode=False)
print("归一化后的均值为:",out_norm.mean(dim=-1,keepdim=True))
print("归一化后的方差为:",out_norm.var(dim=-1,keepdim=True))

在这里插入图片描述
将上述归一化代码进行整合在一个类中:

class LayerNorm(nn.Module):
    def __init__(self, emb_dim):
        super().__init__()
        self.eps = 1e-5
        self.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

最后测试用例:

#%% 归一化测试用例
import torch #导入torch包 类似于一个目录,包含了__init__.py以及其他模块的py文件或者其他子包
import torch.nn as nn # 导入torch包中的nn模块 一个模块就是一个.py文件,一个py文件下有函数以及类
torch.manual_seed(123) #固定随机种子
batch_example = torch.randn(2,5) #随机生成两行五列的张量,然后每行是均值为0,方差为1的正态分布
print("batch_example,随机生成的输入为:",batch_example)
class LayerNorm(nn.Module):
    def __init__(self, emb_dim):
        super().__init__()
        self.eps = 1e-5
        self.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
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)

2.2 GELU激活函数

精确的定义 G E L U ( x ) = x ⋅ ϕ ( x ) GELU(x)=x \cdot \phi{(x)} GELU(x)=xϕ(x),其中 ϕ ( x ) \phi{(x)} ϕ(x)是标准高斯分布的累积分布函数,但是在实际操作中,我们会使用一种计算量较小的近似实现,可通过曲线拟合的方法近似得到, G E L U ( x ) ≈ 0.5 ⋅ x ⋅ { 1 + tanh ⁡ [ π 2 ⋅ ( x + 0.044715 ⋅ x 3 ) ] } GELU(x) \approx 0.5 \cdot x \cdot \{1+\tanh[\sqrt{\frac{\pi}{2}} \cdot (x+0.044715 \cdot x^{3})]\} GELU(x)0.5x{1+tanh[2π (x+0.044715x3)]}

#%% GELU激活函数的前馈神经网络
#GELU激活类
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/torch.pi)))*(x+0.044715 * torch.power(x,3))))
# GELU激活类与ReLU激活类的对比
gelu,relu = GELU(),nn.ReLU() #神经网络初始化
x = torch.linspace(-3,3,100)#生成-3到3之间的100个数
y_gelu = GELU(x)#前向传播
y_relu= nn.ReLU(x)#前向传播
plt.figure(figsize=(8,3))
for i,(label,y) in enumerate(zip(["ReLU","GELU"],[y_relu,y_gelu]),1):
    plt.subplot(1,2,i)
    plt.plot(x,y)
    plt.title(f"{label} activation function")
    plt.xlabel("x")
    plt.ylabel("{label}(x)")
    plt.grid(True)
plt.tight_layout()
plt.show()

在这里插入图片描述
RELU激活函数存在两个问题,第一个问题是当x=0的梯度不存在,第二个问题,输入小于零的输出部分直接被置为0,没有后续神经元的运算。从GELU激活函数与ReLU激活函数对比图可以看出,GELU在小于零部分梯度存在,同时在输入小于0的部分,输出接近于0,并没有置零,还能参与后续的运算。

2.3 前馈神经网络

前馈神经网络先放大输入的节点数,经过GELU激活函数,然后再缩小节点数到与原来的输入的节点数相同,通过这样一个过程,相当于去粗取精。
在这里插入图片描述

#%% 前馈神经网络
import torch
import torch.nn as nn
class FeedForward(nn.Module):#通过大模型进行层数的放大,然后再将层数缩小
    def __init__(self,cfg):#参数初始化,以及神经网络结构的定义
        super().__init__()
        self.layers = nn.Sequential(nn.Linear(cfg['emb_dim'],4*cfg['emb_dim']),nn.GELU(),nn.Linear(4*cfg['emb_dim'],cfg['emb_dim']))#用容器来装载神经网络结构

    def forward(self,x):#定义输入与输出的关系
        return self.layers(x)

2.4 快捷连接

残差连接实际上就是建立一个输入直接连接到输出,这样的话,下一层输入就变成了上一层的输出和上一层的输入的叠加。建立残差连接能够缓解梯度消失问题。
在这里插入图片描述

#%% 残差连接 shortcut
import torch
import torch.nn as nn
class ShortCutNn(nn.Module):
    def __init__(self,layer_sizes,use_shortcut):#参数初始化以及网络结构的定义
        super().__init__()
        self.use_shortcut = use_shortcut
        self.layers = nn.ModuleList([nn.Sequential(nn.Linear(layer_sizes[0],layer_sizes[1]),nn.GELU())#定义网络列表
                                    ,nn.Sequential(nn.Linear(layer_sizes[1],layer_sizes[2]),nn.GELU())
                                    ,nn.Sequential(nn.Linear(layer_sizes[2],layer_sizes[3]),nn.GELU())
                                    ,nn.Sequential(nn.Linear(layer_sizes[3],layer_sizes[4]),nn.GELU())
                                    ,nn.Sequential(nn.Linear(layer_sizes[4],layer_sizes[5]),nn.GELU())])
    def forward(self,x):
        for layer in self.layers:
            layer_out = layer(x)
            if self.use_shortcut and x.shape == layer_out.shape:
                x = x + layer_out
            else:
                x = layer_out
        return x

3 附录

3.1 anaconda+python环境搭建

接下来进行代码准备,首先下载所有的requirements.txt中的所有依赖库,本次实验是在conda+pycharm环境下,推荐大家在conda+pycharm下进行:
首先创建conda下的环境LLM

conda create --p D:/.conda/envs/LLM 

在这里插入图片描述
然后激活conda下的LLM环境:

conda activate  D:/.conda/envs/LLM 
conda install requirements.txt

在这里插入图片描述

#%% 1 初始化信息查看
from importlib.metadata import version
print("matplotlib version:", version("matplotlib"))
print("torch version:", version("torch"))
print("tiktoken version:", version("tiktoken"))
#%% 2 配置GPT模型参数配置
GPT_CONFIG_124M = {
    "vocab_size": 50257,    # Vocabulary size 表示会被BPE分词器使用的由50257个单词的词汇表
    "context_length": 1024, # Context length  能够处理最大词元token的个数
    "emb_dim": 768,         # Embedding dimension 嵌入向量的纬度
    "n_heads": 12,          # Number of attention heads 多头的头数
    "n_layers": 12,         # Number of layers 神经网络的层数
    "drop_rate": 0.1,       # Dropout rate 丢弃率,为了防止过拟合,会随机丢弃10%的隐藏单元
    "qkv_bias": False       # Query-Key-Value bias qkv的偏置项开启与否,默认是关闭
}
nn 用法
Embedding
Dropout
Sequential
Linear

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐