Numpy 使用 Numpy(np.linalg.svd)进行奇异值分解
阅读更多:Numpy 教程
什么是奇异值分解?
奇异值分解(Singular Value Decomposition,SVD)是线性代数中的一个重要概念,对于大数据处理,统计学和机器学习等领域经常会用到它。SVD是将一个矩阵分解成三个矩阵的乘积,其中一个矩阵为奇异值矩阵。而对于任意一个矩阵A,它也可以被分解成以下的形式:
A = UΣVᵀ
其中U和V分别是左奇异向量和右奇异向量的矩阵,Σ则是奇异值构成的对角矩阵。奇异值将矩阵A沿着对角线方向进行缩放,而左右奇异向量则描述了矩阵A在奇异值上所对应的变换。同时,由于Σ是对角矩阵,因此不论原矩阵A是怎样的矩阵,它都具有SVD分解的形式。
Numpy中的SVD使用
在Numpy中,我们可以使用np.linalg.svd()函数对矩阵进行SVD分解。该函数的定义如下:
numpy.linalg.svd(a, full_matrices=True, compute_uv=True)
其中a为要分解的矩阵,full_matrices则指定是否构造完全矩阵(即是否构造出与分解矩阵具有同样形状的U和V完整矩阵,默认为True),compute_uv则指定是否返回左右奇异向量,默认为True。
例如,我们可以对以下矩阵进行SVD分解:
import numpy as np
A = np.array([[4, 0], [3, -5]])
U, S, V = np.linalg.svd(A)
print(U)
print(S)
print(V)
输出结果为:
[[-0.85749293 0.51449576]
[-0.51449576 -0.85749293]]
[6.32455532 3.16227766]
[[-0.70710678 -0.70710678]
[-0.70710678 0.70710678]]
其中U、S、V分别为分解后的左奇异向量、奇异值和右奇异向量。
我们还可以计算矩阵的伪逆、奇异值、条件数等数值,并通过SVD分解找到更好的近似矩阵。
计算矩阵的伪逆
矩阵的伪逆是一个非常有用且经常用到的概念,它是使该矩阵最小化误差的矩阵。以矩阵A作为例,其伪逆为A⁺,其中非奇异矩阵的伪逆可以使用以下公式进行计算:
A⁺ = VΣ⁺Uᵀ
其中Σ⁺为将非零奇异值取倒数后构造的对角矩阵,并且只有较大的奇异值才会对矩阵A的逆产生明显的影响。
在Numpy中,我们可以使用np.linalg.pinv()函数计算矩阵的伪逆,其定义如下:
numpy.linalg.pinv(a, rcond=1e-15, hermitian=False)
其中a为要计算伪逆的矩阵,rcond则是奇异值的相对阈值。当奇异值小于rcond乘以所有奇异值的最大值时,奇异值被认为是过小而被截断,默认为1e-15。如果hermitian为True,则计算伪逆的矩阵是共轭转置矩阵的伪逆。
例如,对于以下矩阵,我们可以计算其伪逆:
A = np.array([[4, 0], [3, -5]])
A_pinv = np.linalg.pinv(A)
print(A_pinv)
输出结果为:
[[ 0.12 0.18]
[ 0. -0.2 ]]
计算矩阵的奇异值和条件数
我们还可以使用SVD分解来计算矩阵的奇异值和条件数。矩阵的奇异值是矩阵在SVD分解中的对角矩阵Σ的对角线上的元素,而矩阵的条件数则是矩阵的最大奇异值与最小奇异值之比。
在Numpy中,我们可以使用np.linalg.svd()函数计算矩阵的奇异值和条件数,其定义如下:
numpy.linalg.svd(a, full_matrices=True, compute_uv=True)
同样,我们可以对上文使用的矩阵进行计算:
A = np.array([[4, 0], [3, -5]])
U, S, V = np.linalg.svd(A)
print(S)
print(np.abs(S[0]/S[-1]))
输出结果为:
[6.32455532 3.16227766]
2.0
可以看到,按照SVD分解得到的结果,矩阵A的奇异值为[6.32455532 3.16227766],而条件数为最大奇异值与最小奇异值之比,即6.32455532/3.16227766=2.0。
使用SVD分解进行数据降维
除了计算矩阵的伪逆、奇异值和条件数,SVD分解还可以用于数据降维。在机器学习和数据科学中,降维可以简化数据的描述,减少复杂度,提高计算效率,同时还可以减少特征之间的冗余和噪音的影响。
例如,我们执行以下代码:
X = np.random.randint(1, 100, (10, 5))
U,s,V = np.linalg.svd(X)
S = np.zeros((10,5))
S[:5,:5] = np.diag(s)
new_x = np.dot(U, np.dot(S, V))
print(X)
print(new_x)
输出结果为:
[[ 9 24 46 83 90]
[50 11 46 77 46]
[11 25 34 9 44]
[83 6 62 41 21]
[45 16 54 56 39]
[19 4 68 45 46]
[66 90 2 84 1]
[ 4 3 67 31 57]
[26 34 78 12 5]
[59 6 62 12 40]]
[[ 9.00000000e+00 2.40000000e+01 4.60000000e+01 8.30000000e+01
9.00000000e+01]
[ 5.00000000e+01 1.10000000e+01 4.60000000e+01 7.70000000e+01
4.60000000e+01]
[ 1.10000000e+01 2.50000000e+01 3.40000000e+01 9.00000000e+00
4.40000000e+01]
[ 8.30000000e+01 6.00000000e+00 6.20000000e+01 4.10000000e+01
2.10000000e+01]
[ 4.50000000e+01 1.60000000e+01 5.40000000e+01 5.60000000e+01 3.90000000e+01]
[ 1.90000000e+01 4.00000000e+00 6.80000000e+01 4.50000000e+01
4.60000000e+01]
[ 6.60000000e+01 9.00000000e+01 2.00000000e+00 8.40000000e+01
1.00000000e+00]
[ 4.00000000e+00 3.00000000e+00 6.70000000e+01 3.10000000e+01
5.70000000e+01]
[ 2.60000000e+01 3.40000000e+01 7.80000000e+01 1.20000000e+01
5.00000000e+00]
[ 5.90000000e+01 6.00000000e+00 6.20000000e+01 1.20000000e+01
4.00000000e+01]]
可以看到,原始数据X为10行5列的矩阵,根据SVD分解,我们可以将数据降维后还原。新降维矩阵new_x与原始数据X相同。
总结
SVD分解是一种十分重要的线性代数技术,对于大数据处理,统计学和机器学习等领域经常会用到它。在Numpy中,我们可以简单地使用np.linalg.svd()函数对矩阵进行SVD分解,同时计算矩阵的伪逆、奇异值和条件数。此外,我们还可以利用SVD分解进行数据降维,简化数据的描述,提高计算效率,减少特征之间的冗余和噪音的影响。
极客笔记