机器学习实战——SVD

1 SVD的应用

  • 优点:简化数据,去除噪声,提高算法的结果。
  • 缺点:数据的转换可能难以理解。
  • 适用数据类型:数值型数据。
  • 应用
    • 隐性语义索引
    • 推荐系统

1.1 隐性语义索引

在LSI中,一个矩阵是由文档和词语组成的。当我们在该矩阵上应用SVD时,就会构建出多个奇异值。这些奇异值代表了文档中的概念或主题,这一特点可以用于更高效的文档搜索。在词语拼写错误时,只基于词语存在与否的简单搜索方法会遇到问题。简单搜索的另一个问题就是同义词的使用。这就是说,当我们查找一个词时,其同义词所在的文档可能并不会匹配上。如果我们从上千篇相似的文档中抽取出概念,那么同义词就会映射为同一概念。

1.2 推荐系统

SVD的另一个应用就是推荐系统。简单版本的推荐系统能够计算项或者人之间的相似度。更先进的方法则先利用SVD从数据中构建一个主题空间,然后再在该空间下计算其相似度。

2 矩阵分解

  • 矩阵分解:
    • 将原始矩阵表示成新的易于处理的形式,这种新形式是两个或多个矩阵的乘积。(类似代数中的因数分解)
    • SVD 是矩阵分解的一种类型,也是矩阵分解最常见的技术
  • SVD 将原始的数据集矩阵分解成三个矩阵 U 、 Σ 、 V U、Σ、V UΣV

矩阵Σ,该矩阵只有对角元素,其他元素均为0(近似于0)。另一个惯例
就是,Σ的对角元素是从大到小排列的。这些对角元素称为奇异值

奇异值与特征值(PCA 数据中重要特征)是有关系的。这里的奇异值就是矩阵 D a t a ∗ D a t a T Data * Data^T DataDataT特征值的平方根

普遍的事实:在某个奇异值的数目(r个 = > => =>奇异值的平方和累加到总值的90%以上)之后,其他的奇异值都置为0(近似于0)。这意味着数据集中仅有r个重要特征,而其余特征则都是噪声或冗余特征

  • 确认r
    • 启发式策略保留矩阵中90%的能量信息,将奇异值平方和累加加到90%
    • 若有上万奇异值,则保留2-3k

3 利用Python实现SVD

NumPy有一个称为linalg的线性代数工具箱,可以直接对矩阵进行奇异值分解:

import numpy as np
U,Sigma,VT = np.linalg.svd([[1,1],[7,7]])

4 基于协同过滤的推荐引擎

  • 推荐系统
    • 利用电子商务网站向客户提供商品信息和建议,帮助用户决定购买什么产品,模拟销售人员帮助客户完成购买过程
  • 基于协同过滤的推荐系统
    • 协同过滤是通过将用户与其他用户的数据进行对比,然后将相似度高的用户喜欢的东西推荐给该用户来实现推荐的当知道了两个用户或两个物品之间的相似度,我们就可以利用已有的数据来预测未知用户的喜好
    • 基于物品的协同过滤和基于用户的协同过滤
    • Item-based or User-based?
    • 用户数量一般都远远大于物品,所以一般情况下都是基于物品的协同过滤(相似度计算时间)
  • 相似度计算
  • 欧式距离相似度:相似度=1/(1+距离) → \rightarrow 0~1
  • 皮尔逊相关系数相似度:度量两个向量的相似度=0.5+0.5*linalg.corrcoef() → \rightarrow 0~1
    P e a r s o n ( x , y ) = ∑ x y − ∑ x ∑ y N ( ∑ x 2 − ( ∑ x ) 2 N ) ( ∑ y 2 − ( ∑ y ) 2 N ) Pearson(x,y) = \frac {\sum xy - \frac {\sum x \sum y}{N}}{\sqrt {(\sum x^2 - \frac {(\sum x)^2}N)(\sum y^2 - \frac {(\sum y)^2}N)}} Pearson(x,y)=(x2N(x)2)(y2N(y)2) xyNxy
  • 余弦相似度:相似度:
    cos ⁡ θ = A B ∣ ∣ A ∣ ∣ ∣ ∣ B ∣ ∣ \cos \theta = \frac {AB}{||A||||B||} cosθ=∣∣A∣∣∣∣B∣∣AB
    余弦相似度取值[-1,1],也可以归一化到0到1之间
    python代码:
