什么是语言模型?语言模型的主要功能包括:

  • 在给定语言序列,计算该序列出现的概率
  • 判断一个语言序列是否是正常语句,即是否是人类的语言
  • 已知若干个词,预测和生成下一个词

语言模型广泛应用于语音识别、拼写检查、手写体识别、OCR等任务中,也是现今LLM背后的底层原理之一。

本讲我们将介绍最简单,也是最基本的语言模型——n-gram语言模型。其表示一类概率模型,核心是由n个词(实际上可理解为词元)组成的序列,包括双词模型(bigram)、三词模型(trigram)等。

以n-gram语言模型为代表的概率统计语言模型在上世纪80年代到2000年左右是自然语言处理的主流方法,直到2000年后逐渐被神经网络语言模型取代。

n-gram语言模型

  • 首先我们需要确定一个语言序列发生的概率。设w={w1,,wn}\mathbf{w}=\{w_1,\cdots,w_n\}为一个由nn个词组成的序列(一句话),那么这个序列出现的概率P(w)=P(w1,,wn)P(\mathbf{w})=P(w_1,\cdots,w_n)(即联合概率)

    • 序列概率满足 wV+P(w)=1,0P(w)1\sum_{\mathbf{w}\in V^+}P(\mathbf{w})=1,\quad 0\leq P(\mathbf{w})\leq 1 其中V+V^+表示由词表VV得到的无限序列集合。
    • 对于序列(联合)概率的计算,使用概率链式规则将其转化为条件概率分布的乘积: p(w)=p(w1w2wk)=p(w1w0)×p(w2w0w1)××p(wk+1w0w1w2wk)=t=1k+1p(wtw0w1wt1)\begin{aligned} p(\mathbf{w}) &= p(w_1 w_2 \ldots w_k) \\ &= p(w_1|w_0) \times p(w_2|w_0 w_1) \times \ldots \times p(w_{k+1}|w_0 w_1 w_2 \ldots w_k) \\ &= \prod_{t=1}^{k+1} p(w_t|w_0 w_1 \ldots w_{t-1}) \end{aligned}
  • 于是我们将序列概率计算问题转化为了条件概率计算问题。然而,在有限语料库的情况下,绝大多数条件概率在语料库中从未出现过,就会被估计成00。另一方面,序列越长,条件概率的计算复杂度就越高。

    • 对此,n-gram语言模型提出马尔可夫假设:条件概率只依赖前n1n-1个词,不考虑所有历史序列。
  • 下面给出n=1,2,3n=1,2,3的情形:

    1. 一元语言模型(Unigram)使用的是条件独立假设: P(w1,,wn)=i=1nP(wi)P(w_1,\cdots,w_n)=\prod_{i=1}^nP(w_i)
      • 其生成的句子基本没有逻辑,故一般不会使用。
    2. 二元语言模型(Bigram)使用一阶马尔可夫假设: P(w1,,wn)=P(w1BOS)×i=2nP(wiwi1)×P(STOPwn)P(w_1,\cdots,w_n)=P(w_1|\texttt{BOS})\times\prod_{i=2}^nP(w_i|w_{i-1})\times P(\texttt{STOP}|w_n)
    3. 三元语言模型(Trigram)使用二阶马尔可夫假设: P(w1,,wn)=P(w1BOS,BOS)P(w2BOS,w1)×i=2nP(wiwi2,wi1)×P(STOPwn,wn1)P(w_1,\cdots,w_n)=P(w_1|\texttt{BOS},\texttt{BOS})P(w_2|\texttt{BOS},w_1)\times \prod_{i=2}^nP(w_i|w_{i-2},w_{i-1})\times P(\texttt{STOP}|w_n,w_{n-1})

    对应的参数估计如下:

    模型条件概率 参数估计
    P(wi)P(w_i) c(wi)N\dfrac{c(w_i)}{N}c(wi)c(w_i)为语料库中wiw_i出现的次数,NN为语料库词总数,下面类似)
    P(wiwi1)P(w_i\vert w_{i-1}) c(wi1,wi)c(wi1)\dfrac{c(w_{i-1},w_i)}{c(w_{i-1})}
    P(wiwi2,wi1)P(w_i\vert w_{i-2},w_{i-1}) c(wi2,wi1,wi)c(wi2,wi1)\dfrac{c(w_{i-2},w_{i-1},w_i)}{c(w_{i-2},w_{i-1})}
  • 由此可以推广到一般的n-gram语言模型:

    P(w)=i=1m+1P(wiwin+1:i1)P(wiwin+1:i1)=C(win+1:i)C(win+1:i1)\begin{aligned} P(\mathbf{w}) &= \prod_{i=1}^{m+1} P(w_i|w_{i-n+1:i-1})\\ P(w_i|w_{i-n+1:i-1}) &= \frac{C(w_{i-n+1:i})}{C(w_{i-n+1:i-1})} \end{aligned}

    其中wa:bw_{a:b}表示wa,wa+1,,wbw_a,w_{a+1},\cdots,w_b序列,wm+1=STOPw_{m+1} = \texttt{STOP},索引in+10i - n + 1 \le 0时填充BOS\texttt{BOS}

    在实际的序列概率计算中,常常使用对数概率相加,最后再通过指数转换回概率值(因为概率乘积本身可能很小,容易导致数值下溢)

    • nn更大时,对下一个词出现的约束信息更多,具有更大的辨别力;当nn更小时,在训练语料库中出现的次数更多,具有更可靠的统计信息。
    • n4n\geq 4时数据稀疏和计算代价又变得显著起来,实际工程中几乎不使用。
    • 深度学习中的循环神经网络RNN可以看作具有无穷元的语言模型(\infty-gram)。
  • 一些n-gram语料库:Google Books Ngrams当代美国英语语料库(COCA)

  • 由于语言具有远距离依赖性,所以n-gram语言模型仍然是一个不充分的语言模型。

