Skip to content

Latest commit

 

History

History
496 lines (364 loc) · 15.6 KB

singular-value-decomposition-for-machine-learning.md

File metadata and controls

496 lines (364 loc) · 15.6 KB

浅谈机器学习的奇异值分解

原文: https://machinelearningmastery.com/singular-value-decomposition-for-machine-learning/

矩阵分解,也称为矩阵分解,涉及使用其组成元素描述给定矩阵。

也许最着名和最广泛使用的矩阵分解方法是奇异值分解或 SVD。所有矩阵都有一个 SVD,这使得它比其他方法更稳定,例如特征分解。因此,它经常用于各种应用,包括压缩,去噪和数据缩减。

在本教程中,您将发现用于将矩阵分解为其组成元素的奇异值分解方法。

完成本教程后,您将了解:

  • 奇异值分解是什么以及涉及什么。
  • 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
  • 如何使用 SVD 计算伪逆并执行降维

让我们开始吧。

  • 更新 Mar / 2018 :修复了重建中的拼写错误。为清晰起见,将代码中的 V 更改为 VT。修正了伪逆方程中的拼写错误。

A Gentle Introduction to Singular-Value Decomposition

奇异值分解 照片由 Chris Heald 拍摄,保留一些权利。

教程概述

本教程分为 5 个部分;他们是:

  1. 奇异值分解
  2. 计算奇异值分解
  3. 从 SVD 重构矩阵
  4. 伪逆的 SVD
  5. 用于降维的 SVD

奇异值分解

奇异值分解(简称 SVD)是一种矩阵分解方法,用于将矩阵减少到其组成部分,以使某些后续矩阵计算更简单。

为简单起见,我们将重点关注实值矩阵的 SVD,并忽略复数的情况。

A = U . Sigma . V^T

其中 A 是我们希望分解的真实 mxn 矩阵,U 是 mxm 矩阵,Sigma(通常由大写希腊字母 Sigma 表示)是 mxn 对角矩阵,V ^ T 是 nxn 矩阵的转置,其中 T 是一个上标。

奇异值分解是线性代数的一个亮点。

Sigma 矩阵中的对角线值称为原始矩阵 A 的奇异值.U 矩阵的列称为 A 的左奇异向量,V 列称为 A 的右奇异向量。

通过迭代数值方法计算 SVD。我们不会详细介绍这些方法。每个矩形矩阵都具有奇异值分解,尽管得到的矩阵可能包含复数,浮点运算的局限性可能会导致某些矩阵无法整齐地分解。

奇异值分解(SVD)提供了另一种将矩阵分解为奇异向量和奇异值的方法。 SVD 允许我们发现一些与特征分解相同的信息。但是,SVD 更普遍适用。

SVD 广泛用于计算其他矩阵运算,例如矩阵逆运算,但也作为机器学习中的数据简化方法。 SVD 还可用于最小二乘线性回归,图像压缩和去噪数据。

奇异值分解(SVD)在统计学,机器学习和计算机科学中有许多应用。将 SVD 应用于矩阵就像在 X 射线视觉中查看它...

计算奇异值分解

可以通过调用 svd()函数来计算 SVD。

该函数采用矩阵并返回 U,Sigma 和 V ^ T 元素。 Sigma 对角矩阵作为奇异值的向量返回。 V 矩阵以转置的形式返回,例如, V.T.

下面的示例定义了 3×2 矩阵并计算奇异值分解。

# Singular-value decomposition
from numpy import array
from scipy.linalg import svd
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# SVD
U, s, VT = svd(A)
print(U)
print(s)
print(VT)

首先运行该示例打印定义的 3×2 矩阵,然后打印 3×3U 矩阵,2 元素 Sigma 向量和从分解计算的 2×2V ^ T 矩阵元素。

[[1 2]
 [3 4]
 [5 6]]

[[-0.2298477   0.88346102  0.40824829]
 [-0.52474482  0.24078249 -0.81649658]
 [-0.81964194 -0.40189603  0.40824829]]

[ 9.52551809  0.51430058]

[[-0.61962948 -0.78489445]
 [-0.78489445  0.61962948]]

从 SVD 重构矩阵

可以从 U,Sigma 和 V ^ T 元素重建原始矩阵。

从 svd()返回的 U,s 和 V 元素不能直接相乘。

必须使用 diag()函数将 s 向量转换为对角矩阵。默认情况下,此函数将创建一个相对于原始矩阵 m x m 的方阵。这导致问题,因为矩阵的大小不符合矩阵乘法的规则,其中矩阵中的列数必须与后续矩阵中的行数匹配。

在创建方形 Sigma 对角矩阵之后,矩阵的大小相对于我们正在分解的原始 m x n 矩阵,如下所示:

U (m x m) . Sigma (m x m) . V^T (n x n)

事实上,我们要求:

U (m x m) . Sigma (m x n) . V^T (n x n)

我们可以通过创建所有零值 m x n(例如更多行)的新 Sigma 格式来实现这一点,并用通过 diag()计算的方形对角矩阵填充矩阵的前 n x n 部分。

