从零开始的Transformers

本文翻译自:https://e2eml.school/transformers.html#second_order_matrix_mult, 名字为 :从零开始的Transformers,作者:Brandon ,仅作为学习使用,如有侵权,将会删除本文。下面是翻译正文:


这篇2017年的 论文介绍了 transformer 作为序列转换的工具-将一个符号序列转换为另一个符号序列。最常见的例子是英语到德语的翻译。它还被修改为执行序列完成-给定一个开始提示,以相同的方式和风格进行。它们已迅速成为自然语言处理研究和产品开发中不可或缺的工具。

在我们开始之前,先提醒一下。我们将讨论很多关于矩阵乘法和反向传播(训练模型的算法)的内容,但你不需要事先知道这些。我们将一次添加一个需要的概念,并进行解释。

这不是一段很短的旅程,但我希望你会很高兴你来了。

One-hot 编码

一开始是单词。我们存在着很多的单词。为了解决计算机不认识单词的问题,我们的第一步是将所有单词转换为数字,以便我们可以对它们进行数学运算。

想象一下,我们的目标是创建响应我们的语音命令的计算机。我们的工作是构建将一系列声音转换为一系列单词的转换器。

我们从选择词汇开始,我们将在每个序列中使用的符号集合。在我们的例子中,将有两组不同的符号,一组用于表示声音的输入序列,另一组用于表示单词的输出序列。

现在,让我们假设我们正在使用英语。英语中有成千上万的单词,可能还有几千个单词涉及计算机专用术语。这将使我们的词汇量达到10万词汇量的一大半。将单词转换为数字的一种方法是从1开始计数,并为每个单词分配自己的数字。然后,一个单词序列可以表示为一个数字列表。

例如,考虑一个很小的语言,它的词汇量只有三个:files、find和my。每个单词都可以交换为一个数字,可能是files = 1, find = 2和my = 3。然后,由单词序列[Find, my, files]组成的句子“Find my files”可以表示为数字序列[2,3,1]。

这是一种将符号转换为数字的完全有效的方法,但事实证明,还有另一种格式对计算机来说更容易处理,即 one-hot 编码。在 one-hot 编码中,一个符号由一个大部分为零的数组表示,与词汇表长度相同,只有一个元素的值为1。数组中的每个元素对应一个单独的符号。

考虑 one-hot 编码的另一种方法是,每个单词仍然被分配了自己的编号,但现在该编号是数组的索引。这是我们上面的示例,采用独热编码表示法。
在这里插入图片描述

因此,句子“Find my files”变成了一个一维数组序列,当你把它们挤在一起时,它们看起来就像一个二维数组。在这里插入图片描述

注意,我将交替使用术语 “one-dimensional array” 和 “vector”。“two-dimensional array” 和 “matrix”也是如此。

点积

关于独热编码表示法,一个非常有用的东西是它让我们可以计算点积。也可以被称作内积和标量积。要得到两个向量的点积,将它们对应的元素相乘,然后将结果相加。

在这里插入图片描述

当我们使用独热编码表示时,点积特别有用。任何 one-hot 向量与自身的点积都是 1。

在这里插入图片描述

任意一个独热向量和另一个独热向量的点积都是零。
在这里插入图片描述

前面的两个例子展示了如何使用点积来度量相似性。作为另一个例子,考虑一个值向量,它表示具有不同权重的单词的组合。一个独热编码的单词可以与点积进行比较,以显示该单词的表示强度。
在这里插入图片描述

矩阵乘法

点积是矩阵乘法的组成部分,是组合一对二维数组的一种非常特殊的方式。我们称第一个矩阵为A,第二个矩阵为B,在最简单的情况下,当A只有一行,B只有一列,矩阵乘法的结果是这两个矩阵的点积。
在这里插入图片描述

请注意 A 中的列数和 B 中的行数,必须相同才能使两个数组匹配并计算点积。

当 A 和 B 开始增长时,矩阵乘法开始变得复杂起来。要处理 A 中的多行,请分别对 B 的每一行进行点积。答案的行数与 A 一样多。
在这里插入图片描述

当 B 占用更多列时,将每列与 A 进行点积,并将结果堆叠在连续的列中。
在这里插入图片描述

在我们可以将其扩展为将任意两个矩阵相乘,只要 A 中的列数与 B 中的行数相同即可。结果将具有与 A 相同的行数和与 B 相同的列数.
在这里插入图片描述

矩阵乘法作为表查找

注意矩阵乘法在这里是如何充当查找表的。

如上图所示,我们的 A 矩阵是由一堆独热向量组成的。它们在第一列、第四列和第三列分别有一个。当我们进行矩阵乘法运算时,它的作用是按照上面这个顺序,抽出B矩阵的第一行,第四行,第三行中对应位置上的值。这种用独热矢量拉出矩阵中某一行的技巧是 Transform 工作原理的核心。

一阶序列模型

我们可以暂时搁置矩阵,然后回到我们真正关心的问题,即单词序列。想象一下,当我们开始开发我们的自然语言计算机界面时,我们只想处理三个可能的命令:

Show me my directories please.

Show me my files please.

Show me my photos please.

目前我们的词汇量现在是七个: {directories, files, me, my, photos, please, show}

表示序列的一种有用方法是使用转换模型。对于词汇表中的每个单词,它都显示下一个单词可能是什么。如果用户一半的时间询问照片,30%的时间询问文件,其余时间询问目录,那么转换模型将是这样的。从任何单词移出的转换的总和总是为1。
在这里插入图片描述

这种特殊的转移模型被称为马尔可夫链,因为它满足马尔可夫性质,即下一个单词的概率仅取决于最近的单词。更具体地说,它是一个一阶马尔可夫模型,因为它只看最近的一个单词。如果它考虑最近的两个单词,它将是一个二阶马尔可夫模型。

接下来,将上面的运算转化为矩阵运算。结果表明,马尔可夫链可以方便地表示为矩阵形式。使用与创建独热向量时相同的索引方案,每一行表示词汇表中的一个单词。每一列也是如此。矩阵转换模型将矩阵视为查找表。找到与你感兴趣的单词对应的行。每一列中的值显示了该单词下一个出现的概率。因为矩阵中每个元素的值代表一个概率,所以它们都落在0到1之间。因为概率总和为 1,所以每行中的值总和为 1。
在这里插入图片描述

