Transformer 模型中的位置编码



借助输入嵌入,Transformer 可以获得离散标记(如单词、子词或字符)的向量表示。但是,这些向量表示不提供这些标记在序列中位置的信息。这就是为什么在 Transformer 的架构中,在输入嵌入子层之后,使用了名为“位置编码”的关键组件。

位置编码使模型能够通过为输入序列中的每个标记提供其位置信息来理解序列顺序。在本章中,我们将了解什么是位置编码,为什么需要它,它的工作原理以及它在 Python 编程语言中的实现。

什么是位置编码?

位置编码是 Transformer 中用于提供输入序列中标记顺序信息的一种机制。在 Transformer 架构中,位置编码组件添加在输入嵌入子层之后。

请查看下图;它是原始 Transformer 架构的一部分,表示位置编码组件的结构 -

Structure of Positional Encoding Component

为什么 Transformer 模型需要位置编码?

Transformer 尽管拥有强大的自注意力机制,但缺乏固有的顺序感。与按特定顺序处理序列的循环神经网络 (RNN) 和长短期记忆网络 (LSTM) 不同,Transformer 的并行处理不提供输入序列中标记位置的信息。因此,模型无法理解上下文,尤其是在单词顺序很重要的任务中。

为了克服这一限制,引入了位置编码,它为输入序列中的每个标记提供其位置信息。然后将这些编码添加到输入嵌入中,确保 Transformer 能够连同其位置上下文一起处理标记。

位置编码如何工作?

我们在上一章中讨论过,Transformer 期望位置编码函数的输出的每个向量表示都位于固定大小的维度空间中(可能是 dmodel = 512 或任何其他常数值)。

例如,让我们看下面给出的句子 -

I am playing with the brown ball and my brother is playing with the red ball.

单词“brown”和“red”可能相似,但在本句中,它们相距甚远。单词“brown”位于位置 6 (pos = 6),单词“red”位于位置 15 (pos = 15)。

这里,问题是我们需要找到一种方法,为输入句子中每个单词的词嵌入添加一个值,以便它包含有关其序列的信息。但是,对于每个词嵌入,我们需要找到一种方法,在 (0, 512) 的范围内提供信息。

位置编码可以通过多种方式实现,但在原始 Transformer 模型中,Vashwani 等人 (2017) 使用了一种基于正弦函数的特定方法来为序列中的每个位置生成唯一的位置编码。

下面的等式显示了如何定义给定位置 pos 和维度 i 的位置编码 -

$$\mathrm{PE_{pos \: 2i} \: = \: sin\left(\frac{pos}{10000^{\frac{2i}{d_{model}}}}\right)}$$

$$\mathrm{PE_{pos \: 2i+1} \: = \: cos\left(\frac{pos}{10000^{\frac{2i}{d_{model}}}}\right)}$$

这里,dmodel 是嵌入的维度。

使用正弦函数创建位置编码

下面是一个使用正弦函数创建位置编码的 Python 脚本 -

def positional_encoding(max_len, d_model):
   pe = np.zeros((max_len, d_model))
   position = np.arange(0, max_len).reshape(-1, 1)
   div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
   pe[:, 0::2] = np.sin(position * div_term)
   pe[:, 1::2] = np.cos(position * div_term)
   return pe

# Parameters
max_len = len(tokens)

# Generate positional encodings
pos_encodings = positional_encoding(max_len, embed_dim)

# Adjust the length of the positional encodings to match the input
input_embeddings_with_pos = input_embeddings + pos_encodings[:len(tokens)]

print("Positional Encodings:\n", pos_encodings)
print("Input Embeddings with Positional Encoding:\n", input_embeddings_with_pos)

现在,让我们看看如何将它们添加到我们在上一章中实现的输入嵌入中 -

import numpy as np

# Example text and tokenization
text = "Transformers revolutionized the field of NLP"
tokens = text.split()

# Creating a vocabulary
vocab = {word: idx for idx, word in enumerate(tokens)}

# Example input (sequence of token indices)
input_indices = np.array([vocab[word] for word in tokens])

print("Vocabulary:", vocab)
print("Input Indices:", input_indices)

# Parameters
vocab_size = len(vocab)
embed_dim = 512		# Dimension of the embeddings

# Initialize the embedding matrix with random values
embedding_matrix = np.random.rand(vocab_size, embed_dim)

# Get the embeddings for the input indices
input_embeddings = embedding_matrix[input_indices]

print("Embedding Matrix:\n", embedding_matrix)
print("Input Embeddings:\n", input_embeddings)

