自己做的网站如何上首页,最新任免名单最新,科技部部长,全国免费发布信息网站大全Transformer的优势
相比之前占领市场的LSTM和GRU模型#xff0c;Transformer有两个显著的优势#xff1a;
1. Transformer能够利用分布式GPU进行并行训练#xff0c;提升模型训练效率。
2. 在分析预测更长的文本时#xff0c;捕捉间隔较长的语义关联效果更好#xff0e…Transformer的优势
相比之前占领市场的LSTM和GRU模型Transformer有两个显著的优势
1. Transformer能够利用分布式GPU进行并行训练提升模型训练效率。
2. 在分析预测更长的文本时捕捉间隔较长的语义关联效果更好
认识Transformer架构
Transformer模型的作用
基于seq2seq架构的transformer模型可以完成NLP领域研究的典型任务如机器翻译文本生成等同时又可以构建预训练语言模型用于不同任务的迁移学习
声明
在接下来的架构分析中我们将假设使用Transformer模型架构处理从一种语言文本到另一种语言文本的翻译工作因此很多命名方式遵循NLP中的规则比如Embeddding层将称作文本嵌入层Embedding层产生的张量称为词嵌入张量它的最后一维将称作词向量等
Transformer总体架构可分为四个部分
输入部分
源文本嵌入层及其位置编码器
文本嵌入层的作用无论是源文本嵌入还是目标文本嵌入都是为了将文本中词汇的数字表示转变为向量表示希望在这样的高维空间捕捉词汇间的关系
位置编码器的作用因为在Transformer的编码器结构中并没有针对词汇位置信息的处理因此需要在Embedding层后加入位置编码器将词汇位置不同可能会产生不同语义的信息加入到词嵌入张量中以弥补位置信息的缺失
# 构建Embedding类实现文本嵌入层代码
import torch
import torch.nn as nn
import math
from torch.autograd import Variableclass Embeddings(nn.Module):def __init__(self, d_model, vocab):类的初始化Args:d_model (_type_): 词嵌入的维度vocab (_type_): 词表的大小super(Embeddings, self).__init__()self.lut nn.Embedding(vocab, d_model)self.d_model d_modeldef forward(self, x):前向传播函数Args:x (_type_): 输入的索引张量形状为 (L, N)Returns:_type_: 词嵌入张量形状为 (L, N, d_model)return self.lut(x) * math.sqrt(self.d_model)
文本嵌入层使用
d_model512
vocab1000
xVariable(torch.LongTensor([[1,2,3,4,5],[6,7,8,9,10]]))
embedding_layerEmbeddings(d_model,vocab)
outputembedding_layer(x)
print(output)
位置编码器
# 位置编码器
class PositionalEncoding(nn.Module):def __init__(self, d_model, dropout, max_len5000):位置编码器Args:d_model (_type_): 词嵌入的维度dropout (_type_): 置零的比例让一定的神经元失效max_len (int, optional): 每个句子的最大长度. Defaults to 5000.super(PositionalEncoding, self).__init__()self.dropout nn.Dropout(pdropout)# 初始化位置编码矩阵pe torch.zeros(max_len, d_model)position torch.arange(0, max_len).unsqueeze(1)div_termtorch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) /d_model))pe[:, 0::2] torch.sin(position * div_term)pe[:, 1::2] torch.cos(position * div_term)pe pe.unsqueeze(0)self.register_buffer(pe, pe)def forward(self, x):_summary_Args:x (_type_): 文本序列的的词嵌入表示xxVariable(self.pe[:, :x.size(1)], requires_gradFalse)return self.dropout(x)
使用
# 绘制词汇向量中特征的分布曲线
import matplotlib.pyplot as plt
import numpy as npplt.figure(figsize(15, 5))
pePositionalEncoding(20,0)
ype(Variable(torch.zeros(1, 100,20)))
plt.plot(np.arange(100), y[0, :, 4:8].data.numpy())
plt.legend([dim %d%p for p in [4, 5, 6, 7]])
# 输出效果分析
# 每条颜色的曲线代表某一个词汇中的特征在不同位置的含义
# 保证同一词汇随着所在位置不同它对应位置嵌入向量会发生变化
# 正弦波和余弦波的值域范围都是1到1这又很好的控制了嵌入数值的大小有助于梯度的快速计算
目标文本嵌入层及其位置编码器
输出部分
线性层的作用通过对上一步的线性变化得到指定维度的输出也就是转换维度的作用
softInax层的作用使最后一维的向量中的数字缩放到0-1的概率值域内并满足他们的和为1.
import torch.nn.functional as F
class Generator(nn.Module):def __init__(self, d_model, vocab):super(Generator, self).__init__()self.proj nn.Linear(d_model, vocab)def forward(self, x):return F.log_softmax(self.proj(x), dim-1)
编码器部分
由N个编码器层堆叠而成
每个编码器层由两个子层连接结构组成
第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
class Encoder(nn.Module):def __init__(self, layer, N):_summary_Args:layer (_type_): 编码器层N (_type_): 编码器层个数super(Encoder, self).__init__()self.layersclones(layer, N)self.normLayerNorm(layer.size)def forward(self, x, mask):for layer in self.layers:xlayer(x, mask)return self.norm(x)
解码器部分
由N个解码器层堆叠而成
每个解码器层由三个子层连接结构组成
第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接
第二个子层连接结构包括一个多头注意力子层和规范化层以及一个残差连接
第三个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接
class Decoder(nn.Module):def __init__(self, layer, N):super(Decoder, self).__init__()self.layersclones(layer, N)self.normLayerNorm(layer.size)def forward(self, x, memory, src_mask, tgt_mask):for layer in self.layers:xlayer(x, memory, src_mask, tgt_mask)return self.norm(x)
专用名词
掩码张量
什么是掩码张量
掩代表遮掩码就是我们张量中的数值它的尺寸不定里面一般只有1和0的元素代表位置被遮掩或者不被遮掩至于是0位置被遮掩还是1位置被遮掩可以自定义因此它的作用就是让另外一个张量中的一些数值被遮掩也可以说被替换它的表现形式是一个张量。
掩码张量的作用
在transformer中掩码张量的主要作用在应用attention将在下一小节讲解时有一些生成的attention张量中的值计算有可能已知了未来信息而得到的未来信息被看到是因为训练时会把整个输出结果都一次性进行Embedding但是理论上解码器的的输出却不是一次就能产生最终结果的而是一次次通过上一次结果综合得出的因此未来的信息可能被提前利用所以我们会进行遮掩。
def subsquest_mask(size):生成掩码张量代码Args:size (_type_): 掩码张量最后两个维度的大小它的最后两维形成一个方阵attn_shape(1, size, size)subsquest_masknp.triu(np.ones(attn_shape), k1).astype(uint8)return torch.from_numpy(1-subsquest_mask)
注意力机制
什么是注意力
我们观察事物时之所以能够快速判断一种事物当然允许判断是错误的, 是因为我们大脑能够很快把注意力放在事物最具有辨识度的部分从而作出判断而并非是从头到尾的观察一遍事物后才能有判断结果正是基于这样的理论就产生了注意力机制
注意力计算规则
它需要三个指定的输入Q(query), K(key), V(value), 然后通过公式得到注意力的计算结果这个结果代表query在key和value作用下的表示而这个具体的计算规则有很多种我这里只介绍我们用到的这一种
什么是注意力机制
注意力机制是注意力计算规则能够应用的深度学习网络的载体除了注意力计算规则外还包括一些必要的全连接层以及相关张量处理使其与应用网络融为一体使用自注意力计算规则的注意力机制称为自注意力机制
注意力机制的计算规则
import torch.nn.functional as Fdef attention(query, key, value, maskNone, dropoutNone):注意力机制的实现代码Args:query (_type_): 查询向量,是一段准备被概括的文本key (_type_): 键向量,是给出的提示value (_type_): 值向量,是大脑中的对提示K的延伸mask (_type_, optional): 掩码张量. Defaults to None.dropout (_type_, optional): 丢弃率. Defaults to None.Returns:_type_: 注意力机制的输出d_kquery.size(-1)scorestorch.matmul(query, key.transpose(-2, -1))/math.sqrt(d_k)if mask is not None:scoresscores.masked_fill(mask0, -1e9)p_attnF.softmax(scores, dim-1)if dropout is not None:p_attndropout(p_attn)return torch.matmul(p_attn, value), p_attn
多头注意力机制
只有使用了一组线性变化层即三个变换张量对Q,K,V分别进行线性变换这些变换不会改变原有张量的尺寸因此每个变换矩阵都是方阵。
得到输出结果后多头的作用才开始显现每个头开始从词义层面分割输出的张量也就是每个头都想获得一组Q,K,V 进行注意力机制的计算但是句子中的每个词的表示只获得一部分也就是只分割了最后一维的词嵌入向量这就是所谓的多头。
将每个头的获得的输入送到注意力机制中就形成多头注意力机制
多头注意力机制的作用这种结构设计能让每个注意力机制去优化每个词汇的不同特征部分从而均衡同一种注意力机制可能产生的偏差让词义拥有来自更多元的表达实验表明可以从而提升模型效果。
import copydef clones(module, N):用于生成相同的网络层Args:module (_type_): 要克隆的目标网络层N (_type_): 需要克隆的数量return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])class MultiHeadedAttention(nn.Module):def __init__(self, head, embedding_dim, dropout0.1):多头注意力机制的实现代码Args:head (_type_): 头数embedding_dim (_type_): 词嵌入维度dropout (_type_, optional): 丢弃率. Defaults to 0.1.super(MultiHeadedAttention, self).__init__()# 保证d_model是头数的整数倍assert embedding_dim%head0self.d_kembedding_dim // headself.headheadself.embedding_dimembedding_dimself.linearsclones(nn.Linear(embedding_dim, embedding_dim), 4)self.attnNoneself.dropoutnn.Dropout(pdropout)def forward(self, query, key, value, maskNone):前向逻辑结构Args:query (_type_): _description_key (_type_): _description_value (_type_): _description_mask (_type_, optional): _description_. Defaults to None.if mask is not None:maskmaskbatch_sizequery.size(0)query, key, value [model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2) for model, x in zip(self.linears, (query, key, value))]x, self.attn attention(query, key, value, mask, self.dropout)x x.transpose(1, 2).contiguous().view(batch_size, -1, self.head * self.d_k)return self.linears[-1](x)
前馈全连接层
在Transformer中前馈全连接层就是具有两层线性层的全连接网络
前馈全连接层的作用
考虑注意力机制可能对复杂过程的拟合程度不够通过增加两层网络来增强模型的能力
class PositionwiseFeedForward(nn.Module):def __init__(self, d_model, d_ff, dropout0.1):_summary_Args:d_model (_type_): _description_d_ff (_type_): 词嵌入的维度dropout (float, optional): _description_. Defaults to 0.1.super(PositionwiseFeedForward, self).__init__()self.w1nn.Linear(d_model, d_ff)self.w2nn.Linear(d_ff, d_model)self.dropoutnn.Dropout(dropout)def forward(self, x):return self.w2(self.dropout(torch.relu(self.w1(x))))
规范化层
规范化层的作用
它是所有深层网络模型都需要的标准网络层因为随着网络层数的增加通过多层的计算后参数可能开始出现过大或过小的情况这样可能会导致学习过程出现异常模型可能收敛非常的慢因此都会在一定层数后接规范化层进行数值的规范化使其特征数值在合理范围内
class LayerNorm(nn.Module):def __init__(self, features, eps1e-6):_summary_Args:features (_type_): 词嵌入维度eps (_type_, optional): 防止分母为0. Defaults to 1e-6.super(LayerNorm, self).__init__()self.a2nn.Parameter(torch.ones(features))self.b2nn.Parameter(torch.zeros(features))self.epsepsdef forward(self, x):_summary_Args:x (_type_): 上一层的输出meanx.mean(-1, keepdimTrue)stdx.std(-1, keepdimTrue)return self.a2*(x-mean)/(stdself.eps)self.b2
子层连接结构
输入到每个子层以及规范化层的过程中还使用了残差链接跳跃连接因此我们把这一部结构整体叫做子层连接代表子层及其链接结构在每个编码器层中都有两个子层这两个子层加上周围的链接结构就形成了两个子层连接结构
class SublayerConnection(nn.Module):def __init__(self, size, dropout):_summary_Args:size (_type_): 词嵌入维度dropout (_type_): 随机抑制比率super(SublayerConnection, self).__init__()self.normLayerNorm(size)self.dropoutnn.Dropout(dropout)def forward(self, x, sublayer):return xself.dropout(sublayer(self.norm(x)))
编码器层
编码器层的作用作为编码器的组成单元每个编码器层完成一次对输入的特征提取过程即编码过程
class EncoderLayer(nn.Module):def __init__(self, size, self_attn, feed_forward, dropout):_summary_Args:size (_type_): 词维度大小self_attn (_type_): 多头注意力层feed_forward (_type_): 实例化的前馈全连接层dropout (_type_): _description_super(EncoderLayer, self).__init__()self.self_attnself_attnself.feed_forwardfeed_forwardself.sublayerclones(SublayerConnection(size, dropout),2)self.sizesizedef forward(self, x, mask):xself.sublayer[0](x, lambda x:self.self_attn(x, x, x, mask))return self.sublayer[1](x, self.feed_forward)
解码器层
class DecoderLayer(nn.Module):def __init__(self, size, self_attn, src_attn, feed_forward, dropout):_summary_Args:size (_type_): 词维度大小self_attn (_type_): 注意力机制自注意力src_attn (_type_): 非自注意力机制feed_forward (_type_): 前馈全连接层dropout (_type_): _description_super(DecoderLayer, self).__init__()self.sizesizeself.self_attnself_attnself.src_attnsrc_attnself.feed_forwardfeed_forwardself.sublayerclones(SublayerConnection(size, dropout), 3)def forward(self, x, memory, src_mask, tgt_mask):_summary_Args:x (_type_): 上一层输入memory (_type_): 来自编码器层语义储存变量src_mask (_type_): 源数据掩码张量tgt_mask (_type_): 目标数据掩码张量mmemoryxself.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask))xself.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))return self.sublayer[2](x, self.feed_forward)