大家好,计算自然语言处理(NLP)是一个迅速发展的领域,其中计算力量与语言学相结合。语言学的一部分主要归功于约翰·鲁珀特·弗斯的分布语义理论,他曾说过以下的名言:“你可以通过其周围的上下文单词来了解一个目标单词”,这表明一个词的语义表示取决于它所在的上下文。
正是基于这一假设,Ashish Vaswani等人的论文“Attention is all you need” 具有重要的开创性,它将Transformer架构设定为许多迅速增长的工具的核心,如BERT、GPT4、Llama等。本文将介绍Transformer架构中编码器部分中的关键处理方法和技术。
1.标记化、embedding和向量空间
在处理NLP问题时,首先要面对的任务是如何对包含在一个句子中的信息进行编码,以便机器能够处理它。机器只能处理数字,这意味着单词、它们的含义、标点符号等都必须被转换为数值表示,这本质上是embedding问题。
在深入讨论embedding是什么之前,需要介绍一个中间步骤,讨论标记化。单词块或单词片段被定义为基本构建块(所谓的标记),稍后将其表示为数字。需要特别注意的是不能用一个数字来刻画一个单词或单词片段,因此使用数字列表(向量)会体现更强的表示能力。
使用将一组单词表示为向量的最简单方式,如果有一个由3个单词组成的句子‘Today is Sunday’,则句子中的每个单词都将用一个向量表示。最简单的形式考虑这仅是3个单词,是一个3维向量空间。例如,可以根据一种一热编码规则为每个单词分配向量:
-
‘Today’ — (1,0,0)
-
‘is’ — (0,1,0)
-
‘sunday’ — (0,0,1)
这个由3维向量组成的结构虽然可以使用,但存在缺点。首先,它以使每个单词与任何其他单词都正交的方式embedding了这些单词,这意味着不能赋予单词之间的语义关系概念。关联向量之间的内积始终为零。
其次,这个特定的结构还可以用于表示由三个不同单词组成的任何其他句子。对于3维空间只能拥有3个线性独立的向量,线性独立意味着集合中的任何向量都不能通过其他向量的线性组合形成。在one-hot编码的背景下,每个向量已经是线性独立的,因此embedding可以处理的单词总数与向量空间的总维数相同。
英语使用者掌握的单词平均数量约为30,000,这意味着需要使用这个大小的向量来处理典型的文本。这样一个高维空间带来了挑战,特别是在内存方面。每个向量只有一个非零分量,这将导致内存和计算资源的非常低效的使用。
为能够描述这个句子的至少一个简单变化,需要扩展向量的大小。在这种情况下,允许使用‘sunday’或‘saturday’,每个单词由一个4维向量空间描述:
‘Today’ — (1,0,0,0)
‘is’ — (0,1,0,0)
‘sunday’ — (0,0,1,0)
‘saturday’ — (0,0,0,1)
3个单词可以堆叠在一起形成一个矩阵X,其中有3行4列:
import numpy as np
from scipy.special import softmax
import math
sentence = "Today is sunday"
vocabulary = ['Today', 'is', 'sunday', 'saturday']
# Initial Embedding (one-hot encoding):
x_1 = [1,0,0,0] # Today
x_2 = [0,1,0,0] # is
x_3 = [0,0,1,0] # Sunday
x_4 = [0,0,0,1] # Saturday
X_example = np.stack([x_1, x_2, x_3], axis=0)
2.单头注意力层:查询、键和值
从X开始,Transformer架构首先通过构建3组其他向量Q、K和V(查询、键和值)来启动。如果在网上查找它们,会发现以下信息:查询是要查找的信息,键是必须提供的信息,而值是实际获得的信息。通过与数据库系统的类比,这确实解释了这些对象的部分信息,但通常认为对其核心理解来自于它们在模型架构中所扮演的角色。
矩阵Q、K和V是通过将X乘以3个其他形状为(4×4)的矩阵W