语言模型(Language Model, LM)是自然语言处理(NLP)领域的核心技术之一。简单来说,它是一个可以预测单词(或字符)序列概率的模型。
想象一个"文字接龙"游戏:
- 输入: "今天天气很____"
此时,语言模型就会分析整个输入序列"今天天气很____",然后计算在无数可能接在"很"后面的词语中(如:好、晴朗、热、冷、糟糕、蓝、不错、舒服...),哪个词出现的概率最高。这个概率是基于其在训练数据中学到的规律(比如,"今天天气很好"比"今天天气很苹果"出现的频率高得多,最后输出一个预测,比如最可能出现的下一个词是:好。
简单来说,语言模型的核心功能是:
- 评估一个序列(句子或短语)在给定语言中出现的可能性(概率)。
- 在给定前文的情况下,预测下一个最可能出现的词(或符号)。
一 经典语言建模
先来了解一个最经典也最简单的语言建模。
比如我现在有如下一段话:
今天晚上我想吃___
后面该如何跟呢?
我们可以建立一个简单的数学模型:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w 1 , w 2 , . . . , w n ) = P ( w 1 ) ∗ P ( w 2 ∣ w 1 ) ∗ P ( w 3 ∣ w 1 , w 2 ) ∗ . . . ∗ P ( w n ∣ w 1 , w 2 , . . . , w n − 1 ) P(w_1, w_2, ..., w_n) = P(w_1) * P(w_2|w_1) * P(w_3|w_1,w_2) * ... * P(w_n|w_1,w_2,...,w_{n-1}) </math>P(w1,w2,...,wn)=P(w1)∗P(w2∣w1)∗P(w3∣w1,w2)∗...∗P(wn∣w1,w2,...,wn−1)
- <math xmlns="http://www.w3.org/1998/Math/MathML"> w 1 , w 2 , . . . , w n w_1, w_2, ..., w_n </math>w1,w2,...,wn表示一个句子中的字符序列,而 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w i ) P(w_i) </math>P(wi)则表示字符在语料库中出现的概率。
- <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w i ∣ w 1 , w 2 , . . . , w n − 1 ) P(w_i|w_1,w_2,...,w_{n-1}) </math>P(wi∣w1,w2,...,wn−1)表示在已知 <math xmlns="http://www.w3.org/1998/Math/MathML"> w 1 , w 2 , . . . , w n − 1 w_1,w_2,...,w_{n-1} </math>w1,w2,...,wn−1的字符概率, <math xmlns="http://www.w3.org/1998/Math/MathML"> w i w_i </math>wi字符出现的概率。
根据这样一个简单的模型,我们就可以根据上文,预测出下文的字符,并最终得到完整的语句出现概率。
假设我们现在填空有三个不同的选项,分别是:
- 牛肉泡馍
- 菜夹馍
- 臊子面
再假设 牛、菜、臊三个字的自然分布概率分别为 0.0008、0.0003、以及0.0005。
什么是文字的自然分布概率?
文字的自然分布概率指单个文字在大型真实语料库(如新闻、书籍、网页文本等)中出现的频率统计,即:
P(c)=语料中所有汉字的总出现次数/汉字 c 在语料中出现的次数
例如,"的"字在中文中出现频率极高(约4%),而"龘"字极低(<0.0001%)。
那么今晚我想吃___
后面就紧跟 牛 吗?因为牛字的概率最高!
并非如此。
在计算后面跟什么字的时候,我们需要依赖当前上下文,在此前提条件下,计算空格处出现每个字的概率,这样我们可以得到一个条件概率公式:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w 5 = 牛 ∣ w 0 − 4 = 今晚 . . . 想吃 ) = P ( w 0 − 5 = 今晚 . . . 想吃牛 ) P ( w 0 − 4 = 今晚 . . . 想吃 ) = 0.000008 0.00003 = 0.27 P(w_{5}=牛|w_{0-4}=今晚...想吃)=\cfrac{P(w_{0-5}=今晚...想吃牛)}{P(w_{0-4}=今晚...想吃)}=\cfrac{0.000008}{0.00003}=0.27 </math>P(w5=牛∣w0−4=今晚...想吃)=P(w0−4=今晚...想吃)P(w0−5=今晚...想吃牛)=0.000030.000008=0.27
通过条件概率分别计算出 牛、菜以及臊的概率,并追加到文本后面。
循环计算,直到得出完整句子。
二 N-gram
N-gram 模型是一种基于统计的语言模型,通过分析文本中连续出现的 N 个词(或字符)的分布规律来捕捉语言的局部统计特征。其核心思想是马尔可夫假设:当前词的出现概率仅依赖于前 N-1 个词。
一般来说,N-gram:由 N 个连续词组成的序列。例如:
- Unigram (1-gram):单个词(如"语言")
- Bigram (2-gram):两个连续词(如"自然语言")
- Trigram (3-gram):三个连续词(如"处理文本数据")
N-gram 模型简化了第一小节的语言模型,第一小节的语言模型在计算时,当句子非常长的时候,会导致计算变得异常复杂,而 N-gram 则在一定程度上简化了计算。
N-gram 模型的数学基础如下:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( 句子 ) ≈ ∏ P ( w i ∣ w i − 1 , ... , w i − N + 1 ) P(\text{句子}) \approx \prod P(w_i | w_{i-1}, \ldots, w_{i-N+1}) </math>P(句子)≈∏P(wi∣wi−1,...,wi−N+1)
例如,句子"我爱自然语言"的Bigram概率为:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( 我 ) × P ( 爱|我 ) × P ( 自然|爱 ) × ... P(\text{我}) \times P(\text{爱|} \text{我}) \times P(\text{自然|} \text{爱}) \times \ldots </math>P(我)×P(爱|我)×P(自然|爱)×...
如果利用 N-gram 计算第一小节的问题,那么我们可以省去 今晚 两个字,而直接基于"我想吃"三个字去推断后面的内容。
那么最终得到的公式类似下面这样:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w 5 = 牛 ∣ w 2 − 4 = 我想吃 ) = P ( w 2 − 5 = 我想吃牛 ) P ( w 2 − 4 = 我想吃 ) = 0.000008 0.00003 = 0.27 P(w_{5}=牛|w_{2-4}=我想吃)=\cfrac{P(w_{2-5}=我想吃牛)}{P(w_{2-4}=我想吃)}=\cfrac{0.000008}{0.00003}=0.27 </math>P(w5=牛∣w2−4=我想吃)=P(w2−4=我想吃)P(w2−5=我想吃牛)=0.000030.000008=0.27
上面这个公式算到最后会有一个问题。就是越算值越无限接近于零,最终可能会计算越界。
三 概率 Log 化
语言模型的 Log 化(Logarithmic Transformation)是将概率计算转换为对数空间的核心技术,主要用于解决概率连乘的数值计算问题。
语言模型计算句子概率时需连乘多个条件概率,例如前面的 N-gram 模型:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( w 5 = 牛 ∣ w 2 − 4 = 我想吃 ) = P ( w 2 − 5 = 我想吃牛 ) P ( w 2 − 4 = 我想吃 ) = 0.000008 0.00003 = 0.27 P(w_{5}=牛|w_{2-4}=我想吃)=\cfrac{P(w_{2-5}=我想吃牛)}{P(w_{2-4}=我想吃)}=\cfrac{0.000008}{0.00003}=0.27 </math>P(w5=牛∣w2−4=我想吃)=P(w2−4=我想吃)P(w2−5=我想吃牛)=0.000030.000008=0.27
这个数学公式会有什么问题呢?
- 下溢(Underflow):概率值范围 [0,1],连乘后急剧趋近 0(如 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0. 1 10 = 1 0 − 10 0.1^{10}=10^{-10} </math>0.110=10−10),超出浮点数精度范围。
- 计算效率低:浮点数乘法比加法慢数倍(CPU/GPU 硬件特性)。
- 优化困难:梯度计算在接近 0 时不稳定。
log 化就是对概率取自然对数。
<math xmlns="http://www.w3.org/1998/Math/MathML"> log ( P ( 句子 ) ) = ∑ i = 1 n log ( P ( w i ∣ w 1 : i − 1 ) ) \log(P(\text{句子})) = \sum_{i=1}^n \log \left( P(w_i | w_{1:i-1}) \right) </math>log(P(句子))=∑i=1nlog(P(wi∣w1:i−1))
这样转换之后,有三个优势:
- 单调性:log函数单调递增,不改变概率大小关系。
- 乘法转加法: <math xmlns="http://www.w3.org/1998/Math/MathML"> log ( a × b ) = log ( a ) + log ( b ) \log(a \times b) = \log(a) + \log(b) </math>log(a×b)=log(a)+log(b)
- 数值稳定性:概率范围 [0,1] → Log概率范围 ( <math xmlns="http://www.w3.org/1998/Math/MathML"> − ∞ , 0 -\infty, 0 </math>−∞,0],避免下溢。
接下来松哥通过一个简单的例子来说明 log 函数的应用。
假设现在有如下任务:
检测句子 他的性恪温和
并纠错(正确应为性格)。
Log 化前:
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( 性格 ) = P ( 性 ) × P ( 格 ∣ 性 ) ≈ 0.008 × 0.7 = 0.0056 P(\text{性格}) = P(\text{性}) \times P(\text{格} | \text{性}) \approx 0.008 \times 0.7 = 0.0056 </math>P(性格)=P(性)×P(格∣性)≈0.008×0.7=0.0056
<math xmlns="http://www.w3.org/1998/Math/MathML"> P ( 性恪 ) = P ( 性 ) × P ( 恪 ∣ 性 ) ≈ 0.008 × 1 0 − 5 = 8 × 1 0 − 8 P(\text{性恪}) = P(\text{性}) \times P(\text{恪} | \text{性}) \approx 0.008 \times 10^{-5} = 8 \times 10^{-8} </math>P(性恪)=P(性)×P(恪∣性)≈0.008×10−5=8×10−8
现在的问题是: <math xmlns="http://www.w3.org/1998/Math/MathML"> 8 × 1 0 − 8 8 \times 10^{-8} </math>8×10−8 在乘法中易丢失精度。
Log 化后:
<math xmlns="http://www.w3.org/1998/Math/MathML"> log P ( 性格 ) ≈ log ( 0.008 ) + log ( 0.7 ) = − 4.83 + ( − 0.36 ) = − 5.19 \log P(\text{性格}) \approx \log(0.008) + \log(0.7) = -4.83 + (-0.36) = -5.19 </math>logP(性格)≈log(0.008)+log(0.7)=−4.83+(−0.36)=−5.19
<math xmlns="http://www.w3.org/1998/Math/MathML"> log P ( 性恪 ) ≈ log ( 0.008 ) + log ( 1 0 − 5 ) = − 4.83 + ( − 11.51 ) = − 16.34 \log P(\text{性恪}) \approx \log(0.008) + \log(10^{-5}) = -4.83 + (-11.51) = -16.34 </math>logP(性恪)≈log(0.008)+log(10−5)=−4.83+(−11.51)=−16.34
这样一来,计算的差值从 0.0056 放大为 -5.19 vs -16.34,模型可明确选择性格。
简单来说,Log 化是语言模型的计算基石,通过:
- 乘法转加法 → 解决下溢与提速;
- 拉大数值间距 → 增强决策鲁棒性;
- 衔接损失函数 → 优化训练稳定性;
在输入法、机器翻译、语音识别等场景中,只要涉及概率比较或长序列生成,Log 化都是不可或缺的工程手段。