Python中的特征分解与奇异值分解(SVD):在降维与推荐系统中的底层实现

好的,我们开始今天的讲座。今天的主题是Python中的特征分解与奇异值分解(SVD),以及它们在降维和推荐系统中的底层实现。

1. 线性代数基础回顾

在深入探讨特征分解和SVD之前,我们需要回顾一些线性代数的基础概念。

  • 向量 (Vector): 一个有序的数值列表。在Python中,通常用NumPy数组表示。
  • 矩阵 (Matrix): 一个二维的数值数组。同样,在Python中,也用NumPy数组表示。
  • 转置 (Transpose): 矩阵的行和列互换。
  • 内积 (Dot Product): 两个向量对应元素相乘再求和。
  • 线性无关 (Linear Independence): 一组向量中,没有一个向量可以表示为其他向量的线性组合。
  • 正交 (Orthogonal): 两个向量的内积为零。
  • 正交矩阵 (Orthogonal Matrix): 一个方阵,其列向量是单位正交向量。正交矩阵的转置等于其逆矩阵。

2. 特征分解 (Eigen Decomposition)

特征分解是一种将矩阵分解为一组特征向量和特征值的技术。只有方阵才能进行特征分解。

  • 特征向量 (Eigenvector): 对于给定的方阵 A,一个非零向量 v 满足 Av = λv,其中 λ 是一个标量。这个 v 就是 A 的特征向量。
  • 特征值 (Eigenvalue): λ 就是对应于特征向量 v 的特征值。

数学公式:

对于一个 n x n 的方阵 A,如果存在 n 个线性无关的特征向量 v1, v2, …, vn,以及对应的特征值 λ1, λ2, …, λn,那么 A 可以被分解为:

A = VΛV⁻¹

其中:

  • V 是一个 n x n 的矩阵,其列向量是 A 的特征向量 v1, v2, …, vn。
  • Λ 是一个 n x n 的对角矩阵,对角线上的元素是对应的特征值 λ1, λ2, …, λn。

Python 实现:

我们可以使用NumPy库的linalg.eig函数来进行特征分解。

import numpy as np

# 定义一个方阵
A = np.array([[4, 1],
              [2, 3]])

# 进行特征分解
eigenvalues, eigenvectors = np.linalg.eig(A)

print("特征值:", eigenvalues)
print("特征向量:n", eigenvectors)

# 验证分解结果
V = eigenvectors
Lambda = np.diag(eigenvalues)
A_reconstructed = V @ Lambda @ np.linalg.inv(V)  # 使用 @ 运算符进行矩阵乘法

print("重构后的矩阵:n", A_reconstructed)

代码解释:

  1. 我们首先定义了一个2×2的方阵 A。
  2. 使用np.linalg.eig(A)函数计算 A 的特征值和特征向量。
  3. eigenvalues 存储了特征值,eigenvectors 存储了特征向量,其中每一列对应一个特征向量。
  4. 我们使用特征向量构建矩阵 V,并使用特征值构建对角矩阵 Lambda。
  5. 最后,我们使用公式 A = VΛV⁻¹ 重构矩阵 A,并打印结果。由于浮点数精度问题,重构后的矩阵可能与原始矩阵略有不同,但非常接近。

特征分解的应用:降维

特征分解可以用于降维,尤其是在主成分分析 (PCA) 中。 PCA 的核心思想是选择具有最大特征值的特征向量作为主成分,这些主成分代表了数据中最重要的方向。

假设我们有一个数据矩阵 X,我们可以计算 X 的协方差矩阵 C,然后对 C 进行特征分解。选择前 k 个最大的特征值对应的特征向量,构成一个转换矩阵 W。 将原始数据 X 投影到 W 上,得到降维后的数据 Y:

Y = XW

import numpy as np

# 假设我们有以下数据矩阵
X = np.array([[1, 2, 3],
              [4, 5, 6],
              [7, 8, 9]])

# 计算协方差矩阵
C = np.cov(X.T) # 注意要转置,因为np.cov默认计算列之间的协方差

# 进行特征分解
eigenvalues, eigenvectors = np.linalg.eig(C)

# 选择前 k 个最大的特征值对应的特征向量 (例如,k=1)
k = 1
top_k_eigenvectors = eigenvectors[:, :k]

# 将数据投影到选定的特征向量上
Y = X @ top_k_eigenvectors

print("降维后的数据:n", Y)

代码解释:

  1. 我们首先定义了一个数据矩阵 X。
  2. 计算 X 的协方差矩阵 C。 注意,np.cov 默认计算列之间的协方差,因此我们需要转置 X。
  3. 对协方差矩阵 C 进行特征分解。
  4. 选择前 k 个最大的特征值对应的特征向量。
  5. 将原始数据 X 投影到选定的特征向量上,得到降维后的数据 Y。

3. 奇异值分解 (Singular Value Decomposition – SVD)

奇异值分解是一种将矩阵分解为三个矩阵的技术。与特征分解不同,SVD 可以应用于任何矩阵,不要求是方阵。

数学公式:

对于一个 m x n 的矩阵 A,SVD 将 A 分解为:

A = UΣVᵀ

其中:

  • U 是一个 m x m 的正交矩阵,称为左奇异向量矩阵。
  • Σ 是一个 m x n 的对角矩阵,对角线上的元素称为奇异值。奇异值通常按降序排列。
  • V 是一个 n x n 的正交矩阵,称为右奇异向量矩阵。

Python 实现:

我们可以使用NumPy库的linalg.svd函数来进行奇异值分解。

import numpy as np

# 定义一个矩阵
A = np.array([[1, 2, 3],
              [4, 5, 6]])

# 进行奇异值分解
U, S, V = np.linalg.svd(A)

print("U:n", U)
print("S:n", S)  # SVD返回的S是一个一维数组,包含奇异值
print("V:n", V)

# 验证分解结果
Sigma = np.zeros((A.shape[0], A.shape[1]))
Sigma[:A.shape[0], :A.shape[0]] = np.diag(S) # 构建完整的奇异值矩阵
A_reconstructed = U @ Sigma @ V

print("重构后的矩阵:n", A_reconstructed)

代码解释:

  1. 我们首先定义了一个2×3的矩阵 A。
  2. 使用np.linalg.svd(A)函数计算 A 的奇异值分解。
  3. U 存储了左奇异向量矩阵,S 存储了奇异值(一个一维数组),V 存储了右奇异向量矩阵的转置。
  4. 我们使用奇异值构建对角矩阵 Sigma。 需要注意的是,np.linalg.svd 返回的 S 只是奇异值的一维数组,我们需要手动构建完整的对角矩阵。
  5. 最后,我们使用公式 A = UΣVᵀ 重构矩阵 A,并打印结果。

SVD 的应用:降维

SVD 也可以用于降维。类似于 PCA,我们可以选择前 k 个最大的奇异值对应的奇异向量,来近似原始矩阵。

假设我们有一个数据矩阵 A,我们可以对 A 进行 SVD 分解。选择前 k 个最大的奇异值,以及对应的左奇异向量和右奇异向量,构成近似矩阵 Ak:

Ak = UkΣkVkᵀ

其中:

  • Uk 是 U 的前 k 列。
  • Σk 是 Σ 的左上角 k x k 的子矩阵,包含前 k 个奇异值。
  • Vk 是 V 的前 k 列。

降维后的表示可以是 UkΣk 或者 ΣkVᵀ,具体取决于应用场景。

import numpy as np

# 假设我们有以下数据矩阵
A = np.array([[1, 2, 3, 4],
              [5, 6, 7, 8],
              [9, 10, 11, 12]])

# 进行奇异值分解
U, S, V = np.linalg.svd(A)

# 选择前 k 个最大的奇异值对应的奇异向量 (例如,k=2)
k = 2
Uk = U[:, :k]
Sk = np.diag(S[:k])
Vk = V[:k, :]

# 构建近似矩阵
Ak = Uk @ Sk @ Vk

print("降维后的表示 Uk*Sk:n", Uk @ Sk)
print("降维后的表示 Sk*VkT:n", Sk @ Vk)
print("近似矩阵 Ak:n", Ak)

代码解释:

  1. 我们首先定义了一个数据矩阵 A。
  2. 对 A 进行 SVD 分解。
  3. 选择前 k 个最大的奇异值,以及对应的左奇异向量和右奇异向量。
  4. 构建近似矩阵 Ak。
  5. 打印降维后的表示 Uk*Sk 和 Sk*VkT,以及近似矩阵 Ak。

SVD 的应用:推荐系统

SVD 在推荐系统中有着广泛的应用,尤其是在协同过滤中。

假设我们有一个用户-物品评分矩阵 R,其中 R[i][j] 表示用户 i 对物品 j 的评分。 矩阵 R 可能非常稀疏,因为用户不可能对所有物品都进行评分。

