推荐系统中的矩阵分解(一)(ALS,SVD,BiasdSVD,SVD ++,surprise工具)
矩阵分解在推荐系统中的位置
首先,我们来看看矩阵分解在推荐系统中的位置,矩阵分解的思想属于协同过滤,推荐系统中的主流思想之一。
基于领域的推荐和基于模型的推荐的区别在于:
- 基于邻域的协同过滤包括UserCF, ItemCF,将用户的所有数据读入到内存中进行运算,也称之为基于内存的协同过滤(Memory-based)。数据量少的情况下,可以在线实时推荐。
- 基于模型的推荐(Model-based),采用机器学习的方式,分成训练集和测试集。离线训练时间比较长,但训练完成后,推荐过程比较快。
推荐系统应用的两大场景:
- 评分预测(主要用于评价网站)矩阵分解主要应用在此处
- TOP-N推荐 (常用于购物网站,拿不到用户的显性评分,通过隐式反馈给用户推荐的前n个item的列表)
评分矩阵分解
评分矩阵往往是稀疏的,我们需要根据已有的评分来估计出评分矩阵中空缺的部分,使得评分矩阵能够反映用户的喜欢程度,同时我们也可以将预测出用户评分前N个推荐给用户。
如何从评分矩阵中分解出User矩阵和Item矩阵,只有左侧的R的某些值是已知的,user矩阵和item矩阵都是未知的,学习出User矩阵和Item矩阵,使得User矩阵*Item矩阵与评分矩阵中已知的评分差异最小 => 最优化问题。
什么是矩阵分解
r
u
i
r_{ui}
rui表示用户u 对item i 的评分,当 >0时,表示有评分,当=0时,表示没有评分
x
u
x_u
xu表示用户u 的向量,k维列向量
y
i
y_i
yi表示item i 的向量,k维列向量
用户矩阵X,用户数N
X
=
[
x
1
,
x
2
,
.
.
.
,
x
N
]
X = [x_1,x_2,...,x_N]
X=[x1,x2,...,xN]
商品矩阵Y,商品数为M
Y
=
[
y
1
,
y
2
,
.
.
.
,
y
M
]
Y = [y_1,y_2,...,y_M]
Y=[y1,y2,...,yM]
用户向量与物品向量的内积,表示用户u 对物品i 的预测评分,矩阵的目标函数:
min
X
,
Y
∑
r
u
i
≠
0
(
r
u
i
−
x
u
T
y
i
)
2
+
λ
(
∑
u
∥
x
u
∥
2
2
+
∑
i
∥
y
i
∥
2
2
)
\mathop {\min }\limits_{X,Y} \sum\limits_{{r_{ui}} \ne 0} {{{\left( {{r_{ui}} - x_u^T{y_i}} \right)}^2}} + \lambda \left( {\sum\limits_u {\left\| {{x_u}} \right\|_2^2 + } \sum\limits_i {\left\| {{y_i}} \right\|_2^2} } \right)
X,Yminrui=0∑(rui−xuTyi)2+λ(u∑∥xu∥22+i∑∥yi∥22)
(L2正则项,保证数值计算稳定性,防止过拟合)
目标函数最优化问题的工程解法:
- ALS,Alternating Least Squares,交替最小二乘法
- SGD,Stochastic Gradient Descent,随机梯度下降
ALS算法
ALS, Alternative Least Square, ALS,交替最小二乘法
-
Step1,固定Y 优化X
-
Step2,固定X 优化Y
-
重复Step1和2,直到X 和Y 收敛。每次固定一个矩阵,优化另一个矩阵,都是最小二乘问题
-
step1. 固定Y优化X
min X u ∑ r u i ≠ 0 ( r u i − x u T y i ) 2 + λ ∑ u ∥ x u ∥ 2 2 \mathop {\min }\limits_{{X_u}} \sum\limits_{{r_{ui}} \ne 0} {{{\left( {{r_{ui}} - x_u^T{y_i}} \right)}^2}} + \lambda \sum\limits_u {\left\| {{x_u}} \right\|_2^2} Xuminrui=0∑(rui−xuTyi)2+λu∑∥xu∥22
将目标函数转化为矩阵表达式:
J ( x u ) = ( R u − Y u T x u ) T ( R u − Y u T x u ) + λ x u T x u J({x_u}) = {\left( {{R_u} - Y_u^T{x_u}} \right)^T}\left( {{R_u} - Y_u^T{x_u}} \right) + \lambda x_u^T{x_u} J(xu)=(Ru−YuTxu)T(Ru−YuTxu)+λxuTxu
R u = [ r u i 1 , … , r u i m ] T {R_u} = {[{r_{u{i_1}}}, \ldots ,{r_{u{i_m}}}]^T} Ru=[rui1,…,ruim]T用户u 对m个物品的评分
Y u = [ y i 1 , … , y i m ] {Y_u} = [{y_{{i_1}}}, \ldots ,{y_{{i_m}}}] Yu=[yi1,…,yim]m 个物品的向量 -
对目标函数J关于xu 求梯度,并令梯度为零,得
∂ J ( x u ) ∂ x u = − 2 Y u ( R u − Y u T x u ) + 2 λ x u = 0 \frac{{\partial J({x_u})}}{{\partial {x_u}}} = - 2{Y_u}\left( {{R_u} - Y_u^T{x_u}} \right) + 2\lambda {x_u} = 0 ∂xu∂J(xu)=−2Yu(Ru−YuTxu)+2λxu=0
求解后可得:
x u = ( Y u Y u T + λ I ) − 1 Y u R u {x_u} = {\left( {{Y_u}Y_u^T + \lambda I} \right)^{ - 1}}{Y_u}{R_u} xu=(YuYuT+λI)−1YuRu
SGD算法
梯度下降法:
h
θ
(
x
)
=
θ
0
+
θ
1
x
1
+
θ
2
x
2
+
⋯
+
θ
n
x
n
{h_\theta }(x) = {\theta _0} + {\theta _1}{x_1} + {\theta _2}{x_2} + \cdots + {\theta _n}{x_n}
hθ(x)=θ0+θ1x1+θ2x2+⋯+θnxn
用它来模拟y,需要将损失函数最小化
损失函数:
J
(
Θ
)
=
1
2
∑
i
=
1
m
(
h
θ
(
x
)
−
y
)
2
J(\Theta ) = \frac{1}{2}\sum\limits_{i = 1}^m {{{\left( {{h_\theta }(x) - y} \right)}^2}}
J(Θ)=21i=1∑m(hθ(x)−y)2
a表示学习率(步长)
Θ
1
=
Θ
0
−
α
∇
J
(
Θ
)
{\Theta _1} = {\Theta _0} - \alpha \nabla J(\Theta )
Θ1=Θ0−α∇J(Θ)
梯度下降法的更新过程:(更新每个特征参数,j=0…n)
θ
j
=
θ
j
−
α
∂
∂
θ
j
J
(
Θ
)
∂
∂
θ
j
J
(
Θ
)
=
∂
∂
θ
j
1
2
(
h
θ
(
x
)
−
y
)
2
l
=
2
⋅
1
2
(
h
θ
(
x
)
−
y
)
⋅
∂
∂
θ
j
(
h
θ
(
x
)
−
y
)
=
(
h
θ
(
x
)
−
y
)
⋅
∂
∂
θ
j
(
∑
i
=
0
n
θ
i
x
i
−
y
)
=
(
h
θ
(
x
)
−
y
)
x
j
\begin{array}{l} {\theta _j} = {\theta _j} - \alpha \frac{\partial }{{\partial {\theta _j}}}J(\Theta )\\ \frac{\partial }{{\partial {\theta _j}}}J(\Theta ) = \frac{\partial }{{\partial {\theta _j}}}\frac{1}{2}{({h_\theta }(x) - y)^2} {l} = 2 \cdot \frac{1}{2}({h_\theta }(x) - y) \cdot \frac{\partial }{{\partial {\theta _j}}}({h_\theta }(x) - y)\\ = \left( {{h_\theta }(x) - y} \right) \cdot \frac{\partial }{{\partial {\theta _j}}}\left( {\sum\limits_{i = 0}^n {{\theta _i}} {x_i} - y} \right)\\ = \left( {{h_\theta }(x) - y} \right){x_j} \end{array}
θj=θj−α∂θj∂J(Θ)∂θj∂J(Θ)=∂θj∂21(hθ(x)−y)2l=2⋅21(hθ(x)−y)⋅∂θj∂(hθ(x)−y)=(hθ(x)−y)⋅∂θj∂(i=0∑nθixi−y)=(hθ(x)−y)xj
SVD矩阵分解在推荐系统中的应用及其扩展
奇异值分解SVD
矩阵分解中的问题
- 很多矩阵都是非对称的
- 矩阵A不是方阵,即维度为m*n
我们可以将它转化为对称的方阵,因为:
A
A
T
A{A^T}
AAT与
A
T
A
{A^T}A
ATA是对称的方阵
我们可以将A和A的转置矩阵进行相乘,得到对称方阵:
A
A
T
=
P
Λ
1
P
T
A
T
A
=
Q
Λ
2
Q
T
\begin{array}{l} A{A^T} = P{\Lambda _1}{P^T}\\ {A^T}A = Q{\Lambda _2}{Q^T} \end{array}
AAT=PΛ1PTATA=QΛ2QT
此时
Λ
1
{\Lambda _1}
Λ1和
Λ
2
{\Lambda _2}
Λ2均为对角矩阵,具有相同的非零特征值。
假设这些特征值为
σ
1
,
σ
2
,
…
,
σ
k
{\sigma _1},{\sigma _2}, \ldots ,{\sigma _k}
σ1,σ2,…,σk,k不超过m和n,也就是k<=min(m,n)
此时矩阵A的特征值:
λ
1
=
σ
1
,
λ
2
=
σ
2
,
…
,
λ
k
=
σ
k
{\lambda _1} = \sqrt {{\sigma _1}} ,{\lambda _2} = \sqrt {{\sigma _2}} , \ldots ,{\lambda _k} = \sqrt {{\sigma _k}}
λ1=σ1,λ2=σ2,…,λk=σk
我们可以得到为奇异值分解:
A
=
P
Λ
Q
T
A = P\Lambda {Q^T}
A=PΛQT
P为左奇异矩阵,mxm维
Q为右奇异矩阵,n*n维
Λ对角线上的非零元素为特征值λ1, λ2, … , λk
在推荐系统中
左奇异矩阵:User矩阵
右奇异矩阵:Item矩阵
但是传统的SVD不适用于推荐系统,主要有以下几个方面
- SVD分解要求矩阵是稠密的 => 矩阵中的元素不能有缺失
- 所以,类似于数据清洗,我们需要先对矩阵中的缺失元素进行补全
- 先有鸡,还是先有蛋。实际上传统SVD更适合做降维
存在的问题:
- 矩阵往往是稀疏的,大量缺失值 => 计算量大
- 填充方式简单粗暴 => 噪音大
因此有以下三种SVD的变种算法用于推荐系统中
FunkSVD算法
FunkSVD算法思想:
- 我们需要设置k,来对矩阵近似求解
- 矩阵补全以后,再预测,实际上噪音大。矩阵分解之后的还原,只需要关注与原来矩阵中有值的位置进行对比即可,不需要对所有元素进行对比
解决思路:
避开稀疏问题,而且只用两个矩阵进行相乘:
M
m
×
n
≈
P
m
×
k
T
Q
k
×
n
{M_{m \times n}} \approx P_{m \times k}^TQ_{k \times n}^{}
Mm×n≈Pm×kTQk×n
损失函数 = P和Q矩阵乘积得到的评分,与实际用户评分之差
让损失函数最小化 => 最优化问题
FunkSVD算法步骤:
- 最小化损失函数: ∑ i , j ( m i j − q j T p i ) 2 \sum\limits_{i,j} {{{\left( {{m_{ij}} - q_j^T{p_i}} \right)}^2}} i,j∑(mij−qjTpi)2
- 为了防止过拟合,增加正则项
arg min p i q j ∑ ( i , j ) ∈ K ( m i j − q j T p i ) 2 + λ ( ∥ p i ∥ 2 2 + ∥ q j ∥ 2 2 ) \arg \mathop {\min }\limits_{{p_i}{q_j}} \sum\limits_{(i,j) \in K} {{{\left( {{m_{ij}} - q_j^T{p_i}} \right)}^2}} + \lambda \left( {\left\| {{p_i}} \right\|_2^2 + \left\| {{q_j}} \right\|_2^2} \right) argpiqjmin(i,j)∈K∑(mij−qjTpi)2+λ(∥pi∥22+∥qj∥22)
1,通过梯度下降法,求解P和Q使得损失函数最小化
2,通过P和Q将矩阵补全
3,针对某个用户i,查找之前值缺失的位置,按照补全值从大到小进行推荐
BiasSVD算法
由于用户有自己的偏好,比如有些用户打分偏高;并且商品也有自己的偏好,比如商品的质量好
因此有了biasSVD算法,考虑到用户的偏好与商品的偏好
将与个性化无关的部分,设置为偏好(Bias)部分
b
i
j
=
μ
+
b
i
+
b
j
{b_{ij}} = \mu + {b_i} + {b_j}
bij=μ+bi+bj
μ:所有记录的整体平均数
bi:用户偏好(与商品无关)
bj:商品偏好(与用户无关
优化目标函数:
arg
min
p
i
q
j
∑
(
i
,
j
)
∈
K
(
m
i
j
−
μ
−
b
i
−
b
j
−
q
j
T
p
i
)
2
+
λ
(
∥
p
i
∥
2
2
+
∥
q
j
∥
2
2
+
∥
b
i
∥
2
2
+
∥
b
j
∥
2
2
)
\arg \mathop {\min }\limits_{{p_i}{q_j}} \sum\limits_{(i,j) \in K} {{{\left( {{m_{ij}} - \mu - {b_i} - {b_j} - q_j^T{p_i}} \right)}^2}} + \lambda \left( {\left\| {{p_i}} \right\|_2^2 + \left\| {{q_j}} \right\|_2^2 + \left\| {{b_i}} \right\|_2^2 + \left\| {{b_j}} \right\|_2^2} \right)
argpiqjmin(i,j)∈K∑(mij−μ−bi−bj−qjTpi)2+λ(∥pi∥22+∥qj∥22+∥bi∥22+∥bj∥22)
在迭代过程中,bi,bj的初始值可以设置为0向量,然后进行迭代:
b
i
=
b
i
+
α
(
m
i
j
−
μ
−
b
i
−
b
j
−
q
j
T
p
i
−
λ
b
i
)
b
j
=
b
j
+
α
(
m
i
j
−
μ
−
b
i
−
b
j
−
q
j
T
p
i
−
λ
b
j
)
\begin{array}{l} {b_i} = {b_i} + \alpha \left( {{m_{ij}} - \mu - {b_i} - {b_j} - q_j^T{p_i} - \lambda {b_i}} \right)\\ {b_j} = {b_j} + \alpha \left( {{m_{ij}} - \mu - {b_i} - {b_j} - q_j^T{p_i} - \lambda {b_j}} \right) \end{array}
bi=bi+α(mij−μ−bi−bj−qjTpi−λbi)bj=bj+α(mij−μ−bi−bj−qjTpi−λbj)
最终得到P和Q
SVD++算法
SVD++算法原理:
在BiasSVD算法基础上进行了改进,考虑用户的隐式反馈
- 隐式反馈:没有具体的评分,但可能有点击,浏览等行为
- 对于某一个用户i,假设他的隐式反馈item集合为I(i)
- 用户i对商品j对应的隐式反馈修正值为 c i j c_{ij} cij
- 用户i所有的隐式反馈修正值之和为 ∑ s ∈ N ( i ) c s j \sum\limits_{s \in N(i)} {{c_{sj}}} s∈N(i)∑csj
- 优化目标函数:
arg min p i q j ∑ ( i , j ) ∈ K ( m i j − μ − b i − b j − q j T p i − q j T ∣ I ( i ) ∣ − 1 2 ∑ s ∈ I ( i ) y s ) 2 + λ ( ∥ p i ∥ 2 2 + ∥ q j ∥ 2 2 + ∥ b i ∥ 2 2 + ∥ b j ∥ 2 2 + ∑ s ∈ I ( i ) ∥ y s ∥ 2 ) \arg \mathop {\min }\limits_{{p_i}{q_j}} \sum\limits_{(i,j) \in K} {{{\left( {{m_{ij}} - \mu - {b_i} - {b_j} - q_j^T{p_i} - q_j^T{{\left| {I(i)} \right|}^{ - \frac{1}{2}}}\sum\limits_{s \in I(i)} {{y_s}} } \right)}^2}} + \lambda \left( {\left\| {{p_i}} \right\|_2^2 + \left\| {{q_j}} \right\|_2^2 + \left\| {{b_i}} \right\|_2^2 + \left\| {{b_j}} \right\|_2^2 + \sum\limits_{s \in I(i)} {{{\left\| {{y_s}} \right\|}^2}} } \right) argpiqjmin(i,j)∈K∑⎝⎛mij−μ−bi−bj−qjTpi−qjT∣I(i)∣−21s∈I(i)∑ys⎠⎞2+λ⎝⎛∥pi∥22+∥qj∥22+∥bi∥22+∥bj∥22+s∈I(i)∑∥ys∥2⎠⎞
在考虑用户隐式反馈的情况下,最终得到P和Q
surprise工具简介
biasSVD算法
使用Surprise工具中的SVD
参数:
- n_factors: k值,默认为100
- n_epochs:迭代次数,默认为20
- biased:是否使用biasSVD,默认为True
- verbose:输出当前epoch,默认为False
- reg_all:所有正则化项的统一参数,默认为0.02
- reg_bu:bu的正则化参数,reg_bi:bi的正则化参数
- reg_pu:pu的正则化参数,reg_qi:qi的正则化参数
funkSVD算法
使用Surprise工具中的SVD
参数:
- n_factors: k值,默认为100
- n_epochs:迭代次数,默认为20
- biased:是否使用biasSVD,设置为False
- verbose:输出当前epoch,默认为False
- reg_all:所有正则化项的统一参数,默认为0.02
- reg_bu:bu的正则化参数,reg_bi:bi的正则化参数
- reg_pu:pu的正则化参数,reg_qi:qi的正则化参数
SVD++算法
使用Surprise工具中的SVDpp
参数:
- n_factors: k值,默认为20
- n_epochs:迭代次数,默认为20
- verbose:输出当前epoch,默认为False
- reg_all:所有正则化项的统一参数,默认为0.02
- reg_bu:bu的正则化参数,reg_bi:bi的正则化参数
- reg_pu:pu的正则化参数,reg_qi:qi的正则化参数
- reg_yj:yj的正则化参数
利用surprise工具预测movielens简单数据集(SVD,biasSVD,SVD++)
from surprise import Dataset
from surprise import Reader
from surprise import BaselineOnly, KNNBasic, NormalPredictor
from surprise import accuracy
from surprise.model_selection import KFold, split
from surprise import SVD,SVDpp
#import pandas as pd
# 数据读取
reader = Reader(line_format='user item rating timestamp', sep=',', skip_lines=1)
data = Dataset.load_from_file('./ratings.csv', reader=reader)
#rain_set = data.build_full_trainset()
train_s,test_s = split.train_test_split(data, train_size=0.8)
algo1 = SVD()
algo2 = SVD(biased = False)
algo3 = SVDpp()
print('SVDbias结果')
algo1.fit(train_s)
pre = algo1.predict(test_s)
accuracy.rmse(pre,verbose=True)
print('SVD结果')
algo2.fit(train_s)
pre = algo2.predict(test_s)
accuracy.rmse(pre,verbose=True)
print('SVD++结果')
algo3.fit(train_s)
pre = algo3.predict(test_s)
accuracy.rmse(pre,verbose=True)
得到结果:RMSE: 0.8321 SVD结果 RMSE: 0.8540 SVD++结果 RMSE: 0.8198
可以看到SVD++预测的效果最好,但是在训练过程中SVD++所要花费的时间也是最长的。
版权声明:本文为博主原创文章,遵循 C 版权协议,转载请附上原文出处链接和本声明。