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)的推荐。可能,基于内容的推荐并不如我们前面介绍的基于协同过滤的推荐效果好 ,但我们拥有它,这就是个良好的开始。 

      

  • 相关阅读:
    1130 Infix Expression (25分)
    1131 Subway Map (30分)
    1132 Cut Integer (20分)
    1133 Splitting A Linked List (25分)
    1134 Vertex Cover (25分)
    1135 Is It A Red-Black Tree (30分)
    tensorflow 1.0的部分项目配置匹配
    1136 A Delayed Palindrome (20分)
    谷粒商城Redisson分布式锁(二十四)
    谷粒商城缓存(二十三)
  • 原文地址:https://www.cnblogs.com/lesleysbw/p/6066444.html
Copyright © 2011-2022 走看看