查看全课大纲(第 63 / 82 节)
- 1.函数与数据泛化
- 2.向量:从几何直觉到高维空间
- 3.矩阵与张量
- 4.特征值与特征向量:矩阵变换的不动方向
- 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
- 6.概率与统计:机器学习的数学基础
- 7.独立性与随机变量:贝叶斯公式与二项分布
- 8.期望、方差与协方差:随机变量的数字画像
- 9.联合概率:当一条轴不够用的时候
- 10.概率分布(一):从伯努利到伽马函数
- 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
- 12.机器学习的模型:找输入与输出的关系
- 13.机器学习的应用
- 14.过拟合
- 15.机器学习的分类
- 16.有监督学习:从房价预测说起
- 17.损失函数
- 18.梯度下降(上):更新规则与学习速率
- 19.梯度下降(2)
- 20.牛顿方法
- 21.KNN简介
- 22.距离测算
- 23.朴素贝叶斯
- 24.朴素贝叶斯的实际应用
- 25.信息熵:决策树构建的度量基础
- 26.信息增益与决策树的构建
- 27.ID3算法
- 28.决策树节点的划分
- 29.CART树
- 30.Bagging
- 31.Boosting:自适应提升算法
- 32.Gradient Boosting
- 33.线性模型:回归与分类的同一副骨架
- 34.最小二乘:把最优参数直接解出来
- 35.最小二乘法的几何与概率视角
- 36.极大似然:给最小二乘一个概率出身
- 37.逻辑回归(1):把线性模型接到分类上
- 38.逻辑回归(2):损失函数与多分类
- 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
- 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
- 41.优化方法(1):坐标下降与前向选择
- 42.优化方法(2):前向策略与最小角回归
- 43.拉格朗日乘子法(1):从无约束到等式约束
- 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
- 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
- 46.最大间隔:把分类问题变成优化问题
- 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
- 48.支持向量:边界上的少数派
- 49.SVM 计算过程:一笔到底算完,再走进核方法
- 50.贝叶斯公式:概率即信念
- 51.隐含变量和概率模型:给看不见的原因建模
- 52.EM 算法:期望与最大化的迭代
- 53.琴生不等式:EM 的数学钥匙
- 54.三个硬币问题:EM 的复盘与衔接
- 55.高斯混合模型:用隐变量解释双峰数据
- 56.隐马尔可夫模型
- 57.隐马尔可夫模型(2):前向算法与动态混合模型
- 58.隐马尔可夫模型在 NLP 中的运用
- 59.EM 算法:把「为什么收敛」推到底
- 60.HMM 在 NLP 中的运用:从分词到机器翻译
- 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
- 62.高维下的高斯模型
- 63.主题模型:从词袋到隐含主题
- 64.Dirichlet 分布(1)
- 65.Dirichlet 分布(2)
- 66.统计学习内容回顾
- 67.K-means 算法
- 68.PCA 降维
- 69.SVD 降维
- 70.从聚类到流形
- 71.局部线性嵌入
- 72.随机邻域嵌入:SNE 与 t-SNE
- 73.无参模型:让数据自己说话
- 74.高斯过程:函数空间里的正态分布
- 75.高斯过程(2):噪声、正则化与核的线性组合
- 76.神经网络简介:从感知器到自编码器
- 77.反向传播:把误差一层层传回去
- 78.卷积神经网络(CNN):让特征自己学出来
- 79.循环神经网络(RNN):给网络装上记忆
- 80.深度学习回顾:静态一类,动态一类
- 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
- 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传
主题模型:从词袋到隐含主题
约 22 分钟
小象实战讲义 · 机器学习算法精讲
上一节我们看到,生成式模型的参数不见得是一个数:高维数据 的生成由高维参数控制——均值是一个向量、协方差是一个矩阵。带着这个认识,我们进入本章的重点:主题模型(Topic Model),学习如何从一堆看似无序的词袋(Bag of Words)中,自动发现隐藏的”主题”,并用概率生成模型刻画文档与词之间的关系。这一节我们重点理解 PLSA 的基本思想,建立”文档-主题-词”的生成直觉。
💡 核心导读
- 从词袋到主题:超越简单的词频统计,引入”主题”作为中间隐含变量,连接文档与词语。
- PLSA模型:概率潜在语义分析,用概率图模型清晰定义文档、主题、词三者的生成关系。
- “硬币”类比:用”作者选择硬币(主题混合)-> 硬币掷出词语”的生动比喻,理解主题模型的生成机制。
- 模型表示:学习概率图模型中的”盘(plate)“表示法,用它简洁描述重复的生成过程。
- 迈向LDA:本节只是最简单的 latent topic 概念引入,真正的 LDA(潜在狄利克雷分配)及其完整数学描述在接下来的几节展开。
文本表示:从序列到词袋
为什么这个问题叫”主题”?这要从自然语言处理的历史说起。语言能力是人类智能最重要的标志之一——小猫小狗也能看见东西、被打时也会躲,但它们的语言能力与人类语言的复杂程度和背后的语义相比,完全不在一个量级。因此早期的人工智能研究很多都从自然语言开始:计算机科学家和语言学家一起,依靠语法、语义、句法规则来构建理解语言的系统。但后来发现语言学家在这件事上往往帮倒忙,业内甚至流传一句名言:每开除一位语言学家,语音识别系统的性能就提高一点。
于是人们渐渐放弃”让机器像人脑一样理解语言”,转向统计与概率的方法:机器处理语言的方式和人本来就不一样,完全可以把词看成数字和符号。当读到一句话时,人脑中会有神经层面的变化从而”理解”了它;而机器可以把每个字直接变成一个编号——这个词是 101,那个词是 372,另一个是 41145——面对这些数字和符号,它依然能完成处理。在这条路上,一个关键的简化就是词袋模型(Bag of Words, BoW):我们并不关心词谁先谁后、词性是什么、代表什么语义,只关心每个词和每个词是不一样的——每个文档就是装着许多词的一个”袋子”。
例如两篇极简的文档:“I love this movie” 和 “I love China”,可以把它们转化成一个词-文档矩阵(Term-Document Matrix):
# 示例:构建一个极简的词-文档矩阵
# 文档1: "I love this movie"
# 文档2: "I love China"
vocabulary = {'I': 0, 'love': 1, 'this': 2, 'movie': 3, 'China': 4}
# 矩阵表示 (行为词,列为文档)
term_doc_matrix = [
[1, 1], # 'I' 在文档1和2中各出现1次
[1, 1], # 'love'
[1, 0], # 'this' 只在文档1出现
[1, 0], # 'movie'
[0, 1], # 'China' 只在文档2出现
]
print("词-文档矩阵(行为词,列为文档):")
for word, idx in vocabulary.items():
print(f"{word:>6}: {term_doc_matrix[idx]}")词-文档矩阵(行为词,列为文档):
I: [1, 1]
love: [1, 1]
this: [1, 0]
movie: [1, 0]
China: [0, 1]矩阵的行是词表(vocabulary,大小记为 ),列是文档(可以不断往下加,加到 篇),整个矩阵的大小是 。对这个矩阵做奇异值分解(SVD),就得到传统文本分析的经典技术——潜在语义分析(LSA):把词和文档映射到一个低维语义空间。SVD 具体怎么分解,后面的课会跟 PCA 一起专门讲,这里只需知道它是直接的代数运算。LSA 的问题是它完全没用到概率和生成模型的概念——这正是 PLSA 的切入点。
概率潜在语义分析(PLSA)
概率潜在语义分析(Probabilistic Latent Semantic Analysis, PLSA)在词袋的基础上引入了概率生成框架和隐含变量。它的基本假设很朴素:如果某些词经常在一起出现,就认为它们的语义相关——以此为语义建模。
基本思想与”硬币”类比
PLSA 的核心思想是:文档中的词并非直接由文档产生,而是通过一个隐含的”主题(Topic)“中介生成的。在图模型里,词 和文档 都是可观测的数据(图中阴影节点),中间引入一个不可直接观测的隐含变量 (也叫 ),它就是我们说的 topic。
主题是什么?一个主题就是词的一种组合、词上的一个分布。比如”计算机”主题——它没有一个具体的标签叫 computer,但你会看到 Bill Gates、Word、PPT、machine learning 这些词在它之下出现的概率很大;而 Bible、God、Jesus 这些词则明显属于另一个偏宗教的话题。我们希望在整个大词典里把词分成这样不同的部分,每个 topic 就看成词的一个分布。
理解这个模型最好的方式还是硬币类比——它和我们学三硬币模型时一脉相承:
- 主题即”硬币”:每个主题好比一枚特殊的硬币,掷出不同面的概率就是不同词的概率。计算机硬币掷出的都是计算机词汇,宗教硬币掷出的都是宗教词汇,还可以有物理硬币、化学硬币……普通的硬币只有正反两面,而这些”主题硬币”可以掷出各自词表里的不同词。
- 文档即”硬币集合”:一篇文档不会只属于一个主题,它是很多硬币的混合。好比一位同学手里拿了不同的硬币,代表他的喜好:如果拿的基本都是计算机硬币、物理硬币、天文硬币,他大概是一位科学爱好者;如果拿的是艺术硬币、文学硬币、美学硬币,那显然是一位文科同学。文档里 topic 的混合比例,就代表它背后语义上的差别。
- 观察与推断:硬币(隐含变量)不能直接观察到,但我们能看到硬币掷出的结果——这位同学每次说出的词。说的都是技术词汇,就是理工科同学;说的都是文学美学,就是文科同学。于是我们不需要观察别的,只要看到这些词,就能对文档进行分类。
这与以前的模型唯一的区别是:以前文档直接被词所替代,直接观察给定文档的词汇;而现在假设这些词汇之上还有一个更高阶的特征——由”主题”这枚硬币掷出来。观察到的词由硬币控制,硬币是隐含变量,隐含变量又跟更上层的 document 相关——一个清晰的三层结构。
下面我们用一段简化的代码来模拟这个”作者-硬币-词语”的生成过程:作者 A 是科技爱好者(80% 科技硬币 + 20% 体育硬币),作者 B 是体育爱好者(20% 科技 + 80% 体育)。
import random
random.seed(42)
# 定义两个主题(两枚"硬币")及其词分布
# 主题0:科技
topic_tech = {'算法': 0.4, '数据': 0.4, '学习': 0.2}
# 主题1:体育
topic_sports = {'比赛': 0.5, '进球': 0.3, '运动员': 0.2}
# 两位作者(文档)的主题混合比例
author_a_mix = [0.8, 0.2] # 作者A:80%科技 + 20%体育
author_b_mix = [0.2, 0.8] # 作者B:20%科技 + 80%体育
def generate_words(num_words, topic_mix):
"""根据主题混合比例生成词语:先选硬币,再掷硬币"""
words = []
topics = [0, 1] # 主题索引
for _ in range(num_words):
# 1. 根据混合比例选择一枚硬币(主题)
chosen_topic = random.choices(topics, weights=topic_mix, k=1)[0]
# 2. 根据选定主题的分布掷出词语
if chosen_topic == 0:
word = random.choices(list(topic_tech.keys()), weights=list(topic_tech.values()), k=1)[0]
else:
word = random.choices(list(topic_sports.keys()), weights=list(topic_sports.values()), k=1)[0]
words.append(word)
return words
# 生成观察数据
words_from_a = generate_words(10, author_a_mix)
words_from_b = generate_words(10, author_b_mix)
print("作者A(科技爱好者)生成的10个词:", words_from_a)
print("作者B(体育爱好者)生成的10个词:", words_from_b)作者A(科技爱好者)生成的10个词: ['算法', '算法', '数据', '比赛', '算法', '数据', '算法', '数据', '数据', '比赛']
作者B(体育爱好者)生成的10个词: ['进球', '比赛', '比赛', '算法', '进球', '进球', '运动员', '进球', '进球', '进球']注意一个细节:作者 A 的袋子里偶尔也会蹦出”比赛”,作者 B 也会蹦出”算法”——因为混合比例不是非 0 即 1,每生成一个词都要重新”抽一次硬币”。这正是主题模型与”一个文档只属于一个主题”的本质区别。
PLSA 的概率图模型
PLSA 用概率图模型来形式化上述生成过程。模型涉及三个变量:
- :文档(可观测)
- :词语(可观测)
- :主题(隐含变量,即类比中的”硬币”)
其生成过程为:
- 以概率 选择一篇文档 。
- 对于文档 中的每一个词的位置: a. 根据条件分布 选择一个主题 。 b. 根据条件分布 从主题 中生成一个词 。
为了简洁地表示重复结构,我们使用盘表示法(Plate Notation):图中的方块(plate)就代表”重复”。如果只画一个隐含变量 对应一个观测 、外面框一个标着 的方块,它展开就是 ——同一对结构重复 次。两个长得像的图模型,差别就看重复的方块把谁圈了进来:圈的位置不同,含义就完全不同。
按这个记法,PLSA 的完整图模型是:外层一个标着 的盘代表 篇文档(,文档参数控制着主题的选择),每篇文档内嵌一个标着 的盘,代表这篇文档的 个词;内盘中对每个词位置 : 指向隐含主题 , 再指向观测词 。一大套复杂的概率关系,用几层”盘子”就简洁地表示出来了。
这个模型想建模的是 与 的联合概率。推导从引入隐含变量开始:我们想知道”第三个文本里出现某个词的概率”,就要先构造隐含变量 ,写出
而 无法直接得到——词并不是从文档直接生成的。注意到 跟 相关( 是 的条件), 又直接跟 相关,于是用贝叶斯公式把 换成 ,再对所有的 topic 求和,就得到:
有了这个联合概率,我们就可以根据已观测的 和 去估计参数(极大似然),进而对未来做预测。具体的求解不需要手推公式——有现成的模型和算法(如期望最大化 EM 算法,正是我们学 GMM 时用过的),而且 topic model 的实现代码有很多不同版本,都可以拿来实验。
展望:从 PLSA 到 LDA
到目前为止,我们其实还没有讲”真正的”主题模型——本节只是最简单地引入了 latent topic 的概念。PLSA 的参数 与训练文档绑定,不是一个完备的生成式模型。接下来两节我们先学习它的数学基石狄利克雷分布(Dirichlet Distribution),然后正式进入 LDA(Latent Dirichlet Allocation,潜在狄利克雷分配)——那里才有完整的数学描述,希望大家跟得上。
📝 动手练一练
- 概念辨析:在PLSA模型中,如果我们已知一篇文档的主题分布 和每个主题的词分布 ,请描述如何计算该文档中出现某个特定词 的概率 。这与我们直接统计文档中词频得到的概率有何本质区别?
👉 点击查看参考答案
根据PLSA的生成过程,文档 中生成词 的概率,需要对所有可能的隐含主题 进行求和: 这个概率是基于模型推断的,它融合了文档的主题倾向和主题本身的用词特点。例如,即使某篇科技文档中从未出现”神经网络”这个词,但如果”神经网络”在”计算机”主题中概率很高,而该文档的”计算机”主题占比也很高,那么模型仍然会赋予 一个可观的概率。
而直接统计词频得到的概率 ,是基于观测数据的。对于未在文档中出现的词,其统计概率为0。主题模型的核心优势就在于能够通过共享的主题信息,实现不同文档间信息的平滑与迁移,对未观测到的词-文档组合进行概率估计。
- 动手实验:下面的代码观察一段词,用贝叶斯推断它更可能来自哪位作者。请你修改
words_manual列表里的词语,观察当生成的词语更偏向体育或更混合时,推断结果如何变化。
import math
# 为保证本块可独立运行,重新定义两枚硬币与两位作者的混合比例
topic_tech = {'算法': 0.4, '数据': 0.4, '学习': 0.2}
topic_sports = {'比赛': 0.5, '进球': 0.3, '运动员': 0.2}
author_a_mix = [0.8, 0.2] # 作者A:80%科技 + 20%体育
author_b_mix = [0.2, 0.8] # 作者B:20%科技 + 80%体育
def likelihood_of_words(words, topic_mix):
"""计算一段词由某混合比例生成的对数似然(对数空间防止下溢)"""
log_likelihood = 0.0
for w in words:
# P(w) = P(w|主题0)*P(主题0) + P(w|主题1)*P(主题1)
p_w_given_t0 = topic_tech.get(w, 0.0)
p_w_given_t1 = topic_sports.get(w, 0.0)
p_w = p_w_given_t0 * topic_mix[0] + p_w_given_t1 * topic_mix[1]
# 使用很小的epsilon防止概率为0导致对数无穷大
log_likelihood += math.log(p_w + 1e-10)
return log_likelihood
# 修改观察到的词,试试不同组合
words_manual = ['比赛', '进球', '运动员']
print("观察到的词语:", words_manual)
like_a = likelihood_of_words(words_manual, author_a_mix)
like_b = likelihood_of_words(words_manual, author_b_mix)
# 假设先验概率相等 P(A)=P(B)=0.5,计算后验概率
# 先做限幅,防止指数溢出
like_a_safe = max(min(like_a, 500), -500)
like_b_safe = max(min(like_b, 500), -500)
posterior_a = math.exp(like_a_safe) / (math.exp(like_a_safe) + math.exp(like_b_safe))
posterior_b = math.exp(like_b_safe) / (math.exp(like_a_safe) + math.exp(like_b_safe))
print("推断结果(假设先验相等):")
print(f" 来自作者A的后验概率: {posterior_a:.4f}")
print(f" 来自作者B的后验概率: {posterior_b:.4f}")
if posterior_a > posterior_b:
print(" 结论:这段文字更可能来自作者A(科技爱好者)。")
else:
print(" 结论:这段文字更可能来自作者B(体育爱好者)。")👉 点击查看参考答案
观察到的词语: ['比赛', '进球', '运动员']
推断结果(假设先验相等):
来自作者A的后验概率: 0.0154
来自作者B的后验概率: 0.9846
结论:这段文字更可能来自作者B(体育爱好者)。这三个词都只出现在”体育”硬币里,作者A(80%科技)生成它们的概率是 ,而作者B是 ——相差 64 倍,后验一边倒。
再加入科技词:如果我们让 words_manual = ['算法', '数据', '比赛'],即两个科技词加一个体育词:
观察到的词语: ['算法', '数据', '比赛']
推断结果(假设先验相等):
来自作者A的后验概率: 0.8000
来自作者B的后验概率: 0.2000
结论:这段文字更可能来自作者A(科技爱好者)。此时,尽管出现了体育词”比赛”,但两个强科技词(在 A 下各 0.32、在 B 下各 0.08)使得整体似然 0.01024 对 0.00256,仍以 4:1 偏向作者A。
全是体育词:如果设置 words_manual = ['比赛', '比赛', '比赛'],即使作者A有 20% 的体育硬币,其连续生成三个”比赛”的概率()也远低于作者B(),系统仍会坚定地判断为作者B(后验与第一组相同:0.0154 对 0.9846)。
要让系统”严重误判”,需要构造一种情况:观察到的词在作者B的强势主题下概率很低,但在作者A的弱势主题下却有相对较高的概率。在本例的简单设定下(每个词只属于一个主题)这很难实现,但在真实的、更复杂的主题分布中,这种交叉情况是可能发生的——这也说明了模型推断的不确定性。
本章小结
本节我们迈出了从简单词袋模型走向深层语义建模的关键一步。从”每开除一位语言学家”的统计主义转向,到词袋与词-文档矩阵的符号化表示,再到 PLSA 引入隐含主题:主题模型通过”主题”这一隐含变量,为文档生成词语的过程提供了优雅的概率解释。其”文档-主题-词”的三层结构、“硬币与同学”的类比、盘表示法与 的联合概率公式,是我们理解后续 LDA 的坚实基础。
📋 行动清单
- 理解核心类比:确保能用”作者选择硬币(主题混合)-> 硬币掷出词语”的比喻,向他人解释主题模型的基本思想。
- 复现关键公式:在纸上写出PLSA的联合概率公式 ,并说明每一项的含义,特别是贝叶斯公式在哪一步介入。
- 运行实验代码:动手修改并运行讲义中的代码,观察不同词语组合如何影响对文档来源(作者)的贝叶斯推断,直观感受主题混合与词语生成的关系。
—— 小象教研组
配套资料 · 直接下载
课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。