文章目录
0. 前言
本文总结于Peter Harrington的《Machine Learning in Action》的第14章-利用SVD简化数据。
1. 关于SVD与特征值分解
SVD(Singular Value Decomposition, SVD)奇异值分解。在说及SVD之前,我们需要了解一下特征值分解。学过线性代数都知道,如果一个方阵可以拆成如下的形式:
则
称为特征值,而v称为特征向量,如果这n个特征向量线性无关,那么矩阵就可以分解为如下的形式:
其中,W是由n个特征向量组成的矩阵,
是由特征值组成的对角阵。通常我们会将W的n个向量进行标准化,即
,此时的n个特征向量为标准正交基,满足
, 于是矩阵A就可以分解为:
这就是矩阵的特征值分解。
这里我们注意到,特征值分解用于方阵,但是实际问题中,往往不是方阵,所以为了对非方阵的矩阵进行分解,就需要我们的SVD。这里我们假设矩阵A是一个m*n的矩阵。
- 如果
,则
是一个m*m的方阵,对这个方阵进行特征值分解有:
会得到m个特征值和m个特征向量,这m个特征向量组成一个矩阵U,我们称之为左奇异矩阵 - 同理,如果
,则
是一个n*n的方阵,对这个方阵进行特征值分解有:
会得到n个特征值和n个特征向量,这n个特征向量组成一个矩阵V,我们称之为右奇异矩阵 - 将右奇异值矩阵对应的特征值进行开方,得到的结果称为奇异值,将这些奇异值组成对角矩阵
于是,一个非方阵
,可以分解为:
这就是奇异值分解的原理。
2. 基于SVD的推荐系统
假如我们要对某个用户喜欢的电影进行预测,推荐系统发现一部电影该用户还没有看过,它就会计算该电影与用户看过的电影之间的相似度,如果相似度高,那么系统就会认为该用户也喜欢这部电影。所以,基于SVD的推荐系统的第一件事就是定义相似度。在推荐系统的中,相似度的定义一般有三种方式:欧式距离、皮尔逊相关系数和余弦距离。
2. 1 相似度
(1) 基于欧式距离的相似度:
如果距离越远,则相似度越小;距离越近,相似度越高。用python可以写为:
from numpy import linalg as la
def ecludSim(inA,inB):
# 计算基于欧式距离的相似度
return 1.0/(1.0 + la.norm(inA - inB))
其中la.norm()
计算范数,默认为2范数。
(2) 基于皮尔逊相关系数的相似度:
def pearsSim(inA,inB):
# 计算基于皮尔逊相关系数的相似度
if len(inA) < 3 : return 1.0
return 0.5+0.5*corrcoef(inA, inB, rowvar = 0)[0][1]
(3) 基于余弦距离的相似度“:
def cosSim(inA,inB):
# 计算基于余弦cos距离的相似度
num = float(inA.T*inB)
denom = la.norm(inA)*la.norm(inB)
return 0.5+0.5*(num/denom)
2.2 使用SVD对未打分的物品进行打分
先看代码:
def svdEst(dataMat, user, simMeas, item):
'''
采用SVD奇异值分解的评价函数
:param dataMat: 数据矩阵
:param user: 用户【行数】
:param simMeas: 相似度计算函数
:param item: 待评价的物品
:return:
'''
# 获取物品的数量
n = shape(dataMat)[1]
simTotal = 0.0; ratSimTotal = 0.0
# 奇异值分解
U,Sigma,VT = la.svd(dataMat)
# 选取包含90%能量的奇异值,在这里是前4个Sigma,构成对角矩阵
Sig4 = mat(eye(4)*Sigma[:4]) #arrange Sig4 into a diagonal matrix
# 将原数据转化到低维空间中
xformedItems = dataMat.T * U[:,:5] * Sig4.I #create transformed items
for j in range(n):
# 取出user对物品j的打分
userRating = dataMat[user,j]
# 如果打分为0或者j等于待打分物品 ,就跳过
if userRating == 0 or j==item: continue
# 计算物品j与物品item在低维空间中的相似度
similarity = simMeas(xformedItems[item,:].T,\
xformedItems[j,:].T)
print ('the %d and %d similarity is: %f' % (item, j, similarity))
simTotal += similarity
ratSimTotal += similarity * userRating
if simTotal == 0: return 0
# 返回关于物品item的打分
else: return ratSimTotal/simTotal
(1)首先获取物品的数量,然后进行奇异值分解,选取包含90%能量以上的奇异值。即计算 中前多少个奇异值之和能达到所有奇异值和的90%。这里选择的是5,这个5是基于测试数据:
def loadExData2():
return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
[0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
[0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
[3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
[5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
[0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
[4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
[0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
[0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
[0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
[1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]
# 对其进行奇异值分解有:
dataMat = mat(loadExData2())
U, Sigma, VT = la.svd(dataMat)
print('Sigma = ',Sigma)
print('Ratio = ',sum( Sigma[:5]) / sum(Sigma))
Sigma = [15.77075346 11.40670395 11.03044558 4.84639758 3.09292055 2.58097379
1.00413543 0.72817072 0.43800353 0.22082113 0.07367823]
Ratio = 0.9014360861521987
(2)然后将原数据矩阵转化到低维空间中:
xformedItems = dataMat.T * U[:,:5] * Sig4.I
这句话很多人都不理解,为什么是这个样子的。我后来在机器学习实战的P264中代码对应的公式推导中找到了答案,其实就是一个很简单的线性变换:
其中
就是A在低维空间中的映射。现在只需要反解出来就可以了:
(3)对于每一个物品,计算与物品item在低维空间的中的相似度,遇到user打分为0或者j==item时就跳过。将这些相似度进行累加,对相似度与rating的乘积也进行累加,最后返回两者的比值:
这个比值其实就是预测用户user在物品item上的打分,这个结果就是将用户对于其他物品打分的分值进行加权平均。而这个权值就是相似度。
2.3 基于SVD的推荐函数
有了上述的打分函数,我们只需要遍历某个user没有打分的物品,使用打分函数 进行打分,最后返回得分最高的N个物品,推荐给用户user。
def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst):
'''
推荐函数
:param dataMat: 数据集
:param user: 待推荐用户
:param N: 推荐的个数
:param simMeas: 相似度函数
:param estMethod: 打分函数
:return: 返回N个推荐的物品及其预测的得分
'''
# 找到用户user没有打分的物品
unratedItems = nonzero(dataMat[user, :].A == 0)[1] #find unrated items
# 如果没有未评价的物品,直接返回
if len(unratedItems) == 0: return 'you rated everything'
itemScores = []
# 对于用户user没有评价的每个物品
for item in unratedItems:
# 计算该物品的得分
estimatedScore = estMethod(dataMat, user, simMeas, item)
# 把该物品及其得分加入到list中
itemScores.append((item, estimatedScore))
# 按照得分进行排序,并返回前N个物品
return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
3. 基于SVD的图像压缩
SVD除了在推荐系统上有着很好的应用,也能应用在图像压缩上。将图像矩阵进行奇异值分解SVD,然后提取能使能量大于90%的前N个奇异值极其对应的左右奇异值矩阵。原来很大的图像,就可以用简单的左奇异矩阵U和右奇异矩阵V以及奇异值 来表示,最后通过三者的乘积就可以复原图片。
4. SVD的评价
优点: 简化数据,去除噪声点,提高算法的结果;
缺点: 数据的转换可能难以理解;
适用于数据类型:数值型。
通过SVD对数据的处理,我们可以使用小得多的数据集来表示原始数据集,这样做实际上是去除了噪声和冗余信息,以此达到了优化数据、提高结果的目的。
参考资料
奇异值分解(SVD)原理与在降维中的应用
奇异值分解(SVD)原理详解
机器学习实战的P264中代码对应的公式推导
机器学习实战——SVD(奇异值分解)