一、推荐算法概述
对于推荐系统(Recommend System, RS),从广义上的理解为:为用户(User)推荐相关的商品(Items)。常用的推荐算法主要有:
基于内容的推荐(Content-Based Recommendation)
协同过滤的推荐(Collaborative Filtering Recommendation)
基于关联规则的推荐(Association Rule-Based Recommendation)
基于效用的推荐(Utility-Based Recommendation)
基于知识的推荐(Knowledge-Based Recommendation)
组合推荐(Hybrid Recommendation)
在推荐系统中,最重要的数据是用户对商品的打分数据,数据形式如下所示:
D1 | D2 | D3 | D4 | |
U1 | 5 | 3 | - | 1 |
U2 | 4 | - | - | 1 |
U3 | 1 | 1 | - | 5 |
U4 | 1 | - | - | 4 |
U5 | - | 1 | 5 | 4 |
其中,U1⋯U5表示的是5个不同的用户,D1⋯D4表示的是4个不同的商品,这样便构成了用户-商品矩阵,在该矩阵中,有用户对每一件商品的打分,其中“-”表示的是用户未对该商品进行打分。
在推荐系统中有一类问题是对未打分的商品进行评分的预测。
二、基于矩阵分解的推荐算法
2.1、矩阵分解的一般形式
矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为Rm×n。可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵Pm×k和Qk×n,我们要使得矩阵Pm×k和Qk×n的乘积能够还原原始的矩阵Rm×n:
其中,矩阵Pm×k表示的是m个用户与k个主题之间的关系,而矩阵Qk×n表示的是k个主题与n个商品之间的关系。
2.2、利用矩阵分解进行预测
在上述的矩阵分解的过程中,将原始的评分矩阵Rm×n分解成两个矩阵Pm×k和Qk×n的乘积:
那么接下来的问题是如何求解矩阵Pm×k和Qk×n的每一个元素,可以将这个问题转化成机器学习中的回归问题进行求解。
2.2.1、损失函数
可以使用原始的评分矩阵Rm×n与重新构建的评分矩阵R^m×n之间的误差的平方作为损失函数,即:
最终,需要求解所有的非“-”项的损失之和的最小值:
2.2.2、损失函数的求解
对于上述的平方损失函数,可以通过梯度下降法求解,梯度下降法的核心步骤是
求解损失函数的负梯度:
根据负梯度的方向更新变量:
通过迭代,直到算法最终收敛。
2.2.3、加入正则项的损失函数即求解方法
通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束,加入L2正则的损失函数为:
利用梯度下降法的求解过程为:
求解损失函数的负梯度:
根据负梯度的方向更新变量:
通过迭代,直到算法最终收敛。
2.2.4、预测
利用上述的过程,我们可以得到矩阵Pm×k和Qk×n,这样便可以为用户i对商品j进行打分:
代码实现:
import numpy as np
import random
'''
mat_data R(m x n )
d1 d2 d3 dn
u1
u2
u3
um
P(m x k )
s1 s2 s3 sk
u1
u2
u3
um
Q(k x n)
d1 d2 d3 dn
s1
s2
s3
sk
'''
#原始矩阵,主题数,迭代次数,步长
def SGD(mat_data,k,iterate_times,alpha):
m = mat_data.shape[0] #shape[0]是矩阵的行
n = mat_data.shape[1] #shape[1]是矩阵的列
#随机数据初始化
P = np.random.random((m,k))
Q = np.random.random((k,n))
for times in range(iterate_times):
i = random.randint(0,m-1)
j = random.randint(0,n-1)
P[i,:] -= alpha * (np.dot(P[i,:],Q[:,j]) - mat_data[i,j])* Q[:,j]
Q[:,j] -= alpha *( np.dot(P[i,:],Q[:,j]) - mat_data[i,j] )* P[i,:]
#打印误差
# print(abs(np.dot(P[i,:],Q[:,j]) - mat_data[i,j] ))
return P,Q
'''
--------------------------begin--------------------------------------
'''
data = np.array([
[0,63,3,7],
[2,3,0,6],
[3,5,46,1]
],float)
a = SGD(data,1000,50000,0.001)
#打印P,Q,以及预测mat M
print(a[0])
print(a[1])
print(np.dot(a[0],a[1]))