def euclidsim(inA, inB):
    return 1.0 / (1.0 + np.linalg.norm(inA - inB))


def pearsSim(inA, inB):
    if len(inA) < 3:
        return 1.0
    return 0.5 + 0.5 * np.corrcoef(inA, inB, rowvar=False)[0][1]


def cosSim(inA, inB):
    num = float(inA.T * inB)
    denom = np.linalg.norm(inA) * np.linalg.norm(inB)
    return 0.5 + 0.5 * (num / denom)
  • 推荐引擎的评价
    • Cross validation:将某些已知的评分去掉,然后对它们进行预测,最后计算预测值与真实值之间的差异
    • 最常用的评价指标为:最小均方根误差(Root Mean Squared Error,RMSE)
  • Example:a restaurant dish recommendation engine给定一个用户,系统为此用户返回N个最好的推荐菜
  1. 寻找用户没有评级的菜,即:用户-物品矩阵为0
  2. 在用户没有评级的所有item中,对每个item预计一个可能的评级分数,即:根据相似度打分
  3. 对这些物品的评分从高到低进行排序,返回前N个物品

python代码

def standEst(dataMat, user, simMeans, item):
    col = np.shape(dataMat)[1]
    simTotal = 0.0
    ratSimTotal = 0.0
    for j in range(col):
        userRating = dataMat[user, j]
        if userRating == 0:
            continue
        overLap = np.nonzero(np.logical_and(dataMat[:, item].A > 0, dataMat[:, j].A > 0))[0]
        if len(overLap) == 0:
            similarity = 0
        else:
            similarity = simMeans(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:
        return ratSimTotal / simTotal


def recommend(dataMat, user, N=3, simMeans=cosSim, estMethod=standEst):
    unratedItems = np.nonzero(dataMat[user, :].A == 0)[1]
    if len(unratedItems) == 0:
        return 'you rated everything'
    itemScores = []
    for item in unratedItems:
        estimateScore = estMethod(dataMat, user, simMeans, item)
        itemScores.append((item, estimateScore))
    return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N]
  • 利用SVD提高推荐效果
    • 实际的数据集往往十分稀疏
    • 考虑到选取总能量90%的奇异值来近似原矩阵:SVD可将11维data → \rightarrow 3维
    • 前3维已经包含了90%能量
      python代码
def svdEst(dataMat, user, simMeas, item):
    col = np.shape(dataMat)[1]
    simTotal = 0.0
    ratSimTotal = 0.0
    U, Sigma, VT = np.linalg.svd(dataMat)
    Sig5 = np.mat(np.eye(5) * Sigma[:5])
    xformedItems = dataMat.T * U[:, :5] * Sig5.I
    for j in range(col):
        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

5 总结

  • Challenges:
    • 在大规模的数据集上,SVD分解会降低程序的速度
    • 存在其他很多规模扩展性的挑战性问题,比如矩阵的表示方法和计算相似度得分消耗资源
    • 推荐系统:如何在缺乏数据时给出好的推荐-称为冷启动cold-start
  • Suggestions:
    • 在大型系统中,SVD分解(可以在程序调入时运行一次)每天运行一次或者其频率更低,并且还要离线运行。
    • 在实际中矩阵存储非零元素节省内存,另一个普遍的做法就是离线计算并保存相似度得分。(物品相似度可能被用户重复的调用)
    • 冷启动问题,解决方案就是将推荐看成是搜索问题,通过各种标签/属性特征进行基于内容的推荐。

猜你喜欢

转载自blog.csdn.net/m0_55818687/article/details/127951412