机器学习实战---SVD简化数据
一:参考资料
(一)机器学习实战
(二)如何让奇异值分解(SVD)变得不“奇异”?(特别好理解SVD)
https://blog.csdn.net/yusisc/article/details/82216800
(三)奇异值分解(SVD)原理与在降维中的应用
(四)深入理解PCA与SVD的关系
二:推荐系统协调过滤算法
(一)文章回顾
https://www.cnblogs.com/ssyfj/p/12951542.html
(二)另一种实现方法
import numpy as np #一:相似度计算,传入的都是列向量 def eulidSim(inA,inB): #相似度=1/(1+距离) 物品对越相似,它们的相似度值就越大。 return 1/(1+np.linalg.norm(inA-inB)) #距离按照第二范式计算 def pearsSim(inA,inB): #皮尔逊相关系数 相似度=0.5+0.5*corrcoef if len(inA) < 3: #为啥小于3???书上说小于3的两个向量完全相关(不应该是2吗?) return 1 return 0.5 + 0.5*np.corrcoef(inA,inB,rowvar=0)[0][1] def cosSim(inA,inB): #余弦相似度 num = inA.T@inB denom = np.linalg.norm(inA)*np.linalg.norm(inB) return 0.5+0.5*(num/denom) #二:数据加载 def loadDataSet(): return np.array([[4,4,0,2,3], [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,0,0]]) #三:实现基于物品相似度的推荐引擎 #1.根据某个用户编号和其对应的多个未评价的物品中的一个,进行估计评分值(是直接对该物品评分) def standEst(dataArr,userId,itemId,simMeas): n = dataArr.shape[1] #所有物品数目 simTotal = 0 #计算整体相似度 ratSimTotal = 0 #计算相似度作为权重,对现有另一个用户评分加权后的评分 #循环所有物品 for j in range(n): userScore = dataArr[userId,j] #获取本用户对物品的评分 if userScore == 0: continue #如果用户没有评分,则不进行相似度评价 #找到一个物品,同我们指定的物品。都同时给予了评价的用户(有无重合,有重合,才可以进行相似度计算) overLap = np.where(np.logical_and(dataArr[:,itemId],dataArr[:,j]))[0] #获取用户索引 if len(overLap) == 0: #无相似度 similarity = 0 else: similarity = simMeas(dataArr[overLap,itemId],dataArr[overLap,j]) #计算重合部分的相似度 simTotal += similarity ratSimTotal += similarity*userScore #进行返回 if simTotal == 0: return 0 #如果没有找到相似物品,返回评分0即可 else: return ratSimTotal/simTotal #通过除以所有的相似度和,对上述相似度评分的乘积进行归一化,使得最后评分在0~5之间,这些评分用来对预测值进行排序 #2.使用上面估分,进行辅助。推荐物品 def recommend(dataArr,userId,N=3,simMeas=cosSim,estMethod=standEst): #N是想要获取的最符合的物品(估计评分最高) #操作该用户没有评价的物品 unratedItems = np.where(dataArr[userId] == 0)[0] if len(unratedItems) == 0: #全部评价了 print("you rated everything") return #获取全部未评价的物品评分值 itemsScore = [] for i in unratedItems: estimatedScore = estMethod(dataArr,userId,i,simMeas) itemsScore.append((i,estimatedScore)) #传入物品编号和评分 #返回我们想要的物品数 return sorted(itemsScore,key=lambda p:p[1],reverse=True)[:N] #获取前N个最高评分的物品 data = loadDataSet() print(recommend(data,2))
三:使用SVD提高推荐效果
(一)不变代码
import numpy as np #一:相似度计算,传入的都是列向量 def eulidSim(inA,inB): #相似度=1/(1+距离) 物品对越相似,它们的相似度值就越大。 return 1/(1+np.linalg.norm(inA-inB)) #距离按照第二范式计算 def pearsSim(inA,inB): #皮尔逊相关系数 相似度=0.5+0.5*corrcoef if len(inA) < 3: #为啥小于3???书上说小于3的两个向量完全相关(不应该是2吗?) return 1 return 0.5 + 0.5*np.corrcoef(inA,inB,rowvar=0)[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) #二:数据加载 def loadDataSet(): return np.array([[4,4,0,2,3], [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,0,0]]) #三:实现基于物品相似度的推荐引擎 #1.根据某个用户编号和其对应的多个未评价的物品中的一个,进行估计评分值(是直接对该物品评分) def standEst(dataArr,userId,itemId,simMeas): n = dataArr.shape[1] #所有物品数目 simTotal = 0 #计算整体相似度 ratSimTotal = 0 #计算相似度作为权重,对现有另一个用户评分加权后的评分 #循环所有物品 for j in range(n): userScore = dataArr[userId,j] #获取本用户对物品的评分 if userScore == 0: continue #如果用户没有评分,则不进行相似度评价 #找到一个物品,同我们指定的物品。都同时给予了评价的用户(有无重合,有重合,才可以进行相似度计算) overLap = np.where(np.logical_and(dataArr[:,itemId],dataArr[:,j]))[0] #获取用户索引 if len(overLap) == 0: #无相似度 similarity = 0 else: similarity = simMeas(dataArr[overLap,itemId],dataArr[overLap,j]) #计算重合部分的相似度 simTotal += similarity ratSimTotal += similarity*userScore #进行返回 if simTotal == 0: return 0 #如果没有找到相似物品,返回评分0即可 else: return ratSimTotal/simTotal #通过除以所有的相似度和,对上述相似度评分的乘积进行归一化,使得最后评分在0~5之间,这些评分用来对预测值进行排序 #2.使用上面估分,进行辅助。推荐物品 def recommend(dataArr,userId,N=3,simMeas=cosSim,estMethod=standEst): #N是想要获取的最符合的物品(估计评分最高) #操作该用户没有评价的物品 unratedItems = np.where(dataArr[userId] == 0)[0] if len(unratedItems) == 0: #全部评价了 print("you rated everything") return #获取全部未评价的物品评分值 itemsScore = [] for i in unratedItems: estimatedScore = estMethod(dataArr,userId,i,simMeas) itemsScore.append((i,estimatedScore)) #传入物品编号和评分 #返回我们想要的物品数 return sorted(itemsScore,key=lambda p:p[1],reverse=True)[:N] #获取前N个最高评分的物品
(二)SVD代码实现
def loadExData2(): return np.array([[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 svdEst(dataArr,userId,itemId,simMeas): #https://zhuanlan.zhihu.com/p/58064462 n = dataArr.shape[1] #获取全部物品数量 simTotal = 0 ratSimTotal = 0 #使用SVD进行分解 #注意:python返回的sigma只有数值,不是矩阵 U,Sigma,VT = np.linalg.svd(dataArr) #注意行为用户,列为物品 #建立对角矩阵 Sig4 = np.eye(4)*Sigma[:4] #建立对角矩阵 注意:我们选取4个最大特征值,保持这四个特征值的总能量>90%全部特征总能量,使用前,先测试 #建立低维空间,使用U特征矩阵,降低冗余样本 xformedItems = dataArr.T@U[:,:4]@np.linalg.inv(Sig4) #现在是n,4但是后面进行转置,变为4,n达到降低冗余 for j in range(n): userRating = dataArr[userId,j] #获取评分 if userRating == 0 or j == itemId: continue #用户没有评分,或者物品就是本身,不处理 similarity = simMeas(np.array([xformedItems[itemId,:]]).T,np.array([xformedItems[j,:]]).T) print(np.array([xformedItems[itemId,:]]).T, np.array([xformedItems[j,:]]).T) print('the %d and %d similarity is: %f' % (itemId, j, similarity)) #[-0.01591788 -0.39205093 0.55707516 0.04356321] [-0.0552444 -0.52034959 -0.36330956 -0.19023805] simTotal += similarity ratSimTotal += similarity*userRating #加权评分 if simTotal == 0: return 0 else: return ratSimTotal/simTotal data = loadExData2() print(recommend(data,1,estMethod=svdEst))