目录
一、SVD基本概念及定义
SVD(Singular Value Decomposition,奇异值分解)是一种重要的矩阵分解方法,SVD矩阵分解是将一个矩阵分解为三个矩阵的乘积,其中第一个矩阵是一个正交矩阵,第二个矩阵是对角矩阵,第三个矩阵是另一个正交矩阵的转置。对于一个m×n的矩阵A,其SVD矩阵分解为:
矩阵 | 别称 | 维度 | 计算方式 | 含义 |
---|---|---|---|---|
U矩阵 | A的左奇异矩阵 | m行m列 | 列由的特征向量组成,且特征向量为单位向量 | 包含了有关行的所有信息(代表自己的观点) |
矩阵 | A的奇异值矩阵 | m行n列 | 对角元素来源于或的特征值的平方根,并且按降序排列,值越大可以理解为越重要 | 记录SVD过程 |
V矩阵 | A的右奇异矩阵 | n行n列 | 列由的特征向量组成,且特征向量为单位向量 | 包含了有关列的所有信息(代表自己的特征) |
其中,U是一个正交矩阵,其列向量称为左奇异向量;是一个n×n的对角矩阵,其对角线上的元素称为奇异值,按照降序排列,ii≥0;V是一个n×n的正交矩阵,其行向量称为右奇异向量。
SVD矩阵分解的本质是对原矩阵进行特征值分解,但是由于SVD可以处理非方阵和奇异矩阵,因此被广泛应用。
其中,U和V的列向量为正交向量,表示一个空间的基,而Σ则是对这个空间的缩放变换。SVD矩阵分解可以帮助我们找到一个最优的低秩近似矩阵,即用较低的秩来近似原矩阵,并在某些应用中取得较好的效果。
通常情况下,我们可以保留前k个奇异值及其对应的左右奇异向量,从而得到原矩阵的一个秩为k的低秩近似矩阵。
算法实现上,可以使用各种数值方法进行迭代计算,如幂法或QR分解等方法,求取特征值和特征向量。但是由于实现较为复杂,因此在实际应用中,通常使用基于随机化的SVD算法,如随机采样SVD(RSVD)或随机化交替最小二乘(rALS)等算法进行计算。
二、SVD矩阵分解数学原理
SVD矩阵分解数学原理较为复杂,涉及到SVD分解的具体算法和数学推导,如线性代数、数值计算等诸多数学知识,包括奇异值、特征值、正交矩阵等概念,以及对矩阵进行特征值分解、奇异值分解等计算方法。SVD矩阵分解数学原理主要用于解释和推导SVD分解的具体实现方式和正确性。
以下是对数学原理进行简要说明:
假设有一个m×n的矩阵A,则可以通过特征值分解得到下面的公式:
其中,Q为列向量为特征向量的正交矩阵,为主对角线上的元素为特征值的对角矩阵。
由于可能存在非方阵和奇异矩阵的情况,因此我们需要对矩阵A进行奇异值分解(Singular Value Decomposition,SVD)。
首先,我们可以将矩阵A转换成或的形式,因为这两者都是对称半正定的,因此可以进行特征值分解,得到如下的公式:
其中,为列向量为左奇异向量的正交矩阵,为对应于左奇异向量的奇异值的对角矩阵。
同样地,将矩阵A转换成的形式,可得:
其中,为列向量为右奇异向量的正交矩阵,为对应于右奇异向量的奇异值的对角矩阵。
将上述两个公式进行整合并简化,可得SVD的公式:
其中,为包含了和中非零奇异值的对角矩阵,按照降序排列。
上述公式中,即为左奇异向量矩阵,即为右奇异向量矩阵,而则是奇异值矩阵。
SVD分解的本质是将原矩阵映射到一个新的坐标系下,并且在该坐标系下找到一组最优基向量,将原始信息表示成这些基向量的线性组合。因此,SVD在数据处理、压缩和特征提取等方面都有广泛的应用。
三、SVD的实现
求解SVD就是求解U、、V这3个矩阵,而求解这三个矩阵就是求解特征值和特征向量,可以通过一下两种方法求出:
方法1:根据三个矩阵的定义,求出U、、V;
方法2:利用Numpy库提供的linalg的线性代数工具箱的svd()函数来求解。
scd()函数:svd(A,full_matrices=1,compure_uv=1)
返回值:u,s,v,从左到右分别对应U矩阵,奇异值,V的转置矩阵。
四、SVD矩阵分解代码实现
1、核心代码
SVD(奇异值分解)是一种重要的矩阵分解方法,广泛应用于信号处理、图像处理、推荐系统等领域。在Python中,我们可以使用Numpy库提供的linalg.svd函数来实现SVD分解,以下是SVD矩阵分解的Python代码实现。
代码如下:
import numpy as np
def svd(A):
# 计算矩阵A的转置矩阵和AA^T矩阵的特征值和特征向量
A_T = np.transpose(A)
A_A_T = np.dot(A, A_T)
eig_value_AA_T, eig_vector_AA_T = np.linalg.eig(A_A_T)
# 将特征向量进行正交化处理得到左奇异向量矩阵U
U = np.dot(A_T, eig_vector_AA_T)
U_norms = np.array([np.linalg.norm(u) for u in U.T])
U = (U.T / U_norms).T
# 计算矩阵A的转置矩阵和A^TA矩阵的特征值和特征向量
A_T = np.transpose(A)
A_T_A = np.dot(A_T, A)
eig_value_A_T_A, eig_vector_A_T_A = np.linalg.eig(A_T_A)
# 将特征向量进行正交化处理得到右奇异向量矩阵V
V = eig_vector_A_T_A
V_norms = np.array([np.linalg.norm(v) for v in V.T])
V = (V.T / V_norms).T
# 计算奇异值矩阵Sigma,按照降序排列
eig_value_sort_idx = np.argsort(eig_value_AA_T)[::-1]
eig_value_sort = eig_value_AA_T[eig_value_sort_idx]
Sigma = np.zeros_like(A, dtype=np.float64)
Sigma[:len(eig_value_AA_T), :len(eig_value_AA_T)] = np.sqrt(np.diag(eig_value_sort))
# 返回左奇异向量矩阵U、奇异值矩阵Sigma和右奇异向量矩阵V
return U, Sigma, V.T
在上述代码中,我们按照SVD分解的流程分别计算矩阵和的特征值和特征向量,然后通过正交化处理得到左奇异向量矩阵U和右奇异向量矩阵V。接着,我们按照降序排列的方式计算奇异值矩阵,并返回U、和V。
需要注意的是,上述代码仅适用于对方阵进行SVD分解的情况。对于非方阵,则需要进行额外的处理。此外,由于SVD分解本身的计算复杂度较高,实际应用中往往使用一些快速的算法和优化技巧来提高分解的效率。
2、代码示例
示例矩阵:
import numpy as np
# 输入矩阵A
A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 使用linalg.svd函数进行SVD分解
U, Sigma, VT = np.linalg.svd(A)
# 输出分解结果
print("左奇异向量矩阵U:")
print(U)
print("奇异值矩阵Sigma:")
print(np.diag(Sigma))
print("右奇异向量矩阵VT:")
print(VT)
在上述代码中,我们首先定义了一个3x3的输入矩阵A,并使用Numpy的linalg.svd函数对其进行了SVD分解。具体地,linalg.svd函数的返回值包括三个部分:
- 左奇异向量矩阵U:一个m×m的正交矩阵,其中m是矩阵A的行数。
- 奇异值矩阵Sigma:一个m×n的矩形对角矩阵,其中m和n分别是矩阵A的行数和列数,m≥n。矩阵的对角线上的元素称为奇异值。
- 右奇异向量矩阵V:一个n×n的正交矩阵,其中n是矩阵A的列数。
在上述代码中,我们通过调用np.diag函数将奇异值矩阵ΣΣ转换成了一个m×n的对角矩阵。这是因为,实际应用中我们通常只需要奇异值矩阵的对角线上的元素。
需要注意的是,在使用linalg.svd函数进行SVD分解时,其返回的左奇异向量矩阵U和右奇异向量矩阵V已经被自动正交化处理(即满足=I 和=I),因此一般无需再手动进行正交化操作。
运行上述代码后,得到以下输出结果:
左奇异向量矩阵U:
[[-0.21483724 -0.88723069 0.40824829]
[-0.52058739 -0.24964395 -0.81649658]
[-0.82633755 0.3879428 0.40824829]]
奇异值矩阵Sigma:
[[1.68481034e+01 0.00000000e+00 0.00000000e+00]
[0.00000000e+00 1.06836951e+00 0.00000000e+00]
[0.00000000e+00 0.00000000e+00 1.47280819e-16]]
右奇异向量矩阵VT:
[[-0.47967103 -0.57236779 -0.66506455]
[ 0.77669099 0.07568653 -0.62531792]
[-0.40824829 0.81649658 -0.40824829]]
可以看到,成功地计算出了输入矩阵A的左奇异向量矩阵U、右奇异向量矩阵V和奇异值矩阵。其中,中的奇异值按照降序排列。