矩阵分解(学习笔记)

推荐系统

推荐系统(Recommend System, RS):从广义上的理解为,为用户(User)推荐相关的商品(Items)。属于资讯过滤的一种应用。推荐系统能够将可能受喜好的资讯或实物(例如:电影、电视节目、音乐、书籍、新闻、图片、网页)推荐给使用者。
基本流程
Step1.首先收集用户的历史行为数据

Step2.然后通过预处理的方法得到用户-评价矩阵

Step3.利用机器学习领域中相关推荐技术(主要指算法)形成对用户的个性化推荐

PS:有的推荐系统还搜集用户对推荐结果的反馈,并根据实际的反馈信息实时调整推荐策略,产生更符合用户需求的推荐结果。 
  
大致分为这几类算法

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

矩阵分解原理

在这里插入图片描述

基于矩阵分解的推荐算法
实例:

在推荐系统中,最重要的数据是用户对商品的打分数据
在这里插入图片描述

  • 矩阵中,描述了5个用户(U1,U2,U3,U4 ,U5)对4个物品(D1,D2,D3,D4)的评分(1-5分), - 表示没有评分,现在目的是预测缺失的评分,然后按预测的分数高低,给用户进行推荐。

  • 如何预测缺失的评分呢?对于缺失的评分,可以转化为基于机器学习的回归问题,也就是连续值的预测。

进行矩阵分解:

矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为 R m × n R_{m×n} Rm×n。可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵 P m × k P_{m×k} Pm×k Q k × n Q_{k×n} Qk×n,我们要使得矩阵 P m × k P_{m×k} Pm×k Q k × n Q_{k×n} Qk×n的乘积能够还原原始的矩阵 R m × n R_{m×n} Rm×n

R m × n ≈ P m × k × Q k × n = R ^ m × n R_{m\times n}\approx P_{m\times k}\times Q_{k\times n}=\hat{R}_{m\times n} Rm×nPm×k×Qk×n=R^m×n

其中,矩阵 P m × k P_{m×k} Pm×k 表示的是 m m m个用户与 k k k个主题之间的关系,而矩阵 Q k × n Q_{k×n} Qk×n表示的是 k k k个主题与 n n n个商品之间的关系。

那么接下来的问题是如何求解矩阵 P m × k P_{m×k} Pm×k Q k × n Q_{k×n} Qk×n的每一个元素,可以将这个问题转化成机器学习中的回归问题进行求解(梯度下降)。

通常情况下,隐因子数量k的选取要远远低于用户和商品的数量,大矩阵分解成两个小矩阵实际上是用户和商品在k维隐因子空间上的映射,这个方法其实是也是一种"降维"(DimensionReduction)过程,同时将用户和商品的表示转化为在这个k维空间上的分布位置,商品和用户的距离越接近表示用户越有可能喜欢这商品,表现在数值上则是各项隐因子符合程度的正负性越一致.

损失函数:

可以使用原始的评分矩阵 R m × n R_{m×n} Rm×n与重新构建的评分矩阵 R ^ m × n \hat{R}_{m\times n} R^m×n之间的误差的平方作为损失函数,即:

e i , j 2 = ( r i , j − r ^ i , j ) 2 = ( r i , j − ∑ k = 1 K p i , k q k , j ) 2 e_{i,j}^2=\left ( r_{i,j}-\hat{r}_{i,j} \right )^2=\left (r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )^2 ei,j2=(ri,jr^i,j)2=(ri,jk=1Kpi,kqk,j)2

目标是求解所有的非“-”项的损失之和的最小值

m i n    l o s s = ∑ r i , j ≠ − e i , j 2 min\; loss= \sum_{r_{i,j}\neq -}e_{i,j}^2 minloss=ri,j=ei,j2

梯度下降法求解:

  • 求解损失函数的负梯度:

∂ ∂ p i , k e i , j 2 = − 2 ( r i , j − ∑ k = 1 K p i , k q k , j ) q k , j = − 2 e i , j q k , j \frac{\partial }{\partial p_{i,k}}e_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )q_{k,j}=-2e_{i,j}q_{k,j} pi,kei,j2=2(ri,jk=1Kpi,kqk,j)qk,j=2ei,jqk,j

