最近在补深度学习基础。说来惭愧,LLM 用了这么久,我一直都没有想着去搞清楚原理,只是理所当然地把这个东西当成了现成的工具。其实本身也没有什么错,但是学完以后发现差点错过了这么美妙的发明,这玩意非常值得一学。

这篇笔记主要是结合网上资料和 LLM 的对话,再经过笔者的思考总结整理而成。


概述

阅读后面的内容之前,请记住一句话—— LLM只做一件事情,就是预测下一个词的概率分布。 这是本篇文章的核心。

目前市面上的绝大多数 LLM,底层都是 Decoder-Only 架构,而要理解这个架构,就先要从 Transformer 和 Attention 机制说起。

Transformer 是 2017 年 Google 在论文《Attention Is All You Need》中提出的一种深度学习架构。它彻底抛弃了传统的卷积神经网络和循环神经网络,仅依赖 Self-Attention(自注意力)机制构建,成为了现代大语言模型的基石。

在 Transformer 出现之前,处理序列数据(文本、音频)等主要依赖 RNN,但存在无法并行和长距离依赖消失的问题。Transformer 解决了这两个问题,可以同时处理序列中所有 token,并且通过自注意力机制,让每个 token 都能”注意到“其他所有的 token,进而解决了长距离依赖问题。

Transformer 结构

