首页 > 其他分享 >复现经典:《统计学习方法》​第17章 潜在语义分析

复现经典:《统计学习方法》​第17章 潜在语义分析

时间:2022-12-26 16:35:23浏览次数:50  
标签:00 01 17 语义 矩阵 单词 复现 文本 向量


第17章 潜在语义分析

本文是李航老师的《统计学习方法》一书的代码复现。作者:黄海广

备注:代码都可以在github中下载。我将陆续将代码发布在公众号“机器学习初学者”,可以在这个专辑在线阅读。

1.单词向量空间模型通过单词的向量表示文本的语义内容。以单词-文本矩阵 为输入,其中每一行对应一个单词,每一列对应一个文本,每一个元素表示单词在文本中的频数或权值(如TF-IDF)

单词向量空间模型认为,这个矩阵的每一列向量是单词向量,表示一个文本,两个单词向量的内积或标准化内积表示文本之间的语义相似度。

2.话题向量空间模型通过话题的向量表示文本的语义内容。假设有话题文本矩阵

其中每一行对应一个话题,每一列对应一个文本每一个元素表示话题在文本中的权值。话题向量空间模型认为,这个矩阵的每一列向量是话题向量,表示一个文本,两个话题向量的内积或标准化内积表示文本之间的语义相似度。假设有单词话题矩阵


其中每一行对应一个单词,每一列对应一个话题,每一个元素表示单词在话题中的权值。

给定一个单词文本矩阵

潜在语义分析的目标是,找到合适的单词-话题矩阵 与话题文本矩阵 ,将单词文本矩阵 近似的表示为 与 的乘积形式。

等价地,潜在语义分析将文本在单词向量空间的表示X通过线性变换 转换为话题向量空间中的表示 。

潜在语义分析的关键是对单词-文本矩阵进行以上的矩阵因子分解(话题分析)

3.潜在语义分析的算法是奇异值分解。通过对单词文本矩阵进行截断奇异值分解,得到

矩阵 表示话题空间,矩阵 是文本在话题空间的表示。

4.非负矩阵分解也可以用于话题分析。非负矩阵分解将非负的单词文本矩阵近似分解成两个非负矩阵 和 的乘积,得到

矩阵 表示话题空间,矩阵 是文本在话题空间的表示。

非负矩阵分解可以表为以下的最优化问题:

非负矩阵分解的算法是迭代算法。乘法更新规则的迭代算法,交替地对 和 进行更新。本质是梯度下降法,通过定义特殊的步长和非负的初始值,保证迭代过程及结果的矩阵 和 均为非负。


LSA 是一种无监督学习方法,主要用于文本的话题分析,其特点是通过矩阵分解发现文本与单词之间的基于话题的语义关系。也称为潜在语义索引(Latent semantic indexing, LSI)。

LSA 使用的是非概率的话题分析模型。将文本集合表示为单词-文本矩阵,对单词-文本矩阵进行奇异值分解,从而得到话题向量空间,以及文本在话题向量空间的表示。

非负矩阵分解(non-negative matrix factorization, NMF)是另一种矩阵的因子分解方法,其特点是分解的矩阵非负。

单词向量空间

word vector space model

给定一个文本,用一个向量表示该文本的”语义“, 向量的每一维对应一个单词,其数值为该单词在该文本中出现的频数或权值;基本假设是文本中所有单词的出现情况表示了文本的语义内容,文本集合中的每个文本都表示为一个向量,存在于一个向量空间;向量空间的度量,如内积或标准化内积表示文本之间的相似度

给定一个含有 个文本的集合 ,以及在所有文本中出现的 个单词的集合 . 将单词在文本的出现的数据用一个单词-文本矩阵(word-document matrix)表示,记作 :

这是一个 矩阵,元素 表示单词 在文本 中出现的频数或权值。由于单词的种类很多,而每个文本中出现单词的种类通常较少,所有单词-文本矩阵是一个稀疏矩阵。

权值通常用单词频率-逆文本率(term frequency-inverse document frequency, TF-IDF)表示:

,

其中, 为单词 在文本 中出现的概率, 是逆文本率,用来衡量单词 对表示语义所起的重要性,

.

单词向量空间模型的优点是模型简单,计算效率高。因为单词向量通常是稀疏的,单词向量空间模型也有一定的局限性,体现在内积相似度未必能够准确表达两个文本的语义相似度上。因为自然语言的单词具有一词多义性(polysemy)及多词一义性(synonymy)。

话题向量空间

1. 话题向量空间