# Reconstruct SVD
from numpy import array
from numpy import diag
from numpy import dot
from numpy import zeros
from scipy.linalg import svd
# define a matrix
A = array([[1, 2], [3, 4], [5, 6]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create m x n Sigma matrix
Sigma = zeros((A.shape[0], A.shape[1]))
# populate Sigma with n x n diagonal matrix
Sigma[:A.shape[1], :A.shape[1]] = diag(s)
# reconstruct matrix
B = U.dot(Sigma.dot(VT))
print(B)

首先运行该示例打印原始矩阵,然后打印从 SVD 元素重建的矩阵。

[[1 2]
 [3 4]
 [5 6]]

[[ 1\.  2.]
 [ 3\.  4.]
 [ 5\.  6.]]

上述与 Sigma 对角线的复杂性仅存在于 m 和 n 不相等的情况下。当重建方形矩阵时,可以直接使用对角矩阵,如下所述。

# Reconstruct SVD
from numpy import array
from numpy import diag
from numpy import dot
from scipy.linalg import svd
# define a matrix
A = array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create n x n Sigma matrix
Sigma = diag(s)
# reconstruct matrix
B = U.dot(Sigma.dot(VT))
print(B)

运行该示例打印原始 3×3 矩阵和直接从 SVD 元素重建的版本。

[[1 2 3]
 [4 5 6]
 [7 8 9]]

[[ 1\.  2\.  3.]
 [ 4\.  5\.  6.]
 [ 7\.  8\.  9.]]

伪逆的 SVD

伪逆是矩形矩阵到矩形矩阵的矩阵逆的推广,其中行和列的数量不相等。

在该方法的两个独立发现者或广义逆之后,它也被称为 Moore-Penrose 逆。

没有为非正方形的矩阵定义矩阵求逆。 [...]当 A 的列数多于行数时,使用 pseudoinverse 求解线性方程式提供了许多可能的解决方案之一。

伪逆表示为 A ^ +,其中 A 是被反转的矩阵,+是上标。

使用 A 的奇异值分解计算伪逆:

A^+ = V . D^+ . U^T

或者,没有点符号:

A^+ = VD^+U^T

其中 A ^ +是伪逆,D ^ +是对角矩阵 Sigma 的伪逆,U ^ T 是 U 的转置。

我们可以通过 SVD 操作获得 U 和 V.

A = U . Sigma . V^T

可以通过从 Sigma 创建对角矩阵来计算 D ^ +,计算 Sigma 中每个非零元素的倒数,并且如果原始矩阵是矩形则采用转置。

         s11,   0,   0
Sigma = (  0, s22,   0)
           0,   0, s33
       1/s11,     0,     0
D^+ = (    0, 1/s22,     0)
           0,     0, 1/s33

伪逆提供了一种求解线性回归方程的方法,特别是当行数多于列时,通常就是这种情况。

NumPy 提供函数 pinv()来计算矩形矩阵的伪逆。

下面的示例定义了一个 4×2 矩阵并计算伪逆。

# Pseudoinverse
from numpy import array
from numpy.linalg import pinv
# define matrix
A = array([
	[0.1, 0.2],
	[0.3, 0.4],
	[0.5, 0.6],
	[0.7, 0.8]])
print(A)
# calculate pseudoinverse
B = pinv(A)
print(B)

首先运行示例打印定义的矩阵,然后打印计算的伪逆。

[[ 0.1  0.2]
 [ 0.3  0.4]
 [ 0.5  0.6]
 [ 0.7  0.8]]

[[ -1.00000000e+01  -5.00000000e+00   9.04289323e-15   5.00000000e+00]
 [  8.50000000e+00   4.50000000e+00   5.00000000e-01  -3.50000000e+00]]

我们可以通过 SVD 手动计算伪逆,并将结果与​​pinv()函数进行比较。

首先,我们必须计算 SVD。接下来,我们必须计算 s 数组中每个值的倒数。然后可以将 s 数组转换为具有添加的零行的对角矩阵,以使其成为矩形。最后,我们可以从元素中计算出伪逆。

具体实现是:

A^+ = V . D^+ . U^V

下面列出了完整的示例。

# Pseudoinverse via SVD
from numpy import array
from numpy.linalg import svd
from numpy import zeros
from numpy import diag
# define matrix
A = array([
	[0.1, 0.2],
	[0.3, 0.4],
	[0.5, 0.6],
	[0.7, 0.8]])
print(A)
# calculate svd
U, s, VT = svd(A)
# reciprocals of s
d = 1.0 / s
# create m x n D matrix
D = zeros(A.shape)
# populate D with n x n diagonal matrix
D[:A.shape[1], :A.shape[1]] = diag(d)
# calculate pseudoinverse
B = VT.T.dot(D.T).dot(U.T)
print(B)

首先运行示例打印定义的矩形矩阵和与 pinv()函数匹配上述结果的伪逆。

[[ 0.1  0.2]
 [ 0.3  0.4]
 [ 0.5  0.6]
 [ 0.7  0.8]]

[[ -1.00000000e+01  -5.00000000e+00   9.04831765e-15   5.00000000e+00]
 [  8.50000000e+00   4.50000000e+00   5.00000000e-01  -3.50000000e+00]]

用于降维的 SVD

SVD 的一种流行应用是降低尺寸。

具有大量特征的数据(例如,比观察(行)更多的特征(列))可以减少到与预测问题最相关的较小特征子集。

结果是具有较低等级的矩阵,据说接近原始矩阵。

为此,我们可以对原始数据执行 SVD 操作,并在 Sigma 中选择前 k 个最大奇异值。这些列可以从 Sigma 和从 V ^ T 中选择的行中选择。

然后可以重建原始向量 A 的近似 B.

B = U . Sigmak . V^Tk

在自然语言处理中,该方法可以用于文档中的单词出现或单词频率的矩阵,并且被称为潜在语义分析或潜在语义索引。

在实践中,我们可以保留并使用名为 T 的数据的描述子集。这是矩阵或投影的密集摘要。

T = U . Sigmak

此外,可以计算该变换并将其应用于原始矩阵 A 以及其他类似的矩阵。

T = V^Tk . A

下面的示例演示了使用 SVD 减少数据。

首先定义 3×10 矩阵,列数多于行数。计算 SVD 并仅选择前两个特征。重新组合元素以给出原始矩阵的准确再现。最后,变换以两种不同的方式计算。

from numpy import array
from numpy import diag
from numpy import zeros
from scipy.linalg import svd
# define a matrix
A = array([
	[1,2,3,4,5,6,7,8,9,10],
	[11,12,13,14,15,16,17,18,19,20],
	[21,22,23,24,25,26,27,28,29,30]])
print(A)
# Singular-value decomposition
U, s, VT = svd(A)
# create m x n Sigma matrix
Sigma = zeros((A.shape[0], A.shape[1]))
# populate Sigma with n x n diagonal matrix
Sigma[:A.shape[0], :A.shape[0]] = diag(s)
# select
n_elements = 2
Sigma = Sigma[:, :n_elements]
VT = VT[:n_elements, :]
# reconstruct
B = U.dot(Sigma.dot(VT))
print(B)
# transform
T = U.dot(Sigma)
print(T)
T = A.dot(VT.T)
print(T)

首先运行该示例打印定义的矩阵然后重建近似,然后是原始矩阵的两个等效变换。

[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]]

[[  1\.   2\.   3\.   4\.   5\.   6\.   7\.   8\.   9\.  10.]
 [ 11\.  12\.  13\.  14\.  15\.  16\.  17\.  18\.  19\.  20.]
 [ 21\.  22\.  23\.  24\.  25\.  26\.  27\.  28\.  29\.  30.]]

[[-18.52157747   6.47697214]
 [-49.81310011   1.91182038]
 [-81.10462276  -2.65333138]]

[[-18.52157747   6.47697214]
 [-49.81310011   1.91182038]
 [-81.10462276  -2.65333138]]

scikit-learn 提供了一个直接实现此功能的 TruncatedSVD 类。

可以创建 TruncatedSVD 类,您必须在其中指定要选择的所需要素或组件的数量,例如, 2.一旦创建,您可以通过调用 fit()函数来拟合变换(例如,计算 V ^ Tk),然后通过调用 transform()函数将其应用于原始矩阵。结果是上面称为 T 的 A 的变换。

下面的示例演示了 TruncatedSVD 类。

from numpy import array
from sklearn.decomposition import TruncatedSVD
# define array
A = array([
	[1,2,3,4,5,6,7,8,9,10],
	[11,12,13,14,15,16,17,18,19,20],
	[21,22,23,24,25,26,27,28,29,30]])
print(A)
# svd
svd = TruncatedSVD(n_components=2)
svd.fit(A)
result = svd.transform(A)
print(result)

首先运行示例打印定义的矩阵,然后打印矩阵的转换版本。

我们可以看到值与上面手动计算的值匹配,除了某些值上的符号。考虑到所涉及的计算的性质以及所使用的底层库和方法的差异,我们可以预期在符号方面存在一些不稳定性。只要对变换进行了重复训练,这种符号的不稳定性在实践中就不应成为问题。

[[ 1  2  3  4  5  6  7  8  9 10]
 [11 12 13 14 15 16 17 18 19 20]
 [21 22 23 24 25 26 27 28 29 30]]

[[ 18.52157747   6.47697214]
 [ 49.81310011   1.91182038]
 [ 81.10462276  -2.65333138]]

扩展

本节列出了一些扩展您可能希望探索的教程的想法。

  • 在您自己的数据上试验 SVD 方法。
  • 研究并列出了 SVD 在机器学习中的 10 个应用。
  • 将 SVD 作为数据缩减技术应用于表格数据集。

如果你探索任何这些扩展,我很想知道。

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

图书

API

用品

摘要

在本教程中,您发现了奇异值分解方法,用于将矩阵分解为其组成元素。

具体来说,你学到了:

  • 奇异值分解是什么以及涉及什么。
  • 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
  • 如何使用 SVD 计算伪逆并执行降维。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。