一、SVD基本原理
提取这些信息的方法称为奇异值分解(Singular Value Decomposition, SVD )。
在很多情况下,数据中的一小段携带了数据集中的大部分信息,其他信息则要么是噪声,要么就是毫不相关的信息。在线性代数中还有很多矩阵分解技术。矩阵分解可以将原始矩阵表示成新的易于处理的形式,这种新形式是两个或多个矩阵的乘积。我们可以将这种分解过程想象成代数中的因子分解。
取前r个非零奇异值,可以还原原来的矩阵Data,即前r个非零奇异值对应的奇异向量代表了矩阵Data的主要特征,可以表示为
SVD优缺点:
二、SVD的应用
2.1 隐形语义索引
SVD的历史巳经超过上百个年头, 但是最近几十年随着计算机的使用, 我们发现了其更多的使用价值。最早的SVD应用之一就是信息检索。我们称利用SVD的方法为隐性语义索引(Latent Semantic Indexing, LSI)或隐性语义分析(Latent Semantic Analysis, LSA)。
在LSI中,一个矩阵是由文档和词语组成的。当我们在该矩阵上应用SVD时,就会构建出多 个奇异值。这些奇异值代表了文档中的概念或主题, 这一特点可以用于更高效的文档搜索。在词语拼写错误时, 只基于词语存在与否的简单搜索方法会遇到问题。简单搜索的另一个问题就是同义词的使用。这就是说,当我们查找一个词时,其同义词所在的文档可能并不会匹配上。如果我们从上千篇相似的文档中抽取出概念,那么同义词就会映射为同一概念。
2.2 推荐系统
SVD的另一个应用就是推荐系统。简单版本的推荐系统能够计算项或者人之间的相似度。 更先进的方法则先利用SVD从数据中构建一个主题空间, 然后再在该空间下计算其相似度。
2.2.1 代码实现
# -*- coding: utf-8 -*-
"""
Created on Fri May 18 20:47:04 2018
@author: lizihua
"""
from numpy import *
from numpy import linalg as la
#加载数据1
def loadExData():
return[[4, 4, 0, 2, 2],
[4, 0, 0, 3, 3],
[4, 0, 0, 1, 1],
[1, 1, 1, 2, 0],
[2, 2, 2, 0, 0],
[5, 5, 5, 0, 0],
[1, 1, 1, 0, 0]]
#计算相似度的三种方法,其中假定inA和inB都是列向量
#欧式距离计算相似度
def euclidSim(inA,inB):
return 1.0/(1.0 +la.norm(inA - inB))
#皮尔逊相关系数计算相似度
def pearsSim(inA,inB):
if len(inA) < 3:
return 1.0
return 0.5 +0.5*corrcoef(inA, inB, rowvar = 0)[0][1]
#余弦相似度
def cosSim(inA,inB):
num = float(inA.T*inB)
denom = la.norm(inA)*la.norm(inB)
return 0.5+0.5*(num/denom)
#标准的评分估计
#计算在给定相似度计算方法的条件下,用户对物品的估计评分值
#输入参数:dataMat:数据矩阵, user:用户编号, simMeas:相似度计算方法, item:物品编号
def standEst(dataMat, user, simMeas, item):
n = shape(dataMat)[1] #item数目
simTotal = 0.0;ratSimTotal = 0.0 #初始化
#遍历user对各个item的评分,若评分为0,则跳过该物品
for j in range(n):
userRating = dataMat[user,j]
if userRating ==0:
continue
#寻找两个用户都评级的物品,并将同时对item和j物品同时进行评级的用户编号返回给overLap
overLap = nonzero(logical_and(dataMat[:,item].A > 0,dataMat[:,j].A > 0))[0]
#若无重合元素,则相似度为0
if len(overLap) == 0:
similarity = 0
else: #反之,则基于这些重合元素计算相似度
similarity = simMeas(dataMat[overLap,item],dataMat[overLap,j])
print("the %d and %d similarity is: %f" % (item,j,similarity))
#相似度会不断累加
simTotal += similarity
#评分相似总和 = 相似度和评分的乘积之和
ratSimTotal += similarity *userRating
if simTotal == 0:
return 0
else: #对相似度评分的乘积进行归一化,使得结果在0-5之间
return ratSimTotal/simTotal
#基于SVD的评分估计
def svdEst(dataMat, user, simMeas, item):
n = shape(dataMat)[1] #物品数目
simTotal = 0.0; ratSimTotal = 0.0 #初始化
U,Sigma,VT = la.svd(dataMat) #使用SVD降维
Sig4 = mat(eye(4)*Sigma[:4]) #建立对角矩阵
xformedItems = dataMat.T * U[:,:4] * Sig4.I #构建转换后的物品
#print(xformedItems)
for j in range(n):
userRating = dataMat[user,j]
if userRating == 0 or j==item: continue
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
else: return ratSimTotal/simTotal
# 推荐引擎,产生最高的N的推荐结果,默认为3个
def recommend(dataMat, user, N=3,simMeas = cosSim,estMethod =standEst):
#对给定用户建立一个未评分的物品列表,(因为要对该用户未评分的物品进行评分估计)
#若不存在未评分物品,则直接退出函数
unratedItems = nonzero(dataMat[user,:].A == 0)[1]
if len(unratedItems) == 0:
return "you rated everything"
itemScores = []
#遍历未评分的物品,计算相似度评分
for item in unratedItems:
estimatedScore = estMethod(dataMat, user,simMeas, item)
itemScores.append((item,estimatedScore))
#按itemScores中第1个元素(从0开始)从大大小排列itemScores,然后返回前N个
return sorted(itemScores,key = lambda p : p[1],reverse = True)[:N]
if __name__ == "__main__":
myMat = mat(loadExData())
print("欧式距离相似度:",euclidSim(myMat[:,0],myMat[:,4]))
print("余弦相似度:",cosSim(myMat[:,0],myMat[:,4]))
print("皮尔逊相似度:",pearsSim(myMat[:,0],myMat[:,4]))
print(recommend(myMat,2))
#print(recommend(myMat,2,simMeas= euclidSim))
#print(recommend(myMat,2,simMeas= pearsSim))
U,Sigma,VT = la.svd(mat(loadExData2()))
print("Sigma:\n",Sigma)
Sig2 = Sigma**2
print("总能量:\n",sum(Sig2))
print("总能量d的90%:\n",sum(Sig2)*0.9)
#前3个元素包含能量超过总体的90%,因此,我们将11维转化为3维的矩阵
print("前三个元素的总能量:",sum(Sig2[:3]))
print(recommend(myMat,1,estMethod=svdEst))