赞
踩
下面的代码运行环境为python3.6
+jupyter5.4
这里暂时用numpy自带的svd函数做图像压缩。
①读取图片
- %matplotlib inline
- import matplotlib.pyplot as plt
- import matplotlib.image as mpimg
- import numpy as np
-
- img_eg = mpimg.imread("../img/beauty.jpg")
- print(img_eg.shape)
图片的大小是600×400×3
②奇异值分解
- img_temp = img_eg.reshape(600, 400 * 3)
- U,Sigma,VT = np.linalg.svd(img_temp)
我们先将图片变成600×1200,再做奇异值分解。从svd
函数中得到的奇异值sigma
它是从大到小排列的。
③取前部分奇异值重构图片
- # 取前60个奇异值
- sval_nums = 60
- img_restruct1 = (U[:,0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums,:])
- img_restruct1 = img_restruct1.reshape(600,400,3)
-
- # 取前120个奇异值
- sval_nums = 120
- img_restruct2 = (U[:,0:sval_nums]).dot(np.diag(Sigma[0:sval_nums])).dot(VT[0:sval_nums,:])
- img_restruct2 = img_restruct2.reshape(600,400,3)
将图片显示出来看一下,对比下效果
- fig, ax = plt.subplots(1,3,figsize = (24,32))
-
- ax[0].imshow(img_eg)
- ax[0].set(title = "src")
- ax[1].imshow(img_restruct1.astype(np.uint8))
- ax[1].set(title = "nums of sigma = 60")
- ax[2].imshow(img_restruct2.astype(np.uint8))
- ax[2].set(title = "nums of sigma = 120")
可以看到,当我们取到前面120个奇异值来重构图片时,基本上已经看不出与原图片有多大的差别。
对于奇异值,它跟我们特征分解中的特征值类似,在奇异值矩阵中也是按照从大到小排列,而且奇异值的减少特别的快,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。
也就是说,我们也可以用最大的k个的奇异值和对应的左右奇异向量来近似描述矩阵。
也就是说:
由于这个重要的性质,SVD可以用于PCA降维,来做数据压缩和去噪。也可以用于推荐算法,将用户和喜好对应的矩阵做特征分解,进而得到隐含的用户需求来做推荐。同时也可以用于NLP中的算法,比如潜在语义索引(LSI)。
下面的X就是上面的A矩阵,这里是从别的blog那copy过来的,就不修改了,能理解就好。
从上面的图片的压缩结果中可以看出来,奇异值可以被看作成一个矩阵的代表值,或者说,奇异值能够代表这个矩阵的信息。当奇异值越大时,它代表的信息越多。因此,我们取前面若干个最大的奇异值,就可以基本上还原出数据本身。
如下,可以作出奇异值数值变化和前部分奇异值和的曲线图,如下图所示
====================图片缺失==================
从上面的第1个图,可以看出,奇异值下降是非常快的,因此可以只取前面几个奇异值,便可基本表达出原矩阵的信息。从第2个图,可以看出,当取到前100个奇异值时,这100个奇异值的和已经占总和的95%左右。
最后,还有一点需要提到的是,如果自己想不调用np.linalg.svd
函数,手动实现奇异值分解的话,单纯利用第2小节的内容实现,有点不够,有个问题需要注意。这里暂时不多做讨论了,大家有兴趣可以看我下面分享的《SVD(奇异值分解)Python实现》,重点可以看看其中SVD算法实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。