diff --git a/docs/14.使用SVD简化数据-LSI举例.png b/docs/14.使用SVD简化数据-LSI举例.png new file mode 100644 index 00000000..65ea196b Binary files /dev/null and b/docs/14.使用SVD简化数据-LSI举例.png differ diff --git a/docs/14.使用SVD简化数据-SVD公式.png b/docs/14.使用SVD简化数据-SVD公式.png new file mode 100644 index 00000000..15f31c5d Binary files /dev/null and b/docs/14.使用SVD简化数据-SVD公式.png differ diff --git a/docs/14.使用SVD简化数据-余弦相似度.png b/docs/14.使用SVD简化数据-余弦相似度.png new file mode 100644 index 00000000..376d24ab Binary files /dev/null and b/docs/14.使用SVD简化数据-余弦相似度.png differ diff --git a/docs/14.使用SVD简化数据-基于物品相似度.png b/docs/14.使用SVD简化数据-基于物品相似度.png new file mode 100644 index 00000000..69bf433d Binary files /dev/null and b/docs/14.使用SVD简化数据-基于物品相似度.png differ diff --git a/docs/14.使用SVD简化数据-基于用户相似度.png b/docs/14.使用SVD简化数据-基于用户相似度.png new file mode 100644 index 00000000..fc2777e0 Binary files /dev/null and b/docs/14.使用SVD简化数据-基于用户相似度.png differ diff --git a/docs/14.使用SVD简化数据.md b/docs/14.使用SVD简化数据.md index e69de29b..3a555b4c 100644 --- a/docs/14.使用SVD简化数据.md +++ b/docs/14.使用SVD简化数据.md @@ -0,0 +1,85 @@ +# 章节内容: +* SVD矩阵分解 +* 推荐引擎 +* 利用SVD提示推荐引擎的性能 + +# 1. SVD 的应用:隐性语义索引(LSI)和推荐系统。 +* 奇异值分解(SVD):提取信息的方法。可以把SVD看成是从噪声数据中抽取相关特征。 +* 优点:简化数据,去除噪声,提高算法的结果。 +* 缺点:数据的转换可能难以理解。 +* 使用的数据类型:数值型数据。 + +# 补充:降维 +* 降维(dimensionality reduction) +* 如果样本数据的特征维度很大,会使得难以分析和理解。我们可以通过降维技术减少维度。 +* 降维技术并不是将影响少的特征去掉,而是将样本数据集转换成一个低维度的数据集。 + +# 1.1 隐性语义索引:矩阵 = 文档 + 词语 +* 是最早的SVD应用之一。我们称利用SVD的方法为隐性语义索引(LSI)或隐性语义分析(LSA)。 +* ![LSA举例](./14.使用SVD简化数据-LSI举例.png) + +# 2. 矩阵分解 +* SVD 是矩阵分解的一种类型,而矩阵分解是将数据矩阵分解为多个独立部分的过程。 +* 矩阵分解可以将原始矩阵表示成新的易于处理的形式,这种新形式时两个或多个矩阵的乘积。(类似代数中的因数分解) + +* 举例:如何将12分解成两个数的乘积?(1,12)、(2,6)、(3,4)都是合理的答案。 + +# SVD 时矩阵最常见的分解技术。 +* SVD将原始的数据集矩阵Data分解成三个矩阵U、∑、VT。 +* 举例:如果原始矩阵Data是m行n列,那么U、∑、VT 就分别是m行m列、m行n列和n行n列。 +* ![SVD公式](./14.使用SVD简化数据-SVD公式.png) +* 上述分解中会构建出一个矩阵∑,该矩阵只有对角元素,其他元素均为0。另一个惯例就是,∑的对角元素是从大到小排列的。这些对角元素称为奇异值,它们对应原始矩阵Data的奇异值。 +* 奇异值与特征值是有关系的。这里的奇异值就是矩阵 Data * DateT 特征值的平方根。 + +* 一个普遍的事实:在某个奇异值的数目(r 个)之后,其他的奇异值都置为0。这意味着数据集中仅有r个重要特征,而其余特征则都是噪声或冗余特征。 + +# 3. 利用Python 实现SVD +* Numpy 有一个称为linalg的线性代数工具箱。 + +# 4. 基于协同过滤的推荐引擎 +* 协同过滤:是通过将用户和其他用户的数据进行对比来实现推荐的。 + +* 当知道了两个用户或两个物品之间的相似度,我们就可以利用已有的数据来预测未知用户的喜好。 + +# 4.1 相似度计算 +* 我们不利用专家所给出的重要属性来描述物品从而计算它们之间的相似度,而是利用用户对它们的意见来计算相似度。 + +# 1)欧氏距离:指在m维空间中两个点之间的真实距离,或者向量的自然长度(即改点到原点的距离)。二维或三维中的欧氏距离就是两点之间的实际距离。 +* 相似度= 1/(1+距离) +* 物品对越相似,它们的相似度值就越大。 + +# 2)皮尔逊相关系数:度量的是两个向量之间的相似度。 +* 相对欧氏距离的优势:它对用户评级的量级并不敏感。 +* 皮尔逊相关系数的取值范围从 -1 到 +1,通过函数0.5 + 0.5*corrcoef()这个函数计算,把值归一化到0到1之间。 + +# 3)余弦相似度:计算的是两个向量夹角的余弦值。 +* 如果夹角为90度,则相似度为0;如果两个向量的方向相同,则相似度为1.0。 +* 余弦相似度的取值范围也在-1到+1之间。 +* ![SVD公式](./14.使用SVD简化数据-余弦相似度.png) + +# 4.2 基于物品的相似度和基于用户的相似度:物品比较多则选择物品相似度,用户比较多则选择用户相似度。 +* 基于物品的相似度:计算物品之间的距离的方法。 +* 基于物品相似度计算的时间会随物品数量的增加而增加。 +* ![SVD公式](./14.使用SVD简化数据-基于物品相似度.png) + +* 基于用户的相似度:计算用户距离的方法。 +* 基于用户的相似度计算的时间则会随着用户数量的增加而增加。 +* ![SVD公式](./14.使用SVD简化数据-基于用户相似度.png) + +# 4.3 推荐引擎的评价 +* 最小均方根误差(RMSE):推荐引擎评价的指标,先计算均方误差然后取其平方根。 + + +# 5. 示例:(看代码) +# 5.1 推荐未尝过的菜肴 +* 1)寻找用户没有评级的菜肴,即在用户-物品矩阵中的0值; +* 2)在用户没有评级的所有物品中,对每个物品预计一个可能的评级分数。这就是说,我们认为用户可能会对物品的打分(这个就是相似度计算的初衷) +* 3)对这些物品的评分从高到低进行排序,返回前N个物品。 + +# 5.2 利用SVD提高推荐效果 +* 将矩阵降维 + +# 5.3 构建推荐引擎面临的挑战 +* 1)在大规模的数据集上,SVD分解会降低程序的速度 +* 2)存在其他很多规模扩展性的挑战性问题,比如矩阵的表示方法和计算相似度得分消耗资源。 +* 3)如何在缺乏数据时给出好的推荐(冷启动问题,解决方案就是将推荐看成是搜索问题) \ No newline at end of file diff --git a/src/python/14.SVD/svdRec.py b/src/python/14.SVD/svdRec.py index 1c877e05..a85fe180 100644 --- a/src/python/14.SVD/svdRec.py +++ b/src/python/14.SVD/svdRec.py @@ -6,8 +6,7 @@ from numpy import linalg as la from numpy import * -def loadExData(): - """ +def loadExData3(): # 利用SVD提高推荐效果,菜肴矩阵 return[[2, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5], @@ -21,6 +20,23 @@ def loadExData(): [0, 0, 0, 3, 0, 0, 0, 0, 4, 5, 0], [1, 1, 2, 1, 1, 2, 1, 0, 4, 5, 0]] + +def loadExData2(): + # 书上代码给的示例矩阵 + return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5], + [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3], + [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0], + [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0], + [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0], + [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0], + [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1], + [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4], + [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2], + [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0], + [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]] + + +def loadExData(): # 推荐引擎示例矩阵 return[[4, 4, 0, 2, 2], [4, 0, 0, 3, 3], @@ -38,6 +54,7 @@ def loadExData(): [1, 1, 0, 2, 2], [0, 0, 0, 3, 3], [0, 0, 0, 1, 1]] + """ # 相似度计算,假定inA和inB 都是列向量 @@ -139,14 +156,14 @@ def svdEst(dataMat, user, simMeas, item): userRating = dataMat[user, j] if userRating == 0 or j == item: continue - # 相似度的计算方法也会作为一个参数传递给该函数 - similarity = simMeas(xformedItems[item, :].T,xformedItems[j, :].T) - # for 循环中加入了一条print语句,以便了解相似度计算的进展情况。如果觉得累赘,可以去掉 - print 'the %d and %d similarity is: %f' % (item, j, similarity) - # 对相似度不断累加求和 - simTotal += similarity - # 对相似度及对应评分值的乘积求和 - ratSimTotal += similarity * userRating + # 相似度的计算方法也会作为一个参数传递给该函数 + similarity = simMeas(xformedItems[item, :].T,xformedItems[j, :].T) + # for 循环中加入了一条print语句,以便了解相似度计算的进展情况。如果觉得累赘,可以去掉 + print 'the %d and %d similarity is: %f' % (item, j, similarity) + # 对相似度不断累加求和 + simTotal += similarity + # 对相似度及对应评分值的乘积求和 + ratSimTotal += similarity * userRating if simTotal == 0: return 0 else: @@ -195,7 +212,7 @@ def imgCompress(numSV=3, thresh=0.8): if __name__ == "__main__": """ - # 对矩阵进行SVD分解 + # 对矩阵进行SVD分解(用python实现SVD) Data = loadExData() U, Sigma, VT = linalg.svd(Data) # 打印Sigma的结果,因为前3个数值比其他的值大了很多,为9.72140007e+00,5.29397912e+00,6.84226362e-01 @@ -224,20 +241,33 @@ if __name__ == "__main__": """ + # 计算相似度的方法 + myMat = mat(loadExData2()) + # print myMat + # 计算相似度的第一种方式 + print recommend(myMat, 1, estMethod=svdEst) + # 计算相似度的第二种方式 + print recommend(myMat, 1, estMethod=svdEst, simMeas=pearsSim) + """ - # 默认推荐 + # 默认推荐(菜馆菜肴推荐示例) print recommend(myMat, 2) """ """ - # 计算相似度的方法 - # 计算相似度的第一种方式 - # print recommend(myMat, 1, estMethod=svdEst) - # 计算相似度的第二种方式 - print recommend(myMat, 1, estMethod = svdEst, simMeas=pearsSim) - """ + # 利用SVD提高推荐效果 + U, Sigma, VT = la.svd(mat(loadExData2())) + print Sigma # 计算矩阵的SVD来了解其需要多少维的特征 + Sig2 = Sigma**2 # 计算需要多少个奇异值能达到总能量的90% + print sum(Sig2) # 计算总能量 + print sum(Sig2) * 0.9 # 计算总能量的90% + print sum(Sig2[: 2]) # 计算前两个元素所包含的能量 + print sum(Sig2[: 3]) # 两个元素的能量值小于总能量的90%,于是计算前三个元素所包含的能量 + # 该值高于总能量的90%,这就可以了 """ + # 压缩图片 # imgCompress(2) - """ + +