zoukankan      html  css  js  c++  java
  • 《机器学习实战》学习笔记——第14章 利用SVD简化数据

    一. SVD 

    1. 基本概念:

    (1)定义:提取信息的方法:奇异值分解Singular Value Decomposition(SVD)

    (2)优点:简化数据, 去除噪声,提高算法的结果

    (3)缺点:数据转换难以想象,耗时,损失特征

    (4)适用于:数值型数据

    2. 应用:

    (1)隐性语义索引(LSI/LSA)

    (2)推荐系统

    3. 原理——矩阵分解

    将原始的数据集矩阵data(m*n)分解成三个矩阵U(m*n), Sigma(n*m), VT(m*n):

    对于Sigma矩阵:

    •  该矩阵只用对角元素,其他元素均为零
    • 对角元素从大到小排列这些对角元素称为奇异值,它们对应了原始数据集矩阵的奇异值
    • 这里的奇异值就是矩阵data特征值的平方根。 
    • 在某个奇异值的数目( 1个 )之后,其他的奇异值都置为0。这就意味着数据集中仅有r个重要特征,而其余特征则都是噪声或冗余特征。 
      • 确认r——启发式策略
        • 保留矩阵中90%的能量信息,将奇异值平方和累加加到90%
        • 若有上万奇异值,则保留2-3k

    4. 实现:

    Numpy中称为linalg的线性代数工具:la.svd()

    import numpy as np
    from numpy impot linalg as la
    
    data = np.array([[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],
               [1, 1, 1, 0, 0],
               [5, 5, 5, 2, 0]])
    
    U, Sigma, VT  = la.svd(data)

    二、推荐系统实现

    2.1. 基于协同过滤的推荐系统(Collaborate Filtering)

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

     2.1.1 相似度计算

      1. 欧式距离(0~1):

        相似度 = 1/ (1 + 距离)

      2. 皮尔逊相关系数(Pearson correlation):

        

    • 介绍:http://mines.humanoriented.com/classes/2010/fall/csci568/portfolio_exports/sphilip/pear.html
    • 该方法相对于欧氏距离的一个优势在于,它对用户评级的量级并不敏感。比如某个狂躁者对所有物品的评分都是5分 ,而另一个忧郁者对所有物品的评分都是1分,皮尔逊相关系数会认为这两个向量是相等的。
    • 皮尔逊相关系数的计算是由Numpy中的corrcoef()函数,他的取值范围在:(-1~1)
    • 后面我们很快就会用到它了。皮尔逊相关系数的取值范围从-1+1
    • 我们通过0.5 + 0 . 5 * corrcoef()把其取值范围归一化到01之间。

    3. 余弦相似度(cosine similarity)

        

    其中,在Numpy中计算范数的公式:linalg.norm()

     1 def ecludSim(inA,inB):
     2     return 1.0/(1.0 + la.norm(inA - inB))  #计算向量的第二范式,相当于直接计算了欧式距离
     3 
     4 def pearsSim(inA, inB):
     5     if len(inA) < 3:
     6         return 1.0
     7     return 0.5 + 0.5 * corrcoef(inA, inB, rowvar=0)[0][1]  
     8 # corrcoef直接计算皮尔逊相关系数
     9 
    10 def cosSim(inA, inB):
    11     num = float(inA.T * inB)
    12     denom = la.norm(inA) * la.norm(inB)
    13     return 0.5 + 0.5 * (num/denom)

    上面的相似度计算都是假设数据采用了列向量方式进行表示。如果利用上述函数来计算两个行向量的相似度就会遇到问题(我们很容易对上述函数进行修改以计算行向量之间的相似度)。这里采用列向量的表示方法,暗示着我们将利用基于物品的相似度计算方法。 

    2.1.2 基于物品的相似度还是基于用户的相似度? 

    行与行之间比较的是基于用户的相似度,列与列之间比较的则是基于物品的相似度。

    到底使用哪一种相似度呢?这取决于用户或物品的数目。基于物品相似度计算的时间会随物品数量的增加而增加,基于用户的相似度计算的时间则会随用户数量的增加而增加。如果我们有一个商店,那么最多会有几千件商品。在撰写本书之际,最大的商店大概有100 000件商品。而在61!«大赛中,则会有480 000个用户17 700部电影。如果用户的数目很多,那么我们可能倾向于使用基于物品相似度的计算方法。对于大部分产品导向的推荐引擎而言,用户的数量往往大于物品的数量,即购买商品的用户数会多于出售的商品种类。 

    2.1.3 推荐系统的评价

    • 采用交叉测试的方法。具体的做法就是,我们将某些已知的评分值去掉,然后对它们进行预测,最后计算预测值和真实值之间的差异。 
    • 评价的指标是称为最小均方根误差RootMeanSquaredError, RMSE ) 的指标。
      • 它首先计算均方误差的平均值然后取其平方根。
      • 如果评级在1星到5星这个范围内,而我们得到的1.0,那么就意味着我们的预测值和用户给出的真实评价相差了一个星级。 

    2.1.4 实现:

     1 def loadExData():
     2     return[[4, 4, 0, 2, 2],
     3            [4, 0, 0, 3, 3],
     4            [4, 0, 0, 1, 1],
     5            [1, 1, 1, 2, 0],
     6            [2, 2, 2, 0, 0],
     7            [1, 1, 1, 0, 0],
     8            [5, 5, 5, 2, 0]]
     9 
    10 def loadExData2():
    11     return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5],
    12            [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3],
    13            [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0],
    14            [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0],
    15            [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0],
    16            [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0],
    17            [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1],
    18            [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4],
    19            [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2],
    20            [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0],
    21            [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]]
    22 
    23 
    24 # 协同过滤算法
    25 # dataMat 用户数据 user 用户 simMeas 相似度计算方式 item 物品
    26 def standEst(dataMat, user, simMeas, item):
    27     n = shape(dataMat)[1]  # 计算列的数量,物品的数量
    28     simTotal = 0.0
    29     ratSimTotal = 0.0
    30     for j in range(n):
    31         userRating = dataMat[user, j]
    32         print(dataMat[user, j])
    33         if userRating == 0: 
    34             continue  # 如果用户u没有对物品j进行打分,那么这个判断就可以跳过了
    35         overLap = nonzero(logical_and(dataMat[:, item] > 0, dataMat[:, j] > 0))[0]  # 找到对物品 j 和item都打过分的用户
    36         if len(overLap) == 0:
    37             similarity = 0
    38         else:
    39             similarity = simMeas(dataMat[overLap, item], dataMat[overLap, j])  # 利用相似度计算两个物品之间的相似度
    40 
    41         print('the %d and %d similarity is: %f' % (item, j, similarity))
    42         simTotal += similarity
    43         ratSimTotal += similarity * userRating  # 待推荐物品与用户打过分的物品之间的相似度*用户对物品的打分
    44     if simTotal == 0:
    45         return 0
    46     else:
    47         return ratSimTotal / simTotal
    48 
    49 def recommand(dataMat, user, N=3, simMeas=pearsSim, estMethod=standEst):
    50     unratedItem = nonzero(dataMat[user, :]==0)[0]
    51     if len(unratedItem) == 0:
    52         return  'You rated everything'
    53     else:
    54         itemScores=[]
    55         # 对于为评分的item,对他进行评分
    56         for item in unratedItem:
    57             estimatedScore = standEst(dataMat, user, simMeas, item)
    58             itemScores.append((item, estimatedScore))
    59     itemScores = sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
    60     print(itemScores)
    61 
    62 
    63 data = array(loadExData())
    64 recommand(data, 2)
    65         

    2.2 利用SVD实现推荐

    2.2.1 选取Sigma奇异值个数r:

     1 # 获取数据:
     2 data = np.array(loadExData2())
     3 
     4 # SVD 分解
     5 U, Sigma, VT = la.svd(data)
     6 
     7 # 计算取几个奇异值
     8 # 计算90%能量:487.83
     9 Sig2 = Sigma ** 2
    10 s = sum(Sig2) * 0.9
    11 
    12 # 计算前两个奇异值包含能量:378(<487.83)
    13 sum(Sig2[:2])
    14 
    15 # 计算前三个奇异值包含能量:500(>487)
    16 sum(Sig2[:3])
    17 #所以选择前三个就可以了

    2.2.2 用SVD实现推荐系统:

     1 #  利用SVD实现推荐系统
     2 def svdEst(dataMat, user, simMeas, item):
     3     n = shape(dataMat)[1]
     4     simTotal = 0.0
     5     ratsimTotal = 0.0
     6     U, Sigma, VT = la.svd(dataMat)
     7     Sig4 = array(eye(4) * Sigma[:4])
     8     xformedItems = dot(dot(dataMat.T, U[:, :4]), Sig4)
     9 
    10     for j in range(n):
    11         userRating = dataMat[user, j]
    12         if userRating == 0 or j == item:
    13             continue
    14         similarity = simMeas(xformedItems[item, :].T, xformedItems[j, :].T)
    15         simTotal += similarity
    16         ratsimTotal += userRating * similarity
    17     if simTotal == 0:
    18         return 0
    19     else:
    20         return ratsimTotal/simTotal
    21 
    22 def recommand(dataMat, user, N=3, simMeas=pearsSim, estMethod=standEst):
    23     unratedItem = nonzero(dataMat[user, :]==0)[0]
    24     if len(unratedItem) == 0:
    25         return  'You rated everything'
    26     else:
    27         itemScores=[]
    28         # 对于为评分的item,对他进行评分
    29         for item in unratedItem:
    30             estimatedScore = svdEst(dataMat, user, simMeas, item)
    31             itemScores.append((item, estimatedScore))
    32     itemScores = sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
    33     print(itemScores)
    34 
    35 
    36 data = array(loadExData())
    37 recommand(data, 2)

    最后结果:

    2.3 冷启动问题

    推荐引擎面临的另一个问题就是如何在缺乏数据时给出好的推荐。这称为冷启动问题,处理起来十分困难。

    冷启动问题的解决方案,就是将推荐看成是搜索问题。在内部表现上,不同的解决办法虽然有所不同,但是对用户而言却都是透明的。为了将推荐看成是搜索问题,我们可能要使用所需要推荐物品的属性。在餐馆菜肴的例子中,我们可以通过各种标签来标记菜肴,比如素食、美式88、价格很贵等。同时,我们也可以将这些属性作为相似度计算所需要的数据,这被称为基于内容(content-based)的推荐。可能,基于内容的推荐并不如我们前面介绍的基于协同过滤的推荐效果好 ,但我们拥有它,这就是个良好的开始。 

      

  • 相关阅读:
    Codeforces 787D. Legacy 线段树优化建图+最短路
    Codeforces 1051E. Vasya and Big Integers
    BZOJ3261 最大异或和
    BZOJ3531 SDOI2014 旅行
    洛谷P2468 SDOI 2010 粟粟的书架
    2018 ICPC 焦作网络赛 E.Jiu Yuan Wants to Eat
    HDU6280 From Tree to Graph
    HDU5985 Lucky Coins 概率dp
    (HDU)1334 -- Perfect Cubes (完美立方)
    (HDU)1330 -- Deck (覆盖物)
  • 原文地址:https://www.cnblogs.com/lesleysbw/p/6066444.html
Copyright © 2011-2022 走看看