查看全课大纲(第 60 / 82 节)
  1. 1.函数与数据泛化
  2. 2.向量:从几何直觉到高维空间
  3. 3.矩阵与张量
  4. 4.特征值与特征向量:矩阵变换的不动方向
  5. 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
  6. 6.概率与统计:机器学习的数学基础
  7. 7.独立性与随机变量:贝叶斯公式与二项分布
  8. 8.期望、方差与协方差:随机变量的数字画像
  9. 9.联合概率:当一条轴不够用的时候
  10. 10.概率分布(一):从伯努利到伽马函数
  11. 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
  12. 12.机器学习的模型:找输入与输出的关系
  13. 13.机器学习的应用
  14. 14.过拟合
  15. 15.机器学习的分类
  16. 16.有监督学习:从房价预测说起
  17. 17.损失函数
  18. 18.梯度下降(上):更新规则与学习速率
  19. 19.梯度下降(2)
  20. 20.牛顿方法
  21. 21.KNN简介
  22. 22.距离测算
  23. 23.朴素贝叶斯
  24. 24.朴素贝叶斯的实际应用
  25. 25.信息熵:决策树构建的度量基础
  26. 26.信息增益与决策树的构建
  27. 27.ID3算法
  28. 28.决策树节点的划分
  29. 29.CART树
  30. 30.Bagging
  31. 31.Boosting:自适应提升算法
  32. 32.Gradient Boosting
  33. 33.线性模型:回归与分类的同一副骨架
  34. 34.最小二乘:把最优参数直接解出来
  35. 35.最小二乘法的几何与概率视角
  36. 36.极大似然:给最小二乘一个概率出身
  37. 37.逻辑回归(1):把线性模型接到分类上
  38. 38.逻辑回归(2):损失函数与多分类
  39. 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
  40. 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
  41. 41.优化方法(1):坐标下降与前向选择
  42. 42.优化方法(2):前向策略与最小角回归
  43. 43.拉格朗日乘子法(1):从无约束到等式约束
  44. 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
  45. 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
  46. 46.最大间隔:把分类问题变成优化问题
  47. 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
  48. 48.支持向量:边界上的少数派
  49. 49.SVM 计算过程:一笔到底算完,再走进核方法
  50. 50.贝叶斯公式:概率即信念
  51. 51.隐含变量和概率模型:给看不见的原因建模
  52. 52.EM 算法:期望与最大化的迭代
  53. 53.琴生不等式:EM 的数学钥匙
  54. 54.三个硬币问题:EM 的复盘与衔接
  55. 55.高斯混合模型:用隐变量解释双峰数据
  56. 56.隐马尔可夫模型
  57. 57.隐马尔可夫模型(2):前向算法与动态混合模型
  58. 58.隐马尔可夫模型在 NLP 中的运用
  59. 59.EM 算法:把「为什么收敛」推到底
  60. 60.HMM 在 NLP 中的运用:从分词到机器翻译
  61. 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
  62. 62.高维下的高斯模型
  63. 63.主题模型:从词袋到隐含主题
  64. 64.Dirichlet 分布(1)
  65. 65.Dirichlet 分布(2)
  66. 66.统计学习内容回顾
  67. 67.K-means 算法
  68. 68.PCA 降维
  69. 69.SVD 降维
  70. 70.从聚类到流形
  71. 71.局部线性嵌入
  72. 72.随机邻域嵌入:SNE 与 t-SNE
  73. 73.无参模型:让数据自己说话
  74. 74.高斯过程:函数空间里的正态分布
  75. 75.高斯过程(2):噪声、正则化与核的线性组合
  76. 76.神经网络简介:从感知器到自编码器
  77. 77.反向传播:把误差一层层传回去
  78. 78.卷积神经网络(CNN):让特征自己学出来
  79. 79.循环神经网络(RNN):给网络装上记忆
  80. 80.深度学习回顾:静态一类,动态一类
  81. 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
  82. 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传

HMM 在 NLP 中的运用:从分词到机器翻译

约 17 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 机器学习算法精讲

