矩阵分解:EVD 和 SVD
1. 矩阵分解
矩阵分解的作用:[1]
- 矩阵填充(通过矩阵分解来填充原有矩阵,例如协同过滤的 ALS 算法就是填充原有矩阵)
- 清理异常值与离群点
- 降维、压缩
- 个性化推荐
- 间接的特征组合(计算特征间相似度)
2. 特征分解
2.1 特征值和特征向量
如果一个向量 是矩阵 的特征向量,将一定可以表示成下面的形式:
其中, 是特征向量 对应的特征值,一个矩阵的一组特征向量是一组正交向量。
2.2 特征值分解
对于矩阵 ,有一组特征向量 ,将这组向量进行正交化单位化,就能得到一组正交单位向量。特征值分解(Eigenvalue decomposition,EVD),就是将矩阵 分解为如下式:
其中, 是矩阵 的特征向量组成的矩阵, 则是一个对角阵,对角线上的元素就是特征值。
解特征值的方式很简单,通过定义我们可以得到
其中 为单位矩阵。
通过特征方程得到特征值,从而求出特征向量。
特征值分解示例
定义方阵 为
下面对 进行特征值分解。
首先,由方阵 的特征方程,求出特征值:
解得 ,其中 为重根。
当 时,求出 ,过程如下
解得 ,,取特征向量为 。
同理当 时,特征向量为 。
方阵 的特征分解为
2.3 Python 实现特征值分解
SymPy 和 NumPy 均支持特征值分解,但 SymPy 是符号解,是精准值但更慢,适合研究。而 NumPy 为数值解,更快但有精度损失,适合生产。
SymPy 进行特征值分解:
from sympy import Matrix
A = Matrix([[-1, 1, 0], [-4, 3, 0], [1, 0, 2]])
print(A.eigenvals())
# {1: 2, 2: 1},特征值和其重根数量
print(A.eigenvects())
# [
# (1, 2, [ Matrix([[-1], [-2], [ 1]]) ]),
# (2, 1, [ Matrix([[0], [0], [1] ]) ]),
# ]
矩阵的 .eigenvects()
方法返回列表,每个列表是一个三元组,分别代表特征值、特征值的重根数和特征向量。
使用 NumPy 进行特征值分解:
import numpy as np
A = np.array([[-1, 1, 0], [-4, 3, 0], [1, 0, 2]])
vals, vecs = np.linalg.eig(A)
print(vals)
# [2. 1. 1.]
print(vecs)
# [[ 0. 0.40824829 0.40824829]
# [ 0. 0.81649658 0.81649658]
# [ 1. -0.40824829 -0.40824829]]
这里的特征向量和 SymPy 计算有所不同是因为 NumPy 进行了单位化。
3. SVD 分解
3.1 奇异值分解
特征值分解只能用于方阵,我们下面要介绍的 奇异值分解(Singular Value Decomposition,SVD)就是可以解决这个问题。
奇异值分解也有各种各样的用途:用 SVD 解 PCA、潜在语言索引也依赖于 SVD 算法。可以说,SVD 是矩阵分解、降维、压缩、特征学习的一个基础的工具,因而 SVD 在机器学习领域也相当的重要。
奇异值分解是一个能适用于任意矩阵的一种分解的方法,对于任意矩阵 总是存在一个奇异值分解:
若 是一个 的矩阵,那么得到的 是一个 方阵, 里面的正交向量被称为左奇异向量。 是一个 的矩阵,且 是对角矩阵,对角线上的元素称为奇异值。 是 矩阵,且 和 都是酉矩阵。
那么我们如何计算左奇异向量、右奇异向量和奇异值呢?我们可以把奇异值和特征值联系起来。
首先,我们用矩阵 的转置乘 ,得到一个方阵,这样我们可以用方阵进行特征分解,得到特征值和特征向量满足下面的等式:
这里的 就是右奇异向量,同理我们用下面的等式得到左奇异向量 :
当然以上结论只是我们的设想,下面进行证明,如果对证明细节不感兴趣可以忽略。
证明
提示:若 是实矩阵,且 ,那么 是实对称矩阵。
从上面的结论可以看出 的特征向量就是 SVD 中的 矩阵。同理 的特征矩阵为 。
从证明中,我们得出求出奇异值的两种方法:
第一种方式是通过 得到
第二种是通过 得出结论,特征值矩阵等于奇异值矩阵平方,也就是说特征值和奇异值满足如下关系
奇异值分解示例
矩阵 定义为
首先计算 和 :
然后,求出 和 的特征值和特征向量:
- 的特征值和特征向量
- 的特征值和特征向量
其次,我们利用 ,,求奇异值:
当然,最简单的方式是直接利用结论 得出奇异值。
最后,我们得到 的奇异值分解为
此时 不为方阵,最后一行一定为 ,我们可以直接将其删去,因此也不需要计算最后一个特征值为 的特征向量。直接写为:
3.2 Python 实现奇异值分解
这里还是使用 SymPy 进行符号解和 NumPy 进行数值解两种方法。
SymPy 有求奇异值和奇异值分解的方法:
from sympy.matrices import Matrix
A = Matrix([[0, 1], [1, 1], [1, 0]])
print(A.singular_values())
# [sqrt(3), 1]
U, S, VH = A.singular_value_decomposition()
print(U)
# Matrix([[sqrt(2)/2, sqrt(6)/6],
# [0, sqrt(6)/3],
# [-sqrt(2)/2, sqrt(6)/6]])
print(S)
# Matrix([[1, 0],
# [0, sqrt(3)]])
print(VH)
# Matrix([[-sqrt(2)/2, sqrt(2)/2],
# [sqrt(2)/2, sqrt(2)/2]])
下面是 NumPy 进行奇异值分解的方法:
import numpy as np
A = np.array([[0, 1], [1, 1], [1, 0]])
U, S, VH = np.linalg.svd(A)
print(U)
# [[-4.08248290e-01 7.07106781e-01 5.77350269e-01]
# [-8.16496581e-01 2.64811510e-17 -5.77350269e-01]
# [-4.08248290e-01 -7.07106781e-01 5.77350269e-01]]
print(S)
# [1.73205081 1. ]
print(VH)
# [[-0.70710678 -0.70710678]
# [-0.70710678 0.70710678]]
机器学习中 SVD 总结,Microstrong,https://mp.weixin.qq.com/s/Dv51K8JETakIKe5dPBAPVg ↩︎