推荐算法—Slope One

时间:2022-12-07 12:20:50

 

一、计算平均偏差

推荐算法—Slope One

u - user,用户 

i,j - 商品

u(i,j) - 同时评价过i,j商品的用户集合

card - 数目

R(u,j) - 用户u对商品j的评分

 

二、基础Slope One

上述式子即为得到的评分偏差,接下来提出最简单的slope one预测评分的式子。

推荐算法—Slope One

 

三、带权重的Slope One

在计算用户u对商品i进行评估时,用户u对j,k进行过评分,j商品共有2000人同时也对i评分了,而k商品仅有200人,明显使用j商品进行评估更合理,所以这里使用了权重。

推荐算法—Slope One

 

四、双极性Slope One

先将用户对某个物品的评分分为like,dislike两类。通过评分是否大于该用户自己的平均评分。

推荐算法—Slope One

推荐算法—Slope One推荐算法—Slope One

类似地,可以定义对item ii 和 jj 具有相同喜好的用户集合

 推荐算法—Slope One

利用上面的定义,我们可以使用下面的公式为(like或dislike的item)获得新的偏差值:

推荐算法—Slope One

这样可以计算从item ii 计算得到的预测值:

推荐算法—Slope One

最终 Bi-Polar Slope One 的预测公式为:

推荐算法—Slope One

 

参考: http://www.cnblogs.com/breezedeus/archive/2011/03/11/1981781.html

 

五、评测指标

这里采用RMSE作为评测指标。计算公式为:

推荐算法—Slope One  或者 推荐算法—Slope One

 

 

C++实现(权重slope-one实现):

  1 #include<iostream>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<fstream>
  5 #include<vector>
  6 #include<cmath>
  7 #include<set>
  8 
  9 using namespace std;
 10 
 11 const int N = 7448;
 12 int userMax = 0;
 13 int itemMax = 0;
 14 int r[N][N]; //记录评分 
 15 int user_t[N]; //记录测试集的数据 
 16 int item_t[N];
 17 int rate_t[N];
 18 set<int> test; //记录测试集行号 
 19 
 20 void getTest(int m,int n){ //得到测试集的行号 
 21     while(test.size()<m){
 22         test.insert(rand()%n);
 23     }
 24 }
 25 
 26 vector<string> split(string str,string pattern)
 27 {
 28     string::size_type pos;
 29     vector<string> result;
 30     str+=pattern;//扩展字符串以方便操作
 31     int size=str.size();
 32 
 33     for(int i=0; i<size; i++)
 34     {
 35         pos=str.find(pattern,i);
 36         if(pos<size)
 37         {
 38             std::string s=str.substr(i,pos-i);
 39             result.push_back(s);
 40             i=pos+pattern.size()-1;
 41         }
 42     }
 43     return result;
 44 }
 45 
 46 int main()
 47 {
 48     ifstream data("train_small_2.txt");
 49     
 50     string buffer;
 51     vector<string> temp;
 52     
 53     getTest(N/5,N);
 54     memset(r,0,sizeof(r));
 55     
 56     int t = 0;
 57     while(!data.eof()){ //数据处理,将一份数据分成训练集与测试集 
 58         getline(data,buffer);
 59         temp = split(buffer," ");
 60         int i = atoi(temp[0].c_str());
 61         int j = atoi(temp[1].c_str());
 62         //cout << i << " " << j <<endl;
 63         if(test.find(t)!=test.end()){
 64             user_t[t] = i;
 65             item_t[t] = j;
 66             rate_t[t] = atoi(temp[2].c_str());
 67         }else{    
 68             if(i > userMax) userMax = i;
 69             if(j > itemMax) itemMax = j;
 70             r[i][j] = atoi(temp[2].c_str());
 71         }
 72         t++;
 73     }
 74     set<int>::iterator it = test.begin();
 75     double rmse = 0;
 76     int num = 0;
 77     for(;it!=test.end();it++){ // uesr_t item_t
 78         int t = *it;
 79         int count = 0;    
 80         int sum = 0;
 81         for(int i=1;i<=itemMax;i++){//对于user_t[t],找到他评价的所有商品 i
 82             if(r[user_t[t]][i]!=0){
 83                 int cnt = 0;
 84                 int dev = 0;
 85                 for(int j=1;j<=userMax;j++){//找到同时评价item[t]与i的用户 
 86                     if(r[j][item_t[t]]!=0 && r[j][i]!=0){
 87                         dev += (-r[j][i]+r[j][item_t[t]]); //求出偏差和 
 88                         cnt++;
 89                     }
 90                 }
 91                 sum += (dev+r[user_t[t]][i]*cnt); //加上对应权重的初始值 
 92                 count += cnt; //算出参与计算的样例总数 
 93             }
 94         }
 95         double ans = sum*1.0/count;
 96         if(ans>=0 && ans <=5){ //由于样例少会出现不能计算的样例的情况,排除这些 
 97             rmse += pow((ans - rate_t[t]),2);
 98             num++;
 99             cout << user_t[t] <<" "<< item_t[t] <<" "<< ans << endl;
100         }
101     }
102     rmse = sqrt(rmse/num);
103     cout << "RMSE:" << rmse << endl;
104 }

 

运行结果:

RMSE:0.995244

可能数据集小的原因,得到的效果很好..