机器学习实战(十四)——利用SVD简化数据

本文介绍了SVD(奇异值分解)在数据压缩和推荐系统中的应用。SVD通过去除噪声和冗余信息,实现数据的降维,常用于隐性语义索引和推荐系统。在推荐系统中,SVD用于计算物品或用户的相似度,通过欧氏距离、皮尔逊相关系数和余弦相似度等方式评估。此外,SVD还可用于图像压缩,显著减少存储需求。文章还展示了如何使用Python实现SVD并应用于餐馆菜肴推荐及图像压缩的实例。

机器学习实战(十四)——利用SVD简化数据

一、SVD的应用

SVD(奇异值分解),可以实现用小得多的数据集来表示原始数据集,达到去除噪声和冗余信息,以及压缩数据的目的。

SVD的主要应用场景有:

  • 隐性语义索引:利用奇异值分解可以将文档中的概念或者主题提取出来
  • 推荐系统:SVD可以从数据中构建一个主题空间,再在该主题空间下计算项的相似度达到推荐的目的

二、矩阵分解

在大多数情况下,原始数据集中很可能一小段就携带了大部分的信息,而其他的信息要么是噪声要么是毫不相关的信息,因此利用矩阵分解技术,可以将原始矩阵表示成新的易于处理的形式。在统计学习方法一书中对SVD方法的定义如下:

在这里插入图片描述

在这里插入图片描述

回顾上一章PCA中,得到的是矩阵的特征值,而此处得到的是矩阵的奇异值,实际上奇异值就是矩阵 A T A A^TA ATA 的特征值的平方根。并且,由于不要求矩阵 A A A是方阵,因此此处可能 m ! = n m!=n m!=n。另外,可能在某一个奇异值 σ r \sigma_r σr之后的奇异值都是0,也就是说数据集中仅有 r r r个重要特征。,而其余的特征都是噪声或者冗余特征。

三、利用Python实现SVD

在numpy库中,有现成的SVD函数,可以直接利用该函数来执行运行:

from numpy import *
if __name__ == "__main__":
    U,Sigma,VT = linalg.svd([[1,1],[7,7]])
    print("U = ",U)
    print("Sigma = ",Sigma)
    print("VT = ",VT)

返回值为:

U =  [[-0.14142136 -0.98994949]
 [-0.98994949  0.14142136]]
Sigma =  [1.00000000e+01 2.82797782e-16]
VT =  [[-0.70710678 -0.70710678]
 [ 0.70710678 -0.70710678]]

可以看到 S i g m a Sigma Sigma原本应该是一个对角矩阵,但是其返回值为一个向量,这是因为对角矩阵除了其对角线元素外都为0,因此这样存储有利于节省空间。

下面对一个较大的数据集实行SVD:

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 = linalg.svd(Data)
    print("Sigma = ",Sigma)

输出为:

Sigma =  [9.72140007e+00 5.29397912e+00 6.84226362e-01 1.89194737e-15
 1.35001244e-31]

可以看到前三个奇异值较大,后面两个奇异值接近于0,这说明可以将后面两个奇异值忽略,来恢复原来的矩阵:

在这里插入图片描述

即取明显不为0的奇异值来进行恢复,这称为紧奇异值分解,还有另外一种为截断奇异值分解,具体为:

  • 紧奇异值分解:设 r ( D a t a ) = r ≤ m i n ( m , n ) r(Data)=r\leq min(m,n) r(Data)=rmin(m,n),那么取 U r = U 1 ( m × r ) U_r=U_1(m\times r) Ur=U1(m×r) Σ r = Σ 1 ( r × r ) \Sigma_r=\Sigma_1(r\times r) Σr=Σ1(r×r) V r = V 1 ( n × r ) V_r=V_1(n\times r) Vr=V1(n×r)来进行恢复矩阵
  • 截断奇异值分解:设 r ( D a t a ) = r ≤ m i n ( m , n ) r(Data)=r\leq min(m,n) r(Data)=rmin(m,n),取 k ( k < r ) k(k<r) k(k<r)个最大的奇异值, 那么 U k = U 1 ( m × k ) U_k=U_1(m\times k) Uk=U1(m×k) Σ k = Σ 1 ( k × k ) \Sigma_k=\Sigma_1(k\times k) Σk=Σ1(k×k) V k = V 1 ( n × k ) V_k=V_1(n\times k) Vk=V1(n×k)来进行恢复矩阵, k k k为指定的压缩大小。

