自然语言处理:笔记整理

Last updated on January 30, 2026 pm

本文为 SJTU-CS3602 自然语言处理课程的笔记整理

Lecture 2: 词向量

词的表示 (Word Representation)

我们首先需要解决一个根本问题:如何让计算机理解和处理单词。

基础版:词的单热点表示 (One-Hot Representation)

  • 概念:为词典中的每一个词分配一个唯一的索引,然后用一个非常长的向量来表示一个词
    • 这个向量的长度等于词典的大小,在代表当前词的索引位置上值为 1,其余所有位置都为 0
  • 示例:对于句子“我 爱 自然语言处理”,假设这五个词在词典中的索引分别是 1 到 5
    • “我” 的向量是 [1, 0, 0, 0, 0]
    • “爱” 的向量是 [0, 1, 0, 0, 0]
    • 以此类推
  • 缺点
    • 语义鸿沟:这种表示方法只是机械地记录了词,无法表达词与词之间的的远近亲疏关系
      • 例如,“我”和“爱”的向量是正交的,无法体现它们之间的关系
    • 维度灾难:当词典非常大时,向量维度极高且非常稀疏,计算效率低

进阶版:词的分布式表示 (Distributed Representation),即词向量 (Word Vectors)

  • 核心思想:使用一个低维、稠密的实数向量来表示一个词
  • 定义:将词表示为高维连续空间中的一个点(向量),向量的每一个维度代表一个潜在的语义特征
  • 优势
    • 表达语义:向量在高维空间中的位置代表了词的语义
    • 表达关系:向量间的远近关系(如余弦相似度)可以度量词与词在语义上的亲疏关系
  • 为什么叫“分布式”表示:与独热表示(局部表示)中一个词只对应一个维度不同,分布式表示中,一个词的语义被“分散”到向量的多个维度中共同表达,因此被称为分布式表示

两个经典的词向量模型

词向量的语义从何而来?英国语言学家 John Rupert Firth 提出:“You shall know a word by the company it keeps.” (观其伴而知其义),即一个词的意义由它周围的词(上下文)来决定。基于这个思想,诞生了两个经典模型。

CBOW (Continuous Bag-of-Words) 模型

  • 任务:用上下文的词来预测中心的当前词
  • 示例:在句子“我 看见 一只 __ 快速 跑进了 教室”中,用周围的词(“我”、“看见”、“一只”、“快速”、…)来预测中间的词是“小猫”

为了表述简洁,我们首先考虑上下文只包含了一个词的 CBOW 模型。

  • 结构:输入层 -> 隐层 -> 输出层
    • 输入层 (x\mathbf{x}):上下文单词的独热向量
    • 隐层 (h\mathbf{h}):输入词的词向量表示
    • 输出层 (y\mathbf{y}):一个概率分布向量,表示词典中每个词是中心词的概率

  • 计算过程
    • 隐层向量 h=Ux\mathbf{h} = \mathbf{U}\mathbf{x},这里的 U\mathbf{U} 是一个参数矩阵
    • 输出向量 o=Vh\mathbf{o} = \mathbf{V}\mathbf{h},这里的 V\mathbf{V} 是另一个参数矩阵
    • 通过 Softmax 函数将输出向量 o\mathbf{o} 归一化,得到最终的概率分布 y=Softmax(o)\mathbf{y} = \operatorname{Softmax}(\mathbf{o})

    yj=exp(oj)t=1Nexp(ot)y_j=\frac{\exp \left(o_j\right)}{\sum_{t=1}^{\mathrm{N}} \exp \left(o_t\right)}

  • 目标:调整参数矩阵 U\mathbf{U}V\mathbf{V},使得模型预测出的概率在真实的中心词上尽可能大,别的词上尽可能小

接着,考虑更多上下文的 CBOW 模型。

  • 结构:CBOW 模型的隐层 h\mathbf{h} 通过取每个上下文单词对应的 h\mathbf{h} 的平均值得到

  • 公式

    h=1C(Ux1+Ux2++UxC)=Ux\mathbf{h}= \frac{1}{C}\left(\mathbf{U} \mathbf{x}_1+\mathbf{U} \mathbf{x}_2+\cdots+\mathbf{U} \mathbf{x}_C\right) = \mathbf{U} \overline{\mathbf{x}}

    • 其中 CC 是上下文单词的数量

Skip-gram 模型

  • 任务:用中心的当前词来预测其上下文的词

  • 示例:在同样的句子中,给定中心词“小猫”,来预测它周围可能出现的词(“我”、“看见”、“一只”、“快速”、…)

  • 结构:与 CBOW 类似,但方向相反。

    • 输入层 (x\mathbf{x}):中心词的独热向量
    • 隐层 (h\mathbf{h}):中心词的词向量表示
    • 输出层 (y\mathbf{y}):预测多个上下文单词的概率分布
  • 目标:调整参数,使得模型在真实的上下文单词上预测出的概率尽可能大

词向量模型的训练:反向传播算法(Back-propagation)

我们已经了解 CBOW 和 Skip-gram 模型的基本结构,现在的问题是:如何训练这些模型,也就是如何学习到最优的参数矩阵 U\mathbf{U}V\mathbf{V}。这里,我们以只有一个词上下文的 CBOW 模型为例。

  • 前向计算y=Softmax(o)=Softmax(Vh)=Softmax(VUx)\mathbf{y}=\operatorname{Softmax}(\mathbf{o})=\operatorname{Softmax}(\mathbf{V h})=\operatorname{Softmax}(\mathbf{V U x})
  • 损失函数:我们的目标是让模型预测的概率 y\mathbf{y} 在真实的中心词(假设其索引为 jj)上尽可能大,于是定义损失函数 E=logyjE = -\log y_j,目标是最小化这个损失,即最大化 yjy_j
  • 核心任务: 为了用梯度下降法最小化损失 EE,我们必须计算出损失EE 对于两个参数矩阵 V\mathbf{V}U\mathbf{U} 的梯度,即 EU\dfrac{\partial \mathrm{E}}{\partial \mathbf{U}}EV\dfrac{\partial \mathrm{E}}{\partial \mathbf{V}}

首先,化简损失函数 E\mathrm{E}

E=logyj=logexp(oj)t=1Nexp(ot)=oj+logt=1Nexp(ot)=vjTUx+logt=1Nexp(vtTUx)\begin{aligned} \mathrm{E} & =-\log y_j \\ & =-\log \frac{\exp \left(o_j\right)}{\sum_{t=1}^{\mathrm{N}} \exp \left(o_t\right)} \\ & =-o_j+\log \sum_{t=1}^N \exp \left(o_t\right) \\ & =-\mathbf{v}_{\mathbf{j}}^{\mathrm{T}} \mathbf{U} \mathbf{x}+\log \sum_{t=1}^N \exp \left(\mathbf{v}_{\mathbf{t}}^{\mathrm{T}} \mathbf{U} \mathbf{x}\right) \end{aligned}

接着,将 E\mathrm{E}V\mathbf{V} 求导。

