文章目录
本文为《A Survey of Large Language Models》系列笔记的一部分,原论文链接:arxiv2303.18223
预训练奠定了大型语言模型(LLMs)能力的基础。通过在大规模语料库上的预训练,LLMs 能够获得基本的语言理解和生成能力。在这一过程中,预训练语料的规模与质量、模型架构、加速方法和优化技术,这些内容对 LLM 能否获得强大能力起着至关重要的作用。
数据收集与准备
与小规模语言模型相比,大型语言模型(LLMs)对高质量数据的需求更强,其模型能力在很大程度上依赖于预训练语料的质量及其处理方式。
数据来源
预训练语料的来源大致可以分为两类:通用数据(general data)和专用数据(specialized data)。
-
通用数据,如网页、书籍和对话文本,因其规模大、类型多样、易于获取,被大多数 LLM 所使用。
-
鉴于 LLM 在泛化方面表现出的优越能力,也有研究进一步将预训练语料扩展到更具专业性的语料,如多语种数据、科学文献和代码,从而赋予 LLM 执行特定任务的能力。
通用文本数据
当前主流的大模型基本都依赖通用文本语料作为语言能力的基础训练材料,主要分为网页、书籍和对话文本。
网页
随着互联网的快速发展,产生了各种类型的数据,为了便于使用这些资源,已有研究通常从网络上大规模抓取网页数据。然而,抓取的网页数据中既包含高质量文本(如 Wikipedia),也包含低质量内容(如垃圾邮件)。因此,对网页数据进行筛选与处理,以提升语料质量,显得尤为重要。
对话文本
对话语料是提升 LLM 对话能力的关键数据类型,研究人员可以利用公开的部分对话语料(如 PushShift.io Reddit corpus),或从社交媒体平台中收集对话数据。由于线上对话通常涉及多位参与者,一种有效的处理方式是将对话转化为树状结构,其中每个发言(utterance)与其回应的发言相连接。同时,数据比例也需控制得当,否则可能影响模型对指令的理解和执行,从而损害指令跟随类任务的表现。
专用文本数据
多语种文本
除了目标语言的文本之外,引入多语种语料还能提升语言模型在语言理解与生成方面的多语言能力。
例如,BLOOM 和 PaLM 在其预训练语料中分别包含了覆盖 46 种和 122 种语言的多语种数据;FLM 则以近乎相等的比例混合了中文与英文语料。这些模型在多语种任务中表现出色,如翻译、多语种摘要、多语种问答,并且能达到甚至超越在目标语言语料上微调的最先进模型(state-of-the-art models)的表现。
科学文本
科学语料的引入能为语言模型赋予科学领域的推理与专业能力。但由于其文本形式复杂多变,在数据处理上比通用语料更具挑战性,因此必须结合专门的预处理手段,才能让模型有效吸收科学知识。
代码
程序生成(Program synthesis)一直是研究社区关注的重要方向。现有的 PLMs(如 GPT-J )在生成高质量、准确性强的程序方面仍然面临挑战。
用大规模代码语料训练 LLMs,可以显著提升生成程序的质量。生成的程序不仅能够通过专家设计的单元测试用例,还可以解决竞赛级编程问题。
预训练 LLM 所使用的代码语料主要有两类来源:1)编程问答社区,如 Stack Exchange;2)公开代码仓库,如 GitHub,从中提取代码数据(包括注释与文档字符串)。
代码具有编程语言的结构特点,涉及长距离依赖关系与精确的执行逻辑。在代码上训练模型可能是获得复杂推理能力(如链式思维能力CoT)的来源之一。此外,已有研究表明,将推理任务转化为代码格式,可以帮助LLMs 生成更准确的结果。
数据预处理
在收集到大量文本数据之后,必须对数据进行预处理,特别是要清除噪声数据、冗余数据、无关数据以及潜在有害数据。因为这些内容会严重影响大型语言模型的能力与表现。
过滤与选择(Filtering and Selection)
现有研究通常采用两类方法:基于分类器(classifier-based)和基于启发式规则(heuristic-based)。
选择分类器(selection classifier)
使用高质量文本作为正例训练,借此识别并过滤掉低质量数据。
这类方法通常使用以下内容作为正例数据训练二分类器:精挑细选的数据(例如 Wikipedia 页面)、高质量合成数据或两者的结合。
同时,从语料中抽取部分候选数据作为负例,并计算每条数据的质量得分来筛选。
然而,一些研究发现,基于分类器的方法可能错误地剔除某些方言、口语或社会语言变体中的高质量文本,进而导致语料偏差和多样性下降。
启发式规则(heuristic-based)的过滤方法
此类方法通过一系列预设规则去除低质量文本。
-
语言过滤(Language based filtering):如果 LLM 主要用于特定语言任务,可过滤其他语言的文本。
-
指标过滤(Metric based filtering):使用语言模型评估指标(如 perplexity)判断文本是否自然,去除异常句子。
-
统计特征过滤(Statistic based filtering):利用文本的统计特征,如标点符号分布、符号与单词比、句子长度等,评估并筛除低质量文本。
-
关键词过滤(Keyword based filtering):根据关键词集识别并移除噪声或无用元素,如 HTML 标签、超链接、模板文字、攻击性词汇等。
-
还可以使用 LLMs 本身(特别是参数较小的模型)来参与数据选择,例如:利用模型计算 perplexity(困惑度),直接通过 prompt 引导 LLM 判断样本重要性等。(这种做法计算成本偏高)
去重(De-duplication)
语料中的重复数据会降低语言模型的多样性,可能导致训练过程不稳定,影响模型性能。
去重可以在多个层级上进行,包括:
-
句子级去重(sentence-level)
-
文档级去重(document-level)
-
数据集级去重(dataset-level)
首先,应去除那些含有重复单词或短语的低质量句子。之后在文档级别,现有研究通常使用表层特征的重合度(如单词或 n-gram 的重合率)来检测并去除内容相似的重复文档。
此外,为了避免数据集污染(dataset contamination)问题,还必须防止训练集与评估集之间的重复。将可能重合的文本从训练集中移除。
隐私信息去除(Privacy Reduction)
出于安全、合规、性能与伦理多方面的考虑,在预训练语料中移除可识别的个人信息(PII, Personally Identifiable Information)是非常必要的。一种直接且有效的方法是使用基于规则的方法(如关键词识别)来检测并移除 PII,例如姓名、地址、电话号码等。去重(de-duplication)也可以在一定程度上降低隐私泄露风险,研究发现大型语言模型在隐私攻击下的脆弱性和重复的PII数据有关。
分词(Tokenization)
分词是数据预处理中的关键步骤。它的目标是将原始文本切分成一系列“token”,这些 token 随后作为大型语言模型(LLMs)的输入。
近几年,在基于 Transformer 的语言模型中,子词分词器(subword tokenizers)被广泛使用,典型方法包括:
-
Byte-Pair Encoding(BPE)
-
WordPiece
-
Unigram
BPE
BPE 从一个基本符号集(如字母表、空格符等)开始,然后在语料中反复合并两个连续出现频率最高的 token 组合,生成新的 token(这一过程称为merge)。
每一次合并的依据是:在语料中相邻两个 token 的共现频率,选择频率最高的组合进行合并。这种合并过程会持续进行,直到生成的词表达到预设大小为止。
此外,BPE 还有一个变体叫做 Byte-level BPE,它以字节(byte)作为基本单位进行合并,适用于多语言语料中包含非 ASCII 字符的情况,可以提升分词质量。
WordPiece
WordPiece 的基本思想与 BPE 非常相似,都是通过迭代合并相邻 token 来构建子词词表,但在“合并选择标准”上略有不同。
WordPiece 的合并过程如下:
1)首先训练一个语言模型;
2)使用该模型为所有可能的 token 对进行打分;
3)每轮合并选择最能提升训练语料整体似然值(likelihood)的 token 对。
注意Google 没有公开 WordPiece 的官方实现。
HuggingFace 在其在线 NLP 教程中给出了一种更直观的衡量方式:对于每对 token,其得分为:
共现次数 ÷ 两个 token 的出现次数之积
Unigram
与 BPE 和 WordPiece 不同,Unigram 分词是一种基于删除的子词分词方法。它从一个足够大的子词集合(即所有可能的子字符串或子 token)出发,逐步删除其中的 token,直到词表缩减到预设的大小。
它的核心逻辑是:
每次尝试移除一个 token,并计算这样做是否会降低语言模型对训练语料的整体似然值(likelihood),从而判断该 token 是否“有用”。
这个过程基于一个训练好的 unigram(单元语)语言模型。
为了估算该语言模型的参数,Unigram 分词使用了期望最大化算法(EM),流程如下:
-
E 步(Expectation):使用旧的语言模型,为词语找到当前最优的 token 划分方式;
-
M 步(Maximization):根据这些最优划分,重新估算各个 token 的概率,更新语言模型。
在这个过程中,为了高效找到某个词的最优划分方式,会使用动态规划算法,如著名的 Viterbi 算法。
虽然直接使用已有的分词器是一个权宜之计,但为预训练语料专门设计分词器会带来明显好处,尤其是当语料包含多种领域、语言和格式时。近年来的 LLM 通常会使用 SentencePiece 库,针对自己的预训练语料训练定制化的分词器。
需要注意的是,BPE 中常用的一些标准化技术(如 NFKC)有时会降低分词效果,自定义的分词器也会有副作用。
总结
数据质量在 LLM 训练中比数据量更重要。哪怕使用大规模语料,如果不进行高质量清洗,最终得到的模型也可能性能不稳定,泛化能力弱,甚至有安全风险。
为了提高训练稳定性、避免性能下降,必须在预训练前采用如下数据预处理方法:
- 质量过滤(quality filtering)
- 毒性过滤(toxic filtering)
- 去重(deduplication)
这类清洗策略在之前已有说明。
数据调度 (data scheduling)
在完成数据预处理之后,若要训练出有能力的大型语言模型(LLM),就必须设计合适的数据调度策略,来合理安排这些多源数据的使用方式。
一般而言,数据调度需要关注两个关键方面:
1)每个数据源的占比(即:数据混合比例,data mixture)
2)每个数据源在训练中出现的顺序(即:数据课程编排,data curriculum)
数据混合比例 (data mixture)
由于每种类型的数据源都与 LLM 某些能力的发展密切相关,因此合理设置各类数据的混合比例非常重要。
数据混合比例通常在全局层面上进行设定(即整个预训练语料的整体分布),但也可以在局部阶段(即不同训练阶段)灵活调整比例。
常见的做法是,像 LLaMA这样的模型会对某些数据源进行上采样(upsampling)或下采样(downsampling),从而生成特定比例的数据混合,作为最终的预训练语料。
LLaMA 的预训练数据主要由以下组成,它是训练通用LLM的重要参考比例:
-
网页数据(超过 80%)
-
来自 GitHub 和 StackExchange 的代码密集型数据(6.5%)
-
图书数据(4.5%)
-
科学论文数据(arXiv,2.5%)
在实际操作中,数据混合的设定往往依赖经验判断(empirical)。下面是一些常见的数据混合策略:
增加数据源的多样性
在某一特定领域上使用过多的数据进行训练,会削弱 LLM 在其他领域的泛化能力。相反,提升数据源的多样性(即包含多种类型的数据),对于提高 LLM 在下游任务中的表现至关重要。
优化数据混合策略
除了手动设置数据混合比例之外,已有一些研究提出了自动优化数据混合的方法。
具体来说,如果已知目标下游任务,可以选取在特征空间上更接近这些任务的数据作为预训练数据,也可以选择那些能对下游任务性能产生积极影响的数据源。
此外,为了减少对具体目标任务的依赖,一种优化流程:
-
首先使用初始的领域权重训练一个小型参考模型;
-
然后再训练一个小型代理模型,并对那些在两个模型中似然差异最大的领域进行加权;
-
最终,将代理模型学到的领域权重应用到大模型的训练中。
一种更简单的方式是:
-
先训练多个小语言模型,分别使用不同的数据混合策略;
-
然后选择那个能带来最理想表现的数据混合方案;
这个策略基于一个假设:小模型和大模型在能力或行为上具有相似性,但这个假设在实际中并不总是成立。
能力定向优化
大型语言模型(LLM)的能力在很大程度上依赖于数据的选择与混合方式,通过提高特定类型数据的比例,可以有针对性地强化某些模型能力。
为提升某项特定技能(如数学或编程),或训练某种专用模型,一个实用的方法是采用多阶段训练策略:
第一阶段使用通用数据进行训练;
第二阶段再使用技能相关的数据进行强化训练。
这种在不同阶段使用不同类型或不同比例的数据源的方式,也被称为数据课程编排(data curriculum),之后详细介绍。
数据课程编排(data curriculum)
已有研究表明,在某些技能学习中,遵循技能序列的学习顺序(如从基础技能 → 目标技能),效果优于直接用目标技能相关数据进行训练。
借鉴课程学习(curriculum learning)的思想,研究者提出并广泛应用了数据课程(data curriculum)方法于模型预训练中。
它定义为:以特定顺序组织预训练数据。在更广义的层面,数据课程也指:在训练过程中,动态调整不同数据源的比例。
目前相关工作主要集中在持续预训练(continual pre-training)上,如代码专用模型 CodeLLaMA、长上下文模型 LongLLaMA。
对于通用 LLM(如 LLaMA)的数据课程策略研究仍较少。
一个实际可行的做法是:
-
利用专门设计的评估基准测试模型关键能力的提升情况;
-
然后根据评估结果动态调整训练中的数据混合比例。
以下是三个常见能力的示例。
编码能力(Coding)
为增强 LLM 的编程能力,CodeLLaMA 基于 LLaMA 2 进行开发,训练过程为:
2T 通用数据 → 500B 含代码比例较高的数据
CodeLLaMA-Python进一步向python优化,训练流程为:
2T 通用数据 → 500B 含代码数据 tokens→ 100GB Python 高比例数据tokens
数学能力(Mathematics)
Llemma 是一个为了提升数学能力的模型,也是基于 CodeLLaMA 开发的。
虽然 CodeLLaMA 原本主要是面向代码能力,但实验发现它在数学基准测试中比基础模型 LLaMA 2 表现更好。
Llemma 的训练过程为:
2T 通用数据 → 500B 含代码数据 tokens → 50∼200B 含数学内容的数据tokens(含科学论文、网页、代码等)
其中还包含5% 的通用领域数据作为正则化,避免模型过度偏向专业领域。
长上下文(Long context)
长上下文建模(Long context)是大型语言模型(LLMs)的一项重要能力,许多研究尝试通过持续训练(continual training)来扩展 LLM 的上下文窗口大小。
对于基于 RoPE(旋转位置编码)的 LLMs,通过修改位置嵌入(如位置插值),可以进一步扩大上下文窗口。
总结
为大型语言模型(LLMs)准备预训练数据的一般流程和关键要点,主要从以下三个方面展开:
-
数据收集(Data collection):建议在预训练数据中引入多样的数据来源。如果模型要专注于某项特定技能,则应提高相应数据源的比例。
-
数据清洗(Data cleaning):收集完原始语料后,提升数据质量的关键步骤是清洗语料。首先要去重,然后去除低质量文本、有害或攻击性内容和涉及隐私的数据。
-
数据调度(Data scheduling):处理完数据后,下一步是确定数据的混合方式和呈现顺序(即数据课程)。数据混合比例和数据训练顺序(课程)的设定是关键;可以先训练多个小模型,在不同的候选数据方案下进行预训练,然后再从中选择效果最好的一个用于大模型训练;数据课程(data curriculum)的寻找通常先监控模型训练中间阶段在特定评估基准上的表现,然后据此动态调整数据混合比例与分布。
模型的架构设计
大型语言模型(LLMs)的架构设计,包括主流架构、预训练目标以及具体配置细节。
主流架构
由于 Transformer 架构拥有出色的并行能力与强大的表达能力,它已经成为开发各种大型语言模型(LLMs)的事实标准主干架构。
总体而言,当前主流的 LLM 架构大致可以分为三类:
-
Encoder-Decoder(编码器-解码器)
-
Causal Decoder(因果解码器)
-
Prefix Decoder(前缀解码器)
Encoder-decoder
原始的 Transformer 模型基于Encoder-decoder架构构建,该架构由两个堆叠的 Transformer 模块组成,分别作为编码器和解码器。编码器采用多头自注意力机制(multi-head self-attention)的堆叠层来对输入序列进行编码,从而生成其潜在表示(latent representations);而解码器则对这些表示执行交叉注意力(cross-attention),并以自回归方式(autoregressively)生成目标序列。只有少数大型语言模型是基于该架构构建的。
Causal Decoder
因果解码器(Causal Decoder)架构采用单向注意力掩码(unidirectional attention mask),以确保每个输入 token 只能关注之前的 token 和它自身。输入和输出 token 都以相同的方式通过解码器处理。以该架构为代表的语言模型包括 GPT 系列模型。特别是 GPT-3 成功展示了该架构的有效性,并表现出了令人惊叹的上下文学习能力(in-context learning)。
需要注意的是,Causal Decoder和接下来将介绍的Prefix Decoder都属于仅解码器架构(decoder-only architecture)。在现有文献中,如果提到“decoder-only architecture”,除非特别说明,通常是指Causal Decoder架构。
Prefix Decoder
Prefix Decoder对Causal Decoder的掩码机制进行了修改,使模型可以在前缀 token上执行双向注意力(bidirectional attention),而在生成的 token 上仍然只进行单向注意力(unidirectional attention)。
通过这种方式,前缀解码器可以像编码器-解码器架构一样,在前缀序列上进行双向编码,并以自回归方式逐个预测输出 token,同时在编码和解码过程中使用相同的参数共享机制。
一种实用的做法是不从零开始训练Prefix Decoder,而是先继续训练Causal Decoder(如 GPT 类模型),然后将其转换为Prefix Decoder,以加快收敛速度。
专家混合架构(Mixture-of-Experts, MoE)
在上述三种架构的基础上,还可以通过“专家混合(Mixture-of-Experts, MoE)”机制来进一步扩展模型。MoE 是一种稀疏激活机制,即对于每个输入,仅激活部分神经网络权重。MoE 的主要优点在于:可以灵活地扩展模型参数规模,而不会增加计算成本。研究表明,通过增加专家数量或总体参数规模,可以显著提升模型性能 。
尽管 MoE 有这些优势,但在训练大型 MoE 模型时也可能遇到不稳定性问题,主要原因是路由操作中涉及的硬切换(hard-switching)机制较为复杂。
最近有大量猜测认为 GPT-4 是基于 MoE 架构开发的,但目前尚未有官方确认。
其他新兴架构
传统的 Transformer 架构在处理长序列时通常面临与序列长度成平方关系的计算复杂度问题,从而导致处理长输入的成本较高。为提高效率,近期研究致力于设计新的语言建模架构,其中大多数基于参数化状态空间模型(State Space Models,SSM),这类模型可以看作是 RNN 与 CNN 的结合体。
一方面,SSM 像 RNN 一样可以递归地产生输出,即在解码时只需要参考前一个状态,而不必像传统 Transformer 那样重新访问所有先前的状态,因此显著提高了解码效率。另一方面,这些模型也具备像 Transformer 一样并行编码整个序列的能力(通过卷积计算实现)。因此,它们可以结合 GPU 并行技术加速训练和推理过程。
尽管 SSM 在计算效率上表现优越,但其性能仍不及 Transformer。因此,研究者提出了多个 SSM 的改进变体,如 Mamba、RetNet、RWKV 和 Hyena。
Mamba
Mamba 架构旨在在状态更新过程中有选择性地过滤或保留信息。它将原本固定的 SSM 层参数替换为输入的函数,根据当前输入,选择性地过滤前一个状态和当前输入中的信息。与传统的状态空间模型(SSM)相比,Mamba 在文本建模能力方面表现出更强的性能。
RWKV
RWKV结合了 Transformer 和 RNN 的优点。它使用了时间混合模块(time-mixing modules),也就是带有门控机制的 RNN,同时还引入了通道混合模块(channel-mixing modules),这是一类特殊的前馈神经网络。
在这些模块中,RWKV 使用一种称为 token shift(token 偏移) 的机制——即当前 token 与前一个 token 的线性组合,而不是直接使用 token 的表示向量作为输入。
RetNet
RetNet提出了多尺度保留机制(Multi-Scale Retention, MSR),用于替代 Transformer 中的注意力模块。与线性注意力(linear attention)类似,在 MSR 模块中,input首先被映射为 QKV,然后通过 K 和 V 的乘积来更新状态(state)。接着,使用 Q 对状态进行投影,生成最终输出。
与传统的状态空间模型(SSM)类似,RetNet 同时保留了并行计算能力和递归计算能力。
详细配置(Detailed Configuration)
Transformer 架构中有四个主要部分的相关配置,包括:
-
归一化(Normalization)
-
位置嵌入(Position Embeddings)
-
激活函数(Activation Functions)
-
注意力与偏置(Attention and Bias)
归一化(Normalization)
在预训练大型语言模型(LLMs)的过程中,训练不稳定性是一项重大挑战。为了缓解这一问题,归一化被广泛采用,用于稳定神经网络的训练过程。
在原始版本的 Transformer 中,使用了 LayerNorm(层归一化)。近年来,研究者提出了多种先进的归一化技术,作为 LayerNorm 的替代方案,例如 RMSNorm 和 DeepNorm。
LayerNorm
在早期的研究中,BatchNorm是一种常用的归一化方法。然而,它在处理变长的序列数据以及小批量数据时存在困难。因此,LayerNorm 被引入,用于在每一层执行归一化操作。
LayerNorm 会对每一层的所有激活值(activations)计算其均值和方差,然后通过这些统计量对激活值进行中心化与缩放处理。
LayerNorm ( x ) = x − μ σ 2 + ϵ ⋅ γ + β \text{LayerNorm}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \cdot \gamma + \beta LayerNorm(x)=σ2+ϵx−μ⋅γ+β
其中:
- μ = 1 H ∑ i = 1 H x i \mu = \frac{1}{H} \sum_{i=1}^{H} x_i μ=H1∑i=1Hxi
- σ 2 = 1 H ∑ i = 1 H ( x i − μ ) 2 \sigma^2 = \frac{1}{H} \sum_{i=1}^{H} (x_i - \mu)^2 σ2=H1∑i=1H(xi−μ)2
RMSNorm
为了提升 LayerNorm(LN)的训练速度,研究人员提出了 RMSNorm,该方法不再使用均值和方差,而是仅使用激活值的均方根(RMS, root mean square)来对激活值进行缩放。RMSNorm 在 训练速度和模型性能方面相较于 LayerNorm 更具优势
RMSNorm ( x ) = x 1 H ∑ i = 1 H x i 2 + ϵ ⋅ γ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{H} \sum_{i=1}^{H} x_i^2 + \epsilon}} \cdot \gamma RMSNorm(x)=H1∑i=1Hxi2+ϵx⋅γ
DeepNorm
DeepNorm 用于稳定深层 Transformer 的训练过程。在引入 DeepNorm 作为残差连接(residual connections)后,Transformer 网络的深度可以扩展到 1000 层,并在训练过程中表现出良好的稳定性和性能。
DeepNorm ( x ) = 1 α ⋅ FFN ( x ) + x \text{DeepNorm}(x) = \frac{1}{\alpha} \cdot \text{FFN}(x) + x DeepNorm(x)=α1⋅FFN(x)+x
或用于自注意力层时:
DeepNorm ( x ) = 1 β ⋅ SelfAttention ( x ) + x \text{DeepNorm}(x) = \frac{1}{\beta} \cdot \text{SelfAttention}(x) + x DeepNorm(x)=β1⋅SelfAttention(x)+x
其中常见取值:
- α = ( 2 L ) 1 / 4 \alpha = \left(2L\right)^{1/4} α=(2L)1/4
- β = ( 8 L ) 1 / 4 \beta = \left(8L\right)^{1/4} β=(8L)1/4
L L L 是网络的总层数。
归一化的位置
目前主要有三种归一化位置的选择:
-
Post-LN
-
Pre-LN
-
Sandwich-LN
Post-LN
Post-LN 是原始 Transformer 中采用的归一化位置策略,其归一化操作被放置在每个残差块之后。然而,已有研究发现,使用 Post-LN 的 Transformer 在训练中容易出现不稳定性,尤其在靠近输出层时会出现较大的梯度。
Pre-LN
与 Post-LN 不同,Pre-LN将归一化操作放置在每个子层(如注意力或前馈网络)之前,并在最终预测层之前再加一个额外的归一化层。
Pre-LN 的训练过程更加稳定;但在性能方面有所下降,即便如此,大多数 LLM 仍采用 Pre-LN。
Sandwich-LN
基于 Pre-LN,Sandwich-LN 在每个残差连接之前再额外添加一层归一化,以避免 Transformer 层输出中出现的数值爆炸(value explosion)问题。然而,研究也发现,Sandwich-LN 有时并不能有效稳定训练,甚至可能导致训练过程完全崩溃。
激活函数(Activation Functions)
为了获得良好的模型性能,在前馈神经网络(feed-forward networks, FFN)中合理设置激活函数也非常重要。在现有的大型语言模型(LLMs)中,GeLU 激活函数被广泛采用。在最新的大型语言模型中, SwiGLU 和 GeGLU等GLU的变体被提出,但是它们的参数开销增加。
GeLU (左边是理论公式,右边是实际上的计算方式)
GeLU ( x ) = x ⋅ Φ ( x ) = x ⋅ 1 2 ( 1 + tanh [ 2 π ( x + 0.044715 x 3 ) ] ) \text{GeLU}(x) = x \cdot \Phi(x) = x \cdot \frac{1}{2} \left(1 + \tanh\left[\sqrt{\frac{2}{\pi}} \left(x + 0.044715 x^3\right)\right] \right) GeLU(x)=x⋅Φ(x)=x⋅21(1+tanh[π2(x+0.044715x3)])
SwiGLU
SwiGLU ( x 1 , x 2 ) = SiLU ( x 1 ) ⋅ x 2 = ( x 1 1 + e − x 1 ) ⋅ x 2 \text{SwiGLU}(x_1, x_2) = \text{SiLU}(x_1) \cdot x_2 = \left( \frac{x_1}{1 + e^{-x_1}} \right) \cdot x_2 SwiGLU(x1,x2)=SiLU(x1)⋅x2=(1+e−x1x1)⋅x2
GeGLU
GeGLU ( x 1 , x 2 ) = GeLU ( x 1 ) ⋅ x 2 \text{GeGLU}(x_1, x_2) = \text{GeLU}(x_1) \cdot x_2 GeGLU(x1,x2)=GeLU(x1)⋅x2
位置编码(Position Embeddings)
由于 Transformer 中的 自注意力机制具有置换等变性(permutation equivariance),即不区分输入 token 的顺序,因此需要使用 位置编码(PE) 来注入绝对或相对的位置信息,以实现序列建模。
绝对位置编码(Absolute Position Embedding)
在原始 Transformer 中 [22],使用了绝对位置编码。在编码器和解码器的底层,将位置编码直接**加到输入嵌入(input embedding)**上。原始论文中提出了两种形式:
-
正弦函数位置编码(sinusoidal)
-
可学习位置编码(learned)
其中,可学习的位置编码目前被大多数预训练语言模型所采用。
相对位置编码(Relative Position Embedding)
与绝对位置编码不同,相对位置编码是根据 query 和 key 之间的偏移量(offset)生成的。相比绝对位置编码,相对位置编码支持长度外推(extrapolation),即可以推广到比训练序列更长的输入。
旋转位置编码(Rotary Position Embedding, RoPE)
RoPE是一种基于每个 key 或 query 的绝对位置设定的旋转矩阵方式。它可以使 key 和 query 之间的注意力得分具备相对位置信息。
RoPE 的关键思想是:将 query 和 key 向量中相邻的一对元素组合成一个二维向量,并作为一个“维度”处理。因此,一个原始长度为 d 的嵌入向量将变为 d/2 个二维旋转维度。
对于每个维度 i ∈ { 1 , … , d / 2 } i \in \{1, \dots, d/2\} i∈{1,…,d/2},RoPE 会按照如下方式进行旋转:
-
旋转角为: t ⋅ θ i t \cdot \theta_i t⋅θi,其中:
- t t t:表示 token 的绝对位置索引;
- θ i \theta_i θi:表示该维度的基频(频率基准)。
这种角度的设定来源于原始 Transformer 中的 正弦位置编码 ,具体定义为:
θ i = b − 2 ( i − 1 ) / d , 其中默认 b = 10000 。 \theta_i = b^{-2(i - 1)/d}, \quad \text{其中默认 } b = 10000。 θi=b−2(i−1)/d,其中默认 b=10000。
此外,近期研究将完成一个完整旋转( 2 π 2\pi 2π)所需的位置距离称为波长(wavelength),即:
λ i = 2 π θ i = 2 π b 2 ( i − 1 ) / d \lambda_i = \frac{2\pi}{\theta_i} = 2\pi b^{2(i - 1)/d} λi=θi2π=2πb2(i−1)/d
由于其出色的性能和长距离衰减特性(long-term decay property),RoPE 被广泛应用于当前最先进的大型语言模型中。
基于 RoPE,xPos 进一步改进了 Transformer 的平移不变性(translation invariance)和长度外推能力(length extrapolation)。它在每个旋转角度的维度上加入了一个特殊的指数衰减因子,当基频越大时,该衰减越小,从而缓解了距离增加带来的训练不稳定问题。
ALiBi
ALiBi 被提出用于提升 Transformer 的长度外推能力(extrapolation)。它与相对位置编码类似,通过引入基于 key 和 query 之间距离的惩罚项(penalty) 来对注意力分数进行偏置处理。
但与像 T5 那样的相对位置编码方法不同,ALiBi 中的偏置分数是预定义的、没有任何可训练参数。ALiBi 在处理超出训练长度的长序列任务中,表现出了优于多种流行位置编码方法的外推性能。
注意力机制(Attention)
注意力机制是 Transformer 架构中的一个关键组件。它使序列中的各个 token 能够彼此交互,从而计算出输入序列和输出序列的表示向量(represen tation)。
全注意力机制(Full Attention)
在原始的 Transformer 中,注意力机制以两两配对的方式(pairwise)进行,考虑序列中所有 token 对之间的关系。它采用的是缩放点积注意力(scaled dot-product attention),即将隐藏状态分别映射为查询(query)、键(key)和值(value)。
此外,Transformer 使用多头注意力(multi-head attention) 替代了单一注意力机制。具体做法是:在不同的“头”中,对 query、key 和 value 分别进行不同的线性投影;然后将每个头的输出进行拼接,作为最终的注意力输出。
稀疏注意力(Sparse Attention)
全注意力机制面临的一个关键挑战是其计算复杂度为平方级,这在处理长序列时会成为严重的计算负担。研究人员提出了多种高效 Transformer 变体,以降低注意力机制的计算复杂度。
GPT-3用了局部带状稀疏注意力(locally banded sparse attention),在这种机制下,每个query仅关注序列中的一部分 token(而非全部),这个子集是基于位置规则选定的。
多查询注意力与分组查询注意力(Multi-query / Grouped-query Attention)
多查询注意力(Multi-query Attention) 是一种注意力机制变体,其特点是:不同的注意力头共享相同的 key 和 value 的线性变换矩阵。这种设计可以在仅略微牺牲模型质量的前提下显著提高推理速度。
为在 多查询注意力 与 多头注意力(Multi-head Attention) 之间进行折中,研究者提出了 分组查询注意力(Grouped-query Attention, GQA)。在 GQA 中,所有注意力头被划分为多个组,每组中的头共享同一套变换矩阵(key 和 value 的线性映射)。大模型的Llama 2用了这个注意力。
FlashAttention
FlashAttention 提出了一种从面向 IO 的角度(IO-aware)优化 GPU 上注意力模块速度和内存消耗的方法。FlashAttention 将输入组织成小块(blocks),并引入必要的重计算(recomputation),从而更充分地利用快速的 SRAM。
FlashAttention 被实现为 CUDA 中的融合内核(fused kernel),并已被集成到主流深度学习框架中。
PagedAttention
在将大型语言模型(LLMs)部署到服务器上时,有研究发现:GPU 内存很大一部分被缓存的 attention key 和 value 张量(KV 缓存)占用。造成这一问题的主要原因是:输入序列的长度不一致,从而导致 内存碎片 和 过度预留(over-reservation)。
受操作系统中经典的分页技术(paging)启发,研究者提出了 PagedAttention,它将每个序列划分为多个子序列(subsequences),并将这些子序列对应的 KV 缓存分配到非连续的物理内存块中。
总结
为了获得更强的泛化能力和训练稳定性,建议使用:RMSNorm(前置形式 pre-RMSNorm) 作为层归一化方法;SwiGLU 或 GeGLU 作为激活函数。
在嵌入层(embedding layer)之后不建议立即使用 LayerNorm,因为这可能会导致性能下降。
关于位置编码方面,RoPE 或 ALiBi 是更佳选择,因为它们在处理长序列时表现更好。
预训练任务(Pre-training Tasks)
训练LLM 时,主要有两类常用的预训练任务:语言建模(Language Modeling)和去噪自编码(Denoising Autoencoding)
语言建模(Language Modeling)
语言建模任务(LM 是用于预训练仅使用解码器(decoder-only)结构的 LLMs中最常见的训练目标。
给定一个 token 序列:
x = { x 1 , x 2 , … , x n } x = \{x_1, x_2, \ldots, x_n\} x={x1,x2,…,xn}
语言建模的目标是以 自回归方式(autoregressively) 预测每个目标 token x i x_i xi,条件是它前面的所有 token x < i x_{<i} x<i。
一般的训练目标是最大化以下似然函数(likelihood):
L LM = ∑ i = 1 n log P ( x i ∣ x < i ) \mathcal{L}_{\text{LM}} = \sum_{i=1}^n \log P(x_i \mid x_{<i}) LLM=i=1∑nlogP(xi∣x<i)
decoder-only 的 LLM 无需微调,就可以通过自回归方式预测下一个 token 自然地迁移到某些下游任务。
语言建模任务的一个重要变体是:前缀语言建模(Prefix Language Modeling),在这种设置中,随机选取的前缀 token 不会被用来计算 loss。也就是说,虽然模型看到的是完整的序列,但 loss 只对 prefix 之后的部分计算,效果略逊于标准语言建模。
去噪自编码(Denoising Autoencoding, DAE)
除了传统的语言建模任务(LM),去噪自编码任务(DAE)也被广泛用于语言模型的预训练。
在 DAE 任务中,输入 x ~ \tilde{x} x~ 是经过破坏的文本,通常表现为随机替换或遮蔽的一段文本(span)。然后,语言模型的目标是恢复原始被替换的 token(记作 x)。
形式化地,DAE 的训练目标可表示为:
L DAE ( x ) = log P ( x ~ ∣ x ∖ x ~ ) \mathcal{L}_{\text{DAE}}(x) = \log P(\tilde{x} \mid x \setminus \tilde{x}) LDAE(x)=logP(x~∣x∖x~)
然而,与语言建模任务相比,DAE 的实现更复杂。因此,它尚未被广泛应用于大规模语言模型的预训练。
混合去噪器(Mixture-of-Denoisers, MoD)
混合去噪器(MoD),也被称为 UL2 损失函数(UL2 loss),是一种用于预训练语言模型的统一目标函数。
MoD 将 语言建模(LM) 和 去噪自编码(DAE) 看作是不同类型的去噪任务,主要包括以下三种:
-
S-denoiser:相当于传统的语言建模任务(LM)
-
R-denoiser:类似 DAE,采用较短的遮蔽片段和较低的破坏率
-
X-denoiser:也是 DAE,但使用较长的遮蔽片段或更高的破坏率
这三种 denoiser 的主要差异在于:
-
遮蔽 span 的长度
-
被破坏文本的比例
在输入句子开头加入不同的特殊标记(例如:[R]、[S]、[X])后,模型会使用对应的去噪器任务进行优化。
解码策略(Decoding Strategy)
背景(Background)
解码主要有两个方向:贪心搜索(greedy search)和基于采样的方法(sampling-based methods)。
由于这类大型语言模型(LLMs)是基于语言建模任务进行预训练的,其最基本的解码方法是 贪心搜索(greedy search)。该方法在生成的每一步,都会选择概率最高的 token。形式化表达如下:
x i = arg max x P ( x ∣ x < i ) x_i = \arg\max_x P(x \mid x_{<i}) xi=argxmaxP(x∣x<i)
其中, x i x_i xi 表示在第 i i i 步生成中,在前文上下文 x < i x_{<i} x<i的条件下,概率最大的 token。
贪心搜索在某些文本生成任务中能达到不错的效果,例如机器翻译和文本摘要,这类任务的输出较为依赖输入内容。然而,在更开放式的生成任务中(如故事创作或对话生成),贪心搜索可能会导致生硬或重复的句子。
另一种可选的解码策略是 基于采样的方法(sampling-based methods),这类方法会根据当前的概率分布,随机选择下一个 token,以提升生成内容的随机性和多样性:
x i ∼ P ( x ∣ x < i ) x_i \sim P(x \mid x_{<i}) xi∼P(x∣x<i)
贪心搜索的改进
在每一步都选择概率最高的 token,虽然简便高效,但可能会忽略整体概率更高但局部概率较低的句子。
接下来,我们将介绍几种改进策略,以缓解这个问题。
Beam Search
在解码过程中,会在每一步保留 前 n 个概率最高的候选序列(n 称为 beam size),并在最后从中选择整体概率最高的生成结果作为输出。通常,beam size 的取值范围在 3 到 6 之间。但如果设置过大的 beam size,反而可能会导致模型性能的下降。
长度惩罚(Length Penalty)
由于Beam Search倾向于生成较短的句子,因此引入长度惩罚(又称长度归一化)是一种常用的解决策略。
该方法会根据句子的长度对其概率进行归一化,通常是除以句子长度的一个指数函数(length 的 α 次方),以此降低短句的偏好性。
score = log P ( x ) ( 5 + ∣ x ∣ ) α / ( 5 + 1 ) α \text{score} = \frac{\log P(x)}{(5 + |x|)^\alpha / (5 + 1)^\alpha} score=(5+∣x∣)α/(5+1)αlogP(x)
其中 ∣ x ∣ |x| ∣x∣ 是句子长度, α \alpha α 控制惩罚强度。
对随机采样的改进(Improvement for Random Sampling)
基于采样的方法会从整个词汇表中进行随机选择,因此可能会生成错误或不相关的词语。为了提升生成质量,研究人员提出了多种策略,用于减少或避免选择那些概率过低的词语。这里说几个比较有名的sampling。
温度采样(Temperature Sampling)
为了调节采样的随机性,一个常用的方法是调整 Softmax 函数中的 温度系数,用于计算词汇表中第 j 个 token 的概率:
P ( x j ∣ x < i ) = exp ( l j / t ) ∑ j ′ exp ( l j ′ / t ) P(x_j | x_{<i}) = \frac{\exp(l_j / t)}{\sum_{j'} \exp(l_{j'} / t)} P(xj∣x<i)=∑j′exp(lj′/t)exp(lj/t)
其中, l j l_j lj 是第 j 个词的 logits 值, t t t 是温度系数。
- 降低温度 t t t:会提升高概率词的选择概率,抑制低概率词。
- 当 t = 1 t = 1 t=1:就是默认的随机采样。
- 当 t → 0 t \to 0 t→0:接近于贪心搜索(始终选最可能的词)。
- 当 t → ∞ t \to \infty t→∞:趋近于均匀采样(所有词等概率选)。
Top-k 采样(Top-k Sampling)
与温度采样不同,Top-k 采样会直接截断低概率的 token,仅从概率最高的前 k 个 token 中采样。
Top-p 采样(Top-p Sampling)
由于 Top-k 采样 不考虑整体概率分布,固定的 k 值可能不适用于所有上下文。因此,提出了 Top-p 采样(又称 nucleus sampling,核采样),其方法是从累计概率大于(或等于)p 的最小 token 集合中进行采样。
如何计算累计概率呢?这个最小集合是这样构建的:
- 将词汇表中所有 token 按生成概率 从高到低排序;
- 然后依次加入 token,直到它们的累计概率值超过设定的阈值 p。
实际应用设置
现有的库(如 Hugging Face 的 Transformers [201])和公开的 LLM 接口(如 OpenAI)已支持多种解码策略。下面是一些实例:
- T5:默认使用贪心搜索(greedy search),并在翻译和摘要任务中使用beam search,束宽度为 4,长度惩罚为 0.6。
- GPT-3:对所有生成任务都使用束搜索,束宽度为 4,长度惩罚为 0.6。
- Alpaca:使用基于采样的策略,包括 top-k(k = 50)、top-p(p = 0.9)、温度设置为 0.7,适用于开放式生成任务。
- LLaMA:根据具体任务采用不同的解码策略。例如:在问答任务中使用贪心搜索;在代码生成任务中,根据评估指标 pass@1 设置温度为 0.1,pass@100 设置为 0.8。
总结
架构和预训练任务的选择可能会引入不同的归纳偏差(inductive biases),从而导致大语言模型(LLMs)具有不同的模型能力。接下来我们只讲第一个架构选择的问题。
目前主流大语言模型(LLMs)普遍采用因果解码器架构(causal decoder architecture)。然而,该架构相比其他方案的理论优势仍缺乏系统论证。
研究表明,基于语言建模(LM)目标的预训练使因果解码器展现出优异的零样本(zero-shot)和小样本(few-shot)泛化能力。
该架构还展现出显著的缩放效应(scaling law):
- 模型规模扩大
- 训练数据量增加
- 计算资源提升
都能显著改善模型性能。这使得"规模化扩展"成为增强因果解码器能力的关键路径。
训练模型
优化设置
batch size
在语言模型的预训练中,现有研究通常将batch size设为一个较大的数值(例如 2,048 个样本或 400 万个 token),以提高训练稳定性和吞吐量。
对于像 GPT-3 和 PaLM 这样的大型语言模型,它们引入了一种新的策略:在训练过程中动态地增加 batch size,最终达到百万级别的 token。这种动态调整 batch size 的方法可以有效提升 LLMs 的训练稳定性。
学习率(Learning Rate)
现有的大型语言模型(LLMs)通常采用类似的学习率调整策略,即预热(warm-up)+ 衰减(decay)方式进行预训练。
具体来说,在训练的前 0.1% 到 0.5% 步骤中,使用线性预热(linear warm-up)策略,逐步将学习率增加到最大值,该最大值通常在 5 × 1 0 − 5 5×10⁻⁵ 5×10−5 到 1 × 1 0 − 4 1×10⁻⁴ 1×10−4 之间。
随后,采用余弦衰减(cosine decay)策略,在接下来的训练中逐渐将学习率减少到最大值的约 10%,直到训练损失收敛为止。
优化器(Optimizer)
在训练大型语言模型(LLMs)时,Adam 优化器和 AdamW 优化器被广泛使用。还有一种名为 Adafactor 优化器的变体,也被应用于一些 LLM(如 PaLM 和 T5)的训练中,专门设计用于节省训练时的 GPU 显存。
稳定训练过程
在大型语言模型(LLM)的预训练过程中,常常会出现训练不稳定的问题,这可能导致模型崩溃(collapse)。研究中用权重衰减(weight decay)和梯度裁剪(gradient clipping)的手段解决了问题。
但是模型的扩大导致,训练过程中出现loss spike(损失函数突变)的概率也显著提高,从而引发训练不稳定。目前有两个方法:
- 在 loss spike 发生前从较早的检查点(checkpoint)恢复训练,并跳过可能引发问题的数据。
- 缩小 embedding 层的梯度值。
可扩展训练技术(Scalable Training Techniques)
训练大型语言模型(LLM)在扩展性方面需要解决以下两个核心技术问题:
-
提升训练吞吐量(throughput)
-
将更大的模型加载进 GPU 显存(memory)中
本节介绍3D并行(3D Parallelism)和混合精度训练(Mixed Precision Training)的策略。
3D并行
三维并行(3D Parallelism)实质上是将三种常见的并行训练技术组合在一起的方式,具体包括:
-
数据并行(Data Parallelism)
-
流水线并行(Pipeline Parallelism)
-
张量并行(Tensor Parallelism)
数据并行(Data Parallelism)
数据并行是一种最基础的提升训练吞吐量的方法。它会将模型参数和优化器状态复制到多个 GPU 上,并将整个训练语料分配到这些 GPU 中。这样,每个 GPU 只需处理分配给它的数据,并执行前向传播和反向传播来计算梯度。
流水线并行(Pipeline Parallelism)
流水线并行的目标是将大型语言模型(LLM)中的不同层分布到多个 GPU 上,以减少在 GPU 之间传输计算得到的隐藏状态或梯度的成本。
然而,简单的流水线并行实现会导致 GPU 利用率较低,因为每个 GPU 必须等待前一个 GPU 完成计算,导致所谓的“气泡开销(bubble overhead)”。
一些改进方案包含:
-
填充多个数据批次(padding multiple batches of data)
-
异步梯度更新(asynchronous gradient update)
这些都用于提高流水线的效率。
张量并行(Tensor Parallelism)
张量并行(Tensor Parallelism)旨在将大型语言模型(LLM)拆分后加载到多个 GPU 上。与流水线并行不同,张量并行专注于拆解模型中的张量(即参数矩阵)。类似于线性代数的矩阵的分块。最后通过GPU 间通信合并两个输出,从而得到最终结果。
混合精度训练(Mixed Precision Training)
在早期的预训练语言模型中,通常使用 32 位浮点数进行预训练。但近年来,为了训练超大规模的语言模型,一些研究开始使用 16 位浮点数(FP16),以减少内存使用和通信开销。
不过,16 位浮点数会导致一定的计算精度损失,从而影响模型的最终表现。为缓解这一问题,一种称为 Brain Floating Point(BF16) 的替代格式被引入。BF16 相比 FP16,分配了更多的指数位,较少的有效位,因此在表示精度上比 FP16 更好,适用于预训练大型语言模型。
1840

被折叠的 条评论
为什么被折叠?



