SVD奇异值分解实现图片降维
目录
- SVD奇异值分解实现图片降维
- 1 SVD奇异值分解
- 1.1 概念
- 1.2 基本步骤
- 1.2.1 矩阵分解
- 1.2.2 选择奇异值
- 1.2.3 重建矩阵
- 1.2.4 降维结果
- 1.3 优缺点
- 1.3.1 优点
- 1.3.2 缺点
- 2 函数
- 2.1 函数导入
- 2.2 函数参数
- 2.3 返回值
- 2.4 通过 k 个奇异值降维
- 3 实际测试
- 3.1 原图数据
- 3.2 代码测试
1 SVD奇异值分解
1.1 概念
SVD(奇异值分解)降维是一种常用的线性降维方法,在数据科学和机器学习中有着广泛的应用。SVD的主要思想是将一个矩阵分解为三个矩阵的乘积,通过保留最重要的奇异值和对应的奇异向量,来近似原矩阵,从而达到降维的目的。
1.2 基本步骤
1.2.1 矩阵分解
给定一个m * n 的数据矩阵A,SVD将其分解为三个矩阵的乘积:
A = U*Σ *V^T
其中:
- U是一个m * n 的正交矩阵,其列向量称为左奇异向量。
- Σ是一个m * n的对角矩阵,对角线上的非负数称为奇异值,按从大到小的顺序排列。
- V^T是m * n的正交矩阵的转置,其行向量称为右奇异向量。
1.2.2 选择奇异值
奇异值在对角矩阵Σ中从大到小排列,前面的几个奇异值往往包含了矩阵中大部分的信息。通过选择前 k 个最大的奇异值( k 小于矩阵的秩),可以保留矩阵的主要特征。
1.2.3 重建矩阵
选择前k个奇异值后,可以构造一个新的对角矩阵Σk,只包含这k 个最大的奇异值,其他位置为零。然后使用U 和V 的前k 个列向量来重建矩阵:
Ak = Uk*Σk *Vk^T
其中Uk和 Vk分别是由U和 V 的前k 个列向量组成的矩阵。
1.2.4 降维结果
矩阵 Ak 即为降维后的矩阵,其维度为 m * n 降低到了m * k 。通常情况下, k 远小于 n,因此达到了降维的效果。
在使用SVD降维时,选择合适的k 非常关键,这通常需要通过实验来确定,以便在降维和保留信息之间找到平衡。
1.3 优缺点
1.3.1 优点
- 信息保留能力:SVD能够有效地提取数据的主要特征,通过保留最大的奇异值,可以最大程度地保留原始数据的方差信息。
- 灵活性:SVD降维不依赖于数据的分布,对于多种类型的数据都适用,包括非负数据、稀疏数据等。
- 简化模型:通过减少特征的数量,SVD可以帮助简化机器学习模型,减少计算量,提高模型的泛化能力,减少过拟合的风险。
- 易于理解:SVD的数学原理相对直观,分解后的奇异值和奇异向量能够提供数据结构的直观解释。
- 稳定性:SVD是一种稳定的算法,对于小的数据扰动,其结果不会产生大的变化。
1.3.2 缺点
- 计算复杂度高:对于大型矩阵,SVD的计算复杂度较高,尤其是当矩阵的维度很高时,计算时间和空间复杂度都会显著增加。
- 不适合非线性数据:SVD是一种线性降维方法,对于非线性结构的数据,其降维效果可能不如非线性降维方法(如t-SNE、Isomap等)。
- 对噪声敏感:虽然SVD相对稳定,但在某些情况下,小的奇异值可能反映了噪声或异常值,这可能导致降维过程中噪声被放大。
- 数据量要求:SVD需要将整个数据集加载到内存中进行处理,对于非常大的数据集,这可能是一个挑战。
- 特征解释性:虽然SVD可以提供数据结构的解释,但分解后的特征可能不如原始特征直观,这可能会降低模型的可解释性。
- 需要选择奇异值数量:在使用SVD降维时,需要手动或通过交叉验证等方法选择保留的奇异值数量(即降维后的维度),这个选择过程可能需要多次尝试,并且可能对最终结果有较大影响。
总的来说,SVD降维是一个强大的工具,特别适用于需要保留数据主要方差特征的场景。然而,它的应用也受到一些限制,特别是当处理大规模数据集或非线性结构的数据时。
2 函数
2.1 函数导入
from numpy.linalg import svd
2.2 函数参数
- 默认值:a, full_matrices=True, compute_uv=True, hermitian=False,一般输入a参数,其他默认
a
:一个形状为(M, N)的数组,需要被分解的矩阵。- full_matrices:布尔值,可选,默认为True。
- 如果为True,则U和V*的形状分别是(M, M)和(N, N),奇异值将被填充为一个(M, N)的对角矩阵。
- 如果为False,则U和V*的形状分别是(M, K)和(K, N),其中K = min(M, N),奇异值仍然是一个(K, )的数组。
- compute_uv:布尔值,可选,默认为True。
- 如果为True,则计算U和V*。
- 如果为False,则只计算奇异值,不计算U和V*。
- hermitian:布尔值,可选,默认为False。
- 这个参数在NumPy的较新版本中已经废弃,不再推荐使用。它用于指定矩阵是否为厄米特矩阵(即共轭转置等于自身的矩阵)。如果为True,则函数计算的是矩阵的 eigendecomposition 而非 SVD。
2.3 返回值
该函数返回三个数组:
U
:形状为(M, M)或(M, K)的数组,取决于full_matrices
参数,包含了左奇异向量。s
:形状为(K, )的数组,包含了矩阵的奇异值,奇异值是按降序排列的。Vh
:形状为(N, N)或(K, N)的数组,取决于full_matrices
参数,包含了右奇异向量的共轭转置。
2.4 通过 k 个奇异值降维
代码展示:
def pic_compress(k,pic_array):
global u,sigma,vt,sig,new_pic
# pic_array为数组
u,sigma,vt = svd(pic_array)
# 创建一个对角矩阵,对角线上的元素是前k个奇异值
sig = np.eye(k) * sigma[:k]
# 通过奇异值分解重构图像,只使用前k个奇异值
new_pic = np.dot(np.dot(u[:,:k],sig),vt[:k,:])
# 计算压缩后图像的大小
size = u.shape[0] * k + sig.shape[0] * sig.shape[1] + k * vt.shape[1]
# 返回重构后的图像和其大小
return new_pic,size
3 实际测试
3.1 原图数据
3.2 代码测试
代码展示:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from numpy.linalg import svd
def pic_compress(k,pic_array):
global u,sigma,vt,sig,new_pic
u,sigma,vt = svd(pic_array)
# 创建一个对角矩阵,对角线上的元素是前k个奇异值
sig = np.eye(k) * sigma[:k]
# 通过奇异值分解重构图像,只使用前k个奇异值
new_pic = np.dot(np.dot(u[:,:k],sig),vt[:k,:])
# 计算压缩后图像的大小
size = u.shape[0] * k + sig.shape[0] * sig.shape[1] + k * vt.shape[1]
# 返回重构后的图像和其大小
return new_pic,size
img = Image.open('at1_gry.png')
ori_img = np.array(img)
new_img,size = pic_compress(50,ori_img)
print(f'original size:{ori_img.shape[0]*ori_img.shape[1]}')
print(f'compress size:{size}')
# 创建一个包含两个子图的图形
fig,ax = plt.subplots(1,2)
# 在第一个子图中显示原始图像
ax[0].imshow(ori_img,cmap='gray')
# 设置第一个子图的标题为before compress
ax[0].set_title('before compress')
ax[1].imshow(ori_img,cmap='gray')
ax[1].set_title('after compress')
plt.show()
运行结果:
可以看到降维后图片比降维前模糊