原文: https://machinelearningmastery.com/singular-value-decomposition-for-machine-learning/
矩阵分解,也称为矩阵分解,涉及使用其组成元素描述给定矩阵。
也许最着名和最广泛使用的矩阵分解方法是奇异值分解或 SVD。所有矩阵都有一个 SVD,这使得它比其他方法更稳定,例如特征分解。因此,它经常用于各种应用,包括压缩,去噪和数据缩减。
在本教程中,您将发现用于将矩阵分解为其组成元素的奇异值分解方法。
完成本教程后,您将了解:
- 奇异值分解是什么以及涉及什么。
- 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
- 如何使用 SVD 计算伪逆并执行降维
让我们开始吧。
- 更新 Mar / 2018 :修复了重建中的拼写错误。为清晰起见,将代码中的 V 更改为 VT。修正了伪逆方程中的拼写错误。
奇异值分解 照片由 Chris Heald 拍摄,保留一些权利。
本教程分为 5 个部分;他们是:
- 奇异值分解
- 计算奇异值分解
- 从 SVD 重构矩阵
- 伪逆的 SVD
- 用于降维的 SVD
奇异值分解(简称 SVD)是一种矩阵分解方法,用于将矩阵减少到其组成部分,以使某些后续矩阵计算更简单。
为简单起见,我们将重点关注实值矩阵的 SVD,并忽略复数的情况。
A = U . Sigma . V^T
其中 A 是我们希望分解的真实 mxn 矩阵,U 是 mxm 矩阵,Sigma(通常由大写希腊字母 Sigma 表示)是 mxn 对角矩阵,V ^ T 是 nxn 矩阵的转置,其中 T 是一个上标。
奇异值分解是线性代数的一个亮点。
- 第 371 页,线性代数导论,第五版,2016 年。
Sigma 矩阵中的对角线值称为原始矩阵 A 的奇异值.U 矩阵的列称为 A 的左奇异向量,V 列称为 A 的右奇异向量。
通过迭代数值方法计算 SVD。我们不会详细介绍这些方法。每个矩形矩阵都具有奇异值分解,尽管得到的矩阵可能包含复数,浮点运算的局限性可能会导致某些矩阵无法整齐地分解。
奇异值分解(SVD)提供了另一种将矩阵分解为奇异向量和奇异值的方法。 SVD 允许我们发现一些与特征分解相同的信息。但是,SVD 更普遍适用。
- 第 44-45 页,深度学习,2016 年。
SVD 广泛用于计算其他矩阵运算,例如矩阵逆运算,但也作为机器学习中的数据简化方法。 SVD 还可用于最小二乘线性回归,图像压缩和去噪数据。
奇异值分解(SVD)在统计学,机器学习和计算机科学中有许多应用。将 SVD 应用于矩阵就像在 X 射线视觉中查看它...
- 第 297 页,无线性代数废话指南,2017 年
可以通过调用 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]]
可以从 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.]]
伪逆是矩形矩阵到矩形矩阵的矩阵逆的推广,其中行和列的数量不相等。
在该方法的两个独立发现者或广义逆之后,它也被称为 Moore-Penrose 逆。
没有为非正方形的矩阵定义矩阵求逆。 [...]当 A 的列数多于行数时,使用 pseudoinverse 求解线性方程式提供了许多可能的解决方案之一。
- 第 46 页,深度学习,2016 年。
伪逆表示为 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 操作,并在 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 作为数据缩减技术应用于表格数据集。
如果你探索任何这些扩展,我很想知道。
如果您希望深入了解,本节将提供有关该主题的更多资源。
- 第 12 章,奇异值和 Jordan 分解,线性代数和矩阵分析统计,2014。
- 第 4 章,奇异值分解和第 5 章,关于 SVD 的更多内容,数值线性代数,1997。
- 第 2.4 节奇异值分解,矩阵计算,2012。
- 第 7 章奇异值分解(SVD),线性代数导论,第 5 版,2016 年。
- 第 2.8 节奇异值分解,深度学习,2016 年。
- 第 7.D 节极性分解和奇异值分解,线性代数完成权,第三版,2015 年。
- 第 3 讲奇异值分解,数值线性代数,1997。
- 第 2.6 节奇异值分解,数字秘籍:科学计算的艺术,第三版,2007。
- 第 2.9 节 Moore-Penrose 伪逆,深度学习,2016。
- numpy.linalg.svd()API
- numpy.matrix.H API
- numpy.diag()API
- numpy.linalg.pinv()API 。
- sklearn.decomposition.TruncatedSVD API
在本教程中,您发现了奇异值分解方法,用于将矩阵分解为其组成元素。
具体来说,你学到了:
- 奇异值分解是什么以及涉及什么。
- 如何计算 SVD 并从 SVD 元素重建矩形和方形矩阵。
- 如何使用 SVD 计算伪逆并执行降维。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。