post

LLM 基本概念查漏补缺随笔

math recap

查漏补缺数学:3Blue1Brown 线性代数课程

vector

向量就是按照顺序排列的一串数。比如我们关心房价,那么 [总面积, 总价格] 就可以组成我们数据的向量表示:

$$
\mathbf{v}=\begin{pmatrix}S\P\end{pmatrix}
$$

其中 $S$ 是总面积,$P$ 是总价格。顺序有意义,交换两个分量后,表示的含义也会变化。

线性变换

假设在一个二维(或更高维)空间中,基向量发生变化,比如放大、缩小或旋转,我们可以用线性变换描述这些变化。严格来说,变换 $T$ 必须满足可加性和齐次性:

$$
\begin{aligned}
T(\mathbf{u}+\mathbf{v})&=T(\mathbf{u})+T(\mathbf{v}),\
T(\alpha\mathbf{v})&=\alpha T(\mathbf{v})
\end{aligned}
$$

合起来就是:

$$
T(\alpha\mathbf{u}+\beta\mathbf{v})
=\alpha T(\mathbf{u})+\beta T(\mathbf{v})
$$

这也意味着原点不动,即 $T(\mathbf{0})=\mathbf{0}$。直观上,网格仍然均匀,平行等距的网格线仍保持平行等距。

把这种变化应用到数值上,就是先把向量写成基向量的线性组合,再替换成变换后的基向量:

$$
\mathbf{v}=x\mathbf{i}+y\mathbf{j}
\quad\Longrightarrow\quad
T(\mathbf{v})=xT(\mathbf{i})+yT(\mathbf{j})
$$

矩阵

矩阵是描述线性变换的工具。

比如我的基向量通过变换,变成了:

$$
\mathbf{i}’=T(\mathbf{i})=\begin{pmatrix}1\2\end{pmatrix},
\qquad
\mathbf{j}’=T(\mathbf{j})=\begin{pmatrix}3\4\end{pmatrix}
$$

那根据上一条,这个平面上的向量 $(x,y)$ 就会变成 $x\mathbf{i}’+y\mathbf{j}’$,也就是:

$$
T\left(\begin{pmatrix}x\y\end{pmatrix}\right)
=x\begin{pmatrix}1\2\end{pmatrix}
+y\begin{pmatrix}3\4\end{pmatrix}
=\begin{pmatrix}x+3y\2x+4y\end{pmatrix}
$$

更一般地,如果变换后的两个基向量分别为 $(a,c)$ 和 $(b,d)$,我们只需要 $a,b,c,d$ 这四个数来描述这个变换。将它们按列打包起来,就形成了一个 $2\times2$ 的矩阵:

$$
A=\begin{pmatrix}a&b\c&d\end{pmatrix},
\qquad
A\begin{pmatrix}x\y\end{pmatrix}
=\begin{pmatrix}ax+by\cx+dy\end{pmatrix}
$$

注意,矩阵的每一列对应一个基向量变换后的坐标。上面的具体例子就是:

$$
A=\begin{pmatrix}1&3\2&4\end{pmatrix}
$$

因此,矩阵乘法才是行乘列,矩阵的列数是基向量的数量,每一列代表了一条基向量,对于一个 $2$ 维的向量,你只能用正好两个基向量对其进行变换(因为你需要描述对每个维度的变换),多了或少了都不对,但是基向量的维度却可以随便动,维度少于 $2$,有部分信息在变换中损失了,维度大于 $2$,等于是把这个向量映射到一个更高维的空间中

比如 $(x,y)$描述了一个平面,将其与$\begin{pmatrix}1&0&1\0&1&1\end{pmatrix}$做变换,则是将这个平面映射到了一个$3$ 维空间,他在这个$3$ 维空间的位置由组成这个映射的两条基向量描述(两条线确定一个面),是一个倾斜的面 $(x,y,x+y)$,这里第三维受x,y控制,说明信息没有凭空产生,这里姑且称这个向量的”自由度“仍是 $2$,但变换可以让信息减少,比如我们使用$\begin{pmatrix}1&0\1&0\0&0\end{pmatrix}$,变换后变成 $(x,x,0)$,$y$ 的信息被丢掉了,这个向量的自由度只剩 $1$ 了,同时这里注意到使用的两个基向量是相关的,两个不同方向的信息,被压到同一方向上了