上一节收尾在循环神经网络——本节开场讲师先把这件事按住:「RNN 做的事情,跟我们这个做的是一回事。」接着把 NLP 的标注问题从简单到难重排一遍(讲师自我修正:「这个稍复杂,如果从简单到难,应该先这样讲」),并在最后加上第四个问题:机器翻译——它涉及对齐与长度变化,比语音识别还要复杂。做完这趟 NLP 之旅,讲师把话锋转回 HMM 本身:「我们还没有对 HMM 进行参数的估计」——维特比与 Baum-Welch 两个名字在这里正式出现,AA、BB 矩阵的定义重述一遍,为下一节的估计公式开题。

💡 核心导读

  • 标注问题的四档难度:分词(0/1 marker)、词性标注(名词/动词/…)、语音识别(特征向量)、机器翻译(英文词)——观测换装,骨架不变。
  • 分词的多解性:同一串字符「我爱中国人民银行」对应多套合法 marker 序列——标注本身就是监督信号。
  • 翻译=学标注关系:不把语言当语言,把它当「被加密的符号」——统计翻译的思想基础。
  • 方法论沉淀:深奥的 NLP 问题都能用「我们会的一个机器学习模型」解释;最底层的数学与模型认知比编程更珍贵。
  • HMM 三件事凑齐名字:算 P(Y)P(Y)(前向-后向)、找最优状态(维特比)、估参数(Baum-Welch);AA、BB 的正式定义,释放 bjb_j 还可以是高斯混合。

从简单到难:重排 NLP 的标注问题

上节从词性标注讲起,其实顺序偏难了。按「从简单到难」重排,第一个问题最简单——给定的标注是 0/1,用来分词。

讲师重新把例子写得「更有意思一些」:把「我爱中国人民银行」整个连在一起写在黑板上——「甚至我不要这么写(预先分好),这样写大家都会觉得我已经知道分词了。」然后口头切分着玩:「我爱中国。人民银行,可以吧?反正什么词,管它呢。」同一串字,训练数据里可以出现不同的标注:一套 0/1 标成五个词,另一套把「中国人民银行」整个标成一个大词——「哎,其实一个大词,这也是可以。」用代码把几种合法切分的 marker 序列都生成出来(口播的 0/1 串以按切分重新生成的为准):

# 分词 = 给字串标 0/1: 0 = 词内继续, 1 = 词到此截止
# 同一串字符「我爱中国人民银行」, 不同切分 -> 不同 marker 序列
# (板书口播的 0/1 串以代码按切分重新生成为准——正确性优先)
chars = "我爱中国人民银行"

def markers(words):
    """把切分转成 0/1 marker 序列: 词内 0, 最后一字 1"""
    seq = []
    for w in words:
        seq += [0] * (len(w) - 1) + [1]
    return seq

segs = [
    ["我", "爱", "中国", "人民", "银行"],
    ["我", "爱", "中国", "人民银行"],
    ["我", "爱", "中国人民银行"],
    ["我", "爱", "中", "国", "人民", "银行"],
]

print(f"字串: {chars} ({len(chars)} 字)")
print()
for words in segs:
    m = markers(words)
    ok = "".join(words) == chars and m[-1] == 1
    print(f"{' / '.join(words)}")
    print(f"  marker = {''.join(map(str, m))}   ({len(words)} 个词, 合法性 {'OK' if ok else 'NG'})")
print()
# 任何一个「末位为 1」的 0/1 序列都是一个合法分词——「反正什么词, 管它呢」
n = len(chars)
print(f"合法性规则只有一条: 末位必为 1 (词必须截止)")
print(f"所以 {n} 字字串的合法标注共有 2^{n-1} = {2**(n-1)} 种 —— 同一句话, 128 种监督信号")
字串: 我爱中国人民银行 (8 字)

我 / 爱 / 中国 / 人民 / 银行
  marker = 11010101   (5 个词, 合法性 OK)
我 / 爱 / 中国 / 人民银行
  marker = 11010001   (4 个词, 合法性 OK)
