用phpnow搭建网站的整个流程,帮企业建网站,wordpress可以上传文件吗,wordpress 分页不出来编者按#xff1a;Transformers 是人工智能领域近年来最引人瞩目的技术之一#xff0c;它为语言生成模型的发展做出了巨大的贡献。随着大语言模型#xff08;LLM#xff09;的兴起#xff0c;公众对其背后的技术原理也越来越感兴趣。但是由于Transformers本身具有一定的复… 编者按Transformers 是人工智能领域近年来最引人瞩目的技术之一它为语言生成模型的发展做出了巨大的贡献。随着大语言模型LLM的兴起公众对其背后的技术原理也越来越感兴趣。但是由于Transformers本身具有一定的复杂性想要真正理解其中的原理并不容易。 今天我们开始为大家带来了一系列以通俗易懂的语言解释Transformers的好文章。作者Chen Margalit希望通过本系列文章用最简单的语言把Transformers的关键要点讲清楚让不同机器学习掌握水平的读者都能受益。 本文是Transformers系列的第一篇。作者着重讲述 Transformers 的输入input部分。依次讲解了分词、词向量表示、位置编码等概念。这些看似简单的步骤却是构建Transformers的基石。在讲解的过程中作者多次使用通俗易懂的例子辅助解释同时辅以代码、公式和图示让读者在理解抽象概念的同时也能对实际操作过程有具体的认知。 相信通过阅读本系列文章不论是人工智能初学者还是有相关经验的从业人员都能对Transformers的运作原理有一个直观且全面的了解。 以下是译文enjoy 欢迎小伙伴们加入AI技术软件及技术交流群追踪前沿热点共探技术难题~
本文经原作者授权由Baihai IDP编译。如需转载译文请联系获取授权。
原文链接https://medium.com/towards-data-science/transformers-part-1-2a2755a2af0e ; https://medium.com/towards-data-science/transformers-part-2-input-2a8c3a141c7d
作者 | Chen Margalit
编译 | 岳扬
Transformers这种深度学习架构为人工智能的发展做出了杰出贡献。它在人工智能和整个技术领域都是一个重要的里程碑但它也有一些复杂之处。目前已经有了很多关于Transformers的好资源了为什么我还要再创作这个系列呢原因有二
我深谙自学之道而且根据我的经验阅读不同的人如何描述相同的观点能够大大加深我们对这些知识的理解。目前我很少在阅读一篇文章时认为这篇文章的解释已经足够简单。技术内容创作者往往总是将概念过度复杂化或没有充分的去解释。我们应该明白没有什么是rocket science甚至连rocket science都不是。只要对其的解释足够好我们可以理解任何东西。 在本系列中我将尝试对Transformers做出足够好的解释。
此外作为一个凭借博客文章和开源代码走上职业道路的人我认为我有义务回报社会。
该系列将尝试为那些几乎不了解人工智能的人和那些了解机器学习原理的人都提供合理的指导。我打算怎么实现这一目标呢首要的是要合理去解释Transformers。在我的职业生涯中我阅读了近1000篇技术文章就像这篇文章一样我阅读时所面临的主要问题是作者或许是潜意识地假设你已经了解了很多相关的知识。在本系列中我打算假设你阅读过的相关技术文章比我为创作这篇文章而阅读的Transformers文章数量还要少。
此外我将结合个人感觉和直观理解、数学公式、代码和可视化元素使这个系列就像一个candy store译者注用来形容某事提供了多种吸引人的选择就像一个糖果店一样糖果店里有各种各样形状、口味的糖果供人选择——适合各种人。考虑到这是一个复杂领域中的前沿概念我愿意冒险地让你认为“哇这文章的进展太慢了不要去解释这些显而易见的东西”。但同时读者产生这种想法的可能性就会大大降低“我不知道他到底在说什么”
01 Transformers值得你花时间去了解吗
有什么值得大惊小怪的它真的那么重要吗因为它是世界上一些最先进的人工智能驱动技术工具如 GPT 等的基础所以可能确实很重要。
尽管和历史上许多次科学突破一样一些相关的思想在之前已经有人提出但对这种架构的深入、完整的描述实际上是来自于 “Attention is all you need” 这篇论文该论文说Transformers是一种 “简单的神经网络架构”。 该架构图来自论文《Attention is all you need》[1]
如果你和大多数人相同就不会认为这是一种简单的神经网络架构。因此我的任务就是努力让你在读完这个系列后心里想这确实不简单但我已经明白了。
那么这张复杂的架构图到底是怎么回事
我们看到的这张图是一种深度学习架构这就意味着其中的每一个方框都对应着一些代码片段将这些代码片段组合在一起就能完成一些人们现在还不知道它们如何完成的事情。
Transformers可以应用于许多不同的应用场景但最著名的使用案例可能是automated chat自动化聊天——可以和它一起探讨各种各样的话题就像在和一个真人交谈一样它看起来也好像无所不知。在某种程度上类似于《黑客帝国》。
02 Inputs
龙从蛋中孵化婴儿从腹中诞生人工智能生成的文本从输入inputs开始。一切都必须从某个地方开始。
Transformers拥有什么样的输入取决于手头的任务。如果正在构建一个语言模型一个能够生成相关文本的软件Transformers架构在各种场景中都很有用那么输入inputs就是文本。然而计算机能够接受任何种类的输入文本、图像、声音并神奇地知道如何处理吗实际上不行。
我相信你认识一些不擅长言辞但擅长处理数字的人计算机类似于这样的人。它不能直接在 CPU/GPU进行计算的地方中处理文本但它绝对可以处理数字你很快就会发现如何将这些文字表示为数字是Transformers中的关键因素。 该图片来自 Vaswani, A. 等人论文《Attention Is All You Need》[2]
2.1 分词器
分词是将语料库所有文本转化为机器可以更好利用的较小部分的过程。 假设我们有一个包含 10,000 篇维基百科文章的数据集我们对每个字符进行处理分词。对文本进行分词的方法有很多让我们看看 OpenAI 的分词器[3]是如何处理以下文本的 “Many words map to one token, but some don’t: indivisible. Unicode characters like emojis may be split into many tokens containing the underlying bytes: Sequences of characters commonly found next to each other may be grouped together: 1234567890 这就是进行分词后的结果 图片由 OpenAI 提供取自此处[3]
如您所见有大约40个单词。在这40个单词中生成了64个词元token。有时词元是整个单词如“Many, words, map”有时是单词的一部分如“Unicode”。为什么要将整个单词分成较小的部分呢为什么要分离句子我们本可以让它们保持完整反正最后它们都会被转换成数字。所以在计算机看来词元token是 3 个字符长还是 30 个字符长又有什么区别呢
词元有助于模型进行学习因为文本就是我们的数据词元就是数据的特征。对这些特征进行不同的工程设计会导致性能的变化。例如在句子 “Get out!!!” 中我们需要判断多个“”是否与一个“”不同或者它是否具有相同的含义。从技术上讲我们可以将这些句子作为一个整体保留下来但试想一下观察一群人与单独观察每个人哪种情况下你能够获得更好的见解
现在我们有了词元token我们可以构建一个查询字典lookup dictionary这样就可以摒弃单词改用索引数字代替。例如如果我们整个数据集是一个句子 “Where is god”。我们可以建立这样一个词汇表它只是一个由单词和代表单词的单个数字组成的键值对。我们不必每次都使用整个单词我们可以使用数字。例如
{Where: 0, is: 1, god: 2}。每当我们遇到单词“is”我们将其替换为1。如果想了解更多关于分词器的内容你可以使用谷歌开发的分词器[4]或者尝试使用OpenAI的TikToken[5]进行更多实验。
2.2 Word to Vector
个人感觉和直观理解 Intuition
在将单词表示为数字的这个过程我们取得了重大进展。下一步将是根据这些词元tokens生成数值形式的语义表征numeric, semantic representations。 为此我们可以使用一种名为 Word2Vec[6] 的算法。该算法的具体细节目前还不是很重要但其主要思想是采用数字向量可以简单想象为一个普通的数组这个向量的大小可以任意设置这篇论文[1]的作者使用了512个数字而这个向量应当代表一个词的语义含义。想象一下像 [-2, 4,-3.7, 41…-0.98] 这样的数组实际上代表了一个单词的语义。如果我们将这些数字向量绘制在二维图上相似的词的位置会比不相似的词更加接近。
如图所示取自此处[7]Baby 与 Aww 和 Asleep 相近而 “Citizen”/“State”/America’s 也有几分相近。
*二维词向量也就是包含 2 个数字的数组甚至不能准确地表示一个词的含义如前所述作者使用了 512 个数字作为向量的大小。由于我们无法绘制 512 维中的任何内容因此我们使用一种叫做 PCA [8] 的方法将维度数量减少到 2希望还能保留大部分的原始含义。 在本系列的后续部分我们将深入探讨这种方法的原理。 Word2Vec 2D 演示——图片由 Piere Mergret 提供摘自此处[7]
这种方法确实有效你可以训练一个模型让其能够生成具有语义的数组。计算机不知道“baby”是一个会尖叫、让人失眠超级可爱的小人儿但它知道通常会在“aww”附近更频繁地看到“baby”这个词而不是“State”和“Government”这些词。我之后会再写一些文章解释其中的实现原理但在那之前如果你有兴趣这篇文章[7]也许还不错。
这些 “数组” 相当重要因此它们在专业的机器学习术语中也有属于自己的名字那就是“嵌入”Embeddings。为什么叫嵌入因为我们正在进行嵌入很有创意这是将术语从一种形式单词映射翻译到另一种形式数组的过程。这些括号内的内容也很重要。
从现在开始我们将把“单词”称为“嵌入”正如前面所解释的那样嵌入是一个数组用于保存任何它被要求表示的词的语义含义。
2.3 使用 Pytorch 创建嵌入
我们首先计算所拥有的唯一词元token的数量为简单起见假设为 2。嵌入层embeddings layer是 Transformer 架构的第一部分创建嵌入层就像编写以下代码一样简单
*备注——请不要将此段代码及其约定视为好的编码风格写这段代码的目的是为了便于理解。
代码 Code
import torch.nn as nnvocabulary_size 2
num_dimensions_per_word 2embds nn.Embedding(vocabulary_size, num_dimensions_per_word)print(embds.weight)
---------------------
output:
Parameter containing:
tensor([[-1.5218, -2.5683],[-0.6769, -0.7848]], requires_gradTrue)现在我们已经有了一个嵌入矩阵在本例中它是一个 2 乘 2 的矩阵是由来自正态分布 N(0,1) 的随机数生成例如均值为 0、方差为 1 的分布。
注意 requirements_gradTrue这是 Pytorch 中表示这 4 个数字是可学习权重的方式。它们可以并将在学习过程中进行自定义以更好地表示模型接收到的数据。
在一个更实际的场景中我们可以期望得到一个接近10,000x512的矩阵用数字形式表示了整个数据集。
vocabulary_size 10_000
num_dimensions_per_word 512embds nn.Embedding(vocabulary_size, num_dimensions_per_word)print(embds)
---------------------
output:
Embedding(10000, 512)*有一个有趣的事实当然我们也许还能想到更有趣的事有时你可能会听说某个语言模型拥有数十亿个参数。而上面这个初始的、看起来不太疯狂的层就已经拥有 10,000 乘以 512 的参数量也就是 500 万个参数。而 LLM 大语言模型是一种相当复杂的东西需要进行大量计算。
“参数”是一个比较 fancy 译者注在这里“fancy” 可能表示参数有一定的价值。的词用来指代那些数字比如-1.525等只不过它们是可以改变的而且在训练过程中会发生变化。
这些数字就是机器正在学习的东西。之后给机器学习模型输入信息时就会将输入信息与这些数字相乘然后祈祷能输出一个好的计算结果。 你看这些数字是不是非常重要。因为你对你父母来说很重要所以你会拥有自己的名字所以这些不是普通的数字它们也有自己的名字参数。
为什么上面的Embedding(10000, 512)这行代码使用 512 而不是 5 因为更大的数字意味着我们可以生成更准确的含义。太好了那么使用 100万 如何为什么不呢因为更大的数字意味着要进行更多的计算、需要更多的计算资源、更昂贵的训练成本等等。经过长期的实验人们发现 512 是个合适的中间值。
2.4 序列长度 Sequence Length
在训练模型时我们会把大量单词组合在一起。这样做的计算效率更高而且有助于模型在获得更多上下文时进行学习。如前文所述每个单词都将由一个 512 维的向量包含 512 个数字的列表来表示而且每次将输入传递给模型又称前向传递forward pass时我们会向模型发送一堆句子而不仅仅是一句。举个例子如果我们决定生成一个单词数量为50的序列。意味着我们需要在一个句子中提取 x 个单词如果 x 50我们就将其拆分只提取前 50 个单词如果 x 50我们仍然需要使得句子的长度为50个单词下面很快就会解释原因。为了解决这个问题我们需要在句子中添加 padding这是一种特殊的虚拟字符串。例如如果我们需要一个 由 7 个单词组成的句子并且现在这个句子为 “where is god”。就需要添加 4 个padding因此输入到模型的内容将变成“Where is god PAD PAD PAD PAD ”。实际上通常我们会添加至少2个额外的特殊 padding这样模型就能知道句子的开始和结束位置所以这个句子会变成类似于 “ StartOfSentence Where is god PAD PAD EndOfSentence ”的样子。
为什么所有的输入向量都必须拥有相同的大小因为软件有 “expectations” 而矩阵的 “expectations” 更为严格译者注“expectations”指对输入数据或操作有特定的期望要求输入数据满足特定的某种规则或条件。我们不能随心所欲地进行“数学”计算必须要遵循某些规则其中规则之一就是需要有足够的向量大小。
2.5 位置编码 Positional encodings
个人感觉和直观理解 Intuition
如前文所述现在有了一种好方法来表示和学习vocabulary中的单词译者注vocabulary指代一个特定数据集或语料库中出现的不同单词的集合。我们可以对单词的位置进行编码进一步改进这个方法。为什么说这个步骤很重要呢看看下面这个例子 The man played with my cat The cat played with my man
我们可以使用完全相同的嵌入来表示这两个句子但这两个句子的含义却不同。我们可能会认为这些数据的顺序并不重要。因为如果我们要计算某些东西的总和顺序可能并不重要。但是在语言中顺序通常很重要。这些嵌入虽然包含单词的语义但没有确切的顺序含义。这些嵌入确实在某种程度上保持了原有的顺序因为这些嵌入最初是根据某种语言逻辑创建的“baby”出现更接近“sleep”而不是“state”但同一个词可以有多个含义更重要的是当它处于不同的语境中时会有不同的含义。
将这些单词转化为没有顺序的文本是不够的但是我们可以改进这一点。我们可以在嵌入中加入位置编码。 具体做法是为每个单词计算一个位置向量position vector然后将两个向量相加求和。位置编码向量positional encoding vectors的大小size必须相同这样才能相加。位置编码通过两个函数来实现偶数位置如第 0 个单词、第 2 个单词、第 4 个单词、第 6 个单词等用正弦函数计算奇数位置如第 1 个单词、第 3 个单词、第 5 个单词等用余弦函数计算。
可视化元素 Visualization
通过观察这些函数红色表示正弦函数蓝色表示余弦函数你也许可以想象为什么要特别选择这两个函数。这两个函数之间存在一定的对称性就像一个单词与它前面的单词之间存在对称性一样这种方法有助于模拟表示这些相关的位置。此外它们的输出值在 -1 到 1 之间数字大小非常稳定不会变得超大或超小。 公式及图片来自 Vaswani, A. 等人的论文[2]
在上述公式中第一行公式表示从 0 开始的偶数i 0并接着表示偶数21、22、2*3。第二行以同样的方式表示奇数。
每个位置向量都是一个数值从 0 到 1 的number_of_dimensions本例中为 512维的向量。
Code
from math import sin, cos
max_seq_len 50
number_of_model_dimensions 512positions_vector np.zeros((max_seq_len, number_of_model_dimensions))for position in range(max_seq_len):for index in range(number_of_model_dimensions//2):theta position / (10000 ** ((2*index)/number_of_model_dimensions))positions_vector[position, 2*index ] sin(theta)positions_vector[position, 2*index 1] cos(theta)print(positions_vector)
---------------------
output:
(50, 512)如果我们打印第一个单词我们可以看到我们只得到0和1这两个值它们是可以互换的译者注根据前文所述译者猜测此处为填充的padding——StartOfSentence
print(positions_vector[0][:10])
---------------------
output:
array([0., 1., 0., 1., 0., 1., 0., 1., 0., 1.])第二个单词中的数字就已经更加多样化了。
print(positions_vector[1][:10])
---------------------
output:
array([0.84147098, 0.54030231, 0.82185619, 0.56969501, 0.8019618 ,0.59737533, 0.78188711, 0.62342004, 0.76172041, 0.64790587])*代码灵感来自这里[9]。
我们已经看到不同的位置会产生不同的表征representations。为了完成整个输入部分下图中红色的正方形我们将位置矩阵position matrix中的数字添加到输入的嵌入矩阵input embeddings matrix中。最终我们会得到一个与嵌入相同大小的矩阵只是这次的这些数字包含了语义顺序。 图片来自 Vaswani, A. 等人的论文[2]
2.6 总结 Summary
本小节讨论了 Transformer 的input部分用红色矩形标记介绍了模型如何获取输入。我们学习了如何将文本分解成特征词元将它们表示为一些数字嵌入以及了解了一种聪明的方式将位置编码添加到这些数字中。
下一小节将重点讨论编码器块的不同机制第一个灰色矩形其中的每个部分都是不同颜色的矩形例如多头注意力机制Multi head attention、Add Norm译者注“Add” 指的是将不同层的输出相加而 “Norm” 指的是归一化主要目标是解决深度神经网络中的梯度消失和梯度爆炸问题等。
END
欢迎小伙伴们加入AI技术软件及技术交流群追踪前沿热点共探技术难题~
参考资料
[1]https://arxiv.org/pdf/1706.03762.pdf
[2]https://proceedings.neurips.cc/paper_files/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf
[3]https://platform.openai.com/tokenizer
[4]https://github.com/google/sentencepiece
[5]https://github.com/openai/tiktoken
[6]https://arxiv.org/abs/1301.3781
[7]https://www.kaggle.com/code/pierremegret/gensim-word2vec-tutorial
[8]https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html
[9]https://machinelearningmastery.com/a-gentle-introduction-to-positional-encoding-in-transformer-models-part-1/