在转换矩阵中,我们可以清楚地看到我们三个句子的结构。几乎所有的跃迁概率都是0或1。在马尔可夫链中只有一个地方会发生分支。在my之后,可能会出现单词目录、文件或照片,每个单词的概率都不同。除此之外,下一个出现的单词是确定的。这种确定性反映在过渡矩阵中大部分是1和0。

我们可以重温我们的技巧,使用一个独热向量的矩阵乘法来提取与任何给定单词相关的转移概率。例如,如果我们只是想分离出哪个单词出现在my之后的概率,我们可以创建一个表示my的单热向量,并将其乘以我们的转移矩阵。它会把相关的行抽出来,然后显示下一个单词的概率分布。
在这里插入图片描述

二阶序列模型

仅根据当前单词预测下一个单词很困难。这就像在给出第一个音符后预测曲调的其余部分。如果我们至少能得到两个音符,我们的机会就会大很多。

让我们看看它是如何在我们的计算机命令的另一种语言模型中工作的。我们希望这个人只会看到两个句子,比例为 40/60。

  • Check whether the battery ran down please.
  • Check whether the program ran please.

使用马尔可夫链说明了这个一阶模型。
在这里插入图片描述

在这里我们可以看到,如果我们的模型查看两个最新的单词,而不是一个,它可以做得更好。当它运行到 “battery” 的时候,它知道下一个单词是 “ran” ,当它看到“program” + “ran” 的时候,下一个单词将是 “please”。这消除了模型中的一个分支,减少了不确定性并增加了信心。回头看两个词,这就变成了二阶马尔可夫模型。它为下一个单词的预测提供了更多的上下文。二阶马尔可夫链的绘制更具挑战性,但这里是展示其价值的联系。
在这里插入图片描述

为了突出两者之间的区别,这里是一阶转移矩阵,
在这里插入图片描述

这是二阶转移矩阵
在这里插入图片描述

请注意二阶矩阵如何为每个单词组合(其中大部分未在此处显示)单独一行。这意味着如果我们从 N 的词汇量开始,那么转换矩阵有 N^2 行。

在二阶模型中有更多的1和更少的分数。只有一行中有分数,在我们的模型中只有一个分支。从直觉上看,看两个单词而不是一个单词可以提供更多的上下文,更多的信息来猜测下一个单词。

第一个跳连有序的序列模型

当我们只需要回顾两个单词来决定下一个单词时,二阶模型很有效。当我们不得不往回看的时候呢? 假设我们正在构建另一个语言模型。这个只需要代表两个句子,每个句子发生的可能性都是一样的。

  • Check the program log and find out whether it ran please.
  • Check the battery log and find out whether it ran down please.

在这个例子中,为了确定哪个单词应该跟在ran之后,我们必须回顾过去的8个单词

如果我们想改进我们的二阶语言模型,我们当然可以考虑三阶和更高阶的模型。然而,对于一个庞大的词汇量,这需要创造力和蛮力的结合来执行。八阶模型的朴素实现将有N^8行,对于任何合理的词汇表来说,这都是一个极其大的数字。

相反,我们可以做一些带有技巧的事情,创建一个二阶模型,但要考虑最近的单词与之前出现的每个单词的组合。它仍然是二阶的,因为我们一次只考虑两个单词,但它允许我们进一步回溯并捕获长期依赖关系。这种二阶跳过和完整的无数阶模型的不同之处在于,我们丢弃了大部分的词序信息和前面单词的组合。剩下的仍然相当强大。

马尔可夫链现在完全失效了,但我们仍然可以表示每对前面的单词和后面的单词之间的联系。这里我们省略了数值权重,而只显示与非零权重相关的箭头。重量越大,线条越粗。
在这里插入图片描述

现在的转换矩阵如下:
在这里插入图片描述

此视图仅显示与预测ran之后的单词相关的行。它显示了最近的单词(ran)前面有词汇表中每个其他单词的实例。只显示相关的值。所有的空单元格都是0。

首先显而易见的是,当我们试图预测ran之后的单词时,我们不再只看一行,而是一整组。我们现在已经离开了马尔可夫领域。**每行不再表示序列在特定点的状态。相反,每一行代表许多特征中的一个,这些特征可以描述特定点上的序列,也就是被用来在特定点上描述整个序列。**将最近的单词与之前的每个单词组合在一起,形成一个适用行的集合,可能是一个大的集合。由于这种意义上的改变,矩阵中的每个值不再代表一个概率,而是代表一个投票。投票将被汇总和比较,以确定下一个单词的预测。

接下来显而易见的是,大多数功能并不重要。大多数单词在两个句子中都出现了,所以它们被看到的事实对预测接下来的句子没有帮助。它们的值都是0.5。只有两个例外是电池和程序。它们的权值是1和0。特征battery, ran表明ran是最近出现的单词,而battery出现在句子较早的某个地方。这个特征与down相关的权值为1,与please相关的权值为0。类似地,特征 program,ran具有相反的权值集。这个结构表明,这两个词在句子前面的出现对预测下一个单词是决定性的。

要将这组词对特征转换为下一个词估计,需要对所有相关行的值求和。检查程序日志,看看它是否对所有单词都产生了0的和,除了表示down的4和表示please的5。检查 battery 日志并找出它是否 ran 相同,除了5表示down和4表示please。通过选择投票总数最高的单词作为下一个单词预测,这个模型得到了正确的答案,尽管有8个单词的深度依赖。

掩码

仔细考虑一下,这是不令人满意的。4票和5票之间的差距相对较小。这表明该模型并没有达到应有的自信。在一个更大、更有机的语言模型中,很容易想象这种细微的差异可能会在统计噪声中消失。

我们可以通过剔除所有信息不丰富的特征投票来提高预测精度,除了battery 和 ran ,program 和 ran。在这一点上,记住我们通过将转换矩阵与显示当前活动特征的向量相乘,将相关行从转换矩阵中拉出来是有帮助的。对于这个例子,到目前为止,我们一直在使用这里所示的隐含特征向量。
在这里插入图片描述

它为每个特征包含一个1,它是ran和它前面的每个单词的组合。任何在它之后的单词都不会包含在特性集中。(在下一个单词预测问题中,这些还没有出现,所以用它们来预测接下来的单词是不公平的。) 这还不包括所有其他可能的单词组合。在这个例子中,我们可以安全地忽略这些,因为它们都是零。

为了改善我们的结果,我们可以通过创建一个 mask 来强制无用的特征为零。它是一个全是1的向量,除了你想隐藏或mask的位置,这些都被设为0。在我们的例子中,我们想屏蔽除“battery,ran”和“program,ran”之外的所有特征,这是仅有的两个有帮助的特征。
在这里插入图片描述

