2 Transformer 模型的输入部分
2. Transformer 模型的输入部分
目标:
- 掌握文本嵌入层和位置编码层的作用
- 掌握文本嵌入层和位置编码层的实现过程
输入部分包含:
- 源文本嵌入层及其位置编码
- 目标文本嵌入层及其位置编码
2.1 Transformer的文本嵌入层(词嵌入)
- 文本嵌入层的作用: 文本嵌入层是为了实现将文本中词汇的数字表示转为向量表示,从而希望在这种更高维度的空间中捕捉词汇间的关系。
文本嵌入层的代码实现:
import imp
from cycler import V
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
import math
import copy
import matplotlib.pyplot as plt
# embedding = nn.Embedding(10,3)
# input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
# print(embedding(input))
# print(embedding(input).shape)
# print(embedding(input).shape[-1])
# embedding = nn.Embedding(10,3, padding_idx=0)
# input = torch.LongTensor([[0,2,0,5],[0,3,2,9]])
# print(embedding(input))
print('-------------------')
# 构建 Embedding 类来实现文本嵌入曾
class Embeddings(nn.Mod9ule):
def __init__(self, d_model, vocab):
# d_model: 词嵌入的维度
# vocab: 词表大小
super(Embeddings, self).__init__()
self.lut = nn.Embedding(vocab, d_model)
self.d_model = d_model
def forward(self, x):
return self.lut(x) * math.sqrt(self.d_model)
d_model = 512
vocab = 100
x = Variable(torch.LongTensor([[1,2,4,5],[4,3,2,9]]))
# 实例化 Embeddings 类
embedding = Embeddings(d_model, vocab)
embr = embedding(x)
print(embr)
print(embr.shape)
2.2 Transformer的位置编码层
-
位置编码层的作用:
- 位置编码层是为了赋予句子单词一个独有的位置编码表示,位置编码是通过正弦余弦函数分别将偶数位置和奇数位置编码出来(都是唯一的)。
- 位置编码可以看作是一个常量,其不参与模型的反向传播过程。位置编码是预先计算好的。
-
位置编码层的实现:
2.3 编码器部分实现
2.3.1 掩码张量
- 什么是掩码张量?
- 掩码表示遮掩掉张量中的一些数值。掩码张量的尺寸不是固定的,掩码张量中通常只有1,0两种数值,其表现形式是张量。
- 掩码张量的作用?
- 掩码张量的作用就是用来让另一个张量中的某些数值被遮掩(也可以说是被替换)。
- 掩码张量的实现过程:
2.3.2 注意力机制
- 什么是注意力计算规则和注意力机制?
- 注意力计算规则有三类,我们只讲一类;
- 注意力是指 针对一个事物(query),我们利用其关键信息(key),得到自己的理解(value)这一过程。
- 注意力机制是指应用注意力的载体,即使用注意力计算规程进行注意力计算的网络结构,我们都能称为注意力机制。
- Q、K、V的理解:以“段落主旨总结”为例,我们设段落为Q,则段落中的关键词就为K。基于段落信息Q,并利用其关键词K所得到的段落主旨,称之为V。
- 其中Q、K、V通常是通过对输入数据进行线性变换得到的,而这些线性变换是由可学习的权重矩阵W_Q、W_K、W_V(也称为线性变换矩阵)完成的。
- 注意力机制的实现过程:
2.3.3 多头注意力机制(MHA)
- 什么是多头注意力机制?
- 多头注意力(Multi-Head Attention)机制是 Transformer 模型中的关键组件,它不是指多个独立的注意力层,而是指在一个注意力层内部并行地执行多个注意力计算过程,每个过程称为一个“头”(head)。这些头相互独立,各自专注于输入数据的不同表示子空间(或者说,从不同的角度或特征维度上关注输入信息),然后将所有头的结果合并起来,以此来增强模型的注意力能力,使其能捕捉更丰富和多样的上下文依赖关系。
- 每个head仅关注每个词嵌入的一部分,即只分割最后一维的词嵌入向量。
- 在多头注意力机制中,有四组线性变换层(linear(input=embedding_dim,output=embedding_dim) 层)用来转换query、key、value矩阵以及用作最后的线性转换。
- 多头注意力层(Multi-Head Attention Layer)的参数主要包括以下几类:查询(Query)、键(Key)、值(Value)的线性变换权重矩阵W_Q、W_K、W_V;缩放系数(Scaling Factor) ; 最终输出的线性变换矩阵; Dropout层的丢弃概率 (一个超参数,决定了在训练期间随机丢弃多少比例的激活单元)。
- 多头注意力机制的作用:
- 让各个头去优化词嵌入的不同特征部分,以缓解单头注意力机制下可能会产生的偏差,同时也可以帮助模型学到更多元的语义信息。
2.3.5 前馈全连接层
- 什么是前馈全连接层?
- 在Transformer中,前馈全连接层表示一个具有两层线性层的全连接网络;
- 前馈全连接层的作用是什么?
- 前馈全连接层能够增强注意力机制对于复杂过程的拟合,能提高模型的性能。
- 前馈全连接层的代码实现PositionwiseFeedForward()
- PositionwiseFeedForward的实例化参数有d_model(词嵌入维度)、d_ff(线性变换维度)、dropout(置零比例(我喜欢写成dropout_rate));
- PositionwiseFeedForward的输入参数是x,x表示上一层的输出;
- PositionwiseFeedForward的输入参数size和其输出结果size一致。
- 前馈全连接层的代码实现
# 构建前馈全连接层网络
class PositionwiseFeedForward(nn.Module):
def __init__(self,d_model,d_ff,dropout_rate=0.1):
# d_model: 词嵌入的维度
# d_ff: 前馈全连接层的维度
# dropout: Dropout的概率
super(PositionwiseFeedForward,self).__init__()
# 实例化两个线性层对象
self.L1=nn.Linear(d_model,d_ff)
self.L2=nn.Linear(d_ff,d_model)
# 实例化Dropout对象
self.dropout=nn.Dropout(dropout_rate)
def forward(self,x):
# x 表示上一层的输出
return self.L2(self.dropout(F.relu(self.L1(x))))
# 定义相关参数
d_model=512
d_ff=64
dropout=0.2
x=mha_result
print("mha_result size:",mha_result.size())
# 实例化PositionwiseFeedForward类
ff = PositionwiseFeedForward(d_model,d_ff,dropout)
# 输出前馈全连接层的结果
ff_result = ff(x)
print("---------------------------")
print("--------ff_result----------")
print(ff_result)
print("ff_result size:",ff_result.size())
2.3.5 规范化层
-
规范化层的作用:
- 随着网络层数的增加,经过多层网络计算后的结果值可能会出现值过大或过小的情况,这种情况可能会导致模型的学习过程出现异常,如模型收敛速度变慢。因此规范化层的作用就是保证这些结果值处于正常合理范围内。
-
LayNorm的实例化参数:
- feature:词嵌入的维度(d_model);
- eps: 极小的正值
-
LayNorm的输入和输出:
- 输入x:上一网络层的输出
- 输出是经过规范化的特征表示
- LayNorm的输入和输出size一致
-
LayNorm的主要参数:
- 缩放因子
- 位移参数
- 上述两个参数都是模型的参数,在反向传播的过程中会被优化。
-
代码实现:
# 构建规范化层类
class LayNorm(nn.Module):
def __init__(self, features, eps):
super(LayNorm,self).__init__()
# 定义两个辅助变量 a2 b2
# a2:缩放因子
# b2:位移参数
self.a2 = nn.Parameter(torch.ones(features))
self.b2 = nn.Parameter(torch.zeros(features))
# 将极小正数值eps引入类中
self.eps = eps
def forward(self,x):
# LayNorm中的forward主要实现对词嵌入维度的均值和方差的计算
# 并同时保证计算过程中输入和输出维度的一致
# 之后再利用规范化公式进行计算
mean = x.mean(-1,keepdim=True)
std = x.std(-1,keepdim=True)
return self.a2*(x-mean)/(std+self.eps)+self.b2
# 定义相关参数
features=d_model=512
eps=1e-6
x=ff_result
print(ff_result)
# 实例化LayNorm对象
ln = LayNorm(features=features,eps=eps)
ln_result = ln(x)
print("---------------------------")
print("--------ln_result----------")
print(ln_result)
print("ln_result size:",ln_result.size())
2.3.6 子层连接结构
- 子层连接结构简称子层,其包含残差连接、归范化层以及相应子层网络的集合,如下图所示就是一个完整的子层连接结构:
- 子层连接结构是编码器和解码器的重要组成部分,其在编码器中有两个,在解码器中有三个。
- 子层连接结构SublayerConnection的主要参数
- 实例化参数
- size:词嵌入的维度(d_model)
- dropout_rate:Dropout的置零比率
- 输入参数
- x:子层连接结构的输入
- sublayer:表示子层连接结构对应的子网络(如Multi-HeadAttention网络)
- 实例化参数
- 子层连接结构的输入和输出的size一致
- 代码实现
# 实现子层连接结构
# 子层连接结构即残差连接、规范化层以及相应的子层结合体
class SublayerConnection(nn.Module):
def __init__(self, size, dropout_rate=0.1):
# size:词嵌入的维度
# dropout_rate:Dropout的概率
super(SublayerConnection,self).__init__()
# 实例化一个规范化层对象
self.norm = LayNorm(size,eps=1e-6)
# 实例化一个Dropout对象
self.dropout = nn.Dropout(dropout)
# 将size传入类中
self.size = size
def forward(self,x,sublayer):
# x: 子层连接的输入
# sublayer: 相应的子层网络
# 子层连接的输入和输出的size是一致的
return x+self.dropout(sublayer(self.norm(x)))
# 相关变量
size=d_model=512
head=8
dropout_rate=0.2
x=pe_result
mask=Variable(torch.zeros(8,4,4))
self_attn=MultiHeadAttention(head,d_model,dropout_rate)
sublayer=lambda x: self_attn(x,x,x,mask)
# 实例化子层连接对象
sc = SublayerConnection(size,dropout_rate)
sc_result=sc(x,sublayer)
print("---------------------------")
print("--------sc_result----------")
print(sc_result)
print(sc_result.size())
-
编码器层的作用:
- 是编码器的组成单元;
- 编码器层用来完成一次对输入特征的提取过程,即编码过程
-
编码器层的构成图
-
编码器层的主要参数
-
EncoderLayer的实例化参数:
- size: 词嵌入的维度
- self_attn: 多头注意力对象
- feedforward: 前馈全连接对象
- dropout_rate: Dropout的概率
-
EncoderLayer的输入参数:
- x:代表子层连接结构的输入
- mask:掩码张量
-
-
数据x经过编码器层之后,其size不会发生改变。
-
编码器层的代码实现:
# 构建编码器层
class EncoderLayer(nn.Module):
def __init__(self,size,self_attn,feedforward,dropout_rate):
# size: 词嵌入的维度
# self_attn: 多头注意力对象
# feedforward: 前馈全连接对象
# dropout_rate: Dropout的概率
super(EncoderLayer,self).__init__()
# 将多头注意力对象传入类中
self.self_attn = self_attn
# 将前馈全连接对象传入类中
self.feedforward = feedforward
# 将词嵌入维度传入类中
self.size = size
# 实例化子层连接对象
self.sublayer = clones(SublayerConnection(size,dropout_rate),2)
def forward(self,x,mask):
# x:代表子层连接结构的输入
# mask:掩码张量
# EncoderLayer中的foeward函数主要用来实现x在多头注意力子层结构和前馈全连接层子层结构间的传递
x=self.sublayer[0](x,lambda x: self.self_attn(x,x,x,mask))
return self.sublayer[1](x,self.feedforward)
# 相关参数
size=d_model=512
head=8
dropout_rate=0.2
d_ff=64
x=pe_result
# 实例化相关网络层
self_attn=MultiHeadAttention(head,d_model,dropout_rate)
feed_forward=PositionwiseFeedForward(d_model,d_ff,dropout_rate=dropout_rate)
mask=Variable(torch.zeros(8,4,4))
# 实例化EncoderLayer
el=EncoderLayer(size,self_attn,feed_forward,dropout_rate)
el_result=el(x,mask)
print("---------------------------")
print("--------el_result----------")
print(el_result)
print(el_result.size())
2.3.8 编码器
- 编码器的作用:
- 用于对输入(词嵌入)进行指定的特征提取,即编码过程;
- 由N个编码器层组成
- 编码器结构图
- 编码器Encoder的主要参数
- Encoder的实例化参数
- layer: 编码器层的实例化对象
- N: 编码器层的个数
- Encoder的输入参数
- x:Encoder的输入,即词嵌入pe_result
- mask:掩码张量
- Encoder的关键参数
- self.layers = clones(layer,N)
- Encoder的实例化参数
- 代码实现
# 构建编码器类
class Encoder(nn.Module):
def __init__(self,layer,N):
# layer: 编码器层的实例化对象
# N: 编码器层的个数
super(Encoder,self).__init__()
# 使用深度克隆函数clones将layer克隆N个
self.layers = clones(layer,N)
# 实例化规范化层对象
self.norm = LayNorm(layer.size)
def forward(self,x,mask):
# x:Encoder的输入,即词嵌入pe_result
# mask:掩码张量
# Encoder的forward函数主要用来实现多个EncoderLayer的堆叠
# x会依次经过各个编码器层,并在最后经过一次规范化层再输出
for layer in self.layers:
x=layer(x,mask)
return self.norm(x)
# 相关变量
size=d_model=512
head=8
dropout_rate=0.2
x=pe_result
d_ff=64
N=8
mask=Variable(torch.zeros(8,4,4))
# 实例化相关网络结构
c =copy.deepcopy
attn=MultiHeadAttention(head=head,embedding_dim=d_model)
ff=PositionwiseFeedForward(d_model=d_model,d_ff=d_ff,dropout_rate=dropout)
layer=EncoderLayer(size=size,self_attn=c(attn),feedforward=c(ff),dropout_rate=dropout_rate)
# 实例化Encoder
en = Encoder(layer,N)
en_result = en(x,mask)
print("---------------------------")
print("--------en_result----------")
print(en_result)
print(en_result.size())
2.4 解码器部分
2.4.1 解码器层
- 解码器层的作用:
- 是构成解码器的组成单元
- 每个解码器层都会根据给定的输入,向目标方向进行特征提取,即解码过程;
- 每个解码器层都由三个子层连接结构构成,分别为:
- 解码器多头自注意力子层;
- 编码器-解码器多头注意力子层;
- 前馈全连接子层
- 解码器层的结构图:
- 代码实现:
# 构建解码器层
class DecoderLayer(nn.Module):
def __init__(self,size,self_attn,src_attn,feed_forward,dropout_rate):
# size:词嵌入的维度
# self_attn:解码器自注意力实例化对象
# src_attn:编码器-解码器注意力实例化对象
# feed_forward:前馈全连接实例化对象
# dropout_rate:Dropout的概率
super(DecoderLayer,self).__init__()
# 将参数传入类中
self.size = size
self.self_attn = self_attn
self.src_attn = src_attn
self.feed_forward = feed_forward
self.dropout_rate = dropout_rate
# 使用clones函数实例化子层连接对象
self.layer=clones(SublayerConnection(size,dropout_rate),3)
def forward(self,x,memory,source_mask,target_mask):
# x:解码器的输入
# memory:编码器的输出
# source_mask:源数据的掩码张量
# target_mask:目标数据的掩码张量
m=memory
# x经过解码器多头自注意力子层结构
x=self.layer[0](x,lambda x:self.self_attn(x,x,x,target_mask))
# x经过编码器-解码器多头注意力子层结构
x=self.layer[1](x,lambda x:self.src_attn(x,m,m,source_mask))
# x经过前馈全连接子层结构
return self.layer[2](x,self.feed_forward)
# 相关参数
head=8
size=d_model=512
dropout_rate=0.2
d_ff=64
x=pe_result
memory=en_result
source_mask=target_mask=Variable(torch.zeros(8,4,4))
# 实例化相关网络
self_attn=src_attn=MultiHeadAttention(head=head,embedding_dim=d_model,dropout_rate=dropout_rate)
ff=PositionwiseFeedForward(d_model=d_model,d_ff=d_ff,dropout_rate=dropout_rate)
# 实例化解码器层
dl=DecoderLayer(size=size,self_attn=self_attn,src_attn=src_attn,feed_forward=ff,dropout_rate=dropout_rate)
dl_result=dl(x=x,memory=memory,source_mask=source_mask,target_mask=target_mask)
print("---------------------------")
print("--------dl_result----------")
print(dl_result)
print(dl_result.size())
2.4.2 解码器
-
解码器的作用:
- 根据编码器的结果以及解码器上一次预测结果,对下一次可能出现的值进行特征表示
-
解码器的结构图:
- 解码器的代码实现:
# 构建解码器
class Decoder(nn.Module):
def __init__(self,layer,N):
# layer:解码器层DecoderLayer的实例化对象
# N:解码器层的个数
super(Decoder,self).__init__()
# 使用clones克隆N个解码器层
self.layers = clones(layer,N)
# 实例化规范化层对象
self.norm=LayNorm(layer.size)
def forward(self,x,memory,source_mask,target_mask):
# x:表示目标数据的嵌入表示
# memory:表示编码器的输出
# source_mask:源数据的掩码张量
# target_mask:目标数据的掩码张量
# x 会依次经过N个解码器层
for layer in self.layers:
x=layer(x=x,memory=memory,source_mask=source_mask,target_mask=target_mask)
return self.norm(x)
# 相关变量
size=d_model=512
head=8
dropout_rate=0.2
d_ff=64
self_attn=src_attn=MultiHeadAttention(head=head,embedding_dim=d_model,dropout_rate=dropout_rate)
ff=PositionwiseFeedForward(d_model=d_model,d_ff=d_ff,dropout_rate=dropout_rate)
# Decoder的实例化变量
N=8
layer=DecoderLayer(size=size,self_attn=c(self_attn),src_attn=c(src_attn),feed_forward=c(ff),dropout_rate=dropout_rate)
# Decoder的输入变量
x=pe_result
memory=en_result
source_mask=target_mask=Variable(torch.zeros(8,4,4))
# 实例化Decoder对象
de=Decoder(layer=layer,N=N)
de_result=de(x=x,memory=memory,source_mask=source_mask,target_mask=target_mask)
print("---------------------------")
print("--------de_result----------")
print(de_result)
print(de_result.size())
2.5 输出部分
- 输出部分的组成:线性层和softmax层
- 线性层的作用:转换维度
- softmax的作用:将最后一维中的向量中数值缩放到0-1的概率区间内,并满足他们之和为1
- nn.functional工具包中包含的是只计算,而没有参数的网络层
- 输出层的网络结构:
- 代码实现
import torch.nn.functional as F
# 构建Transformer的输出部分
class Generator(nn.Module):
def __init__(self,d_model,vocab_size):
# d_model:词嵌入的维度
# vocab:词表大小
super(Generator,self).__init__()
# 实例化线性层对象
self.proj=nn.Linear(d_model,vocab_size)
def forward(self,x):
# x:表示Transformer的输出
# 返回线性层的输出
return F.log_softmax(self.proj(x),dim=-1)
# 输出部分Generator的实例化参数
d_model=512
vocab_size=1000
# 输出部分Generator的输入参数
x=de_result
# 实例化输出部分
gen = Generator(d_model=d_model,vocab_size=vocab_size)
gen_result=gen(x)
print("---------------------------")
print("--------gen_result----------")
print(gen_result)
print(gen_result.size())
print(gen_result[1])

浙公网安备 33010602011771号