zoukankan      html  css  js  c++  java
  • 协同过滤推荐算法

    转自:http://www.cnblogs.com/zhangchaoyang/articles/2664366.html

    Collaborative Filtering Recommendation

    向量之间的相似度

    度量向量之间的相似度方法很多了,你可以用距离(各种距离)的倒数,向量夹角,Pearson相关系数等。

    皮尔森相关系数计算公式如下:

    ρX,Y=cov(X,Y)σxσy=E((Xμx)(Yμy))σxσy

    分子是协方差,分子是两个变量标准差的乘积。显然要求X和Y的标准差都不能为0。

    因为μX=E(X),σ2X=E(XμX)2=E(X2)E2(X)所以皮尔森相关系数计算公式还可以写成:

    ρX,Y=E(XY)E(X)E(Y)E(X2)E2(X)E(Y2)E2(Y)

    当两个变量的线性关系增强时,相关系数趋于1或-1。

    Pearson相关系数有个特点,它在计算两个数列的相似度时忽略其平均值的差异。比如说有的用户对商品评分普遍偏低,有的用户评分普遍偏高,而实际上他们具有相同的爱好,他们的Pearson相关系数会比较高。用户1对某一个商品的评分是X=(1,2,3),用户2对这三个商品的评分是Y=(4,5,6),则X和Y的Pearson相关系数是0.865,相关性还是挺高的。

     

      iterm1 ………… itemn
    user1 R11   R1n
    ……   Rij  
    userm Rm1   Rmn

    用户评分数据矩阵

    基于用户的协同过滤

    step1.如果用户i对项目j没有评过分,就找到与用户i最相似的K个邻居(采用Pearson相关系数)

    step2.然后用这K个邻居对项目j的评分的加权平均来预测用户i对项目j的评分。

    U1=(r1,1,r1,2...r1,n)

    U2=(r2,1,r2,2...r2,n)

    要预测用户u对商品i的评分ru,i

    用户u对所有商品的平均得分为ru¯

    用户x评分的商品集合为Ix,用户y评分的商品集合为Iy,其并集为Ixy

    采用Pearson相关系数用户x和y的相似度:

    sim(x,y)=iIxy(rx,irx¯)(ry,iry¯)iIxy(rx,irx¯)2iIxy(ry,iry¯)2

    ru,i=ru¯+zuUsim(u,u)(ru,iru¯)

    其中U是用户u的近邻,z是归一化因子,z=1uUsim(u,u)

     这各预测方法充分考虑了用户一向的评分习惯是偏高还是偏低,因为用户u的近邻对u产生影响时已经减去了各自的平均值。

    计算用户U1U2的相似度时并不是去拿原始的评分向量去计算,而是只关注他们的评交集Ix,y,这是因为一个用户只对很少的物品有过评分,这样用户评分向量是个高度稀疏的向量,采用Pearson相关系数计算两个用户的相似度时很不准。

    基于物品的协同过滤

    step1.如果用户i对项目j没有评过分,就把ri,j置为0。找到与物品j最相似的k个近邻(采用余弦距离)

    step2.然后用这K个邻居对项目j的评分的加权平均来预测用户i对项目j的评分。

    I1=(r1,1,r2,1...rm,1)

    I2=(r1,2,r2,2...rm,2)

    每一项减去各个用户评分的均值:

    I1=(r1,1r1¯,r2,1r2¯...rm,1rm¯)

    I2=(r1,2r1¯,r2,2r2¯...rm,2rm¯)

    商品i和j之间的相似度采用余弦计算:

    sim(i,j)=x(rx,irx¯)(rx,jrx¯)x(rx,irx¯)2x(rx,jrx¯)2

    预测用户u对商品i的评分:

    ru,i=iNsim(i,i)ru,iiNsim(i,i)

    其中N是商品i的近邻。

    由于物品之间的相似度比较稳定,可以离线先算好,定期更新即可。在电商行业这种算法用的比较多。

    混合协同过滤

    所谓的混合算法,主体思路还是基于用户的协同过滤,只是在计算两个用户的相似度时又嵌套了item-based CF思想。

    度量用户i和用户j相似度更好的方法是:

    1.用户i参与评分的项目集合为Ii,用户j参与评分的项目集合为Ij,找到它们的并集Uij=IiIj

    2.在集合Uij中用户i未评分的项目是Ni=UijIi,采用item-based CF方法重新估计用户i对Ni中每个项目的评分。

    3.这样用户i和j对Uij的评分就都是非0值了,在此情况下计算他们的相似度。

    示例代码:

    复制代码
      1 #include<iostream>
      2 #include<queue>
      3 #include<cmath>
      4 #include<cassert>
      5 #include<cstdlib>
      6 #include<fstream>
      7 #include<sstream>
      8 #include<vector>
      9 #include<algorithm>
     10 
     11 using namespace std;
     12 
     13 const int ITERM_SIZE=1682;
     14 const int USER_SIZE=943;
     15 const int V=15;        //ITERM的最近邻居数
     16 const int S=10;        //USER的最近邻居数
     17 
     18 struct MyPair{
     19     int id;
     20     double value;
     21     MyPair(int i=0,double v=0):id(i),value(v){}
     22 };
     23 
     24 struct cmp{
     25     bool operator() (const MyPair & obj1,const MyPair & obj2)const{
     26         return obj1.value < obj2.value;
     27     }
     28 };
     29 
     30 double rate[USER_SIZE][ITERM_SIZE];    //评分矩阵
     31 MyPair nbi[ITERM_SIZE][V];            //存放每个ITERM的最近邻居
     32 MyPair nbu[USER_SIZE][S];            //存放每个USER的最近邻居
     33 double rate_avg[USER_SIZE];            //每个用户的平均评分
     34 
     35 //从文件中读入评分矩阵
     36 int readRate(string filename){
     37     ifstream ifs;
     38     ifs.open(filename.c_str());
     39     if(!ifs){
     40         cerr<<"error:unable to open input file "<<filename<<endl;
     41         return -1;
     42     }
     43     string line;
     44     while(getline(ifs,line)){
     45         string str1,str2,str3;
     46         istringstream strstm(line);
     47         strstm>>str1>>str2>>str3;
     48         int userid=atoi(str1.c_str());
     49         int itermid=atoi(str2.c_str());
     50         double rating=atof(str3.c_str());
     51         rate[userid-1][itermid-1]=rating;
     52         line.clear();
     53     }
     54     ifs.close();
     55     return 0;
     56 }
     57 
     58 //计算每个用户的平均评分
     59 void getAvgRate(){
     60     for(int i=0;i<USER_SIZE;++i){
     61         double sum=0;
     62         for(int j=0;j<ITERM_SIZE;++j)
     63             sum+=rate[i][j];
     64         rate_avg[i]=sum/ITERM_SIZE;
     65     }
     66 }
     67 
     68 //计算两个向量的皮尔森相关系数
     69 double getSim(const vector<double> &vec1,const vector<double> &vec2){
     70     int len=vec1.size();
     71     assert(len==vec2.size());
     72     double sum1=0;
     73     double sum2=0;
     74     double sum1_1=0;
     75     double sum2_2=0;
     76     double sum=0;
     77     for(int i=0;i<len;i++){
     78         sum+=vec1[i]*vec2[i];
     79         sum1+=vec1[i];
     80         sum2+=vec2[i];
     81         sum1_1+=vec1[i]*vec1[i];
     82         sum2_2+=vec2[i]*vec2[i];
     83     }
     84     double ex=sum1/len;
     85     double ey=sum2/len;
     86     double ex2=sum1_1/len;
     87     double ey2=sum2_2/len;
     88     double exy=sum/len;
     89     double sdx=sqrt(ex2-ex*ex);
     90     double sdy=sqrt(ey2-ey*ey);
     91     assert(sdx!=0 && sdy!=0);
     92     double sim=(exy-ex*ey)/(sdx*sdy);
     93     return sim;
     94 }
     95 
     96 //计算每个ITERM的最近邻
     97 void getNBI(){
     98     for(int i=0;i<ITERM_SIZE;++i){
     99         vector<double> vec1;
    100         priority_queue<MyPair,vector<MyPair>,cmp> neighbour;
    101         for(int k=0;k<USER_SIZE;k++)
    102             vec1.push_back(rate[k][i]);
    103         for(int j=0;j<ITERM_SIZE;j++){
    104             if(i==j)
    105                 continue;
    106             vector<double> vec2;
    107             for(int k=0;k<USER_SIZE;k++)
    108                 vec2.push_back(rate[k][j]);
    109             double sim=getSim(vec1,vec2);
    110             MyPair p(j,sim);
    111             neighbour.push(p);
    112         }
    113         for(int j=0;j<V;++j){
    114             nbi[i][j]=neighbour.top();
    115             neighbour.pop();
    116         }
    117     }
    118 }
    119 
    120 //预测用户对未评分项目的评分值
    121 double getPredict(const vector<double> &user,int index){
    122     double sum1=0;
    123     double sum2=0;
    124     for(int i=0;i<V;++i){
    125         int neib_index=nbi[index][i].id;
    126         double neib_sim=nbi[index][i].value;
    127         sum1+=neib_sim*user[neib_index];
    128         sum2+=fabs(neib_sim);
    129     }
    130     return sum1/sum2;
    131 }
    132 
    133 //计算两个用户的相似度
    134 double getUserSim(const vector<double> &user1,const vector<double> &user2){
    135     vector<double> vec1;
    136     vector<double> vec2;
    137     int len=user1.size();
    138     assert(len==user2.size());
    139     for(int i=0;i<len;++i){
    140         if(user1[i]!=0 || user2[i]!=0){
    141             if(user1[i]!=0)
    142                 vec1.push_back(user1[i]);
    143             else
    144                 vec1.push_back(getPredict(user1,i));
    145             if(user2[i]!=0)
    146                 vec2.push_back(user2[i]);
    147             else
    148                 vec2.push_back(getPredict(user2,i));
    149         }
    150     }
    151     return getSim(vec1,vec2);
    152 }
    153 
    154 //计算每个USER的最近邻
    155 void getNBU(){
    156     for(int i=0;i<USER_SIZE;++i){
    157         vector<double> user1;
    158         priority_queue<MyPair,vector<MyPair>,cmp> neighbour;
    159         for(int k=0;k<ITERM_SIZE;++k)
    160             user1.push_back(rate[i][k]);
    161         for(int j=0;j<USER_SIZE;++j){
    162             if(j==i)
    163                 continue;
    164             vector<double> user2;
    165             for(int k=0;k<ITERM_SIZE;++k)
    166                 user2.push_back(rate[j][k]);
    167             double sim=getUserSim(user1,user2);
    168             MyPair p(j,sim);
    169             neighbour.push(p);
    170         }
    171         for(int j=0;j<S;++j){
    172             nbu[i][j]=neighbour.top();
    173             neighbour.pop();
    174         }
    175     }
    176 }
    177             
    178 //产生推荐,预测某用户对某项目的评分
    179 double predictRate(int user,int iterm){
    180     double sum1=0;
    181     double sum2=0;
    182     for(int i=0;i<S;++i){
    183         int neib_index=nbu[user][i].id;
    184         double neib_sim=nbu[user][i].value;
    185         sum1+=neib_sim*(rate[neib_index][iterm]-rate_avg[neib_index]);
    186         sum2+=fabs(neib_sim);
    187     }
    188     return rate_avg[user]+sum1/sum2;
    189 }
    190 
    191 //测试
    192 int main(){
    193     string file="/home/orisun/DataSet/movie-lens-100k/u.data";
    194     if(readRate(file)!=0){
    195         return -1;
    196     }
    197     getAvgRate();
    198     getNBI();
    199     getNBU();
    200     while(1){
    201         cout<<"please input user index and iterm index which you want predict"<<endl;
    202         int user,iterm;
    203         cin>>user>>iterm;
    204         cout<<predictRate(user,iterm)<<endl;
    205     }
    206     return 0;
    207 }
    复制代码

     

    原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun

  • 相关阅读:
    4-1 R语言函数 lapply
    3-6 向量化操作
    3-5 处理缺失值
    3-4 列表的子集
    3-3 数据框的子集
    3-2 矩阵的子集
    bootstrap 模式对话框
    手机端 超链接 识别电话号码
    jQuery设置和获取HTML、文本和值
    TP 框架 ajax[利用异步提交表单]
  • 原文地址:https://www.cnblogs.com/walccott/p/4956973.html
Copyright © 2011-2022 走看看