标准的 Transformer 采用 Encoder-Decoder 结构,主要用于序列到序列(Seq2Seq)的任务,最开始这个架构是为了处理机器翻译任务而诞生的。但后来人们发现这个架构有很好的扩展性,再加上算力慢慢充足,GPT-3 横空出世,才有了现在的一切。(好像以前就写过类似的东西,详情参见 https://blog.yukiip.top/post/89dfd466.html

这里把原论文的架构图贴出来。

如图,整体分为两部分,Encoder编码器和Decoder解码器。

编码器将输入序列编码成一组富含上下文信息的向量,由 $N$ 个相同的层构成,每一层包含多头自注意力模块和多层感知机(也叫前馈网络)模块,以及进入每个模块前的层归一化和之后的残差连接。

解码器根据编码器的“记忆”和已生成的部分输出,自回归地生成目标序列,同样由 $N$ 个相同的层构成,每一层包含掩码多头自注意力、多头注意力、多层感知机,层归一化和残差连接。

当然,输入时还有词嵌入、位置编码,输出时还有softmax归一化得到概率分布的步骤。

这篇文章的重点在于LLM,所以会着重讲Decoder的部分,而Encoder是类似的。

输入数据

我们看到输入的数据会先经过Embedding词嵌入模块,之后进行Positional Encoding位置编码,再发送到内部层中。

讲词嵌入之前先要明确 token 的概念。token 就是模型处理的最小单元,可以近似理解为单字或者短语。当然实际程序里,根据分词器的不同,token的拆分可能略有出入,但这不影响我们的理解。对于一段长文本,整体而言是不好处理的,把整体细分成小部件才好操作。所以词嵌入过程前隐藏了一步分词,即文本读入后会先被分词器拆分成若干token的序列。

词嵌入可以理解为token到某个高维语义空间的映射。输入一个token的ID(例如苹果对应 $1145$),就可以得到一个高维的向量(例如 $[0.12, -0.45, \dots,0.86]$)。这个语义空间的维度不定,几千到几万不等,每个维度的含义基本上不太能用人类的方式去理解,是模型通过大量的训练而学习到的一些特征,我们可以近似认为某个方向对应颜色,某个方向对应情绪,等等,总之这个高维空间包含了极多信息。

至于位置编码,是因为注意力机制本身不具备位置信息(我们说它是置换不变的,即打乱输入序列的顺序,自注意力模块的输出向量集合不变,只是顺序跟着打乱),因此我们要显式地注入序列的顺序信息。论文中使用的方法是通过正弦和余弦函数生成一组固定的位置向量,与高维向量相加。

Attention 机制

到了我们万众瞩目的注意力机制。先下个结论,注意力机制并没有什么高深的东西,本质上就是在做矩阵乘法,模型学到的所有信息都保存在了矩阵的参数里。

我们要理解一件事,词嵌入的过程本质上是在一一映射,即一个token只会被唯一映射到某一个向量上。但是,一个词语根据上下文的不同,显然会包含不同的语义。例如“球飞了过来”,这个球根据上下文的不同,可能是篮球、足球、排球、网球等等。我们说过了,LLM或者说Decoder只做一件事情,就是预测下一个词的概率分布。为了提高预测的准确度,模型需要更加精确地理解每个词乃至整篇文章的语义。高维语义向量空间中,某一个方向可能都代表着一些语义,那么我们为了让词嵌入后的初始向量指向其在原文中所含语义的方向,就需要给这个向量加一个变化量。注意力模块就是在求这个向量变化量,就是在进行动态的语义附加与修正。还是刚刚球的例子,初始情况下球的向量和篮球、足球、排球对应的向量都不同,我们需要求出一个具体的变化量,把语义附加给原token。

一个注意力模块内只有三个矩阵:$W_Q$、$W_K$、$W_V$,这是三个可学习的线性变换矩阵,它们分别与上下文窗口内的token序列对应的向量组成的矩阵相乘,得到矩阵 $Q$、$K$、$V$。这三个矩阵分别有什么作用?$Q$ 是 Query,某一列代表着对应的token向上下文窗口内其他token的询问,查询其他token是否和这个词存在某种关联。$K$ 是 Key,某一列代表着对应token包含的语义信息的某些特征,如果Query的某个列向量和Key的某个列向量的余弦相似度高(夹角小),说明询问的某种关联在Key列向量对应的那个token里有所体现,其点积就会比较大。我们直接把 $Q$ 和 $K$ 的转置相乘,这样得到的矩阵的每个位置就对应着两个token之间的某种语义关联。至于 $V$ ,是 Value,某一列代表着对应token包含的语义信息的某些特征对应的具体内容。我们把 $K^TQ$ 矩阵进行 softmax 归一化,再把某一列取出来,得到一组权值,每个权值与 $V$ 对应那一列的每个位置相乘,就能得到一个新的向量,这个向量就代表着该注意力模块结合预训练的语义理解和上下文的具体语义对原向量的语义修正。

这里为了讲解注意力机制,简化了一个小细节。对 $K^TQ$ 矩阵进行 softmax 归一化前,实际上要对每个元素除以 $\sqrt{d_k}$,其中 $d_k$ 为高维语义空间的维度数。 这一步是为了防止注意力分数出现极端值,当维度数很大时,点积结果方差会很大(可以证明其方差为 $d_k$),再经过 softmax ,极大值接近 $1$ ,极小值接近 $0$ ,梯度会趋于 $0$ ,即出现梯度消失的现象。除以 $\sqrt{d_k}$ 可以把方差控制为 $1$ ,进行标准化,防止出现梯度消失,同时还丢失了信息的多样性。

下面是原论文的示意图。

可以看到,图片里的大部分我们都已经讲到了。MatMul 和 Softmax 自不必说,Scale 就是除以 $\sqrt{d_k}$ 进行缩放,只剩下一个 Mask。这是什么?

简单来说,这是训练模型时保证自回归性质的一种手段。为了提高训练效率,一种可行的办法是,同时对上下文窗口内所有合法前缀进行下一个token的预测,这样一个训练样本就能提供多次训练机会。

这里借用3b1b的图片来演示。

但是,注意力机制会让一个token同时注意到全文的语义信息,也就是说这个token也能获得后方所有token的信息,这就和我们的训练目的背道而驰了。为了防止模型简单的抄答案,我们需要一种手段让每个token看不到其后方的token。具体而言,我们把 $K^TQ$ 矩阵的下三角全部填充为 $-\infty$ ,这样进行 softmax 归一化后值就是 $0$ ,这样token就不会得到后方token的语义信息对其的修正,就能保证自回归性质了。

还没完。以上讲的这些全部都是在一个“头”里进行的,我们称之为单头注意力。而架构图中是 Multi-Head,也就是多头。(不是期货那个多头)不过这个也不难,就是把多个单头注意力模块并行起来同时计算。这一步可以理解为每个头可能关注语义信息中不同的部分,比如第一个头关注语气,第二个头关注文风等,总之这种结构实践下来确实有效,深度学习这块可解释性一直很差,有用就完事了!

多层感知机

我们常说“Attention is All You Need”,但事实并非如此。注意力模块的参数只占了整个模型的一部分,而另外的一个大头属于多层感知机。事实上,在GPT-3中,多层感知机的参数数量占整个模型参数的2/3。

这么多参数,那么多层感知机到底起了什么作用?经过现有的研究,我们发现,模型所记忆的大量事实性知识的“检索/模式匹配”功能,主要由 MLP 承担。研究者们拿运动员的名字和其参与的体育项目进行实验,例如输入”Michael Jordan plays”,下一个token为basketball的概率占比极高,证明模型确实从训练语料中学习到了这个事实。

如果说自注意力模块负责建立token之间的动态关联,那么MLP负责对每个token进行深层的语义加工。

但这个模块非常平凡,就是线性层+激活函数+线性层。

从图中可以看到,MLP中每个token的向量是独立进行计算的,不会像注意力模块那样混合其他token的信息。

还是那句话,深度学习领域的可解释性非常差。非要解释的话,可以认为第一个线性层的矩阵的每一行是在凭借已学习到的知识提出各种问题,将高维向量投影到更高维空间,可以看作是在展开输入向量,将其分解为更细粒度的特征组合,检测其中是否存在某些特定的语义模式。再经过激活函数,激活那些检测到的强特征模式,抑制无关特征,让向量的每个位置可能包含了对于刚刚问题的一些回答,再经过第二个线性层,将激活后的高维特征压缩回原始维度,即模型根据得到的回答给向量附加上深层的语义信息。

输出数据

经过多轮的注意力和MLP模块,每个token对应的向量已经带有了极为丰富的语义信息。这时我们只看最后一个token对应的向量,这个向量结合了上下文和模型所学知识,是所有信息的“结晶”。它此刻描述的是整个上下文的语义状态。我们通过不断的训练,强迫模型在结合了前 $i$ 个 token 的语义状态后,会和第 $i+1$ 个 token 的状态非常相近,那么我们通过那个结晶向量,去寻找周围方向类似的嵌入向量,这个向量对应的就是可能的下一个 token 。

具体操作而言,我们通过一个线性变换(常称为解嵌入)来计算该结晶向量与词表中所有词向量的相似度得分,再对其进行 softmax 归一化,此时每个位置对应的就是这个位置的 token 作为下一个 token 的概率。

注意,解嵌入后得到的不是下一个 token 的向量,而是下一个 token 可能是谁的得分列表!

到此,我们终于获得了下一个 token 的概率分布,接下来我们可以通过不同策略进行取样,例如贪婪搜索、随机采样、带温度采样、top-k 、top-p 等等,按需调参即可。

LLM原理与本质

有了前面的铺垫,这里就可以直接下结论了。大语言模型的本质就是分类模型,一种token就属于一类,LLM每次计算下一个token的概率分布,通过这个分布拿到下一个token,将其并入上下文窗口,再去计算之后的token,直到拿到某个特殊token,意味着整段文本结束,模型就停止预测。

这个流程也解释了为什么LLM每次都是一点一点回答的,因为它只能逐token进行输出。

明确了LLM本质上是分类模型,我们就能回答最后一个问题了——LLM的损失函数怎么定义。熟悉深度学习的读者们应当知道,分类模型的损失函数常用交叉熵损失,LLM也是如此。已经有了概率分布,计算交叉熵损失是平凡的。

延伸

讲都讲到这里了,有几个相关的概念顺便一起说明一下。

MoE 混合专家模式

MoE是对MLP层的一种改进,实质是把多个MLP模块并行起来,每次只激活需要的部分模块,其他模块不工作。核心目标是在不显著增加计算量的前提下,指数级扩大模型的总参数量,实现降低推理成本,同时提高推理速度。

MoE把单纯的MLP换成了一个路由器加上多个MLP。每个MLP称为专家,处理特定的数据模式(语法、逻辑、代码等),路由器就是一个轻量级的分类器,判断哪些专家擅长处理当前的token,再把token发送过去。

这引来了新的问题。某些专家可能过载,而其他专家躺平,也就是负载不均衡,因此需要在损失函数上加上负载均衡约束,惩罚被频繁选中的专家,鼓励路由器雨露均沾;路由器的决策是离散的,这导致梯度传播不连续,模型更难以收敛。不过近两年这些问题慢慢得到了解决,这个架构也用在了很多模型上(例如Deepseek V3)。

CoT 思维链

这个东西听起来高大上,实际上是最 naive 的。本质上是一种提示词工程,强迫模型推理的时候输出推理过程。这样考虑,你拿到一个问题,直接给答案可能容易犯错,如果给你一张草稿纸让你边写边算,出错的几率就会小。CoT 也就是这个原理,强迫模型给出结果前插入一段用自然语言描述的推理链。

结语

感觉学习什么都需要一个契机,而推动我学LLM原理的契机大概是面试找工作吧。非常功利的答案,但也非常真实。也并没有什么不好,我们适当地把压力转化为动力,往往能取得不错的成果。

之后有机会会去研究研究图像生成。和文本模态有确定的主流范式完全不同,图像模态由于其特性,现在的研究方向百花齐放。还有原生多模态,如何让高维语义空间同时含有文本和图像的语义,又要如何定义损失函数……都是很有学习价值的方向,慢慢学吧。

我确实认为 LLM 是21世纪最伟大的发明,在大语言模型以前很多人对 AI 是没有什么具体概念的,很多模型都是内置在软件里,人们也没办法直观地感受。自从 LLM 出现,ChatGPT、Claude、Gemini、Deepseek、Kimi、豆包、千问、Minimax等模型不断涌现,惊艳众人。我也算是看着 LLM 长大的人了,从20年那会 GPT-3 还笨笨的,慢慢的聪明起来,到现在LLM的通用知识已经随便超出普通人的知识储备了,毫不夸张的说,辩证地使用 LLM ,可以快速获得任意一个领域的专家级的知识量,记得时刻在网上查证防止幻觉就好。