u - user,用戶 html
i,j - 商品ios
u(i,j) - 同時評價過i,j商品的用戶集合測試
card - 數目spa
R(u,j) - 用戶u對商品j的評分3d
上述式子即爲獲得的評分誤差,接下來提出最簡單的slope one預測評分的式子。code
在計算用戶u對商品i進行評估時,用戶u對j,k進行過評分,j商品共有2000人同時也對i評分了,而k商品僅有200人,明顯使用j商品進行評估更合理,因此這裏使用了權重。xml
先將用戶對某個物品的評分分爲like,dislike兩類。經過評分是否大於該用戶本身的平均評分。htm
相似地,能夠定義對item ii 和 jj 具備相同喜愛的用戶集合blog
利用上面的定義,咱們可使用下面的公式爲(like或dislike的item)得到新的誤差值:字符串
這樣能夠計算從item ii 計算獲得的預測值:
最終 Bi-Polar Slope One 的預測公式爲:
這裏採用RMSE做爲評測指標。計算公式爲:
或者
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
可能數據集小的緣由,獲得的效果很好..