矩阵的乘法就是变换操作的叠加/复合,这也解释了为什么矩阵的乘法不具有交换律,一条线,你把他先拉伸再旋转,和先旋转再拉伸,最后在数轴上指向的点当然不一样

上面两个映射分别写成:

$$
\begin{pmatrix}1&0\0&1\1&1\end{pmatrix}
\begin{pmatrix}x\y\end{pmatrix}
=\begin{pmatrix}x\y\x+y\end{pmatrix},
\qquad
\begin{pmatrix}1&0\1&0\0&0\end{pmatrix}
\begin{pmatrix}x\y\end{pmatrix}
=\begin{pmatrix}x\x\0\end{pmatrix}
$$

两个线性变换先做 $A$,再做 $B$,其复合为:

$$
\mathbf{v}\longmapsto A\mathbf{v}\longmapsto B(A\mathbf{v})=(BA)\mathbf{v},
\qquad AB\ne BA\quad\text{(一般情况下)}
$$

determinant

一个矩阵的determinant表示了在经过了这个变换后,原坐标系中原来面积为1的图形面积变化的倍率,对体积 or更高维同理,比如

$$
A=\begin{pmatrix}1&0\0&2\end{pmatrix}
$$

二维方阵的行列式及面积缩放关系为:

$$
\det(A)=ad-bc,\qquad S_{\mathrm{after}}=|\det(A)|S_{\mathrm{before}}
$$

的det就是2,y值变成2倍大,面积增大2倍

如果det为负,说明进行了取向翻转

LLM

神经网络

神经网络有CNN,RNN,transformer等数不清的变体,但他们都由多层神经元组成,一个神经元就是一个包含一个数的节点,这个数的范围由激活函数决定

进入神经网络的所有信息都储存在这些节点中,所以输入输出值都是以神经元节点的值表示的

比如识别一个 $28\times28$ 像素的手写数字,输入像素可以按照灰度编码成784个节点作为神经网络的第一层,而最后一层则由10个节点代表识别结果($0$ 到 $9$)

为什么采用多层结构,因为多层结构将复杂问题拆解成简单的子问题

以数字识别为例,假设采用两层中间层,就是像素 $\to$ 边/小型片段 $\to$ 形状 $\to$ 数字

层与层节点间的连边记录了权重用于表示两个节点间的关联性,假设$x$ 层的一个节点被激活了,一个负的权重表示$x+1$ 层的该节点不该被激活,一个正的权重则表示$x+1$ 层的该节点该被激活,这些节点的权重可能是互相冲突的,但我们希望经过合适选择的权重可以在最后输出正确的结果

以 Sigmoid 作为激活函数,将上一层各节点的值乘以对应的权重,求和后加上偏移,再计算下一层节点的值:

$$
a_{0,1}=\sigma\left(a_{0,0}w_{00}+a_{1,0}w_{10}+\cdots+a_{n-1,0}w_{(n-1)0}+b_0\right)
=\sigma\left(\sum_{i=0}^{n-1}a_{i,0}w_{i0}+b_0\right)
$$

这里大量的$w_{**}$和$b_*$就是训练时要调的参数

这里 $a_{i,\ell}$ 表示第 $\ell$ 层第 $i$ 个节点的值,$w_{i0}$ 表示从第 $0$ 层第 $i$ 个节点连向第 $1$ 层第 $0$ 个节点的权重,$b_0$ 是该目标节点的偏移,$n$ 是上一层的节点数。Sigmoid 的公式是:

$$
\sigma(z)=\frac{1}{1+e^{-z}}
$$

这里的计算可以转换为矩阵运算,然后使用GPU进行加速(并行浮点数乘加核心和矩阵运算加速核心)