为了应用mask,我们将两个向量元素相乘。任何未遮挡位置的特征活动值将乘以1并保持不变。遮挡位置的任何特征活动值都将乘以零,从而强制为零。

amsk具有隐藏大量转换矩阵的效果。它隐藏了ran 与除 program 和 battery 之外的所有内容的组合,只留下重要的功能。
在这里插入图片描述

在 mask 无用的特征后,下一个单词的预测变得更强。当单词 battery 出现在句子的前面时,run之后的单词被预测为down,权重为1, please权重为0。25%的重量差变成了无穷大的差距。毫无疑问,接下来会出现什么词。当程序在早期运行时,同样强烈的预测也会出现在please上。

这种选择性 mask 的过程就是在原论文的标题中被称为 “attention” 的机制。到目前为止,我们所描述的只是论文中注意力是如何实现的一个近似。它抓住了重要的概念,但细节是不同的。我们稍后会缩小差距。

回顾和精进

恭喜你走到这一步。如果你愿意,你可以停下来。选择性二阶跳过模型正是 transformers 所使用方法的机制,至少在解码器方面是这样。它体现了像OpenAI的GPT-3这样的生成语言模型在做什么。它没有讲述完整的故事,但它代表了故事的中心主旨。

接下来的部分将更多地介绍这种直观的解释与 transformers 实现方式之间的差距。这些主要是由三个实际考虑因素驱动的。

  1. **计算机特别擅长矩阵乘法。**有一个专门为快速矩阵乘法构建计算机硬件的行业。任何可以表示为矩阵乘法的计算都可以变得非常高效。它就像是子弹头列车。如果你能把行李装进去,它会很快把你带到你想去的地方。
  2. **每一步都必须是可微的。**到目前为止,我们只是在使用简单的样例,并且已经有了手工挑选所有转移概率和mask值→模型参数。在实践中,这些必须通过反向传播来学习,这取决于每个计算步骤是可微的。这意味着对于参数的任何微小变化,我们都可以计算出相应的模型误差或损失的变化。
  3. **渐变需要平滑和良好的条件。**所有参数的所有导数的组合就是损失梯度。在实践中,让反向传播表现良好需要平滑的梯度,也就是说,当你在任何方向上小步前进时,斜率都不会很快改变。当梯度条件良好时,它们也会表现得更好,也就是说,梯度在一个方向上不会比另一个方向上大得多。如果你把损失函数描绘成景观,大峡谷将是一个条件不佳的景观。取决于你是沿着底部走,还是沿着侧面走,你会有非常不同的斜率。相比之下,经典的Windows屏幕的起伏的山丘将有一个良好的条件梯度。

如果构建神经网络的科学是创建可区分的构建块,那么它们的艺术就是以梯度不会变化太快且在每个方向上大致相同的方式堆叠各个部分。

注意力作为矩阵乘法

特征权重可以通过计算训练中每个单词对/下一个单词转换发生的频率来直接构建,但注意,mask 不是这样的。到目前为止,我们已经凭空找到了mask向量。transformers 如何找到相关的 mask 很重要。使用某种形式的查找表是很自然的,但是现在我们将重点放在用矩阵乘法来表示所有东西上。我们可以使用与上面介绍的相同的查找方法,将每个单词的掩码向量堆叠到一个矩阵中,并使用最新单词的one-hot表示来提取相关的掩码。
在这里插入图片描述

在显示掩码向量集合的矩阵中,为了清楚起见,我们只显示了我们试图提取的那个。

我们终于到了可以开始结合论文的地步了。此mask 查找由注意力方程中的 QK^T 项表示。
在这里插入图片描述

查询Q表示感兴趣的特征,矩阵K表示 mask 集合。因为它是用 mask 存储在列中,而不是行中,所以在相乘之前需要(用T运算符)对它进行转置。当我们全部完成的时候,我们会对它做一些重要的修改,但是在这个层次上,它抓住了一个可微查找表的概念,这个表是 transformers 使用的。

作为矩阵乘法的二阶序列模型

到目前为止,我们一直在处理的另一个步骤是过渡矩阵的构造。我们已经清楚了逻辑,但不清楚如何用矩阵乘法来实现。

一旦我们得到了注意力步骤的结果,一个包含最新单词和之前单词的一小部分集合的向量,我们需要将其转化为特征,每个特征都是一个单词对。attention mask 为我们提供了我们需要的原材料,但它并没有建立那些词对特征。为了做到这一点,我们可以使用单层全连接神经网络。

为了了解神经网络层如何创建这些对,我们将手工制作一个。它将被人为地清理和风格化,其权重将与实践中的权重没有相似之处,但它将展示神经网络如何具有构建这两个词对特征所必需的表达能力。为了保持简洁,我们只关注这个例子中的三个单词,battery,program,ran。
在这里插入图片描述

在上面的层图中,我们可以看到权重如何将每个单词的存在和不存在组合成一个特征集合。这也可以用矩阵形式表示。
在这里插入图片描述

它可以通过矩阵乘法与表示到目前为止所见单词集合的向量来计算。
在这里插入图片描述

battery 和ran元素为1,program 元素为0。偏置元素总是1,这是神经网络的一个特征。通过矩阵乘法运算,表示电池的元素为1,表示程序的元素为1,表示程序的元素为-1。另一种情况的结果是类似的。
在这里插入图片描述

计算这些单词组合特征的最后一步是应用线性修正单元 (ReLU) 非线性。 这样做的效果是用零替换任何负值。 这会清除这两个结果,因此它们表示每个单词组合特征的存在(1)或不存在(0)。

有了这些体操,我们终于有了一个基于矩阵乘法的方法来创建多词特征。 虽然我最初声称这些由最近的词和一个较早的词组成,但仔细观察这种方法就会发现它也可以构建其他特征。 当特征创建矩阵被学习时,而不是硬编码,其他结构可以被学习。 即使在这个玩具示例中,也没有什么可以阻止创建电池、程序、运行等三个词的组合。 如果这种组合发生得足够普遍,它可能最终会被代表。 没有任何方法可以表明单词出现的顺序(至少现在还没有),但我们绝对可以使用它们的共现来做出预测。 甚至可以使用忽略最近单词(如电池、程序)的单词组合。 这些和其他类型的特征可能是在实践中创建的,暴露了我在声称 transformers 是一个选择性二阶带跳过序列模型时所做的过度简化。 它的细微差别远不止于此,现在您可以准确地看到细微差别是什么。 这不会是我们最后一次改变故事以融入更多微妙之处。

