当我开车载着全家人做长途旅行时,为了打发时间,我们有时候会玩起「成语接龙」的游戏,类似这样:
海阔天空
空前绝后
后发制人
人山人海
…
游戏的规则是,每个人轮流说出一个成语。
当某个成员一时想不出来的时候,其他人通常会帮助他/她想出来一个合适的成语,使得接龙游戏能够继续下去。一般情况下,过不了太长时间,成语就会开始重复。比如,就像上面给出的例子,「人山人海」的下一个成语很可能又是:「海阔天空」!
当我们在说话或写文字的时候,其实也可以看成是在进行某种类似的「接龙游戏」,只不过这个过程更加微妙,涉及到的语言规模也更加庞大。
在成语接龙游戏中,我们根据前一个成语来「预测」下一个成语。而在说话或写作时,我们不停地在「预测」下一个字或下一个词语。虽然像成语接龙那么强的语境规则不再存在了,但是,我们需要随时在更大的语言空间内搜索词句,让语句或文段变得自然,并符合最基本的语法和常识。
相信很多人都有过这样的体验:当进行远程通话时,即使信号断断续续,你可能仍然能够大致听懂对方要表达的意思。这是因为,你的大脑根据已经掌握的语言知识以及常识,及时地「预测」出了对方词句中缺失的部分。
在自然语言处理 (NLP) 中,我们通常用一个条件概率来描述这一预测过程的概率分布。
假设我们已经知道了一段文本的前面n-1
个字:w1w2…wn-1,那么第n
个字wn出现的概率是:
P(wn|w1w2…wn-1)
现在的LLM,通过在大规模的文本语料上进行训练,就是学会了这样的一个条件概率(至于如何学会的,后面章节再讨论)。当然,LLM通常进行预测的单元不直接是字或词,而是token。这就是所谓的predict next token
。
在本文中,我不打算涉及token和字词的区别这样的细节。读者可以姑且认为一个token就是一个字或一个词语(这不影响理解)。但是,在后面的章节中,我们将换用token的概念来进行描述。也就是说,从w1到wn,每一个变量都代表一个token。
前面的式子P(wn|w1w2…wn-1),则表示,在知道了前n-1
个token的前提下,预测第n
个token的条件概率。
假设某个LLM已经学会了如何预测前面的条件概率,那么,wn需要在整个字典中进行取值。也就是说,对于字典中的任何一个可能的token,LLM都能预测出它在序列中第n
个位置出现的概率。在机器学习中,这是一个分类问题,只是我们面临的类别数目非常庞大。以汉语为例,汉字的总量大约在十万量级左右(token字典的规模也应该在同一个数量级)。
我们知道,在机器学习中,解决分类问题有三种方法[1],复杂度从高到低分别是:
Generative Model
:生成式模型。直接预测整个联合概率分布P(x,Ck)。Discriminative Model
:判别式模型。只预测后验的条件概率分布P(Ck|x)。Discriminant Function
:判别函数。直接将输入x映射到类别Ck,不涉及到概率分布。对应到LLM,它对于生成序列的预测上,我们会发现:
在上一小节,我们已经给出了条件概率的公式,LLM可以用它来predict next token
。那么,这难道说明,LLM属于判别式模型?
等一下,似乎不太对!最常见的LLM,基本都是基于GPT架构的。大家都知道,GPT的全称是Generative Pre-trained Transformer
,这里面的Generative
一词,清楚地表明了它属于生成式模型。
这是怎么回事呢?对于序列的建模,有它的特殊性。当我们说,模型可以预测条件概率P(wn|w1w2…wn-1)的时候,我们的意思是说,这个预测对于任意的n都成立!也就是说,以下的条件概率,LLM都能够预测出来:
我们发现,根据概率论的链式法则,把上面这些条件概率全部乘起来,再在前面乘以一个P(w1),就会得到:
P(w1) P(w2|w1) P(w3|w1w2) … P(wn|w1w2…wn-1) = P(w1w2…wn-1wn)
可见,LLM是能够预测一个序列的联合概率分布的(只不过需要多步来完成,每步只预测一个token)。严格来说,上面的等式左面还多了一个P(w1),但由于实际中LLM在生成时总会输入一个长度大于0的前缀序列,所以多出来的这一项P(w1)不影响大局。
因此,我们可以说,LLM是货真价实的生成式模型。而上一小节的条件概率和本小节的联合概率分布,在描述LLM的能力时是等价的。
根据机器学习的一些理论基础,我们知道,生成式模型的主要优劣势如下:
这些情况,与LLM的实际情况吻合。
在本文第一小节,为了做到predict next token
,我们已经定义了条件概率P(wn|w1w2…wn-1)。现在,我们来讨论一下,这个条件概率如何才能够计算出来。
一个自然的想法可能是,通过统计计数的方法来估算。假设有一个很大的文本语料库,我们可以统计出这个语料库中序列w1w2…wn-1出现的次数,记为C(w1w2…wn-1)。容易想象出,在这个语料库中,序列w1w2…wn-1的下一个token,可能是wn,也可能不是。我们再计算出序列w1w2…wn-1wn在语料库中出现的次数,记为C(w1w2…wn-1wn)。于是,前面的条件概率可以用这两个次数的比值来估算:
P(wn|w1w2…wn-1) ≈ C(w1w2…wn-1wn) / C(w1w2…wn-1)
这种估算条件概率的方法,其实就是早期的n-gram语言模型所使用的方法。它的计算有一定的道理,但却存在一个致命的问题:对于语料库中不存在的序列,以上公式中的两个次数都无法计算出来。由于语言是具有创造性的,所以即使语料库再大,也不可能涵盖所有可能出现的token序列。在真实的语言任务中(比如写作),通常不太可能像本文开头的成语接龙那样,那么容易就会出现重复。
这里面临一个很重要的generalization的问题:如何从训练语料库中已经见过的序列,估算出从未出现过的新序列的概率分布。
对于语言的建模是个离散的问题。我们前面提到过,语言的字典规模可能在十万量级,规模很大。但token不能取任意实数,它只能在字典中取值,因此是离散的。离散系统有一个问题:即使输入序列产生非常微小的变化(变成了新的序列),对这个新序列的概率的预估,也可能变化非常剧烈。这显然不是所期望的。
为了解决这个致命的问题,对语言序列进行的建模,我们必须选择具备连续性的概率模型。我们知道,神经网络是基本具备这样的特性的。
图灵奖得主Yoshua Bengio和他的同事在2003年的一篇文章[2],比较好地解决了这一问题:
embedding
(原文中称为word feature vector
)。这使得语义上相似的token,在embedding
空间中也具备近似的数值。以此为基础,后面学术界又出现了很多对自然语言建模的重要进展,特别是2017年出现的Transformer[3]。后来的GPT-2[4],GPT-3[5],也都是在这些研究的基础上发展出来的。
我们前面一直在谈论概率分布,然后又提到了使用神经网络来表达这个概率分布。但这里似乎存在一个让人困惑的问题:概率在本质上是随机的,而神经网络本质上却是个确定性的函数,这两者是怎样调和到一起的?
本来这算不上一个问题,只是理解和认知上的。但我跟一些参加面试的候选人聊的时候发现,即使是统计学或机器学习专业的同学,似乎对这个问题也很困惑。因此,我们在这里展开讨论一下这个问题(虽然有些费口舌)。
如前所述,我们想要建模的,是一个概率,也就是:P(wn|w1w2…wn-1)。它表示,在知道了前n-1
个token的前提下,预测第n
个token的条件概率。也就是说,即使我们知道了前n-1
个token作为输入序列,第n
个token到底是什么,也没办法百分之百确定。这就是一个概率的意思。
但是,神经网络本质上是一个函数。你输入一个x,它就确定性地输出一个y。当然了,LLM也是一个神经网络,一个复杂一些的神经网络。
那么,一个本质上用来表达随机性的概率分布,为什么可以用一个确定性的函数来描述呢?答案存在于概率学本身。
首先,在概率论中,任何一个随机变量的概率分布,都是表达成一个函数的。随机变量有两种:离散随机变量和连续随机变量。
不管是P(x)还是p(x),它们都是随机变量x的函数。我们前面说过,LLM是一个离散的概率模型。因此下面我们重点关注P(x)。
虽然P(x)是x的函数,这里确实出现了函数,但是,在实际的机器学习问题中,一般来说,我们的目标是去预测x取某个特定值的时候的概率。这时候P(x)就变成一个常量了。我们发现,自变量x消失了。
真正让机器学习模型以一个复杂的函数(比如神经网络)的形式来呈现的原因,主要是两个:
显然,这两个原因,是存在于概率学本身的。
现实中的实际问题一般都是比较复杂的,对这些问题进行建模,也需要足够复杂的函数来描述一个概率分布。对神经网络来说,输入x一般指的是条件概率中表示给定事件的那个随机变量,它每经过网络的一层,就经过一次函数变换(而且是非线性的)。可以想象,经过足够多层的变换,最终得到的函数应该是个足够复杂的函数。实际上,神经网络是可以近似任何函数计算的[6]。
简单总结一下:概率是用来表达随机性的,但在数学上是用一个确定性的函数来描述的。神经网络经过多层的函数变换,最终能够近似表达任意的函数,也就能够近似表达任意的概率分布。
最后,回到LLM生成模型,它和其他神经网络一样,经过一系列函数计算,得到了条件概率P(wn|w1w2…wn-1)的确定值。然后,还要经过一个采样(Sampling)的过程,才能真正得到要生成的token。前面的函数计算是确定性的过程;后面的采样则是表现了随机性的过程,而且随机性的程度大小可以使用温度值 (temperature
) 来控制(温度值的细节本文不展开)。
至此,我们还遗留了一个问题:LLM是怎么学会语言的联合概率分布的?
先回顾一下我几年之前写的一篇文章《深度学习、信息论与统计学》中所提到的:模型训练时使用的loss function
,通常来源于最大似然估计 (Maximum Likelihood Estimation),简称为MLE
。
在预训练阶段,对于LLM生成模型来说,MLE
意味着让模型不断调整参数,使得从模型中采样得到的(即生成的)序列恰好等于整个训练集(即训练语料库)的概率达到最大。同样在这篇文章中,我们还推导了另一种等价形式,训练过程也可以看成是使得训练集分布p’data与模型分布pmodel之间的Cross-Entropy
最小化的过程。这个Cross-Entropy的值记为:
H(p’data, pmodel) = -Ex~p’data[log pmodel(y|x;θ)]
根据本文第一小节,这个式子中的x和y分别应该对应:
这其实就是Transformer中广泛使用的自回归方法 (auto-regressive) ,predict next token
。注意,在训练时,理论上w1w2…wn-1wn应该遍历整个训练集。
可能有人会问:LLM不是预测整个序列的联合概率分布的吗?为什么这里的表述都是条件概率的形式?我们前面在第二小节其实已经讨论过这个问题。看下面的图,会让这个过程更加清晰(图片出自[7]):
上图表示训练过程中,输入进LLM的某一个batch中的其中一个sample(文本序列):
LLMs learns to predict one word at a time
基于LLM的auto-regressive
和causal attention
机制,针对这个输入序列,模型实际上在同时做多个预测。可以拆解为:
类似于前面第二小节做过的计算,这些项虽然都是条件概率,但全部乘起来,就(近似)等于整个序列的联合概率分布:
因此,可以说,预训练阶段的predict next token
,实际上是在学习预测一个联合概率分布。
在SFT阶段,底层原理仍然是predict next token
。但是,要预测的条件概率中多了一个instruction
,即:P(output|input, instruction)。要对这个概率分布进行建模,传统的方法是设计新的模型架构,来接收和处理多出来的instruction
。不过,按照现代LLM的思路,instruction
也是文本序列,在这一点上它没有特殊性。因此,instruction
应该可以和input
合并到一起,基于某种prompt style
格式化成一个更大的输入序列。
关于instruction
作为输入序列一部分的一个有趣的例子是翻译。与Transformer不同,GPT模型最开始设计的时候,是基于decoder-only
的架构。这种架构的目的是基于输入序列做predict next token
,研究者并没有预期它可以像原始的Transformer那样能够执行翻译任务。但是,人们意外地发现它也具备了翻译的能力[7]。
在本文中,我们解析了LLM背后的概率学原理,试图将基础的数学原理与LLM的实践结合起来。
一方面,LLM赖以运作的底层数学原理,与传统机器学习方法没有本质的不同;另一方面,它也是一项创举,开创了用一种简单的predict next token
的机制,使用同一个模型架构来解决多种task的技术路线,为AGI
的出现带来了一线曙光(虽然还有很多争议)。这种开创性的工作至少包括:
instruction
也看成是文本序列,与input
一起,做predict next token
。同时,我们也应该认识到,LLM的能力还远没有达到完美,存在诸如幻觉、推理能力 (Reasoning) 不够强等问题。理解它的原理与如何使用好它,是两个非常不同的问题。恰恰是因为LLM的不完美,我们才需要在应用层面做出更多的创新(不管是技术上的,还是产品上的)。
(正文完)
其它精选文章: