1. 矩阵分解回顾

在博文推荐算法——基于矩阵分解的推荐算法中,提到了将用户-商品矩阵进行分解,从而实现对未打分项进行打分。矩阵分解是指将一个矩阵分解成两个或者多个矩阵的乘积。对于上述的用户-商品矩阵(评分矩阵),记为推荐算法——非负矩阵分解(NMF)_矩阵分解,可以将其分解成两个或者多个矩阵的乘积,假设分解成两个矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解_02推荐算法——非负矩阵分解(NMF)_NMF_03,我们要使得矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解_02推荐算法——非负矩阵分解(NMF)_NMF_03的乘积能够还原原始的矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解

推荐算法——非负矩阵分解(NMF)_损失函数_07

其中,矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解_02表示的是推荐算法——非负矩阵分解(NMF)_推荐算法_09个用户与推荐算法——非负矩阵分解(NMF)_非负矩阵分解_10个主题之间的关系,而矩阵推荐算法——非负矩阵分解(NMF)_NMF_03表示的是推荐算法——非负矩阵分解(NMF)_非负矩阵分解_10个主题与推荐算法——非负矩阵分解(NMF)_NMF_13个商品之间的关系。

通常在用户对商品进行打分的过程中,打分是非负的,这就要求:

推荐算法——非负矩阵分解(NMF)_非负矩阵分解_14

推荐算法——非负矩阵分解(NMF)_NMF_15

这便是非负矩阵分解(Non-negtive Matrix Factorization, NMF)的来源。

2. 非负矩阵分解

2.1. 非负矩阵分解的形式化定义

上面简单介绍了非负矩阵分解的基本含义,简单来讲,非负矩阵分解是在矩阵分解的基础上对分解完成的矩阵加上非负的限制条件,即对于用户-商品矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解,找到两个矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解_02推荐算法——非负矩阵分解(NMF)_NMF_03,使得:

推荐算法——非负矩阵分解(NMF)_损失函数_07

同时要求:

推荐算法——非负矩阵分解(NMF)_非负矩阵分解_14

推荐算法——非负矩阵分解(NMF)_NMF_15

2.2. 损失函数

为了能够定量的比较矩阵推荐算法——非负矩阵分解(NMF)_矩阵分解和矩阵推荐算法——非负矩阵分解(NMF)_推荐算法_23的近似程度,在参考文献1中作者提出了两种损失函数的定义方式:

  • 平方距离

推荐算法——非负矩阵分解(NMF)_非负矩阵分解_24

  • KL散度

推荐算法——非负矩阵分解(NMF)_推荐算法_25

在KL散度的定义中,推荐算法——非负矩阵分解(NMF)_NMF_26,当且仅当推荐算法——非负矩阵分解(NMF)_损失函数_27时取得等号。

当定义好损失函数后,需要求解的问题就变成了如下的形式,对应于不同的损失函数:

求解如下的最小化问题:

  • 推荐算法——非负矩阵分解(NMF)_NMF_28
  • 推荐算法——非负矩阵分解(NMF)_推荐算法_29

2.3. 优化问题的求解

在参考文献1中,作者提出了乘法更新规则(multiplicative update rules),具体的操作如下:

对于平方距离的损失函数:

推荐算法——非负矩阵分解(NMF)_矩阵分解_30

推荐算法——非负矩阵分解(NMF)_NMF_31

对于KL散度的损失函数:

推荐算法——非负矩阵分解(NMF)_损失函数_32

推荐算法——非负矩阵分解(NMF)_矩阵分解_33

上述的乘法规则主要是为了在计算的过程中保证非负,而基于梯度下降的方法中,加减运算无法保证非负,其实上述的乘法更新规则与基于梯度下降的算法是等价的,下面以平方距离为损失函数说明上述过程的等价性:

平方损失函数可以写成:

推荐算法——非负矩阵分解(NMF)_NMF_34

使用损失函数对推荐算法——非负矩阵分解(NMF)_NMF_35求偏导数:

KaTeX parse error: No such environment: align* at position 7: \begin{̲a̲l̲i̲g̲n̲*̲}̲ \frac{\partia…

则按照梯度下降法的思路:

推荐算法——非负矩阵分解(NMF)_矩阵分解_36

即为:

推荐算法——非负矩阵分解(NMF)_损失函数_37

推荐算法——非负矩阵分解(NMF)_推荐算法_38,即可以得到上述的乘法更新规则的形式。

2.4. 非负矩阵分解的实现

对于如下的矩阵:

推荐算法——非负矩阵分解(NMF)_非负矩阵分解_39

通过非负矩阵分解,得到如下的两个矩阵:

推荐算法——非负矩阵分解(NMF)_损失函数_40

推荐算法——非负矩阵分解(NMF)_NMF_41

对原始矩阵的还原为:

推荐算法——非负矩阵分解(NMF)_非负矩阵分解_42

实现的代码

#!/bin/python

from numpy import * 

def load_data(file_path):
	f = open(file_path)
	V = []
	for line in f.readlines():
		lines = line.strip().split("\t")
		data = []
		for x in lines:
			data.append(float(x))
		V.append(data)
	return mat(V)

def train(V, r, k, e):
	m, n = shape(V)
	W = mat(random.random((m, r)))
	H = mat(random.random((r, n)))
	
	for x in xrange(k):
		#error 
		V_pre = W * H
		E = V - V_pre
		#print E
		err = 0.0
		for i in xrange(m):
			for j in xrange(n):
				err += E[i,j] * E[i,j]
		print err

		if err < e:
			break

		a = W.T * V
		b = W.T * W * H
		#c = V * H.T
		#d = W * H * H.T
		for i_1 in xrange(r):
			for j_1 in xrange(n):
				if b[i_1,j_1] != 0:
					H[i_1,j_1] = H[i_1,j_1] * a[i_1,j_1] / b[i_1,j_1]
		
		c = V * H.T
		d = W * H * H.T
		for i_2 in xrange(m):
			for j_2 in xrange(r):
				if d[i_2, j_2] != 0:
					W[i_2,j_2] = W[i_2,j_2] * c[i_2,j_2] / d[i_2, j_2]
	
	return W,H 


if __name__ == "__main__":
	#file_path = "./data_nmf"
	file_path = "./data1"

	V = load_data(file_path)
	W, H = train(V, 2, 100, 1e-5 )

	print V
	print W
	print H
	print W * H

收敛曲线如下图所示:

推荐算法——非负矩阵分解(NMF)_损失函数_43

'''
Date:20160411
@author: zhaozhiyong
'''

from pylab import *
from numpy import *

data = []

f = open("result_nmf")
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()

参考文献