给定一个含有 个文本的集合 ,以及在所有文本中出现的 个单词的集合 . 可以获得其单词-文本矩阵 :

假设所有文本共含有 个话题。假设每个话题由一个定义在单词集合 上的 维向量表示,称为话题向量,即:

其中 单词 在话题 的权值, , 权值越大,该单词在该话题中的重要程度就越高。这 个话题向量 张成一个话题向量空间(topic vector space), 维数为 .话题向量空间是单词向量空间的一个子空间

话题向量空间 :

矩阵 ,称为单词-话题矩阵

2. 文本在话题向量空间中的表示  :

考虑文本集合 的文本 , 在单词向量空间中由一个向量 表示,将 投影到话题向量空间 中,得到话题向量空间的一个向量 , 是一个 维向量:

其中, 是文本 在话题 中的权值, , 权值越大,该话题在该文本中的重要程度就越高。

矩阵

也可写成:

3. 从单词向量空间到话题向量空间的线性变换

如此,单词向量空间的文本向量 可以通过他在话题空间中的向量 近似表示,具体地由 个话题向量以 为系数的线性组合近似表示:

所以,单词-文本矩阵 可以近似的表示为单词-话题矩阵 与话题-文本矩阵 的乘积形式。

直观上,潜在语义分析是将单词向量空间的表示通过线性变换转换为在话题向量空间中的表示。这个线性变换由矩阵因子分解式的形式体现。

潜在语义分析算法

潜在语义分析利用矩阵奇异值分解,具体地,对单词-文本矩阵进行奇异值分解,将其左矩阵作为话题向量空间,将其对角矩阵与右矩阵的乘积作为文本在话题向量空间的表示。

给定一个含有 个文本的集合 ,以及在所有文本中出现的 个单词的集合 . 可以获得其单词-文本矩阵 :

截断奇异值分解

潜在语义分析根据确定的话题数 对单词-文本矩阵 进行截断奇异值分解:

矩阵 的每一个列向量 表示一个话题,称为话题向量。由这

称为话题向量空间

综上, 可以通过对单词-文本矩阵的奇异值分解进行潜在语义分析:

得到话题空间 , 以及文本在话题空间的表示( ).

非负矩阵分解算法

非负矩阵分解也可以用于话题分析。对单词-文本矩阵进行非负矩阵分解,将其左矩阵作为话题向量空间,将其右矩阵作为文本在话题向量空间的表示

非负矩阵分解

若一个矩阵的索引元素非负,则该矩阵为非负矩阵。若 是非负矩阵,则: .

给定一个非负矩阵 , 找到两个非负矩阵 和 , 使得:

即非负矩阵 分解为两个非负矩阵 和 的乘积形式,成为非负矩阵分解。因为 与 完全相等很难实现,所以只要求近似相等。

假设非负矩阵 是 矩阵,非负矩阵 和 分别为 矩阵和 矩阵。假设 即 和 小于原矩阵 , 所以非负矩阵分解是对原数据的压缩。

称 为基矩阵,

为话题向量空间,

表示文本集合的 个话题, 令 为文本在话题向量空间的表示,

表示文本集合的 个文本。

算法

非负矩阵分解可以形式化为最优化问题求解。可以利用平方损失或散度来作为损失函数。

目标函数 关于 和 的最小化,满足约束条件 , 即:

乘法更新规则:



选择初始矩阵 和 为非负矩阵,可以保证迭代过程及结果的矩阵 和

算法 17.1 (非负矩阵分解的迭代算法)

输入:单词-文本矩阵 , 文本集合的话题个数 , 最大迭代次数 ;
输出:话题矩阵 , 文本表示矩阵 。

1). 初始化

, 并对 的每一列数据归一化;

2). 迭代

对迭代次数由1到 执行下列步骤:
a. 更新 的元素,对 从1到 从1到 按(17.33)更新 ;
a. 更新 的元素,对 从1到 从1到 按(17.34)更新 ;

图例 17.1