在这种形式下,多词特征矩阵准备好再进行一次矩阵乘法,即我们上面开发的带有跳跃的二阶序列模型。总的来说,顺序是

  • 特征创建矩阵乘法,
  • ReLU 非线性,
  • 和 转移矩阵乘法

是应用注意力后应用的前馈处理步骤。论文中的等式 2 以简明的数学公式显示了这些步骤。
在这里插入图片描述

该论文的图 1 架构图显示了这些作为前馈块集中在一起。
在这里插入图片描述

序列补全

到目前为止,我们只讨论了下一个单词预测。 我们需要添加几个部分来让我们的解码器生成一个长序列。 第一个是提示,一些示例文本为转换器提供运行开始和构建其余序列的上下文。 它被送入解码器,即上图中右侧的列,其中标记为“输出(右移)”。 选择给出有趣序列的提示本身就是一门艺术,称为提示工程。 这也是人类改变行为以支持算法的一个很好的例子,而不是相反。

一旦解码器有一个部分序列可以开始,它就会进行前向传递。 最终结果是一组预测的单词概率分布,序列中的每个位置都有一个概率分布。 在每个位置,分布显示了词汇表中每个下一个单词的预测概率。 我们不关心序列中每个已建立单词的预测概率。 他们已经成立了。 我们真正关心的是提示结束后下一个单词的预测概率。 有几种方法可以选择该词应该是什么,但最直接的方法称为贪心,即选择概率最高的词。

然后将新的下一个单词添加到序列中,用解码器底部的“输出”替换,然后重复该过程,直到您厌倦为止。

我们还没有完全准备好详细描述的部分是另一种形式的掩蔽,确保当 transformer 做出预测时它只看后面,而不是前面。 它应用于标记为“Masked Multi-Head Attention”的块中。 当我们可以更清楚它是如何完成的时,我们将在稍后重新讨论它。

Embeddings层

正如我们到目前为止所描述的,transformers太大了。 对于假设为 50,000 的词汇量 N,所有单词对和所有潜在的下一个单词之间的转换矩阵将具有 50,000 列和 50,000 平方(25 亿)行,总计超过 100 万亿个元素。即使对于现代硬件平台来说,这仍然是一个庞大的计算量。

问题不仅仅是矩阵的大小。 为了构建稳定的过渡语言模型,我们必须至少多次提供说明每个潜在序列的训练数据。 训练数据集的容量将是非常非常大的。

幸运的是,这两个问题都有一个解决方法,即嵌入。

在一种语言的单热表示中,每个词都有一个向量元素。 对于大小为 N 的词汇表,该向量是一个 N 维空间。 每个单词代表该空间中的一个点,沿多个轴之一距原点一个单位。 我还没有找到绘制高维空间的好方法,但下面有一个粗略的表示。
在这里插入图片描述

在嵌入中,这些词点全部被获取并重新排列(投影,用线性代数术语)到低维空间中。 例如,上图显示了它们在二维空间中的样子。 现在,我们不需要 N 个数字来指定一个单词,而只需要 2 个。这些是新空间中每个点的 (x, y) 坐标。 这是我们的玩具示例的二维嵌入的样子,以及一些单词的坐标。
在这里插入图片描述

一个好的词嵌入将具有相似含义的词组合在一起。 使用词嵌入的模型在嵌入空间中学习模式。 这意味着无论它学会用一个词做什么,都会自动应用到它旁边的所有词上。 这具有减少所需训练数据量的额外好处。 每个例子都提供了一点点学习,可以应用于整个单词邻域。

在在上面的图中,我试图通过将重要组件放在一个区域(电池、日志、程序)、将介词放在另一个区域(向下、向外)以及靠近中心的动词(检查、查找、运行)来展示这一点。 在实际嵌入中,分组可能不是那么清晰或直观,但基本概念是相同的。 行为相似的单词之间的距离很小。

词嵌入大大减少了所需的参数数量。 然而,嵌入空间中的维度越少,原始单词的信息就越多。 一种语言的丰富性仍然需要相当多的空间来布置所有重要的概念,以免它们互相踩到脚趾。 通过选择嵌入空间的大小,我们可以在计算负载与模型精度之间进行权衡。

坐标投影是矩阵最擅长的,将单词从其单热表示投影到嵌入空间涉及矩阵乘法 。 从具有一行和 N 列的单热矩阵开始,然后移动到二维嵌入空间,投影矩阵将具有 N 行和两列,如下所示。
在这里插入图片描述

这个例子展示了一个单热向量,例如代表电池,如何提取与其关联的行,其中包含单词在嵌入空间中的坐标。 为了使关系更清晰,one-hot 向量中的零被隐藏,所有其他未从投影矩阵中拉出的行也是如此。 完整的投影矩阵是密集的,每一行都包含与之关联的单词的坐标。

投影矩阵可以将 one-hot 词汇向量的原始集合转换为您想要的任何维度空间中的任何配置。 最大的技巧是找到一个有用的投影,一个将相似的词组合在一起,一个有足够的维度来分散它们。 对于常见的语言,比如英语,有一些不错的预计算嵌入。 此外,与 transformer 中的其他所有内容一样,它可以在训练期间学习。

在原始论文的图 1 架构图中,绿色的地方是词嵌入的地方。
在这里插入图片描述

位置编码

到目前为止,我们假设单词的位置被忽略,至少对于最近单词之前的任何单词都是如此。现在我们开始使用位置嵌入来解决这个问题。

有几种方法可以将位置信息引入我们的嵌入式单词表示中,但在原始的 transformer 中完成的方法是添加一个圆形摆动。
在这里插入图片描述
单词在嵌入空间中的位置充当圆心。 根据它在单词序列中的位置,向它添加一个扰动。 对于每个位置,单词以不同的角度移动相同的距离,从而在您在序列中移动时形成圆形图案。 序列中彼此靠近的词具有相似的扰动,但相距较远的词在不同的方向上受到扰动。

由于圆是二维图形,表示圆形摆动需要修改嵌入空间的二维。 如果嵌入空间包含两个以上的维度(几乎总是如此),则在所有其他维度对中重复圆形摆动,但具有不同的角频率,也就是说,它在每个维度中扫出不同的旋转次数 案件。 在某些维度对中,摆动会扫过圆的许多旋转。 在其他对中,它只会扫过一小部分旋转。 所有这些不同频率的圆形摆动的组合可以很好地表示单词在序列中的绝对位置。