def positional_encoding(max_len, d_model):
   pe = np.zeros((max_len, d_model))
   position = np.arange(0, max_len).reshape(-1, 1)
   div_term = np.exp(np.arange(0, d_model, 2) * -(np.log(10000.0) / d_model))
   pe[:, 0::2] = np.sin(position * div_term)
   pe[:, 1::2] = np.cos(position * div_term)
   return pe

# Parameters
max_len = len(tokens)

# Generate positional encodings
pos_encodings = positional_encoding(max_len, embed_dim)

# Adjust the length of the positional encodings to match the input
input_embeddings_with_pos = input_embeddings + pos_encodings[:len(tokens)]

print("Positional Encodings:\n", pos_encodings)
print("Input Embeddings with Positional Encoding:\n", input_embeddings_with_pos)

输出

运行上述脚本后,我们将得到以下输出 -

Vocabulary: {'Transformers': 0, 'revolutionized': 1, 'the': 2, 'field': 3, 'of': 4, 'NLP': 5}
Input Indices: [0 1 2 3 4 5]
Embedding Matrix:
 [[0.71034683 0.08027048 0.89859858 ... 0.48071898 0.76495253 0.53869711]
 [0.71247114 0.33418585 0.15329225 ... 0.61768814 0.32710687 0.89633072]
 [0.11731439 0.97467007 0.66899319 ... 0.76157481 0.41975638 0.90980636]
 [0.42299987 0.51534082 0.6459627  ... 0.58178494 0.13362482 0.13826352]
 [0.2734792  0.80146145 0.75947837 ... 0.15180679 0.93250566 0.43946461]
 [0.5750698  0.49106984 0.56273384 ... 0.77180581 0.18834177 0.6658962 ]]
Input Embeddings:
 [[0.71034683 0.08027048 0.89859858 ... 0.48071898 0.76495253 0.53869711]
 [0.71247114 0.33418585 0.15329225 ... 0.61768814 0.32710687 0.89633072]
 [0.11731439 0.97467007 0.66899319 ... 0.76157481 0.41975638 0.90980636]
 [0.42299987 0.51534082 0.6459627  ... 0.58178494 0.13362482 0.13826352]
 [0.2734792  0.80146145 0.75947837 ... 0.15180679 0.93250566 0.43946461]
 [0.5750698  0.49106984 0.56273384 ... 0.77180581 0.18834177 0.6658962 ]]

Positional Encodings:
[[ 0.00000000e+00  1.00000000e+00  0.00000000e+00 ...  1.00000000e+00
   0.00000000e+00  1.00000000e+00]
 [ 8.41470985e-01  5.40302306e-01  8.21856190e-01 ...  9.99999994e-01
   1.03663293e-04  9.99999995e-01]
 [ 9.09297427e-01 -4.16146837e-01  9.36414739e-01 ...  9.99999977e-01
   2.07326584e-04  9.99999979e-01]
 [ 1.41120008e-01 -9.89992497e-01  2.45085415e-01 ...  9.99999948e-01
   3.10989874e-04  9.99999952e-01]
 [-7.56802495e-01 -6.53643621e-01 -6.57166863e-01 ...  9.99999908e-01
   4.14653159e-04  9.99999914e-01]
 [-9.58924275e-01  2.83662185e-01 -9.93854779e-01 ...  9.99999856e-01
   5.18316441e-04  9.99999866e-01]]

Input Embeddings with Positional Encoding:
[[0.71034683  1.08027048  0.89859858 ...  1.48071898  0.76495253  1.53869711]
 [1.55394213  0.87448815  0.97514844 ...  1.61768813  0.32721053  1.89633072]
 [1.02661182  0.55852323  1.60540793 ...  1.76157479  0.4199637   1.90980634]
 [0.56411987 -0.47465167  0.89104811 ...  1.58178489  0.13393581  1.13826347]
 [-0.4833233   0.14781783  0.1023115  ... 1.15180669  0.93292031  1.43946452]
 [-0.38385447  0.77473203 -0.43112094 ... 1.77180567  0.18886009  1.66589607]]

结论

在本章中,我们介绍了位置编码的基础知识、其必要性、工作原理、Python 实现以及在 Transformer 模型中的集成。位置编码是 Transformer 架构的一个基本组成部分,它使模型能够捕获序列中标记的顺序。

了解和实现位置编码的概念对于充分利用 Transformer 模型的潜力并将其有效地应用于解决复杂的 NLP 问题非常重要。

广告