• 基于矩阵分解的简单推荐算法


    本文将要讨论基于矩阵分解的推荐算法,这一类型的算法通常会有很高的预测精度,也活跃于各大推荐系统竞赛上面,前段时间的百度电影推荐最终结果的前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++),代码加一些注释有助于理解
     
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    using namespace std;
    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)
    {
         for(int step =0;step<steps;++step)
        {
                   for(int i=0;i<N;++i)
                  {
                        for(int j =0; j < M; j++)
                       {
                              if(R[i*M+j] < 0)
                              {
                                         //这里面的error 就是公式6里面的e(i,j)
                                          double error = R[i*M+j]; 
                                          for(int k=0;k < K;++k)
                                               error -= P[i*K+k]*Q[k*M+j]; 
                                          for(int k=0;k < K;++k)
                                          {
                                                 P[i*K+k]+=alpha*(2*error*Q[k*M+j] -                     beta*P[i*K+k]);
                                                 Q[k*M+j]+=alpha*(2*error*P[i*K+k])-beta*
    Q[k*M+j];
                                          }
                              }
    
                       }
                  }
    double loss =0;
    for(int i=0; i < N; i++)
    {
     for(int j=0;j<M;++j)
    {
          if(R[i*M+j]<0) 
          {
                double error = 0;
                for(int k =0; k < K; k++)
                          error+=P[i*K+k]*Q[k*M+j];
                         loss +=pow(R[i*M+j]-error,2);
                for(int k=0; k < K; k++)
                      loss+=(beta/2)*(pow(P[i*K+k],2)+pow(Q[k*M+j],2));    
          }
    }
    }
    if(loss<0.001) break;
    if(step%1000==0)
    cout<<“loss:”<<loss<<endl;
        }
    }  
    
    int main()
    {
        int N=5;    //用户数
        int M=4;   //物品数
        int K=2;   //主题个数
        double *R = new double[N*M];
        double *P = new double[N*K];
        double *Q =new double[M*K];
             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;
    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;
    cout<<"R矩阵"<<endl;
    for(int i = 0; i<N ;i++)
    {
       for(int j =0; j< M;j++)
        cout<<R[i*M+j] <<',';
    cout<<endl;
    }
    
    srand(1);
      for(int i =0; i<N;i++)
       for(int j =0; j<K;j++)
            P[i*K+j] =rand()%9;
      for(int i =0; i<K;i++)
       for(int j =0; j<M;j++)
            Q[i*M+j] = rand()%9;
    cout<<"矩阵分解开始"<<endl;
    matrix_factorization(R,P,Q,N,M,K);
    cout<<"矩阵分解结束"<<endl;
    cout<<"重构出来的R矩阵"<<endl;
    for(int i =0; i<N; i++)
    {
        for(int j=0; j < M;j++)
        {
              double temp= 0;
              for(int k=0; k < K; k++)
                 temp+=P[i*K+k]*Q[k*M+j];
              cout<<temp <<' ,';
         }
        cout<<endl;
    }
    free(P),free(Q),free(R);
    return 0;
    }
    
                             
    

      

    三,展望
           前两个部分,已经简单的介绍了最基本的基于矩阵分解的推荐算法,基于该算法的一些变种,类似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.
        3) http://www.quuxlabs.com/blog/2010/09/matrix-factorization-a-simple-tutorial-and-implementation-in-python/
  • 相关阅读:
    .NET互操作性入门系列(一):.NET平台下互操作性介绍
    .NET互操作性入门系列(三):平台调用中的数据封送处理
    .NET互操作性入门系列(二):使用平台调用调用Win32 函数
    [C# 基础知识系列]专题十四:深入理解Lambda表达式
    [C#基础知识]专题十三:全面解析对象集合初始化器、匿名类型和隐式类型
    [C#基础知识系列]专题十二:迭代器
    [C# 基础知识系列]专题十一:匿名方法解析
    [C# 基础知识系列]专题十六:Linq介绍
    [C#基础知识系列]专题十七:深入理解动态类型
    网页时钟 当前时间 备留
  • 原文地址:https://www.cnblogs.com/LyningCoder/p/3664727.html
Copyright © 2020-2023  润新知