我仍在深度思考为什么会这样。 它似乎以一种不会破坏单词和注意力之间学习关系的方式将位置信息添加到组合中。 为了更深入地了解数学和含义,我推荐 Amirhossein Kazemnejad 的位置编码教程

在标准的架构图中,这些绿色的块显示了位置代码的生成及其对嵌入词的添加。
在这里插入图片描述

解码嵌入层

嵌入单词使它们的使用效率大大提高,但是一旦多层注意力结束,它们就需要从原始词汇表中转换回单词。 解码嵌入的方式与嵌入的方式相同,即从一个空间到另一个空间的投影,即矩阵乘法。

解码嵌入矩阵的形状与嵌入矩阵相同,但行数和列数翻转了。 行数是我们要转换的空间的维度。 在我们一直使用的例子中,它是我们嵌入空间的大小,2。 列数是我们要转换到的空间的维度——完整词汇表的单热表示的大小,在我们的示例中为 13。
在这里插入图片描述

良好的解码嵌入矩阵中的值不像嵌入矩阵中的值那样易于说明,但效果相似。 当表示单词程序的嵌入向量乘以解码嵌入矩阵时,相应位置的值很高。 然而,由于投影到更高维空间的工作方式,与其他词相关的值不会为零。 嵌入空间中最接近程序的词也将具有中高值。 其他词的价值接近于零。 并且可能会有很多带有负值的词。 词汇空间中的输出向量将不再是单热或稀疏的。 它将是密集的,几乎所有值都不为零。
在这里插入图片描述

但是问题不大。 我们可以通过选择与最高值关联的词来重新创建单热向量。 此操作也称为 argmax,给出最大值的参数(元素)。 如上所述,这就是贪婪序列补全的方法。 这是一个很棒的第一关,但我们可以做得更好。

如果一个嵌入很好地映射到几个词,我们可能不想每次都选择最好的一个。 它可能只是比其他选择好一点点,添加一点多样性可以使结果更有趣。 此外,有时在确定最终选择之前先看几个词并考虑句子可能的所有方向是很有用的。 为了做到这些,我们必须首先将我们的去嵌入结果转换为概率分布。

Softmax

argmax 函数在最高值获胜的意义上是“困难的”,即使它仅比其他值无限大。 如果我们想同时考虑多种可能性,最好使用我们从 softmax 获得的“软”最大值函数。 要获得向量中值 x 的 softmax,请将 x 的指数 e^x 除以向量中所有值的指数之和。

由于三个原因,softmax 在这里很有用。

  • 首先,它将我们的去嵌入结果向量从任意一组值转换为概率分布。 作为概率,比较不同单词被选中的可能性变得更容易,如果我们想进一步展望未来,甚至可以比较多单词序列的可能性。
  • 其次,它使顶部附近的区域变薄。 如果一个词的得分明显高于其他词,softmax 会夸大这种差异,使其看起来几乎像一个 argmax,获胜值接近 1,而所有其他词接近 0。 但是,如果有几个单词都接近顶部,它会将它们全部保留为极有可能,而不是人为地压倒接近第二名的结果。
  • 第三,softmax 是可微分的,这意味着我们可以计算结果的每个元素将发生多少变化,给定任何输入元素的微小变化。 这允许我们将它与反向传播一起使用来训练我们的变压器。

如果您想深入了解 softmax,(或者如果您晚上难以入睡),这里有一篇更完整的文章来讲解 softmax。

解码嵌入变换(如下面的线性块所示)和 softmax 函数一起完成了去嵌入过程。
在这里插入图片描述

多头注意力

现在我们已经接受了投影(矩阵乘法)和空间(向量大小)的概念,我们可以重新审视核心注意力机制。 如果我们可以更具体地了解每个阶段的矩阵形状,将有助于阐明算法。 有一个重要数字的简短列表。

  • N:词汇量。在我们的示例中为 13。通常在数万个。
  • n:最大序列长度。在我们的示例中为 12。transformer文章中上大概有几百个。 GPT-3 的是 2048(文中没有详细说明)。
  • d_model:整个模型使用的嵌入空间的维数。论文中是 512。

原始输入矩阵的构造方法是从句子中获取每个词的单热表示,并将它们堆叠起来,使每个单热向量都是它自己的行。 生成的输入矩阵有 n 行和 N 列,我们可以将其缩写为 [n x N]。
在这里插入图片描述

如前所述,嵌入矩阵有 N 行和 d_model 列,我们可以将其缩写为 [N x d_model]。 将两个矩阵相乘时,结果采用第一个矩阵的行数和第二个矩阵的列数。 这使嵌入的词序列矩阵具有 [n x d_model] 的形状。

我们可以通过 Transformer 跟踪矩阵形状的变化,作为跟踪正在发生的事情的一种方式。 在初始嵌入之后,位置编码是相加的,而不是相乘的,所以它不会改变事物的形状。 然后嵌入的词序列进入注意力层,并以相同的形状从另一端出来。 (我们稍后会回到它们的内部工作原理。)最后,去嵌入将矩阵恢复到其原始形状,为序列中每个位置的词汇表中的每个单词提供概率。
在这里插入图片描述

为什么我们需要不止一个注意力头

终于到了面对我在第一次解释注意力机制时所做的一些简单假设的时候了。 单词表示为密集嵌入向量,而不是单热向量。 注意力不只是 1 或 0、开或关,还可以是介于两者之间的任何位置。 为了让结果落在 0 和 1 之间,我们再次使用 softmax 技巧。 它具有强制所有值位于我们的 [0, 1] 注意力范围内的双重好处,它有助于强调最高值,同时积极挤压最小值。 这是我们之前在解释模型的最终输出时利用的差分 almost-argmax 行为。

关注 softmax 函数的一个复杂结果是它会倾向于关注单个元素。 这是我们以前没有的限制。 有时在预测下一个词时记住前面的几个词是很有用的,而 softmax 只是剥夺了我们这一点。 这是模型的问题。

解决方案是让多个不同的注意力实例或头同时运行。 这让 transformer 在预测下一个词时同时考虑几个先前的词。 它带回了我们在将 softmax 引入图片之前所拥有的力量。

不幸的是,这样做确实会增加计算量。 计算注意力已经是大部分工作,我们只是将它乘以我们想要使用的头数。 为了解决这个问题,我们可以重新使用将所有内容投影到低维嵌入空间的技巧。 这缩小了所涉及的矩阵,从而大大减少了计算时间。

