对于推荐系统(Recommend System, RS),从广义上的理解为:为用户(User)推荐相关的商品(Items)。常用的推荐算法主要有:
在推荐系统中,最重要的数据是用户对商品的打分数据,数据形式如下所示:
其中, U1⋯U5 表示的是 5 个不同的用户, D1⋯D4 表示的是 4 个不同的商品,这样便构成了用户-商品矩阵,在该矩阵中,有用户对每一件商品的打分,其中“-”表示的是用户未对该商品进行打分。
在推荐系统中有一类问题是对未打分的商品进行评分的预测。
矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为 Rm×n 。可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵 Pm×k 和 Qk×n ,我们要使得矩阵 Pm×k 和 Qk×n 的乘积能够还原原始的矩阵 Rm×n :
其中,矩阵 Pm×k 表示的是 m 个用户与 k 个主题之间的关系,而矩阵 Qk×n 表示的是 k 个主题与 n 个商品之间的关系。
在上述的矩阵分解的过程中,将原始的评分矩阵 Rm×n 分解成两个矩阵 Pm×k 和 Qk×n 的乘积:
那么接下来的问题是如何求解矩阵 Pm×k 和 Qk×n 的每一个元素,可以将这个问题转化成机器学习中的回归问题进行求解。
可以使用原始的评分矩阵 Rm×n 与重新构建的评分矩阵 R^m×n 之间的误差的平方作为损失函数,即:
最终,需要求解所有的非“-”项的损失之和的最小值:
对于上述的平方损失函数,可以通过梯度下降法求解,梯度下降法的核心步骤是
通过迭代,直到算法最终收敛。
通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束,加入 L2 正则的损失函数为:
利用梯度下降法的求解过程为:
通过迭代,直到算法最终收敛。
利用上述的过程,我们可以得到矩阵 Pm×k 和 Qk×n ,这样便可以为用户 i 对商品 j 进行打分:
对于上述的评分矩阵,通过矩阵分解的方法对其未打分项进行预测,最终的结果为:
程序代码如下:
#!/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("\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
其中,利用梯度下降法进行矩阵分解的过程中的收敛曲线如下所示:
''' 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()