python实现svd++推荐算法

转载:https://blog.csdn.net/taozhaojie/article/details/52790032

之前写过用python实现svd推荐算法,这次更进一步,在原来的基础上实现了svd++算法,基本框架和之前一篇是类似的.

SVD++算法的预测评分式子如下。

与SVD相比增加的是这部分:


它的含义是这样的:评分行为从侧面反映了用户的喜好,可以将这样的反映通过隐式参数形式体系在模型中,得到的就是上式的部分,其中Iu是用户u评论过的物品的集合,yj为隐藏的评价了物品j的个人喜好偏置,也通过梯度下降算法优化。这里的-1/2是个经验值。

详细代码如下:

[python]  view plain  copy
  1. import numpy as np  
  2. import random  
  3. ''''' 
  4. author:huang 
  5.  
  6. svd++ algorithm 
  7.  
  8.  
  9. '''  
  10.   
  11.   
  12. class SVDPP:  
  13.     def __init__(self,mat,K=20):  
  14.         self.mat=np.array(mat)  
  15.         self.K=K  
  16.         self.bi={}  
  17.         self.bu={}  
  18.         self.qi={}  
  19.         self.pu={}  
  20.         self.avg=np.mean(self.mat[:,2])  
  21.         self.y={}  
  22.         self.u_dict={}  
  23.         for i in range(self.mat.shape[0]):  
  24.               
  25.             uid=self.mat[i,0]  
  26.             iid=self.mat[i,1]  
  27.             self.u_dict.setdefault(uid,[])  
  28.             self.u_dict[uid].append(iid)  
  29.             self.bi.setdefault(iid,0)  
  30.             self.bu.setdefault(uid,0)  
  31.             self.qi.setdefault(iid,np.random.random((self.K,1))/10*np.sqrt(self.K))  
  32.             self.pu.setdefault(uid,np.random.random((self.K,1))/10*np.sqrt(self.K))  
  33.             self.y.setdefault(iid,np.zeros((self.K,1))+.1)  
  34.     def predict(self,uid,iid):  #预测评分的函数  
  35.         #setdefault的作用是当该用户或者物品未出现过时,新建它的bi,bu,qi,pu及用户评价过的物品u_dict,并设置初始值为0  
  36.         self.bi.setdefault(iid,0)  
  37.         self.bu.setdefault(uid,0)  
  38.         self.qi.setdefault(iid,np.zeros((self.K,1)))  
  39.         self.pu.setdefault(uid,np.zeros((self.K,1)))  
  40.         self.y.setdefault(uid,np.zeros((self.K,1)))  
  41.         self.u_dict.setdefault(uid,[])  
  42.         u_impl_prf,sqrt_Nu=self.getY(uid, iid)  
  43.         rating=self.avg+self.bi[iid]+self.bu[uid]+np.sum(self.qi[iid]*(self.pu[uid]+u_impl_prf)) #预测评分公式  
  44.         #由于评分范围在1到5,所以当分数大于5或小于1时,返回5,1.  
  45.         if rating>5:  
  46.             rating=5  
  47.         if rating<1:  
  48.             rating=1  
  49.         return rating  
  50.       
  51.     #计算sqrt_Nu和∑yj  
  52.     def getY(self,uid,iid):  
  53.         Nu=self.u_dict[uid]  
  54.         I_Nu=len(Nu)  
  55.         sqrt_Nu=np.sqrt(I_Nu)  
  56.         y_u=np.zeros((self.K,1))  
  57.         if I_Nu==0:  
  58.             u_impl_prf=y_u  
  59.         else:  
  60.             for i in Nu:  
  61.                 y_u+=self.y[i]  
  62.             u_impl_prf = y_u / sqrt_Nu  
  63.           
  64.         return u_impl_prf,sqrt_Nu  
  65.       
  66.     def train(self,steps=30,gamma=0.04,Lambda=0.15):    #训练函数,step为迭代次数。  
  67.         print('train data size',self.mat.shape)  
  68.         for step in range(steps):  
  69.             print('step',step+1,'is running')  
  70.             KK=np.random.permutation(self.mat.shape[0]) #随机梯度下降算法,kk为对矩阵进行随机洗牌  
  71.             rmse=0.0  
  72.             for i in range(self.mat.shape[0]):  
  73.                 j=KK[i]  
  74.                 uid=self.mat[j,0]  
  75.                 iid=self.mat[j,1]  
  76.                 rating=self.mat[j,2]  
  77.                 predict=self.predict(uid, iid)  
  78.                 u_impl_prf,sqrt_Nu=self.getY(uid, iid)  
  79.                 eui=rating-predict  
  80.                 rmse+=eui**2  
  81.                 self.bu[uid]+=gamma*(eui-Lambda*self.bu[uid])    
  82.                 self.bi[iid]+=gamma*(eui-Lambda*self.bi[iid])  
  83.                 self.pu[uid]+=gamma*(eui*self.qi[iid]-Lambda*self.pu[uid])  
  84.                 self.qi[iid]+=gamma*(eui*(self.pu[uid]+u_impl_prf)-Lambda*self.qi[iid])  
  85.                 for j in self.u_dict[uid]:  
  86.                     self.y[j]+=gamma*(eui*self.qi[j]/sqrt_Nu-Lambda*self.y[j])  
  87.                                       
  88.             gamma=0.93*gamma  
  89.             print('rmse is',np.sqrt(rmse/self.mat.shape[0]))  
  90.       
  91.     def test(self,test_data):  #gamma以0.93的学习率递减  
  92.           
  93.         test_data=np.array(test_data)  
  94.         print('test data size',test_data.shape)  
  95.         rmse=0.0  
  96.         for i in range(test_data.shape[0]):  
  97.             uid=test_data[i,0]  
  98.             iid=test_data[i,1]  
  99.             rating=test_data[i,2]  
  100.             eui=rating-self.predict(uid, iid)  
  101.             rmse+=eui**2  
  102.         print('rmse of test data is',np.sqrt(rmse/test_data.shape[0]))  
  103.       
  104.       
  105. def getMLData(): #获取训练集和测试集的函数  
  106.     import re  
  107.     f=open("C:/Users/xuwei/Downloads/ml-100k/ml-100k/u1.base",'r')  
  108.     lines=f.readlines()  
  109.     f.close()  
  110.     data=[]  
  111.     for line in lines:  
  112.         list=re.split('\t|\n',line)  
  113.         if int(list[2]) !=0:  
  114.             data.append([int(i) for i in list[:3]])  
  115.     train_data=data  
  116.     f=open("C:/Users/xuwei/Downloads/ml-100k/ml-100k/u1.test",'r')  
  117.     lines=f.readlines()  
  118.     f.close()  
  119.     data=[]  
  120.     for line in lines:  
  121.         list=re.split('\t|\n',line)  
  122.         if int(list[2]) !=0:  
  123.             data.append([int(i) for i in list[:3]])  
  124.     test_data=data  
  125.       
  126.     return train_data,test_data  
  127.       
  128. train_data,test_data=getMLData()  
  129. a=SVDPP(train_data,30)    
  130. a.train()  
  131. a.test(test_data)  
  132.           
  133.                    
  134.               
测试数据的结果RMSE大概是0.93左右,比SVD略微好一些,不过训练速度慢了很多,应该是因为计算yj那里的关系。

猜你喜欢

转载自blog.csdn.net/m0_37870649/article/details/80546147
今日推荐