矩阵分解,也称为矩阵因子分解,涉及使用其组成元素来描述给定的矩阵。
奇异值分解(SVD)可能是最广为人知和广泛使用的矩阵分解方法。所有矩阵都具有SVD,这使其比其他方法(如特征分解)更稳定。因此,它常用于各种应用中,包括压缩、去噪和数据降维。
在本教程中,您将学习奇异值分解方法,用于将矩阵分解为其组成元素。
完成本教程后,您将了解:
- 什么是奇异值分解以及它涉及哪些内容。
- 如何计算SVD并从SVD元素重构矩形和方阵。
- 如何使用SVD计算伪逆和执行降维。
使用我的新书机器学习线性代数,启动您的项目,包括分步教程和所有示例的Python源代码文件。
让我们开始吧。
- 2018年3月更新:修正了重建中的拼写错误。为清晰起见,将代码中的 V 改为 VT。修正了伪逆方程中的拼写错误。
- 2019年4月更新:修正了 SVD 重建示例解释中数组大小的小错误。

奇异值分解简介
图片由Chris Heald提供,保留部分权利。
教程概述
本教程分为5个部分,它们是:
- 奇异值分解
- 计算奇异值分解
- 从 SVD 重构矩阵
- SVD 用于伪逆
- SVD 用于降维
在机器学习线性代数方面需要帮助吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
奇异值分解
奇异值分解,简称 SVD,是一种矩阵分解方法,用于将矩阵分解为其组成部分,以简化后续的某些矩阵计算。
为简单起见,我们将重点关注实值矩阵的 SVD,而忽略复数的情况。
1 |
A = U . Sigma . V^T |
其中 A 是我们希望分解的实数 m x n 矩阵,U 是一个 m x m 矩阵,Sigma(通常用大写希腊字母 Sigma 表示)是一个 m x n 对角矩阵,V^T 是一个 n x n 矩阵的转置,其中 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 矩阵并计算其奇异值分解。
1 2 3 4 5 6 7 8 9 10 11 |
# 奇异值分解 from numpy import array from scipy.linalg import svd # 定义一个矩阵 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×3 U 矩阵、2 元素 Sigma 向量和 2×2 V^T 矩阵元素,这些都是从分解中计算出来的。
1 2 3 4 5 6 7 8 9 10 11 12 |
[[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 元素不能直接相乘。
s 向量必须使用 diag() 函数转换为对角矩阵。默认情况下,此函数将创建一个相对于我们原始矩阵的 n x n 方阵。这导致了一个问题,因为矩阵的大小不符合矩阵乘法的规则,即一个矩阵的列数必须与后续矩阵的行数匹配。
创建方阵 Sigma 对角矩阵后,矩阵的大小相对于我们要分解的原始 m x n 矩阵,如下所示:
1 |
U (m x m) . Sigma (n x n) . V^T (n x n) |
而实际上,我们需要:
1 |
U (m x m) . Sigma (m x n) . V^T (n x n) |
我们可以通过创建一个所有元素为零的 m x n (例如,行数更多) 的新 Sigma 矩阵来实现这一点,并用通过 diag() 计算出的方对角矩阵填充该矩阵的前 n x n 部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 重构 SVD from numpy import array from numpy import diag 从 numpy 导入 dot from numpy import zeros from scipy.linalg import svd # 定义一个矩阵 A = array([[1, 2], [3, 4], [5, 6]]) print(A) # 奇异值分解 U, s, VT = svd(A) # 创建 m x n 的 Sigma 矩阵 Sigma = zeros((A.shape[0], A.shape[1])) # 用 n x n 对角矩阵填充 Sigma Sigma[:A.shape[1], :A.shape[1]] = diag(s) # 重构矩阵 B = U.dot(Sigma.dot(VT)) print(B) |
运行示例首先打印原始矩阵,然后打印从 SVD 元素重构的矩阵。
1 2 3 4 5 6 7 |
[[1 2] [3 4] [5 6]] [[ 1. 2.] [ 3. 4.] [ 5. 6.]] |
上述 Sigma 对角线的复杂性只存在于 m 和 n 不相等的情况。在重构方阵时,对角矩阵可以直接使用,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 重构 SVD from numpy import array from numpy import diag 从 numpy 导入 dot from scipy.linalg import svd # 定义一个矩阵 A = array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(A) # 奇异值分解 U, s, VT = svd(A) # 创建 n x n 的 Sigma 矩阵 Sigma = diag(s) # 重构矩阵 B = U.dot(Sigma.dot(VT)) print(B) |
运行示例打印原始的 3×3 矩阵,以及直接从 SVD 元素重构的版本。
1 2 3 4 5 6 7 |
[[1 2 3] [4 5 6] [7 8 9]] [[ 1. 2. 3.] [ 4. 5. 6.] [ 7. 8. 9.]] |
SVD 用于伪逆
伪逆是方阵逆的推广,适用于行数和列数不等的矩形矩阵。
它也被称为 Moore-Penrose 逆,以该方法的两位独立发现者命名,或称为广义逆。
矩阵求逆对非方阵没有定义。[…] 当 A 的列数多于行数时,使用伪逆求解线性方程提供了一种可能的解。
— 第 46 页,《深度学习》,2016。
伪逆表示为 A^+,其中 A 是被求逆的矩阵,+ 是上标。
伪逆是使用 A 的奇异值分解计算的
1 |
A^+ = V . D^+ . U^T |
或者,不使用点符号
1 |
A^+ = VD^+U^T |
其中 A^+ 是伪逆,D^+ 是对角矩阵 Sigma 的伪逆,U^T 是 U 的转置。
我们可以从 SVD 运算中得到 U 和 V。
1 |
A = U . Sigma . V^T |
D^+可以通过从Sigma创建一个对角矩阵,计算Sigma中每个非零元素的倒数,并在原始矩阵是矩形的情况下进行转置来计算。
1 2 3 |
s11, 0, 0 Sigma = ( 0, s22, 0) 0, 0, s33 |
1 2 3 |
1/s11, 0, 0 D^+ = ( 0, 1/s22, 0) 0, 0, 1/s33 |
伪逆提供了一种解决线性回归方程的方法,特别是在行数多于列数的情况下,这种情况经常发生。
NumPy 提供了 pinv() 函数用于计算矩形矩阵的伪逆。
下面的示例定义了一个 4×2 矩阵并计算其伪逆。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 伪逆 from numpy import array from numpy.linalg import pinv # 定义矩阵 A = array([ [0.1, 0.2], [0.3, 0.4], [0.5, 0.6], [0.7, 0.8]]) print(A) # 计算伪逆 B = pinv(A) print(B) |
运行示例首先打印定义的矩阵,然后打印计算出的伪逆。
1 2 3 4 5 6 7 |
[[ 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 数组可以转换为对角矩阵,并添加一排零使其成为矩形。最后,我们可以从这些元素计算伪逆。
具体的实现是
1 |
A^+ = V . D^+ . U^V |
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 通过SVD计算伪逆 from numpy import array from numpy.linalg import svd from numpy import zeros from numpy import diag # 定义矩阵 A = array([ [0.1, 0.2], [0.3, 0.4], [0.5, 0.6], [0.7, 0.8]]) print(A) # 计算svd U, s, VT = svd(A) # s 的倒数 d = 1.0 / s # 创建 m x n 的 D 矩阵 D = zeros(A.shape) # 用 n x n 对角矩阵填充 D D[:A.shape[1], :A.shape[1]] = diag(d) # 计算伪逆 B = VT.T.dot(D.T).dot(U.T) print(B) |
运行示例首先打印定义的矩形矩阵,然后是与上面 pinv() 函数结果匹配的伪逆。
1 2 3 4 5 6 7 |
[[ 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。
1 |
B = U . Sigmak . V^Tk |
在自然语言处理中,这种方法可应用于文档中单词出现次数或单词频率的矩阵,称为潜在语义分析或潜在语义索引。
在实践中,我们可以保留并使用数据的一个描述性子集,称为 T。这是矩阵的密集摘要或投影。
1 |
T = U . Sigmak |
此外,这种变换可以计算并应用于原始矩阵 A 以及其他相似的矩阵。
1 |
T = V^k . A |
下面的示例演示了使用 SVD 进行数据降维。
首先定义一个 3×10 矩阵,其中列多于行。计算 SVD 并仅选择前两个特征。将元素重新组合以准确重现原始矩阵。最后以两种不同的方式计算变换。
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 |
from numpy import array from numpy import diag from numpy import zeros from scipy.linalg import svd # 定义一个矩阵 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) # 奇异值分解 U, s, VT = svd(A) # 创建 m x n 的 Sigma 矩阵 Sigma = zeros((A.shape[0], A.shape[1])) # 用 n x n 对角矩阵填充 Sigma Sigma[:A.shape[0], :A.shape[0]] = diag(s) # 选择 n_elements = 2 Sigma = Sigma[:, :n_elements] VT = VT[:n_elements, :] # 重构 B = U.dot(Sigma.dot(VT)) print(B) # 变换 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 |
[[ 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 类。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from numpy import array from sklearn.decomposition import TruncatedSVD # 定义数组 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 |
[[ 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 章,奇异值和约旦分解,《统计学的线性代数与矩阵分析》,2014 年。
- 第 4 章,奇异值分解 和 第 5 章,关于 SVD 的更多内容,《数值线性代数》,1997 年。
- 第 2.4 节 奇异值分解,《矩阵计算》,2012 年。
- 第 7 章 奇异值分解 (SVD),《线性代数导论》,第五版,2016 年。
- 第 2.8 节 奇异值分解,《深度学习》,2016 年。
- 第 7.D 节 极分解和奇异值分解,《线性代数正确学》,第三版,2015 年。
- 讲座 3 奇异值分解,《数值线性代数》,1997 年。
- 第 2.6 节 奇异值分解,《数值食谱:科学计算的艺术》,第三版,2007 年。
- 第 2.9 节 莫尔-彭罗斯伪逆,《深度学习》,2016 年。
API
- numpy.linalg.svd() API
- numpy.matrix.H API
- numpy.diag() API
- numpy.linalg.pinv() API.
- sklearn.decomposition.TruncatedSVD API
文章
总结
在本教程中,您学习了奇异值分解方法,用于将矩阵分解为其组成元素。
具体来说,你学到了:
- 什么是奇异值分解以及它涉及哪些内容。
- 如何计算SVD并从SVD元素重构矩形和方阵。
- 如何使用SVD计算伪逆和执行降维。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
有一件重要的事情需要澄清:SVD 仅适用于实数,因此不应应用于序数或分类变量。
很棒的观点。
嗯。你的评论与维基百科的说法自相矛盾。
1) SVD 仅对实数有效
2) 伪逆对于所有包含实数或复数的矩阵都是定义明确且唯一的。它可以利用奇异值分解来计算。
https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_inverse
谢谢你的文章!一些校对修正:
“其中 A 是我们希望分解的实数 n x m 矩阵……”
A 是 m x n。
“运行示例首先打印定义的 3×2 矩阵,然后是 3×3 U 矩阵、2 元素 Sigma 向量和 2×3 V^T 矩阵元素,这些都是从分解中计算出来的。”
V^T 是 2×2。
“其中 A^+ 是伪逆,D^+ 是对角矩阵 Sigma 的伪逆,V^T 是 V^T 的转置。”
最后一部分应该是关于 U^T 是 U 的转置。
谢谢,已修复!
您好。很棒的教程。一个问题。如果 A 矩阵的行数多于列数,会发生什么?我倾向于将 A 定义为 [特征,样本]。
好问题,我暂时不确定。
如果您将其用于降维,不妨尝试一下,看看投影如何影响模型技能。
谢谢。您的课程很棒!
再次感谢您。
谢谢!
谢谢 Jason 先生!我正在处理一个 M×N 矩阵,其中 M > N。如果我想对它实现 SVD 降维怎么办?我在 Sigma[:A.shape[0], :A.shape[0]] = diag(s) 处得到了矩阵大小错误,如果我将其更改为 Sigma[:diag(s).shape[0], :diag(s).shape] = diag(s) 怎么办?顺便说一下,它运行得非常好。
你说 U.dot(Sigma) 和 A.dot(VT.T) 是原始矩阵的两个等效变换。
那么为什么 U.dot(Sigma) == A.dot(VT.T) 返回的大多是 False 呢?
其次,你能给我们展示一下如何使用 randomized_svd 函数对随机 SVD 转换数据进行操作吗?
您确定吗?我到底在哪里说过?
感谢您的建议。
像 FunkSVD 这样的迭代 SVD 能够增量更新,但如果用于推荐系统,标准 SVD 需要完全重新计算以合并评分矩阵中的新行或列。这种快速更新功能对于实用的推荐系统至关重要。FunkSVD 不是精确的 SVD,但足够接近,并且对于添加用户或项目非常高效,这在亚马逊或 Netflix 等大型网站上经常发生。对于大型推荐系统来说,完全重新计算的成本太高,而且在 24 小时流量的全球网站上何时执行它——你无法做到。
但是,对于研究论文或数据是静态的地方,只要数据集足够小,普通 SVD 可能就非常适用。
SVD 也不适用于高度稀疏的评分矩阵,因为 SVD 完全无法合并任何缺失数据。您需要提供值,以便没有数据缺失。您可以随意将 0 或用户或项目的平均值或全局平均值分配给缺失值。但这样您就告诉 SVD 一些不真实的事情,SVD 就会像对待实际存在的诚实数据一样接受您的谎言。SVD 只能将您提供的值视为所有实际真实值。因此,分解同样会受到您在分解之前选择的无意义数据值的强烈影响。尽管如此,SVD 和 FunkSVD 等迭代 SVD 近似值都已用于具有原始评分矩阵中缺失数据的系统,并取得了一些成功,表明尽管“真实”数据存在问题,SVD 仍能产生一些信号。
当存在高度稀疏性时,情况更糟。我最近看到的一个 subreddit 推荐器中存在 99.99% 的稀疏性,其中用户对 subreddit 的每个评论为 1,没有评论则为缺失数据。大多数 Reddit 用户只在少数 subreddit 中评论。在一个为期 5 天的 Reddit 评论流量样本中,有 200 万独立用户和 5 万个 subreddit 提交评论。
有些推荐器中缺失数据确实为 0,例如隐式推荐器,其中 1 表示用户点击,缺失数据表示 0 次点击。将 0 放在那里是处理缺失数据的正确做法,因为它实际上是 0。那么 SVD 就完全适用,它可以被提供 100% 真实数据进行分解,因此它产生的输出也可能很好,而不是建立在垃圾输入之上。
您可以使用专门将缺失评分从损失函数中排除的算法,来正确地使用梯度下降算法(如Adam)在TensorFlow中训练矩阵分解模型。这种模型设计和代码设计完全不受预先插补的缺失数据的影响。这很好地解决了某些推荐系统中的缺失值问题。
进行一项实验可能很有趣,以经验性地看看如果正确处理缺失值并在模型训练期间不使用它们,与在数据缺失的地方填入虚假数据值并将其纳入模型训练相比,预测效果会好多少。
我鼓励你进行这样的实验,Geoffrey。
我认为“从SVD重建矩阵”部分下第一个方程中指定的矩阵大小不正确。根据上面的讨论,diag(S)应该生成一个n x n大小的矩阵。
你为什么这么说?
确实如此。
根据文档,svd()返回“s:ndarray [...] 形状为(K,),其中K = min(M, N)。”
(https://docs.scipy.org.cn/doc/scipy/reference/generated/scipy.linalg.svd.html)
这意味着S是一个向量,其长度是原始矩阵的最小尺寸。
因此,diag(S)将创建一个大小为(min(M, N), min(M, N))的对角矩阵。
然后,如果m > n(如示例A的形状是(3, 2)),则公式
U (m x m) . Sigma (m x m) . V^T (n x n)
将变成
U (m x m) . Sigma (n x n) . V^T (n x n)
我们可以用SVD进行聚类吗?
我没有关于聚类的材料,所以我无法给你很好的即时建议。
谢谢你所做的一切,Jason。
非常欢迎!我很高兴这些帖子有用!
非常感谢这个教程。
你能解释一下你所说的“这种符号的不稳定性在实践中不应该是一个问题,只要转换经过训练以便重用”是什么意思吗?
“这种符号的不稳定性在实践中不应该是一个问题,只要转换经过训练以便重用。”
这意味着符号(+或-)可能会根据找到的不同解而改变,对此不必担心。
很棒的文章!!!!
您能否进一步解释符号(+或-)的意义,以便我们得出一个关于为何不必担心它的结论性理解?我的意思是,这个符号意味着什么?
分析上,正解或负解是两个可能的解,实际上它们是同一回事。
你好,如果我们使用TruncatedSVD,我们如何知道捕获的方差量,或者换句话说,如何决定n_components的数量?对于PCA,我们通过“pca.explained_variance_ratio_”方法获得解释的方差。SVD有类似的选项吗?
您可以使用svd.explained_variance_ratio_
更多信息在这里
https://scikit-learn.cn/stable/modules/generated/sklearn.decomposition.TruncatedSVD.html
如上所述,SVD执行特征降维任务。特征降维具体是指特征提取还是特征选择(因为两者都是特征降维技术)?我猜SVD在做的是特征提取。我说的对吗?如果是这样,它能用于特征选择吗?
是的,它是一种投影或特征提取。
它是特征选择的替代方案。
你好,我想从一个矩阵中找到第一个奇异向量……如果我使用svd,我该如何从中找到奇异向量。请帮助我……
同样的问题,请帮助!
也许你指的是这个?
https://en.wikipedia.org/wiki/Singular_value
嗨,Jason——感谢您的精彩教程。它们对我的研究非常有帮助。我意识到这可能有点偏题,但我似乎找不到一个对我来说有意义的答案。它与sklearn的FactorAnalysis有关——与本帖的主题不同,但相关。基本上,在传统的探索性因子分析中,我认为变量多于观测值会阻止模型收敛。当我在R中尝试运行这样的模型时,出现了错误。然而,我使用sklearn的FactorAnalysis运行相同的数据时,得到了可解释的结果。我很想得到一个简短的解释,为什么机器学习版本的EFA可以收敛,而我的传统EFA却没有。(注意:我没有在R中尝试使用多个包,所以我可能错了。)提前感谢!
很好的问题!
抱歉,我无法给您一个好的答案,我不熟悉sklearn的因子分析实现。
也许API
https://scikit-learn.cn/stable/modules/generated/sklearn.decomposition.FactorAnalysis.html
或源代码
https://github.com/scikit-learn/scikit-learn/blob/7813f7efb/sklearn/decomposition/factor_analysis.py#L35
会给您一些启发。
eigh?不需要通过减去均值和除以(n-1)来预处理数据吗?
如果我有一个m x n的数据矩阵X(m=20000特征-行,n=19样本-列),我仍然可以使用
u, s, v = np.linalg.svd(X)
返回的u是(20000,20000)的特征向量吗?
返回的s**2是(19,)的特征值吗?
我有一个关于以下内容的问题
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)
# 奇异值分解
U, s, VT = svd(A)
对于上面的“s”,它的形状应该是(10,),因为我们有10个特征,但显示的是(3,)。我很困惑。我们再考虑一个例子
A = array([[1, 2], [3, 4], [5, 6]])
print(A.shape)
# 奇异值分解
U, s, VT = svd(A)
这里“s”的形状显示为(2,)
我不明白为什么形状会有差异。请解释一下。
“s”是Sigma。来自帖子
感谢Jason的精彩教程。
我被鼓励使用SVD进行降维,但我无法理解在上述教程中,维度到底在哪个点被降低了?我可以看到当选择顶部SVD元素时,Sigma和VT将具有较低的维度。对吗?
我可以将这两者视为降维,还是必须考虑U,当U、S、Vt从SVD创建时,维度会增加?
请指教。
请参阅上面标题为“SVD for Dimensionality Reduction”的部分。
嗨,Jason,
我正在尝试从T计算U,公式为
U = T.dot(inv(Sigma))
这里Sigma应该是一个方阵才能计算逆矩阵。
有没有办法仅从T和VT重构原始矩阵?
不
A = U . Sigma . V^T
嗨,Jason,
我们可以使用U、s和V^T的最小维度来构建原始矩阵,如下所示
np.dot(U[:, :n_components], np.dot(VT[:n_components, :], s[:n_components]))
因此,这里计算原始数据所需的维度更少。所以压缩更多。
感谢分享。
一如既往的精彩!
几年前我做过这个,它也揭示了伪逆的使用背后的一些秘密
https://glowingpython.blogspot.com/2011/06/svd-decomposition-with-numpy.html
希望它能帮助这里的读者。
感谢分享!
感谢这个精彩的教程!
我认为当您用特征值填充S矩阵时,您应该使用A的秩而不是n,例如
r = npl.matrix_rank(A)
# 创建 m x n 的 Sigma 矩阵
Sigma = np.zeros((A.shape[0], A.shape[1]))
# 用 n x n 对角矩阵填充 Sigma
Sigma[:r, :r] = np.diag(s)
应该是
Sigma[:r, :r] = np.diag(s)[:r,:r]
感谢您的建议。
在“从SVD重建矩形矩阵”代码部分,第17行您写道
B = U.dot(Sigma.dot(VT))
应该是B=U.dot(Sigma.dot(VT.T)),其中VT.T是VT的转置矩阵。
你为什么这么说?
我认为这可能是一个混淆。重构是B= U (m x m) . Sigma (m x n) . V^T (n x n) ,而您没有使用V的转置,用您的符号表示将是VT.T。
但是Scipy直接且隐式地提供了V的转置,所以您不需要转置V矩阵。
谢谢。
你太棒了,哇哦哦哦哦哦哦哦,解释得很好,很清楚,非常感谢你。
谢谢!
谢谢你,Jason。
我现在理解了SVD的基本概念,但是SVD如何用于图像隐写术中的封面图像选择呢?
我很乐意获得一个想法或Python源代码。提前感谢
从头开始编写代码,看看是否有帮助。
scipy的代码在我的情况下不起作用。我认为,它应该被修复为
# SVD
U, s, VT = svd(A)
# 创建 m x n 的 Sigma 矩阵
Sigma = np.zeros((A.shape))
# populate Sigma with n x n diagonal matrix
Sigma[:, :min(A.shape)] = np.diag(s)
print(U)
print(Sigma)
print(VT)
谢谢。但是这里的代码似乎在最新版本的scipy中仍然运行良好。
是的,真的很好