EV={Evi=Ux+1t=1Nexp(vtUTx)exp(viTUx)Ux=h+yih(i=j)Evi=1t=1Nexp(vtUTx)exp(viTUx)Ux=yih(ij)\frac{\partial \mathrm{E}}{\partial \mathbf{V}}= \begin{cases}\cfrac{\partial \mathrm{E}}{\partial \mathbf{v}_{\mathrm{i}}}=-\, \mathbf{U} \mathbf{x}+\cfrac{1}{\sum_{t=1}^N \exp \left(\mathbf{v}_{\mathrm{t}} \mathbf{U}^{\mathrm{T}} \mathbf{x}\right)} \exp \left(\mathbf{v}_{\mathrm{i}}^{\mathrm{T}} \mathbf{U} \mathbf{x}\right) \mathbf{U} \mathbf{x} & =-\mathbf{h}+y_i \mathbf{h}& (i=j) \\ \cfrac{\partial \mathrm{E}}{\partial \mathbf{v}_{\mathrm{i}}}=\cfrac{1}{\sum_{t=1}^N \exp \left(\mathbf{v}_{\mathrm{t}} \mathbf{U}^{\mathrm{T}} \mathbf{x}\right)} \exp \left(\mathbf{v}_{\mathrm{i}}^{\mathrm{T}} \mathbf{U} \mathbf{x}\right) \mathbf{U} \mathbf{x} & = y_i \mathbf{h} & (i \neq j)\end{cases}

为了简化表达,定义误差信号 e\mathbf{e}

eRN×1={yi1(i=j)yi(ij)\mathbf{e} \in \mathbb{R}^{N \times 1}=\left\{\begin{array}{cc} y_i-1 & (\mathrm{i}=\mathrm{j}) \\ y_i & (\mathrm{i} \neq \mathrm{j}) \end{array}\right.

从而 EV\dfrac{\partial \mathrm{E}}{\partial \mathbf{V}} 可以简写为:

EV=ehT\frac{\partial \mathrm{E}}{\partial \mathbf{V}}=\mathbf{e h}^{\mathrm{T}}

最后,将 E\mathrm{E}U\mathbf{U} 求导。

EU=vjTxT+1t=1Nexp(vtUx)t=1Nexp(vtUx)vtTxT=(1t=1Nexp(vtUx)t=1Nexp(vtUx)vtTvjT)xT=VTexT\begin{aligned} \dfrac{\partial \mathrm{E}}{\partial \mathbf{U}} & =-\mathbf{v}_{\mathbf{j}}^{\mathrm{T}} \mathbf{x}^{\mathrm{T}}+\dfrac{1}{\sum_{t=1}^N \exp \left(\mathbf{v}_{\mathbf{t}} \mathbf{U} \mathbf{x}\right)} \sum_{t=1}^N \exp \left(\mathbf{v}_{\mathbf{t}} \mathbf{U} \mathbf{x}\right) \mathbf{v}_{\mathbf{t}}{ }^{\mathrm{T}} \mathbf{x}^{\mathrm{T}} \\ & =\left(\dfrac{1}{\sum_{t=1}^N \exp \left(\mathbf{v}_{\mathbf{t}} \mathbf{U} \mathbf{x}\right)} \sum_{t=1}^N \exp \left(\mathbf{v}_{\mathbf{t}} \mathbf{U} \mathbf{x}\right) \mathbf{v}_{\mathbf{t}}{ }^{\mathrm{T}}-\mathbf{v}_{\mathbf{j}}{ }^{\mathrm{T}}\right) \mathbf{x}^{\mathrm{T}}=\mathbf{V}^{\mathrm{T}} \mathbf{e x}^{\mathrm{T}} \end{aligned}

计算出梯度后,再设置一个学习率 η\eta,我们就可以用梯度下降法来更新参数了。

{V=VηEV=VηehTU=UηEU=UηVTexT\left\{\begin{array}{l} \mathbf{V}=\mathbf{V}-\eta \dfrac{\partial \mathrm{E}}{\partial \mathbf{V}}=\mathbf{V}-\eta \mathbf{e h}^{\mathrm{T}} \\ \mathbf{U}=\mathbf{U}-\eta \dfrac{\partial \mathrm{E}}{\partial \mathbf{U}}=\mathbf{U}-\eta \mathbf{V}^{\mathrm{T}} \mathbf{e x}^{\mathrm{T}} \end{array}\right.

那么,这一算法为什么叫“反向传播”呢?

  • 答案:因为这个算法的核心在于预测误差 e\mathbf{e} 的逐层“反向传播”。
  • 理解
    • 观察两个梯度公式:

      {EV=ehTEU=VTexT\left\{\begin{array}{l} \dfrac{\partial \mathrm{E}}{\partial \mathbf{V}}=\mathbf{e h}^{\mathrm{T}} \\ \dfrac{\partial \mathrm{E}}{\partial \mathbf{U}}=\mathbf{V}^{\mathrm{T}} \mathbf{e x}^{\mathrm{T}} \end{array}\right.

    • 我们可以把任何一层权重矩阵的梯度分解成两部分:该层的输入 和 该层接收到的误差信号
    • 对于输出层权重 V\mathbf{V} 来说,它的输入是 h\mathbf{h},它直接接收到的误差信号就是 e\mathbf{e}
    • 对于输入层权重 U\mathbf{U} 来说,它的输入是 x\mathbf{x},它接收到的误差信号是 e\mathbf{e} 从输出层经过 V\mathbf{V} 反向传播回来的结果,这个结果就是 VTe\mathbf{V}^T \mathbf{e}
    • 因此,“反向传播”形象地描述了误差信号从网络末端(输出层)开始,乘以前一层权重的转置,一步步向网络前端(输入层)传递的过程。

注意,在这个简化的模型里,隐层没有激活函数。如果中间层存在激活函数(例如 Sigmoid 或 ReLU),那么在反向传播误差时,还需要再乘上激活函数的导数。

对于 Skip-gram 模型,其损失函数定义为:

E=logP(wj1,wj2,,wjCwk)=logi=1CP(wjiwk)=i=1ClogP(wjiwk)\begin{aligned} \mathrm{E} & =-\log P\left(w_{j_1}, w_{j_2}, \ldots, w_{j_C} \mid w_k\right) \\ & =-\log \prod_{i=1}^C P\left(w_{j_i} \mid w_k\right)=-\sum_{i=1}^C \log P\left(w_{j_i} \mid w_k\right) \end{aligned}

词向量模型的优化

  • 问题:实际应用中的词汇表通常非常大,可能包含几十万甚至上百万个词,而 Softmax 函数需要对词典中每一个词都计算一次指数并求和,计算开销巨大
  • 解决方案:将一个大的多分类问题转化为多个二分类问题

Hierarchical Softmax (分层柔性最大化)

  • 思想:将词典中的所有词构建成一棵哈夫曼树(Huffman Tree),其中每个叶子节点代表词典中的一个词
    • 预测一个特定单词的概率,就变成了从树的根节点开始,经过一系列“向左走”还是“向右走”的二分类决策,最终到达对应叶子节点的过程
    • 目标词的最终概率,等于这条路径上所有二分类决策概率的乘积

  • 哈夫曼树:一种最优二叉树,高频词的路径短,低频词的路径长,可以最小化平均查找路径
    • 在这里,频率就是每个类别的样本比例
    • 构造:可以利用贪心法,不断地合并权重最小的子树得到
  • 优点:将计算复杂度从 O(V)O(V)VV 是词典大小)降低到 O(logV)O(\log V)

Negative Sampling (负采样)

  • 核心思想:我们训练模型的目的,只是为了得到高质量的词向量,并不需要在所有词上计算精确的输出概率

    • 我们只需要模型能够在正确单词上的输出尽可能大,错误单词上的输出尽可能小就可以了
  • 新的优化目标:我们把优化函数重新设计成为:

    • 最大化正确单词上的输出:对于一个给定的(上下文,中心词)正样本对,我们希望模型给出的分数尽可能高
    • 最小化“噪声”单词上的输出:我们再在噪声分布 Pnoise P_{\text {noise }} 下随机抽取 KK 个错误的词(称为负样本),我们希望模型给这些负样本的分数尽可能低

    E=logσ(oj)+i=1KEiPnoise (i)[log(σ(oi))]\mathrm{E}=\log \sigma\left(o_j\right)+\sum_{i=1}^K \mathbb{E}_{i \sim P_{\text {noise }}(i)}\left[\log \left(\sigma\left(-o_i\right)\right)\right]

    其中 σ()\sigma() 是 Sigmoid 函数,输出值范围是 (0,1)(0, 1)

    σ(y)=eyey+1=11+ey\sigma(y)=\frac{e^y}{e^y+1}=\frac{1}{1+e^{-y}}

  • 优势:不需要归一化,甚至不需要计算每个单词的输出概率!

    • 我们只需要计算正样本和选中的几个负样本的分数并更新对应的权重即可,计算量大大减少
  • 噪声分布:负样本的抽样并不是完全均匀随机的,而是采用如下的噪声分布,其中 αi\alpha_i 是单词 ii 在语料库中出现的频率

    P(wi)=αi34jαj34P(w_i)=\frac{\alpha_i^{\frac{3}{4}}}{\sum_j \alpha_j^{\frac{3}{4}}}

    • 采用小于 1 的幂,可以适当增加罕见词被采样为负样本的概率,避免高频词总是被选中,有助于改善训练得到的词向量性能

词向量的性质

通过训练得到的词向量具有非常有趣的语义特性。

  • 语义相似性:语义相近的词,其词向量在空间上也相近
    • 例如,king 和 queen 的向量很接近
  • 线性关系:词向量在空间中的偏移量可以捕捉到词与词之间的类比关系
    • 示例 1u(queen)u(woman)u(king)u(man)\mathbf{u}(\text{queen}) - \mathbf{u}(\text{woman}) \approx \mathbf{u}(\text{king}) - \mathbf{u}(\text{man})
    • 示例 2u(France)u(Paris)u(England)u(London)\mathbf{u}(\text{France}) - \mathbf{u}(\text{Paris}) \approx \mathbf{u}(\text{England}) - \mathbf{u}(\text{London})

词向量模型的缺点

传统的词向量模型存在多义词问题。

  • 多义词问题:传统的词向量模型为每个词只学习一个固定的向量,无法解决一词多义的问题
  • 示例:“苹果”可以指水果,也可以指苹果公司
    • “他手里拿了一个苹果正在吃”
    • “他手里拿了一个苹果然后插上了充电器”
    • 在这两种语境下,模型会给“苹果”同一个词向量,这显然是不准确的

高级版:与上下文相关的词向量(context-dependent word vectors)

  • 解决方案:需要词向量的高级版——与上下文相关的词向量 (Context-dependent word vectors)
    • 例如后续课程会讲到的动态词向量和预训练模型

常用工具

Lecture 3: 矩阵求导和反向传播

这节课讲解了神经网络的数学基础,以及反向传播算法。

导数与梯度

  • 导数 (Derivative):

    • 定义:一个标量,表示当输入变量 xx 发生微小改变时,标量函数 f(x)f(x) 的变化率

    f(x)f(x+h)f(x)hf^{\prime}(x) \approx \frac{f(x+h)-f(x)}{h}

  • 偏导数 (Partial Derivative):

    • 定义:一个标量,对于多变量函数 f(x1,,xn)f(x_1, \dots, x_n),偏导数是只考虑其中一个变量变化时的导数,而将其他变量视为常数
    • 示例:对于 f(x,y)=x2+xyf(x, y) = x^2 + xy,对 xx 的偏导 fx=2x+y\dfrac{\partial f}{\partial x}=2 x+y
  • 梯度 (Gradient):

    • 定义:一个向量,由函数所有变量的偏导数按顺序组成
    • 公式:假设函数 ff 具有 nn 个输入和 1 个输出,即 f(x)=f(x1,x2,,xn)f(\boldsymbol{x})=f\left(x_1, x_2, \ldots, x_n\right),那么

    f(x)=fx=[fx1,fx2,,fxn]\nabla f(\boldsymbol{x})=\frac{\partial f}{\partial \boldsymbol{x}}=\left[\frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, \ldots, \frac{\partial f}{\partial x_n}\right]

    • 用途:梯度向量指向函数值增长最快的方向
      • 在机器学习中,我们通常沿着负梯度方向更新参数,以最快速度减小损失函数
  • Hessian 矩阵 (海森矩阵):

    • 定义:一个矩阵,是梯度的一阶导,即对梯度的每个分量(偏导数)再求一次所有偏导数,包含了函数的所有二阶偏导数

    H(f(x))=2f(x)=2fx2=[2fx122fx1xn2fxnx12fx12]H(f(\boldsymbol{x}))=\nabla^2 f(\boldsymbol{x})=\frac{\partial^2 f}{\partial \boldsymbol{x}^2}=\left[\begin{array}{ccc} \dfrac{\partial^2 f}{\partial x_1^2} & \cdots & \dfrac{\partial^2 f}{\partial x_1 \partial x_n} \\ \vdots & \ddots & \vdots \\ \dfrac{\partial^2 f}{\partial x_n \partial x_1} & \cdots & \dfrac{\partial^2 f}{\partial x_1^2} \end{array}\right]

雅各比矩阵 (Jacobian Matrix)

当函数不仅有多个输入,还有多个输出时,梯度的概念被推广为雅各比矩阵。

  • 场景:函数 ffnn 个输入 和 mm 个输出

    f(x)=[f1(x1,x2,,xn),,fm(x1,x2,,xn)]\boldsymbol{f}(\boldsymbol{x})=\left[f_1\left(x_1, x_2, \ldots, x_n\right), \ldots, f_m\left(x_1, x_2, \ldots, x_n\right)\right]

  • 定义:一个 m×nm \times n 的矩阵,其中第 $i4 行、第 jj 列的元素是第 ii 个输出 fif_i 对第 jj 个输入 xjx_j 的偏导数 fixj\dfrac{\partial f_i}{\partial x_j}

    fx=[f1x1f1xnfmx1fmxn]\frac{\partial \boldsymbol{f}}{\partial \boldsymbol{x}}=\left[\begin{array}{ccc} \dfrac{\partial f_1}{\partial x_1} & \cdots & \dfrac{\partial f_1}{\partial x_n} \\ \vdots & \ddots & \vdots \\ \dfrac{\partial f_m}{\partial x_1} & \cdots & \dfrac{\partial f_m}{\partial x_n} \end{array}\right]

    • 雅各比矩阵是向量对向量求导的结果,包含了所有可能的一阶偏导数
  • 经典例子

    • f(x)=Wx+b,xRn,WRm×n,bRmfx=W,fb=I\boldsymbol{f}(\boldsymbol{x})=\boldsymbol{W} \boldsymbol{x}+\boldsymbol{b}, \boldsymbol{x} \in \boldsymbol{R}^n, \boldsymbol{W} \in \boldsymbol{R}^{m \times n}, \boldsymbol{b} \in \boldsymbol{R}^m \quad \Rightarrow \quad \dfrac{\partial \boldsymbol{f}}{\partial \boldsymbol{x}} = \boldsymbol{W}, \dfrac{\partial \boldsymbol{f}}{\partial \boldsymbol{b}} = \boldsymbol{I}
    • f(u)=uTh,uRn,hRnfu=hT\boldsymbol{f}(\boldsymbol{u})=\boldsymbol{u}^T \boldsymbol{h}, \boldsymbol{u} \in \boldsymbol{R}^n, \boldsymbol{h} \in \boldsymbol{R}^{n} \quad \Rightarrow \quad \dfrac{\partial \boldsymbol{f}}{\partial \boldsymbol{u}} = \boldsymbol{h}^T

链式法则 (Chain Rule)

  • 复合函数的梯度(标量形式)

    • 法则:最终输出对最初输入的导数,等于所有中间环节偏导数的乘积
    • 示例:设有函数 z=3yz = 3yy=x2y = x^2,那么 zzxx 的导数

    dzdx=dzdydydx=(3)(2x)=6x\frac{d z}{d x}=\frac{d z}{d y} \frac{d y}{d x}=(3)(2 x)=6 x

  • 复合矩阵函数的梯度

    • 法则:当函数涉及向量和矩阵时,法则依然成立,只是乘法变成了雅各比矩阵的乘法
    • 示例:设有矩阵函数 h=f(z)\boldsymbol{h} = f(\boldsymbol{z})z=Wx+b\boldsymbol{z} = \boldsymbol{W}\boldsymbol{x} + \boldsymbol{b},那么 h\boldsymbol{h}x\boldsymbol{x} 的雅各比矩阵

    dhdx=dhdzdzdx\frac{d \boldsymbol{h}}{d \boldsymbol{x}}=\frac{d \boldsymbol{h}}{d \boldsymbol{z}} \frac{d \boldsymbol{z}}{d \boldsymbol{x}}

这正是反向传播算法的数学本质:误差从网络末端逐层向前端传播,每经过一层,就乘以该层的雅各比矩阵。

反向传播算法实例

我们以带有激活函数的 3 层 MLP 为例,介绍反向传播算法。

我们的目标是利用雅各比矩阵和链式法则,推导出损失 EE 对每一层权重 V\boldsymbol{V}, W\boldsymbol{W}, U\boldsymbol{U} 的梯度。

首先,计算输出层的误差信号,即损失 EE 对输出分数 o\boldsymbol{o} 的梯度。

E=oj+logt=1Nexp(ot)E=-o_j+\log \sum_{t=1}^N \exp \left(o_t\right)

Eo={1+exp(oi)t=1Nexp(ot)(i=j)exp(oi)t=1Nexp(ot)(ij)\frac{\partial \mathrm{E}}{\partial \mathbf{o}}=\left\{\begin{array}{cc} -1+\dfrac{\exp \left(o_i\right)}{\sum_{t=1}^N \exp \left(o_t\right)} & (\mathrm{i}=\mathrm{j}) \\ \dfrac{\exp \left(o_i\right)}{\sum_{t=1}^N \exp \left(o_t\right)} & (\mathrm{i} \neq \mathrm{j}) \end{array}\right.

引入误差信号 e\mathbf{e},则得到

Eo=e={yi1(i=j)yi(ij)\frac{\partial \mathrm{E}}{\partial \mathbf{o}}=\mathbf{e}=\left\{\begin{array}{cc} y_i-1 & (\mathrm{i}=\mathrm{j}) \\ y_i & (\mathrm{i} \neq \mathrm{j}) \end{array}\right.

现在有了起始的误差信号 e\mathbf{e},就可以利用链式法则逐层向后计算了。

EV=EooV=eh2T\frac{\partial \mathrm{E}}{\partial \mathbf{V}}=\frac{\partial \mathrm{E}}{\partial \mathbf{o}} \frac{\partial \mathbf{o}}{\partial \mathbf{V}}=\mathbf{e} \boldsymbol{h}_2^T

EW=Eooh2h2W=eV1[Wh10]h1T\frac{\partial \mathrm{E}}{\partial \mathbf{W}} =\frac{\partial \mathrm{E}}{\partial \mathbf{o}} \frac{\partial \mathbf{o}}{\partial \boldsymbol{h}_2} \frac{\partial \boldsymbol{h}_2}{\partial \mathbf{W}} = \mathbf{e V} \cdot \mathbf{1}\left[\boldsymbol{W} \boldsymbol{h}_1 \geq \mathbf{0}\right] \cdot \boldsymbol{h}_1^T

EU=Eooh2h2h1h1U=eV1[Wh10]W1[Ux0]xT\frac{\partial \mathrm{E}}{\partial \mathbf{U}} =\frac{\partial \mathrm{E}}{\partial \mathbf{o}} \frac{\partial \mathbf{o}}{\partial \boldsymbol{h}_{\mathbf{2}}} \frac{\partial \boldsymbol{h}_{\mathbf{2}}}{\partial \boldsymbol{h}_{\mathbf{1}}} \frac{\partial \boldsymbol{h}_{\mathbf{1}}}{\partial \mathbf{U}} = \mathbf{e V} \cdot \mathbf{1}\left[\boldsymbol{W} \boldsymbol{h}_1 \geq \mathbf{0}\right] \cdot \mathrm{W} \cdot \mathbf{1}[\boldsymbol{U} \boldsymbol{x} \geq \mathbf{0}] \cdot \mathbf{x}^T

观察上述推导,我们可以总结出反向传播的规律:

任何一层权重矩阵的梯度 = 反向传播到该层的误差信号 × 该层在前向传播时的输入

  • 对于 V\boldsymbol{V},误差信号是 e\mathbf{e},输入是 h2\boldsymbol{h}_2
  • 对于 W\boldsymbol{W},误差信号是 e\mathbf{e} 经过 V\boldsymbol{V} 和 ReLU 传播后的 eV1[Wh10]\mathbf{e V} \cdot \mathbf{1}\left[\boldsymbol{W} \boldsymbol{h}_1 \geq \mathbf{0}\right],输入是 h1\boldsymbol{h}_1
  • 对于 U\boldsymbol{U},误差信号是继续经过 W\boldsymbol{W} 和 ReLU 传播后的 eV1[Wh10]W1[Ux0]\mathbf{e V} \cdot \mathbf{1}\left[\boldsymbol{W} \boldsymbol{h}_1 \geq \mathbf{0}\right] \cdot \mathrm{W} \cdot \mathbf{1}[\boldsymbol{U} \boldsymbol{x} \geq \mathbf{0}],输入是 x\mathbf{x}

计算图 (Computational Graph)

使用计算图,我们可以更系统、更模块化地理解和实现反向传播。

  • 计算图:来表复合函数的运算过程,其中每个内部节点代表一次计算
    • 前向传播:数据和计算结果沿着图的边,从输入到输出正向流动
    • 反向传播:误差信号(梯度)沿着图的边,从输出到输入反向流动

  • 反向传播的法则:Downstream Gradient = Upstream Gradient × Local Gradient
    • Upstream Gradient (上游梯度): 从上游(靠近最终输出)传来的梯度
    • Local Gradient (本地梯度): 节点自身运算对输入的导数(雅各比矩阵)
    • Downstream Gradient (下游梯度): 节点计算后,准备传给下游(靠近初始输入)的梯度

  • 简单实例

反向传播的代码实现

现代深度学习框架正是基于计算图的思想来自动实现反向传播的。

  • 实现概述

    • 第一步:前向传播:按拓扑顺序遍历计算图,计算每个节点的值,并缓存中间结果(如输入值)
    • 第二步:反向传播:逆序遍历计算图,每个节点接收上游梯度,利用缓存的输入值计算本地梯度,然后计算并传递下游梯度
  • 伪代码

1
2
3
4
5
6
7
8
9
10
11
12
class ComputationalGraph:
def forward(inputs):
# 按照拓扑顺序执行每个节点的 forward()
for gate in self.graph.nodes_topologically_sorted():
gate.forward()
return loss

def backward():
# 按照拓扑逆序执行每个节点的 backward()
for gate in reversed(self.graph.nodes_topologically_sorted()):
gate.backward() # 内部应用链式法则
return input_gradients
  • 单节点代码实现:以乘法节点为例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 乘法节点的实现
class MultiplyGate:
def forward(x, y):
self.x = x # 缓存输入,供反向传播使用
self.y = y
z = x * y
return z

def backward(dz): # dz 是 upstream_gradient
# local_gradients: dz/dx = y, dz/dy = x
# downstream = upstream * local
dx = self.y * dz
dy = self.x * dz
return [dx, dy]

Lecture 4: 语言模型

什么是语言模型

  • 语言建模 (Language Modeling):是一个学习任务,其核心目标是让一个模型学会根据给定的上文,来预测下一个最可能出现的词
    • 例子:给定句子“学生们在课堂上打开了他们的___”,模型需要预测空格处最可能填入的词,比如“课本”、“电脑”等
    • 正式定义:给定一个词序列 x(1),x(2),,x(t)x^{(1)}, x^{(2)}, \ldots, x^{(t)},语言建模任务的目标是计算下一个词 x(t+1)x^{(t+1)} 的条件概率分布 P(x(t+1)x(1),x(2),,x(t))P\left(x^{(t+1)} \mid x^{(1)}, x^{(2)}, \ldots, x^{(t)}\right),这个分布会告诉我们词典 VV 中每一个词出现在下一个位置的概率
  • 语言模型 (Language Model):执行“语言建模”这个任务的系统或模型,就被称为语言模型

语言模型的应用场景

  • 计算文本概率:语言模型最基本的功能是计算一段文本出现的概率,可以理解为这段文本有多“通顺”

    • 原理:利用概率的链式法则,一段文本 x(1),x(2),,x(T)x^{(1)}, x^{(2)}, \ldots, x^{(T)} 的联合概率可以分解为一系列条件概率的乘积:

      P(x(1),x(2),,x(T))=P(x(1))×P(x(2)x(1))××P(x(T)x(T1),,x(1))=t=1TP(x(t)x(t1),,x(1))\begin{aligned} P\left(x^{(1)}, x^{(2)}, \ldots, x^{(T)}\right) & =P\left(x^{(1)}\right) \times P\left(x^{(2)} \mid x^{(1)}\right) \times \cdots \times P\left(x^{(T)} \mid x^{(T-1)}, \ldots, x^{(1)}\right) \\ & =\prod_{t=1}^T P\left(x^{(t)} \mid x^{(t-1)}, \ldots, x^{(1)}\right) \end{aligned}

      这个分解后的每一项,正好就是语言模型所计算的概率。
  • 联想输入/自动补全:在搜索引擎、输入法中,当你输入“我明天要去…”时,系统会推荐“医院”、“学校”等词

    • 这背后就是语言模型在预测下一个最可能的词
  • 文本生成:语言模型可以像“文字接龙”一样生成连贯的文本

    • 生成过程
      1. 给定一个初始条件(如 “today the”)
      2. 语言模型计算出下一个词的概率分布
      3. 从这个分布中采样一个词(如 “price”)并添加到末尾
      4. 将新的序列(“today the price”)作为新的条件,重复上述过程
    • 通过这种自回归的方式,模型可以生成很长的文本
  • 机器翻译:在统计机器翻译中,语言模型是核心组件之一

    • 翻译模型可能会生成多个候选句子,例如对于“我对你感到满意”,可能生成 “I satisfied with you” 或 “I’m satisfied with you”
    • 语言模型会判断出 “I’m satisfied with you” 这句话在英语中出现的概率更高,更通顺,从而帮助翻译引擎选择最优的翻译结果

语言模型的种类

  • 基于统计的方法:这类方法的代表就是 N-gram 语言模型,它通过统计大规模语料库中不同词序列(n-grams)的出现频率来计算概率

  • 基于神经网络的方法:这类方法使用神经网络(如 RNN, Transformer)来学习词与词之间的复杂依赖关系

如何评价语言模型的性能:混淆度(perplexity)

  • 分类精度不适用:对于分类任务,我们可以用“准确率”来评价模型好坏,但语言模型不行,原因是:

    • 我们关心的不仅仅是“下一个词是否预测正确”,而是模型在每个预测位置给出的概率分布,特别是真实的下一个词的概率
    • 因此,简单判断“下一个词是否预测正确”无法有效衡量语言模型的性能
  • 评价标准:给定一段模型没有见过的、足够长的、真实的测试语料,模型对这段语料预测的概率越高,说明这个模型越好

    • 核心思想:一个好的语言模型,应该能够给真实、自然的句子赋予高概率
    • 足够长:避免模型因为运气好,刚好在要预测的几个位置上表现良好
    • 没见过:防止模型通过“背诵”训练集来作弊
  • 混淆度 (Perplexity, PPW) 的推导

    1. 计算测试集概率:首先,我们计算模型预测 TT 个单词组成的测试集 Dtest =[x(1),x(2),,x(T)]\mathfrak{D}_{\text {test }}=\left[x^{(1)}, x^{(2)}, \ldots, x^{(T)}\right] 的文本概率

    P(x(1),x(2),,x(T))=P(x(1))×P(x(2)x(1))××P(x(T)x(T1),,x(1))=t=1TP(x(t)x(t1),,x(1))\begin{aligned} P\left(x^{(1)}, x^{(2)}, \ldots, x^{(T)}\right) & =P\left(x^{(1)}\right) \times P\left(x^{(2)} \mid x^{(1)}\right) \times \cdots \times P\left(x^{(T)} \mid x^{(T-1)}, \ldots, x^{(1)}\right) \\ & =\prod_{t=1}^T P\left(x^{(t)} \mid x^{(t-1)}, \ldots, x^{(1)}\right) \end{aligned}

    1. 长度归一化(几何平均)
      • 由于概率是连乘的,句子越长,总概率会越小
      • 为了消除长度影响,我们将总概率平均到每个词上
      • 因为是乘积,所以使用几何平均:

      P(x(1),x(2),,x(T))T=t=1TP(x(t)x(t1),,x(1))T\sqrt[T]{P\left(x^{(1)}, x^{(2)}, \ldots, x^{(T)}\right)}=\sqrt[T]{\prod_{t=1}^T P\left(x^{(t)} \mid x^{(t-1)}, \ldots, x^{(1)}\right)}

      • 物理意义:在测试集中,平均每个模型做出预测的位置上,那个实际出现的真实词被模型赋予的概率
    2. 取倒数,得到混淆度:为了让指标更直观(数值越低越好),我们对上一步的结果取倒数,得到最终的评价指标——混淆度 (Perplexity per Word, PPW)

    PPW=1t=1TP(x(t)x(t1),,x(1))T\mathrm{PPW}=\frac{1}{\sqrt[T]{\prod_{t=1}^T P\left(x^{(t)} \mid x^{(t-1)}, \ldots, x^{(1)}\right)}}

    • 物理意义:在测试集中,对于每个模型做出预测的位置,那个实际出现的真实词平均被模型赋予的概率的倒数
  • 混淆度的理解

    • 取值范围1PPWV1 \le \mathrm{PPW} \le |V| (词典大小),PPW 越低,模型性能越好
    • 两个极端例子
      • 完美模型:总能以 100% 的概率预测到正确的词,其 PPW 为 1
      • 白板模型:对所有词都给出均匀概率 1V\frac{1}{|V|},其 PPW 为 V|V|
    • 直观解释:混淆度可以被看作是模型在预测下一个词时不确定的选项数量
      • 如果一个模型的混淆度是 30,这意味着模型在做预测时,其不确定性等价于从 30 个合理的候选词中进行随机猜测
      • 这个值越低,说明模型对下一个词的判断越有把握,候选范围越小,性能越好

N-gram 语言模型

在了解了什么是语言模型之后,我们介绍第一种实现方法——基于统计的 N-gram 语言模型。

N-gram 的定义

  • 定义:N-gram 是指文本中连续出现的 nn 个单词组成的块
    • 以句子 “学生/打开/了/他们/的/____” 为例
    • Unigram (1-gram): 单个词,如 “学生”, “打开”, “了”
    • Bigram (2-gram): 两个连续的词,如 “学生 打开”, “打开 了”
    • Trigram (3-gram): 三个连续的词,如 “学生 打开 了”
    • 4-gram: “学生 打开 了 他们”。
  • 核心思想:N-gram 语言模型通过统计不同 n-gram 在大型语料库中出现的频率,来预测下一个词的概率

如何确定 N-gram 语言模型中的参数

我们知道,一个词的出现概率严格来说依赖于它前面所有的词。为了简化问题,N-gram 模型引入了马尔科夫假设。

  • 马尔科夫假设:一个词的出现概率,只与它前面 n1n-1 个词有关,而与其他更早的词无关

  • N-gram 概率的计算方法:有了马尔科夫假设,可以利用条件概率来计算 N-gram 概率

    • 分子是整个 n-gram 序列出现的概率
    • 分母是其前缀 (n-1)-gram 序列出现的概率
    • 通过在大型语料库中进行计数的方式,来近似这些概率
  • 示例

最大似然估计

我们上面用“数数”的方法(即用频率代替概率)来估计参数,这看起来非常直观。但这种做法有理论依据吗?它是否能保证我们的模型是“最优”的呢?

  • 训练模型的初衷:调整模型参数,以最大化模型在训练数据上预测出的总概率(即似然函数)

  • 最大似然估计 (MLE): 是一种经典的参数估计方法

    • 核心思想:寻找一组参数 θ\theta,使得在这组参数下,我们观测到的训练数据样本出现的概率是最大的
  • MLE 的结论:通过数学推导(拉格朗日乘子法),我们发现对 N-gram 语言模型,能够使其在训练集上似然函数最大化的参数 P(vy)P(v|y)(即给定前文 yy,下一个词是 vv 的概率),其最优解恰好就是我们通过频率计数得到的结果

    • 理解:我们凭直觉使用的“数数”方法,实际上就是在进行最大似然估计,它不仅简单直观,而且在数学上也是最优的

Unigram、bigram 示例

  • N-gram 模型的局限性
    • 上下文窗口有限:马尔科夫假设丢弃了长距离的依赖关系
      • 例如,在 “当监考员发出指令后,学生打开了他们的___” 这个例子中,“监考员”这个远距离的词其实对预测“试题”很有帮助,但 N-gram 模型(如果 n 不够大)会忽略这个信息
    • 生成的文本语义不连贯:虽然 N-gram 生成的文本在局部(n 个词的窗口内)是通顺的,但由于缺乏全局的语义理解,长文本往往会出现逻辑跳跃和语义不连贯的问题
    • 数据稀疏问题:这是 N-gram 模型最致命的问题,我们将在下一部分详细讨论

N-gram 语言模型中的数据稀疏问题及解决办法

数据稀疏是所有基于统计的自然语言处理方法(尤其是 N-gram 模型)面临的核心挑战。

  • 问题根源:语言的组合性是爆炸性的,随着 $n4 的增大,可能出现的 n-gram 组合数量会急剧增加,远远超过任何有限语料库能够覆盖的范围
  • 具体表现
    • 零概率 (Unseen Events):很多在现实中完全合理、通顺的 n-gram,可能因为我们的训练语料库不够大,而一次都没有出现过
    • 低频次 (Very Low Frequency):某些 n-gram 虽然出现过,但次数非常少(比如仅 1 次),基于这么少的样本做出的概率估计是极其不可靠的
  • 例子:在 Berkeley Restaurant Corpus 上,即使是对于最简单的 bigram 模型,语料中超过 90% 的可能的 bigram 组合都是没有出现过的(即次数为 0)

折扣法(Discounting)

  • 方法:修正原始的 n-gram 的频次,将一部分高频 n-gram 的概率值分配到那些零样本的 n-gram 上,使得概率量重新分布

回退(Backoff)及插值(interpolation)

  • 回退 (Backoff):当更高阶的 n-gram 不存在时,回退到使用低一阶的 n-gram 算出来的条件概率来替代

    • 例子:当我们试图计算一个 trigram 概率 P(w3w1,w2)P(w_3|w_1, w_2) 时:
      • 先检查 (w1,w2,w3)(w_1, w_2, w_3) 这个 trigram 在语料中是否出现过
      • 如果出现过,就直接用它的统计数据来计算概率
      • 如果没出现过(数据稀疏),我们就回退到低一阶的模型,即用 bigram 概率 P(w3w2)P(w_3|w_2) 来近似替代
      • 如果 bigram (w2,w3)(w_2, w_3) 也没出现过,就继续回退到 unigram 模型,即用 P(w3)P(w_3) 来替代
  • 插值 (Interpolation):将不同阶的 n-gram 给出的条件概率线性组合起来,给它们不同的权重

神经网络语言模型

前馈神经网络

这种方法直接借鉴了 N-gram 模型的思路:使用固定长度的前文(历史词)来预测下一个词。但它用神经网络代替了简单的频率计数。

  • 方法:将 n-1 个历史词的词向量拼接起来,形成一个大的向量,然后将其输入到一个前馈神经网络中,网络最终输出下一个词的概率分布

  • 计算流程:以 “喜 欢 这 部 __” 为例

    • 输入层:给定前文 “喜”, “欢”, “这”, “部”,我们将每个字表示为其独热向量 (one-hot vector) x(1),x(2),x(3),x(4)\boldsymbol{x}^{(1)}, \boldsymbol{x}^{(2)}, \boldsymbol{x}^{(3)}, \boldsymbol{x}^{(4)}
    • 词嵌入层:通过查询一个词向量矩阵 EE (这是一个可学习的参数),将每个字的独热向量转换为低维稠密的词向量 e(1),e(2),e(3),e(4)\boldsymbol{e}^{(1)}, \boldsymbol{e}^{(2)}, \boldsymbol{e}^{(3)}, \boldsymbol{e}^{(4)}
    • 拼接 (Concatenation):将这4个词向量拼接成一个长向量 e=[e(1),e(2),e(3),e(4)]\boldsymbol{e}=\left[\boldsymbol{e}^{(1)}, \boldsymbol{e}^{(2)}, \boldsymbol{e}^{(3)}, \boldsymbol{e}^{(4)}\right]
    • 隐藏层:将拼接后的向量 e\boldsymbol{e} 通过一个带激活函数 ff 的全连接层,计算出隐藏层表示 h\boldsymbol{h}h=f(W1e+b1)\boldsymbol{h}=f\left(\boldsymbol{W}_{\mathbf{1}} \boldsymbol{e}+\boldsymbol{b}_{\mathbf{1}}\right)
    • 输出层:将隐藏层表示 h\boldsymbol{h} 通过第二个全连接层,并使用 Softmax 函数,最终得到词典中每个词的概率分布 y^\boldsymbol{\hat{y}}y^=Softmax(W2h+b2)\hat{\boldsymbol{y}}=\operatorname{Softmax}\left(\boldsymbol{W}_{\mathbf{2}} \boldsymbol{h}+\boldsymbol{b}_{\mathbf{2}}\right)

这个模型由深度学习先驱 Yoshua Bengio 在 2003 年提出,是神经网络语言模型的开山之作。

  • 优点

    • 缓解数据稀疏:通过使用词向量,模型可以在一个语义空间中操作
      • 即使“喜欢这部电影”没见过,但如果模型知道“电影”和“影片”的词向量很相似,它就能将在“喜欢这部影片”上学到的知识泛化过来,这比 N-gram 的严格匹配要好得多
    • 参数效率更高:N-gram 模型需要存储海量的 n-gram 频次表,而神经网络模型只需要存储几个权重矩阵,通常占用空间更小
    • 并行计算:由于每次预测的输入长度是固定的,理论上可以并行处理不同位置的预测任务
  • 问题与局限

    • 无法有效建模长时依赖:和 N-gram 一样,它只能看到固定长度 n 的历史,无法建模长时依赖
    • 参数量与窗口大小 n 相关:权重矩阵 W1\boldsymbol{W_1} 的大小 dh×(n×d)d_h \times (n \times d) 直接依赖于 nn,如果想扩大历史窗口 nn,参数量会线性增长,导致存储和计算问题

循环神经网络

为了解决前馈神经网络语言模型的局限性,特别是固定窗口和无法建模长时依赖的问题,循环神经网络 (RNN) 应运而生。其突破性思想在于在时间维度上共享参数

  • 权重矩阵的问题:前馈模型中,权重矩阵 W1\boldsymbol{W_1} 对每个位置的输入词都有独立的参数,这意味着:

    • 需要单独对每一个历史时刻的词学习参数
    • 不同时刻间共通的处理要不断重复学习
  • 重构权重矩阵:RNN 认为,处理不同位置的词的方式应该是相同的,因此,它将 W1\boldsymbol{W_1} 分解并重构为:

    • 一个用于处理当前时刻输入的权重 WeRdh×d\boldsymbol{W_e} \in \mathbb{R}^{d_h \times d}
    • 一个用于处理上一时刻信息的权重 WhRdh×dh\boldsymbol{W_h} \in \mathbb{R}^{d_h \times d_h}
    • 这两个权重矩阵 We\boldsymbol{W_e}Wh\boldsymbol{W_h} 的尺寸不再与历史长度 nn 相关,并且在处理序列的每一步中,使用的都是同一套 We\boldsymbol{W_e}Wh\boldsymbol{W_h}
  • RNN 的计算流程:RNN 引入了隐藏状态 (Hidden State) h\boldsymbol{h},可以理解为一个记忆单元,在时间步之间传递信息

    • 初始化:在处理序列开始前,初始化一个隐藏状态 h(0)\boldsymbol{h^{(0)}} (通常为零向量)
    • 循环计算 (在每个时间步 tt)
      1. 输入:当前时刻的词向量 e(t)\boldsymbol{e^{(t)}} 和上一时刻的隐藏状态 h(t1)\boldsymbol{h^{(t-1)}}
      2. 更新隐藏状态:将两者结合,通过一个激活函数 σ\sigma,计算出当前时刻的隐藏状态 h(t)\boldsymbol{h^{(t)}}

      h(t)=σ(Whh(t1)+Wee(t)+b1)\boldsymbol{h}^{(t)}=\sigma\left(\boldsymbol{W}_{\boldsymbol{h}} \boldsymbol{h}^{(t-1)}+\boldsymbol{W}_{\boldsymbol{e}} \boldsymbol{e}^{(t)}+\boldsymbol{b}_{\mathbf{1}}\right)

      1. 输出:使用当前隐藏状态 h(t)\boldsymbol{h^{(t)}} 来预测下一个词的概率分布:

      y^(t)=Softmax(Woh(t)+b2)\hat{\boldsymbol{y}}^{(t)}=\operatorname{Softmax}\left(\boldsymbol{W}_{\boldsymbol{o}} \boldsymbol{h}^{(t)}+\boldsymbol{b}_{\mathbf{2}}\right)

  • RNN 的训练
    • 损失函数:在每个时间步 tt,我们都有一个预测 y^(t)\boldsymbol{\hat{y}^{(t)}} 和一个真实目标(即序列中的下一个词 x^(t+1)\boldsymbol{\hat{x}^{(t+1)}}),我们可以计算一个交叉熵损失

      L(t)(θ)=wVpw(t)logp^w(t)=logp^wt+1(t)\mathcal{L}^{(t)}(\theta)=-\sum_{w \in \mathcal{V}} p_w^{(t)} \log \hat{p}_w^{(t)}=-\log \hat{p}_{w_{t+1}}^{(t)}

    • 总损失:整个序列的总损失是所有时间步损失的平均值

      L(θ)=1Tt=1TL(t)(θ)=1Tt=1Tlogp^wt+1(t)\mathcal{L}(\theta)=\frac{1}{T} \sum_{t=1}^T \mathcal{L}^{(t)}(\theta)=-\frac{1}{T} \sum_{t=1}^T \log \hat{p}_{w_{t+1}}^{(t)}

  • RNN 的梯度计算:由于权重 Wh\boldsymbol{W_h}, We\boldsymbol{W_e}, Wo\boldsymbol{W_o} 在所有时间步都是共享的,一个参数在 tt 时刻的梯度,会受到 tt 时刻以及所有未来时刻损失的影响
    • 这意味着误差需要沿着时间步反向传播,这个过程被称为通过时间的反向传播 (BPTT)

  • RNN 的问题:RNN 在理论上可以捕捉无限长的依赖,但在实践中却很困难,主要原因是梯度消失/爆炸问题
    • 数学根源:在 BPTT 中,梯度从后向前传播时,需要反复连乘权重矩阵 Wh\boldsymbol{W_h}
      • 如果 Wh\boldsymbol{W_h} 的模(或最大奇异值)大于 1,经过多次连乘后,梯度会指数级增长,导致梯度爆炸(训练不稳定)
      • 如果 Wh\boldsymbol{W_h} 的模小于 1,经过多次连乘后,梯度会指数级衰减至 0,导致梯度消失
    • 梯度爆炸的解决:可以通过梯度裁剪 (gradient clipping) 来解决,即当梯度的范数超过一个阈值时,就将其缩放到阈值大小
    • 梯度消失
      • 根源:信息在时间维度上传递时的无差别衰减
      • 后果:模型无法学习到长距离依赖关系
        • 例如,在 “This kind of books is/are great.” 中,决定动词形式的是远处的 “kind” 而非 “books”,如果梯度在传回到 “kind” 之前就消失了,模型就无法学到这个语法规则
      • 解决思路:向下一时刻传递信息时,考虑当前时刻的上下文情况,有选择地保留、遗忘或更新信息
        • 我们需要一种机制,让模型可以根据当前输入 e(t)\boldsymbol{e^{(t)}} 的重要性,来动态地决定应该保留多少 h(t1)\boldsymbol{h^{(t-1)}} 中的旧信息,并添加多少新信息

长短时记忆网络 LSTM 及 GRU

长短时记忆网络 (Long Short-Term Memory, LSTM)

LSTM 是 RNN 的一种高级变体,旨在解决梯度消失问题,从而更好地捕捉长距离依赖。

  • 核心思想

    • 分离长期记忆与短期记忆
      • 长期记忆 (Cell State):引入一个新的向量 c(t)Rn\boldsymbol{c^{(t)}} \in \mathbb{R}^n,负责存储和传递长期的状态信息,每一时刻根据上下文情况更新,且不能被外界直接观察
      • 短期记忆 (Hidden State)h(t)Rn\boldsymbol{h^{(t)}} \in \mathbb{R}^n 依然存在,但其角色变为当前时刻被“激活”的长期记忆,它根据当前上下文,从长期记忆 c(t)\boldsymbol{c^{(t)}} 中读取信息,用于指导当前时刻的输出和下一时刻的长期记忆更新
    • 门控机制 (Gating Mechanism):引入了门 (Gate) 的概念来精细地控制信息的流动
      • 门是什么:一个与记忆向量维度相同的向量,其每个元素都是 0 到 1 之间的实数(通过 Sigmoid 激活函数得到)
      • 如何工作:将“门”向量与另一个信息向量进行逐元素相乘
        • 当门的值接近 0 时,相当于“关闭”,信息无法通过
        • 当门的值接近 1 时,相当于“开启”,信息可以顺利通过
      • 动态生成:门是根据当前输入 x(t)\boldsymbol{x^{(t)}} 和上一时刻的短期记忆 h(t1)\boldsymbol{h^{(t-1)}} 动态生成的,这使得 LSTM 可以根据上下文智能地控制信息流
  • LSTM 的公式与三个核心门:在每个时间步 tt,LSTM 输入词向量 x(t)\boldsymbol{x^{(t)}},通过三个门来更新其长期记忆 c(t)\boldsymbol{c^{(t)}} 和短期记忆 h(t)\boldsymbol{h^{(t)}}

    • 遗忘门 (Forget Gate, f(t)\boldsymbol{f^{(t)}}):决定应该从上一时刻的长期记忆 c(t1)\boldsymbol{c^{(t-1)}} 中丢弃哪些信息。

      f(t)=σ(Wf[h(t1);x(t)]+bf)\boldsymbol{f}^{(t)}=\sigma\left(\boldsymbol{W}_{\boldsymbol{f}}\left[\boldsymbol{h^{(t-1)}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{f}}\right)

    • 输入门 (Input Gate, i(t)\boldsymbol{i^{(t)}}):决定当前时刻有哪些新的信息可以被写入长期记忆

      i(t)=σ(Wi[h(t1);x(t)]+bi)\boldsymbol{i}^{(t)}=\sigma\left(\boldsymbol{W}_{\boldsymbol{i}}\left[\boldsymbol{h^{(t-\mathbf{1})}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{i}}\right)

    • 输出门 (Output Gate, o(t)\boldsymbol{o^{(t)}}):决定从更新后的长期记忆 c(t)\boldsymbol{c^{(t)}} 中,提取哪些信息作为当前时刻的短期记忆 h(t)\boldsymbol{h^{(t)}} 输出出去

      o(t)=σ(Wo[h(t1);x(t)]+bo)\boldsymbol{o}^{(\boldsymbol{t})}=\sigma\left(\boldsymbol{W}_{\boldsymbol{o}}\left[\boldsymbol{h^{(t-1)}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{o}}\right)

  • 状态更新

    • 更新长期记忆 c(t)\boldsymbol{c^{(t)}}
      • 生成候选单元状态

        c~(t)=tanh(Wc[h(t1);x(t)]+bc)\tilde{\boldsymbol{c}}^{(t)}=\tanh \left(\boldsymbol{W}_{\boldsymbol{c}}\left[\boldsymbol{h^{(t-\mathbf{1})}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{c}}\right)

      • 更新单元状态:第一项表示忘记旧信息,第二项表示学习新信息

        c(t)=f(t)c(t1)+i(t)c~(t)\boldsymbol{c^{(\boldsymbol{t})}}=\boldsymbol{f^{(t)}} \circ \boldsymbol{c}^{(\boldsymbol{t}-\mathbf{1})}+\boldsymbol{i^{(t)}} \circ \tilde{\boldsymbol{c}}^{(\boldsymbol{t})}

    • 更新短期记忆 h(t)\boldsymbol{h^{(t)}}:从长期记忆中过滤并输出当前所需的信息

      h(t)=o(t)tanh(c(t))\boldsymbol{h}^{(\boldsymbol{t})}=\boldsymbol{o}^{(\boldsymbol{t})} \circ \tanh \left(\boldsymbol{c}^{(\boldsymbol{t})}\right)

  • 梯度消失的缓解:LSTM 让网络能够更容易的保留远距的信息,从而极大的缓解了梯度消失的问题
    • 朴素 RNN 需要 Wh\boldsymbol{W_h} 为单位矩阵以完全保留历史信息
    • 而 LSTM 只需要让遗忘门 f(t)\boldsymbol{f^{(t)}} 的值为全 1,就可以完全保留历史信息

门控循环单元 (Gated Recurrent Unit, GRU)

GRU 是 2014 年提出的 LSTM 的一个简化变体,它同样非常有效。

  • 核心思想

    • 合并记忆状态:GRU 没有独立的长期记忆单元 c\boldsymbol{c},而是将所有记忆信息都存储在唯一的隐藏状态 h\boldsymbol{h}
    • 简化门控:GRU 只使用两个门来控制信息流
  • GRU 的公式与两个核心门

    • 更新门 (Update Gate, z(t)\boldsymbol{z^{(t)}}):类似于 LSTM 中遗忘门和输入门的结合,它同时决定要忘记多少旧信息,以及要加入多少新信息

      z(t)=σ(Wz[h(t1);x(t)]+bz)\boldsymbol{z^{(t)}}=\sigma\left(\boldsymbol{W}_{\boldsymbol{z}}\left[\boldsymbol{h^{(t-\mathbf{1})}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{z}}\right)

    • 重置门 (Reset Gate, r(t)\boldsymbol{r^{(t)}}):决定在计算当前候选状态时,要忽略掉多少上一时刻的隐藏状态 h(t1)\boldsymbol{h^{(t-1)}}

      r(t)=σ(Wr[h(t1);x(t)]+br)\boldsymbol{r^{(t)}}=\sigma\left(\boldsymbol{W}_{\boldsymbol{r}}\left[\boldsymbol{h^{(t-\mathbf{1})}} ; \boldsymbol{x^{(t)}}\right]+\boldsymbol{b}_{\boldsymbol{r}}\right)

  • 状态更新

    • 计算候选状态:重置门 r(t)\boldsymbol{r^{(t)}} 控制了历史信息 h(t1)\boldsymbol{h^{(t-1)}} 的使用程度

      h~(t)=tanh(Wh[r(t)h(t1);x(t)]+bh)\boldsymbol{\tilde{h}^{(t)}}=\tanh \left(\boldsymbol{W}_{\boldsymbol{h}}\left[\boldsymbol{r^{(t)}} \circ \boldsymbol{h^{(\boldsymbol{t}-\mathbf{1})}} ; \boldsymbol{x^{(\boldsymbol{t})}}\right]+\boldsymbol{b}_{\boldsymbol{h}}\right)

    • 更新最终隐藏状态:更新门 z(t)\boldsymbol{z^{(t)}} 像一个开关,在旧状态 h(t1)\boldsymbol{h^{(t-1)}} 和新候选状态 h^(t)\boldsymbol{\hat{h}^{(t)}} 之间进行插值

      h(t)=(1z(t))h(t1)+z(t)h~(t)\boldsymbol{h^{(t)}}=\left(1-\boldsymbol{z^{(t)}}\right) \circ \boldsymbol{h^{(t-\mathbf{1})}}+\boldsymbol{z^{(t)}} \circ \tilde{\boldsymbol{h}}^{\boldsymbol{(t)}}

不同语言模型效果对比

Lecture 5: 分词

分词(Tokenization),又称为词元切分,是将原始的文本字符串分割成一个个有意义的基本单元(称为“词元”或 token)的过程。这是所有自然语言处理任务的第一步。

问题与需求

看似简单的切分任务,在实际中会遇到各种复杂情况。

  • 简单情况:对于像 “You are what you eat.” 这样的简单英文句子,直接按照空格和标点符号切分,通常就能得到不错的结果
    • You / are / what / you / eat / .
  • 复杂情况:缩写词 (Contractions)
    • 问题:如何处理像 “I’m”, “aren’t” 这样的缩写词
    • 方法一:作为单个词
      • I'maren't 视为独立的词元。
      • 优点:简单
      • 缺点:会增加词汇表的大小,加剧数据稀疏问题
    • 方法二:拆分处理
      • 将缩写词拆分为其组成部分,如 I'm -> I / 'maren't -> are / n't
      • 优点:可以有效减小词汇表,因为 aren't 可能会在别处出现
      • 缺点:需要制定更复杂的拆分规则
  • 复杂情况:数字与符号
    • 问题:如何处理像 $300,000,000Fig. 3 这样的组合
    • 方法一:完全拆分
      • $300,000,000 切分为 $ / 300 / , / 000 / , / 000
      • 缺点:完全失去了数字作为一个整体的语义
    • 方法二:不拆分
      • $300,000,000Fig. 3 保留为单个词元
      • 优点:保留了其作为一个整体的意义
      • 缺点:会造成词汇表爆炸,如 $300,000,001$300,000,002 将被视为两个完全不同的词元

词级别的词元切分 (Word-level Tokenization)

这种方法的目标是把文本切分成我们传统意义上的“单词”。

  • 基于规则的方法

    • 核心:使用正则表达式 (Regular Expressions) 来定义各种切分规则
    • 过程:通过不断编写和完善一系列正则表达式,来处理上述提到的缩写、数字、标点等各种复杂情况
    • 优点:实现相对简单,可控性强
    • 缺点:规则需要专家手工制定,难以覆盖所有语言现象,泛化能力有限
  • 基于学习的方法

    • 核心:将分词任务看作是一个序列标注 (Sequence Labeling) 任务
    • 过程:类似于中文分词,通过在大量已标注好的语料上训练模型,让模型自动学习切分的边界
    • 优点:不需要手工制定规则,模型可以从数据中自动学习

亚词级别的词元切分 (Subword-level Tokenization)

现代 NLP 模型,尤其是大型预训练语言模型,普遍采用亚词切分。

  • 亚词切分的必要性
    • 词汇表爆炸问题
      • 对于英语、德语等形态变化丰富的语言,一个词根(如 response)可以派生出多种形式(responsive, responsible, responsibility),如果都按词级别切分,词汇表会变得异常庞大(一个中大型语料库可达 30-60 万词)
      • 巨大的词汇表不仅消耗内存,更严重的是会导致未登录词 (Out-of-Vocabulary, OOV) 问题,即测试时遇到训练时没见过的词,模型将无法处理
    • 词与字母的权衡
      • 词级别 (Word-level):语义单元好,但词表太大,有 OOV 问题
      • 字母级别 (Char-level):词表非常小(约几十到一百个),无OOV问题,但破坏了单词的完整性,语义信息丢失严重,且序列会变得非常长
      • 亚词级别 (Subword-level):试图在两者之间找到一个最佳平衡点
    • 齐夫定律 (Zipf’s Law):语言中词频分布极不均匀,少数高频词占据了绝大部分文本,而大量词汇是低频的,这种长尾分布的特性使得按词切分效率很低

BPE:Byte-Pair Encoding (字节对编码)

BPE 是目前最主流的亚词切分算法之一,最早用于数据压缩领域,后被引入 NLP。

  • 核心思想:一个自底向上的、基于频率的数据驱动算法,它从最基本的单元(字母)开始,不断地将语料中最高频的相邻单元对合并成一个新的、更长的单元

  • 算法流程

    • 初始化:将语料库中的所有单词拆分成单个字母的序列,初始词汇表包含所有出现过的单个字母
    • 迭代合并:在设定的迭代次数内,重复以下步骤:
      • 扫描当前语料库(由现有词元组成的序列),找到出现频率最高的相邻词元对(bi-gram)
      • 将这个最高频的词元对合并成一个新的、更长的词元
      • 将这个新的词元添加到词汇表中
    • 终止:达到预设的迭代次数或词汇表大小后停止
  • 示例: 语料: {'low_': 5, 'lowest_': 2, 'newer_': 3, 'wider_': 6}

    • 初始:词汇表为 {l, o, w, e, s, t, n, r, i, d, _}
    • 迭代1:最高频的相邻对是 r_ (在 newer_wider_ 中共出现 3+6=9 次),合并 r_,词汇表新增 r_
    • 迭代2:最高频的相邻对是 er_,合并 er_,词汇表新增 er_
    • 迭代3:最高频的相邻对是 lo,合并 lo,词汇表新增 lo
    • 迭代4:最高频的相邻对是 low,合并 low,词汇表新增 low
    • 最终词汇表中的词元数目为:初始词汇数 + 迭代次数
  • 优点

    • 在不同切分粒度间取得平衡:有效解决了单纯按字母切分(破坏语义)和按单词切分(词表爆炸)各自的弊端,并且最终的词元数量可以精确控制
    • 解决未登录词(OOV)问题:任何未见过的词都可以被拆分成已知的亚词单元的组合,最坏情况下可以拆成单个字母,因此不存在严格意义上的 OOV
    • 高效实用:该算法的计算复杂度低,使其能够在大规模语料库上高效地进行训练和应用
    • 应用广泛:是目前绝大多数 NLP 大模型所采用的主流词元切分方法

常用工具

Lecture 6: 文本分类

文本分类概述

文本分类是自然语言处理中的基础任务。

任务定义、类型与应用

  • 任务定义
    • 输入:视具体应用而定的不同类型、不同长度的文本
    • 输出:预先定义的分类标签
  • 分类:可以依照输入的篇幅、分类标签体系、应用场景等分类,以输入文段的篇幅为例
    • 短文段:包含一句或几句话,如普通微博、推文等
    • 中等文段:如英文阅读理解的文章等
    • 长篇文段:如长篇的学术论文等

用卷积神经网络(CNN)做文本分类

我们可以利用 CNN(一维卷积)编码文本训练并做文本分类。

  • 卷积层:卷积核以小窗口的形式在输入序列上移动,每个位置产生一个输出
    • 计算方法:设窗口内的向量序列为 A=[a1,a2,,an]\boldsymbol{A}=\left[\boldsymbol{a}_1, \boldsymbol{a}_2, \ldots, \boldsymbol{a}_n\right],其中 ai=[a1i,a2i,,adi]Rd\boldsymbol{a}_i=\left[a_{1 i}, a_{2 i}, \ldots, a_{d i}\right]^{\top} \in \mathbb{R}^d,单输出通道的卷积核 B=(bij)d×n\boldsymbol{B}=\left(b_{i j}\right)_{d \times n},那么卷积层运算为 Conv1d(A,B)=i=1dj=1naijbij\operatorname{Conv1d}(\boldsymbol{A}, \boldsymbol{B})=\sum_{i=1}^d \sum_{j=1}^n a_{i j} b_{i j},即计算窗口内向量序列与卷积核的内积
    • 卷积核大小:通常为 1×Fc×D1 \times F_c \times D,其中 FcF_c​ 是卷积窗口大小,DD 是特征向量维度
    • 输出特征图大小NF stride +1\dfrac{N-F}{\text { stride }}+1
      • 其中 NN 为输入大小,FF 为卷积核大小
      • 步长 (Stride):控制卷积核滑动的距离
      • 补零 (Padding):在边界处补 0 可以使输入和输出大小保持相同
    • 理解
      • 在模长相同时,内积度量了向量方向的相关性,输出值越大,说明窗口内容与卷积核定义的模式越相似
      • 每个通道的卷积核可以看作特定的模式提取器,抽取特定的局部特征

  • 池化层:在每个激活图上单独运算,使特征表示(Representation)更小、更容易管理
    • 常见操作:最大池化、平均池化、最小池化等
    • 常规做法:通常设置池化大小与步长相同,使池化过程不重叠
    • 池化大小:通常为 1×Fp1 \times F_p,其中 FpF_p 是池化窗口大小

卷积、池化操作后,原输入文本被编码为长度缩减的特征序列,可用 LSTM 或者 DNN 继续处理。

用 FastText 模型做文本分类

FastText 是一种轻量的词向量模型和文本分类模型。

  • 特点:仅需要在 CPU 上训练,模型轻量且预测速度快

  • 核心问题:提取文本的 N-gram 特征,但当训练样本很多时,存储所有的 n-gram 的 embedding 不现实,内存消耗过大

  • 解决方案:哈希桶

    • 原理:将每个 n-gram 词通过一个哈希函数映射到一个哈希桶中,哈希值相同的 n-gram 词共享同一个 embedding
    • 本质:是一种简单粗暴的方法,强行减少了需要存储的 n-gram 数量,虽然会有冲突,但能有效控制模型大小
  • 工作流程:网络由输入层、隐藏层和输出层组成

    • Step 1: 获取文档特征
      • 输入是一个文档的 NN 个 n-gram 特征 {x1,,xN}\{\mathbf{x}_1​,\dots,\mathbf{x}_N​\}
      • 文档的整体特征向量 z\mathbf{z} 定义为所有 n-gram 特征的平均值

      z=1Nn=1Nxn\mathbf{z} = \frac{1}{N} \sum_{n=1}^N \mathbf{x}_n

    • Step 2: 将文档特征 z\mathbf{z} 输入线性层进行变换,获取隐层向量 h\mathbf{h}

      h=Ad×nz\mathbf{h}=\mathbf{A}_{d \times n} \mathbf{z}

      • 其中 nn 为 embedding 维度,dd 为隐藏层维度
    • Step 3: 从隐藏层获得输出概率分布 p\mathbf{p}

      p=softmax(Bk×dh)\mathbf{p}=\operatorname{softmax}\left(\mathbf{B}_{k \times d} \mathbf{h}\right)

      • 其中 kk 为类别数
    • Step 4: 计算预测结果与真实标签的 Loss

      L=yTlog(p)L=-\mathbf{y}^{\mathrm{T}} \log (\mathbf{p})

      • 其中 y\mathbf{y} 是待分类别的 ground truth 的 multi-hot 向量,同一段文本可以有多个类别

文本分类中的经典任务 - 情感分析 (Sentiment Analysis)

任务介绍

  • 定义:识别、提取并量化文本中表达的情感倾向,包括情绪、态度和主观性
  • 核心: 从细微的语言差异中捕捉人类情感
  • 分类体系
    • 按粒度:可分为文档级、句子级等
    • 按数量:二分类(如好/坏)或多分类(如积极、消极、中性)
    • 示例
      • 积极:“这部电影太棒了!”
      • 消极:“我对这次服务感到非常失望。”
      • 中性:“天气很好。”

典型模型介绍:词袋模型 (Bag-of-Words)

这是情感分类中最基础、简单高效的模型。它直接忽略单词出现的顺序,仅考虑文本中出现的单词。

  • 核心思想:将文本看作一个装满单词的“袋子”

    • 忽略:词出现的顺序、语法和上下文
    • 只关心:哪些词出现了,以及出现的频率
  • 关键特性

    • 忽略词序:“我喜欢你”和“你喜欢我”在词袋模型中的表示是完全相同的
    • 性价比高:算法简单、计算成本低,虽然效果有限,但容易理解和实现
  • 工作流程

    • 构建词汇表:统计所有训练文本中的不重复单词,构成一个巨大的词表
    • 文本预处理:转小写、去除标点符号、删除停用词(如 is, the 等)、词干提取/词形还原(如 felt -> feel)
    • 向量化:将文本表示为一个长向量,长度等于词汇表大小,向量中每个位置的数值表示对应单词在文本中出现的次数
    • 分类:使用分类器(可以是简单的统计模型,也可以是神经网络)根据向量判断情感标签
      • 分类器的训练:将文本的向量表示 x\mathbf{x} 和对应的情感标签 y\mathbf{y} 做成一对对数据 (x,y)(\mathbf{x}, \mathbf{y}) 训练分类模型

使用双向 LSTM 做情感分析

词袋模型丢失了语序信息,为了更精细地分类,我们需要能建模句式结构的深度模型。

  • LSTM 的优势

    • 捕捉结构特征:情感往往隐藏在否定词(not)、转折词(however)、反问、虚拟语气等结构中,词袋模型无法处理这些
    • 处理长距离依赖:例如 “The movie sounds great… However, it can’t hold up.”(开头看起来是夸奖,最后转折才是真实情感),LSTM 能记住前面的铺垫并在最后进行反转
  • 模型架构

    • Input:输入文本序列
    • Embedding:将单词映射为词向量序列 (x0,x1,)(x_0​,x_1​,\dots)
    • Bi-LSTM 层:使用双向 LSTM 处理序列
      • 分别从前向后和从后向前编码,捕捉上下文信息
    • Concat:拼接得到最后的输出序列
    • Output:通过全连接层输出分类结果

文本分类中的经典任务 - 文本蕴含 (Textual Entailment)

任务介绍

  • 定义:判断两个句子(一个前提 Premise 和一个假设 Hypothesis)之间是否存在推理关系
  • 核心:需要识别自然语言中的逻辑关系,这比简单的主题分类更复杂,需要对语义进行深度理解
  • 关系类型
    • 蕴含 (Entailment):前提为真时,结论必然为真
      • :前提“一个女人在弹吉他”,假设“一个女人在弹乐器”
    • 矛盾 (Contradiction):两个句子表述了互相冲突的事实
      • :前提“两只狗在追一只猫”,假设“没有动物在移动”
    • 中性 (Neutral):两个句子之间没有明确的蕴含或矛盾关系
      • :前提“男人在街上跑”,假设“他跑得很快”
  • 方法与模型演进
    • 规则/树编辑:早期的传统方法
    • 神经网络:引入 LSTM 和注意力机制
    • 交互式建模 (DIIN):专门设计交互层捕捉深层语义
    • 预训练 Transformer:BERT, RoBERTa 等
    • 大模型 Prompting:当前的最新范式

典型模型介绍:DIIN (Densely Interactive Inference Network)

DIIN 是一个专门针对文本蕴含任务设计的神经网络,其核心创新在于引入了 Interaction Space 来捕捉句子间更深层次的语义关系。

  • 模型架构(由下至上)
    • Embedding Layer (嵌入层)
      • 输入:Premise (P\boldsymbol{P}) 和 Hypothesis (H\boldsymbol{H})
      • 处理:将 premise 和 hypothesis 的单词 token 编码成向量,通常使用预训练的词向量(如 Skip-gram 或 GloVe)
    • Encoding Layer (编码层)
      • 目的:捕捉句子内部的上下文信息
      • 组件:由 Highway Network + Self-attention + Fuse Gate 组成
      • 输出:经过上下文编码和融合后的 Premise (P~\tilde{\boldsymbol{P}}) 和 Hypothesis (H~\tilde{\boldsymbol{H}})
    • Interaction Layer (交互层)
      • 核心操作:构造一个 Interaction Tensor,表示premise 和 hypothesis 中所有词的两两交互
      • 公式β(a,b)=ab\beta(a,b)=a\circ b,对特征向量进行逐对操作,提取词对之间的匹配特征
    • Feature Extraction Layer (特征提取层)
      • 架构:使用 DenseNet(一种带有残差连接的 CNN)
      • 特点:每一层卷积都与前面所有层相连,能够从 Interaction Tensor 中有效提取高维特征
    • Output Layer (输出层)
      • 分类:将提取到的特征展平,通过多层感知机 (MLP) 进行最终的三分类(Entailment / Contradiction / Neutral)

Lecture 7: 文本检索

概述

什么是文本检索

  • 定义:从大规模文档集合中,根据用户询问的内容 (Query),找到与用户查询最相关的文档(Document)
  • 典型应用场景
    • 搜索引擎:输入关键词,返回相关网页(如 Google, Baidu)
    • 问答系统:根据输入的问题,检索相关的段落进行回答
    • 推荐系统:根据用户的兴趣,检索相关的内容进行推荐

文本检索方法的分类

现代文本检索的核心逻辑是将 Query 和 Document 都表示为向量形式,并通过计算向量的点积来衡量相关性。根据向量表示形式的不同,文本检索方法可分为两大类:

  • 稀疏检索 (Sparse Retrieval)

    • 核心特点:基于词汇匹配,文档被表示为稀疏向量,向量中只有出现的词对应的位置有值,其余为 0
    • 文档向量维度:等于词表大小,通常非常大(几万到几十万维)
    • 代表方法:TF-IDF、BM25
  • 稠密检索 (Dense Retrieval)

    • 核心特点:基于语义理解,文档被表示为稠密向量,即用神经网络将文本编码为低维的实数向量
    • 文档向量维度:等于模型维度,通常较小(几百到几千维)
    • 代表方法:Contriever、DPR、BGE

稀疏检索器(Sparse Retriever)

大规模稀疏检索的技巧:倒排索引(Inverted Index)

倒排索引是解决大规模稀疏检索效率问题的核心技术,也是搜索引擎背后的基础架构。

  • 必要性:以搜索引擎为例,当我们在拥有海量文档的数据库中搜索关键词(如“姚明”)时,存在两种方案:

    • 方案 1:暴力遍历
      • 做法:根据关键词,遍历所有文档,逐个检查是否含有关键词
      • 缺点:在大规模数据下,时间与计算复杂性极大,效率不可接受
    • 方案 2:倒排索引 (Inverted Index)
      • 做法:提前遍历所有文档,建立“关键词 → 文档”的映射关系
      • 核心:将“文档包含哪些词”转化为“哪些文档包含这个词”
      • 优点:搜索时基本相当于一次查表,时间复杂度极低
  • 构建过程

    • 提前遍历所有文档,储存每个文档中出现的关键词
    • 对每一个关键词,记录它出现过的文档标号
  • 检索过程:只需要对用户 Query 中的关键词依次查询出现过的文档,并对结果取交集即可

TF-IDF

TF-IDF 是稀疏检索器的核心算法之一,主要解决了倒排索引中“如何选取关键词”的问题。

  • 核心思想:根据词汇的重要性进行加权匹配

  • 词频 (Term Frequency, TF):衡量某个词在当前文档中有多重要

    • 定义:表示某一个词在给定的文档中出现的频率
    • 逻辑:词语在文档中出现的频率越大,则其关键程度越高
    • 计算公式:词语 wiw_i 在文档 dj\boldsymbol{d}_j 中的词频为

      tf(i,dj)=ni,jdjt f\left(i, \boldsymbol{d}_j\right)=\frac{n_{i, j}}{\left|\boldsymbol{d}_j\right|}

      • 其中 ni,jn_{i, j} 是词语 wiw_i 在文件 dj\boldsymbol{d}_j 中出现的次数,dj\left|\boldsymbol{d}_j\right| 是文档 dj\boldsymbol{d}_j 的长度
    • 局限性:一些常用词(如“是”、“的”)会有极大的词频,但并没有包含有效信息,只看 TF 会导致这些无意义的词占据高分
  • 逆向文档频率 (Inverse Document Frequency, IDF):衡量某个词在整个语料中有多重要

    • 逻辑:越通用的词语(在很多文档里都出现),权重越低;越稀有的词语,权重越高
    • 计算公式

      idfi=logD{j:widj}i d f_i=\log \frac{|\boldsymbol{D}|}{\left|\left\{j: w_i \in \boldsymbol{d}_j\right\}\right|}

      • 其中 D|\boldsymbol{D}| 是语料库中的文档总数,{j:widj}\left|\left\{j: w_i \in \boldsymbol{d}_j\right\}\right| 是包含词语 wiw_i 的文档数目
    • 作用:通过 IDF 加权,像“的”、“是”这种在所有文档都出现的词,其 IDF 值会趋近于 0,从而被过滤掉
  • TF-IDF 综合评分

    tfidfi,j= def tf(i,dj)×idfi=ni,jdj×logD{j:widj}\begin{aligned} t f-i d f_{i, j} & \stackrel{\text { def }}{=} t f\left(i, \boldsymbol{d}_j\right) \times i d f_i \\ & =\frac{n_{i, j}}{\left|\boldsymbol{d}_j\right|} \times \log \frac{|\boldsymbol{D}|}{\left|\left\{j: w_i \in \boldsymbol{d}_j\right\}\right|} \end{aligned}

    • 一个词对一篇文档的重要性 = 它在这篇文档的频率 × 它在语料库的稀有度
  • 查询相关度评分:对于一个包含关键词 {q1,q2,,qn}\left\{q_1, q_2, \cdots, q_n\right\} 的查询 QQ,它与第 jj 个文档 dj\boldsymbol{d}_j​ 的相关度得分为所有关键词 TF-IDF 值的总和

    score(Q,dj)=i=1ntf(i,dj)×idfi\operatorname{score}\left(Q, \boldsymbol{d}_j\right)=\sum_{i=1}^n t f\left(i, \boldsymbol{d}_j\right) \times i d f_i

  • 局限性

    • 本质还是词袋模型:没有考虑词的位置信息与语义信息
    • 假设简单:IDF 简单地假设“文档频率越大,单词越无用”,这在某些特定情况下可能不准确

BM25

BM25 (Best Matching 25) 是工业界最常用的稀疏检索算法之一,它是对 TF-IDF 的改进,重点解决了词频无限增长和文档长度偏差的问题。

  • 核心公式:在 TF-IDF 的基础上,对 TF 项进行了一系列改进

    BM25(i,dj)=tf(i,dj)(k1+1)tf(i,dj)+k1[1+b(djdavg 1)]idfi\operatorname{BM25}\left(i, \boldsymbol{d}_j\right)=\frac{t f\left(i, \boldsymbol{d}_j\right) \cdot\left(k_1+1\right)}{t f\left(i, \boldsymbol{d}_j\right)+k_1 \cdot\left[1+b\left(\frac{\left|\boldsymbol{d}_j\right|}{|\boldsymbol{d}|_{\text {avg }}}-1\right)\right]} \cdot i d f_i

    • 其中 dj\left|\boldsymbol{d}_j\right| 是当前文档长度,davg |\boldsymbol{d}|_{\text {avg }} 是所有文档的平均长度,k1k_1bb 是人为指定的超参数
  • 改进点一:词频饱和 (TF Saturation)

    • TF-IDF 的问题:在 TF-IDF 中,词频越高,得分线性越高,但实际上,一个词在文档中出现 200 次的重要性并不是出现 100 次的两倍
    • BM25 的改进
      • 削权:对特别高频的词进行“饱和”处理,使其得分增长逐渐趋缓,不再无限增加
      • k1k_1​ 的作用:限制 TF 项的最大值,分子中的 (k1+1)(k_1​+1) 就是 TF 项能逼近的极限值;k1k_1​ 越大,增长曲线越平滑,词频对单词权重的影响越小

  • 改进点二:文档长度归一化 (Document Length Normalization)
    • TF-IDF 的问题:长文档天然容易包含更多词,导致词频更高,但一个词在短文档中出现 1 次,通常比在长文档中出现 1 次包含的信息量更大
    • BM25 的改进
      • 长度加权:引入文档长度 dj\left|\boldsymbol{d}_j\right| 与平均长度 davg |\boldsymbol{d}|_{\text {avg }}​ 的比值,对 TF 进行惩罚
      • bb 的作用:调整文档长度对得分的影响,bb 越大,文章长度对单词权重的影响越大,即对长文档的惩罚越重

  • BM25 总结

稠密检索器(Dense Retriever)

  • 定义:使用神经网络将文本编码为低维稠密向量,通过向量点积来衡量语义相似度
  • 优势:相比高维稀疏向量,大大提升了检索效率,并且能够捕捉词汇之外的语义信息
  • 模型架构:以 BERT 的架构为例
    • 输入:自然语言文本(Query 或 Document)
    • 分词 (Tokenizer):处理成 Token 序列,加上 [CLS][SEP] 标记
    • 编码 (Encoder):通过 BERT 等预训练 Transformer 模型进行编码
    • 输出表示:通常取 [CLS] 标记对应的输出向量,作为整个文档或查询的语义表示

Contriever

Contriever 是一个无监督的稠密检索模型,它的核心贡献在于通过巧妙的对比学习 (Contrastive Learning) 策略,在没有标注数据的情况下训练出了强大的检索器。

  • 训练框架:对比学习
    • 目标:拉近相关文本(正样本)的向量距离,推远不相关文本(负样本)的向量距离
    • 样本构成:每个训练样本是一个三元组
      • qq:查询 (Query)
      • k+k_+​:正样本文档 (Positive Document),与 qq 相关
      • kik_i​:负样本文档 (Negative Document),与 qq 不相关

  • 损失函数:使用 InfoNCE Loss 形式的损失函数

    L(q,k+)=logexp(s(q,k+)τ)exp(s(q,k+)τ)+Σexp(s(q,ki)τ)L\left(q, k_{+}\right)=-\log \frac{\exp \left(\frac{s\left(q, k_{+}\right)}{\tau}\right)}{\exp \left(\frac{s\left(q, k_{+}\right)}{\tau}\right)+\Sigma \exp \left(\frac{s\left(q, k_i\right)}{\tau}\right)}

    • 分子部分:查询与正样本的相似度(越大越好)
    • 分母部分:查询与所有样本(正样本+负样本)的相似度总和
    • 温度参数 τ\tau:控制分布的尖锐程度,τ\tau 越小,分布越尖锐,模型越确定;τ\tau 越大,分布越平滑,模型越不确定
  • 正样本的构建策略:对于一个给定的文档库,如何无监督地构建查询和正样本

    • 逆向填空任务 (Inverse Cloze Task, ICT)
      • 做法:从一篇文档中,随机抽取其中一段话作为 Query,把文档剩下的部分作为 Positive Document
      • 逻辑:一段话通常能概括或推导出上下文的内容

    • 独立裁剪 (Independent Cropping)
      • 做法:从同一篇文档中,独立随机地采样两个片段,分别作为查询和文档
      • 特点:两个片段可以有重叠,也可以不重叠;通常片段长度为原文档的 20%-80%
      • 逻辑:同一篇文档里的不同部分,语义是相关的

  • 负样本的构建策略:对于一个给定的文档库,如何无监督地构建查询和负样本

    • 同批内采样 (In-batch Sampling)
      • 做法:在一个 Batch 内,假设不同文档之间是不相关的,对于 Query qiq_i​,除了它对应的 did_i​ 是正样本,Batch 内所有其他的 dj(ji)d_j​(j \neq i) 全部当作负样本
      • 优点:计算高效,充分利用显存。

    • 跨批次采样 (Across-batch Sampling)
      • 动机:Batch size 有限,负样本数量不够多
      • 做法:维护一个内存队列存储历史批次的文档向量
        • 训练时,从内存库中随机抽取 KK 个向量作为负样本
        • 当前 Batch 计算完后,把文档向量放入内存库
      • 优点:可以获得极大量的负样本,提升对比学习效果

BGE

相比于 Contriever,BGE 引入了更加丰富的训练任务,旨在通过更多样化的数据和策略来增强模型能力。

  • 训练流程

    • 阶段 1:预训练 (Pre-training)
      • 数据:使用了 1.2B 的大规模预训练语料
      • 损失函数:使用和 Contriever 完全相同的对比损失函数
    • 阶段 2:微调 (Fine-tuning)
      • 数据:使用了有监督数据和合成的长文数据
      • 核心策略:使用三种向量表示得到一个混合分数,然后再使用和之前完全相同的损失函数
  • 混合分数计算:在微调阶段,模型计算 Query 和 Document 的相似度时,融合了三种不同维度的分数

    s(q,k)=λ1sdense (q,k)+λ2slex (q,k)+λ3smulti (q,k)s(q, k)=\lambda_1 \cdot s_{\text {dense }}(q, k)+\lambda_2 \cdot s_{\text {lex }}(q, k)+\lambda_3 \cdot s_{\text {multi }}(q, k)

这三种分数分别对应三种不同的向量表示形式。设查询和文档经过 BGE 模型最后一层输出的向量分别为 HqH_qHpH_p

  • 密集检索 (Dense Retrieval)

    • 原理:经典的稠密向量匹配
    • 计算:直接取 [CLS] 标记对应的向量 (Hq[0]H_q​[0]Hp[0]H_p​[0]) 进行点积
    • 公式

      sdense =Hq[0]Hp[0]s_{\text {dense }}=H_q[0] \cdot H_p[0]

    • 特点:捕捉全局语义信息
  • 词汇检索 (Lexical Retrieval)

    • 原理:一种可学习的稀疏检索。
    • 计算
      • 通过一个可学习的映射将每个 Token 的向量映射为一个标量权重 ww
      • 计算查询和文档中重合词元的权重乘积之和
    • 公式

      wqt=RELU(Wlex THq[i])w_{q_t}=\operatorname{RELU}\left(W_{\text {lex }}^T H_q[i]\right)

      slex =tqpwqtwpts_\text{lex }=\sum_{t \in q \cap p} w_{q_t} * w_{p_t}

    • 特点:模拟了传统稀疏检索(如 BM25)的精确匹配能力,但权重是学出来的
  • 多向量检索 (Multi-Vector Retrieval)

    • 原理:细粒度的交互匹配(类似 ColBERT)
    • 计算
      • 将查询和文档的所有 Token 向量经过投影矩阵 WmulW_\text{mul} 映射
      • 对于查询中的每一个 Token,在文档中找到与其相似度最高的词元,然后将这些最大相似度求和
    • 公式

      smulti =1Ni=1Nmaxj=1MEq[i]Ep[j]s_\text{multi }=\frac{1}{N} \sum_{i=1}^N \max _{j=1}^M E_q[i] \cdot E_p[j]

      • 其中 NNMM 分别表示查询和文档的长度,EqE_qEpE_p 分别表示查询和文档经过 BGE 模型和投影矩阵的多向量表示
    • 特点:捕捉更加精细的局部交互信息,解决单向量无法承载复杂语义的问题

最终将三种分数加权即可得到最终的混合分数。

Lecture 8: 序列标注

Lecture 9: 机器翻译与 Transformer 模型

Lecture 10: 文本生成

Lecture 11: 预训练语言模型

Lecture 12: 大语言模型的分布式训练

Lecture 13: 预训练语言模型的有监督微调

Lecture 14: RLHF: 从人类反馈中学习

  • 大模型的经典训练流程
    • 预训练 (Pretraining)
      • 数据:海量无标注数据
      • 算力:消耗巨大的算力
      • 目标:让模型获得基础的语言能力和知识
    • 有监督微调 (Supervised Fine-Tuning, SFT)
      • 数据:少量的、高质量的人工标注数据(Prompt-Answer 对)
      • 算力:相对较少的算力
      • 目标:教会模型如何遵循指令对话
    • 基于人类反馈的强化学习 (RLHF)
      • 数据:没有标注答案
      • 算力:相对较少的算力
      • 目标:让模型更符合人类的偏好和价值观

RLHF (Reinforcement Learning from Human Feedback)

  • RLHF:使用强化学习的框架,从人类的反馈中进一步提升模型表现
    • 目标:优化 LM 使其更符合人类的偏好
    • 思路:使用强化学习 (RL) 最大化 LM 生成结果的奖励期望 Es^pθ(s)[R(s^)]\mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]

为什么需要 RLHF

  • 鲁棒性问题:SFT 后的模型面对多样提示词 (Prompt) 的鲁棒性不足
    • 具体表现:Prompt 的微小变化可能导致模型的回答完全不同
    • RLHF 的效果:RLHF 后,模型的回复对问题的细微变化更鲁棒,交互更丝滑

强化学习(Reinforcement Learning)概念简述

  • 强化学习(RL):从交互中学习
    • 目标:得到使奖励 R(s)R(s) 较高的策略

RL 直到近期才被应用于 NLP 领域。这是由于 NLP 任务的复杂性高,传统 RL 算法难以处理庞大、高维的状态空间,并且 RL 在复杂问题上很难收敛到较好的策略。然而,RL 领域的新算法(如 PPO 算法)能适用于大型神经网络,因此被运用到了 NLP 领域。

从人类反馈中学习:策略梯度算法(Policy Gradients)

  • 问题:如何更新 LM 的参数 θ\theta,才能最大化模型生成的回复 ss 所获得人类奖励的期望 Es^pθ(s)[R(s^)]\mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]

  • 梯度下降的困境:梯度下降法要求 Es^pθ(s)[R(s^)]\mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]θ\theta 的导数,然而 R(s^)R(\hat{s}) 是一个人类给出的奖励反馈,无法求导

    • 解决方法:策略梯度算法,提供了根据 R(s)R(s) 进行梯度更新的方法
  • 策略梯度算法(Policy Gradients)

    • 种类:包含了 REINFORCE、Actor-Critic、A3C、A2C、TRPO、PPO 等一系列算法
    • 核心问题:为了使用梯度下降 θt+1θt+αθtEs^pθ(s)[R(s^)]\theta_{\mathrm{t}+1} \gets \theta_{\mathrm{t}}+\alpha \nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})],我们需要求解 θtEs^pθ(s)[R(s^)]\nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]

接下来,我们重点推导 θtEs^pθ(s)[R(s^)]\nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})] 的近似求解。我们希望最终的形式是:期望在导数外,这样可以通过采样来估计;求导项方便对 θt\theta_t 求导,而不是现在的 R(s)R(s)

首先,将期望的定义展开。

θtEs^pθ(s)[R(s^)]=θsR(s)pθ(s)=sR(s)θpθ(s)\nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]=\nabla_\theta \sum_s R(s) p_\theta(s)=\sum_s R(s) \nabla_\theta p_\theta(s)

接着,利用如下的对数梯度公式,进行变换。

θlogpθ(s)=θpθ(s)pθ(s)θpθ(s)=pθ(s)θlogpθ(s)\nabla_\theta \log p_\theta(s)=\frac{\nabla_\theta p_\theta(s)}{p_\theta(s)} \quad \Rightarrow \quad \nabla_\theta p_\theta(s)=p_\theta(s) \nabla_\theta \log p_\theta(s)

代入,得:

θtES^pθ(s)[R(S^)]=θsR(s)pθ(s)=sR(s)θpθ(s)=spθ(s)R(s)θlogpθ(s)=Es^pθ(s)[R(s^)θlogpθ(s^)]\begin{aligned} \nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{S} \sim p_\theta(s)}[R(\hat{S})] & =\nabla_\theta \sum_s R(s) p_\theta(s)=\sum_s R(s) \nabla_\theta p_\theta(s) \\ & =\sum_s p_\theta(s) R(s) \nabla_\theta \log p_\theta(s) \\ & =\mathbb{E}_{\hat{s} \sim p_\theta(s)}\left[R(\hat{s}) \nabla_\theta \log p_\theta(\hat{s})\right] \end{aligned}

这样,求导就变成了对模型回复 s^\hat{s} 所对应的生成概率的求导。而且由于期望在梯度外面,我们可以通过采样估计,来近似这个梯度。

θtES^pθ(s)[R(s^)]=ES^pθ(s)[R(s^)θlogpθ(s^)]1mi=1mR( si)θlogpθ(si)\begin{aligned} \nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{S} \sim p_\theta(s)}[R(\hat{s})] & =\mathbb{E}_{\hat{S} \sim p_\theta(s)}\left[R(\hat{s}) \nabla_\theta \log p_\theta(\hat{s})\right] \\ & \approx \frac{1}{m} \sum_{i=1}^m R\left(\mathrm{~s}_i\right) \nabla_\theta \log p_\theta\left(\mathrm{s}_i\right) \end{aligned}

这样,梯度下降的公式就可以写作:

θt+1:=θt+αθtEs^pθ(s)[R(s^)]=θt+α1mi=1mR( si)θtlogpθ(si)\begin{aligned} \theta_{\mathrm{t}+1} & :=\theta_{\mathrm{t}}+\alpha \nabla_{\theta_{\mathrm{t}}} \mathbb{E}_{\hat{s} \sim p_\theta(s)}[R(\hat{s})]\\ & =\theta_{\mathrm{t}}+\alpha \frac{1}{m} \sum_{i=1}^m R\left(\mathrm{~s}_i\right) \nabla_{\theta_{\mathrm{t}}} \log p_\theta\left(\mathrm{s}_i\right) \end{aligned}

可以观察到,奖励 R(si)R(s_i) 越大,pθ(si)p_\theta(s_i) 在梯度更新中的权重越大,从而使文本 sis_i 更可能被生成。

奖励模型:用奖励模型替代人类反馈

有了策略梯度算法,我们已经可以:给出奖励函数 R(s)R(s),训练 LLM 来最大化期望奖励。那么接下来要讨论的是,奖励函数 R(s)R(s) 应该如何设置。

  • 人工标注的问题:是否可以直接用人工标注的 R(s)R(s) 训练 LLM
    • 问题 1:手工标注数据比较稀缺,耗时耗力
      • 解决方法:把摘要打分视作一个独立任务,构建一个奖励模型 (Reward Model, RM) 用于预测人类的打分,手工标注数据仅用于训练奖励模型,再用奖励模型去训练真模型
    • 问题 2:手工标注的打分数据主观性强,标注人员难以给出准确分数
      • 解决方法:将打分题变为比较题,让标注人员比较多个回复的好坏

Instruct-GPT: 奖励模型训练

Instruct-GPT 的 RL 训练分为两大步:训练奖励模型、用奖励模型训练主模型。

  • 奖励模型训练流程

    • 将 prompt 输入语言模型(记作 πSFT(yx)\pi^{S F T}(y \mid x)),生成多个输出
    • 人类对多个输出进行排序
    • 利用排序数据,训练奖励模型(6B 参数)
  • 奖励模型大小的选择:不需要选择太大的模型(175B)

    • 更大的模型训练不稳定,且计算资源需求高
    • 训练一个 6B 的 RM 后,可用于不同大小 RL 模型训练
  • 奖励模型的初始化:使用改造的语言模型

    • πSFT(yx)\pi^{S F T}(y \mid x) 模型初始化
    • 将 final unembedding layer 移除,输出映射到标量
  • 训练方法:采用对比学习的损失函数

    • 对比式
      • 公式:假设 sws^w 是 比较题中较好的样例 (winning sample),sls^l 是比较题中较差的样例 (losing sample),则损失函数表示为:

        JRM(ϕ)=E(sw,sl)D[logσ(RMϕ(sw)RMϕ(sl))]J_{R M}(\phi)=-E_{\left(s^w, s^l\right) \sim D}\left[\log \sigma\left(R M_\phi\left(s^w\right)-R M_\phi\left(s^l\right)\right)\right]

      • 目标:使 sws^w 的奖励尽量大、sls^l 的奖励尽量小
      • 问题:只适合两者比对场景
    • 排序式
      • 公式:设 KK 是模型输出 response 的个数,则损失函数表示为:

        JRM(ϕ)=1(K2)E(sw,sl)D[logσ(RMϕ(sw)RMϕ(sl))]J_{R M}(\phi)=-\frac{1}{\binom{K}{2}} E_{\left(s^w, s^l\right) \sim D}\left[\log \sigma\left(R M_\phi\left(s^w\right)-R M_\phi\left(s^l\right)\right)\right]

      • 优势:考虑了所有 KK 个输出之间的两两偏序关系,可以学习到更为全局的排序关系,更好地拟合人类的价值观和偏好

Instruct-GPT: RLHF

  • 强化学习微调训练流程
    • 将 prompt 输入给 πθRL\pi_\theta^{R L},得到模型输出

    • 奖励模型 RMϕR M_\phiπθRL\pi_\theta^{R L} 模型生成的输出进行打分

    • 利用得到的打分结果作为 reward,使用 PPO 算法对 πθRL\pi_\theta^{R L} 进行优化

    • 注意

      • 强化学习模型 πθRL\pi_\theta^{R L} 是用 πSFT\pi^{S F T} 模型初始化的
      • 由于 RL 过程比较难训练成功,因此训练多个 RL 模型,取其中最好的一个

PPO (Proximal Policy Optimization)

为了推导 PPO 算法公式,我们先回顾最开始强化学习的目标。

ObjectiveRL(θ)=E(x,y)DπθRL[RMϕ(x,y)]\operatorname{Objective}_{R L}(\theta)=E_{(x, y) \sim D_{\pi_\theta^{R L}}}\left[R M_\phi(x, y)\right]

其中 xx 是输入提示词,yy 是模型 πθRL\pi_\theta^{R L} 生成的回答,RMϕ(x,y)R M_\phi(x, y) 是奖励模型给出的分数。

这会导致一个问题:直接这样优化会导致模型过度拟合人类偏好的奖励信号。因此,PPO 在目标函数中增加了 KL 惩罚项,目的是防止模型的生成结果偏离原模型 πSFT\pi^{S F T} 太多。

ObjectiveRL(θ)=E(x,y)DπθRL[RMϕ(x,y)βlog(πθRL(yx)πSFT(yx))]\operatorname{Objective}_{R L}(\theta)=E_{(x, y) \sim D_{\pi_\theta^{R L}}}\left[R M_\phi(x, y)-\beta \log \left(\frac{\pi_\theta^{R L}(y \mid x)}{\pi^{S F T}(y \mid x)}\right)\right]

总结一下,目前我们已经有:一个 SFT 后的预训练语言模型 πSFT(yx)\pi^{S F T}(y \mid x)、一个奖励模型 RMϕ(x,y)R M_\phi(x, y)、正在 RL 训练的语言模型 πθRL(yx)\pi_\theta^{R L}(y \mid x)

然而,这样训练出来的模型仍然存在问题:PPO 优化会损害 RL 模型在 NLP 数据集上的原始性能,并且调整 β\beta 超参数并不能解决这个问题。

因此,Instruct-GPT 在 RL 过程中混入预训练优化,得到了 PPO-ptx 算法。具体来说,在 RL 训练中混入 10% 的预训练任务,将预测下一个词的损失也加入 RL 损失中。

ObjectiveRL(θ)=E(x,y)DπθRL[RMϕ(x,y)βlog(πθRL(yx)πSFT(yx))]+γExDpretrain [log(πθRL(x))]\begin{aligned} & \operatorname{Objective}_{R L}(\theta) \\ & =E_{(x, y) \sim D_{\pi_\theta^{R L}}}\left[R M_\phi(x, y)-\beta \log \left(\frac{\pi_\theta^{R L}(y \mid x)}{\pi^{S F T}(y \mid x)}\right)\right] \\ & +\gamma E_{x \sim D_{\text {pretrain }}}\left[\log \left(\pi_\theta^{R L}(x)\right)\right] \end{aligned}

最终各种模型的效果对比如下图所示。

总结

  • RLHF 的优点

    • 更符合人类价值观:通过人类反馈引导模型,使其回答更加符合社会道德、伦理和
      人类价值观,减少产生有害或不恰当的内容
    • 提升模型的实用性:在处理模糊或开放性问题时,RLHF 可以使模型生成更加真实、
      有帮助的回答,提高用户体验和应用效果
    • 减少偏见和错误:通过人类反馈校正模型的误导性回答或潜在偏见,提升模型生
      成内容的可靠性和公平性
  • RLHF 的缺点

    • RL 在复杂问题上很难收敛到较好的策略,需要进行精细的调参工作
    • 人类反馈并不可靠,根据人类反馈构建的奖励模型(RM)更加不可靠
      • 后果:RLHF 倾向于生成“看似权威可靠但不一定正确”的内容

Lecture 15: 大语言模型的高效化策略

Lecture 16: 新的发展方向

16-1: 混合专家模型 (MoE)

16-2: 检索增强生成 (RAG)

16-3: 多模态大模型 (MLLM)

参考资料

本文参考上海交通大学《自然语言处理》课程 CS3602 林洲汉老师的 PPT 课件整理。


自然语言处理:笔记整理
https://cny123222.github.io/2026/01/14/自然语言处理:笔记整理/
Author
Nuoyan Chen
Posted on
January 14, 2026
Licensed under