∂ ∂ q k , j e i , j 2 = − 2 ( r i , j − ∑ k = 1 K p i , k q k , j ) p i , k = − 2 e i , j p i , k \frac{\partial }{\partial q_{k,j}}e_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )p_{i,k}=-2e_{i,j}p_{i,k} qk,jei,j2=2(ri,jk=1Kpi,kqk,j)pi,k=2ei,jpi,k

  • 根据负梯度的方向更新变量:
    p i , k ′ = p i , k − α ∂ ∂ p i , k e i , j 2 = p i , k + 2 α e i , j q k , j {p_{i,k}}'=p_{i,k}-\alpha \frac{\partial }{\partial p_{i,k}}e_{i,j}^2=p_{i,k}+2\alpha e_{i,j}q_{k,j} pi,k=pi,kαpi,kei,j2=pi,k+2αei,jqk,j
    q k , j ′ = q k , j − α ∂ ∂ q k , j e i , j 2 = q k , j + 2 α e i , j p i , k {q_{k,j}}'=q_{k,j}-\alpha \frac{\partial }{\partial q_{k,j}}e_{i,j}^2=q_{k,j}+2\alpha e_{i,j}p_{i,k} qk,j=qk,jαqk,jei,j2=qk,j+2αei,jpi,k
    通过迭代,直到算法最终收敛。

加入正则项的损失函数:

通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束,加入L2L2正则的损失函数为:

E i , j 2 = ( r i , j − ∑ k = 1 K p i , k q k , j ) 2 + β 2 ∑ k = 1 K ( p i , k 2 + q k , j 2 ) E_{i,j}^2=\left (r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )^2+\frac{\beta }{2}\sum_{k=1}^{K}\left ( p_{i,k}^2+q_{k,j}^2 \right ) Ei,j2=(ri,jk=1Kpi,kqk,j)2+2βk=1K(pi,k2+qk,j2)

利用梯度下降法的求解过程为:

  • 求解损失函数的负梯度:

∂ ∂ p i , k E i , j 2 = − 2 ( r i , j − ∑ k = 1 K p i , k q k , j ) q k , j + β p i , k = − 2 e i , j q k , j + β p i , k \frac{\partial }{\partial p_{i,k}}E_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )q_{k,j}+\beta p_{i,k}=-2e_{i,j}q_{k,j}+\beta p_{i,k} pi,kEi,j2=2(ri,jk=1Kpi,kqk,j)qk,j+βpi,k=2ei,jqk,j+βpi,k

∂ ∂ q k , j E i , j 2 = − 2 ( r i , j − ∑ k = 1 K p i , k q k , j ) p i , k + β q k , j = − 2 e i , j p i , k + β q k , j \frac{\partial }{\partial q_{k,j}}E_{i,j}^2=-2\left ( r_{i,j}-\sum_{k=1}^{K}p_{i,k}q_{k,j} \right )p_{i,k}+\beta q_{k,j}=-2e_{i,j}p_{i,k}+\beta q_{k,j} qk,jEi,j2=2(ri,jk=1Kpi,kqk,j)pi,k+βqk,j=2ei,jpi,k+βqk,j

  • 根据负梯度的方向更新变量:

p i , k ′ = p i , k − α ( ∂ ∂ p i , k e i , j 2 + β p i , k ) = p i , k + α ( 2 e i , j q k , j − β p i , k ) {p_{i,k}}'=p_{i,k}-\alpha \left ( \frac{\partial }{\partial p_{i,k}}e_{i,j}^2+\beta p_{i,k} \right )=p_{i,k}+\alpha \left ( 2e_{i,j}q_{k,j}-\beta p_{i,k} \right ) pi,k=pi,kα(pi,kei,j2+βpi,k)=pi,k+α(2ei,jqk,jβpi,k)

q k , j ′ = q k , j − α ( ∂ ∂ q k , j e i , j 2 + β q k , j ) = q k , j + α ( 2 e i , j p i , k − β q k , j ) {q_{k,j}}'=q_{k,j}-\alpha \left ( \frac{\partial }{\partial q_{k,j}}e_{i,j}^2+\beta q_{k,j} \right )=q_{k,j}+\alpha \left ( 2e_{i,j}p_{i,k}-\beta q_{k,j} \right ) qk,j=qk,jα(qk,jei,j2+βqk,j)=qk,j+α(2ei,jpi,kβqk,j)
通过迭代,直到算法最终收敛。

利用上述的过程,我们可以得到矩阵 P m × k P_{m×k} Pm×k Q k × n Q_{k×n} Qk×n,这样便可以为用户i对商品j进行打分:
∑ k = 1 K p i , k q k , j \sum_{k=1}^{K}p_{i,k}q_{k,j} k=1Kpi,kqk,j

矩阵分解算法

from numpy import *
from pylab import *
def load_data(path):
    f = open(path)
    data = []
    for line in f.readlines():
        arr = []
        lines = line.strip().split("\t")
        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)



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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值