我 / 爱 / 中国人民银行
  marker = 11000001   (3 个词, 合法性 OK)
我 / 爱 / 中 / 国 / 人民 / 银行
  marker = 11110101   (6 个词, 合法性 OK)

合法性规则只有一条: 末位必为 1 (词必须截止)
所以 8 字字串的合法标注共有 2^7 = 128 种 —— 同一句话, 128 种监督信号

这串例子把 14.1 的 0/1 编码往前推了一步:同一句话有 128 套合法标注,语料给哪一套,模型就学哪一套——标注本身就是监督信号。而且分词时 BB 矩阵特别简单:yy 只有 0 和 1 两个取值,每个状态两列就装下了。

第二个问题,词性标注——分词已经有了之后再做:我(名词)、爱(动词)、中国(名词)、人民银行(讲师自注:「具体词性标注我不太记得了,可能是个名词短语或什么其他吧,假设我们先这样去写」)。总之会有很多这样的标注,这块叫 POS tagging(part of speech tagging),刚才那块叫 segmentation(分词)。

第三个问题,语音识别:标注不再是一个词性,而是一个向量——比如某个状态标注 (0.3,0.2,0.1)(0.3, 0.2, 0.1),另一个标 (0.7,0.5,0.4)(0.7, 0.5, 0.4)……「这里边的向量其实代表什么呢?从语音系统里边抽象出来的这些特征」——这就是上一节讲过的 speech recognition。

第四个问题:机器翻译

还是这些词,讲师又换了一支蓝色的粉笔——第四种标注是英文:句子上方标出「I love people’s bank of China」(讲师第一次口误读岔了,自己纠正:「可能是一般来讲不会这样说话的,可能是这样的」)。这一段英文是什么?是这句话对它的英文翻译。于是「我们就学习它们之间的标注关系——不就是学习一个自然语言翻译嘛。」这里的思想非常朴素:

我们并不把它看成语言——词与词之间的、一个 marker 或标志之间的关系而已。

就像有人最早说过的:看到一串俄语,我并不觉得它是「俄语」,我觉得它是用一种英文加密的方式在写;看到阿拉伯语,觉得是有种信号把我们熟悉的话加密成了这种符号——我看到符号解密就可以了。「所以这种形式实际上变成了后来一直到今天,自然语言翻译最重要的基础。」历史上正好是一个转折:以前做翻译要根据语法、词典,分析 people 和「人民」之间有什么对应关系;后来人们渐渐放弃了这条路线,完全基于大量语料库、训练它们之间的对应关系。

讲师讲了一个有名的故事:吴军的老师 Jelinek(Frederick Jelinek,先在 IBM 领导统计语音与语言研究、后来去了 Johns Hopkins)在做统计翻译系统时说过一句经典的话——

我每次开除一个语言学家,我这个系统的性能就能提高一点。

「后来会发现,做语言翻译的这些人全是计算机学家,根本不懂任何语法,但仍然翻译得非常好,比语言学家翻译得还好。」所以到今天,处理自然语言的(包括我们自己),都可以把这些语言当成数据——不理解深奥的语言学语法,照样做词性标注、词的分类、机器翻译。

但翻译的难度要高很多。刚才所有问题里,标签与字词都是一对一的关系;而翻译时「China」对应的是「中国」,没有对齐。它还涉及句子长短不一样:这里可能是四个词,翻译出来是六个词;原先五个词,翻出来八个词。「首先词长短不一样,所对齐的方式也不一样。之后呢,才有这个标注,再学习标注。所以自然语言翻译要远远难于词性标注和分词,甚至比语音识别还要复杂很多。」当然,今天的翻译大部分已经是基于 RNN 式结构的了——RNN 里还有一些极端的方式,比如 LSTM(Long Short-Term Memory)。「这个在深度学习里面大家会有很多讲述,我们这课里就不讲了。」它的 capability(自由度)比这个模型更广泛:可以考虑一个词跟它前面很久很久的词之间的关系——这是 HMM 没有做到过的。