把上一层的 $n$ 个节点和下一层的 $m$ 个节点分别组成列向量,沿用 $w_{ij}$ 表示“上一层第 $i$ 个节点到下一层第 $j$ 个节点”的权重:

$$
\mathbf{a}^{(0)}=\begin{pmatrix}a_{0,0}\a_{1,0}\\vdots\a_{n-1,0}\end{pmatrix},
\qquad
W=\begin{pmatrix}
w_{00}&w_{10}&\cdots&w_{(n-1)0}\
w_{01}&w_{11}&\cdots&w_{(n-1)1}\
\vdots&\vdots&\ddots&\vdots\
w_{0(m-1)}&w_{1(m-1)}&\cdots&w_{(n-1)(m-1)}
\end{pmatrix},
\qquad
\mathbf{b}=\begin{pmatrix}b_0\b_1\\vdots\b_{m-1}\end{pmatrix}
$$

这里 $W\in\mathbb{R}^{m\times n}$,每一行收集一个下一层节点的全部输入权重,每一列对应一个上一层节点。整层的激活公式就是:

$$
\mathbf{a}^{(1)}
=\begin{pmatrix}a_{0,1}\a_{1,1}\\vdots\a_{m-1,1}\end{pmatrix}
=\sigma\left(W\mathbf{a}^{(0)}+\mathbf{b}\right)
=\begin{pmatrix}
\sigma\left(\sum_{i=0}^{n-1}a_{i,0}w_{i0}+b_0\right)\
\sigma\left(\sum_{i=0}^{n-1}a_{i,0}w_{i1}+b_1\right)\
\vdots\
\sigma\left(\sum_{i=0}^{n-1}a_{i,0}w_{i(m-1)}+b_{m-1}\right)
\end{pmatrix}
$$

注意,这里的 $\sigma$ 对向量中的每个分量分别应用 Sigmoid,不是对整个矩阵求一个值。维度对应为 $(m\times n)(n\times1)+(m\times1)=m\times1$,结果的第一个分量就是前面的 $a_{0,1}$。

推广到第 $\ell$ 层到第 $\ell+1$ 层,可以简写为:

$$
\mathbf{a}^{(\ell+1)}=\sigma\left(W^{(\ell)}\mathbf{a}^{(\ell)}+\mathbf{b}^{(\ell)}\right)
$$

sigmoid函数现在已经不用了,因为它在左右两侧都太平缓了,这导致当输入很大时,神经元的变化值却不大,导致训练的过程变得非常慢,现在普遍使用ReLU($\operatorname{ReLU}(x)=\max(0,x)$)等反馈更明显的函数,或其改进变体,但总体图像都类似左侧贴近x轴,右侧接近线性增长不收敛

ReLU 的公式为:

$$
\operatorname{ReLU}(x)=\max(0,x)
$$

cost function

神经网络的学习指的就是最小化cost function的值,仍然以数字识别为例,假设答案为2,我们将输出层只有代表2的 神经元 激活 的状态和神经网络实际的输出状态求距离,显然如果预测错了,输出了乱七八糟的东西(多个节点都被激活了),此时的cost function会很大,反之cost function为0

假设输出层有 $K$ 个节点,$\hat y_k$ 是第 $k$ 个节点的实际输出,$y_k$ 是答案对应的目标值,则cost function可以写成:

$$
C=\sum_{k=0}^{K-1}\left(\hat y_k-y_k\right)^2
$$

实际上的cost function不是对单个样本求出的值,而是基于训练数据集、以神经网络的所有参数作为输入的函数。对于 $N$ 个样本,可以写成:

$$
C^{(r)}(\theta)=\sum_{k=0}^{K-1}\left(\hat y_k^{(r)}(\theta)-y_k^{(r)}\right)^2,
\qquad
J(\theta)=\frac{1}{N}\sum_{r=1}^{N}C^{(r)}(\theta)
$$

梯度下降

若用参数向量 $\theta$ 表示网络中的所有权重和偏置,一步梯度下降更新可以写成:

