《机器学习实战》第14章学习笔记(数据约简工具---SVD)

一、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))
2.2.2 结果显示

猜你喜欢

转载自blog.csdn.net/LZH_12345/article/details/80384382