前言
本节用Python实现奇异值分解SVD算法,重点介绍了它在推荐系统以及图像压缩方面的应用。
导航
一、SVD介绍
奇异值分解(Singular Value Decomposition,SVD)是提取信息的强大工具。它可以用来简化数据,去除噪声,提高算法的结果。它的思想是利用较小的数据来表示原始的数据集。
在很多情况下,数据中的一小部分包含了数据集的大部分信息,其他信息多为噪声或者无关信息。利用矩阵分解,就可以找到关键信息,进而实现原始矩阵的简化。
SVD是一种矩阵分解方法,它将原始数据集Data分成 U U U、 Σ \Sigma Σ和 V T V^T VT三个矩阵:
D a t a m ∗ n = U m ∗ m ∗ Σ m ∗ n ∗ V n ∗ n T Data_{m*n}=U_{m*m} *\Sigma_{m*n}*V^T_{n*n} Datam∗n=Um∗m∗Σm∗n∗Vn∗nT
其中, Σ \Sigma Σ是一个对角矩阵,对角元素是按照从大到小排列,称为奇异值。在日常应用中,我们只取部分奇异值,然后将剩余奇异值全取0,构建新的矩阵,进而实现数据的简化。
二、用Python实现SVD
我们使用numpy.linalg
包来实现SVD分解。例如我们想对[[1, 1], [7, 7]]这个矩阵进行分解,代码如下:
import numpy
U, Sigma, VT = numpy.linalg.svd([[1, 1], [7, 7]])
代码非常简单。我们发现,Sigma矩阵不是以2*2的形式返回,而是直接以行向量的形式返回。这是因为Sigma矩阵除对角线外其他值均为0,因此为了节省空间,仅返回对角线元素。
同样,对于接下来的一个矩阵,我们可以得到奇异值矩阵为:
我们发现前3个奇异值比其他的大了很多,因此我们不妨只取前3个奇异值,构建一个3*3的奇异值矩阵Sig3
,然后构建原矩阵的相似矩阵。 Σ \Sigma Σ是3*3,因此矩阵 U U U应取前3列即m*3,矩阵 V T V^T VT取前3行即3*n,得到相似矩阵a。
a = U [ : , : 3 ] ∗ S i g 3 ∗ V T [ : 3 , : ] a=U[:, :3]*Sig3*V^T[:3, :] a=U[:,:3]∗Sig3∗VT[:3,:]
import numpy
def loadExData():
return[[1, 1, 1, 0, 0],
[2, 2, 2, 0, 0],
[1, 1, 1, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 0, 2, 2],
[0, 0, 0, 3, 3],
[0, 0, 0, 1, 1]]
if __name__ == '__main__':
Data = loadExData()
U, Sigma, VT = numpy.linalg.svd(Data)
print('左奇异矩阵为:')
print(U)
print('***'*20)
print('奇异值矩阵为:')
print(Sigma)
print('***' * 20)
print('右奇异矩阵为:')
print(VT)
print('***' * 20)
Sig3 = numpy.mat([[Sigma[0], 0, 0], [0, Sigma[1], 0], [0, 0, Sigma[2]]])
print('最后的近似矩阵为:')
print(U[:, :3]*Sig3*VT[:3, :])
以上代码就实现了矩阵的简化。如何确立保留奇异值的个数?典型的做法是保留矩阵90%的能量信息。计算总的能量信息(所有奇异值的平方和),然后保留的奇异值平方和累积到总信息的90%即可。
三、应用1:推荐系统
奇异值分解的代码很简单,大部分步骤进行了封装,我们直接调用包即可。但是它的用法却不简单,首先介绍奇异值分解在推荐系统中的应用。
(一)推荐思想介绍
在这里首先介绍一下个性化推荐的相关内容,参考了本科时老师讲的内容。当时听得有些模模糊糊,但是现在看来老师讲得非常清晰!
当我们设计一个个性化推荐系统时,流程为 ①数据:主要进行数据的收集 ②召回:根据用户的行为数据,从海量数据中粗选一批待推荐的内容 ③排序:在召回的基础上进行更加精准的排序,对于每一个内容进行精确打分 ④结果:得到最终的推荐结果。
同时,设计推荐系统时,同样要从四个方面来考虑:①数据整合:哪些数据可以反映用户的兴趣? ②兴趣预测:用哪些方法预测用户兴趣? ③产品推荐:推荐哪些产品满足用户兴趣? ④推荐评估:推荐的产品是否满足了用户的兴趣?
对于用户兴趣预测的方法有很多,在这里介绍基于内容的(content-based)推荐以及协同过滤(collaborative filtering)推荐。
- content-based 计算产品之间的相似度,根据用户的购买历史推荐相似的产品。例如音乐推荐、新闻推荐可以采用这种方式。
- collaborative filtering 协同过滤是采用群体的智慧。在进行推荐时不聚焦于单个个体,而往往关注某种趋势或者人群中共性的部分。协同过滤一般是在海量的用户中发掘出一小部分和你品位比较类似的,在协同过滤中,这些用户成为邻居,然后根据他们喜欢的其他东西组织成一个排序的目录作为推荐给你。协同过滤又分为基于用户(User-based)和基于项目(Item-based)。
接下来主要介绍协同过滤。
content-based
和Item-based
我之前一直傻傻分不清,都需要计算物品之间的相似度,那它们有什么区别呢?content-based
是根据物品本身的属性来计算相似度,例如它们的用途、价格、使用方式等;而Item-based
是基于用户的选择(或者打分)来计算相似度,许多人在选择商品A的同时也选择了商品B,我们就认为A与B是相似的。经典的“啤酒与尿布”,通过基于内容的方法就很难找到它们之间属性的相似,但是通过协同过滤就可以发现物品被同时选择的几率较大,可以进行捆绑销售。
在协同过滤时,我们还应该考虑是采用基于物品相似度还是用户相似度?对于一个商店来说,可以最多有几千件商品。但是用户可能成千上万。如果用户很多,我们倾向于采用基于物品相似度的方法。
在这里推荐一篇文章,看完 什么是协同过滤推荐算法?你一定会对它有更深的理解。
(二)相似度计算
在上文的介绍中,涉及到了对于用户或者物品之间的相似度计算。作者介绍了三种相似度计算方法:
- 欧式距离:最常见的距离计算公式,为每一个维度距离之差的平方和再开根号。
- 余弦相似度:用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的度量。
- 皮尔逊系数:cos计算的改进,在计算之前两个向量都先进行中心化处理。相对于欧式距离与余弦距离,它对于用户评分的量级不敏感,原因是加入了标准化的处理。在计算时只考虑用户共同评分的项,忽略只有一个用户评分的项对相似性计算的影响。
下面给出了具体计算的案例,在计算时,对于每一种方法的结果都进行了归一化处理。解释一下矩阵的含义,称之为用户-商品矩阵,矩阵的每一行代表一个用户,每一列代表一件商品,中间的数值代表用户对于商品的评分。由于我们采用的是基于项目的协同过滤,因此要计算列与列之间的相似度。
import numpy as np
import numpy.linalg as la
def loadExData():
return[[1, 1, 1, 0, 0],
[2, 2,