全文约1.7w字,分为上中下篇,本文(上篇)建议阅读8分钟
本文通过通俗易懂的初中数学知识来辅助理解大语言模型的工作机制。
一个关于大语言模型内部工作机制独立的,完整的解释。
在本文中,我们从零开始讨论了大语言模型(LLM)如何工作——假定你只知道两个数的加法和乘法。我们首先用笔和纸构建一个简单的生成式AI,然后通过我们对现代大语言模型和Transformer架构的理解来完成一切。本文将去掉机器学习中所有花哨的语言和行话,并简单地表示一切:数字。
从加法/乘法到今天最先进的人工智能模型,不假设其他知识或参考其他来源,这意味着我们覆盖了很多领域。这不是一个小型大语言模型的解释——理论上可以从这里的所有信息重建一个现代大语言模型。
我们有哪些内容?
15.Transformer体系结构
让我们开始。
首先要注意的是,神经网络只能接受数字作为输入,也只能输出数字,没有例外。问题在于弄清楚如何将输入转化为数字,并以实现目标的方式解释输出的数字。最后,构建神经网络,它将接受你提供的输入,并给出你想要的输出(给定你为这些输出选择的解释)。让我们来看看如何从数字的加法和乘法到像Llama 3.1(https://ai.meta.com/blog/meta-llama-3-1/)这样的东西。
1. 一个简单的神经网络
让我们通过一个简单的神经网络来对物体进行分类:
下面是一片叶子和向日葵的数据:
图片来自作者
现在让我们建立一个神经网络来做这个分类。我们需要决定输入/输出解释。我们的输入已经是数字了,所以我们可以把它们直接输入网络。我们的输出是两个物体,叶子和花,这是神经网络不能输出的。让我们看看我们可以在这里使用的几个方案:
这两种方案都允许网络输出可以解释为叶子或花的数字。我们在这里选择第二种方案,因为它可以很好地推广到我们稍后会看到的其他东西。这是一个神经网络,它使用这个方案进行分类。让我们来算一下:
图片来自作者
一些行话:
要计算这个网络的预测/输出(称为“向前传递”),从左边开始。我们有输入层神经元的数据。为了“向前”移动到下一层,将圆圈中的数字与相应神经元配对的权重相乘,然后将它们全部加起来。我们在上面演示了蓝色和橙色的圆。运行整个网络,我们看到输出层的第一个数字更大,因此我们将其解释为“网络将这些(RGB,Vol)值分类为叶子”。一个训练有素的网络可以接受各种输入(RGB,Vol)并正确分类对象。
这个模型不知道叶子或花是什么,也不知道(RGB,Vol)是什么。它的任务是输入恰好4个数字,输出恰好2个数字。4个输入数字是(RGB,Vol),2个输出的数字,如果第一个数字更大就是一个叶子。最后,这也取决于我们选择正确的权重,这样模型就会接受我们的输入数字,并给我们正确的两个数字,并据此得到我们想要的解释。
一个有趣的副作用是你可以使用相同的网络但不输入(RGB,Vol),而是输入其他4个数字,例如云量、湿度等等,并解释两个数字为“单小时日照”或者“单小时雨量”,如果你有准确测量的权重值,你可以获取完全相同的网络来同时做两件事——分类叶片/花朵并预测单小时雨量!网络仅给你两个数字,无论你将其解释为分类或者预测或者其他东西都可以。
剩下的东西简化(可以忽略,不必非要理解)
2.这些模型如何被训练?
在以上案例中,我们奇迹般地拥有那些能够让我们将数据输入并得到良好输出权重。但是这些权重是怎么来的呢?设置权重(或“参数”)的过程叫作“训练模型”,我们需要一些训练数据。
假设我们有一些输入数据,并且已经知道每个数据关联叶子或花,这就是我们的“训练数据”。每一套(R,G,B,Vol)数值都有叶子/花的标签,叫作“标签数据”。
以下是它的工作方式:
注意事项:
在实践中,训练深度网络是一个困难而复杂的过程,因为梯度很容易失控,在训练过程中趋于零或无穷大(称为“消失梯度”和“爆炸梯度”问题)。我们在这里讨论的损失的简单定义是完全有效的,但很少使用,因为存在更好的功能形式,可以很好地用于特定目的。对于包含数十亿参数的现代模型,训练模型需要大量的计算资源,这有其自身的问题(内存限制,并行化等)。
3. 如何生成语言?
记住,神经网络输入数值,给予训练参数一些数学运算,并给出其他一些数值。每件事都是关于解释和训练参数(例如,将其设置为一些数值)。如果我们可以将两个数值解释为“叶子/花”或者“单小时雨或日照”,我们也可以将其解释为“句子中的下一个词”。
但是英语中有超过2个字母,所以我们必须扩展输出层中的神经元数量,比如说英语中的26个字母(把一些像空格,句号的符号也丢进去)。每个神经元可以关联一个字符,我们观察(大约26个)输出层的神经元,并将关联最高数值的神经元作为输出字符。现在我们有一个可以输入输出字符的网络。
如果我们将网络中的输入替换为这些字符:“Humpty Dumpt”,并要求它输出一个字符,将其解释为“网络对我们刚刚输入的序列中下一个字符的建议”。我们可以很好地设置权重,让它输出“y”——从而完成“Humpty Dumpty”。但我们如何在网络中输入这些字符列表?我们的网络只接受数字!!
一个简单的解决办法就是,将数值赋给每个字符。我们让a=1,b=2以此类推。现在我们可以输入“humpty dumpt”并训练它给我们输出“y”。我们的网络看起来像这样:
图片来自作者
现在我们可以通过给网络提供字符列表来提前预测一个字符,可以用此来构建一个完整的句子。例如,一旦我们预测了“y”,我们可以将“y”附加到我们拥有的字符列表中,并将其提供给网络,并要求它预测下一个字符。如果训练有素,它应该反馈一个空格。到最后,我们应该能够递归生成“矮胖子(Humpty Dumpty)坐在墙上”。我们有生成式人工智能。此外,我们现在有了一个能够生成语言的网络!现在,没有人会真的输入随机分配的数字,我们将看到更明智的方案。如果等不及,请随意查看附录中的one-hot编码部分。
聪明的读者会注意到,我们实际上不能将“Humpty Dumpty”输入到网络中,因为图中的方式是,它在输入层只有12个神经元,每个神经元对应“Humpty Dumpty”中的每个字符(包括空格)。那么我们如何在下一次传递中输入y呢?把第13个神经元放在那里需要我们修改整个网络,这是不可行的。解决方案很简单,我们把“h”去掉,发送最近的12个字符。所以我们会发送“empty dumpty ”网络会预测一个空格。然后我们输入“empty dumpty”,它就会产生一个s,以此类推。它看起来是这样的:
图片来自作者
我们在最后一行丢掉了很多信息,只给模型提供了“坐在墙上”。那么,当今最新、最好的网络是做什么的呢?差不多就是这样。我们可以输入到网络中的输入长度是固定的(由输入层的大小决定)。这被称为“上下文长度”——提供给网络以做出未来预测的上下文。现代网络可以有非常大的上下文长度(几千个单词),这非常有用。有一些输入无限长度序列的方法,这些方法的性能虽然令人印象深刻,但已经被其他具有大(但固定)上下文长度的模型所超越。
细心的读者会注意到的另一件事是,对于相同的字母,我们对输入和输出有不同的解释!例如,当输入“h”时,我们只是用数字8表示它,但在输出层,我们并没有要求模型输出单个数值(8表示“h”,9表示“i”等等),而是要求模型输出26个数值,然后我们看哪一个是最高的,然后如果第8个数值是最大的,我们将输出解释为“h”。为什么我们不在两端使用相同的、一致的解释?我们可以这样做,但在语言中,比较不同的解释中做出选择会让你有机会建立更好的模型。碰巧的是,目前已知的对输入和输出最有效的解释是不同的。事实上,我们在这个模型中输入数字的方式并不是最好的方法,我们很快就会看到更好的方法。