前言
在上一篇的文章中,我们讲到了推荐系统中矩阵分解的三种方法。而这三种基本方法中,Funk-SVD由于其对稀疏数据的处理能力好以及空间复杂度低,是最合适推荐系统情景的,(Funk-SVD只是这三个基本方法里最好的,不代表就是推荐系统中最好的,还有更多衍生出来的优秀的方法,未来会给大家介绍)我们这篇文章就以Funk-SVD为基础,为大家介绍下如何求解矩阵分解时运用的梯度下降法以及其具体代码的实现。(梯度下降法的思想不仅运用于求解矩阵分解,同时也是神经网络中反向传播的基础,因此,希望大家多多留意。)
这是上一篇矩阵分解的基本方法和其优缺点,有兴趣的朋友可以看一看:推荐系统玩家:推荐系统玩家 之 矩阵分解(Matrix Factorization)的基本方法 及其 优缺点zhuanlan.zhihu.com
矩阵分解的认识
那么首先,我们来回顾一下矩阵分解的过程:
其中,
那么问题来了,我们如何将矩阵
首先,从上面的式子我们可以知道,
基于以上原理,我们采用均方差做为损失函数可以得到:
其中,
在这里,我们需要说明下,损失函数的种类也有很多,Simon Funk 在这里运用均方根误差,也就是我们所熟悉的RMSE作为损失函数,同时这也就是说Funk-SVD引入了线性回归思想的体现。同时正则化的种类也不只以上这一种。因此,我们在后续的文章中还会专门介绍损失函数的种类以及什么是过拟合现象和正则化。
那么当我们已经有了损失函数,下一步就是要求损失函数的最小值,这就是我们常说的最小化损失函数。那么,如何最小化损失函数呢?
梯度下降法
至此,我们来到了本篇文章的重点。说实话,在学习这部分的时候,我始终处于懵的状态,因为无论是书籍还是知乎上,都用了大面积的数学公式来讲解导数,偏导数,多元函数求偏导等等,导致我很难理解,那么在这里我会用简单的思路来解释。但希望大家看后有不一样的理解。其中,导数,微分的定义与推导的基本知识我默认大家都有,想要复习的朋友可以看下这篇:
初中生能看懂的梯度下降算法mp.weixin.qq.com那么,我在这里主要会解决这三个问题:
- 什么是梯度?
- 为什么梯度反方向是函数值局部下降最快的方向?为什么沿着梯度的反方向更新参数?
- 梯度下降如何运用于推荐系统中求解矩阵分解
1.什么是梯度
首先明确几个概念,一元函数曲线上的切线斜率叫做导数, 也就是函数在该点的变化率。那么多元函数是个曲面,曲面上的切线可以有无数条。那么,我们找曲面上沿
这里有一个三维的视频非常好帮助大家理解的导数,偏导数以及梯度的意义:
推荐系统玩家 之 导数,偏导数,方向导数的三维视图_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com2.为什么梯度反方向是函数值局部下降最快的方向?(梯度下降的推导)
高中的时候,我们学过一个公式叫做泰勒公式,在这里,我们以一阶泰勒展开式为例:
如上图,函数
理解了一阶泰勒展开式我们继续向下看,公式中的
因此上式就变为:
我们做梯度下降的意义就在于最小化损失函数,也就是最小化
其中,
在这里,我们需要仔细研究下这个简单的不等式。首先,
从上图可以看出,当
从这里我们就可以看出,为什么梯度反方向是函数值局部下降最快的方向。在上面我们提到过梯度方向是指函数变化率最大的方向,也就是斜率越来越大的方向,曲线会越来越陡。那么梯度的反方向,就是函数值局部下降最快的方向,斜率是越来越小的,斜率最终会为0。这也就是为什么我们高中求解二元一次方程组的最小值的时候,求导后需要导数等于0。
当我们知道了
因为
因为向量的模是个标量,因此:
其中,
至此,我们就得到了梯度下降法中参数
说了这么多,我相信还有很多朋友很懵。因为我在学习梯度下降初期,也不明白为什要理解什么表达式,为什么要知道这么多导数微分的知识。所以,我用一个例子,来给大家解释下我们上面那么长篇幅所学到内容的意义所在。
首先我们来看一个最简单的例子,我们目标函数为:
那么我来看下以
首先我们设
%matplotlib inline
import d2lzh as d2l
import math
from mxnet import nd
import numpy as np
def gd(eta):
x = 10
results = [x]
for i in range(10):
x -= eta * 2 * x # f(x) = x * x的导数为f'(x) = 2 * x
results.append(x)
print('epoch 10, x:', x)
return results
res = gd(0.2)
绘制
def show_trace(res):
n = max(abs(min(res)), abs(max(res)), 10)
f_line = np.arange(-n, n, 0.1)
d2l.set_figsize()
d2l.plt.plot(f_line, [x * x for x in f_line])
d2l.plt.plot(res, [x * x for x in res], '-o')
d2l.plt.xlabel('x')
d2l.plt.ylabel('f(x)')
show_trace(res)
我们在里稍微讲一下学习率
同样是目标函数
当我们调整学习率为
因此可以看出,
3.随机梯度下降如何运用于推荐系统中求解矩阵分解
能看到这里,我相信大家对于梯度下降应该已经有了一个很直观的理解。梯度下降的方法其实还分为批量梯度下降法BGD,随机梯度下降SGD以及min-batch 小批量梯度下降法MBGD,那么,在这里,我们会讲解随机梯度下降是如何求解推荐系统中的矩阵分解的。
我们以上的概念和例子都是基于最简单的一维函数的。那么对于多维函数,又该怎么办呢?道理其实是一样,对一元函数,我们求其导数。那么对于多元函数,我们就分别求其偏导数。
那么梯度下降参数更新的公式就变为:
还记得我们做矩阵分解时得到的损失函数么?
这其中
那么,我们的对损失函数中的
将以上两个偏导分别带入梯度下降参数更新公式
在以上公式中,
至此,随机梯度下降法的原理就解释完毕了。当然,梯度下降的方法还分为批量梯度下降法BGD,随机梯度下降SGD以及min-batch 小批量梯度下降法MBGD,我们就不在这里一一具体讲解,有兴趣的朋友可以看看这篇文章:
忆臻:详解梯度下降法的三种形式BGD、SGD以及MBGDzhuanlan.zhihu.com当我们有了参数更新的公式,就可以付诸于代码了。以下便为随机梯度下降求解矩阵分解的python代码和Matlab代码,同时数据集可以用Movielens 100k 开源数据集进行测试:
MovieLens 100K Datasetgrouplens.orgPython:
def MF(train_list, test_list, N, M, K=10, learning_rate=0.001, lamda_regularizer=0.1, max_iteration=50):
# train_list: train data
# test_list: test data
# N:the number of user
# M:the number of item
# learning_rate: the learning rata
# K: the number of latent factor
# lamda_regularizer: regularization parameters
# max_iteration: the max iteration
P = np.random.normal(0, 0.1, (N, K))
Q = np.random.normal(0, 0.1, (M, K))
train_mat = sequence2mat(sequence = train_list, N = N, M = M)
test_mat = sequence2mat(sequence = test_list, N = N, M = M)
records_list = []
for step in range(max_iteration):
los=0.0
for data in train_list:
u,i,r = data
P[u],Q[i],ls = update(P[u], Q[i], r=r, learning_rate=learning_rate, lamda_regularizer=lamda_regularizer)
los += ls
mae, rmse, recall, precision = evaluation(P, Q, train_mat, test_mat)
records_list.append(np.array([los, mae, rmse, recall, precision]))
if step % 10 ==0:
print(' step:%d n loss:%.4f,mae:%.4f,rmse:%.4f,recall:%.4f,precision:%.4f'
%(step,los,mae,rmse,recall,precision))
print(' end. n loss:%.4f,mae:%.4f,rmse:%.4f,recall:%.4f,precision:%.4f'
%(records_list[-1][0],records_list[-1][1],records_list[-1][2],records_list[-1][3],records_list[-1][4]))
return P,Q,np.array(records_list)
def update(P, Q, r, learning_rate=0.001, lamda_regularizer=0.1):
error = r - np.dot(P, Q.T)
P = P + learning_rate*(error*Q - lamda_regularizer*P)
Q = Q + learning_rate*(error*P - lamda_regularizer*Q)
loss = 0.5 * (error**2 + lamda_regularizer*(np.square(P).sum() + np.square(Q).sum()))
return P,Q,loss
Matlab:
function [F, G] = mySVDBiasedV2(X,testSet,option)
k = option.k;
lRate = option.lRate;
lambda1 = option.lambda1;
iteLimit = option.iteLimit;
[rowNum colNum] = size(X);
XInd = find(X);
a = -1;
b = 1;
F = (b-a).*rand(rowNum, k) + a;
G = (b-a).* rand(colNum, k) + a;
rowSum = (b-a) .* rand(rowNum, 1) + a;
colSum = (b-a) .* rand(colNum, 1) + a;
M = F * G';
error = [Inf];
errorRMSE = [Inf];
trainErr = [Inf];
trainRMSE = [Inf];
iteNum = 0;
while true
for rInd = 1:length(XInd)
curError = X(XInd(rInd)) - M(XInd(rInd));
[curR, curC] = ind2sub([rowNum colNum], XInd(rInd));
for kInd = 1:k
F(curR, kInd) = F(curR, kInd) + lRate * curError * G(curC, kInd) - lambda1 * F(curR, kInd);
G(curC, kInd) = G(curC, kInd) + lRate * curError * F(curR, kInd) - lambda1 * G(curC, kInd);
end
end
M = F*G';
p = [];
trainRMSE = [trainErr sqrt(sum((M(XInd) - X(XInd)).^2)/length(XInd))];
trainErr = [trainErr sum(abs(M(XInd) - X(XInd)))/length(XInd)];
if (~isempty(testSet))
for testInd = 1:size(testSet, 1)
p = [p M(testSet(testInd, 1), testSet(testInd, 2))];
end
errorRMSE = [error sqrt(sum((p - testSet(:, 3)').^2)/size(testSet, 1))];
error = [error sum(abs(p - testSet(:, 3)'))/size(testSet, 1)];
end
iteNum = iteNum + 1;
fprintf('iteNum: %d, (trainMAE = %f, testMAE = %f), (trainRMSE = %f, testRMSE = %f)n', iteNum, trainErr(end), error(end), trainRMSE(end), errorRMSE(end));
if trainErr(end) > trainErr(end-1) || iteNum > iteLimit
plot(trainErr, '-ob');
if (~isempty(testSet))
hold on
plot(error, '-sr');
hold off
end
return;
end
end
上一篇:
推荐系统玩家:推荐系统玩家 之 矩阵分解(Matrix Factorization)的基本方法 及其 优缺点zhuanlan.zhihu.com下一篇:
推荐系统玩家:推荐系统玩家 之 推荐系统入门——推荐系统的发展历程(上)zhuanlan.zhihu.com参考文献
1.https://zh.d2l.ai/chapter_optimization/gd-sgd.html
2.为什么局部下降最快的方向就是梯度的负方向?_红色石头的专栏-CSDN博客_负梯度方向
3.https://zhuanlan.zhihu.com/p/30759733
4.https://mp.weixin.qq.com/s/jrMs8U_Mg82lPxZB3h4SbQ
5.https://zh.d2l.ai/chapter_optimization/gd-sgd.html
6.https://zhuanlan.zhihu.com/p/33321183
7.https://zhuanlan.zhihu.com/p/25765735