$$
\theta_{t+1}=\theta_t-\eta\nabla_\theta J(\theta_t)
$$

我们有了cost function,并且我们要它最小,理想状态下我们找一个斜率为0的地方,但是现实是cost function通常乱七八糟的,而且包含大量的参数,所以更现实的方法是找局部最优,也就是找斜率小于0的地方走,在更高维的概念里,我们寻找梯度 – 一个指示函数值下降/增长方向的向量,机器学习的过程就是重复计算当前梯度 - 向负梯度方向移动,而每次移动的距离倍率被称作学习率

如果把当前所有参数打包成一个向量,那梯度也是一个同样维的向量,指示对应参数调整的方向和程度

如果cost function足够平滑,那寻找最小值会更简单,这也是为什么神经网络的激活状态是连续变化的浮点数

反向传播

从informal的角度

为什么是反向,因为我们希望output layer(假设为layer n)的结果符合我们的预期,那么我们有3种调整的方式

  • 对于第$n$ 层希望激活的节点,增大basis,反之亦然
  • 对于第$n$ 层希望激活的节点,增大$n-1$ 层激活的节点连向它的权重
  • 对于权重为正的链接,让$n-1$ 层更加容易被激活

可以看到第三步是可以递归的,所以我们是从答案倒推中间状态

在实践中,我们不止计算1个样本,我们把所有的样本叠加起来,然后综合计算所有权重的变化

从formal的角度

对单个样本,记第 $\ell$ 层的加权输入为 $\mathbf z^{(\ell)}$、激活值为 $\mathbf a^{(\ell)}$,前向计算为:

$$
\mathbf z^{(\ell)}=W^{(\ell)}\mathbf a^{(\ell-1)}+\mathbf b^{(\ell)},
\qquad
\mathbf a^{(\ell)}=\phi\left(\mathbf z^{(\ell)}\right)
$$

令 $\boldsymbol\delta^{(\ell)}=\partial C/\partial\mathbf z^{(\ell)}$。使用当前的平方误差代价函数时,输出层和隐藏层的误差项可以递推计算:

$$
\boldsymbol\delta^{(L)}=2\left(\mathbf a^{(L)}-\mathbf y\right)\odot\phi’\left(\mathbf z^{(L)}\right),
\qquad
\boldsymbol\delta^{(\ell)}=\left((W^{(\ell+1)})^\mathsf{T}\boldsymbol\delta^{(\ell+1)}\right)\odot\phi’\left(\mathbf z^{(\ell)}\right)
$$

于是每层权重和偏置对应的梯度为:

$$
\frac{\partial C}{\partial W^{(\ell)}}=\boldsymbol\delta^{(\ell)}(\mathbf a^{(\ell-1)})^\mathsf{T},
\qquad
\frac{\partial C}{\partial\mathbf b^{(\ell)}}=\boldsymbol\delta^{(\ell)}
$$

其中 $\odot$ 表示逐元素相乘,$\phi’$ 是激活函数的导数;对多个样本的梯度取平均即可得到数据集上的梯度。

反向传播是计算负梯度的算法

更具体的,反向传播就是对于一组样本的平均损失函数,计算其对于每个参数(basis,weight)的偏导,把这些值打包成一个向量就是负梯度

顺带一提这个公式看着头疼,大概知道其实就是个连续求导算法就行了,从C一直递归求到你需要的那个参数的偏导

llm是什么,训练又是训练什么

这里讨论的LLM(大语言模型)是用于自回归生成的模型,可以把它理解成一个接收 token 序列的函数。它的任务是预测在这个序列下,下一个 token 的概率分布。也就是对于 $x_1,x_2,\ldots,x_{t-1}$ 这个序列,预测 $x_t$:

$$
f_\theta(x_1,\ldots,x_{t-1})
=p_\theta(,\cdot\mid x_1,\ldots,x_{t-1})
$$

其中 $\theta$ 表示模型的参数,$x_t$ 是第 $t$ 个 token。模型先给词表中的每个候选 token 分配概率,再通过解码策略选择下一个 token,比如取概率最大的一个,或者按概率采样。

