基於矩陣分解的推薦算法,簡單入門


       本文將要討論基於矩陣分解的推薦算法,這一類型的算法通常會有很高的預測精度,也活躍於各大推薦系統競賽上面,前段時間的百度電影推薦最終結果的前10名貌似都是把矩陣分解作為一個單模型,最后各種ensemble,不知道正在進行的阿里推薦比賽(http://102.alibaba.com/competition/addDiscovery/index.htm),會不會驚喜出現。。。。好了,閑話不扯了,本文打算寫一篇該類型推薦算法的入門篇
 
目錄
一,基於矩陣分解的推薦算法相關理論介紹
二,C++代碼實現
三,總結跟展望一下
四,后續計划
 
一, 基於矩陣分解的推薦算法相關理論介紹
      我們知道,要做推薦系統,最基本的一個數據就是,用戶-物品的評分矩陣,如下圖1所示
 
圖1
          矩陣中,描述了5個用戶(U1,U2,U3,U4 ,U5)對4個物品(D1,D2,D3,D4)的評分(1-5分),- 表示沒有評分,現在目的是把沒有評分的 給預測出來,然后按預測的分數高低,給用戶進行推薦。
       如何預測缺失的評分呢?對於缺失的評分,可以轉化為基於機器學習的回歸問題,也就是連續值的預測,對於矩陣分解有如下式子,R是類似圖1的評分矩陣,假設N*M維(N表示行數,M表示列數),可以分解為P跟Q矩陣,其中P矩陣維度N*K, P矩陣維度K*M。
 
式子1
       對於P,Q矩陣的解釋,直觀上,P矩陣是N個用戶對K個主題的關系,Q矩陣是K個主題跟M個物品的關系,至於K個主題 具體 是什么,在算法里面K是一個參數,需要調節的,通常10~100之間。
式子2
       對於式子2的左邊項,表示的是R^ 第i行,第j列的元素值,對於如何衡量,我們分解的好壞呢,式子3,給出了衡量標准,也就是損失函數,平方項損失,最后的目標,就是每一個元素(非缺失值)的e(i,j)的總和 最小
 
 
式子3
         OK,目前現在評分矩陣有了,損失函數也有了,該優化算法登場了,下面式子4是,基於梯度下降的優化算法,p,q里面的每個元素的更新方式
 
 
式子4
             然而,機器學習算法都喜歡加一個正則項,這里面對式子3稍作修改,得到如下式子5,beita 是正則參數
 
式子5
         相應的p,q矩 陣各個元素的更新也換成了如下方式
 
式子6
        至此,P,Q矩陣元素求出來了之后,計算某個用戶i對某個物品j的評分計算就是p(i,1)*q(1,j)+ p(i,2)*q(2,j)+....+ p(i,k)*q(k,j)。
 
二, C++代碼實現
       第一部分已經給出了,基於矩陣分解的推薦算法的整個流程,下面是該算法編程實現(C/C++),代碼加一些注釋有助於理解
 1 /**  2 
 3 評分矩陣R如下  4 
 5  D1 D2 D3 D4  6 
 7 U1 5 3 - 1  8 
 9 U2 4 - - 1  10 
 11 U3 1 1 - 5  12 
 13 U4 1 - - 4  14 
 15 U5 - 1 5 4  16 
 17 ***/ 
 18 
 19 #include<iostream> 
 20 
 21 #include<cstdio> 
 22 
 23 #include<cstdlib> 
 24 
 25 #include<cmath> 
 26 
 27 using namespace std;  28 
 29  
 30 
 31 void matrix_factorization(double *R,double *P,double *Q,int N,int M,int K,int steps=5000,float alpha=0.0002,float beta=0.02)  32 
 33 {  34 
 35  for(int step =0;step<steps;++step)  36 
 37  {  38 
 39   for(int i=0;i<N;++i)  40 
 41  {  42 
 43    for(int j=0;j<M;++j)  44 
 45  {  46 
 47     if(R[i*M+j]>0)  48 
 49  {  50 
 51      //這里面的error 就是公式6里面的e(i,j) 
 52 
 53      double error = R[i*M+j];  54 
 55      for(int k=0;k<K;++k)  56 
 57       error -= P[i*K+k]*Q[k*M+j];  58 
 59  
 60 
 61      //更新公式6 
 62 
 63      for(int k=0;k<K;++k)  64 
 65  {  66 
 67       P[i*K+k] += alpha * (2 * error * Q[k*M+j] - beta * P[i*K+k]);  68 
 69       Q[k*M+j] += alpha * (2 * error * P[i*K+k] - beta * Q[k*M+j]);  70 
 71  }  72 
 73  }  74 
 75  }  76 
 77  }  78 
 79   double loss=0;  80 
 81   //計算每一次迭代后的,loss大小,也就是原來R矩陣里面每一個非缺失值跟預測值的平方損失 
 82 
 83   for(int i=0;i<N;++i)  84 
 85  {  86 
 87    for(int j=0;j<M;++j)  88 
 89  {  90 
 91     if(R[i*M+j]>0)  92 
 93  {  94 
 95      double error = 0;  96 
 97      for(int k=0;k<K;++k)  98 
 99       error += P[i*K+k]*Q[k*M+j]; 100 
101      loss += pow(R[i*M+j]-error,2); 102 
103      for(int k=0;k<K;++k) 104 
105       loss += (beta/2) * (pow(P[i*K+k],2) + pow(Q[k*M+j],2)); 106 
107  } 108 
109  } 110 
111  } 112 
113   if(loss<0.001) 114 
115    break; 116 
117   if (step%1000==0) 118 
119     cout<<"loss:"<<loss<<endl; 120 
121  } 122 
123 } 124 
125  
126 
127 int main(int argc,char ** argv) 128 
129 { 130 
131  int N=5; //用戶數 
132 
133  int M=4; //物品數 
134 
135  int K=2; //主題個數 
136 
137  double *R=new double[N*M]; 138 
139  double *P=new double[N*K]; 140 
141  double *Q=new double[M*K]; 142 
143  R[0]=5,R[1]=3,R[2]=0,R[3]=1,R[4]=4,R[5]=0,R[6]=0,R[7]=1,R[8]=1,R[9]=1; 144 
145  R[10]=0,R[11]=5,R[12]=1,R[13]=0,R[14]=0,R[15]=4,R[16]=0,R[17]=1,R[18]=5,R[19]=4; 146 
147  
148 
149  cout<< "R矩陣" << endl; 150 
151  for(int i=0;i<N;++i) 152 
153  { 154 
155   for(int j=0;j<M;++j) 156 
157    cout<< R[i*M+j]<<','; 158 
159   cout<<endl; 160 
161  } 162 
163  
164 
165  //初始化P,Q矩陣,這里簡化了,通常也可以對服從正態分布的數據進行隨機數生成 
166 
167  srand(1); 168 
169  for(int i=0;i<N;++i) 170 
171   for(int j=0;j<K;++j) 172 
173    P[i*K+j]=rand()%9; 174 
175  
176 
177  for(int i=0;i<K;++i) 178 
179   for(int j=0;j<M;++j) 180 
181    Q[i*M+j]=rand()%9; 182 
183  cout <<"矩陣分解 開始" << endl; 184 
185  matrix_factorization(R,P,Q,N,M,K); 186 
187  cout <<"矩陣分解 結束" << endl; 188 
189  
190 
191  cout<< "重構出來的R矩陣" << endl; 192 
193  for(int i=0;i<N;++i) 194 
195  { 196 
197   for(int j=0;j<M;++j) 198 
199  { 200 
201    double temp=0; 202 
203    for (int k=0;k<K;++k) 204 
205     temp+=P[i*K+k]*Q[k*M+j]; 206 
207    cout<<temp<<','; 208 
209  } 210 
211   cout<<endl; 212 
213  } 214 
215  free(P),free(Q),free(R); 216 
217  return 0; 218 
219 } 

 

   執行的結果如下圖所示,

 
三,展望
       前兩個部分,已經簡單的介紹了最基本的基於矩陣分解的推薦算法,基於該算法的一些變種,類似svd++,pmf等,都是針對某一些特定的數據場景進行的一些改進,那有沒有統一的框架來整合這些場景呢??前兩年在KDDcup大賽,大出風頭的Factorization Machine(FM),其中FM的核心理論在於用Factorization來刻畫feature跟feature之間的關系,如下面公式
 
       <Vi,Vj>正是刻畫了xi,xj的關系,上面式子可以理解為FM=SVM+Factorization Methods,后續准備開一篇博文,來闡釋FM模型,跟其作者開源的LibFM工具箱,最后貼一張八卦的圖,圖中講的是bickson(graphlab/graphchi的里面推薦工具包的作者),在一次會議上,對steffen(libfm的作者)問的一個問題
 
四,后續計划
   1),介紹FM模型
   2),LibFM源碼剖析
 
參考資料
   1),bickson.blogspot.com/2012/08/steffen-rendle-libfm.html
   2),S. Rendle.Factorization machines.In Proceedings of the 10th IEEE International Conference on Data Mining. IEEE Computer Society,  2010.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM