因为Transformer模型并不包括任何的循环(recurrence)或卷积,所以给模型添加位置编码,为模型提供一些关于单词在句子中相对位置的信息。
位置信息实现方式有两种:1、在模型中训练得到;2、直接使用公式计算。论文中使用的第二种方式,此处对第二种方式进行解析。
如图所示:pos代表单词在句子中的位置,i代表单词向量中每一个数值的位置。一般,句子长度使用seq_len表示,词向量长度使用num_units或depth表示。
具体实现代码如下:
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
def positional_encoding(seq_len, num_units, visualization=False):
"""
:param seq_len: 语句的长度
:param num_units: 词向量的长度
:param visualization: 是否画图展示位置编码内容
:return: [1,seq_len,num_units]
"""
def __get_angles(pos, i, d_model):
"""
:param pos: 单词在语句中的位置序列,为[seq_len,1]矩阵
:param i: 词向量中的位置序列,为[1,d_model]矩阵
:param d_model: 词向量的长度
:return: [seq_len,d_model]
"""
angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
return pos * angle_rates
#np.arange(seq_len)[:, np.newaxis]:使用seq_len长度序列产生向量,然后扩展一维,变成两维向量
pos_encoding = __get_angles(np.arange(seq_len)[:, np.newaxis],
np.arange(num_units)[np.newaxis, :],
num_units)
#分别对奇数位置和偶数位置的数据进行sin和cos计算
#方法一、双层遍历的方法
# for i in range(pos_encoding.shape[0]):
# for j in range(pos_encoding.shape[1]):
# if j % 2 == 0:
# pos_encoding[i][j] = np.sin(pos_encoding[i][j])
# else:
# pos_encoding[i][j] = np.cos(pos_encoding[i][j])
#方法二:第二维按层计算
pos_encoding[:, 0::2] = np.sin(pos_encoding[:, 0::2])
pos_encoding[:, 1::2] = np.cos(pos_encoding[:, 1::2])
pos_encoding = pos_encoding[np.newaxis, ...]
if visualization:
plt.figure(figsize=(12, 8))
plt.pcolormesh(pos_encoding[0], cmap='RdBu')
plt.xlabel('Depth')
plt.xlim((0, num_units))
plt.ylabel('Position')
plt.colorbar()
plt.show()
return tf.cast(pos_encoding, tf.float32)
if "__main__" == __name__:
positional_encoding(5, 8, visualization=True)