如何用Keras进行变换器标记和位置嵌入

295 阅读5分钟

简介

有很多指南解释了转化器是如何工作的,以及如何建立对其中一个关键因素的直觉--符号和位置嵌入。

位置嵌入令牌允许转化器表示令牌(通常是词)之间的非刚性关系,这在语言建模中更好地模拟了我们的语境驱动的语音。虽然这个过程相对简单,但它是相当通用的,而且实现起来很快就变成了模板。

在这个简短的指南中,我们将看看我们如何使用KerasNLP,即官方的Keras插件,来执行PositionEmbeddingTokenAndPositionEmbedding

KerasNLP

KerasNLP是一个用于NLP的水平补充。截至发稿时,它还很年轻,只有0.3版本,文档也还相当简短,但这个包已经不仅仅是可用的了。

它提供了对Keras层的访问,如TokenAndPositionEmbeddingTransformerEncoderTransformerDecoder ,这使得构建自定义的转化器比以往更容易。

要在我们的项目中使用KerasNLP,你可以通过pip 来安装它。

$ pip install keras_nlp

一旦导入到项目中,你可以把任何keras_nlp 层作为标准的Keras层使用。

符号化

计算机用数字工作。我们用文字表达我们的想法。为了让计算机能够处理它们,我们必须以某种形式将文字映射为数字。

一个常见的方法是简单地将单词映射为数字,每个整数代表一个单词。一个词的语料库创造了一个词汇,词汇中的每个词都有一个索引。因此,你可以把一个词的序列变成一串被称为 "tokens"的指数。

def tokenize(sequence):
    # ...
    return tokenized_sequence

sequence = ['I', 'am', 'Wall-E']
sequence = tokenize(sequence)
print(sequence) # [4, 26, 472]

然后,这一连串的标记可以被嵌入到一个密集的向量中,该向量定义了潜在空间中的标记。

[[4], [26], [472]] -> [[0.5, 0.25], [0.73, 0.2], [0.1, -0.75]]

这通常是通过Keras中的Embedding 层完成的。变形器并不只使用标准的Embedding 层进行编码。它们执行EmbeddingPositionEmbedding ,并将它们加在一起,通过它们在潜空间中的位置置换常规嵌入。

通过KerasNLP--执行TokenAndPositionEmbedding ,将常规标记嵌入(Embedding )与位置嵌入(PositionEmbedding )相结合。

