LLM 基本概念
AI 学习笔记系列的第四篇,介绍 LLM 相关的基本概念,包括 Transformer 架构、语言模型等内容。
Transformer
Transformer 架构是现代 LLM 的基础架构,最初提出时是 Encoder-Decoder 结构,后续常见变体包括 Encoder-Only 和 Decoder-Only。
Transformer Encoder
之前 NLP 文章提到,我们还需要加入**位置编码(Positional Encoding)**来让模型知道输入 token 的位置信息。 旋转位置编码(RoPE, Rotary Position Embedding)是一种位置编码方法,把相对位置信息以旋转的形式注入到 $Q$ / $K$(常见实现)中,让注意力里自然出现相对位移相关性。
**多头注意力(Multi-Head Attention)**允许模型在不同子空间中并行地关注输入的不同部分,从而捕捉更多样化的关系和特征。 有了多头注意力,每个头可捕捉输入不同方面,例如句法关系与语义关系(比如可以 Head 1 关注实体,Head 2 关注句法,由于这部分也是学习来的,所以实际每个头关注的东西不一定有明确的语义解释)。
前馈层(FFN,Feed-Forward Network)是一个两层的全连接网络,通常在每个注意力层之后使用,它通过非线性变换进一步处理每个 token 的表示,增强模型的表达能力。
残差(Residuals)的概念来自于计算机视觉,指把某层计算输出与其输入相加。 残差连接通过直接将输入添加到输出的方式,让梯度可更容易回传,鼓励模型学习接近恒等映射,让权重有自由学习非恒等信息,梯度更平滑,从而提升训练速度与稳定性。
**层归一化(Layer Normalization)**通过平移缩放改善训练,它取一个向量,计算其均值和标准差,定义两个可学习权重向量(或固定 1 与 0),重缩放该向量,从而使得模型在训练过程中更稳定。 缩放点积注意力是因为维度增大时,点积值变大,点积更大意味着 softmax 后梯度更小。
以上这些构成了 Transformer Encoder(实际实现还包含 dropout 等技术)。
BERT 就是一个典型的 Transformer Encoder 模型,典型的 BERT 使用了 WordPiece 分词,结合 Transformer 架构,用大量数据训练,是一个高性能的模型。
Transformer Decoder
使用因果掩码(Masked Self-Attention)来防止模型在生成时偷看未来 token,再通过注意力引入 Encoder 状态(可选),然后加 FFN 并做层归一化,应用 softmax 决定输出,这就是 Transformer Decoder 的核心结构。
也可从残差流(Residual Stream)的视角理解:Decoder 在每层的残差流中引入了一个新的注意力模块(交叉注意力),它允许模型在生成过程中关注编码器的输出,从而更好地利用输入信息来生成相关的输出。
Transformer Decoder 与 RNN Encoder-Decoder 两者都可用于 Seq2Seq(序列到序列)生成,也可用来产生上下文化词表示并输入到其他模型。
GPT 是一个典型的 Transformer Decoder 模型,典型的 GPT 使用了 BPE 分词,结合 Transformer 架构,用大量数据训练,也是一个高性能的模型。
补充说明
Encoder 中多头注意力是自注意力(Self-Attention),而 Decoder 里既有自注意力,也有交叉注意力(Cross-Attention)。
自注意力和交叉注意力有相同的相似度计算方式和权重使用方式。 但前者是输入序列内部的注意力,后者是对不同序列之间的注意力,即他们的输出的使用方式不同和 $K$ / $V$ / $Q$ 的来源不同。
备注:$K$ / $V$ / $Q$ 矩阵即 Key / Value / Query,它们通过不同线性投影把同一输入映射到查询 / 键 / 值空间,使注意力能用 $Q \cdot K$ 计算权重,再对 $V$ 做加权求和。
Decoder 和 Encoder 的自注意力也不同。 Decoder 的自注意力只能看当前 token 左边,而 Encoder 的每个 token 都可与全序列其他 token 做自注意力。 原因是 Decoder 按从左到右生成,在处理第 $N$ 个输入时,不知道 $N+1, N+2, \ldots$。
Transformer 的前馈层和自注意力一定包含训练中学习的权重矩阵,层归一化有时也有可训练参数,但不是矩阵。
Transformer 的残差连接和层归一化有助于训练更平滑。
工程实践
训练速度更快通常意味着更好的工程可扩展性:在算力、数据量和参数规模增大时,系统仍能保持较高吞吐和较好的并行效率。 但两者不完全等价,单机/单卡更快,不一定代表多机分布式也能线性扩展,还要看通信、并行策略与收敛稳定性等因素。
这也引出缩放定律(Scaling Laws):它们是基于大量实验总结出的经验规律,描述模型效果(如损失)如何随参数规模、数据规模与计算量近似按幂律变化。 利用这些规律,在给定训练预算和数据量的约束下,可以进一步推导或拟合出更接近最优的模型规模与数据配比。
标准注意力在序列长度 $L$ 上通常有 $O(L^2)$ 的时间/显存开销,因此长上下文时可能成为瓶颈。 为此有不少变体尝试降低对 $L^2$ 的依赖:例如 Performer 用随机特征近似实现线性注意力,Longformer 用稀疏 / 局部窗口(可带少量全局 token)减少计算,Linformer 用低秩投影近似压缩注意力矩阵,从而在长序列上更省算力和显存。
不过在工程实践中,理论复杂度只是一个维度,显存容量和尤其是内存带宽往往同样关键。 注意力需要频繁读写大张量和中间结果,很多时候瓶颈不在算力(FLOPs),而在数据搬运速度,导致计算单元无法被充分利用。 FlashAttention 的显著改进点在于通过分块与算子融合等方式重排计算 / 访存,减少中间张量写回显存的次数,从而大幅降低对内存带宽的压力并提升吞吐(它主要优化常数与带宽开销,不改变 $O(L^2)$ 的数量级)。
此外,在常见的上下文长度与标准 Dense Transformer(每一层里所有参数对每个 token 都全量参与计算)设置下,随着模型变大,前馈网络(FFN)往往会成为主要的计算开销来源。 因此也有大量针对 FFN 的优化工作,例如混合专家(MoE,Mixture of Experts)在每个 token 只激活少量专家,以较低的每 token 计算量换取更大的模型容量,但它也可能带来路由、通信与额外参数带宽 / 显存方面的工程权衡。
对于下面的任务,最常见的模型类型如下:
- 情感分析(分类):Encoder;Decoder;Encoder-Decoder。
- NER(Token 级预测):Encoder;Decoder。
- Coreference(结构化输出):Encoder-Decoder(其优势之一是输入输出性质可差异很大)。
- 摘要(生成):Decoder。
- 翻译(跨语言生成):Decoder。
语言模型(LM)
语言模型(Language Model,LM)是一个概率分布模型,它给定一个字符串,输出一个概率值,表示该字符串在语言中的合理程度,可以用于预测下一个词。
N-gram 语言模型基于词序列计数来估计概率,可以用下面几种方式来计算分数 / 概率:
- 用大查表:把所有可能的 n-gram 组合都存下来,查询时直接查表。
- 用链式法则拆分计算:将联合概率分解为条件概率的乘积。
- 用近似:根据马尔可夫(Markov)假设,假设一个词的出现只与前面 $n - 1$ 个词有关,从而把条件概率简化为 $p(x_t \mid x_{t-n+1}, \ldots, x_{t-1})$。
这种近似(Markov 假设)方法很强,但无法捕捉长距离依赖。
实践中可采用较弱假设,即避免把 n 设得很高,因为稀疏性(序列越长,概率越难准确估计)和存储复杂度(要存的可能性太多)都会成为问题。
我们常用对数概率避免数值问题,避免概率连乘导致下溢,改在 log 空间做加法。
简单来说,语言模型输入字符串,输出分数。 N-gram 通过 Markov 假设和序列计数来实现,需要处理低频词、序列起止标记和小概率问题。 通过统计数据中的 n-gram 计数,再通过一个计数除以另一个计数即可得到概率。
既然语言模型只是计算一个字符串的概率 / 分数,那我们如何用语言模型来完成其他任务呢? 以新闻分类为例,我们可以把分类标签(如体育、娱乐、科技)和新闻文本拼接成一个字符串(比如在新闻后加上一句“该新闻的主题是科技”),然后让语言模型计算这个字符串的概率,最后选择概率最高的标签作为分类结果。
嵌入(Embedding)
我们在 NLP 文章中就提到了嵌入,但没有深入展开。
嵌入是一种先进的文本表示方法,它把离散的符号(如词、字符)转换成连续的向量表示。 它不仅仅可以用于文本的表示,还可以用于其他类型的数据,比如图像、音频等等,它们都可以通过嵌入来转换成向量的形式,以便于模型进行计算和预测。 能理解图像、音频等非文本数据的 LLM 也被称为多模态模型(Multimodal Model),它们通常通过把图像 / 音频编码成向量,再与文本表示对齐 / 融合,具有更强的表达能力和应用场景。
历史上最常见的就是词嵌入(Word Embedding),它把每个词转换成一个向量(Vector)。 这些向量可以有很多的维度(Dimension),从几百到上千维,这些维度可以捕捉到词之间的语义关系,比如相似的词会有相似的向量表示。 这些维度不一定有明确的现实意义(人类也不一定能理解模型认为的相似),它们是通过训练数据自动学习出来的,在训练时模型会根据上下文来调整这些向量的值,使得它们能够更好地计算下一个词的概率分布。
除了词嵌入,还有其他的文本表示方法,比如字符嵌入(Character Embedding)和子词嵌入(Subword Embedding),它们分别把文本分解成更小的单位来进行表示,这样可以更好地处理一些罕见的词或者新词。
大语言模型(LLM)
大语言模型(Large Language Model,LLM)和 LM 的区别在于它的规模更大,通常有数十亿到数千亿的参数。
LLM 做的事情其实很简单,它根据前面的文本(Context,上下文)来预测下一个最可能出现的词(更准确的说法是词元,token,见后文),即根据输入的值来计算输出的概率分布,然后从中选择一个词作为输出。 通过不断地预测和生成文本,LLM 可以完成各种各样的任务,比如写文章、回答问题、翻译语言等等。
LLM 到底在学什么? LLM 学的是:给定已有文本(上下文),下一个 token 出现的概率分布,可以写成 $p(x_t \mid x_{<t})$。
生成文本就是一个不断重复的过程:
- 看上下文
- 给出下一步的概率分布
- 选一个 token 接上去
- 回到第一步,继续预测下一个 token
模型会先给每个候选 token 一个分数(Logits),再用 softmax 变成概率。 最后用不同策略选 token(确定性的贪婪算法或者带随机性的采样算法):top-1、top-k、top-p 等等。
**温度(Temperature)**是一个调节采样随机性的参数,值越高,输出越随机;值越低,输出越确定。
这也解释了为什么同一 Prompt 可能得到不同回答:通常是采样策略带来的。
备注:即使是同一个策略,固定了随机种子(Random Seed)也不一定能得到完全一样的结果,因为模型的计算可能涉及到并行处理和非确定性的操作(比如 GPU 上的浮点运算),这也是为什么在实际使用中,LLM 的输出可能会有一些微小的差异。
简单概括从输入到输出的流程:
- 用户输入文本(Prompt)
- 模型把文本分成 token(Tokenization)
- 模型把 token 转成向量(Embedding)
- 模型根据上下文计算下一个 token 的概率分布
- 模型根据策略选一个 token 输出
- 模型把输出的 token 转成文本(Detokenization)
- 输出文本给用户