训练就是调整这个函数的参数,让训练数据中真实的下一个 token 获得更高概率。一个序列的概率可以写成:

$$
p_\theta(x_1,\ldots,x_T)
=\prod_{t=1}^{T}p_\theta(x_t\mid x_{<t})
$$

其中 $x_{<t}$ 表示第 $t$ 个 token 之前的序列,第一个位置通常以起始标记或空上下文为条件。常用的训练目标是最小化平均负对数似然,也就是这里的交叉熵损失:

$$
\mathcal{L}(\theta)
=-\frac{1}{T}\sum_{t=1}^{T}\log p_\theta(x_t\mid x_{<t})
$$

真实 token 的预测概率越高,对应的损失就越小。
更具体的,当前的LLM都是transformer架构,主要由多层attention block和MLP(multilayer perceptorn)组成

token

token是向量化保存的文字片段,具体划分方式和长度取决于词表和embedding,一个同样需要训练的神经网络,语义相近的token在向量化后趋向于拥有相近的距离,向量直接也可以加减,用于提取token之间的共同信息
在输出时,token需要经过unembedding重新映射回文本,unembedding的操作和embedding类似,只不过是反过来,通过一个调整过的矩阵和输出的token向量算出词表中所有词的出现概率,将每个词对应的计算结果做softmax处理,最终所有的结果都会落在一个$[0,1]$的分布里,如果对softmax函数的参数额外除一个常量 $T$,那么 $T$ 越大分布会越平均,产生输出的随机性

可以把分词和嵌入过程写成:

$$
s \xrightarrow{\mathrm{tokenizer}} (t_1,t_2,\ldots,t_n),
\qquad
\mathbf e_i=E[t_i]
$$

其中 $s$ 是输入文本,$t_i\in{0,1,\ldots,V-1}$ 是词表大小为 $V$ 时第 $i$ 个 token 的编号;$E\in\mathbb{R}^{V\times d_{\mathrm{model}}}$ 是嵌入矩阵,$E[t_i]$ 表示取出第 $t_i$ 行,作为这个 token 的向量。这里的嵌入通常是一个可训练的矩阵查表操作。

两个 token 向量的余弦相似度可以写成:

$$
\operatorname{cos}(\mathbf e_i,\mathbf e_j)
=\frac{\mathbf e_i\cdot\mathbf e_j}{|\mathbf e_i|,|\mathbf e_j|}
$$

相似度可以用来比较向量方向,但向量相近不保证两个 token 在所有上下文中含义相同。

设最后一层输出的上下文行向量为 $\mathbf h\in\mathbb{R}^{1\times d_{\mathrm{model}}}$,unembedding 把它映射到词表中每个 token 的分数(logit):

$$
\mathbf z=\mathbf h W_U+\mathbf b_U,
\qquad
W_U\in\mathbb{R}^{d_{\mathrm{model}}\times V},\quad
\mathbf b_U\in\mathbb{R}^{1\times V},\quad
\mathbf z\in\mathbb{R}^{1\times V}
$$

再用带温度 $T>0$ 的 softmax 把分数转换成概率:

$$
p_j=\frac{\exp(z_j/T)}{\sum_{r=0}^{V-1}\exp(z_r/T)},
\qquad j=0,1,\ldots,V-1
$$

$T$ 是温度:$T=1$ 时为普通 softmax;$T$ 越大,概率分布通常越平缓。

attention

在输入文本进行embedding转换为token时,上下文信息是未进入计算的,也就是说出现在不同位置的多个token,他们的向量都是完全相同的,但我们知道文本的含义受其位置的影响是很大的,所以这就是attention block要解决的问题

注意力层还需要让模型知道每个向量在序列中的位置。一种常见的写法是把位置向量加到 token 嵌入上:

$$
\mathbf x_i=\mathbf e_i+\mathbf p_i
$$

其中 $\mathbf e_i,\mathbf p_i\in\mathbb{R}^{1\times d_{\mathrm{model}}}$;具体位置表示方法因模型而异。