模型评估

  • 语言模型的评估主要分为外部评估内部评估
    • 外部评估方法是指训练语言模型后,将其直接应用于下游任务,观察其准确性,然后直接针对下游应用进行优化。【另一方面是对完成下游任务的时间和空间效率进行评估】
    • 虽然外部评估是评价一个语言模型性能的最佳方法,但是端到端地运行大型NLP系统往往成本高昂。于是,我们会倾向于使用内部评估方法快速评估语言模型潜在改进的指标。
  • 在内部评估过程中,与机器学习方法类似,需要将数据划分为训练集、验证集与测试集。
    • 训练集用于学习模型的参数。对于n-gram语言模型,其参数由语料库统计得到的n元组频率,再归一化得到的概率值。【一般假设训练集中的语料质量较高】
    • 测试集则是一组与训练集完全不重叠的独立数据,用于评估模型的表现。如果模型对语言逻辑通顺的句子计算出的概率越高,说明模型的性能越好。
    • 而测试集也不能被多次使用(否则会导致对测试集的针对性调优,削弱泛化能力),因此在训练过程中会使用验证集进行调优。
  • 一个常用的内部评估指标是困惑度(perplexity)。它是一种基于概率的函数,通过对测试集概率取倒数,再按词数进行归一化得到(因此也被称为每词困惑度)。具体而言:
    • 设测试语料由ll个句子S=(s1,,sl)S=(s_1,\cdots,s_l)组成,那么整个测试集出现的概率为 P(S)=i=1lP(si)=i=1lj=1mP(wj(i)wjk+1:j1(i))i=1nP(wiwik+1:i1)P(S) = \prod_{i=1}^{l} P(s_i)=\prod_{i=1}^{l}\prod_{j=1}^{m} P(w_j^{(i)}|w_{j-k+1:j-1}^{(i)})\triangleq \prod_{i=1}^{n} P(w_i|w_{i-k+1:i-1}) 其中n=m×ln=m\times l(即整个测试集的总词数)。对这一概率计算交叉熵: Hp(S)=1nlog2P(S)H_p(S)=-\frac{1}{n} \log_2 P(S) 而困惑都就是对交叉熵取指数: PPLp(S)=2Hp(S)=P(S)1n=1i=1nP(wiwik+1:i1)n\text{PPL}_p(S) = 2^{H_p(S)} = P(S)^{-\frac{1}{n}} = \sqrt[n]{\frac{1}{\displaystyle\prod_{i=1}^{n} P(w_i|w_{i-k+1:i-1})}} 由此可见,困惑度的量纲是词数,其可以理解为平均每次预测有多少个等效等概率候选词。

      特别地,当测试集概率为11时,交叉熵为00,此时困惑都即为11,模型能够完美预测(理论下界)。

    • 语言模型在数据上的困惑度越低,模型表现越好。n-gram对于英语文本的困惑度范围一般为50~1000,而如今大模型的困惑度可以稳定在20~30左右。