那就有疑问,重构出来的矩阵大小与原来的相同,为什么仍然称为压缩呢?

其实,SVD降维不是体现在矩阵的大小上,而是减少了矩阵的秩

这里引用[知乎]((29 封私信 / 50 条消息) SVD 降维体现在什么地方? - 知乎 (zhihu.com))某位大佬的回答:

在这里插入图片描述

四、基于协同过滤的推荐引擎

协同过滤是通过将用户和其他用户的数据进行对比来实现推荐的

4.1、相似度计算

在比较物品之间的相似程度时,如果我们采用的是“专家‘给出的描述物品的重要属性,例如对于食品来说有热量、配料等等,但这些属性当我们更换物品的时候就需要重新设置了,因此较为繁琐。

协同过滤中所使用的方法是:利用用户对于物品的意见来计算相似度,它不关心物品的描述属性,而是严格按照许多用户的观点来计算相似度。

计算相似度有以下几种常见方式:

  • 用欧氏距离表征的相似度:取相似度为 相似度 = 1 / ( 1 + 距离) 相似度=1/(1+距离) 相似度=1/1+距离),就可将距离转换到在0和1之间变化的相似度,且相似度越大代表距离越小,物品之间越接近。
  • 用皮尔逊相关系数表征的相似度:皮尔逊相关系数的优势在于它对于用户评级的量级并不敏感,例如某个人对于每种物品评分都是5分,而另外一个人都是1分,在皮尔逊中它认为这两个向量是相等的。取相似度为 相似度 = 0.5 + 0.5 × c o r r c o e f ( ) 相似度=0.5+0.5\times corrcoef() 相似度=0.5+0.5×corrcoef(),将在-1和1之间变量的皮尔逊相关系数转换到0和1之间变换的相似度
  • 余弦相似度:其计算的是两个向量夹角的余弦值,如果夹角为90度,那么相似度为0,如果两个向量方向相同那么相似度为1(因此也会忽略量级的影响)。余弦相似度的定义为: c o s Θ = A × B ∥ A ∥ ∥ B ∥ cos\Theta = \frac{A\times B}{\| A\| \| B \| } cosΘ=A∥∥BA×B。之后同样要取相似度为 相似度 = 0.5 + 0.5 × c o s Θ 相似度=0.5+0.5\times cos\Theta 相似度=0.5+0.5×cosΘ

具体代码如下:

def ecludSim(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=False)[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)
4.2、基于物品的相似度还是基于用户的相似度

在计算时如果取向量为列向量,那么就是计算物品之间的相似度;如果取为行向量那么就是计算用户之间的相似度。如何选择这两种方式就取决于数据中用户的数据多还是物品的数据多。如果用户的数目很多,那么会倾向于使用基于物品相似度的计算方法。

4.3、推荐引擎的评价

具体做法是将数据集划分为训练集和测试集,在测试集上预测已知的分数,查看偏差程度。

五、示例:餐馆菜肴推荐引擎

5.1、推荐未尝过的菜肴

具体的流程大致为:

  1. 寻找用户没有评级的菜肴,即用户-物品矩阵中的0值
  2. 在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数,也就是打分
  3. 对这些物品的评分从高到低进行排序,返回前N个物品

具体代码为:

def standEst(dataMat,user,simMeas,item):
    n = shape(dataMat)[1]  # 有多少个物品
    simTotal = 0.0
    ratSimTotal = 0.0
    for j in range(n):
        userRating = dataMat[user,j]  # 遍历该用户对每一个物品的评分
        if userRating == 0:  # 如果没有评分则跳过
            continue
        overLap = nonzero(logical_and(dataMat[:,item].A>0,dataMat[:,j].A>0))[0]
        if len(overLap) == 0:  # 没有都对这两个物品都评分过的用户
            similarity = 0
        else:
            similarity = simMeas(dataMat[overLap,item],dataMat[overLap,j])  # 计算相似度
        simTotal += similarity
        ratSimTotal += similarity * userRating
    if simTotal == 0:
        return 0
    else:
        return ratSimTotal / simTotal

def recommend(dataMat,user,N = 3,simMeas = cosSim,estMethod = standEst):
    unratedItems = nonzero(dataMat[user,:].A == 0)[1]  # 寻找未评分的物品
    print(nonzero(dataMat[user,:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值