深度学习 Transformers 笔记(一)
001:Transformer简介 🚀
在本节课中,我们将深入探讨人工智能领域最具突破性的进展——Transformer架构。Transformer是一种深度学习模型,它彻底改变了AI行业,是ChatGPT、谷歌翻译、Meta AI以及DALL-E等图像生成工具的核心。我们将了解什么是Transformer,它为何如此强大,并概述本系列课程的学习目标。
概述
从本视频开始,我们将深入探讨人工智能领域最具突破性的进展,那就是Transformer。Transformer是如今彻底改变AI行业的深度学习模型。它们是ChatGPT、谷歌翻译、Meta AI以及DALL-E等图像生成工具的关键组成部分。Transformer使得AI能够执行理解和回应自然语言等认知任务,而所有先前的模型都未能做到这一点。如今,Transformer已经非常先进,甚至被用于计算机视觉任务。
那么,什么是Transformer?它们是如何变得如此强大的?这正是我们将在本系列视频中学习的内容。我们将了解什么是Transformer,是什么让它们如此特别,并详细剖析Transformer的每一个组成部分。我们不会像互联网上大量内容那样只停留在表面概述,而是会深入探讨其背后的数学原理、科学家们决定在Transformer架构中加入某些组件的原因,以及这一切如何神奇地协同工作。
在本系列课程结束时,我的目标是让你能够极其详细地理解Transformer架构和设计,并能够在纸上从头开始复现它。这将是一门中级课程,你需要对神经网络、循环神经网络有基本的理解,最好也了解卷积神经网络。一旦你了解了这些知识,理解Transformer的工作原理就会变得非常容易。如果你之前没有学习过神经网络和循环神经网络,可以点击右上角的“i”按钮,它会带你进入我的神经网络和循环神经网络播放列表。
RNN的局限性
上一节我们介绍了本系列的学习目标。本节中,我们来看看Transformer出现之前,主流模型RNN(循环神经网络)存在哪些局限性,这些局限性正是催生Transformer的原因。
如果你阅读RNN生成的文字,会发现它们虽然不错,但缺乏句子中的自然流畅感。有时会出现单词或句子的重复,有时内容不恰当。例如,它会突然将上下文切换到完全不同的内容,或者有时语法不正确。我们甚至有很多由RNN生成的诗歌例子。但当我们阅读它们时,会感觉非常生硬、难以阅读,缺乏节奏感且不自然。
另一方面,基于Transformer的应用程序生成的文字非常好,甚至可以帮助我们纠正语法错误,并改善电子邮件、文章或消息中文字的自然流畅度。事实上,我在编写本视频脚本时,就借助了ChatGPT来撰写和润色。
那么,RNN到底存在什么问题?如果你还记得,RNN是这样工作的:我们在不同的时间步依次传递输入单词,并更新这个隐藏状态,它充当记忆上下文。这个记忆上下文在每个时间步都会被每个新单词覆盖,在句子结束时,记忆上下文理论上拥有整个句子的上下文摘要。
RNN的问题在于,其记忆上下文是有限的,只能存储有限数量的数据,并且每个新单词都会更新它。它可能不记得在句子开头看到的内容,这被称为梯度消失问题——句子开头的梯度在句子后期被覆盖或消失了。我们确实尝试通过LSTM和GRU等RNN模型的变体来解决这个问题,并在一定程度上增加了句子长度。但即使是LSTM和GRU,它们的记忆上下文也是有限的,在处理像多个段落、文章或论文这样的大句子时,仍然难以应对梯度消失问题。因此,RNN无法处理长文本中的长距离依赖,即出现在句子开头的单词与出现在句子末尾的单词之间的关系。
假设我们以某种方式克服了梯度消失问题,并能够保存文章的整个上下文。这种顺序处理输入的另一个问题是,处理长句子需要更多时间,因为输入是按顺序处理的,我必须等待前一个输入处理完毕才能处理当前输入。通读整篇文章需要更多时间,因此训练会非常慢。
除此之外,RNN仍然有一些无法完全解决的局限性。例如,英语中有很多单词在不同的上下文中可能意味着完全不同的事物。英语语言有很多错综复杂和微妙之处,基于RNN的模型无法解释。
一个单词在英语中可以表示不同的事物。例如,如果我提到单词“light”,你会想到什么?可能是人类眼睛可见的波长光谱。但如果我说“light weight”,这与作为波长的“light”无关,现在我们的上下文完全转移到了重量度量上,指的是重量较轻的东西。那“light blue”呢?现在上下文完全转移到了颜色上,表示较浅的蓝色阴影。再说“light a candle”,现在“light”充当动词。同一个单词在不同的上下文中可能意味着完全不同的事物。
RNN无法解决这个问题,这就是为什么RNN生成的单词感觉不自然或生硬。
RNN无法解决这个问题的原因是它使用词嵌入来表示单词。词嵌入是向量中的一组固定数字,对应于对象的某些特征。例如,单词“apple”的词嵌入可能有一些较高的值对应于“科技”,也可能有一些较高的值对应于“水果”,但它是一个固定的向量。如果我有两个句子:“I love Apple phones”和“I love Apple juice”,那么我将在两个句子中使用相同的固定词嵌入来表示单词“apple”。假设它的值有50%的科技属性和50%的水果属性。现在,因为它50%科技、50%水果,所以无法有效地表示该句子中“apple”这个词。我需要的是能够根据上下文灵活、动态地改变单词“apple”的词嵌入的能力。例如,如果我谈论的是苹果汁,我希望“是水果”、“是食物”等属性的值更高;当我谈论苹果手机时,我希望“是科技”、“能打电话”、“智能”等属性的值更高。只有使用这种词嵌入,我才能有效地表示该上下文中的单词“apple”。
Transformer的核心:自注意力机制
上一节我们探讨了RNN的局限性,特别是其无法动态理解上下文。本节中,我们将介绍Transformer解决此问题的核心机制——自注意力。
Transformer拥有这种能力,它可以动态改变单词的词嵌入表示,因为Transformer使用了称为自注意力的机制。自注意力是Transformer的核心,使其异常强大。我们将在后续视频中深入探讨自注意力,这里先简要介绍它是什么。
自注意力是一种机制,用于捕捉输入句子中单词之间的依赖关系和关联。它会创建一个映射,告诉哪些单词彼此密切相关。例如,如果我有这个句子“I love Apple”,那么自注意力可能会在单词“I”和“love”之间建立紧密的依赖关系,表明主语“I”正在执行“love”这个动作。同样,它可能在单词“apple”和“phone”之间建立紧密的依赖关系,表明这两个词在句子中是一起的。
一旦识别出这些依赖关系,它将为句子中的每个单词生成一个新的词嵌入表示。例如,如果我将“I love apple phones”这个句子输入自注意力机制,它会尝试识别相关和依赖的单词,并基于这种依赖关系,改变该句子中每个单词的嵌入表示。因此,最初“apple”有50%科技和50%水果属性,经过自注意力处理后,它会更多地偏向科技侧,将其表示为一家科技公司。这样,它就被一个更丰富的嵌入所取代,这个嵌入代表了“apple”在这个句子中的真实含义。同样,如果我有“Apple juice”,那么它可能会在单词“apple”和“juice”之间建立紧密的依赖关系,并将“apple”的词嵌入更多地偏向水果侧。理解这一点非常重要,所以我将再举一个例子来解释。
以“light”为例。如果最初“light”的词嵌入与“闪光”、“明亮”、“波”等相关,那么在自注意力操作之后,它将修改单词“light”的嵌入,使其更恰当地表示句子中的上下文。如果我谈论的是“light weight”,那么“light”的词嵌入将更多地偏向“轻盈的”、“重量”、“精致”、“透气”等词。如果我谈论的是“light blue”,那么它将更多地偏向颜色侧,也许介于蓝色和白色之间。如果我说“light-headed”,它可能会转向“放松”、“昏厥”或“头晕”等词。由于Transformer的这种能力,Transformer理解和生成的语言自然流畅。它理解“Apple phones”与“Apple juice”的含义区别。
不仅如此,这还赋予了Transformer想象不存在事物的能力。例如,如果我要求ChatGPT生成一张“长满叶子的云”的图片。它不会为我生成云和叶子两张独立的图片,而是会识别“长满叶子的”和“云”这两个词之间的关系,并生成完全想象出来的东西。但因为自注意力机制动态改变了单词“cloud”的表示,它会为其添加“长满叶子的”属性,结果将是一朵具有叶子属性的云,使其成为“长满叶子的云”。这难道不令人着迷吗?现在AI甚至具备了像我们人类一样想象事物的能力。
但Transformer的能力不仅限于此,它还能做得更多。你是否想过,同一个英语句子可以有多种解释?例如,以这个句子为例:“She saw the man with the telescope.” 它可以有多种解释,在英语中可能是模糊的。一种解释可能是:她看到了那个拿着望远镜的男人。另一种可能是:她用望远镜看到了那个男人。这两种解释都是有效的。在观察这个句子时,Transformer不仅可以生成一种,还可以生成多种不同的注意力图,捕捉句子的不同解释。在第一种解释中,它可能捕捉“man”和“telescope”之间的关系;而在第二种解释中,它可能捕捉单词“she”和“telescope”之间的关系。生成多种解释后,它可以根据段落的周围上下文选择适当的解释。这被称为多头注意力,我们将在本系列后面详细讨论。对于RNN来说,这是绝对不可能做到的。
Transformer的架构规模与并行处理
上一节我们了解了自注意力如何赋予Transformer动态理解上下文的能力。本节中,我们来看看Transformer的架构规模,以及它是如何通过并行处理实现高效训练的。
那么,Transformer是如何做到这些令人着迷的事情的?它的架构有多大?有多复杂?Transformer的规模如何?有多少参数?现代基于Transformer的应用程序拥有非常非常多的参数。OpenAI的GPT-3模型大约有1750亿个参数,这相当于仅可学习参数本身就占用了350GB的数据。你知道GPT-4有多少参数吗?1.8万亿。这相当于3.6TB的参数。很疯狂,对吧?那么你应该问我,训练如此庞大的模型是如何可能的?训练它们可能需要数年时间,也许在你我变老之前都无法完成。
我对这个问题的回答是,Transformer拥有另一个RNN无法具备的能力。还记得吗,对于RNN,每个输入单词都是顺序处理的。要处理任何单词,我需要更新其之前所有单词的上下文向量。RNN的这种顺序处理使其非常慢,因此很难用大量数据训练RNN模型。而在Transformer中,我从未说过需要按顺序处理单词。在Transformer中,所有单词同时作为输入传递,新的词嵌入向量是基于它们彼此之间的关系计算出来的。因此,Transformer中没有这种顺序依赖关系。正因为如此,我可以并行处理所有单词。这四个单词是同时处理的。由于这种并行化,我甚至可以使用多个GPU来处理一个长输入句子,同时传递输入中的所有单词,多个GPU将并行处理它们,为每个单词生成新的嵌入表示。因此,如果我的句子有100个单词,那么处理这100个单词所需的时间与处理1个单词所需的时间相同。而类似的事情在RNN中是不可能的。RNN只能使用1个GPU,因为每个单词都是按顺序一个接一个处理的。这使得Transformer的训练速度比基于RNN的模型快得多,甚至预测也更快。虽然涉及1.8万亿个参数,但GPT-4只需几秒或几毫秒就能做出响应。正因为如此,“大语言模型”(LLM)这个术语才成为行业的热门词汇,因为现在人们可以轻松地用海量数据训练模型。
之前我说过,RNN难以处理长距离依赖或捕捉彼此相距很远的单词之间的关系(可能相隔多个段落)。自注意力和Transformer在这方面有帮助吗?由于我们并行同时处理单词,我可以将所有单词视为处于同一空间中。无论单词在句子中相距多远,我都可以计算它们之间的依赖关系。通过消除单词的顺序处理,我们甚至解决了捕捉长距离依赖的问题,并且可以处理任意长度的句子。
迁移学习与多模态能力
上一节我们探讨了Transformer如何通过并行处理实现高效训练。本节中,我们来看看Transformer另外两个关键优势:迁移学习能力和多模态处理能力。
正如我之前所说,我们才刚刚开始列举Transformer的好处。我们还没有结束。另一个使Transformer在工业界被广泛接受并具有巨大可扩展性的特性是,它允许迁移学习。什么是迁移学习?你可能以前听说过。迁移学习是一种技术,我分两个阶段训练模型:一个是预训练阶段,我训练它执行一些通用任务;另一个是微调阶段,我针对特定任务训练它。这最初是在卷积神经网络中引入的。我可以创建一个深度卷积神经网络模型,并对模型进行预训练,使其学习宇宙中的通用特征和通用对象。主要目的是让模型能够理解不同对象的样子,并对这些特征有一个大致的了解。
一旦模型预训练完成,我就可以针对特定任务(如猫狗分类或物体检测)进行微调。与CNN类似,我可以采用一个Transformer模型并对其进行预训练,使其理解通用的英语语言。我会做的是:提供随机的部分句子,并让它预测下一个单词。我将使用来自书籍、新闻文章等的大量此类数据对其进行训练。尽管这种训练没有达到任何特定目的,但模型将对英语语言有一个大致的了解,比如哪些单词彼此相关,它将掌握性别和语法的一般概念。一旦模型对英语语言和单词之间的关系有了大致了解,我就可以采用这个预训练模型,并针对更具体的任务(如问答聊天机器人、文本摘要或语言翻译等)进行微调。同一个预训练模型可以针对多个不同的任务进行微调。微调需要的数据量和时间比预训练要少得多。由于我已经创建了一个预训练模型并重用于多个不同的应用程序,因此创建特定应用程序的监控成本和时间显著减少。
这种预训练在RNN中是不可能的,或者即使可能,由于RNN有限的记忆上下文大小及其在大量数据上训练的局限性,效果也不是很好。如果没有预训练和微调阶段,传统的RNN将需要从头开始分别针对所有这些特定任务进行训练,从而显著增加成本和时间。此外,由于Transformer模型的这一特性,我们能够快速扩展并在很短的时间内创建基于Transformer的应用程序。最近,一家名为Hugging Face的公司在该行业变得非常流行,它提供了各种预训练的基于Transformer的模型,以及用少量数据和几行代码进行微调的工具。任何新的AI初创公司都可以来到这个平台,Hugging Face将提供开箱即用的预训练模型,并帮助他们根据需求在特定任务上微调该模型,而无需从头开始创建和训练模型。这难道不神奇吗?当我第一次了解到这一点时,我感到非常惊奇。现在,任何人都可以在很短的时间内创建可扩展的大语言模型,这些模型可以像我们人类一样执行认知任务。技术和AI正在飞速发展。
最后,我想讨论的Transformer的最后一个特性是多模态。Transformer中的自注意力不仅可以用于解释文本,还可以用于解释不同类型的数据,如图像、音频和视频。它可以理解和生成跨不同数据类型的内容。这意味着我可以输入我想要生成的图像的文本描述,Transformer将生成一张图像;或者,另一方面,我可以向Transformer输入一张图像,它可以生成该图像的文本描述;或者,我可以同时提供图像和文本作为输入,并向其提出一些具体问题。这个处理视觉和语言的领域被称为视觉语言模型。
在本系列后面,我们将看到这是如何完成的,以及为什么Transformer如此容易同时处理不同类型的数据。OpenAI有一个名为DALL-E的应用程序,你可以在其中给出你想要生成的图像的描述,它将为你生成一张图像。甚至在生成图像之后,你还可以根据你的需求进行细化,比如你可以改变面部表情或改变图像中的其他一些细节。借助Transformer,我们可以融合来自不同模态的信息,从而产生更丰富、更具上下文感知能力的模型,该模型可以同时处理图像、文本、音频和视频。通过这种方式,AI能够比历史上任何时候都更接近地模仿人类的理解和创造力。
总结与展望
本节课中,我们一起学习了Transformer架构的简介。我们首先概述了Transformer在AI领域的重要性及其应用。接着,我们探讨了其前身RNN模型的局限性,包括梯度消失问题、顺序处理的低效性以及无法动态理解上下文。然后,我们深入介绍了Transformer的核心——自注意力机制,它如何通过计算单词间的关系来动态生成上下文相关的词嵌入,从而解决RNN的不足。我们还了解了Transformer庞大的参数规模和高效的并行处理能力,这使得训练大语言模型成为可能。最后,我们讨论了Transformer支持迁移学习和处理多模态数据(文本、图像、音频、视频)的强大特性,这使其成为当今最先进的AI模型基础。
我非常高兴能带你继续探索Transformer的工作原理。我们将看到Transformer的每一个组成部分,是什么构成了Transformer,以及它们为何如此有效。我们不仅会看到直观的表面概述,还会深入探讨其中的数学原理。和往常一样,如果你觉得本视频有帮助,请点击点赞按钮,与你的朋友分享,因为制作这样的视频需要很多精力。考虑订阅并支持本频道,以便我能继续制作更多这样的未来视频。请在下面的评论部分告诉我你对本视频的看法,我们下期再见。
002:自注意力机制详解
在本节课中,我们将要学习Transformer架构的核心——自注意力机制。我们将从零开始,一步步理解它要解决的问题、其工作原理以及它为何如此强大。
概述:为何需要自注意力?
在自然语言中,同一个词在不同的上下文中可能具有完全不同的含义。例如,“light”这个词,在“light weight”中表示“轻的”,在“light blue”中表示“浅色的”,在“light a candle”中则表示“点燃”。传统的词嵌入模型为每个词分配一个固定的向量表示,无法根据上下文动态调整其含义。自注意力机制的目标,就是为句子中的每个词生成一个上下文相关的词表示,使其能更准确地反映该词在特定句子中的真实含义。
上一节我们介绍了自注意力要解决的核心问题,本节中我们来看看它是如何具体实现的。
从问题到解决方案
要生成“Apple”在句子“I love Apple phones”中的新表示,我们需要考虑句子中所有其他词对它的影响。一个直观的想法是,新的“Apple”表示可以由句子中所有词的词嵌入按一定比例组合而成。
以下是构建新词表示的基本思路:
-
新表示构成:新“Apple” = (与“love”的关联度 × “love”的词嵌入) + (与“Apple”自身的关联度 × “Apple”的词嵌入) + (与“phones”的关联度 × “phones”的词嵌入)。
-
核心任务:关键在于如何计算“Apple”与句子中每个词之间的“关联度”。
这些关联度本质上衡量的是词与词之间的相似性。在词嵌入空间中,距离越近的词通常语义越相似。因此,我们可以通过计算词嵌入向量之间的点积来量化相似性。
公式:相似性分数 = 词向量A · 词向量B的转置
然而,直接计算出的点积分数可能很大、很小甚至是负数,我们需要将其转化为概率分布(总和为1),以便进行加权组合。这里我们使用Softmax函数。
公式:概率 = Softmax(相似性分数)
最终,新的词表示就是这些概率与对应词向量的加权和。
引入可学习参数
上述基础方法存在两个主要问题:
-
缺乏可学习性:整个过程是固定的数学运算,模型无法通过训练学习针对特定任务调整哪些相似性是重要的。
-
对称性问题:使用相同的词向量计算相似性,会导致“A与B的相似性”等于“B与A的相似性”,这可能不符合实际需求。
为了解决这些问题,我们为每个词引入三个不同的可学习变换,生成三个新的向量:
-
查询向量 (Query, Q):用于“主动询问”与其他词的相关性。
-
键向量 (Key, K):用于“被询问”,提供与其他词匹配的键。
-
值向量 (Value, V):包含词的实际信息内容,用于最终的加权组合。
生成方式:Q = 词嵌入 * W_q, K = 词嵌入 * W_k, V = 词嵌入 * W_v
其中 W_q, W_k, W_v 是可训练的权重矩阵。
现在,计算“Apple”与“phones”的关联度,变为计算 Q_apple 和 K_phones 的点积。这样,关联度计算变得不对称且可学习。
完整的自注意力操作流程
以下是计算一个词(例如“Apple”)新表示的具体步骤:
-
计算相似性分数:用“Apple”的查询向量
Q_apple分别与句子中所有词(包括自身)的键向量K做点积。分数_i = Q_apple · K_i^T -
应用Softmax:将所有分数通过Softmax函数,得到归一化的注意力权重(概率)。
注意力权重_i = Softmax(分数_i) -
加权求和:将注意力权重与对应词的值向量
V相乘并求和,得到“Apple”的新表示。新表示_apple = Σ(注意力权重_i * V_i)
这个过程会并行应用于句子中的每一个词,为每个词都生成一个全新的、上下文相关的表示。
自注意力的优势
自注意力机制带来了两大关键优势:
-
并行计算:由于句子中每个词的新表示计算是相互独立的,因此可以完全并行化处理。这与RNN必须按序列顺序处理截然不同,极大地提升了训练和推理速度。其计算复杂度与序列长度呈线性关系,且能充分利用GPU的并行计算能力。
-
捕获长程依赖:在计算某个词的表示时,自注意力会直接考虑句子中所有其他词的信息,无论它们距离多远。这有效解决了RNN难以处理长距离依赖的问题。
矩阵形式与最终公式
在实际实现中,所有计算都以矩阵形式进行,以提升效率。
假设输入句子矩阵 X 的形状为 (n, d_model),其中 n 是词数,d_model 是词嵌入维度。
-
计算Q, K, V矩阵:
Q = X * W_q,K = X * W_k,V = X * W_v -
计算注意力分数并应用Softmax:
注意力权重 = Softmax( (Q * K^T) / sqrt(d_k) )(注:除以
sqrt(d_k)是为了稳定梯度,防止点积结果过大,这将在后续课程中详解。) -
计算输出:
输出 = 注意力权重 * V
最终公式:
自注意力(Q, K, V) = softmax( (Q K^T) / sqrt(d_k) ) V
这正是论文《Attention Is All You Need》中提出的缩放点积注意力公式。
总结
本节课中我们一起学习了Transformer的核心——自注意力机制。我们从上下文词表示的需求出发,逐步推导出通过计算词间相似性、引入可学习的Q、K、V向量来动态调整词表示的方法。我们详细剖析了自注意力操作的每一步流程,并理解了它带来的并行计算和捕获长程依赖的巨大优势。最后,我们看到了其高效的矩阵运算形式及标准数学公式。掌握自注意力是理解现代大语言模型的基础,在接下来的课程中,我们将以此为基础,探索Transformer架构的其他组成部分。
003:自注意力中的线性变换
在本节课中,我们将深入探讨自注意力机制中的线性变换。我们将理解为何需要引入可学习的参数,以及线性变换如何帮助模型更准确地捕捉词语间的相关性和生成更贴合上下文的词表示。
在上一节中,我们介绍了自注意力机制的基本操作,并提到需要将输入词嵌入与权重矩阵 WQ**、**WK、W^V 相乘。本节中,我们将详细分析这种线性变换为何是自注意力机制的关键,并通过数学示例证明其有效性。
自注意力机制回顾
首先,让我们快速回顾自注意力机制的目的和基本流程。自注意力旨在为句子中的每个词生成一个基于上下文的新表示。例如,在句子“我爱苹果手机”和“我爱苹果汁”中,“苹果”一词的含义不同。我们希望“苹果”的新表示能根据其周围的词(如“手机”或“汁”)进行调整。
以下是自注意力计算新词表示的基本步骤:
-
为句子中的每个词计算其与所有词(包括自身)的相似度得分。
-
使用Softmax函数将这些得分转换为概率分布(总和为1)。
-
将这些概率作为权重,对原始词嵌入进行加权求和,得到新词表示。
在基础版本中,相似度通过词嵌入向量的点积计算:相似度 = 词嵌入_i · 词嵌入_j。然而,这种方法存在局限性。
基础自注意力的局限性
让我们通过一个例子来理解基础方法的不足。考虑句子“我爱苹果手表”。我们希望“手表”的新表示能体现“苹果智能手表”的含义。
在词嵌入空间中,“手表”的向量可能靠近“时钟”、“时间”等词,而“苹果”的向量可能同时包含“水果”和“科技公司”的属性。因此,直接计算“手表”和“苹果”的点积相似度可能会很低,因为它们的原始向量在空间中可能相距较远。
这会导致两个问题:
-
相关性捕捉不足:生成的“手表”新表示中,来自“苹果”的成分会非常少。
-
特征混合不当:即使加入了一点“苹果”的成分,这个成分也同时包含了“水果”和“科技公司”的特征,而理想情况下,“苹果手表”不应包含“水果”特征。
为了解决这些问题,我们需要在计算相似度和生成新表示时引入可学习的参数。
引入线性变换
解决方案是将输入词嵌入与三个可训练的权重矩阵相乘:
-
查询(Query):
Q = 词嵌入 * W^Q -
键(Key):
K = 词嵌入 * W^K -
值(Value):
V = 词嵌入 * W^V
这种向量与矩阵的乘法操作称为线性变换。它在神经网络中很常见,其核心公式为:
输出 = 输入 * W + b
在自注意力中,我们通常省略偏置项 b 和非线性激活函数,专注于线性变换本身。
线性变换主要实现两个目标:
-
改变维度:例如,将512维的词嵌入向量投影到64维的空间。
-
提取并增强相关特征:这是更关键的作用。权重矩阵
W经过训练后,能够从原始输入中筛选并放大对当前任务(如理解“苹果手表”)最重要的特征,同时弱化或不相关的特征。
线性变换如何工作:一个数学示例
让我们通过一个简化的例子来具体理解。假设我们的词嵌入只有三个维度,分别代表三个特征:[是科技产品, 是手表, 是水果]。
我们定义三个词的嵌入向量:
-
苹果
[2, 0, 2]:高“是水果”特征,中等“是科技产品”特征(因为苹果公司)。 -
橙子
[0, 0, 3]:只有高“是水果”特征。 -
手表
[1, 3, 0]:高“是手表”和“是科技产品”特征。
现在,假设我们有一个为电子产品商店训练的权重矩阵 W,其目的是提取“科技相关”的特征。我们设定:
W = [[4, 2, 0], [3, 1, 1]]
我们对每个词进行线性变换:新向量 = 旧向量 * W
-
苹果的新向量:
[2, 0, 2] * W = [2*4+0*2+2*0, 2*3+0*1+2*1] = [8, 8] -
橙子的新向量:
[0, 0, 3] * W = [0*4+0*2+3*0, 0*3+0*1+3*1] = [0, 3] -
手表的新向量:
[1, 3, 0] * W = [1*4+3*2+0*0, 1*3+3*1+0*1] = [10, 6]
观察结果:
-
经过变换后,“苹果”的向量
[8,8]值很高,表明其科技属性被提取和放大了。 -
“橙子”的向量
[0,3]值很低,表明其科技属性很少。 -
“手表”的向量
[10,6]也具有高值。
这个新向量空间更专注于“科技相关”的特征。对于模型来说,使用变换后的[8,8]来表示“苹果”,比使用原始的[2,0,2]更能清晰地表明“苹果科技公司”的含义。
线性变换如何解决“苹果手表”问题
现在,我们将这个原理应用到自注意力中。在计算“手表”的新表示时:
-
计算查询Q、键K、值V:
-
假设
Q_watch = E_watch * W^Q = [10, 6](如上例计算) -
假设
K_apple = E_apple * W^K,K_watch = E_watch * W^K。通过精心设计(实则是训练)W^K,我们可以得到类似K_apple = [10, 6],K_watch = [4, 4]的结果。
-
-
计算相似度:
-
相似度(手表, 苹果) = Q_watch · K_apple = [10,6] · [10,6] = 136 -
相似度(手表, 手表) = Q_watch · K_watch = [10,6] · [4,4] = 64
-
-
结果:现在,“手表”与“苹果”的相似度(136)远高于“手表”与自身的相似度(64)。这意味着在生成“手表”的新表示时,“苹果”的贡献将占主导。
同时,V_apple = E_apple * W^V 生成的“值”向量,也经过了线性变换,主要包含“苹果科技公司”的丰富特征,而不是“水果”特征。
因此,最终“手表”的新表示将是这些高权重、富含科技特征的向量的加权和,从而能够更准确地表示“苹果智能手表”的概念。
模型的强大之处
你可能会问,模型如何为成千上万的词语学会正确的权重?在真实的Transformer模型中,参数规模极其庞大:
-
原始论文中,词嵌入维度为512,线性变换后为64。
-
他们不使用一个,而是使用8个这样的权重矩阵组(即8个头),以便从不同角度捕捉信息。
-
这仅仅是一个编码器块中的一个多头注意力层。一个完整的Transformer模型有6个这样的编码器块,还有解码器部分。
这意味着,处理像“手表”这样的单个词语,一个Transformer块就可能动用数百万个参数。通过在海量文本数据上进行训练,反向传播算法会自动调整这些数以百万计的参数,使模型学会捕捉词语之间复杂的、对任务有用的依赖关系,并生成精准的上下文相关表示。
总结
本节课中,我们一起学习了自注意力机制中线性变换的核心作用。我们首先回顾了基础自注意力的局限性,即无法有效捕捉特定上下文下的词语相关性。然后,我们引入了可学习的权重矩阵 WQ**、**WK、W^V,并通过线性变换的数学示例,详细说明了它如何实现降维和提取任务相关特征这两个关键功能。最后,我们证明了通过引入这些可训练参数,模型能够学会显著提升相关词语(如“手表”和“苹果”)之间的相似度得分,并利用富含相关特征的“值”向量,最终生成更准确、更贴合上下文的新词表示。这为理解Transformer模型强大的表征能力奠定了基础。
004:为什么叫查询、键和值?自注意力机制详解
在本节课中,我们将要学习Transformer自注意力机制中“查询”、“键”和“值”命名的由来。理解这些术语的类比,有助于我们更直观地把握自注意力机制的工作原理。
在上一节关于自注意力的视频中,我们了解到需要将词嵌入向量分别与三个权重矩阵 WQ、WK 和 WV 相乘。你可能会好奇,为什么作者特意选择了“查询”、“键”和“值”这些名称。本节将对此进行解释。
计算机科学中的概念类比
在计算机科学中,“查询”、“键”和“值”有特定的含义。我们可以通过一个Python字典的例子来理解。
假设我们有一个字典,它将动物名称映射到其叫声:
animal_sounds = {
"dog": "woof",
"cat": "meow",
"cow": "moo"
}
在这个例子中:
-
键 是字典的索引,例如
"dog","cat"。 -
查询 是我们向字典提出的问题,例如
animal_sounds["dog"]。 -
值 是查询返回的实际信息,例如
"woof"。
字典会根据我们提供的“键”来响应“查询”,并返回对应的“值”。基于不同的键,返回的值也会不同。
自注意力机制中的类比
类似的类比被应用在自注意力机制中。在自注意力中,我们将一个词的“查询”向量与句子中所有词的“键”向量进行比较。
例如,我们的句子是“我爱苹果手机”,我们想为“苹果”这个词生成新的表示。
我们会将“苹果”的查询向量 Q苹果 与“我”、“爱”、“苹果”、“手机”的键向量 K我、K爱、K苹果、K手机 逐一比较。基于这个比较,会生成一个相似度分数。
你可以这样理解:Q苹果 在向所有的键向量提出一个查询。它在问:“你们和我有多相似?” 而键向量则通过它们所携带的信息来“回答”这个问题。
这就是为什么这些向量被称为“查询”和“键”向量。查询向量向键向量提出一个具体问题(关于相似度),键向量则用其包含的信息来回应。Q 和 K 的点积运算最终返回这两个向量之间的相似度分数。
值向量的作用
得到相似度分数后,我们会将其输入Softmax函数,转换为概率分布。这些概率随后会与“值”向量相乘。
以下是为什么它们被称为“值”向量的原因:不同“值”向量的加权组合,最终形成了“苹果”这个词的新表示 E’苹果。
整体上,这个新的 E’苹果 是由这些值向量按不同比例组合而成的。因此,值向量是构成新词表示的实际的、相关的信息。正因为它们是被传递以生成新表示的实际相关信息,所以这些向量被称为“值”向量。
总结
本节课中,我们一起学习了Transformer自注意力机制中“查询”、“键”和“值”命名的来源。我们通过字典的类比,理解了查询提出问题、键作为索引、值作为返回信息的基本概念,并将这一概念映射到了自注意力机制中查询向量与键向量计算相似度、并用值向量加权求和生成新表示的过程。理解这些术语有助于夯实Transformer架构的基础知识。
005:缩放点积注意力机制 🎯
在本节课中,我们将要学习Transformer架构中的一个核心组件——缩放点积注意力机制。我们将深入探讨其数学原理,特别是为什么需要在计算中引入一个缩放因子。
在上一节中,我们从零推导了自注意力机制的公式。然而,如果你查看原始论文《Attention Is All You Need》中的公式,会发现一个细微但至关重要的区别:在计算点积后,需要除以一个因子 sqrt(d_k)。本节中,我们将详细解释这个缩放因子的作用及其必要性。
回顾:自注意力机制基础
首先,让我们简要回顾一下上一节的内容。
假设我们有一个句子:“Love Apple Phones”。我们会进行以下操作:
-
获取每个单词的词嵌入向量,并组成矩阵
X。 -
将
X分别与三个权重矩阵W_Q、W_K、W_V相乘,得到查询(Query)、键(Key)和值(Value)矩阵:Q、K、V。 -
计算
Q和K的转置的点积,得到注意力分数矩阵,该矩阵表示了单词之间的依赖关系。 -
对注意力分数矩阵应用
softmax函数,将其转换为概率分布。 -
将
softmax的输出与值矩阵V相乘,得到新的、融合了上下文信息的词表示。
我们推导出的公式如下:
Attention(Q, K, V) = softmax(Q * K^T) * V
然而,论文中的标准公式是:
Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V
这里的 d_k 是查询和键向量的维度。我们的核心问题就是:为什么需要除以 sqrt(d_k)?
缩放的必要性:方差问题
我们需要除以 sqrt(d_k),主要是基于一个数学特性:矩阵点积结果的方差与参与计算的向量的维度成正比。
这意味着什么?假设我们计算 Q 和 K^T 的点积得到矩阵 X。X 中每个元素 x_ij 是 Q 的第 i 行向量与 K^T 的第 j 列向量(即 K 的第 j 行)的点积。随着向量维度 d_k 增大,这些点积结果的值的分布(方差)也会变得更大。
直观理解
我们可以通过一个简单的例子来直观感受:
-
假设我们使用维度为2的随机向量(值在-10到10之间)进行点积,结果值的范围可能在-23到23之间,均值接近0。
-
如果使用维度为5的随机向量进行同样的操作,结果值的范围可能扩大到-49到122之间,均值也可能偏离0更远。
显然,高维向量点积结果的数值波动(方差)更大。这是因为点积是多个乘积项的和,维度越高,求和项越多,总和的潜在最大值和最小值就越大,从而导致方差增大。
数学关系
在理想条件下(假设矩阵 Q 和 K 中的元素是均值为0、方差为1的独立随机变量),点积结果矩阵 C = Q * K^T 的方差满足:
Var(C) = d_k * Var(Q_row) * Var(K_row) ≈ d_k
这表明,C 的方差确实与维度 d_k 成正比。
高方差在深度学习中的问题
那么,在注意力机制中,高方差会带来什么问题?关键在于后续的 softmax 操作。
softmax 函数的特性是放大输入值之间的差异。对于输入向量 [7, 3],softmax 输出可能是 [0.98, 0.02],将70% vs 30%的差异放大到了98% vs 2%。
如果输入 softmax 的矩阵 Q*K^T 方差很大(即某些值极大,某些值极小),softmax 会将其推向极端:某些输出概率接近1,而其他所有输出概率接近0。
这会导致梯度消失问题,阻碍模型训练:
-
在反向传播时,梯度通过
softmax传递。 -
当
softmax输出非常尖锐(一个值接近1,其余接近0)时,相对于非最大值的输入梯度会变得非常小。 -
这些微小的梯度在多层网络中反向传播时会不断衰减,最终导致底层网络的权重几乎得不到更新。
-
模型无法有效利用所有的参数进行学习,训练过程会陷入停滞。
解决方案:除以 sqrt(d_k)
为了稳定训练,我们需要控制点积结果的方差,使其不随 d_k 增大而剧烈变化。目标是将方差保持在一个稳定的水平。
根据方差的性质:如果 Var(X) = σ^2,那么 Var(a * X) = a^2 * σ^2。
-
设未缩放的注意力分数为
X = Q * K^T。 -
在理想情况下,
Var(X) ∝ d_k。我们可以表示为Var(X) = d_k * σ^2,其中σ^2是d_k=1时的基础方差。 -
为了抵消
d_k的影响,我们将X除以sqrt(d_k)。 -
令缩放后的分数为
X_scaled = X / sqrt(d_k)。 -
那么,
Var(X_scaled) = Var(X) / (sqrt(d_k))^2 = (d_k * σ^2) / d_k = σ^2。
结论:通过除以 sqrt(d_k),我们确保了无论查询和键的维度 d_k 是多少,输入到 softmax 函数的值的方差都大致保持恒定。这防止了梯度消失,使得模型训练更加平稳和高效。
因此,完整的、稳定的缩放点积注意力公式为:
Attention(Q, K, V) = softmax( (Q * K^T) / sqrt(d_k) ) * V
总结
本节课中我们一起学习了缩放点积注意力机制的核心思想。我们首先回顾了自注意力的计算流程,然后指出了原始公式与标准公式的关键差异——缩放因子 1/sqrt(d_k)。
我们深入探讨了其背后的原因:
-
矩阵点积结果的方差与向量维度
d_k成正比。 -
高方差的输入会使
softmax输出变得极端,引发梯度消失问题,阻碍深度学习模型训练。 -
通过除以
sqrt(d_k),我们可以将点积结果的方差归一化,使其不受维度影响,从而保证训练的稳定性。
理解这个缩放步骤对于掌握Transformer架构的细节至关重要。它为接下来学习多头注意力机制奠定了坚实的数学和概念基础。在下一节中,我们将看到如何并行地使用多个这样的缩放点积注意力头,以增强模型捕捉不同子空间信息的能力。
006:多头注意力机制
概述
在本节课中,我们将要学习Transformer架构中的一个核心组件:多头注意力机制。我们将了解为什么单一的注意力头不足以理解复杂的语言,以及如何通过多个注意力头来捕捉句子中丰富的、多层次的语义关系。
从单头注意力到多头注意力
在之前的课程中,我们详细介绍了单头注意力机制的工作原理。它通过分析句子中词语之间的关系,为每个词生成基于上下文的新表示。然而,语言是极其复杂的。
同一个词在不同的语境下可能有完全不同的含义。例如,“light”可以指“光”、“轻的”、“浅色的”或“点燃”。单头注意力机制试图在一个统一的视角下捕捉所有这些关系,但这存在局限性。
上一节我们介绍了单头注意力如何解决词义的上下文依赖问题,本节中我们来看看它的局限性以及为什么需要升级。
单头注意力的局限性
语言的理解需要同时建立词语之间多种复杂的关系,包括主语、宾语、地点、情感等。有时,一个句子甚至可能存在歧义,拥有多种可能的解释。
例如,分析句子“她用望远镜看到了那个男人”。
-
一种解释是:她使用了望远镜去看那个男人。(“她”和“望远镜”之间存在强依赖)
-
另一种解释是:她看到了那个拿着望远镜的男人。(“男人”和“望远镜”之间存在强依赖)
如果我们只使用一个注意力头,它可能只能捕捉到其中一种依赖模式,而无法同时为两种可能的解释生成不同的注意力模式。模型因此可能做出不准确的预测。
随着句子变长,需要捕捉的依赖关系也更多。对于单头注意力来说,要在一个注意力模式中有效突出多个实体间的强依赖关系,几乎是不可能的。它只能捕捉有限数量的关系。
因此,我们的自注意力机制需要一个升级版本来捕捉句子中的这种复杂性。好消息是,这个升级可以很容易地实现。
多头注意力的核心思想
为了理解为什么需要多头注意力,我们可以类比计算机硬件。想象你的电脑只有一根内存条。对于简单任务,它快速高效。但当运行高分辨率游戏或处理大型数据集时,一根内存条就不够用了,系统会变慢甚至无法处理。
为了解决复杂的日常任务,我们只需增加一根内存条,将内存容量提升一倍,从而获得更强大的引擎来处理复杂任务。如果两根不够,你甚至可以添加更多。
单头注意力就像一根内存条。它专注于捕捉句子中的某些模式或关系,例如识别主语或跟踪某个词在上下文中的含义。但是,随着语言复杂性的增加——其微妙的含义、长距离依赖和层次化的语义——单头注意力无法有效地全部捕捉。
这就是多头注意力发挥作用的地方。
多头注意力如何工作
在多头注意力中,我们使用多个注意力头,每个头都专门关注句子的特定部分或特定类型的关系。
以下是多头注意力中不同头可能关注的示例:
-
一个注意力头可能识别实体之间的空间关系(例如,物体放在哪里)。
-
另一个头可能识别主语-动词-宾语的关系。
-
第三个头可能识别不同时间点实体之间的关系。
-
再一个头可能以完全不同的方式解读句子。
所有这些不同的关系为句子中的实体提供了更丰富、更多样化的上下文信息。
这就像在卷积神经网络中使用不同的滤波器。在CNN的一层中,我们使用多个滤波器,每个滤波器从图像中识别独特的模式(如垂直边缘、水平边缘或颜色模式)。这些滤波器共同产生丰富的特征图,模型通过这些特征图来理解图像。
Transformer采用了类似的策略。我们可以将Transformer中的每个自注意力头视为CNN中的一个滤波器。每个自注意力头识别词语之间的关系以产生上下文特征。我们使用多个注意力头来识别句子中词语之间不同且多样的关系。所有这些关系结合起来,为我们提供了从句子中提取的丰富多样的特征,帮助我们做出高度上下文感知的预测,并理解语言的全部细微差别。
多头注意力的工作机制
现在,让我们具体看看多头注意力在Transformer中是如何工作的。
让我们回顾一下句子“我爱苹果手机”的简单例子。在自注意力中,关键组件是通过乘以权重矩阵 WK, WQ, WV 得到的键、查询和值矩阵。
单头注意力的流程如下:
-
首先,将查询矩阵
Q和键矩阵K相乘,以识别句子中词语之间的关系。 -
将结果除以
√(d_k),其中d_k是查询、键和值向量的长度(用于缩放,稳定梯度)。 -
之后,将结果传入Softmax函数。
-
最后,将Softmax的输出与值矩阵
V相乘。 -
这为句子中的所有词生成了新的词表示。例如,会得到“爱”、“苹果”、“手机”各自的新向量表示。
在多头注意力中,我们所做的改变是:
MultiHead(Q, K, V) = Concat(head_1, head_2, ..., head_h) * W^O
where head_i = Attention(Q * W_i^Q, K * W_i^K, V * W_i^V)
具体来说:
-
我们不再使用一组可学习的矩阵
WQ, WK, WV,而是使用多组这样的矩阵。 -
这为相同的输入生成了多组查询、键和值。每组都帮助我们捕捉输入的不同视角。
-
一个注意力头将使用第一组
(Q1, K1, V1)进行处理。 -
另一个注意力头将使用第二组
(Q2, K2, V2)进行处理,依此类推。
每个头内部的注意力机制是相同的,都是基于上下文感知生成新的词表示。但现在,我们不再只为每个词生成一个新表示,而是为句子中的每个词生成 H 个新表示(H 是注意力头的数量)。
这意味着,对于“苹果”这个词,向量 Apple'1 是它的一个新表示,Apple'2 是另一个新表示,直到 Apple'H。每个输出都捕捉了句子的不同方面。
整合多头输出
一旦我们从每个注意力头获得了输出,我们如何进一步处理呢?
以下是处理步骤:
-
拼接:我们将所有头的输出拼接起来,形成一个单一的矩阵。这个长向量是
Apple'1, Apple'2, ..., Apple'H的拼接。 -
线性变换:然后,我们通过一个线性变换对这个拼接后的矩阵进行进一步处理。这个线性变换将为句子中的每个词创建最终的统一词表示。这个变换是通过将拼接后的矩阵
Z乘以另一个可学习的参数矩阵W^O来实现的:Z' = Z * W^O。
你可能会问,为什么需要这个线性变换?
让我们再次回到CNN的类比。在CNN中,卷积层使用不同滤波器产生特征图后,我们会在末尾添加一个全连接层。这个全连接层的工作是解释卷积滤波器生成的不同特征模式,并对整个输入图像进行理解。
多头注意力中的线性变换起着类似的作用。首先,我们从“苹果”的静态词嵌入开始。然后,不同的注意力头为“苹果”创建了不同的特征模式(向量)。每个头从不同视角看待“苹果”。然而,我们仍然需要解释这个拼接后的输出。而且,有可能某个头将“苹果”解释为水果,而忽略了它可能是科技公司的可能性,这种解释可能是不相关的。线性变换的工作就是保留相关特征,并为“苹果”创建一个合适的、统一的表示。这个统一表示将包含“苹果”在该句子中的整体上下文信息。
维度与配置
在我们结束本视频之前,让我们谈谈这里涉及的矩阵维度,并回答“应该使用多少个注意力头”的问题。
在原始的《Attention Is All You Need》论文中:
-
每个词嵌入的维度是
d_model = 512。 -
假设输入有3个词,则输入矩阵大小为
[3, 512]。 -
该输入通过与大小为
[512, d_k]的W矩阵进行线性变换,得到键、查询、值矩阵,其大小为[3, d_k]。论文中设定d_k = d_v = d_model / h = 64(当h=8时)。 -
每个注意力头处理这些
[3, 64]维的键、查询、值,并生成一个同样为[3, 64]维的输出。 -
在论文中,头的数量
h被设为8。 -
拼接这8个
[3, 64]维的输出后,我们得到一个大小为[3, 512]的结果矩阵(因为64 * 8 = 512)。 -
然后,这个矩阵再通过一个大小为
[512, 512]的W^O矩阵进行线性变换,得到最终的输出[3, 512]。
请注意,我们以 [3, 512] 大小的输入开始,并以相同大小的输出结束。但现在,这个输出是高度上下文感知的,甚至能够捕捉不同的解释。
总结
本节课中,我们一起学习了Transformer架构中的多头注意力机制。我们首先回顾了单头注意力的局限性,它难以捕捉语言中复杂、多层次的语义关系和歧义。接着,我们引入了多头注意力的概念,它通过使用多个独立的注意力头,让模型能够从不同视角(如语法、语义、空间关系等)并行分析句子,就像CNN使用多个滤波器提取不同图像特征一样。
我们详细探讨了多头注意力的工作机制:为相同的输入生成多组查询、键和值,分别计算注意力并得到多个上下文向量,最后将这些向量拼接并通过一个线性变换层整合成统一的表示。我们还了解了其典型的维度配置。
多头注意力是Transformer强大理解能力的关键之一。例如,GPT-3模型使用了96个注意力头,使其能够从96种不同的视角解读每个句子,获得了接近甚至超越人类的语言理解能力。下一节课,我们将探讨Transformer的另一个关键部分:位置编码。
注:本教程根据提供的视频内容整理,旨在解释多头注意力机制的核心概念,已移除原视频中的语气词和互动提示,并按照要求的结构和格式进行呈现。
007:位置编码详解 🧠
在本节课中,我们将要学习Transformer架构中的一个核心组件:位置编码。我们将探讨为什么需要它、它是什么、以及它如何帮助模型理解单词的顺序。我们还将深入分析位置编码如何与词嵌入结合,以及为什么这种结合不会相互干扰。
为什么需要位置编码?🤔
上一节我们介绍了自注意力机制并行处理单词的能力。本节中我们来看看这种并行化带来的一个关键问题。
自注意力机制并行处理单词。这意味着它天生不具备任何顺序感。
我们需要找到一种机制,能够注入单词的位置信息,以便模型能够按顺序解释句子。
这就是位置编码发挥作用的地方。
在自然语言处理任务中,了解单词的顺序至关重要。请看以下两个句子:
-
“猫追老鼠”
-
“老鼠追猫”
这两个句子使用了相同的单词,但由于顺序不同,它们的含义完全不同。如果将它们输入自注意力机制,由于并行处理所有单词且无法感知顺序,它会为这两个句子生成完全相同的注意力模式,仿佛它们是相同的句子。这种缺乏顺序理解的能力是自注意力的一个显著限制。
位置编码的设计思路 💡
既然我们不能回到RNN那样的顺序处理方式,那么如何解决这个问题呢?唯一的办法是,在将词嵌入输入自注意力机制时,同时传递一些额外的信息,让模型了解句子中单词的顺序。
以下是设计位置编码时需要考虑的几个关键点:
-
不能使用离散值:像1, 2, 3这样的离散数字不利于神经网络训练,模型难以像人类一样解释它们。
-
数值需要有界:如果句子长度增加到100或1000个单词,位置值也会随之增大。引入如此大的值会扭曲训练焦点,导致梯度不稳定。
-
需要连续且规律的值:我们需要的是一种能生成连续值、且所有值都在特定范围内的函数。
我们最终的目标是向模型提供一个特定的模式,使其能够理解两个单词是彼此接近还是相距甚远。
正弦函数:一个起点 📈
正弦函数是连续的、周期性的,其值在-1到1的特定范围内。这似乎符合我们的部分要求。
我们可以尝试用 sin(1) 表示第一个位置,sin(2) 表示第二个位置,依此类推。这解决了离散性和无界性的问题。
但正弦函数存在一个明显问题:周期性。在足够长的序列中,不同位置可能产生相同的正弦值,导致两个位置编码相同,从而混淆模型。
正弦-余弦对:增强唯一性 🔄
为了降低两个位置编码完全相同的概率,我们可以使用向量而非标量来表示位置。
一个简单的想法是使用一个正弦-余弦对。这样,第一个位置由向量 [sin(1), cos(1)] 编码,第二个位置由 [sin(2), cos(2)] 编码。
使用正弦-余弦对降低了向量完全相同的可能性,但由于模式本身具有周期性,在足够长的句子中,向量仍有可能再次变得相同。
多频率正弦-余弦对:最终的解决方案 🏗️
为了进一步降低重复概率,我们可以使用更多维度的向量,即使用多对具有不同频率的正弦和余弦函数。
例如,我们可以使用一个四维向量,其中两对正弦-余弦使用不同的频率(如第一对频率为ω,第二对频率为ω/2)。这样,在向量再次相同之前,我们可以处理更长的位置范围。
这正是原始Transformer论文作者所做的。他们使用了512维的位置编码向量,这由256对正弦和余弦函数生成。每一对都有递减的频率(例如,第一对频率为ω,第二对频率为ω/2,第三对频率为ω/3,等等)。
通过使用这种512维、具有正弦-余弦对的位置编码,我们可以处理极长的句子(超过数千万单词),并保证所有值连续、有界,且能唯一标识位置。
以下是Transformer论文中使用的精确公式:
对于位置 pos 和维度索引 i:
-
在偶数维度(
2i):PE(pos, 2i) = sin(pos / 10000^(2i/d_model)) -
在奇数维度(
2i+1):PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
其中 d_model 是位置编码的维度(例如512)。i 是遍历向量维度的索引。
模型如何理解相对位置? 🧩
模型如何仅凭正弦和余弦值来识别单词的位置呢?关键在于我们为模型提供了一个特定且可预测的模式。
如果我们为100个位置生成128维的位置编码并绘制热图,会发现它遵循一个可预测的一致模式。颜色梯度随着位置下移而平滑变化。
观察任意两个位置编码向量:
-
如果两个位置相近,它们向量中的大多数值几乎相似,只有最初几个维度的值发生变化。
-
如果两个位置相距较远,那么即使在较高维度,你也会开始注意到差异。
这种模式如此可预测,以至于如果知道位置 p 的编码,我们就有能力预测位置 p+k 的编码。对于任何偏移量 k,都存在一个变换矩阵 T(k),使得:
PE(pos + k) = T(k) * PE(pos)
这意味着模型在训练过程中将学会识别这种可预测的模式。我们并没有明确告诉模型计算两个位置向量之间的差异,但由于这种特定的可预测性,模型将能够隐式地理解位置之间的偏移量 k,从而理解单词的相对顺序。
位置编码如何与词嵌入结合? ➕
现在,我们知道了如何生成位置编码。接下来需要将其与词嵌入结合,作为自注意力机制的输入。
一种直观的方法是拼接:将一个512维的词嵌入向量和一个512维的位置编码向量拼接起来,形成一个1024维的输入向量。
但这种方法存在一个缺点:它会显著增加模型参数。输入维度翻倍意味着查询、键、值权重矩阵(W_Q, W_K, W_V)的尺寸也要翻倍。考虑到Transformer使用多头注意力和多个层,参数总量将大幅增加,从而减慢训练和预测速度。
因此,原始Transformer论文采用了一种更巧妙的方法:元素级加法。将512维的词嵌入向量与512维的位置编码向量直接相加,得到一个512维的结果向量。
你可能会问:将两个向量相加,难道不会相互干扰并扭曲它们所携带的信息吗?位置编码不会扭曲词嵌入的语义含义吗?反之亦然?
答案是:不会。位置编码的设计使其不会干扰词嵌入的语义含义。因为位置编码是由不同频率的正弦曲线生成的,它具有一种与词嵌入截然不同的特定模式。
实验证明,在将位置编码添加到词嵌入之后,语义结构仍然保持完整。含义相似的单词在向量空间中仍然聚集在一起。同时,位置编码的内在模式也得以保留。因此,模型能够分别识别词嵌入的语义含义和单词的位置顺序。
通过使用元素级加法,Transformer避免了因输入尺寸增大而带来的计算开销,显著减少了训练负担,且没有牺牲模型的预测能力。
总结 📚
本节课中我们一起学习了Transformer中位置编码的核心概念。我们了解到:
-
由于自注意力机制的并行性,模型需要额外信息来理解单词顺序。
-
位置编码通过一组具有递减频率的正弦和余弦函数生成,创造了连续、有界且唯一的位置表示。
-
这种正弦曲线模式具有可预测性,使模型能够学会理解单词之间的相对位置。
-
位置编码通过元素级加法与词嵌入结合,这种方式既高效又能保持两者信息的独立性。
理解这些细节有助于我们深入认识Transformer架构为何如此强大和高效。在接下来的课程中,我们将继续探索Transformer的其他方面。
008:层归一化 (Layer Normalization) 的最简解释
在本节课中,我们将学习Transformer架构中的一个关键组件:层归一化。我们将探讨为什么在训练深度神经网络时需要归一化,理解“内部协变量偏移”问题,并详细解释层归一化的工作原理及其在Transformer中的具体应用方式。
为什么需要归一化?🤔
上一节我们提到了训练深度神经网络的挑战。本节中,我们来看看为什么数据预处理中的归一化至关重要。
训练一个深度神经网络,特别是Transformer,是具有挑战性的。它涉及数十亿甚至数万亿的参数。参数如此之多,梯度消失或爆炸等问题很常见。层归一化有助于我们稳定模型的训练。
归一化能提高训练速度和稳定性。以下是一个例子来说明原因。
想象我们有一个包含两列的数据集:一列是房屋的平方英尺面积,另一列是房间数量。使用这两个特征,我们想预测房屋价格。但这里存在一个问题:这两列数据的数值尺度差异巨大。房屋面积可能以千计,而房间数量范围是1到10。这种数据值尺度或范围存在巨大差异的情况,被称为未归一化数据。
当我们把这种未归一化数据输入模型时,损失函数的等高线会变得细长。这会产生严重的影响。对于细长的损失函数等高线,我们不能使用较高的学习率,否则会“超调”,导致训练无法进行。因此,为了稳定训练,我们不得不使用较小的学习率。但较小的学习率意味着需要更多时间收敛到最小值,训练会变慢。除此之外,数据还具有高方差,我们已经讨论过高方差如何导致梯度爆炸或消失,这是训练任何深度学习模型时的严重问题。
这就是归一化发挥作用的地方。
什么是归一化?📊
那么,究竟什么是归一化呢?归一化意味着以某种方式缩放数据,使其均值变为0,标准差变为1。
如果最初数据是这样不对称分布的,那么在归一化之后,它将围绕均值(即0)中心化。因此,归一化将数据排列成围绕零对称的分布。
我们如何用数学方法将这种数据转换成那种数据呢?这非常简单。我们所做的就是用每个数据点减去其均值,再除以标准差。
所以,如果这是我的数据,我将计算其均值和标准差,然后用每个数据点减去均值,再除以标准差,这将把数据转换成这种形式。
以我们的房屋面积列为例,我计算了它的均值(结果是1710)和标准差(结果是605)。为了归一化这一列,我们必须用每个值减去均值并除以标准差。应用这个归一化操作后,你可以看到大多数值都围绕0中心化。
一旦我们归一化了数据,损失函数的等高线就变成了圆形。有了这种圆形等高线,我们甚至可以使用稍高的学习率,这将确保我们的训练更快。
因此,归一化通过避免梯度消失或爆炸来帮助我们稳定模型训练,并且还能使模型更快收敛。
内部协变量偏移 🔄
但是,在一个非常深的神经网络中,仅仅归一化输入是不够的。因为这类深度网络会经历一种称为内部协变量偏移的现象。
现在,这是什么意思?让我们一步步分解。
术语“协变量偏移”指的是训练和测试之间数据分布的差异。例如,假设你训练一个模型来识别红玫瑰。模型完美地学会了识别红玫瑰。你给它看任何红玫瑰的图片,它都能以高置信度识别出来。但如果你在测试时展示一张白玫瑰的图片会发生什么?这会完全迷惑模型,因为输入分布与它在训练期间看到的不同。
在深度神经网络中,类似的数据分布偏移在训练期间于这些中间层中持续发生。在这样的网络中,后续层的输入是前一层产生的输出。在训练模型时,这些权重在反向传播操作中不断变化,这反过来又改变了每一层产生的输出。因此,在这一轮迭代中馈送到该层的数据将与它之前看到的非常不同。这就好比这些层在每次迭代中看到的数据都大不相同。因为这种数据分布在深度神经网络的中间层中持续变化,所以被称为内部协变量偏移。
这就像学习一门新语言。想象一下,如果你的老师每天教你完全不同的语法规则,这难道不会让人不知所措吗?你还没有巩固之前学过的知识,现在又被大量的新信息轰炸。你真正需要的是分布的渐进变化,以使训练过程更平滑。
所以,即使你已经对网络的输入进行了归一化,但由于它已经被处理了这么多次,这些层产生的输出不会是归一化的。因此,我们也需要对这些后续的中间层进行归一化。这就是层归一化的相关性所在。
如何归一化中间层?🧠
那么,显而易见的问题是:我们如何归一化这些中间层?我们已经看到了如何归一化输入数据。但是,这个归一化过程如何应用到这些中间层呢?让我们来看一下。
我展示了一个深度神经网络中某一层的简化图像。想象它是其中一个中间层,这些是与前一层连接的权重。这一层将产生一些输出。我们希望在训练期间归一化这一层产生的输出。
模型的输入是一批数据,所以假设我们的批量大小为3个数据点,并且这一层有5个神经元。总共有 3 x 5 个数据值。这是一批数据。这是第一个数据点产生的激活值,这是第二个数据点产生的相同激活或神经元的值,依此类推。
现在,我们想做的是分别归一化每个神经元的激活值。这意味着我想分别归一化这个激活值、这个激活值,以及这个激活值。
所以,对于这三个数据点(对应第一个神经元),我将计算它的 μ1 和 σ1。对于这三个数据点(对应第二个神经元),我将计算 μ2 和 σ2,依此类推。
现在,为了归一化这个值,我将用每个数据点减去其对应的均值,再除以其标准差。
所以,这个数据点可以通过减去 μ4 并除以 σ4 来归一化。同样地,这个数据点可以通过减去 μ5 并除以 σ5 来归一化。
因此,我们是按批次、为每个激活值单独进行归一化的。这就是归一化应用于内部层的方式。
缩放与偏移:可学习的参数 ⚖️
但是,我们并不止步于仅仅归一化。我们还会执行另一个操作,这个操作称为缩放和偏移。
假设 Z 是我的初始数据点。我执行了归一化过程,得到了 Z_norm。我单独处理这个 Z_norm。我所做的是将一个 gamma 参数乘以 Z_norm,再加上一个 beta 参数。
这些 gamma 和 beta 是可学习的参数。它们的值在模型训练期间进行调整,就像我们调整权重和偏置的值一样。
所以,我们不仅仅是像这样归一化数据,我们还执行了一个缩放和偏移操作,其中我们将归一化后的值与可学习参数 gamma 和 beta 相乘和相加。
我知道你可能会想:为什么要执行这个缩放和偏移操作?它难道不会抵消归一化过程吗?因为在归一化中,你是用某个值减去,再用另一个值除;而在缩放和偏移中,你是用某个值乘,再加上另一个值。所以这个操作与你归一化所做的完全相反。如果在训练模型后,你的 gamma 结果等于你的 sigma,而你的 beta 结果等于 mu,那么会发生什么?你最终会得到与开始时完全相同的值。所以这个缩放和偏移看起来像是归一化的逆过程。
那么,我们为什么这样做呢?答案是灵活性。有时,某些层并不需要数据完全归一化,它们在数据稍微未归一化时表现更好。所以我们让模型自己决定什么对它最有利。
这些参数 gamma 和 beta 在训练模型之前被初始化为 1 和 0。在模型训练期间,它们的值在反向传播操作中调整,就像权重和偏置的值被调整一样,模型将自行决定它需要多少归一化。
因此,归一化稳定了训练,但缩放和偏移让模型学习最优的变换。
批归一化 vs. 层归一化 ⚔️
我刚刚强调的这个归一化和缩放偏移的两阶段过程被称为批归一化。因为我们是跨批次、为每个激活值单独进行归一化的。
等等,我说对了吗?这是关于层归一化的视频,不是批归一化。那我为什么要介绍批归一化呢?这是因为批归一化和层归一化之间有一点区别。
批归一化的过程在深度神经网络中非常常见,但它不能应用于Transformer。用于NLP任务的Transformer需要使用层归一化。
一个显而易见的问题仍然存在:这种归一化将如何应用于Transformer?到目前为止,我们只看了简单的神经网络,但这将如何应用于Transformer呢?
让我们看看Transformer中的层是如何组成的。Transformer层由自注意力和其他一些块组成。我们想要归一化这些层产生的数据。
我们知道自注意力是做什么的:它接收输入词嵌入,并为每个词创建一个新的词表示。如果我的嵌入向量是512维的,那么它也会为该词产生另一个512维的输出,这是该词的新表示。
在训练模型时,它以批次接收数据。假设这里的批量大小是2,每个批次我们传递两个句子。每个句子的最大长度是3个词,每个词是一个512维的词嵌入。
所以我有一个大小为 2 x 3 x 512 的输入矩阵。注意,对于较短的句子,我用零填充它们,以匹配最大句子长度。
当我将这个输入传递给自注意力块时,它将为每个词生成新的词表示,并生成另一个 2 x 3 x 512 维的矩阵。这些填充将不受影响,因为任何数乘以0都将保持为0。
我们的目标是归一化这个 2 x 3 x 512 维的矩阵。我们该怎么做呢?
让我尝试对此应用批归一化。批归一化是跨批次、针对特定特征应用的。为此,让我将两个句子堆叠在一起,这样我的矩阵就变成了 6 x 512 维。
所以我有512维的特征。如果我想应用批归一化,那么我将针对一个特定特征跨批次应用它。这意味着我将计算它的 μ1 和 σ1,然后执行归一化操作。对于最后一个特征,我将计算 μ512 和 σ512,我可以简单地像这样执行归一化。
但是,等等,这里有一个问题。你能识别出那个问题吗?问题是这个数据包含零填充,而这些零将成为这个归一化过程的一部分,并会影响我们的 μ 和 σ 值。实际上,我只想归一化这四个有效数据点,但在归一化过程中,如果我也包含这些零,那么它会影响我的 μ 和 σ。你认为,如果我们在归一化过程中保留这些零填充,归一化后的值会是数据的真实表示吗?不,不会,因为这些零会扭曲结果。所以我们需要将这些零从归一化中排除。这就是为什么批归一化不能应用于Transformer。
Transformer中的层归一化 🏗️
那么,如果不是跨批次归一化,而是跨特定数据点归一化呢?这正是层归一化所做的。
与批归一化不同,层归一化在每个数据点上独立操作。所以 μ 和 σ 的值将针对一个特定数据点计算,但我的 gamma 和 beta 参数将针对特定特征添加。
因此,归一化是沿着这个方向(特征维度),但缩放和偏移是沿着这个方向(批次和序列维度)。
所以,如果我要向你展示对这个点(例如,第二个句子的第一个词的第三个特征)的归一化,它将通过减去 μ1、除以 σ1、乘以 gamma3 并加上 beta3 来获得。
如果你查看原始的《Attention Is All You Need》论文,你会注意到这种层归一化被应用于Transformer中的每一层之后。
在现实世界的应用和像GPT这样的模型中,我们堆叠了多个Transformer块,这使得整个架构极其深。这使得归一化对于训练的稳定性至关重要。
即使归一化是一个简单的概念,它也极其重要。没有它,训练将不会有效,并且将是时间和金钱的简单浪费。
总结 📝
本节课中,我们一起学习了层归一化在Transformer架构中的核心作用。
-
归一化的必要性:处理未归一化数据可以稳定训练,避免梯度问题,并加速收敛。
-
内部协变量偏移:深度网络中间层输入分布持续变化,需要持续归一化。
-
层归一化原理:对单个样本的所有特征计算均值μ和标准差σ,进行归一化,再通过可学习的参数γ和β进行缩放和偏移,公式为:
输出 = γ * ((输入 - μ) / σ) + β。 -
与批归一化的区别:批归一化跨批次归一化同一特征,受填充零影响,不适用于Transformer。层归一化跨单个样本的所有特征归一化,不受批次内其他样本影响,是Transformer的首选。
-
在Transformer中的应用:层归一化被应用于每个子层(如自注意力层、前馈网络层)的输出之后,为深层堆叠的Transformer块提供了稳定的训练环境。
归一化是确保现代大型深度学习模型能够被成功训练的基础技术之一。
009:残差连接如何稳定训练 🧠
在本节课中,我们将要学习Transformer架构中的一个关键概念——残差连接。我们将探讨深度神经网络训练中的难题,以及残差连接如何通过提供信息流动的“捷径”来解决这些问题,从而稳定和加速深度模型的训练。
深度网络训练的挑战
像Transformer这样的深度神经网络训练起来非常困难。它们常常遭受梯度消失或梯度爆炸问题的困扰,这使得训练极具挑战性。
在上一节视频中,我们学习了层归一化,它有助于稳定模型的训练。本节中,我们来讨论另一个强大的概念——残差连接。
为什么训练深度网络如此困难?
想象一个未经训练的、权重随机初始化的深度神经网络。当你将一个输入通过网络传递时,它会在每一层与这些随机权重相乘。这个过程会逐渐扭曲输入,导致信息丢失。
对于最初的几层,这种丢失可能微不足道。然而,随着输入在多个层中传播,每一次变换都会造成额外的损失。当信号到达输出层时,几乎所有相关信息都已丢失,只剩下噪声。
在反向传播过程中,模型会根据它在输出预测中看到的情况来尝试更新权重。由于输出预测只是噪声,后面几层的权重更新将不会有效。不仅如此,前面几层的权重更新也不会有效,因为在反向传播期间梯度向后流动时,它们会经过许多层而呈指数级缩小。
这些梯度的值很小且小于1。为了更新初始层的权重,所有后续层的梯度将被相乘。当你将许多小数相乘时,结果会趋近于0。这种现象被称为梯度消失问题。
这就是为什么在训练一个非常深的网络时,我们会在很长一段训练时间内几乎看不到损失函数的下降。甚至有可能我们的模型永远无法收敛。如果我们无法训练它,那么我们创建的深度复杂模型实际上就变得毫无用处。
解决方案:残差连接
那么,我们该如何应对?解决方案比想象的要简单。
我们的目标是为网络中的所有层提供有意义的输入信息,无论网络有多深。问题在于,后面的层完全依赖前面的层来提供输入信息,而信息由于与许多随机初始化的权重矩阵相乘而丢失。因此,这些后面的层无法获得相关的输入信息,并且在反向传播中,由于梯度消失问题,权重更新也不够有效。
为了解决这个问题,我们能否为信息流动设计一条更好的路径呢?与其只有图中黄色高亮显示的这条长路径,我们能否为所有这些层提供一条“捷径”路径,让它们能更好地访问输入信息?
这正是残差连接所做的事情。残差连接不是只有单一的信息流动路径,而是增加了另一条“捷径”路径,将输入传递给后面的层。
现在,输入信息将在两条路径中流动:一条是图中黄色高亮显示的较长路径,另一条是蓝色高亮显示的较短路径。
残差连接的工作原理
在残差连接中,我们将网络划分为多个块。每个块的输入是两个向量的和:一个向量是前一个块的输入,另一个向量是前一个块的输出。
这意味着,我们不是直接将这一层的输出传递给下一层,而是将该输出与其输入进行逐元素相加,然后将结果传递给下一个块。
因此,现在网络在块内部有更好的信息流动(输入将通过黄色高亮显示的常规路径流动),但在块外部,输入将通过蓝色高亮显示的较短路径传播。这些较短的路径被称为跳跃连接。
这种设计确保了输入数据能够适当地通过网络的所有层。这就像为训练过程提供了一个“启动器”。
想象一下你去健身房训练自己。如果你从第一天就开始举极重的重量,你能举起来并取得进步吗?不能,你可能需要他人的辅助。训练深度网络也是如此。与其要求模型从一开始就承担所有繁重的工作,我们通过提供更好的输入信息访问途径来辅助它。这确保了训练从最初的几个周期就开始逐步进行,我们应该会立即看到损失函数稳步下降。
残差连接的优势
但这还不是全部。残差连接还提供了其他一些优势。
优势一:更好的梯度流动
通过残差连接,我们不仅在正向传播中有了更好的信息流,在反向传播中也获得了更好的梯度流动。梯度现在有一条更短的路径到达早期层,权重更新也将通过两条路径发生:一条通过较长的黄色路径,另一条通过较短的蓝色路径。
在模型训练的初始阶段,我们看到较长的路径会因梯度微弱和梯度消失问题而挣扎。但现在,由于我们有了短路径,这将确保有意义的更新从一开始就发生。这应该会带来即时且稳定的损失下降。随着训练的进行,当长路径变得有效时,损失将继续下降,使模型能够超越浅层网络。
优势二:减轻每层负担
由于网络的每个块都能访问几乎未改变的输入,这些层的负担减轻了。它们不再需要独自找出输入的每一个重要细节。相反,这些层可以专注于学习残差信息——它们可以专注于在输入之上添加什么额外信息,以增强信息流,从而获得更好的预测。这使得它们的工作变得更容易,因为它们现在有更简单的信息需要学习,从而实现了深度网络相对更快的训练。
优势三:支持构建更深网络
由于我们有了信息流动的捷径路径,以及将信息传递到非常深层的方法,我们可以增加网络的深度。我们将有可能训练更深的网络。这就是为什么现代架构可以远超传统的浅层网络,使其能够稳健地学习极其复杂的信息。
残差连接的设计考量
使用残差连接唯一需要注意的是,它对网络设计施加了一些限制。由于我们执行的是逐元素加法,我们必须确保该层的维度与输入的维度相同。因为如果你想对两个向量进行逐元素相加,它们的大小必须相同。这就是为什么网络中的某些层必须具有与输入相同的形状。
如果你不想执行逐元素加法,也可以在这两者之间执行连接操作。通过连接,你可以拥有不同的维度。但请记住,每当你执行连接操作时,整体维度将急剧增加,与之相关的权重也会增加,这意味着参数数量会爆炸式增长。因此,通常执行的是逐元素加法,而不是连接。
尽管如此,残差连接是改进和稳定极深度网络训练的一个极佳方法。
残差连接在Transformer中的应用
我知道你心中可能还有一个问题:这很棒,我知道残差连接了,但这与Transformer有什么关系?我们如何将残差连接应用到Transformer中?
答案在于Transformer设计的精妙之处。Transformer网络的每一层产生的输出大小与其输入相同。例如,多头注意力块的输出与其输入相同:它接收每个词的512维向量,也为每个词生成512维向量。同样,前馈网络也保持这个维度。前馈网络的输入是多头注意力产生的输出,即每个词的512维向量。前馈网络也为每个词生成512维输出。无论你在整个架构中选择哪一层,这种设计都是一致的。
你在每一层之后看到的“Add”部分,就是残差连接。多头注意力的输入会与其输出相加。前馈网络的输入会与其输出相加。这个过程在所有层中持续进行。
我们将在下一个视频中详细讨论这个架构,研究编码器块和解码器块的整个设计,以及各层如何堆叠在一起。但我希望你理解了残差连接的重要性及其在Transformer中的用法。
在Transformer架构中添加这些残差连接确保了其在训练期间的稳定性。你可以看到编码器块被乘以N次,这意味着我们将堆叠许多编码器块,然后在上面堆叠许多解码器块。在像GPT这样的模型中,深度极其巨大,这就是为什么残差连接在模型训练中扮演着重要角色。
像残差连接和归一化这样微小的细节,显著影响了训练的稳定性和性能。它们可能在原始论文中没有受到太多关注,但它们使深度学习的突破成为可能。
现在,可以说你已经完全准备好学习我们将在下一个视频中介绍的编码器架构了。
总结
本节课中,我们一起学习了Transformer架构中的残差连接。我们首先探讨了深度神经网络训练中面临的梯度消失挑战。接着,我们介绍了残差连接的概念,它通过创建信息流动的“捷径”来解决这些问题。我们详细说明了残差连接的工作原理、它带来的三大优势(更好的梯度流动、减轻层负担、支持更深网络),以及其在设计上的考量。最后,我们明确了残差连接是Transformer架构稳定训练的关键组件,其逐元素相加的特性完美适配了Transformer各层输入输出维度一致的设计。
理解残差连接,是深入掌握现代深度神经网络,尤其是Transformer系列模型的重要一步。
010:编码器架构详解 🧠
在本节课中,我们将深入探讨Transformer模型的核心组成部分——编码器的完整架构。我们将从输入开始,逐步构建每一层,并解释其作用和重要性。
输入:词嵌入与位置编码
首先,我们从模型的输入开始。对于自然语言处理任务,输入通常是词嵌入。词嵌入是一种将词语表示为数字向量的有效方法,它能通过比较两个词嵌入来提供词语的上下文或含义。
然而,词嵌入存在一个问题:它们是静态的。这意味着同一个词在不同语境下(例如“light”在“light weight”和“light blue”中)使用的是相同的向量表示,这可能导致模型理解不准确。我们需要一种动态调整词嵌入值的方法,使其能根据上下文变化。
为了解决这个问题,我们引入了位置编码。位置编码是向量,它为多头注意力机制提供词语的位置信息。我们将词嵌入向量与位置编码向量进行逐元素相加,然后再输入到编码器中。这样,模型就能同时理解词语的含义和其在句子中的顺序。
公式表示:
输入 = 词嵌入 + 位置编码
核心机制:多头自注意力
上一节我们介绍了如何准备输入,本节中我们来看看编码器的核心——多头自注意力机制。自注意力是Transformer成功的关键,它允许模型根据输入上下文动态调整词语的表示。
自注意力以词嵌入(已加上位置编码)作为输入,通过观察句子中其他词语,为每个词生成新的、包含上下文信息的表示。它能捕捉词语之间的依赖关系。
但是,语言的复杂性使得单一的自注意力头难以捕捉所有关系。例如,在句子“Keys on the table belong to Sarah”中,单词“keys”需要同时捕捉与“on the table”的空间关系以及与“belong to Sarah”的所属关系。
因此,我们使用多头自注意力。这就像为你的电脑添加更多内存条,每个“头”专注于捕捉一种特定类型的关系(如空间、主语-宾语、时间等)。在原始论文中,使用了8个注意力头。
以下是多头自注意力的工作流程:
-
输入(例如,3个词,每个词512维)被复制并送入8个独立的注意力头。
-
每个头独立计算,输出一个新的词表示(例如,3个词,每个词64维)。
-
将8个头的输出在特征维度上拼接起来,得到一个3x512的矩阵。
-
将这个拼接后的矩阵通过一个线性变换层(权重矩阵
W),最终输出一个3x512的矩阵。
这个输出包含了丰富的、上下文感知的信息。此外,多头注意力机制可以并行处理所有词语,极大提升了训练速度。
稳定训练:Add & Norm 层
在多头注意力层之后,我们添加了一个“Add & Norm”层。这个层执行两个关键操作:“加”和“归一化”。
“加”指的是残差连接。它将多头注意力层的输入直接加到其输出上。这创建了一条“捷径”,让原始信息能更顺畅地流向网络深处,有助于缓解深度网络中的梯度消失问题。
“归一化”指的是层归一化。它对激活值进行标准化处理,确保数据在通过网络层时保持稳定分布,从而加速模型训练的收敛过程。
公式表示(简化):
输出 = LayerNorm(注意力层输出 + 注意力层输入)
这个“Add & Norm”层在编码器和解码器的每个主要子层后都会使用,是保证模型训练稳定的重要组件。
引入非线性:前馈神经网络
到目前为止,我们介绍的操作(线性变换、注意力计算)都是线性的。然而,仅使用线性操作会严重限制模型学习复杂模式的能力,并且多个线性层可以等效为一个线性层。
为了解决这个问题,编码器中引入了前馈神经网络层。这是一个简单的全连接网络,通常包含两个线性变换层和一个非线性激活函数(如ReLU)。
其工作流程如下:
-
输入(例如,3x512)首先通过第一个线性层,将维度扩展到更大的空间(例如,2048维)。这增加了模型的容量,使其能够学习更复杂的特征。
-
然后应用ReLU激活函数,引入非线性。
-
最后通过第二个线性层,将维度缩减回原始大小(512维),以匹配后续“Add & Norm”层的输入要求。
代码表示(概念):
output = linear_layer_2( relu( linear_layer_1(input) ) )
前馈网络的作用是:1) 引入非线性,使模型能够拟合复杂数据;2) 通过扩展维度增加模型的学习能力。
构建深度:堆叠编码器块
现在,我们已经了解了编码器的一个完整块:它由多头自注意力层、Add & Norm层、前馈网络层和另一个Add & Norm层顺序连接而成。
为了学习更深层次和更抽象的语言模式,原始Transformer论文将6个这样的编码器块堆叠起来。每个块的输出直接作为下一个块的输入。
关于堆叠编码器块,有几个关键点需要注意:
-
参数独立:每个编码器块中的权重参数都是独立且不同的,在训练过程中分别学习。
-
单次位置编码:位置编码只在第一个编码器块的输入处添加一次,后续块直接传递前一块的输出。
-
维度一致:得益于“Add & Norm”层,每个块的输入和输出维度始终保持一致(例如,序列长度 x 512)。
这种深度堆叠的结构类似于其他深度神经网络:浅层可能捕捉词语级别的简单关系,而深层则能构建更复杂的句子级语义和抽象表示。
总结与展望
本节课中,我们一起学习了Transformer编码器的完整架构。我们从词嵌入和位置编码开始,理解了如何为模型准备包含顺序信息的输入。然后,我们深入探讨了多头自注意力机制,它是模型理解上下文的核心。接着,我们介绍了Add & Norm层如何通过残差连接和层归一化来稳定训练。最后,我们解释了前馈神经网络的作用以及通过堆叠多个编码器块来构建深度模型的方法。
编码器的输出将被传递给解码器,用于生成最终的预测结果。在下一节课中,我们将探讨解码器的架构,特别是其中关键的掩码多头注意力机制。完成这两部分的学习后,你将能透彻理解Transformer的设计精髓。
更多推荐



所有评论(0)