zoukankan      html  css  js  c++  java
  • 转推荐算法——基于矩阵分解的推荐算法

    推荐算法概述

    对于推荐系统(Recommend System, RS),从广义上的理解为:为用户(User)推荐相关的商品(Items)。常用的推荐算法主要有:

    • 基于内容的推荐(Content-Based Recommendation)
    • 协同过滤的推荐(Collaborative Filtering Recommendation)
    • 基于关联规则的推荐(Association Rule-Based Recommendation)
    • 基于效用的推荐(Utility-Based Recommendation)
    • 基于知识的推荐(Knowledge-Based Recommendation)
    • 组合推荐(Hybrid Recommendation)

    在推荐系统中,最重要的数据是用户对商品的打分数据,数据形式如下所示:

    这里写图片描述

    其中,U1U5表示的是5个不同的用户,D1D4表示的是4个不同的商品,这样便构成了用户-商品矩阵,在该矩阵中,有用户对每一件商品的打分,其中“-”表示的是用户未对该商品进行打分。

    在推荐系统中有一类问题是对未打分的商品进行评分的预测。

    目前推荐系统中用的最多的就是矩阵分解方法,在Netflix Prize推荐系统大赛中取得突出效果。以用户-项目评分矩阵为例,矩阵分解就是预测出评分矩阵中的缺失值,然后根据预测值以某种方式向用户推荐。常见的矩阵分解方法有基本矩阵分解(basic MF),正则化矩阵分解)(Regularized MF),基于概率的矩阵分解(PMF)等。今天以“用户-项目评分矩阵R(N×M)”说明三种分解方式的原理以及应用。

    用户-项目评分矩阵

    • Basic MF:

      Basic MF是最基础的分解方式,将评分矩阵R分解为用户矩阵U和项目矩阵S, 通过不断的迭代训练使得U和S的乘积越来越接近真实矩阵,矩阵分解过程如图: 
      矩阵分解过程

      预测值接近真实值就是使其差最小,这是我们的目标函数,然后采用梯度下降的方式迭代计算U和S,它们收敛时就是分解出来的矩阵。我们用损失函数来表示误差(等价于目标函数): 
      损失函数 公式1

      公式1中R_ij是评分矩阵中已打分的值,U_i和S_j相当于未知变量。为求得公式1的最小值,相当于求关于U和S二元函数的最小值(极小值或许更贴切)。通常采用梯度下降的方法: 
      梯度下降

      学习速率是学习速率,表示迭代的步长。其值为1.5时,通常以震荡形式接近极值点;若<1迭代单调趋向极值点;若>2围绕极值逐渐发散,不会收敛到极值点。具体取什么值要根据实验经验。


      • Regularized MF

        正则化矩阵分解是Basic MF的优化,解决MF造成的过拟合问题。其不是直接最小化损失函数,而是在损失函数基础上增加规范化因子,将整体作为损失函数。 
        Regularized MF

        红线表示正则化因子,在求解U和S时,仍然采用梯度下降法,此时迭代公式变为:(图片截取自相关论文,S和V等价) 
        梯度下降

        其中, E

        梯度下降结束条件:f(x)的真实值和预测值小于自己设定的阈值(很小的值,之前一直理解为是变量U和V的迭代值差小于阈值就行,弄了一天才懂。)

    程序实现

    对于上述的评分矩阵,通过矩阵分解的方法对其未打分项进行预测,最终的结果为:

    这里写图片描述

    程序代码如下:

    #!/bin/python
    '''
    Date:20160411
    @author: zhaozhiyong
    '''
    from numpy import *
    
    def load_data(path):
        f = open(path)
        data = []
        for line in f.readlines():
            arr = []
            lines = line.strip().split("	")
            for x in lines:
                if x != "-":
                    arr.append(float(x))
                else:
                    arr.append(float(0))
            #print arr
            data.append(arr)
        #print data
        return data
    
    def gradAscent(data, K):
        dataMat = mat(data)
        print dataMat
        m, n = shape(dataMat)
        p = mat(random.random((m, K)))
        q = mat(random.random((K, n)))
    
        alpha = 0.0002
        beta = 0.02
        maxCycles = 10000
    
        for step in xrange(maxCycles):
            for i in xrange(m):
                for j in xrange(n):
                    if dataMat[i,j] > 0:
                        #print dataMat[i,j]
                        error = dataMat[i,j]
                        for k in xrange(K):
                            error = error - p[i,k]*q[k,j]
                        for k in xrange(K):
                            p[i,k] = p[i,k] + alpha * (2 * error * q[k,j] - beta * p[i,k])
                            q[k,j] = q[k,j] + alpha * (2 * error * p[i,k] - beta * q[k,j])
    
            loss = 0.0
            for i in xrange(m):
                for j in xrange(n):
                    if dataMat[i,j] > 0:
                        error = 0.0
                        for k in xrange(K):
                            error = error + p[i,k]*q[k,j]
                        loss = (dataMat[i,j] - error) * (dataMat[i,j] - error)
                        for k in xrange(K):
                            loss = loss + beta * (p[i,k] * p[i,k] + q[k,j] * q[k,j]) / 2
    
            if loss < 0.001:
                break
            #print step
            if step % 1000 == 0:
                print loss
    
        return p, q
    
    
    if __name__ == "__main__":
        dataMatrix = load_data("./data")
    
        p, q = gradAscent(dataMatrix, 5)
        '''
        p = mat(ones((4,10)))
        print p
        q = mat(ones((10,5)))
        '''
        result = p * q
        #print p
        #print q
    
        print result

    其中,利用梯度下降法进行矩阵分解的过程中的收敛曲线如下所示:

    这里写图片描述

    '''
    Date:20160411
    原文作者:@author: zhaozhiyong
    '''
    
    from pylab import *
    from numpy import *
    
    data = []
    
    f = open("result")
    for line in f.readlines():
        lines = line.strip()
        data.append(lines)
    
    n = len(data)
    x = range(n)
    plot(x, data, color='r',linewidth=3)
    plt.title('Convergence curve')
    plt.xlabel('generation')
    plt.ylabel('loss')
    show()
  • 相关阅读:
    Map以及其子类
    java.util.Collection List与其子类 Set与其子类
    JDK5的新特性:泛型、可变参数、静态导入
    oracle配置数据库连接方式
    PL/SQL连接Oracle客户端步骤
    最新小程序教学视频,欢迎加小程序交流群免费获取(微信小程序开发(交流QQ群:604788754)
    小程序中的bindtap和catchtap的区别(交流QQ群:604788754)
    微信小程序中的bindTap事件(微信小程序开发QQ群:604788754)
    微信小程序绑定数据(微信小程序交流群:604788754)
    小程序配置单个页面导航栏的属性(微信小程序交流群:604788754)
  • 原文地址:https://www.cnblogs.com/onemorepoint/p/8167942.html
Copyright © 2011-2022 走看看