import numpy as np
from sklearn.decomposition import TruncatedSVD
X = [[2, 0, 0, 0], [0, 2, 0, 0], [0, 0, 1, 0], [0, 0, 2, 3], [0, 0, 0, 1], [1, 2, 2, 1]]
X = np.asarray(X);X
array([[2, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 1, 0],
[0, 0, 2, 3],
[0, 0, 0, 1],
[1, 2, 2, 1]])
# 奇异值分解
U,sigma,VT=np.linalg.svd(X)
U
array([[-7.84368672e-02, -2.84423033e-01,  8.94427191e-01,
-2.15138396e-01, -6.45002451e-02, -2.50012770e-01],
[-1.56873734e-01, -5.68846066e-01, -4.47213595e-01,
-4.30276793e-01, -1.29000490e-01, -5.00025540e-01],
[-1.42622354e-01, 1.37930417e-02, -1.25029761e-16,
6.53519444e-01, 3.88575115e-01, -6.33553733e-01],
[-7.28804669e-01, 5.53499910e-01, -2.24565656e-16,
-1.56161345e-01, -3.23288048e-01, -1.83248673e-01],
[-1.47853320e-01, 1.75304609e-01, 8.49795536e-18,
-4.87733411e-01, 8.40863653e-01, 4.97204799e-02],
[-6.29190197e-01, -5.08166890e-01, -1.60733896e-16,
2.81459486e-01, 1.29000490e-01, 5.00025540e-01]])
sigma
array([4.47696617, 2.7519661 , 2.        , 1.17620428])
VT
array([[-1.75579600e-01, -3.51159201e-01, -6.38515454e-01,
-6.61934313e-01],
[-3.91361272e-01, -7.82722545e-01, 3.79579831e-02,
4.82432341e-01],
[ 8.94427191e-01, -4.47213595e-01, -2.23998094e-16,
5.45344065e-17],
[-1.26523351e-01, -2.53046702e-01, 7.68672366e-01,
-5.73674125e-01]])
# 截断奇异值分解

svd = TruncatedSVD(n_components=3, n_iter=7, random_state=42)
svd.fit(X)
TruncatedSVD(algorithm='randomized', n_components=3, n_iter=7, random_state=42,
tol=0.0)
print(svd.explained_variance_ratio_)
[0.39945801 0.34585056 0.18861789]
print(svd.explained_variance_ratio_.sum())
0.933926460028446
print(svd.singular_values_)
[4.47696617 2.7519661  2.        ]

非负矩阵分解

def inverse_transform(W, H):
# 重构
return W.dot(H)

def loss(X, X_):
#计算重构误差
return ((X - X_) * (X - X_)).sum()
# 算法 17.1

class MyNMF:
def fit(self, X, k, t):
m, n = X.shape

W = np.random.rand(m, k)
W = W/W.sum(axis=0)

H = np.random.rand(k, n)

i = 1
while i < t:

W = W * X.dot(H.T) / W.dot(H).dot(H.T)

H = H * (W.T).dot(X) / (W.T).dot(W).dot(H)

i += 1

return W, H
model = MyNMF()
W, H = model.fit(X, 3, 200)
W
array([[0.00000000e+00, 4.27327705e-01, 6.30117924e-27],
[5.11680721e-97, 8.57828102e-01, 0.00000000e+00],
[2.97520805e-88, 2.39454414e-18, 4.36332453e-01],
[2.15653741e+00, 3.38756557e-21, 8.38350315e-01],
[7.36106818e-01, 1.00339294e-54, 8.72892573e-38],
[6.78344810e-01, 1.07009504e+00, 8.57259947e-01]])
H
array([[7.14647214e-10, 1.01233436e-03, 3.76097657e-02, 1.35755597e+00],
[9.30509415e-01, 1.86788842e+00, 1.16682319e-02, 4.54479182e-03],
[4.95440453e-03, 6.18432747e-04, 2.28890170e+00, 8.61836630e-02]])
# 重构
X_ = inverse_transform(W, H);X_
array([[3.97632453e-01, 7.98200474e-01, 4.98615876e-03, 1.94211546e-03],
[7.98217125e-01, 1.60232718e+00, 1.00093372e-02, 3.89865014e-03],
[2.16176748e-03, 2.69842277e-04, 9.98722093e-01, 3.76047290e-02],
[4.15352814e-03, 2.70160021e-03, 2.00000833e+00, 2.99987233e+00],
[5.26056687e-10, 7.45186228e-04, 2.76848049e-02, 9.99306203e-01],
[9.99980721e-01, 2.00003500e+00, 2.00018226e+00, 9.99636205e-01]])
# 重构误差

loss(X, X_)
4.002356735601103

使用 sklearn 计算

