查看全课大纲(第 31 / 69 节)
- 1.NLP 的研究领域、应用与学习路径
- 2.自然语言的基本术语
- 3.信息论基础:信息熵与交叉熵
- 4.语言的进化与语义变迁
- 5.主观概率、客观概率与条件概率
- 6.贝叶斯原理与推理
- 7.随机变量:二项式、期望方差与联合概率
- 8.常见概率分布
- 9.辛普森悖论与贝叶斯解题实例
- 10.语言模型:语言概率与词袋模型
- 11.n-gram 语言模型(二元 / 三元 / N-Gram)
- 12.语言模型评价:交叉熵、困惑度与插值
- 13.朴素贝叶斯文本分类:垃圾邮件
- 14.拼写检查
- 15.中文分词:构词法、最大匹配、最优路径与 Jieba
- 16.词表示问题与语义相似度
- 17.TF-IDF 权重处理
- 18.One-Hot 表达与词表示的局限
- 19.Word2Vec 核心思想
- 20.Word2Vec 训练过程
- 21.Skip-gram 模型详解
- 22.词性标注任务与标注方法(规则 + N-Gram)
- 23.EM 算法与混合模型
- 24.高斯混合模型 GMM
- 25.从混合模型到隐马尔可夫模型
- 26.隐马尔可夫模型(HMM)推导与维特比解码
- 27.线性链条件随机场 CRF
- 28.贝叶斯网络与图模型
- 29.隐马尔可夫链与 HMM 推导
- 30.PLSA 主题模型
- 31.LSI 与 SVD 向量表达
- 32.LDA 与狄利克雷分布
- 33.主题模型实验复现(困惑度 / 分类 / 相似度 / 延展)
- 34.上下文无关文法(CFG)与句法分析
- 35.概率上下文无关文法 PCFG
- 36.统计机器翻译:词对齐与翻译模型
- 37.统计机器翻译中的对齐模型与参数估计
- 38.从统计机器翻译到神经机器翻译
- 39.神经元与反向传播速览(指向深度学习课)
- 40.CNN 原理及其在 NLP 中的应用
- 41.循环神经网络的基本原理与 Elman / Jordan 网络
- 42.LSTM:长短期记忆网络的核心思想与分步实现
- 43.Encoder-Decoder 框架
- 44.Seq2Seq 与早期注意力机制
- 45.注意力机制的背景与动机
- 46.注意力模型实现原理与匹配函数
- 47.记忆网络:组成与计算
- 48.注意力机制的应用与历史延展
- 49.Transformer 结构:Encoder-Decoder 栈与 Self-Attention
- 50.Multi-Head Attention、FFNN 与位置编码
- 51.层归一化、损失函数与 Universal Transformer 延展
- 52.预训练范式:从 ELMo 到 BERT
- 53.BERT 与掩码语言模型
- 54.BERT 下游任务微调与 GLUE 口径
- 55.GPT 与自回归语言建模
- 56.GPT-1 到 GPT-4 的演进
- 57.指令微调与 RLHF
- 58.分词器:BPE、WordPiece 与中文词表
- 59.上下文窗口与长文本处理
- 60.能力边界、成本与适用场景
- 61.文本分类实战:从朴素贝叶斯到逻辑回归
- 62.命名实体识别实战
- 63.文本生成与摘要实战
- 64.语义检索与向量库实战
- 65.RAG 知识库问答
- 66.NLP 评估指标:BLEU / ROUGE / BERTScore 与人工评估
- 67.幻觉与事实性问题
- 68.文本与知识结合(知识图谱压缩)
- 69.跨媒体信息检索与未来展望
LSI 与 SVD 向量表达
小象实战讲义 · 自然语言处理
在自然语言处理中,如何将文本从离散的符号序列转化为连续的数学向量,是进行语义分析和计算的基础。本节我们将深入探讨一种经典的文本向量化方法——潜在语义索引(LSI),其核心是利用奇异值分解(SVD)技术,将高维的词-文档矩阵映射到低维的语义空间。通过学习,你将掌握如何用线性代数工具揭示文本背后的潜在主题结构,并实现基于语义相似度的文档检索。
💡 核心导读
- 从词袋到语义空间:理解如何超越简单的词频统计,通过矩阵分解将文本映射到蕴含语义的低维向量空间。
- SVD 的数学原理与应用:掌握奇异值分解(SVD)的几何意义及其在数据降维和特征提取中的核心作用。
- LSI 的工作流程:学习构建词-文档矩阵、进行 SVD 分解、降维以及在新空间中计算语义相似度的完整流程。
- 语义距离的度量:了解如何利用降维后的向量(如通过 KL 散度或余弦相似度)来衡量文本之间的语义相关性。
- 方法的局限与演进:认识 LSI/SVD 作为线性模型的局限性,并了解其如何启发了后续更复杂的非线性模型(如主题模型、神经网络词向量)。
从词表示到文本表示
在之前的课程中,我们学习了如何用 One-Hot 和 Word2Vec 等方法将词表示为向量。现在,我们需要将视角提升到文本(如句子、段落、文档)层面。一个最直观的起点是 词袋模型。
词袋模型将文本视为一个词的集合,忽略词序和语法,只关心词的出现与否或频率。例如,对于词典 ["rock", "granite", "rubble", "music", "song", "band"],我们可以用每个词在文档中的出现次数来表示该文档。
# 示例:六个文档(D1-D6)和一个查询(Q1)的词袋表示
# 词典顺序: [rock, granite, rubble, music, song, band]
import numpy as np
# 构建词-文档矩阵 (6个词 × 7个文本:6个文档 + 1个查询)
# 列:D1, D2, D3, D4, D5, D6, Q1
term_doc_matrix = np.array([
[2, 2, 1, 1, 0, 0, 1], # rock
[1, 1, 1, 0, 0, 0, 0], # granite
[1, 0, 1, 0, 0, 0, 1], # rubble
[0, 0, 0, 1, 2, 2, 0], # music
[0, 0, 0, 1, 1, 2, 0], # song
[0, 0, 0, 0, 1, 1, 0], # band
])
print("词-文档矩阵 (行:词, 列:文本 D1-D6, Q1):")
print(term_doc_matrix)
print("\n文档向量示例 - D1:", term_doc_matrix[:, 0])
print("查询向量示例 - Q1:", term_doc_matrix[:, 6])词-文档矩阵 (行:词, 列:文本 D1-D6, Q1):
[[2 2 1 1 0 0 1]
[1 1 1 0 0 0 0]
[1 0 1 0 0 0 1]
[0 0 0 1 2 2 0]
[0 0 0 1 1 2 0]
[0 0 0 0 1 1 0]]
文档向量示例 - D1: [2 1 1 0 0 0]
查询向量示例 - Q1: [1 0 1 0 0 0]在这个例子中,词 “rock” 具有歧义(既可指“岩石”也可指“摇滚乐”)。在 D1、D2、D3 的上下文中,它更可能指“岩石”;在 D4 中它与音乐词共现,倾向“摇滚乐”义项;D5、D6 由 music、song、band 等音乐词构成,并不包含 rock 一词。简单的词频向量无法捕捉这种基于上下文的语义差异。我们的目标是为每个文本找到一个低维的语义向量,使得语义相近的文本在向量空间中也彼此接近。
奇异值分解(SVD)与降维
奇异值分解 是线性代数中一种强大的矩阵分解技术。对于任意 的实数矩阵 ,SVD 将其分解为三个矩阵的乘积:
其中:
- 是一个 的正交矩阵,其列向量称为左奇异向量,构成 的一组正交基;其中前 个非零奇异值对应的列张成原始矩阵的列空间(词所在的空间)。
- 是一个 的对角矩阵(非方阵时,其余部分为0),对角线上的元素 称为奇异值, 是矩阵 的秩。奇异值代表了对应维度的重要性。
- 是一个 的正交矩阵 的转置, 的列(即 的行)称为右奇异向量,构成 的一组正交基;其中前 个非零奇异值对应的列张成原始矩阵的行空间(文档所在的空间)。
SVD 的几何意义可以理解为:先在文档空间 () 中进行旋转,然后在各个轴上按奇异值 () 进行缩放,最后在词空间 () 中进行旋转。
在文本处理中,我们对词-文档矩阵 ( 个词, 个文档)进行 SVD。分解后,我们可以通过保留前 个最大的奇异值及其对应的奇异向量,实现降维:
这里 是 , 是 , 是 。 的每一列(即 的每一行)就可以看作对应文档在 维语义空间中的新向量表示。
# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
term_doc_matrix = np.array([
[2, 2, 1, 1, 0, 0, 1], # rock
[1, 1, 1, 0, 0, 0, 0], # granite
[1, 0, 1, 0, 0, 0, 1], # rubble
[0, 0, 0, 1, 2, 2, 0], # music
[0, 0, 0, 1, 1, 2, 0], # song
[0, 0, 0, 0, 1, 1, 0], # band
])
# 对词-文档矩阵的前6个文档(D1-D6)进行SVD分解
# 我们只对文档部分进行分解,Q1留作查询
A_docs = term_doc_matrix[:, :6] # 取前6列
U, S, Vt = np.linalg.svd(A_docs, full_matrices=False)
print("奇异值 S:", np.round(S, 4))
print("\n左奇异向量矩阵 U (6x6):")
print(np.round(U, 4))
print("\n右奇异向量矩阵 Vt (6x6):")
print(np.round(Vt, 4))奇异值 S: [4.1023 3.6486 1.0678 0.6852 0.4573 0.1997]
左奇异向量矩阵 U (6x6):
[[-0.308 0.7838 0.4324 -0.0203 0.1997 -0.252 ]
[-0.1228 0.4353 -0.3063 -0.2643 -0.591 0.5315]
[-0.0789 0.2848 -0.7961 0.3853 0.3255 -0.1566]
[-0.6932 -0.2479 -0.0491 -0.2978 0.467 0.3858]
[-0.5597 -0.1864 0.138 0.6574 -0.4435 -0.0634]
[-0.3002 -0.1371 -0.2532 -0.5102 -0.3 -0.6904]]
右奇异向量矩阵 Vt (6x6):
[[-0.1993 -0.1801 -0.1242 -0.3805 -0.5475 -0.684 ]
[ 0.627 0.549 0.4122 0.0958 -0.2246 -0.2756]
[-0.2226 0.523 -0.6275 0.4882 -0.1998 -0.0705]
[ 0.1172 -0.4451 0.1468 0.4951 -0.6546 0.3049]
[ 0.2928 -0.4191 -0.1439 0.4879 0.4164 -0.5536]
[-0.6466 0.1374 0.6154 0.3528 0.0897 -0.2276]]观察奇异值,前两个值(4.1023, 3.6486)远大于后面的值(1.0678 及其余),这表明数据的主要信息集中在两个维度上。我们可以进行截断,将文档映射到2维语义空间。
# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
term_doc_matrix = np.array([
[2, 2, 1, 1, 0, 0, 1], # rock
[1, 1, 1, 0, 0, 0, 0], # granite
[1, 0, 1, 0, 0, 0, 1], # rubble
[0, 0, 0, 1, 2, 2, 0], # music
[0, 0, 0, 1, 1, 2, 0], # song
[0, 0, 0, 0, 1, 1, 0], # band
])
A_docs = term_doc_matrix[:, :6] # 取前6列
U, S, Vt = np.linalg.svd(A_docs, full_matrices=False)
# 截断到2维 (k=2)
k = 2
U_k = U[:, :k]
S_k = np.diag(S[:k])
Vt_k = Vt[:k, :]
print("截断左奇异向量矩阵 U_k (6x2):")
print(np.round(U_k, 4))
print("\n截断奇异值矩阵 S_k (2x2):")
print(np.round(S_k, 4))
print("\n截断右奇异向量矩阵 Vt_k (2x6):")
print(np.round(Vt_k, 4))
# 文档在2维语义空间中的坐标:即 Vt_k 的每一列(或 Σ_k * Vt_k 的每一列,取决于定义,常用后者)
doc_semantic_coords = S_k @ Vt_k # 2x6 矩阵,每一列是一个文档的2维向量
print("\n文档在2维语义空间中的坐标 (Σ_k * Vt_k, 2x6,每列一个文档):")
print(np.round(doc_semantic_coords, 4))截断左奇异向量矩阵 U_k (6x2):
[[-0.308 0.7838]
[-0.1228 0.4353]
[-0.0789 0.2848]
[-0.6932 -0.2479]
[-0.5597 -0.1864]
[-0.3002 -0.1371]]
截断奇异值矩阵 S_k (2x2):
[[4.1023 0. ]
[0. 3.6486]]
截断右奇异向量矩阵 Vt_k (2x6):
[[-0.1993 -0.1801 -0.1242 -0.3805 -0.5475 -0.684 ]
[ 0.627 0.549 0.4122 0.0958 -0.2246 -0.2756]]
文档在2维语义空间中的坐标 (Σ_k * Vt_k, 2x6,每列一个文档):
[[-0.8176 -0.7388 -0.5096 -1.5608 -2.2462 -2.8059]
[ 2.2878 2.003 1.5039 0.3495 -0.8193 -1.0057]]现在,每个文档(D1-D6)都被表示为一个2维向量,坐标就是上面 Σ_k * Vt_k 的两行六列,每一列是一个文档的二维坐标:
- D1: (-0.8176, 2.2878)
- D2: (-0.7388, 2.0030)
- D3: (-0.5096, 1.5039)
- D4: (-1.5608, 0.3495)
- D5: (-2.2462, -0.8193)
- D6: (-2.8059, -1.0057)
观察发现,两个维度的分工很清楚:第一坐标把两组文档排成一列有序队列(岩石组 D1—D3 依次是 -0.82、-0.74、-0.51,音乐组 D4—D6 依次是 -1.56、-2.25、-2.81),音乐文档在第一坐标上更负、岩石文档更靠右;第二坐标则把两组整体分开——岩石组全为正(1.50 ~ 2.29),音乐组从 +0.35 一路滑到 -1.01。所以单看第一维只能得到”谁更偏左”,必须两个维度一起看才能读出主题分离。
更值得注意的是顺序:D1→D3 沿第一坐标递增(-0.82→-0.51)、第二坐标递减(2.29→1.50),D4→D6 也是同样的单调走向。也就是说 SVD 不只是把两组分开了,还给出了组内的连续性——这正是潜在语义空间能支撑”按语义接近程度排序”的原因。
潜在语义索引(LSI)与语义查询
潜在语义索引 的核心思想正是利用上述 SVD 降维技术。降维后的空间被称为“潜在语义空间”,因为它捕捉了词和文档之间潜在的关联模式,而非表面的共现频率。
对于一个新文档或查询 (其原始词频向量为 ),我们需要把它投影到同一个语义空间中。这里有个必须交代清楚的细节:文档坐标和查询坐标是配套的一套约定,不能各挑一种。本节代码把文档坐标取为 中 一侧带上的缩放,即 的每一列;与之配套的查询坐标就是
因为只有这样两边才处在同一个基里,内积 才正好等于查询与文档在降维后矩阵中的相似度。另一种常见写法是 (文献里的”折入式 fold-in”),它配的是不带 缩放的文档坐标 ,两支不能混用——混用会让相似度排序被 的权重扭曲。
算完 之后,就可以用 与所有文档语义向量之间的余弦相似度来找最相关的文档。
# —— 重新构建上文用到的微型数据(本节各代码块相互独立,可单独运行)
import numpy as np
term_doc_matrix = np.array([
[2, 2, 1, 1, 0, 0, 1], # rock
[1, 1, 1, 0, 0, 0, 0], # granite
[1, 0, 1, 0, 0, 0, 1], # rubble
[0, 0, 0, 1, 2, 2, 0], # music
[0, 0, 0, 1, 1, 2, 0], # song
[0, 0, 0, 0, 1, 1, 0], # band
])
A_docs = term_doc_matrix[:, :6] # 取前6列
U, S, Vt = np.linalg.svd(A_docs, full_matrices=False)
k = 2
U_k = U[:, :k]
S_k = np.diag(S[:k])
Vt_k = Vt[:k, :]
doc_semantic_coords = S_k @ Vt_k # 2x6 矩阵,每一列是一个文档的2维向量
# 查询 Q1 的原始向量: [rock, granite, rubble, music, song, band] = [1, 0, 1, 0, 0, 0]
query_vec_original = np.array([1, 0, 1, 0, 0, 0])
# 将查询投影到同一空间:由于 A_k = U_k Σ_k Vt_k 且文档坐标取 Σ_k*Vt_k 的列,
# 与它配套的查询坐标就是 q^T U_k(两边同处一个基,内积才对得上)。
# 注意不要写成 q^T U_k Σ_k^{-1}:那是"折入式(fold-in)"的另一套约定,和 Σ_k*Vt_k 混用会让余弦相似度失真。
query_vec_semantic = query_vec_original @ U_k
print("查询 Q1 ('rock and rubble') 在2维语义空间中的向量:")
print(np.round(query_vec_semantic, 4))
# 计算 Q1 与每个文档的余弦相似度
from numpy.linalg import norm
def cosine_sim(vec_a, vec_b):
return np.dot(vec_a, vec_b) / (norm(vec_a) * norm(vec_b))
for i in range(doc_semantic_coords.shape[1]):
doc_vec = doc_semantic_coords[:, i]
sim = cosine_sim(query_vec_semantic, doc_vec)
print(f"Q1 与 D{i+1} 的余弦相似度: {sim:.4f}")查询 Q1 ('rock and rubble') 在2维语义空间中的向量:
[-0.3869 1.0687]
Q1 与 D1 的余弦相似度: 1.0000
Q1 与 D2 的余弦相似度: 1.0000
Q1 与 D3 的余弦相似度: 0.9998
Q1 与 D4 的余弦相似度: 0.5376
Q1 与 D5 的余弦相似度: -0.0024
Q1 与 D6 的余弦相似度: 0.0032结果显示,查询 Q1 (“rock and rubble”) 与 D1、D2、D3(关于岩石的文档)的相似度都在 0.9998 以上,而与 D4 掉到 0.5376、与 D5、D6 几乎为 0(-0.0024、0.0032,符号已无意义,只是浮点噪声)。
这里要看清 LSI 到底解决了什么:词表里只有一个 rock,它在 D1(岩石文档)和 D4(音乐文档)里都出现了;如果按关键词匹配,“rock” 一出现就会把 D4 这类音乐文档一并捞上来。而 SVD 利用共现把含 rock 的岩石文档与音乐文档投到了语义空间的不同区域,查询只落在岩石那一侧;rock 本身在词—文档矩阵中仍只有一行静态向量,LSI 不会为两个义项各学一个向量,所以这并不等于完成了词义消歧。D1 与 D2 都显示 1.0000 是四舍五入的结果——它们在语义空间里几乎同向,微小差异被 4 位小数吃掉了,不是”完全相同”。D3 的 0.9998 也是同族。
不过别把这当成”消歧成功”的强结论:本例的矩阵极小、两类文档几乎正交,SVD 只需一个维度就能把它们分开。真实语料上词义消歧远没这么干净,这正是后面要学主题模型与上下文化词向量的原因。
语义距离的度量与应用
一旦文本被表示为语义空间中的向量(无论是通过 LSI 还是后续要学的主题模型),我们就可以用各种距离或相似度度量来衡量它们之间的语义关系。
- 余弦相似度:最常用的方法,衡量两个向量方向的接近程度,对向量的长度不敏感,适合文本向量。
- KL 散度:如果文本被表示为主题分布(一个概率向量,如 LDA 的输出),那么衡量两个分布差异的常用方法是 KL 散度。 KL 散度越小,表示两个分布越接近。
应用场景:
- 文档检索与推荐:如上例,找到与查询语义最相近的文档。
- 文本分类:计算待分类文本与各类别代表文本的语义距离,将其归入距离最近的类别。
- 作者归属分析:将多位作者的作品分别表示为向量集合,对于一篇匿名文本,计算其与每位作者作品平均向量的距离,来判断最可能的作者。
- 案例匹配:在法律领域,将当事人的案情描述与历史判决书库进行语义匹配,找到最相似的先例,辅助法律咨询和结果预测。
# 伪代码示例:基于主题分布和KL散度的法律案例匹配
# 假设我们已有一个训练好的LDA模型 `lda_model` 和一个判决书向量库 `case_vectors`
# 以及一个待查询的案情描述 `new_case_text`
# 1. 将新案情表示为主题分布
# new_case_topic_dist = lda_model.transform(vectorize(new_case_text)) # 得到一个概率分布向量
# 2. 计算与库中每个判决书的KL散度 (注意KL散度不对称,通常用对称版本或固定P为查询)
# distances = []
# for each case_vec in case_vectors:
# kl_div = calculate_kl_divergence(new_case_topic_dist, case_vec) # 需要处理零概率
# distances.append(kl_div)
# 3. 找到KL散度最小的前K个案例
# most_similar_cases = argsort(distances)[:K]
# 在实际系统中,还会结合法条、判决结果等信息进行综合研判。LSI 的局限与演进
LSI 是一种强大的工具,但它也存在明显的局限性:
- 线性假设:SVD 是一种线性变换,它假设数据中的关系是线性的,但自然语言的语义关系往往更为复杂和非线性。
- 可解释性:降维后的语义空间维度(潜在主题)缺乏明确的语义标签,不易直接解释。
- 矩阵稀疏性:对于大规模语料,词-文档矩阵极其稀疏,直接进行 SVD 计算效率低。
为了克服这些限制,后续发展出了更多模型:
- 概率潜在语义分析:为 LSI 提供了概率化的解释。
- 潜在狄利克雷分配(Latent Dirichlet Allocation, LDA):引入了贝叶斯框架和狄利克雷先验,能生成更具可解释性的主题。
- 神经网络词向量与文档向量:如 Doc2Vec、Sentence-BERT 等,利用神经网络学习非线性、更强大的文本表示,逐渐成为主流。
LSI 的价值在于它清晰地展示了将文本映射到连续向量空间并进行语义计算这一核心范式,为后续研究奠定了重要基础。
📝 动手练一练
- 概念辨析:LSI 通过 SVD 降维得到的“语义空间”,与 Word2Vec 得到的“词向量空间”,在表示对象和空间意义上有什么根本区别?
👉 点击查看参考答案
- 表示对象不同:
- LSI 语义空间:主要表示文档(或较长的文本单元)。文档向量是该空间中一个点。
- Word2Vec 词向量空间:主要表示词。每个词是该空间中一个点。
- 空间意义与构建方式不同:
- LSI 空间:通过对词-文档共现矩阵进行全局矩阵分解(SVD)得到。空间维度(潜在主题)反映了文档集合中全局的、潜在的语义结构。词也可以通过 矩阵的行被映射到这个空间,但其主要目的是服务于文档表示。
- Word2Vec 空间:通过神经网络模型(CBOW/Skip-gram)基于局部上下文窗口预测目标词来学习得到。空间维度是连续的、稠密的向量,词与词之间的几何关系(如距离、方向)类比于其语义和语法关系。
- 简单说:LSI 是“文档导向”的全局语义降维,Word2Vec 是“词语导向”的分布式表示学习。当然,后续的 Doc2Vec 等方法试图将文档也嵌入到与词向量兼容的同一空间中。
- 手算实践:假设有一个极简的词-文档矩阵 (两个词,两个文档)。请手动验证其 SVD 分解(),并说明如果降维到 1 维(k=1),两个文档的 1 维语义坐标分别是什么?这反映了什么?
👉 点击查看参考答案
矩阵 。
- 求 和 : , 。
- 特征值与奇异值: 的特征值为 。因此奇异值 。。
- 特征向量与奇异向量:
- 对于 , 的特征向量为 。
- 对于 , 的特征向量为 。 因此 , 。
- 左奇异向量 。 。 。 因此 。
- 验证:。
降维到 k=1: 保留 , , 。 文档的 1 维语义坐标通常取 ,即 。
- 文档1(对应 第一列)坐标:
- 文档2(对应 第二列)坐标:
反映内容:在1维语义空间中,文档1的坐标值(2)远大于文档2(0)。这反映了文档1在第一个词(对应 ,即第一个词的方向)上的权重非常高,而文档2在这个主要维度上几乎没有贡献。降维后,我们主要依据第一个词来区分文档,第二个词的信息被舍弃了。这展示了 SVD 降维会保留数据方差最大的方向(主成分)。
本章小结
本节我们深入探讨了如何利用线性代数工具为文本构建语义向量表示。潜在语义索引(LSI)通过对词-文档矩阵进行奇异值分解(SVD),将高维稀疏的向量映射到低维稠密的语义空间。在这个空间中,文本的语义相似度可以通过向量间的余弦相似度等度量来计算,从而实现了超越关键词匹配的语义检索和分析。
LSI 体现了 NLP 中一个核心思想:将语言符号转化为连续的数学向量,并在向量空间中定义语义关系。尽管它被后续更强大的概率模型和神经网络模型所超越,但其揭示的“降维”和“潜在语义”概念影响深远。
📋 行动清单
- 理解 SVD 分解 中三个矩阵的数学和几何意义,以及奇异值大小与维度重要性的关系。
- 掌握 LSI 的完整流程:构建词-文档矩阵 -> SVD 分解 -> 选择k降维 -> 投影查询 -> 计算语义相似度。
- 尝试在小型文本集(如新闻标题)上,手动或编程实现 LSI,观察降维后语义相近的文档是否在空间中聚集。
—— 小象教研组