近日,OpenAI 研究科学家 Hyung Won Chung 在首尔国立大学做了题为「Large Language Models (in 2023)」的演讲。他在自己的 YouTube 频道上写到:「这是一次雄心勃勃的尝试,旨在总结我们这个爆炸性的领域。」
视频地址:https://www.youtube.com/watch?v=dbo3kNKPaUA
在这次演讲中,他谈到了大型语言模型的涌现现象以及大模型的训练和学习过程,其中包括预训练和后训练阶段,最后他还展望了一下未来,认为下一次范式转变是实现可学习的损失函数。
在深入这次演讲的具体内容之前,我们先简单认识一下这位演讲者。
Hyung Won Chung 是一位专攻大型语言模型的研究者,博士毕业于麻省理工学院,之后曾在谷歌大脑工作过三年多时间,于今年二月份加入 OpenAI。
他曾参与过一些重要项目的研究工作,比如 5400 亿参数的大型语言模型 PaLM 和 1760 亿参数的开放式多语言语言模型 BLOOM(arXiv:2211.05100)。机器之心也曾介绍过他为一作的论文《Scaling Instruction-Finetuned Language Models》。
下面进入演讲内容。
演讲开篇,Chung 便指出,现在所谓的大型语言模型(LLM)在几年后就会被认为是小模型。随着人们对模型规模(scale)的认知的变化,目前有关 LLM 的许多见解、观察和结论都会变得过时甚至可能被证明是错误的。
但他也指出,幸运的是,那些基于第一性原理(First Principle)的见解却会有相对更长的生命力,因为它们比那些看似绚丽多彩的先进思想更为基础。
Chung 的这次演讲聚焦的正是这些更为基础的思想,他希望这些内容在未来几年内依然具有参考价值。
大模型的涌现现象
大型语言模型有一个有趣的现象:只有当模型达到一定规模时,某些能力才会显现。
如下图所示,很多模型在规模达到一定程度时,在准确度等某些性能指标上会出现急剧的变化,甚至模型会突然有能力解决在规模较小时完全无法解决的问题。这种现象被称为涌现(emergence)。
这个有趣现象给 AI 研究带来了很多重要的新视角。
Chung 首先提到的视角是「yet」,也就是说就算某个想法或能力目前无法实现,但随着规模扩展,也许后面会突然能够实现。
这一视角转变可能看似简单,却涉及到我们对语言模型的根本看法。一项对当前模型无用的技术也许三五年后就能变得有用,因此我们不应对当前的各种事物抱有永恒不变的观念。
他指出,「yet」视角之所以并不是显而易见的,是因为我们习惯了在一个基础公理不变的环境中工作。就像在进行自然科学实验时,如果你已经通过实验发现某个科学思想不对,那么你必定相信如果三年后再实验一次,这个思想还是不可能变正确;而且就算再过三十年,结果依然如此。
那么语言模型领域是否也存在类似于这类公理的概念呢?
Chung 认为可以把一定时段内最强大的模型视为这种「公理」,因为在这段时间里,很多研究实验都是基于该模型进行的。但有趣的地方在于:最强大的模型会变化。
举个例子,在 GPT-4 诞生时,它是最强大的,研究者基于其进行了大量实验,得到了许多研究成果和见解。但当新的更强大模型出现时,之前发现的一些见解和想法就过时了,甚至出现了许多新旧实验结果相矛盾的情况。
这就需要我们持续刷新已知的知识和观念,Chung 使用了「unlearn」一词,也就是说要刻意地去忘记已经不可行的思路。
Chung 表示目前还很少有人这样实践。而在竞争激烈的 AI 领域,很多只有一两年经验的新人却能提出有重大意义的思想,Chung 认为其中一部分原因就是这些新人会去尝试之前有经验的人尝试过的无效想法 —— 但这些想法却能有效地用于当前的模型。
因此,Chung 呼吁研究者要走在规模扩展曲线之前。
他分享说自己在进行文档实验时发现有些实验会因为模型「智力不足」而失败,也就是说模型没有足够的推理能力来解决一些困难的数学或编程问题。他会将这些失败实验记录下来,但并不会断言这些实验就彻底失败了,而是会进行一些处理,使得未来能轻松地重新运行这些实验。每当有更好的新模型出现时,他就会重新运行这些实验,观察其中哪些实验会成功,哪些会继续失败。通过这种方式,他可以 unlearn 一些东西,不断更新自己的认知和理解,让自己适应模型随规模扩展的涌现现象。
接下来,Chung 以一种简单直观的方式对涌现现象进行了说明。
如左图所示,能力 1 是 GPT-4 尚不具备但却非常接近获得的能力,稍强一点的模型可能就能获得这一能力,实现突然的能力跃升。对于中图的能力 2,即使强大的 GPT-4 也遥不可及,在短期内可能无论如何也不可能触及。至于右图的能力 3,GPT-3 就已经具备,之后改进只会给这项能力带来增量式的提升。
但在现实中,研究者可能很难确定自己正在解决的问题是属于哪一类。而 Chung 认为,只要有前面所说的思维框架 —— 不断更新自己的认知和理解,就能更轻松地识别自己正在解决的问题。
规模扩展何以有效?
Chung 说:「总结起来,我们做的一切都与规模相关,采用规模优先(scale first)的视角是至关重要的。」但规模扩展何以有效呢?
首先我们要从 Transformer 谈起。
目前所有的 LLM 都使用了 Transformer 架构。但这里不关心其架构细节,而是着眼其基本思想。
下面我们就从功能的角度来看看 Transformer。
概括地看,Transformer 就是使用了大量矩阵乘法的序列到序列的映射,之后再进行一些数组变换。
其输入是一个由 [batch, d_model, length] 构成的数组,其中 d_bacth 差不多就是该 Transformer 的宽度,length 是序列长度。
在训练阶段,输出是一个与输入大小相同的数组。当然,在推理阶段的情况不一样,不过由于规模扩展发生在预训练阶段,所以输出和输入的长度一样。
这就是 Transformer 的核心思想,非常简单的序列到序列映射。
下面将从功能角度描述从输入到输出的过程。
通常一开始有一个句子,比如「Many words don't map to one token: indivisible.」这是一个字符串,其形状为 []。
首先,将这个句子 token 化。token 化通常是通过一个外部模型来完成的,比如 BPE 或 SentencePiece,其目标是尽可能地压缩文本。这里经过 token 化后得到了一个整数列表,其形状就为 [length]。
然后,将这些整数嵌入到一个隐藏空间中,这通常被称为词嵌入(word embedding)。现在,每个 token 都被表示成了一个宽为 d_model 的向量,其长度为 length,那么其形状就为 d_model×length。
接下来就是计算量最大的地方 ——n 层 Transformer。简单来说,这个过程就是一个序列到序列的映射。这里的目标是让每个序列 token 与该序列中的其它 token 交互。这里我们不对其交互细节做任何假设,只是让它们交互。在 Transformer 中,让它们交互的方式就是让它们可以执行点积运算。模型要学习的就是如何执行这个点积运算。
之所以主要的计算量都在这里,是因为这里的计算涉及到高维数组,需要执行大量矩阵乘法和数组运算。
经过 n 层 Transformer 后,可以得到一个序列,之后再使用一个损失函数运算一番,基于预测的下一 token 得到一个最大似然。最后得到一个数值。
之后,使用结果执行反向传播,更新所有参数。
在实际操作中,这个训练过程是批量进行的,这就需要在数据结构的维度中增添一个 batch 维度。在这些数据批之间,唯一的依赖关系就是在最后计算损失时是计算它们的平均损失。
当我们谈论扩展 Transformer 时,我们通常说的就是扩展其中计算量最大的那部分。
依照第一性原理,扩展 Transformer 就意味着要使用大量计算机高效地执行上面的矩阵乘法运算。
这个过程需要将 Transformer 层中涉及的所有矩阵(数组)分配到各台计算机中。这个分配过程至关重要,必须要在尽可能降低机器之间通信量的同时来实现它。这是从非常底层的视角来理解规模扩展。
矩阵乘法
为了更好地理解这一点,我们首先需要了解矩阵乘法,尤其是在多台机器上执行矩阵乘法。如下图所示,现在假设我们有 8 台机器 —— 它们可能是 CUP 或 GPU。
现在我们要执行一个 16×16 大小的矩阵乘法:A×B=C。
首先我们以一种抽象的方式来思考硬件:定义一个 2×4 的 mesh 网格。注意这个布局是虚拟的,与这些机器的实际物理位置无关。
然后为该网格定义 x 和 y 轴(硬件轴),之后每个参与计算的数组都将按照这个坐标轴进行映射 —— 将每个数组轴映射到硬件轴。我们可以从下图的颜色对应中看到这种映射。
现在我们来看输出矩阵 C。我们希望在矩阵乘法运算完成之后,C 矩阵左上角的 1 部分能位于机器 1 中。
这时候机器 1 要做的就是对矩阵 A 的第 1 行和矩阵 B 的第 1 列执行 all-gather 操作(这是 MPI 的操作之一),之后再执行计算得到 C 的 1 部分。
以矩阵 A 的第 1 行为例,all-gather 需要四台机器之间进行通信。机器 1 在与 2、3、4 通信之后会获取其本地数据的副本;机器 2、3、4 也会执行类似的操作。故而该操作有 all-gather(全收集)之名。
all-gather 之后,机器 1 就有了计算所需的所有数据副本。
这个过程的关键之处在于其可以在全部 8 台机器上并行地执行。因此这个过程可以通过并行的方式得到加速,而其一大成本来源就是机器之间的通信。因此,在速度和通信成本存在一个权衡。
einsum
现在可以将矩阵乘法泛化成爱因斯坦求和方法(einsum),这是一种更高层面的看待数组计算的视角。
它的有两个规则:1. 如果一个字母在两个输入中都出现了,那么就执行逐分量的乘法;2. 如果输出中不包含一个字母,则在该维度上执行求和。
对于规则 1,以上图中的第一行运算为例,np.einsum ("i,i->i",a,b) 中的两个输入中都有 i(见引号内部),这就意味着要直接执行逐分量乘法来得到 i;这在 Numpy 中就等价于 a*b。
对于规则 2,则可见第二行运算,其中有 "i,i→",这时候就需要先执行逐分量乘法,然后求和。
而在第三行中,则有 "ij,j->i",这时候就需要在 j 上执行点积,这也可被视为矩阵向量乘法。
当然,上面只给出了一两维的示例,einsum 也可以支持更多维度。
从 einsum 的角度看,矩阵乘法可以写成如下形式:
现在回到前面在 8 台机器上的矩阵乘法。
现在我们已经为数组轴定义了 m、n、p 这样的标签,就可以将它们映射到硬件轴,比如将 m 映射到 y,将 n 映射到 x。现在我们希望通过一个神奇的装饰器函数 parallelize 来做到这一点(后面会更具体说明),它所做的就是在这两个维度上以并行方式执行 all-gather。
现在我们了解了矩阵乘法,接下来看 Transformer。
在 Transformer 中,最复杂的运算操作是自注意力层,其中除了 softmax 之外的一切都可以使用 einsum 表示。
然后将其对应到之前设定的 8 台机器,这时候我们不再使用 x 和 y 来标记硬件轴,而是使用研究者更习惯的「model」和「data」,分别对应于模型并行维度和数据并行维度。
现在稍微修改一下上面的代码,添加并行化,将 b 映射到 data,n 是序列长度(Transformer 不对序列长度做并行化处理),h 是注意力头的数量(代表模型)—— 对注意力机制的并行化就是通过多头来实现。
如此,接下来只需使用相同的代码,就能实现并行化;下面是使用 8 台机器的示例,但这一框架在任何机器数量下都适用。
一个 TPU v4 pod 有 3072 块 TPU chip。Chung 表示在训练 PaLM 模型时,他们使用了 2 个 pod,也就是 6144 块 TPU chip,其中每一块都与最高端的 GPU 一样强大。
现在有了这么多机器,可以和之前一样定义一个网格:模型并行维度为 48、数据并行维度为 64。
最后还有一个细节:数据中心网络(DCN)数据并行维度。这是因为这两个 pod 并不是直接连接在一起的,而是通过数据中心网络连接的,其速度大概是 25 Gbps。这比 pod 内部的通信慢多了。因此不应在这个层面上执行模型并行化。实际上,他们的做法是在梯度计算之后在这个数据中心网络上对梯度求和。这只需要做一次,耗时很短。(在训练 5400 亿参数的 PaLM 模型时,每个训练步骤耗时大概 17 秒,因此这点耗时对整体影响不大。)
并行化装饰器
前面我们是假设并行化装饰器有效,但它究竟是如何工作的呢?一种方法是 GSPMD(arXiv:2105.04663)。
GSPMD 是一种基于编译器的方法。使用该方法,你在写神经网络时可以假设你的机器拥有无限内存而不考虑并行化。然后将神经网络的核心部分表示成计算图,再将该图的输入和输出映射到硬件轴。最后将该图交给 XLA;它会自动插入必要的通信操作(如 all-gather),从而充分利用机器的全部能力。
Chung 表示这个过程很神奇,就像是魔法,但该方法并不总是有效,一些人在使用时会遇到困难。但整体来说还是有用的,毕竟 T5、PaLM、Switch Transformer 等来自谷歌的大模型的后端都使用了 GSPMD。
当然,也还存在其它一些方法,但它们都涉及到将数组轴映射到硬件。
对大多数研究者来说,GSPMD 可能很复杂,但 JAX 提供了一个前端 pjit,其使用方法如下:
大模型的规模扩展问题
对于大模型来说,预训练的成本很高。下图是 Llama-2 模型预训练过程的困惑度变化情况,可以看到最后每个模型都处理了 2 万亿个 token!这可需要不少的时间。
但在实践中,我们不会等到训练完成才观察结果,也许一开始我们会训练 500 亿个 token,然后得到这样的图表:
这个时候我们就能断言其中的 70B 模型表现最好吗?并不能,因为它们的表现还很接近。这时候要考虑如何投入资源是很困难的。
这就涉及到了预训练的一大根本课题:扩展律(scaling laws)。
如图所示的扩展律来自《GPT-4 技术报告》(其中 x 轴是以 GPT-4 为标准归一化之后的训练计算成本),其中的规律是根据更小的模型得出的,但其能准确预测 GPT-4 的最终损失。
现在进行规模扩展是比几年前容易多了,但整体依然很困难,并不是说改一些参数就能实现。
举个例子,在 PaLM 的训练过程中,出现了损失突刺(loss spike)现象(比如损失从 2 突然变成了 6),这让很多人都感到不安。
他们使用同样的数据训练了三个不同规模的模型,但只有最大的一个出现了损失突刺现象。这让研究者很难进行调试,因为无法在更小的模型上复现出来。而且这也不是由数据质量差导致的。而当出现这种情况,让人无法决定该怎么办时,都只得让大量机器闲置下来,造成巨大浪费。这些方面都有需要攻克的难题。
尽管现在已经有 Llama-2 等一些模型让人可以更轻松地训练给定大小的模型,但是进一步扩展就困难重重了。
后训练也很重要
所以,扩大规模并非万能方法,还需要开发出很多工程方法,其中很多都可以归类为后训练(post-training)。
为什么我们需要后训练?
首先,我们无法直接与预训练模型对话,因为其训练目标就只是预测下一个 token。下面给出了一个例子:对于左侧的输入,预训练后的 PaLM 540B 就只会不断预测下一个词;但我们期望是类似右侧的答案。
当然,我们可以通过一个技巧来解决这个问题,也就是将问题构造成某种形式,使得下一个 token 就是答案本身。下图给出了一个示例:
如果模型还是不回答问题,那么我们就可以在前面提供一些示例,演示模型该如何给出下一个 token。这种方法被称为 few-shot prompting。这种方法很强大,但并不普遍适用。
预训练模型还有另一个问题:总是生成 prompt 的自然延续,即便 prompt 本身是恶意和有害的。预训练模型不知道如何拒绝响应恶意 prompt。而这些能力可通过后训练方法来获得;人们通常将这样的过程称为对齐人类价值观。
对于当前的 LLM,后训练是指下图中预训练之后的阶段。
指令微调
概括来说,指令微调是将所有任务都表述成自然语言指令到自然语言响应的映射。
以文本分类任务为例,就是文本到标签的映射。但 2018 年的 BERT 在这个过程中必需要有针对这个特定任务的线性层,以将句子投影到分类空间。这样一来,用于本文分类任务的模型就很难用于其它任务了。
一年后诞生的 T5 模型就不需要这样的线性层了。它做的是文本到文本的映射,能广泛地适用于各种不同的文本任务。
但这也会有个问题:T5 支持多种不同任务,但它怎么知道当前任务是什么任务呢?研究者的做法是在输入中添加元数据,比如 cola 和 stsb。
但这种做法不自然,不符合人类的表达习惯。接下来的发展就是让模型能够理解以自然语言表达的任务。比如现在我们不再使用 cola 来指示模型执行 GLUE 中的 CoLA 任务,而是问:「下面的句子是否是可接受的?」
为什么 T5 模型诞生的时候没有采用这种方法呢?Chung 表示是因为当时人们认为语言模型不能理解自然语言指令,反而觉得使用元数据的方式更自然。
当更大的模型出现后,理解自然语言的能力就涌现出来了,然后我们就能使用丰富的自然语言将各种任务统一起来。之后,当模型遇到未曾见过的任务时,模型只需响应自然语言指令即可。这也是一种泛化。
这就引出了一个问题:如果训练集中有更多指令,能不能得到更好的模型,实现更好的泛化呢?
Chung 等人通过一个大规模实验检验了这一假设。为此,他们收集了 1836 个学术任务,然后将它们混合起来用于训练。
如下图所示,y 轴是在评估集上的平均分数。他们选择了 80 亿参数的 PaLM 模型难以应对的 6 个训练期间未曾见过的任务来进行测试,发现随着模型增大以及训练任务增多,模型的表现越来越好。但可以看出来,任务数量增至一定程度时,模型的性能增幅也会降低。这是因为任务的数量没有任务的多样性重要。
机器之心曾经报道过这项研究,参阅《30 亿跑赢 GPT-3 的 1750 亿,谷歌新模型引热议,然而却把 Hinton 年龄搞错了》。
于是 Chung 得出了结论:指令微调的效果很好,但却存在固有的限制。这一现象的根本原因是什么呢?
首先我们确定一下指令微调的学习目标:不管是使用交叉熵损失还是最大似然,目标都是对于给定输入都能给出单个正确回答,而其它答案都是错的。在强化学习文献中,这被称为行为克隆(behavior cloning)。
我们希望如果能有足够多的这些答案的变体,模型就能泛化用于不同的任务类型。为此,就需要形式化模型在给定输入下的正确行为,以便模型克隆。
过去,这种操作执行起来很简单,但现在难度却在增大。
下面将通过一些示例来说明。
首先是一个思想实验,假设有一个问题,它只有唯一正确的答案,比如「2+3=?」有唯一正确答案「5」。这没有异议。
而对于翻译任务,我们也可以大致提供一个唯一正确答案,当然,一个句子翻译成另一种语言时可能会有多种变体。
但对于以下任务呢:以圣诞老人的口吻写一封信给一位五岁孩童,解释圣诞老人不是真的,要求语气温柔,不要让这个孩子伤心。
Chung 表示自己没信心为这个任务给出一个好答案。
对于这样的任务,Chung 认为并不适合使用最大似然作为大模型的学习目标。
接下来是一个更为实际的例子,Chung 表示自己经常使用这一 prompt 来测试新模型:用 Python 通过梯度下降实现 logistic 回归。
这个任务并不存在唯一正确的答案 —— 可能有函数式编程风格的答案,也可能有面向对象式的答案。这些不同的答案可能都是正确的。这样一来,使用其中一个作为唯一正确答案是合适的做法吗?
Chung 给出了一些观察:
- 我们越来越希望教会模型掌握更抽象的行为
- 指令微调的目标函数似乎是教授这些行为的「瓶颈」
- 最大似然目标是「预定义的」函数(即不可学习的参数)
- 我们能否参数化目标函数并学习它?
这就是 RLHF 的核心思想。
使用人类反馈的强化学习(RLHF)
强化学习(RL)提供了一种学习目标函数的方法。
强化学习的目标是最大化预期的奖励函数,而我们可以使用一个神经网络模型(奖励模型)来为更为复杂的情况制定奖励。
那么怎么训练这个奖励模型呢?
对于给定的输入,为其提供两个可能的答案,然后让人类提供对这两个答案的偏好。也就是说不是提供一个最佳答案,而是让人类评估两个答案中哪个更好。AI 模型就可以依照这种方式学习人类的偏好。
比如在上图的例子中,人类更偏好结果 2,但这个结果并不见得就是最佳结果,只能说比结果 1 更好。
对于有着清晰明确答案的简单任务,这种比较方法可能用处不大,不如使用有最大似然目标的监督学习。
但对于开放式的生成任务,相比于为答案打分,比较候选答案之间的相对优劣会更容易。
下面是这种奖励模式的数学描述:
有了奖励模型之后,就可以通过强化学习来学习语言模型的参数,以最大化预期奖励。
这里,目标函数就是刚刚的奖励模型,也就是参数 Φ,其在初始的强化学习后就固定了。
在这个公式中,对于一个 prompt,策略模型(通常是根据监督式指令微调的检查点模型进行初始化)会生成一些候选结果;然后它们被提供给奖励模型,奖励模型返回分数,策略模型就可以根据这个反馈进行调整。这就像是一个试错过程。
我们可以通过基于梯度的迭代方法来最大化预期奖励,这个过程要用到一些策略梯度算法,如 PPO。
总结起来即为:奖励模型编码人类偏好,然后将其传递给策略模型,让其通过强化学习进行学习。
但在实践中,很多人并不喜欢 RLHF,甚至希望抛弃这个方法,因为它很难做好。
RLHF 的一个常见问题是「奖励攻击(reward hacking)」。
举个例子,假设有人类标注者对一些完成结果进行标注 —— 标记一对结果中哪一个更好,而如果他们标记的都刚好是更长的结果更好,那么策略模型就会认为奖励模型更喜欢更长的结果,然后渐渐地,它就会越来越多地给出很长但看起来很蠢的结果。这时候奖励模型给出的奖励越来越高,但人类的满意度却会下降。这种现象其实很难控制,Chung 表示目前还没有彻底解决这一问题的方法。
但即使存在这些问题,Chung 认为我们还是应该继续研究 RLHF,他认为原因包括:
- 最大似然有过于强大的归纳偏见,当模型规模变大时,这个问题会更显著;
- 学习目标函数是一种不同的范式,有助于缓解这个问题,能提供很大的提升空间,ChatGPT 等一些成功利用 RLHF 的案例只是一个开始;
- 其原理很可靠,值得进一步发掘其功效。
AI 的未来发展
Chung 最后简单回顾了 AI 过去的发展历程并谈到了自己对 AI 未来发展的展望。
他分享说,从基于规则的系统到经典机器学习技术,再到深度学习以及使用 RLHF 的深度学习,AI 系统中可学习的部分(图中蓝色部分)在不断增多,其能力也越来越强大。
现在,损失函数也正在变成系统中的可学习部分,并且已经有 GAN 和 RLHF 这样的成功案例。这让 AI 系统可以学习去做那些正确行为难以形式化的任务。
Chung 认为这就是未来的下一个范式,其将带来的技术进步不会亚于之前每一次范式转变。
至于哪个 AI 模型将成为这个新范式的旗舰代表,就让我们拭目以待吧。