数据平滑

  • 对于n-gram语言模型困惑度的计算,由于语言的稀疏性与语料库大小有限,测试集上的n-gram可能在训练集上从未出现过,从而导致条件概率为00,此时困惑度就无法计算。
  • 为解决这一问题,我们考虑使用数据平滑,使零概率增值,非零概率下调,消除零概率,改进模型的整体正确率。
  • 数据平滑的方法主要包括:
    1. 拉普拉斯平滑(最简单)
      每一种情况出现的次数加11或者一个极小值α\alpha并重新规范化。

      • 以Bigram为例: P(wiwi1)=C(wi1,wi)C(wi1)P(wiwi1)=C(wi1,wi)+αC(wi1)+αVP(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i)}{C(w_{i-1})}\Longrightarrow P(w_i|w_{i-1}) = \frac{C(w_{i-1}, w_i) + \alpha}{C(w_{i-1}) + \alpha |V|}
    2. 减值法/折扣法
      修改训练样本中事件的实际计数,使样本中(实际出现的)不同事件的概率之和小于1,剩余的概率量分配给未登录词。具体包括以下方法:

      • Good-Turing 估计
        NN为训练样本中所有n-gram的总数,rr为样本中n-gram出现的频数,nrn_r为出现次数恰好为rr的n-gram数量,则有

        N=r=1nrr=r=0nr+1(r+1)N=\sum_{r=1}^\infty n_rr=\sum_{r=0}^\infty n_{r+1}(r+1)

        于是考虑调整频数rrr=(r+1)×nr+1nrr^*=(r+1)\times\dfrac{n_{r+1}}{n_r}(如果nr+1=0n_{r+1}=0则频数保持不变),那么出现rr次的每个n-gram出现的概率变为

        pr=rNp_r=\frac{r^*}{N}

        此时r=1pr<1\displaystyle\sum_{r=1}^\infty p_r< 1,多余的概率就分给未登录词(每个未登录词出现概率为n1Nn0\dfrac{n_1}{Nn_0})。

      • Back-off方法(也称为katz回退)

        • 当某个n-gram在样本中出现的频率大于阈值KK(通常取0011)时,在原有概率估计值上乘以折扣系数(类似Good-Turing);
        • 否则使用低阶(即(n-1)-gram概率)替代n-gram概率(需要乘上归一化因子)。

        以Bigram为例:

        pkatz(wiwi1)={drC(wi1,wi)C(wi1)if C(wi1,wi)=r>0α(wi1)C(wi)Nif C(wi1,wi)=0p_{\text{katz}}(w_i \mid w_{i-1}) = \begin{cases} d_r \dfrac{C(w_{i-1},w_i)}{C(w_{i-1})} & \quad \text{if } C(w_{i-1},w_i) = r > 0 \\[10pt] \alpha(w_{i-1}) \dfrac{C(w_i)}{N} & \quad \text{if } C(w_{i-1},w_i) = 0 \end{cases}
      • 绝对减值法

        • 对于所有出现频次大于00的n-gram,将词频统一减去一个常量bb0<b10< b\leq 1),最终得到的概率估计为: pr={rbN,r>0br=1nrNn0,r=0p_r=\begin{cases} \dfrac{r-b}{N},&r>0\\[10pt] \dfrac{b\sum_{r=1}^\infty n_r}{Nn_0},&r=0 \end{cases}
      • 线性减值法
        从每个频数rr中减去与该频数成正比的量(减值函数为线性的),剩余概率量α\alphan0n_0个未见事件均分,得到概率估计:

        pr={(1α)rNr>0αn0r=0p_r = \begin{cases} \dfrac{(1-\alpha)r}{N} & r > 0 \\[10pt] \dfrac{\alpha}{n_0} & r = 0 \end{cases}

        其中α\alpha的优化值为n1N\dfrac{n_1}{N}。【与Good-Turing对齐】

    3. 插值法
      用低阶语法估计高阶语法,即当(n+1)-gram的值不能从训练数据中准确估计时,用n-gram来替代。示例:

      P^(wiwi2,wi1)=λ1P(wiwi2,wi1)+λ2P(wiwi1)+λ3P(wi)\hat{P}(w_i|w_{i-2}, w_{i-1}) = \lambda_1 P(w_i|w_{i-2}, w_{i-1}) + \lambda_2 P(w_i|w_{i-1}) + \lambda_3 P(w_i)

      其中iλi=1\displaystyle\sum_{i}\lambda_i=1
      具体步骤:先将训练语料分为两部分,即从原始语料中删除一部分作为留存数据。第一部分用于训练条件概率,第二部分(留存数据)用于优化权重λi\lambda_i,使整体模型困惑度最低。