本文是李航老师的《统计学习方法》一书的代码复现。作者:黄海广 备注:代码都可以在github中下载。我将陆续将代码发布在公众号“机器学习初学者”,可以在这个专辑在线阅读。
1.单词向量空间模型通过单词的向量表示文本的语义内容。以单词-文本矩阵
为输入,其中每一行对应一个单词,每一列对应一个文本,每一个元素表示单词在文本中的频数或权值(如TF-IDF)
单词向量空间模型认为,这个矩阵的每一列向量是单词向量,表示一个文本,两个单词向量的内积或标准化内积表示文本之间的语义相似度。
2.话题向量空间模型通过话题的向量表示文本的语义内容。假设有话题文本矩阵
其中每一行对应一个话题,每一列对应一个文本每一个元素表示话题在文本中的权值。话题向量空间模型认为,这个矩阵的每一列向量是话题向量,表示一个文本,两个话题向量的内积或标准化内积表示文本之间的语义相似度。假设有单词话题矩阵
其中每一行对应一个单词,每一列对应一个话题,每一个元素表示单词在话题中的权值。
给定一个单词文本矩阵
LSA 是一种无监督学习方法,主要用于文本的话题分析,其特点是通过矩阵分解发现文本与单词之间的基于话题的语义关系。也称为潜在语义索引(Latent semantic indexing, LSI)。
LSA 使用的是非概率的话题分析模型。将文本集合表示为单词-文本矩阵,对单词-文本矩阵进行奇异值分解,从而得到话题向量空间,以及文本在话题向量空间的表示。
非负矩阵分解(non-negative matrix factorization, NMF)是另一种矩阵的因子分解方法,其特点是分解的矩阵非负。
word vector space model
给定一个文本,用一个向量表示该文本的”语义“, 向量的每一维对应一个单词,其数值为该单词在该文本中出现的频数或权值;基本假设是文本中所有单词的出现情况表示了文本的语义内容,文本集合中的每个文本都表示为一个向量,存在于一个向量空间;向量空间的度量,如内积或标准化内积表示文本之间的相似度。
1. 话题向量空间:
假设所有文本共含有k个话题。假设每个话题由一个定义在单词集合w上的m维向量表示,称为话题向量,即:
3. 从单词向量空间到话题向量空间的线性变换:
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
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
[6] 火烫火烫的: https://blog.csdn.net/tudaodiaozhale
[7] hktxt: https://github.com/hktxt/Learn-Statistical-Learning-Method