方法论:模型认知比编程更珍贵

到这里讲师特意停下来讲了一段方法论。研究自然语言里三个(加上翻译是四个)非常深奥的问题,我们都可以用一个我们会用的机器学习模型去解释;如果模型学得更好、在里面加更多假设、知道更多先验,就会把这个模型做得非常好。

今天很多学机器学习、需要学人工智能的人——其实最底层的数学和对这个模型的认知是最重要的。看到一个实际的问题,如何把它抽象成一个数学问题(或者一个机器学习问题),之后再编程出来,这才是一个完整的解决步骤。

「反而会编程的人多了好多,但是能把这个具体背后的东西理解出来、或者做出模型的人,显得非常珍贵。」所以做算法工程师,实际上主要就是在做这件事——找到特别合适的一种算法去解决这个问题。这句话和上一节 EM 开场的「最底层」遥相呼应:这一整章的用意,就是把底层交到你手里。

回到 HMM:参数怎么估(开题)

方法论讲完,话锋一转:「HMM——我们回过头来说这件事,我们还没有对 HMM 进行刚才的这个参数的估计。」先盘点已经会的东西:

  • 算 P(Y)P(Y):通过前向后向算法,任何一个 P(Y)P(Y) 都可以估计成所有 qTq_T 的和——形式上就是 ∑qTα(qT)\sum_{q_T}\alpha(q_T)(13.4 走过的完整表格);
  • 给定一组 yy 求极大似然值:这件事已经会了;
  • 对某一个状态做估计:给定了好多可观察量的时候,想知道某一时刻状态的概率——它就是 γ(qt)=α(qt) β(qt)/P(Y)\gamma(q_t) = \alpha(q_t)\,\beta(q_t) / P(Y),其中 α\alpha 从前往后推、β\beta 从后往前推(前向后向算法的公式,上次课已有描述)。

再往前一步:给定很多 yy,「我们就使这个 γ\gamma 最大就可以了——那就找到了给定这些观察数据的时候,哪些状态的极大似然最大。」这一步有个通行的名字,讲师在这里正式点出:维特比算法(Viterbi)。(小注:严格的维特比是「整条状态路径最优」的动态规划;讲师此处按「使 γ\gamma 最大」的口径点名,方向一致、口径从宽。)而今天要用另外一个算法——Baum-Welch——来推断这里边的具体参数该怎么估计。到这里,经典 HMM 的三件事凑齐了名字:算 P(Y)P(Y) 用前向-后向、找最优状态用维特比、估参数用 Baum-Welch。

A 与 B 的正式定义:Baum-Welch 的原料

往下推之前,把两个矩阵的定义正式重述一遍(这是下一节的原料)。

先看两条条件独立——「这个大家已经知道,不用解释了」:

  1. P(qt∣qt−1,…,q1,  Yt−1,…,Y1)=P(qt∣qt−1)P(q_t \mid q_{t-1}, \dots, q_1,\; Y_{t-1}, \dots, Y_1) = P(q_t \mid q_{t-1})。观察到以前的数据和以前的状态时,当前状态的概率——因为它本身是隐马尔可夫链的结构,qtq_t 的概率最直接相关的就是前一个状态;
  2. P(yt∣所有其他状态与观测)=P(yt∣qt)P(y_t \mid \text{所有其他状态与观测}) = P(y_t \mid q_t)。板书上从 QTQ_T 一直写到 Q1Q_1、YTY_T 一直写到 Y1Y_1,但中间偏偏没有 yty_t——「最重要的是目前的这个 qtq_t,才跟 yty_t 是直接相关的。」

转移矩阵 AA(板书花絮:讲师写「AA 等于 A QT−1A\, Q_{T-1}——Sorry,写到上面」,顺手把下标搬正):

Aij=P(qt=j∣qt−1=i)A_{ij} = P(q_t = j \mid q_{t-1} = i)

任何两个状态之间的 transformation(过渡)。下标 ii、jj 从 1 一直循环到最大状态值:有三种状态就是 3×33\times 3,八种状态就是 8×88\times 8,两万种状态——就像刚才自然语言那个例子——就是两万乘两万。