要了解怎么做,我们可以继续查看矩阵形状。通过多头注意块的分支和延展来追踪矩阵形状需要下面三个参数:

  • d_k:用于键和查询的嵌入空间中的维度。 在论文中是 64。
  • d_v:用于值的嵌入空间中的维度。 在论文中是 64。
  • h:头数。 在论文中是 8。
    在这里插入图片描述

[n x d_model]的嵌入单词序列作为后续所有内容的基础。 在每种情况下,都有一个矩阵 Wv、Wq 和 Wk(在架构图中都无用地显示为“线性”块),它们将嵌入词的原始序列转换为值矩阵 V、查询矩阵 Q、 和键矩阵 K。K 和 Q 具有相同的形状,都是[n x d_k],但 V 可以不同,为[n x d_v]。 论文中的 d_k 和 d_v 相同,这让事情有点混乱,但它们不一定是。 此设置的一个重要方面是每个注意力头都有自己的 Wv、Wq 和 Wk 转换。 这意味着每个头都可以放大和扩展它想要关注的嵌入空间的部分,并且它可以与其他每个头所关注的不同。

每个注意力头的结果与 V 的形状相同。现在我们有 h 个不同的结果向量的问题,每个向量关注序列的不同元素。 为了将它们合二为一,我们利用线性代数的力量,将所有这些结果连接成一个巨大的 [n x h * d_v] 矩阵。 然后,为了确保它以与开始时相同的形状结束,我们使用形状 [h * d_v x d_model] 的另一个变换。

简单地说。公式如下:
在这里插入图片描述

重温单头注意力

我们已经完成了上面注意力的概念说明 . 实际的实现有点混乱,但我们早先的直觉仍然有帮助。查询和键不再容易检查和解释,因为它们都被投射到它们自己的特殊子空间中。在我们的概念图中,查询矩阵中的一行代表词汇空间中的一个点,感谢 one-hot 表示,它代表一个且仅一个词。在它们的嵌入形式中,查询矩阵中的一行代表嵌入空间中的一个点,该点将靠近一组具有相似含义和用法的词。概念图将一个查询词映射到一组键,这些键又过滤掉所有未被关注的值。实际实现中的每个注意力头将一个查询词映射到另一个低维嵌入空间中的一个点。结果是注意力变成了词组之间的关系,而不是单个词之间的关系。它利用语义相似性(嵌入空间中的接近性)来概括它对相似词的了解。

通过注意计算跟踪矩阵的形状有助于跟踪它在做什么。
在这里插入图片描述

查询和键矩阵QK都具有形状 [ n x d_k ]。由于K在乘法之前被转置, QK^T的结果 给出了 [ n x d_k ] * [ d_k x n ] = [ n x n ]的矩阵。将该矩阵的每个元素除以d_k的平方根已被证明可以防止值的大小疯狂增长,并有助于反向传播表现良好。正如我们提到的,softmax 将结果硬塞进 argmax 的近似值,倾向于将注意力集中在序列中的一个元素而不是其余元素上。在这种形式中,[ n x n ] 注意力矩阵粗略地将序列的每个元素映射到序列的另一个元素,表明它应该关注什么,以便获得最相关的上下文来预测下一个元素。它是一个最终应用于值矩阵V的过滤器,只留下参与值的集合。这具有忽略序列中之前出现的绝大多数内容的效果,并将聚光灯照在最需要注意的一个先验元素上。
在这里插入图片描述

理解这组计算的一个棘手部分是要记住,它正在为我们输入序列的每个元素计算注意力,为我们句子中的每个单词计算注意力,而不仅仅是最近的单词。它还在计算对前面单词的注意力。我们并不真正关心这些,因为他们的下一个词已经被预测和确定。它还在计算未来单词的注意力。这些目前还没有多大用处,因为它们太过遥远,而且它们的直接前任还没有被选中。但是这些计算可以通过间接路径影响对最近单词的注意力,因此我们将它们全部包括在内。它’

Mask 块强制执行约束,至少对于这个序列完成任务,我们不能展望未来。它避免了从虚构的未来词中引入任何奇怪的人工制品。它粗糙而有效 - 手动将对所有超过当前位置的单词的注意力设置为负无穷大。在 带注释的变压器中,对于逐行显示 Python 实现的论文来说,这是一个非常有用的伴侣,掩码矩阵是可视化的。紫色单元格显示不允许注意的地方。每行对应于序列中的一个元素。允许第一行关注其自身(第一个元素),但之后什么都不关注。允许最后一行处理自身(最后一个元素)和之前的所有内容。掩码是 [ n x n] 矩阵。它不是与矩阵乘法一起应用,而是与更直接的逐元素乘法一起应用。这具有手动进入注意力矩阵并将掩码中的所有紫色元素设置为负无穷大的效果。
在这里插入图片描述

注意力实现方式的另一个重要区别是,它利用单词在序列中呈现的顺序,并且将注意力表示为位置到位置关系,而不是单词到单词的关系。这在其 [ n x n] 形状。它将序列中的每个元素(由行索引指示)映射到序列的某些其他元素(由列索引指示)。这有助于我们更容易地可视化和解释它在做什么,因为它在嵌入空间中运行。我们省去了在嵌入空间中寻找附近词来表示查询和键之间关系的额外步骤。

Skip Connect(跳跃连接)

注意力是 transformer 所做的最基本的部分。它是核心机制,我们现在已经遍历了它一个非常具体的层次。从这里开始的一切都是使其正常工作所必需的管道。它是让注意力拉动我们繁重的工作量的其余部分。

我们还没有解释的一件事情是跳跃连接。这些发生在多头注意块周围,以及标记为“添加和规范”的块中的元素明智的前馈块周围。在跳过连接中,输入的副本被添加到一组计算的输出中。注意块的输入被添加回其输出。元素前馈块的输入被添加到它的输出。
在这里插入图片描述

跳过连接有两个目的。

首先:是它们有助于保持梯度平滑,这对反向传播有很大帮助。注意力是一个过滤器,这意味着当它正常工作时,它会阻止大部分试图通过它的东西。这样做的结果是,如果许多输入碰巧落入被阻塞的通道,那么许多输入的微小变化可能不会对输出产生太大变化。这会在平坦的梯度中产生死点,但仍然离谷底不远。这些鞍点和脊线是反向传播的重要触发点。跳过连接有助于解决这些问题。在注意力的情况下,即使所有的权重都为零并且所有的输入都被阻止,跳过连接会将输入的副本添加到结果中,并确保任何输入的微小变化仍然会在结果中产生明显的变化。这可以防止梯度下降卡在远离好的解决方案的地方。