from sklearn.decomposition import NMF
model = NMF(n_components=3, init='random', max_iter=200, random_state=0)
W = model.fit_transform(X)
H = model.components_
W
array([[0.        , 0.53849498, 0.        ],
[0. , 1.07698996, 0. ],
[0.69891361, 0. , 0. ],
[1.39782972, 0. , 1.97173859],
[0. , 0. , 0.65783848],
[1.39783002, 1.34623756, 0.65573258]])
H
array([[0.00000000e+00, 0.00000000e+00, 1.43078959e+00, 1.71761682e-03],
[7.42810976e-01, 1.48562195e+00, 0.00000000e+00, 3.30264644e-04],
[0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.52030365e+00]])
X__ = inverse_transform(W, H);X__
array([[3.99999983e-01, 7.99999966e-01, 0.00000000e+00, 1.77845853e-04],
[7.99999966e-01, 1.59999993e+00, 0.00000000e+00, 3.55691707e-04],
[0.00000000e+00, 0.00000000e+00, 9.99998311e-01, 1.20046577e-03],
[0.00000000e+00, 0.00000000e+00, 2.00000021e+00, 3.00004230e+00],
[0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00011424e+00],
[1.00000003e+00, 2.00000007e+00, 2.00000064e+00, 9.99758185e-01]])
loss(X, X__)
4.000001672582457

本章代码来源:https://github.com/hktxt/Learn-Statistical-Learning-Method

下载地址

​https://github.com/fengdu78/lihang-code​

参考资料:

[1] 《统计学习方法》: https://baike.baidu.com/item/统计学习方法/10430179

[2] 黄海广: https://github.com/fengdu78

[3]  github: https://github.com/fengdu78/lihang-code

[4]  wzyonggege: https://github.com/wzyonggege/statistical-learning-method

[5]  WenDesi: https://github.com/WenDesi/lihang_book_algorithm

[7]  hktxt: https://github.com/hktxt/Learn-Statistical-Learning-Method

复现经典:《统计学习方法》​第17章 潜在语义分析_矩阵分解

标签:00,01,17,语义,矩阵,单词,复现,文本,向量
From: https://blog.51cto.com/u_15671528/5969317

相关文章

  • leetcode-17. 电话号码的字母组合
    17.电话号码的字母组合给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。答案可以按任意顺序返回。给出数字到字母的映射如下(与电话按键相同)。注意1不对应......
  • 阿里达摩院联合上海科大、浙大和新科大将知识引入命名实体识别,摘得10个榜首,荣获SemEva
    背景SemEval(SemanticEvaluation)是由国际计算语言学协会(AssociationforComputationalLinguistics,ACL)下属的SIGLEX主办的在自然语言处理(NLP)领域全球范围内影响力最......
  • 【2022-12-17】连岳摘抄
    23:59明智的做法是把你的愤怒指向问题,而不是指向人,把你的精力集中在寻求解决方案上,而不是集中在寻求借口上。                  ......
  • AHOI,但是初中组,2017-2018
    你觉得我这种彩笔像是能去做省选题的样子吗?=w=合肥人,做初中的屑安徽题,很正常吧。AH也不知道为啥搞啥市赛啊区赛啊省赛啊就挺离谱的反正摆烂人也不想打┓( ´∀` )┏20......
  • Java编程思想17
    第二十一章:并发基本的线程机制  并发编程使我们可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立任务(也被称为子任务)中的每一个都将由执行线程......
  • 代码随想录算法训练营Day25|216. 组合总和 III、17. 电话号码的字母组合
    代码随想录算法训练营Day25|216.组合总和III、17.电话号码的字母组合216.组合总和III216.组合总和III与「77.组合」类似,但区别在于题干要求的变化:只使用数字1......
  • [LeetCode] 1759. Count Number of Homogenous Substrings
    Givenastring s,return thenumberof homogenous substringsof s. Sincetheanswermaybetoolarge,returnit modulo 109 +7.Astringis homogenou......
  • Visual Studio 2017(vs2017)绿色便携版-北桃特供
    原版的VisualStudio2017有几十G,安装起来特别慢,不少用户叫苦连天。该版本是精简过的vs2017,且简化了原来的安装程序,特别适用于教学、个人开发者、某些要求不高的企业。该绿......
  • 力扣---217. 存在重复元素
    给你一个整数数组nums。如果任一值在数组中出现至少两次,返回true;如果数组中每个元素互不相同,返回false。示例1:输入:nums=[1,2,3,1]输出:true示例2:输入:nums=[1......
  • P1772 [ZJOI2006] 物流运输
    P1772[ZJOI2006]物流运输题意:需要将物品从\(A\)移动到\(B\),需要\(n\)天才能运输完成,运输过程中需要转停很多个码头。求指定一个\(n\)天的运输计划,使得总成本......