transformer架构解析{词嵌入层,位置编码器}(含代码)-2
位置编码器(Positional Encoding)的作用:因为在transformer的编码器结构中并没有针对词汇向量位置信息的处理,因此需要在Embedding层后加入位置编码器,将词汇的位置不同 可能产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失。文本(词向量)嵌入层的作用:将文本词汇(其他数据)的数字表征转变成为向量表示。经过Embedding层将每个数字都转换成为512维的向
·
目录
学习目标
了解文本(词向量)嵌入层和位置编码器的作用
掌握文本(词向量)嵌入层和位置编码器的实现过程
作用
文本(词向量)嵌入层的作用:将文本词汇(其他数据)的数字表征转变成为向量表示。至于为何转换,大家可以参考作者的另外一篇文章自然语言处理-词向量模型-Word2Vec。
位置编码器(Positional Encoding)的作用:因为在transformer的编码器结构中并没有针对词汇向量位置信息的处理,因此需要在Embedding层后加入位置编码器,将词汇的位置不同 可能产生不同语义的信息加入到词嵌入张量中,以弥补位置信息的缺失。
词嵌入层的实现
Embedding类的实现
#导入必备工具包
import torch
import numpy as np
#预定义的网络层torch.nn,开发者给我们弄好了一些常用层
#比如,卷积层,lstm,embeding层等
import torch.nn as nn
#数学工具包
import math
import torch.nn.functional as F
# torch中变量封装了函数Variable
from torch.autograd import Variable
#用于深度拷贝的copy包
import copy
#定义Embeddings类来实现文本嵌入层,这里S说明代表两个一摸一样的嵌入层,他们共享参数
#该类继承nn.Module,这样就有标准层的一些功能,这里我们也可以解释为一种模式,我们自己实现所有的层
class Embeddings(nn.Module):
def __init__(self, d_model, vocab):
#类的初始化函数,有两个参数,d_model:指词嵌入的维度,vocab:指词表大小
#接着就是使用super的方式指明继承nn.Module的初始化函数,
super(Embeddings,self).__init__()
#之后就是去调用nn中的预定义层Embeding,获得一个词嵌入对象
self.lut = nn.Embedding(vocab,d_model)
#将d_model传入类中
self.d_model = d_model
def forward(self,x):
#参数x:因为Embedding是首层,所以代表输入给模型的文本通过词汇映射后的张量
return self.lut(x) * math.sqrt(self.d_model)
#将x传self.lut并与根号下self.d_model相乘作为返回结果
d_model = 512
vocab = 1000
x = Variable(torch.LongTensor([[100,23,5,6],[123,34,5,7]]))
emb = Embeddings(d_model,vocab)
embr = emb(x)
print("embr:",embr)
print(embr.shape)
经过Embedding层将每个数字都转换成为512维的向量
PositionalEncoding类的实现
#定义位置编码器类,我们同样把它看成一个层,因此会继承nn.Module
class PositionalEncoding(nn.Module):
def __init__(self, d_model,dropout,max_len=5000):
#位置编码器类的初始化函数,共有3个参数,d_model:词嵌入维度,dropout:置0比率,max_len:每个句子的最大长度
super(PositionalEncoding,self).__init__()
#实例化dropout,获得dropout对象
self.dropout = nn.Dropout(p=dropout)
#初始化一个位置编码矩阵,他是一个0矩阵,大小为max_len x d_model
pe = torch.zeros(max_len,d_model)
#初始化一个绝对位置矩阵,可以用索引去表示
#arrange犯法获得一个来连续的自然数向量,用unsqueeze拓展向量维度
#传入参数1变成max_len x 1的矩阵
position = torch.arange(0,max_len).unsqueeze(1)
#考虑将绝对位置矩阵位置信息加入到位置编码矩阵中
#最简单的:max_len x 1 x (div_term(1 x d_model)) = max_len x d_model
#还希望将自然数的绝对位置编码缩放成足够小的数字,有助于梯度下降过程的收敛
#首先使用arange获得一个自然数矩阵,用一个跳跃,只初始化一半的1xd_model/2的矩阵
#看成初始化两次,每次初始化的变换矩阵做不同的处理
#把这两次分别填充到矩阵编码的奇数和偶数位置上,组成最终的位置编码
div_trem = torch.exp(torch.arange(0,d_model,2) * -(math.log(10000.0) / d_model))
pe[:,0::2] = torch.sin(position * div_trem)
pe[:,1::2] = torch.cos(position * div_trem)
#这样我们就得到了位置编码pe pe是一个二维的矩阵,想要和embeding的输出一致
#拓展一个维度,使用unsqueeze拓展
pe = pe.unsqueeze(0)
#最后把pe的位置编码注册成模型的buffer,什么是buffer
#我们把它认为是对模型效果有帮助的,但却不是模型结构中超参数,不需要随着优化步骤更新
#注册之后我们就可以在模型保存后重加载和模型结构与参数一同被加载
self.register_buffer('pe',pe)
def forward(self,x):
#forward函数的参数是x,表示文本序列的词嵌入表示
#对pe进行适配,将三维张量的第二维:句子最大长度的那一维切片
#max_len=5000太大了
#最后用variable进行封装,使其与x的样式相同,但不需要进行梯度求解
x = x + Variable(self.pe[:,:x.size(1)],requires_grad = False)
#最后使用self.dropout对象进行‘丢弃’操作
return self.dropout(x)
d_model = 512
drop_out = 0.1
max_len = 60
x = embr
pe = PositionalEncoding(d_model,drop_out,max_len=max_len)
pe_result = pe(x)
print(pe_result)
print(pe_result.shape)
加入位置编码器后的张量大小不变
词汇向量中特征的分布
#绘制词汇向量中特征的分布曲线
import matplotlib.pyplot as plt
#创建15x5的画布
plt.figure(figsize=(15,5))
#实例化pe
pe = PositionalEncoding(20,0)
#向pe传入被variable封装的tensor,这样pe会直接执行forward函数
y = pe(Variable(torch.zeros(1,100,20)))
#横纵坐标:横坐标到100的长度,纵坐标表示某一个词汇中的某维特征在不同长度下对应的值
#因为总共有20维之多,查看4,5,6,7维的向量
plt.plot(np.arange(100),y[0,:,4:8].data.numpy())
#在画布上填写维度信息
plt.legend(['dim %d'%p for p in [4,5,6,7]])
呈现 sin和cos函数曲线的特点
更多推荐
所有评论(0)