释放矩阵 BB:

bj(yt)=P(yt=某个具体值∣qt=j)b_j(y_t) = P(y_t = \text{某个具体值} \mid q_t = j)

「状态等于 jj 的时候,观察到 yty_t 这个值的概率是多大」——这就是 BB 的定义。而 BB 的形态是自由的:「我们当然可以把这个 bb 认为是一个高斯分布,或者是一个伯努利分布——都可以。」比如状态 jj 的释放用一个两分量高斯混合(板书花絮:讲师先说「派一加上第一个」又自己改口「sorry 不用,派一乘上第一个」):

bj(y)=π1 N(y∣μ1,σ1)+π2 N(y∣μ2,σ2)b_j(y) = \pi_1\, \mathcal{N}(y \mid \mu_1, \sigma_1) + \pi_2\, \mathcal{N}(y \mid \mu_2, \sigma_2)

「这都是可以的。」把这条板书落成数——它就是 12 章三硬币、13 章高斯混合的又一次重逢,只不过现在嵌在 HMM 的 BB 矩阵里:

import math

# 释放概率 B 不必是离散表格——讲师板书: b_j(y) 可以是高斯混合
# b_j(y) = pi_1 * N(y | mu_1, sigma_1) + pi_2 * N(y | mu_2, sigma_2)

def gauss(y, mu, sigma):
    return math.exp(-0.5 * ((y - mu) / sigma) ** 2) / (sigma * math.sqrt(2 * math.pi))

pi1, mu1, s1 = 0.6, 2.0, 0.5    # 分量 1
pi2, mu2, s2 = 0.4, 5.0, 1.0    # 分量 2

print(f"b_j(y) = {pi1}*N(y|mu={mu1}, sigma={s1}) + {pi2}*N(y|mu={mu2}, sigma={s2})")
print(f"权重检查: pi_1 + pi_2 = {pi1} + {pi2} = {pi1 + pi2}")
print()
print(f"{'y':>4} | {'pi_1*N_1':>10} | {'pi_2*N_2':>10} | {'b_j(y)':>8}")
for y in [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]:
    c1 = pi1 * gauss(y, mu1, s1)
    c2 = pi2 * gauss(y, mu2, s2)
    print(f"{y:4.1f} | {c1:10.4f} | {c2:10.4f} | {c1 + c2:8.4f}")
b_j(y) = 0.6*N(y|mu=2.0, sigma=0.5) + 0.4*N(y|mu=5.0, sigma=1.0)
权重检查: pi_1 + pi_2 = 0.6 + 0.4 = 1.0

   y |   pi_1*N_1 |   pi_2*N_2 |   b_j(y)
 1.0 |     0.0648 |     0.0001 |   0.0648
 2.0 |     0.4787 |     0.0018 |   0.4805
 3.0 |     0.0648 |     0.0216 |   0.0864
 4.0 |     0.0002 |     0.0968 |   0.0969
 5.0 |     0.0000 |     0.1596 |   0.1596
 6.0 |     0.0000 |     0.0968 |   0.0968

一眼看去是双峰:yy 靠近 2 时分量 1 说了算(0.4787 的绝对主力),靠近 5 时分量 2 接管(0.1596)——一个状态内部再挂一个混合模型,bjb_j 就有了「一峰不够再补一峰」的表达力。参数全给定了,具体怎么从数据里把它们估计出来——π\pi、AA、BB 每一个怎么算——就是下一节 Baum-Welch 要正式展开的内容。

📝 动手练一练

  1. marker 的多解与计数:「我爱中国人民银行」(8 字)除代码里四种切分外还有许多合法标注。(a) 证明:nn 字字串的合法 marker 序列总数是 2n−12^{n-1};(b) 手写「我 / 爱 / 中国人民 / 银行」的 marker 序列,并验证末位规则。
👉 点击查看参考答案

