与PCA一样的学习过程,在学习SVD时同样补习了很多的基础知识,现在已经大致知道了PCA的应用原理,SVD个人感觉相对要难一点,但主要步骤还是能勉强理解,所以这里将书本上的知识和个人的理解做一个记录。主要关于(SVD原理、降维公式、重构原矩阵、SVD的两个实际应用),当然矩阵的分解和相对的公式我会给出写的更好的文章对于说明(个人基础有限)。
(最后给出两条SVD最重要的公式)
- 优点:简化数据,去除噪声点,提高算法的结果;
- 缺点:数据的转换可能难以理解;
- 适用于数据类型:数值型。
通过SVD对数据的处理,我们可以使用小得多的数据集来表示原始数据集,这样做实际上是去除了噪声和冗余信息,以此达到了优化数据、提高结果的目的。
隐形语义索引:最早的SVD应用之一就是信息检索,我们称利用SVD的方法为隐性语义检索(LSI)或隐形语义分析(LSA)
推荐系统:SVD的另一个应用就是推荐系统,较为先进的推荐系统先利用SVD从数据中构建一个主题空间,然后再在该空间下计算相似度,以此提高推荐的效果。
SVD与PCA不同,PCA是对数据的协方差矩阵进行矩阵的分解,而SVD是直接在原始矩阵上进行的矩阵分解。并且能对非方阵矩阵分解,得到左奇异矩阵U、sigma矩阵Σ、右奇异矩阵VT。
奇异性分解可以将一个矩阵分解成3个矩阵、、,其中U、VT都是单式矩阵(unitary matrix),Σ是一个对角矩阵,也就是说只有对角线有值。对角元素称为奇异值,它们对应了原始矩阵Data的奇异值,如下:
[[2 0 0]
[0 3 0]
[0 0 4]
[0 0 0]]
一般奇异值我们只选择某一部分,选择的规则很多种,主要的一种为:
选择奇异值中占总奇异值总值90%的那些奇异值。(下面有演示如何选择)
SVD分解公式如下(类似于因式分解):
=
图形化表示奇异值分解:
在PCA中我们根据协方差矩阵得到特征值,它们告诉我们数据集中的重要特征,Σ中的奇异值亦是如此。奇异值和特征值是有关系的,这里的奇异值就是矩阵特征值的平方根。
SCV实现的相关线性代数,但我们无需担心SVD的实现,在Numpy中有一个称为线性代数linalg的线性代数工具箱能帮助我们。下面演示其用法对于一个简单的矩阵:
[[1 1]
[1 7]]
通过简单的使用该工具就能得到运算的结果,所以我们着重应该理解的应该是这些结果的含义以及后续对它们的使用,下面通过推荐系统这个示例来进行实际的操作(数据集降维、重构数据集)。
我之前在集体编程智慧中学习了该算法,大致有两种方法来实现:
- 基于用户的协作型过滤
- 基于物品的协作型过滤
两种方法大致相同,但是在不同的环境下,使用最佳的方法能最大化的提升算法的效果。如下图(后面的示例数据)所示,对两样商品直接的距离进行计算,这称为基于物品的相似度。而对行与行(用户之间)进行距离的计算,这称为基于用户的相似度。到底该选用那种方法呢?这取决与用户或物品的数量,基于物品相似度的计算时间会随着物品数量的增加而增加。基于用户相似度则取决于用户数量,例如:一个最大的商店拥有大概100000种商品,而它的用户可能有500000人,这时选择基于物品相似度可能效果好很多。
用上面的数据解释了如何选择基于协同过滤,下面使用基于物品相似度的方法来构建推荐系统(先直接使用原始矩阵来构建,然后再将处理函数换为SVD的处理函数,以便作比较)。
(说明:数据间的距离计算采用余玄相似、欧式距离、皮尔逊相关度其中任一种,这里不再解释,提供链接自行学习)
代码:
上面代码种用了三种计算距离的函数,经过测试后使用其中一种便可以了。然后对于物品评分函数中的nonzero(logical_and)不是很明白的请看这篇专门讲解的文章。以上为普通的处理方式,下面使用SVD来做基于物品协同过滤。
SVD方法,用下面函数(svdEst)来替换上面的物品评价函数(standEst)即可,并且这里使用更复杂的数据集:
上面的之所以使用4这个数字,是因为通过预先计算得到能满足90%的奇异值能量的前N个奇异值。判断计算如下:
在函数svdEst中使用SVD方法,将数据集映射到低纬度的空间中,再做运算。其中的xformedItems = dataMat.T*U[:,:4]*Sig4.I可能不是很好理解,它就是SVD的降维步骤,通过U矩阵和Sig4逆矩阵将商品转换到低维空间(得到 商品行,选用奇异值列)。
以上是SVD的一个示例,但是对此有几个问题:
- 我们不必在每次评分是都做SVD分解,大规模数据上可能降低效率,可以在程序调用时运行一次,在大型系统中每天运行一次或频率不高,还要离线运行;
- 矩阵中有很多0,实际系统中0更多,可以通过只存储非0元素来节省空间和计算开销;
- 计算资源浪费来自于相似度的计算,每次一个推荐时都需要计算多个物品评分(即相似度),在需要时此记录可以被用户重复使用。实际中,一个普遍的做法是离线计算并保存相似度得分,这一点在之前学习集体编程智慧中有说明。
这里不采用书中的例子来讲解,因为无趣所以这里换作我们的男神来做一个简单的SVD图片压缩作为一个示例:
首先放上男神图片:
基于SVD图片压缩原理其实很简单,图片其实就是数字矩阵,通过SVD将该矩阵降维,只使用其中的重要特征来表示该图片从而达到了压缩的目的。
直接上代码:
原图片为870x870,保存像素点值为870x870 = 756900,使用SVD,取前50个奇异值则变为:
存储量大大减小,仅50个奇异值就已经能很好的反应原数据了。
值得一提的是,奇异值从大到小衰减得特别快,在很多情况下,前 10% 甚至 1% 的奇异值的和就占了全部的奇异值之和的 99% 以上了。这对于数据压缩来说是个好事。下面这张图展示了本例中奇异值和奇异值累加的分布(参考博客下面附上链接):
SVD两个个人觉得最重要的计算步骤这里说一下:
- 数据集降维: 这里的sigma为对角矩阵(需要利用原来svd返回的sigma向量构建矩阵,构建需要使用count这个值)。U为svd返回的左奇异矩阵,count为我们指定的多少个奇异值,这也是sigma矩阵的维数。
- 重构数据集: 这里的sigma同样为对角矩阵(需要利用原来svd返回的sigma向量构建矩阵,构建需要使用count这个值),VT为svd返回的右奇异矩阵,count为我们指定的多少个奇异值(可以按能量90%规则选取)。
以上为两个个人觉得最重要的公式,如果有不明白的可以参考上面的代码,有使用到这两个公式。(虽然不负责任,但还是说一下:如果你不能立刻理解SVD的原理,可以先记下这两个公式来使用,后面有时间了在来深入了解哈哈)
————————————————————
更新于2019/12/24:关于后续男神图片的处理问题
由于有不少同学私信或提问关于后续图像处理报错的问题,所以抽了点时间对这个问题做一个回复解决(工作比较忙十分抱歉)
正文:
首先这个问题我之前也没有发现,将图片有 CSDN 上传后会发生改变,灰度图像将变更为三通道的彩色图像,这是问题的所在。导致使用 np.mat(data) 转换为矩阵时出现报错:ValueError: shape too large to be a matrix。
补充一个知识点:np.matrix 最多只能处理二维数据,如果试图构建三维或以上矩阵将无法处理。
解决方法:所以如果你是从我的文章中复制的图像做 SVD 处理,那这是一个 (870, 870, 3) 的三维矩阵,需要在读取图像是指定灰度图,即:data = io.imread(path, as_gray=True),得到 (870, 870) 的二维矩阵,这样既可以做后续的处理了。
我这里做了尝试可以正常处理,如果还有问题可以在下面评论指出大家讨论,有时间我也会一起解决。
————————————————————
参考书籍:《机器学习实战》