自 ResNet 图像分类器问世以来,跳过连接变得流行,因为它们可以提高性能。它们现在是神经网络架构中的标准功能。在视觉上,我们可以通过比较有和没有跳过连接的网络来看到跳过连接的效果。本文中的下图 显示了具有和不具有跳跃连接的 ResNet。当使用跳跃连接时,损失函数山丘的斜率更加温和和均匀。如果您想更深入地了解工作原理和原因,这篇文章中有更深入的介绍 。
在这里插入图片描述

skip connections 的第二个目的是 transformers 特有的——保留原始输入序列。即使有很多注意力头,也不能保证一个词会关注到它自己的位置。注意过滤器有可能完全忘记最近的单词,转而关注所有可能相关的早期单词。跳过连接采用原始单词并手动将其添加回信号中,这样就不会丢失或忘记它。这种鲁棒性的来源可能是 transformer 在如此多不同的序列完成任务中表现良好的原因之一。

图层归一化

归一化是与跳过连接配对良好的步骤。没有理由必须将它们放在一起,但在一组计算(如注意力或前馈神经网络)之后放置时,它们都能发挥最佳作用。

层归一化的简短版本是将矩阵的值移动为均值为零,并缩放为标准差为 1。
在这里插入图片描述

更长的版本是,在像 transformer 这样的系统中,有很多移动部分,其中一些不是矩阵乘法(例如 softmax 运算符或整流线性单元),重要的是值有多大以及它们如何在积极和消极之间取得平衡。如果一切都是线性的,您可以将所有输入加倍,输出将是原来的两倍,一切都会正常进行。神经网络并非如此。它们本质上是非线性的,这使得它们非常具有表现力,但对信号的幅度和分布也很敏感。归一化是一种技术,已被证明可用于在整个多层神经网络的每一步中保持信号值的一致分布。它鼓励参数值的收敛,通常会产生更好的性能。

关于规范化,我最喜欢的一点是,除了像我刚才给出的高级解释之外,没有人完全确定它为何如此有效。如果你想比这个兔子洞更深入一点,我写了一篇关于批量归一化的更详细的 文章 ,它是变形金刚中使用的层归一化的近亲。

多层

当我们在上面奠定基础时,我们展示了一个注意块和一个精心选择权重的前馈块足以构建一个像样的语言模型。在我们的示例中,大多数权重为零,其中一些为 1,而且它们都是手工挑选的。从原始数据进行训练时,我们不会有这种奢侈。一开始权重都是随机选择的,大部分接近于零,少数不是我们需要的。距离我们的模型要表现良好所需的位置还有很长的路要走。

通过反向传播的随机梯度下降可以做一些非常了不起的事情,但它在很大程度上依赖于运气。如果只有一种方法可以得到正确的答案,只有一种网络运行良好所必需的权重组合,那么它不太可能找到自己的方法。但是,如果有很多路径可以找到一个好的解决方案,那么模型到达那里的可能性就会大得多。

只有一个注意力层(只有一个多头注意力块和一个前馈块)只允许一条通往一组好的转换器参数的路径。每个矩阵的每个元素都需要找到正确的值才能使事情顺利进行。它很脆弱,很可能会陷入一个远非理想的解决方案,除非对参数的初始猜测非常非常幸运。

Transformers 回避这个问题的方法是拥有多个注意力层,每个注意力层都使用前一个的输出作为其输入。跳过连接的使用使整个管道对个别注意块失败或给出不稳定的结果具有鲁棒性。拥有倍数意味着还有其他人在等待填补空缺。如果一个人偏离了轨道,或者以任何方式未能发挥其潜力,就会有另一个下游有另一个机会来缩小差距或修复错误。该论文表明,层数越多性能越好,尽管在 6 层之后改进变得微乎其微。

考虑多层的另一种方法是将其视为传送带装配线。每个注意块和前馈块都有机会将输入拉下线,计算有用的注意矩阵并进行下一个单词预测。无论它们产生什么结果,有用与否,都会被添加回传送带,并传递到下一层。
在这里插入图片描述

这与传统的将多层神经网络描述为“深度”形成对比。由于跳过连接,连续的层不会像提供冗余一样提供越来越复杂的抽象。在一层中错过的任何集中注意力、创建有用特征和做出准确预测的机会都可以被下一层抓住。工人成为流水线上的工人,每个人都尽其所能,但不必担心接住每一件,因为下一个工人会接住他们错过的。

解码器堆叠

到目前为止,我们已经小心地忽略了编码器堆栈(转换器架构的左侧),而选择了解码器堆栈(右侧)。我们将在几段中解决这个问题。但值得注意的是,解码器本身就非常有用。

正如我们在序列完成任务描述 中所布局的那样 ,解码器可以完成部分序列并将它们扩展到您想要的程度。OpenAI 创建了生成式预训练 (GPT) 系列模型来执行此操作。他们在本报告中描述的架构 应该看起来很熟悉。它是一个带有编码器堆栈的变压器,它的所有连接都通过手术移除了。剩下的是一个 12 层的解码器堆栈。
在这里插入图片描述

每当您遇到生成模型(如 BERTELMoCopilot )时,您都可能看到正在运行的转换器的解码器一半。

编码器堆叠

我们所了解的关于解码器的几乎所有内容也适用于编码器。最大的区别是,最后没有做出明确的预测,我们可以用来判断其表现的对错。相反,编码器堆栈的最终产品是令人失望的抽象——嵌入空间中的一系列向量。它被描述为序列的纯语义表示,脱离了任何特定的语言或词汇,但这对我来说太浪漫了。我们可以肯定的是,它是一个有用的信号,可用于向解码器堆栈传达意图和意义。

拥有一个编码器堆栈可以充分发挥转换器的潜力,而不仅仅是生成序列,它们现在可以将序列从一种语言翻译(或转换)为另一种语言。翻译任务的训练不同于序列完成任务的训练。训练数据需要原始语言的序列和目标语言的匹配序列。完整的原始语言通过编码器运行(这次没有屏蔽,因为我们假设我们在创建翻译之前看到整个句子)和结果,最终编码器层的输出作为输入提供给每个解码器层。然后解码器中的序列生成像以前一样进行,

交叉注意力

启动和运行完整转换器的最后一步是编码器和解码器堆栈之间的连接,即交叉注意块。我们把它留到最后,多亏了我们奠定的基础,没有太多要解释的了。