(a) 合法性规则只有一条:末位必为 1(最后一个词必须截止),前 n−1n-1 位每一位独立地取 0 或 1——所以总数 2×2×⋯×2=2n−12 \times 2 \times \dots \times 2 = 2^{n-1}。8 字即 27=1282^7 = 128 种。每一种 0/1 串都唯一对应一个切分(连续的 0 段加末尾的 1 就是一个词),所以「标注」与「切分」一一对应。

(b) 我(1)、爱(1)、中(0)、国(0)、人(0)、民(1)、银(0)、行(1)——marker 为 1, 1, 0, 0, 0, 1, 0, 11,\,1,\,0,\,0,\,0,\,1,\,0,\,1,末位为 1,合法。词数 = 1 的个数 = 4:我、爱、中国人民、银行。

  1. 释放概率的形态:状态 jj 的释放用高斯混合 bj(y)=π1N(μ1,σ1)+π2N(μ2,σ2)b_j(y) = \pi_1 \mathcal{N}(\mu_1, \sigma_1) + \pi_2 \mathcal{N}(\mu_2, \sigma_2)。(a) 权重要满足什么约束?(b) 这与 12 章的哪个模型是重逢?13.4 讲过的「动态混合」与这里的用法差在哪?(c) 如果观测是离散的 0/1(分词 marker),bjb_j 该长什么样、满足什么约束?
👉 点击查看参考答案

(a) π1+π2=1\pi_1 + \pi_2 = 1 且各 π≥0\pi \ge 0(本课代码里的 0.6 + 0.4 = 1.0 即此约束)。

(b) 与 12 章的三硬币(伯努利混合)、男女生身高(高斯混合)同一形态——静态混合。区别在装法:13.4 的动态混合把混合分量放进状态、分量间的跳跃由 AA 控制(AA 管跳跃、BB 管各高斯的 μσ\mu\sigma);本节的用法是给单个状态的释放再装一个内部混合——一个在状态之间跳、一个在状态内部混,两种装法都成立。

(c) 两列的伯努利表:bj(1)=P(yt=1∣qt=j)b_j(1) = P(y_t = 1 \mid q_t = j),bj(0)=1−bj(1)b_j(0) = 1 - b_j(1)——约束就是把一行加起来等于 1。

本章小结

本节先把 NLP 的标注问题按难度重排:分词(0/1 marker,同一句话 128 套合法标注——标注即监督)、词性标注(分词之后再逐词标)、语音识别(标注是语音特征向量),再添上第四个问题机器翻译——把语言当「被加密的符号」、用大量语料学对应关系(Jelinek「开除语言学家」的名言),但对齐与长度变化使它比语音识别还难,今天的解法是 RNN/LSTM(本课不展开)。方法论沉淀成一句话:最底层的数学与模型认知最珍贵,算法工程师的本职是把现实问题抽象成数学问题再选对算法。后半节回到 HMM 参数估计的开题:已会 P(Y)=∑α(qT)P(Y)=\sum\alpha(q_T)、极大似然与 γ(qt)=αβ/P(Y)\gamma(q_t)=\alpha\beta/P(Y);三件事凑齐名字(前向-后向、维特比、Baum-Welch);Aij=P(qt=j∣qt−1=i)A_{ij}=P(q_t=j|q_{t-1}=i) 与 bj(yt)=P(yt∣qt=j)b_j(y_t)=P(y_t|q_t=j) 正式定义,释放还可以用高斯混合——具体怎么估,下一节 Baum-Welch 展开。

📋 行动清单

  • 在 14.1 的「三问题表」上添第四行:机器翻译——YY 与标注各是什么、难在哪(对齐、长度)。
  • 给自己写一句话(8 字以上),仿照代码块枚举三种合法切分并验证 marker 合法性。
  • 回顾 12.2-12.3 的静态混合与 13.4 的动态混合,对照本节 bjb_j 的高斯混合形态,把三种「混合装法」各写一句话。
  • 带着开题的问题进入下一节:Baum-Welch 里 π\pi、AA、BB 的估计公式怎么落地。

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第14章笔记:隐马尔可夫模型(HMM)
    下载