K,Q,V

首先,完整的token序列先经过一个称作query matrix的矩阵的变换,产生 $n$ 个query向量,这个向量的维数要比token数少得多

然后,使用key matrix做一遍相同操作,产生同样的 $n$ 个key向量,key向量被认为是先前query的潜在答案,如果两个向量在空间中的距离非常近,那么说明这两个token之间存在联系,计算的方法是进行$n^2$的点积运算后再由softmax处理,产生一个$n^2$大小的分布表格,这个表格指示了当前上下文中token之间的关联,值越大的关联性越强,这个表格的值在后续的value matrix计算有用

value计算用于更新每个token,在实践中不会直接一步完成计算,因为token向量的维数通常比较大,一般会把value矩阵拆成两个规模和 $K,Q$ 相同的矩阵相同的小矩阵,value down负责把输入向量变化为一个更小的向量,然后根据注意力表格中求出来的权重,针对每个token决定其获得来自其余token的更新比例,这一步可以通过矩阵运算完成,然后再通过value up把低维value更新回高维token值

把输入序列表示成矩阵 $X\in\mathbb{R}^{n\times d_{\mathrm{model}}}$,每一行对应一个 token 在当前层的向量。Query、Key、Value 由三组可训练矩阵投影得到:

$$
Q=XW_Q,\qquad K=XW_K,\qquad V=XW_V
$$

其中 $W_Q\in\mathbb{R}^{d_{\mathrm{model}}\times d_k}$、$W_K\in\mathbb{R}^{d_{\mathrm{model}}\times d_k}$、$W_V\in\mathbb{R}^{d_{\mathrm{model}}\times d_v}$;因此 $Q,K\in\mathbb{R}^{n\times d_k}$,$V\in\mathbb{R}^{n\times d_v}$。对位置 $i$ 的 Query 和位置 $j$ 的 Key,缩放点积得分为:

第 $i$ 行分别是当前位置对应的向量:$\mathbf q_i=Q_{i,:}$、$\mathbf k_i=K_{i,:}$、$\mathbf v_i=V_{i,:}$。

$$
s_{ij}=\frac{\mathbf q_i\cdot\mathbf k_j}{\sqrt{d_k}}
$$

除以 $\sqrt{d_k}$ 用来控制点积的数值尺度。Query 表示当前位置要寻找什么,Key 用来与它匹配,Value 携带匹配后要汇总的信息。注意力权重按每个 Query 的行分别做 softmax:

$$
A=\operatorname{softmax}\left(\frac{QK^{\mathsf T}}{\sqrt{d_k}}+M\right),
\qquad
O=AV
$$

这里 $A\in\mathbb{R}^{n\times n}$,$A_{ij}$ 表示位置 $i$ 从位置 $j$ 汇入信息的权重,$O\in\mathbb{R}^{n\times d_v}$ 是注意力输出。自回归模型通常使用因果掩码(主要在训练中):

$$
M_{ij}=\begin{cases}
0,&j\le i\
-\infty,&j>i
\end{cases}
$$

这样每个位置只能关注自己和之前的位置。多个注意力头可以并行计算,再拼接并投影:

$$
\operatorname{head}_r=\operatorname{Attention}(XW_Q^{(r)},XW_K^{(r)},XW_V^{(r)}),
\qquad
\operatorname{MHA}(X)=\operatorname{Concat}(\operatorname{head}_1,\ldots,\operatorname{head}_h)W_O
$$

其中 $h$ 是注意力头数,$\operatorname{Concat}$ 沿特征维拼接各个头的输出,$W_O\in\mathbb{R}^{h d_v\times d_{\mathrm{model}}}$ 是输出投影矩阵。

对于正文提到的 Value down/up 压缩写法,可以表示为:

$$
V_{\mathrm{down}}=XW_{V,\mathrm{down}},
\qquad
O=A V_{\mathrm{down}},
\qquad
H=O W_{V,\mathrm{up}}
$$