位置嵌入(PositionEmbedding

让我们先来看看PositionEmbedding 。它接受张量和参差不齐的张量,并假设最后一维代表特征,而倒数第二维代表序列。

# Seq
(5, 10)
     # Features

该层接受一个sequence_length 参数,表示输入和输出序列的长度。让我们继续前进,在位置上嵌入一个随机的统一张量。

seq_length = 5
input_data = tf.random.uniform(shape=[5, 10])

input_tensor = keras.Input(shape=[None, 5, 10])
output = keras_nlp.layers.PositionEmbedding(sequence_length=seq_length)(input_tensor)
model = keras.Model(inputs=input_tensor, outputs=output)
    
model(input_data)

这就产生了。

<tf.Tensor: shape=(5, 10), dtype=float32, numpy=
array([[ 0.23758471, -0.16798696, -0.15070847,  0.208067  , -0.5123104 ,
        -0.36670157,  0.27487397,  0.14939266,  0.23843127, -0.23328197],
       [-0.51353353, -0.4293166 , -0.30189738, -0.140344  , -0.15444171,
        -0.27691704,  0.14078277, -0.22552207, -0.5952263 , -0.5982155 ],
       [-0.265581  , -0.12168896,  0.46075982,  0.61768025, -0.36352775,
        -0.14212841, -0.26831496, -0.34448475,  0.4418767 ,  0.05758983],
       [-0.46500492, -0.19256318, -0.23447984,  0.17891657, -0.01812166,
        -0.58293337, -0.36404118,  0.54269964,  0.3727749 ,  0.33238482],
       [-0.2965023 , -0.3390794 ,  0.4949159 ,  0.32005525,  0.02882379,
        -0.15913549,  0.27996767,  0.4387421 , -0.09119213,  0.1294356 ]],
      dtype=float32)>

代号和位置嵌入(TokenAndPositionEmbedding

代号和位置嵌入可以归结为在输入序列上使用Embedding ,在嵌入的代号上使用PositionEmbedding ,然后将这两个结果加在一起,有效地在空间中置换代号嵌入以编码它们的相对意义关系。

这在技术上可以做到:

seq_length = 10
vocab_size = 25
embed_dim = 10

input_data = tf.random.uniform(shape=[5, 10])

input_tensor = keras.Input(shape=[None, 5, 10])
embedding = keras.layers.Embedding(vocab_size, embed_dim)(input_tensor)
position = keras_nlp.layers.PositionEmbedding(seq_length)(embedding)
output = keras.layers.add([embedding, position])
model = keras.Model(inputs=input_tensor, outputs=output)
    
model(input_data).shape # ([5, 10, 10])

输入被嵌入,然后进行位置嵌入,之后将它们加在一起,产生一个新的位置嵌入形状。另外,你可以利用TokenAndPositionEmbedding 层,它在引擎盖下做这个。

... # https://github.com/keras-team/keras-nlp/blob/master/keras_nlp/layers/token_and_position_embedding.py
def call(self, inputs):
        embedded_tokens = self.token_embedding(inputs)
        embedded_positions = self.position_embedding(embedded_tokens)
        outputs = embedded_tokens + embedded_positions
        return outputs

这使得执行TokenAndPositionEmbedding 更加简洁。

seq_length = 10
vocab_size = 25
embed_dim = 10

input_data = tf.random.uniform(shape=[5, 10])

input_tensor = keras.Input(shape=[None, 5, 10])
output = keras_nlp.layers.TokenAndPositionEmbedding(vocabulary_size=vocab_size, 
                                                     sequence_length=seq_length, 
                                                     embedding_dim=embed_dim)(input_tensor)
model = keras.Model(inputs=input_tensor, outputs=output)
    
model(input_data).shape # ([5, 10, 10])

我们传入该层的数据现在被定位嵌入到一个10维的潜在空间中。

model(input_data)
<tf.Tensor: shape=(5, 10, 10), dtype=float32, numpy=
array([[[-0.01695484,  0.7656435 , -0.84340465,  0.50211895,
         -0.3162892 ,  0.16375223, -0.3774369 , -0.10028353,
         -0.00136751, -0.14690581],
        [-0.05646318,  0.00225556, -0.7745967 ,  0.5233861 ,
         -0.22601983,  0.07024342,  0.0905793 , -0.46133494,
         -0.30130145,  0.451248  ],
         ...

你将学习如何。

  • 预处理文本
  • 轻松实现文本输入的矢量化
  • tf.data API合作并建立高性能的数据集
  • 使用TensorFlow/Keras和KerasNLP(Keras的官方水平补充)从头开始构建变形器,以构建最先进的NLP模型。
  • 构建混合架构,其中一个网络的输出被编码为另一个网络。

我们如何构建图像说明?大多数人认为这是生成式深度学习的一个例子,因为我们正在教一个网络生成描述。然而,我喜欢把它看作是神经机器翻译的一个实例--我们正在把图像的视觉特征翻译成文字。通过翻译,我们产生了该图像的新表述,而不仅仅是产生了新的意义。把它看作是翻译,而只是延伸到生成,从不同的角度来看待这个任务,并使它更直观一些。

把问题看作是一个翻译问题,就更容易弄清我们要使用的架构。仅有编码器的转化器在理解文本(情感分析、分类等)方面非常好,因为编码器对有意义的表示进行编码。仅有解码器的模型非常适合生成(如GPT-3),因为解码器能够将有意义的表征推断为另一个具有相同意义的序列。翻译通常是由编码器-解码器架构完成的,其中编码器对一个句子(或图像,在我们的例子中)的有意义的表征进行编码,解码器学习将这个序列变成另一个对我们来说更容易解释的有意义的表征(如一个句子)。

结论

自2017年以来,变形金刚已经掀起了大浪,许多伟大的指南提供了关于它们如何工作的见解,然而,由于定制实现的开销,它们对许多人来说仍然难以捉摸。KerasNLP解决了这个问题,提供了让你建立灵活、强大的NLP系统的构建块,而不是提供预先包装的解决方案。

在本指南中,我们看了一下用Keras和KerasNLP进行的标记和位置嵌入。