Cross-attention 的工作原理与 self-attention 类似,只是关键矩阵K和值矩阵V 基于最终编码器层的输出,而不是前一个解码器层的输出。查询矩阵 Q仍然是根据前一个解码器层的结果计算出来的。这是来自源序列的信息进入目标序列并引导其创建朝着正确方向发展的渠道。有趣的是,相同的嵌入式源序列被提供给解码器的每一层,支持连续层提供冗余并且都合作执行相同任务的概念。
在这里插入图片描述

Tokenizing

我们一路通过 transformer!我们对它进行了足够详细的介绍,应该不会留下任何神秘的黑盒子。有一些我们没有深入研究的实现细节。您需要了解它们才能为自己构建一个工作版本。最后的几个花絮与其说是关于变压器如何工作,不如说是关于让神经网络表现良好。 Annotated Transformer 将帮助您填补这些空白。

我们还没有完全完成。关于我们如何开始表示数据,还有一些重要的事情要说。这是一个很贴近我的心,但很容易被忽视的话题。这与其说是算法的强大功能,不如说是深思熟虑地解释数据并理解其含义。

我们顺便提到,词汇表可以用高维单热向量表示,每个词都有一个元素。为了做到这一点,我们需要确切地知道我们要表示多少个词以及它们是什么。

一种天真的方法是列出所有可能的单词,就像我们可能在韦氏词典中找到的那样。对于英语,这将为我们提供数万个,具体数字取决于我们选择包含或排除的内容。但这是过于简单化了。大多数单词有多种形式,包括复数、所有格和变位。单词可以有替代拼写。除非您的数据经过非常仔细的清理,否则它会包含各种印刷错误。这甚至没有涉及自由格式文本、新词、俚语、行话和广阔的 Unicode 世界所带来的可能性。所有可能的单词的详尽列表将是不可行的。

一个合理的后备位置是让单个字符而不是单词作为构建块。详尽的字符列表完全在我们必须计算的能力范围内。然而,这有几个问题。在我们将数据转换到嵌入空间后,我们假设该空间中的距离具有语义解释,也就是说,我们假设靠得很近的点具有相似的含义,而距离较远的点则具有非常不同的含义。这使我们能够隐式地将我们对一个词的了解扩展到它的直接邻居,这是我们依赖于计算效率的假设,并且转换器从中汲取了一些泛化能力。

在单字层面,语义内容非常少。例如英语中有一些单字词,但并不多。表情符号是个例外,但它们并不是我们正在查看的大多数数据集的主要内容。这使我们处于无用的嵌入空间的不幸境地。

如果我们能够观察足够丰富的字符组合来构建语义上有用的序列,如单词、词干或单词对,那么理论上仍有可能解决这个问题。不幸的是,转换器在内部创建的功能更像是一组输入对,而不是一组有序的输入。这意味着单词的表示将是字符对的集合,而没有强烈表示它们的顺序。转换器将被迫不断地使用字谜,这使得它的工作变得更加困难。事实上,字符级表示的实验表明,转换器在它们方面表现不佳。

Byte pair encoding

幸运的是,有一个优雅的解决方案。称为 字节对编码。从字符级表示开始,每个字符都被分配了一个代码,它自己的唯一字节。然后在扫描一些有代表性的数据后,将最常见的一对字节组合在一起并分配一个新字节,一个新代码。新代码被替换回数据,然后重复该过程。

表示字符对的代码可以与表示其他字符或字符对的代码组合,以获得表示更长字符序列的新代码。代码可以表示的字符序列的长度没有限制。它们会根据需要增长,以表示通常重复的序列。字节对编码最酷的部分是推断要从数据中学习哪些长字符序列,而不是笨拙地表示所有可能的序列。 它学习用单字节代码来表示像transformer这样的长词,但不会将代码浪费在类似长度的任意字符串上,例如ksowjmckder. 而且因为它保留了其单个字符构建块的所有字节代码,它仍然可以表示奇怪的拼写错误、新单词,甚至外语。

当您使用字节对编码时,您可以为其分配一个词汇量大小,并且它将不断构建新代码直到达到该大小。词汇量需要足够大,字符串足够长以捕获文本的语义内容。他们必须意味着什么。然后它们将足够丰富来为变压器供电。

在训练或借用字节对编码器后,我们可以用它来预处理输出数据,然后再将其送入转换器。这将不间断的文本流分成一系列不同的块(希望其中大部分是可识别的单词)并为每个块提供简洁的代码。这是称为标记化的过程。

Audio input

现在回想一下,当我们开始整个冒险时,我们最初的目标是将音频信号或口头命令转换为文本表示。到目前为止,我们所有的例子都是在假设我们正在处理书面语言的字符和单词的情况下得出的。我们也可以将其扩展到音频,但这将更大胆地涉足信号预处理。

音频信号中的信息受益于一些重型预处理,以提取我们的耳朵和大脑用来理解语音的部分。该方法称为梅尔频率倒谱滤波,顾名思义,它的每一点都像巴洛克式的。如果您想深入了解引人入胜的细节, 这里有一个图文并茂的 教程。

预处理完成后,原始音频将变成一系列向量,其中每个元素代表特定频率范围内音频活动的变化。它是密集的(没有元素为零)并且每个元素都是实值的。

从积极的方面来说,每个向量都为转换器提供了一个很好的“词”或标记,因为它意味着某些东西。它可以直接翻译成一组可识别为单词一部分的声音。

另一方面,将每个向量视为一个词是很奇怪的,因为每个向量都是独一无二的。同一组向量值出现两次的可能性极小,因为声音的组合有很多微妙的不同。我们之前的 one-hot representation 和 byte pair encoding 策略没有帮助。

这里的技巧是注意像这样的密集实值向量是我们在嵌入单词 最终得到的。变形金刚喜欢这种格式。为了利用它,我们可以像使用文本示例中的嵌入词一样使用 ceptrum 预处理的结果。这为我们节省了标记化和嵌入的步骤。

值得注意的是,我们也可以对我们想要的任何其他类型的数据执行此操作。许多记录的数据以一系列密集向量的形式出现。我们可以将它们直接插入到转换器的编码器中,就好像它们是嵌入的单词一样。

资源和学分

  • ► OG 论文,Attention is All You Need。
  • ► 一个非常有用的 transformer 的 Python 实现。
  • ► Jay Alammar 富有洞察力的transformer 演练
  • ► Lukasz Kaiser(作者之一) 解释 transformer 工作原理的 演讲。
  • ► Google 幻灯片中的 插图。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值