我们可以使用 SVD 对 R 进行分解,然后使用分解后的矩阵来预测用户未评分的物品的评分。

具体步骤如下:

  1. 对评分矩阵 R 进行 SVD 分解: R = UΣVᵀ
  2. 选择前 k 个最大的奇异值,以及对应的左奇异向量和右奇异向量。
  3. 使用分解后的矩阵来预测用户 i 对物品 j 的评分: R[i][j] ≈ U[i, :] @ Σ @ V[:, j]
import numpy as np

# 用户-物品评分矩阵 (缺失评分用 0 表示)
R = np.array([[5, 3, 0, 1],
              [4, 0, 0, 1],
              [1, 1, 0, 5],
              [1, 0, 0, 4],
              [0, 1, 5, 4]])

# 进行奇异值分解
U, S, V = np.linalg.svd(R)

# 选择前 k 个最大的奇异值 (例如,k=2)
k = 2
Uk = U[:, :k]
Sk = np.diag(S[:k])
Vk = V[:k, :]

# 预测用户 1 对物品 3 的评分
user_id = 0  # 用户ID 从0开始
item_id = 2  # 物品ID 从0开始
predicted_rating = Uk[user_id, :] @ Sk @ Vk[:, item_id]

print("预测的用户", user_id+1, "对物品", item_id+1, "的评分:", predicted_rating)

# 完整预测所有缺失值(将原始矩阵中的0替换为预测值)
R_predicted = Uk @ Sk @ Vk
R_filled = R.copy()
for i in range(R.shape[0]):
    for j in range(R.shape[1]):
        if R[i, j] == 0:
            R_filled[i, j] = R_predicted[i, j]

print("填充后的矩阵:n", R_filled)

代码解释:

  1. 我们首先定义了一个用户-物品评分矩阵 R。
  2. 对 R 进行 SVD 分解。
  3. 选择前 k 个最大的奇异值,以及对应的左奇异向量和右奇异向量。
  4. 使用分解后的矩阵来预测用户 1 对物品 3 的评分。
  5. 循环遍历整个矩阵,将所有评分0的位置替换为预测评分。

需要注意的是:

  • 实际应用中,评分矩阵通常非常稀疏,直接使用 SVD 可能会导致性能问题。可以使用一些优化的 SVD 算法,例如 Truncated SVD。
  • 在推荐系统中,通常需要对数据进行一些预处理,例如填充缺失值、归一化评分等。
  • SVD 只是推荐系统中的一种技术,还可以结合其他技术,例如基于内容的推荐、混合推荐等。

表格总结:特征分解与 SVD 的比较

特征 特征分解 (Eigen Decomposition) 奇异值分解 (Singular Value Decomposition – SVD)
适用矩阵 方阵 任意矩阵
分解形式 A = VΛV⁻¹ A = UΣVᵀ
特征向量/奇异向量 特征向量 左奇异向量 (U), 右奇异向量 (V)
特征值/奇异值 特征值 奇异值 (Σ)
应用 PCA降维,图算法等 降维,推荐系统,图像压缩等
是否要求矩阵可逆 V 需要可逆 无要求
与数据矩阵的关系(降维时) 基于协方差矩阵 直接分解原矩阵

4. 实际应用中的考量

在实际应用中,特征分解和SVD都需要考虑一些性能和精度的问题。

  • 大规模矩阵: 对于大规模矩阵,直接进行特征分解或SVD的计算成本非常高。 可以使用一些优化的算法,例如Lanczos算法, Power Iteration等。
  • 稀疏矩阵: 对于稀疏矩阵,可以使用专门针对稀疏矩阵的SVD算法,例如IRLBA (implicitly restarted Lanczos bidiagonalization algorithm)。
  • 增量更新: 当数据发生变化时,重新计算分解结果的成本很高。 可以使用一些增量更新的算法,例如Online SVD。
  • 数值稳定性: 在进行特征分解和SVD时,可能会遇到数值稳定性问题。 可以使用一些正则化技术来提高数值稳定性。

特征分解与SVD:总结

特征分解只能用于方阵,而SVD可以用于任意矩阵。两者都可以用于降维,在推荐系统中SVD应用广泛。

降维与推荐:核心要点

无论是特征分解还是SVD,降维的核心都是找到数据中最主要的成分;推荐系统则利用降维后的信息,预测用户对未评分物品的喜好。

实际应用的优化:性能与稳定性

实际应用中,要考虑大规模矩阵、稀疏矩阵以及数值稳定性等问题,选择合适的优化算法和技术。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注