其中 $W_{V,\mathrm{down}}\in\mathbb{R}^{d_{\mathrm{model}}\times d_v}$、$W_{V,\mathrm{up}}\in\mathbb{R}^{d_v\times d_{\mathrm{model}}}$;$H\in\mathbb{R}^{n\times d_{\mathrm{model}}}$。这是对 Value 投影做低维分解的一种写法。

MLP

在解释MLP之前,我们先假设在token所处的向量空间中,如果一个token向量编码了向量A所对应的信息,那么这个token向量和A的点积为1,否则点积<=0
MLP全称multilayer perceptorn,可以认为这是llm储存知识的地方,MLP是一个大矩阵,但不是将其理解为一种线性变换,而是大量的按照行排列的向量,向量的维数和token向量维数相同,里面的每一条向量可以认为是编码了一条知识,MLP的计算是并行的,所以只需要考虑单个token的情况,token先与所有向量做点乘,产生一个新的更大的向量,可以认为这个向量表示了当前token与这些知识的相关性,我们在这里只想要相关的结果,因此将这个向量减去一个basis向量,用于过滤一些点积为正但是不够大的结果,然后进过一个ReLU或类似的激活函数,舍弃负值,这就得到了一个包含了当前token和各知识联系权重的高维向量,然后我们要把这个高维向量降维到和token同维度,此时我们有第二个包含大量知识的大矩阵,我们将其理解为大量编码了知识的向量,但是按列排列,将先前的每一个激活权重和对应向量相乘并叠加,最后得到了一个”注入了对应比例对应知识的向量”,在这里称其为更新向量,输出同样有一个basis向量,这个向量的作用是即使神经元未被激活,最终输出也有一个默认更新,最后把原始的token向量和更新向量相加,这里我们就得到了更新了信息后的token

SFT

SFT 全称为 Supervised Fine-Tuning,也就是监督微调。它是在一个已经训练好的模型的基础上,通过更多的输入输出数据来调整模型的参数。

可以将模型想象成一个黑盒,里面是一堆复杂的函数。为了直观理解参数调整,先拿一个简单的函数举例:

$$
g_\theta(z)=az^b+c,\qquad\theta=(a,b,c)
$$

这里假设 $z>0$,方便讨论实数指数 $b$。这个函数只是帮助理解的例子,本身不一定是合法的概率函数。我们可以通过一点点改变 $a,b,c$ 的值,再拿之前做好的输入输出数据去比较。如果调整后的输出和我们的预期更接近了,就是拟合更好了。

对于语言模型,假设第 $i$ 个样本的输入为 $q^{(i)}$,目标回答为 $y^{(i)}=(y_1^{(i)},\ldots,y_{m_i}^{(i)})$,一种常见做法是只计算回答部分的损失:

$$
\mathcal{L}{\mathrm{SFT}}(\theta)
=-\frac{1}{\sum
{i=1}^{N}m_i}
\sum_{i=1}^{N}\sum_{t=1}^{m_i}
\log p_\theta\left(y_t^{(i)}\mid q^{(i)},y_{<t}^{(i)}\right)
$$

其中 $N$ 是样本数量,$m_i$ 是第 $i$ 个回答的 token 数。训练时提供真实的回答前缀,这种方式叫做教师强制(teacher forcing)。实际哪些位置参与损失计算,取决于训练数据和损失掩码的设置。

参数通常通过梯度下降及其变体来更新,最简单的更新形式是:

$$
\theta\leftarrow\theta-\eta\nabla_\theta\mathcal{L}(\theta)
$$

其中 $\eta$ 是学习率,$\nabla_\theta\mathcal{L}$ 是损失对参数的梯度。

当然也可能调歪了:在训练数据上拟合得很好,在没见过的数据上表现却变差,这就是过拟合。顺带一提,监督的意思是样本提前提供了目标输出;在 SFT 里通常是示范回答,不一定是简单的“对 / 错”标签。

Author

SGSG

Posted on

2026-10-09

Updated on

2026-10-15

Licensed under