赞
踩
Transformer的优势
# 1. Transformer能够利用分布式GPU进行并行训练,提升模型训练效率
# 2. 在分析预测更长文本时,捕捉间隔较长的语义关联效果更好,
作用:
基于seq2seq架构的transformer模型可以完成NLP领域研究的典型任务,如机器翻译,文本生成等,同时又可以构建预训练语言模型,用于其他任务
Transformer架构
输入部分
输出部分
编码器部分
解码器部分
文本词嵌入
作用:对输入文本数值化以方便拟合算法
import math
import torch
import torch.nn as nn
class Embeddings(nn.Module):
def __init(self, d_model, vocab):
"""
d_model: 词嵌入的维度
vocab: 词表的大小
"""
super(Emdedings,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)
位置编码器
作用:因为在transformer的编码器结构中,并没有对词汇位置信息的处理,所以需要在embedding层后加入位置编码器,将词汇位置不同可能会产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失
class PositionalEncoding(nn.Module): """ d_model: 词嵌入的维度 dropout: 置0比率 max_len: 句子最大长度 """ def __init__(self, d_model, dropout, max_len=5000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(p=dropout) # 初始化一个位置编码矩阵,它是0阵,大小维max_len * d_model pe = torch.zeros(max_len, d_model) # 绝对位置矩阵,词汇的绝对位置就是用它的索引去表示 position = torch.arange(0, max_len).unsqueeze(1) # 将位置信息加入到位置编码矩阵中,将绝对位置矩阵有max_len*1变成max_len*d_model, 覆盖原来的初始化位置编码矩阵 # 先需要一个1*d_model的变换矩阵div_term,其实div_term是一个1*d_model/2的矩阵 div_term = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000.)/d_model)) # 位置编码数值控制在-1,1之间有助于梯度下降 pe[:, 0::2] = torch.sin(position*div_term) pe[:, 1::2] = torch.cos(position*div_term) # 现在pe是一个二维矩阵,embedded之前要扩展一个维度 pe = pe.unsqueeze(0) # 将位置编码矩阵注册成模型的buffer,对模型效果有帮助,却不是模型结构中的超参数,不需要随着优化步骤进行更新的增益对象 # 注册之后我们可以在模型保存后重加载时和模型结构与参数一同加载 self.register_buffer('pe', pe) def forward(self, x): # x.size(1),根据输入句子的长度,截取pe的范围 print("位置编码:", Variable(self.pe[:, :x.size(1)]).size()) x = x + Variable(self.pe[:, :x.size(1)], requires_grad=False) return self.dropout(x)
多头注意力机制
作用:多头的本质是多个独立的attention计算,作为一个集成的作用,防止过拟合
# 注意力机制 def attention(query, key, value, mask=None, dropout=None): # 词嵌入维度大小 d_k = query.size(-1) # 计算注意力矩阵score scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k) # 判断是否使用掩码张量 if mask: scores = scores.masked_fill(mask == 0, -1e9) # 对scores进行最后一维进行softmax p_atten = F.softmax(scores, dim=-1) if dropout: p_atten = dropout(p_atten) return torch.matmul(p_atten, value), p_atten def clone(module, N): """ 实现模型深拷贝 :param module:要拷贝的目标网络层 :param N: 拷贝数量 """ return nn.ModuleList([copy.deepcopy(module) for _ in range(N)]) class MultiHeadedAttention(nn.Module): """ 多头注意力机制 head:头数 embedding_dim: 词嵌入维度 dropout: 置零率 """ def __init__(self, head, embedding_dim, dropout=0.1): super(MultiHeadedAttention, self).__init__() self.head = head self.attn = None self.dropout = nn.Dropout(p=dropout) # 判断head能否整除词嵌入维度 assert embedding_dim % head == 0 self.d_k = embedding_dim // head # 4表示多头注意力中Q,K,V各需要一个,最后拼接的矩阵还需要一个 self.linears = clone(nn.Linear(embedding_dim, embedding_dim), 4) def forward(self, query, key, value, mask=None): if mask: mask = mask.unsqueeze(1) # 获取batch size大小 batch_size = query.size(0) # 多头处理环节, 将embedding.size变为head.size * d_k.size # 对二、三维进行转置操作,让句子长度维度和词向量维度能够相邻,这样注意力机制才能找到词义与句子的位置关系 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))] # 接下来传入attention x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout) # 获得每个头计算结果组成的4维张量 x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head*self.d_k) return self.linears[-1](x)
前馈全连接层
作用:考虑注意力机制拟合程度不够,故意增加两层网络来增强模型的能力
class PositionWiseFeedForward(nn.Module):
"""
前馈全连接层
d_model: 词嵌入的维度
d_ff: 全连接层维度
"""
def __init__(self, d_model, d_ff, dropout=0.1):
super(PositionWiseFeedForward, self).__init__()
self.fc1 = nn.Linear(d_model, d_ff)
self.fc2 = nn.Linear(d_ff, d_model)
self.dropout = nn.Dropout(p=dropout)
def forward(self, x):
return self.fc2(self.dropout(F.relu(self.fc1(x))))
规范化层
作用:有助于模型收敛,防止过拟合
class LayerNorm(nn.Module): """ 数据规范化 features: 词嵌入的维度 """ def __init__(self, features, eps=1e-6): super(LayerNorm, self).__init__() # nn.parameter代表模型参数 self.a = nn.Parameter(torch.ones(features)) self.b = nn.Parameter(torch.zeros(features)) self.eps = eps def forward(self, x): mean = x.mean(-1, keepdim=True) std = x.std(-1, keepdim=True) return self.a*(x-mean) / (std+self.eps) + self.b
残差连接
作用:很好解决深度模型退化问题,同时使模型收敛更快,前后向传播更加顺畅
class sublayerConnection(nn.Module):
"""
子层残差连接结构
size: 词嵌入的维度
"""
def __init__(self, size, dropout=0.1):
super(SublayerConnection, self).__init__()
# 规范化层
self.norm = LayerNorm(size)
self.dropout = nn.Dropout(p=dropout)
def forward(self, x, sublayer):
return x + self.dropout(sublayer(self.norm(x)))
编码器层
作用:作为编码器组成部分,完成对输入的特征提取
class EncoderLayer(nn.Module): """ 编码器 size: 词嵌入维度 self_attn: 多头自注意力机制 feed_forward: 前馈全连接层 dropout: 置零比例 """ def __init__(self, size, self_attn, feed_forward, dropout): super(EncoderLayer, self).__init__() self.self_attn = self_attn self.feed_forward = feed_forward self.size = size # 编码器层有两个残差连接结构 self.sublayer = clone(SublayerConnection(size, dropout), 2) def forward(self, x, mask): x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask)) return self.sublayer[1](x, self.feed_forward)
编码器
作用:对输入的特征提取,由若干个编码层组成
class Encoder(nn.Module):
"""
实现编码器
encoder_layer: 编码器层
num_layer: 编码器层的个数
"""
def __init__(self, encoder_layer, num_layer):
super(Encoder, self).__init__()
self.layers = clone(encoder_layer, num_layer)
self.norm = LayerNorm(encoder_layer.size)
def forward(self, x, mask):
for layer in self.layers:
x = layer(x, mask)
return self.norm(x)
解码器层:
作用:根据给定的输入向目标进行特征提取
class DecoderLayer(nn.Module): """ 解码器层 size: 词嵌入的维度大小 self_attn: 多头自注意力机制 src_attn: 多头注意力机制 feed_forward: 前馈神经网络 """ def __init__(self, size, self_attn, src_attn, feed_forward, 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 = dropout self.sublayer = clone(SublayerConnection(size, dropout), 3) def forward(self, x, memory, source_mask, target_mask): """ x: 上一层输出 memory: 编码器语义存储变量 source_mask: 源数据掩码张量, 遮掩住对结果没有意义的字符 target_mask: 目标数据掩码张量,不希望模型利用后续信息 """ m = memory x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, target_mask)) x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, source_mask)) return self.sublayer[3](x, self.feed_forward)
编码器
作用:提取encoder输出的特征
class Decoder(nn.Module):
"""
解码器
decoder_layer: 解码器层
num_layer: 解码器层个数
"""
def __init__(self, decoder_layer, num_layer):
super(Decoder, self).__init__()
self.layers = clone(decoder_layer, num_layer)
self.norm = LayerNorm(decoder_layer.size)
def forward(self, x, memory, source_mask, target_mask):
for layer in self.layers:
x = layer(x, memory, source_mask, target_mask)
return self.norm(x)
输出结构
class Generator(nn.Module):
"""
输出层结构
d_model: 词嵌入维度
vocab_size: 词表大小
"""
def __init__(self, d_model, vocab_size):
super(Generator, self).__init__()
self.project = nn.Linear(d_model, vocab_size)
def forward(self, x):
return F.log_softmax(self.project(x), dim=-1)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。