Latent-factor method
上课所提到的电影推荐算法通过找到用户A的相似用户B进而向A推荐B喜欢的电影或找到A喜欢的电影C的相似电影来做推荐。而潜在因子方法是通过用户的潜在因子矩阵得知用户的电影偏好,通过电影的潜在因子矩阵得知电影的风格类型,两者相匹配得到用户喜欢的电影。
用具体例子说明潜在因子矩阵:
假设电影的类型分为恐怖,喜剧,科幻,用0~1范围内的数字表示用户对电影的喜欢程度或电影的符合程度,0表示不感兴趣或不符合,1表示非常喜欢或非常符合。那么用户A和B对各类型的喜欢程度可能如下:
恐怖 | 喜剧 | 科幻 | |
---|---|---|---|
A | 0.9 | 0.1 | 0.7 |
B | 0 | 0.9 | 0.3 |
可以看出用户A喜欢恐怖、科幻的电影,而B更喜欢喜剧电影。
假设电影C、D分别为科幻恐怖电影和喜剧电影,那么它们的符合程度可能如下:
恐怖 | 喜剧 | 科幻 | |
---|---|---|---|
C | 0.8 | 0 | 0.8 |
D | 0 | 0.9 | 0.1 |
将用户表里的每一行视为一个向量
p
u
\mathbf{p}_u
pu,电影表的每一行视为一个向量
q
i
\mathbf{q}_i
qi,它们的内积
p
u
T
q
i
\mathbf{p}_u^T\mathbf{q}_i
puTqi可以反映用户的喜欢程度。
例如用户A对C的喜欢程度:
p
a
T
q
c
=
0.9
∗
0.8
+
0.7
∗
0.8
=
1.28
\mathbf{p}_a^T\mathbf{q}_c=0.9*0.8+0.7*0.8=1.28
paTqc=0.9∗0.8+0.7∗0.8=1.28,A对D的喜欢程度:
p
a
T
q
d
=
0.1
∗
0.9
+
0.7
∗
0.1
=
0.16
\mathbf{p}_a^T\mathbf{q}_d=0.1*0.9+0.7*0.1=0.16
paTqd=0.1∗0.9+0.7∗0.1=0.16,用户B对C的喜欢程度:
p
b
T
q
c
=
0.3
∗
0.8
=
0.24
\mathbf{p}_b^T\mathbf{q}_c=0.3*0.8=0.24
pbTqc=0.3∗0.8=0.24,B对D的喜欢程度:
p
b
T
q
d
=
0.9
∗
0.9
+
0.3
∗
0.1
=
0.84
\mathbf{p}_b^T\mathbf{q}_d=0.9*0.9+0.3*0.1=0.84
pbTqd=0.9∗0.9+0.3∗0.1=0.84。可以看出A更喜欢C,B更喜欢D。推荐系统就能利用这些内积来进行推荐。潜在因子矩阵即这两张表的矩阵形式
P
\mathbf{P}
P和
Q
\mathbf{Q}
Q。
实际中我们不能直接获得潜在因子,而是获得用户对电影的总评价。如:
C | D | |
---|---|---|
A | 1.28 | 0.16 |
B | 0.24 | 0.84 |
其矩阵形式记作矩阵
R
\mathbf{R}
R。而且这个表往往大且稀疏,因为用户很多,但用户不是常常都会对电影做出评价。潜在因子方法所做的就是将矩阵R分解为潜在因子矩阵P和Q,即
R
=
P
Q
\mathbf{R}=\mathbf{P}\mathbf{Q}
R=PQ。
首先定义R矩阵在(u,i)位置上的
r
u
i
r_{ui}
rui为用户u对电影i的感兴趣程度,而
r
^
u
i
\hat{r}_{ui}
r^ui为预测,我们通过潜在因子做出预测:
r
^
u
i
=
p
u
T
q
i
=
∑
k
K
p
u
k
q
k
i
\hat{r}_{ui}=\mathbf{p}_u^T\mathbf{q}_i=\sum\limits_k^Kp_{uk}q_{ki}
r^ui=puTqi=k∑Kpukqki
p
u
\mathbf{p}_u
pu和
q
i
\mathbf{q}_i
qi为潜在因子矩阵P,Q的一行,
p
u
k
p_{uk}
puk表示用户u对电影分类k的喜欢程度,
q
k
i
q_{ki}
qki表示电影i对k分类的匹配程度,K为总类数。
我们希望用户的评价
R
\mathbf{R}
R和我们的预测
R
^
\mathbf{\hat{R}}
R^接近,可以通过最小化损失函数,损失函数L选择均方误差MSE:
min
P
,
Q
L
=
∑
(
u
,
i
)
(
r
u
i
−
p
u
T
q
i
)
2
\min\limits_{P,Q}L=\sum\limits_{(u,i)}(r_{ui}-\mathbf{p}_u^T\mathbf{q}_i)^2
P,QminL=(u,i)∑(rui−puTqi)2
损失函数也可以加入正则化项防止过拟合,加入后如下:
L
=
∑
(
u
,
i
)
(
r
u
i
−
p
u
T
q
i
)
2
+
λ
∑
u
∣
∣
p
u
∣
∣
2
+
λ
∑
i
∣
∣
q
i
∣
∣
2
L = \sum\limits_{(u,i)}(r_{ui}-\mathbf{p}_u^T\mathbf{q}_i)^2+\lambda\sum\limits_u||\mathbf{p}_u||^2+\lambda\sum\limits_i||\mathbf{q}_i||^2
L=(u,i)∑(rui−puTqi)2+λu∑∣∣pu∣∣2+λi∑∣∣qi∣∣2
求解方法
交替最小二乘法
由于矩阵P和Q都未知,都需要求解,所以我们可以在求解P时固定Q,转换成最小二乘法问题,反之求Q时固定P,交替执行来求解直至误差满足条件或到达迭代上限。
具体过程如下:
1.指定
Q
\mathbf{Q}
Q初值
Q
0
\mathbf{Q}_0
Q0,可以随机生成
2.固定
Q
0
\mathbf{Q}_0
Q0,求解
P
0
\mathbf{P}_0
P0
3.固定
P
0
\mathbf{P}_0
P0,求解
Q
1
\mathbf{Q}_1
Q1
4.固定
Q
1
\mathbf{Q}_1
Q1,求解
P
1
\mathbf{P}_1
P1
…(不断重复)
5.满足误差需求或迭代上限,迭代结束。
具体过程:
固定Q,求解P时,
min
P
,
Q
L
\min\limits_{P,Q}L
P,QminL可以转化为:
min
P
[
∑
u
,
i
(
r
u
i
−
p
u
T
q
i
)
2
]
+
λ
∑
u
∣
∣
p
u
∣
∣
2
=
∑
u
min
P
[
∑
i
(
r
u
i
−
p
u
T
q
i
)
2
]
+
λ
∣
∣
p
u
∣
∣
2
]
\min\limits_P[\sum\limits_{u,i}(r_{ui}-\mathbf{p}_u^T\mathbf{q}_i)^2]+\lambda\sum\limits_{u}||\mathbf{p}_u||^2=\sum\limits_u\min\limits_P[\sum\limits_i(r_{ui}-\mathbf{p}_u^T\mathbf{q}_i)^2]+\lambda||\mathbf{p}_u||^2]
Pmin[u,i∑(rui−puTqi)2]+λu∑∣∣pu∣∣2=u∑Pmin[i∑(rui−puTqi)2]+λ∣∣pu∣∣2]
令
l
u
(
p
u
)
=
∑
i
(
r
u
i
−
p
u
T
q
i
)
2
+
λ
∣
∣
p
u
∣
∣
2
l_u(\mathbf{p}_u)=\sum\limits_i(r_{ui}-\mathbf{p}_u^T\mathbf{q_i})^2+\lambda||\mathbf{p}_u||^2
lu(pu)=i∑(rui−puTqi)2+λ∣∣pu∣∣2
问题转化为最小化
l
u
(
p
u
)
l_u(\mathbf{p}_u)
lu(pu)
求偏导得:
∂
l
u
∂
p
u
=
2
(
∑
i
p
u
T
q
i
q
i
−
∑
i
r
u
i
q
i
+
λ
p
u
)
\frac{\partial l_u}{\partial \mathbf{p}_u}=2(\sum\limits_i\mathbf{p}_u^T\mathbf{q}_i\mathbf{q}_i-\sum\limits_ir_{ui}\mathbf{q}_i+\lambda\mathbf{p}_u)
∂pu∂lu=2(i∑puTqiqi−i∑ruiqi+λpu)
令偏导值为0,有:
(
∑
i
q
i
q
i
T
+
λ
I
)
p
u
=
∑
i
r
u
i
q
i
(\sum\limits_i\mathbf{q}_i\mathbf{q}_i^T+\lambda\mathbf{I})\mathbf{p}_u=\sum\limits_ir_{ui}\mathbf{q}_i
(i∑qiqiT+λI)pu=i∑ruiqi
(
Q
Q
T
+
λ
I
)
p
u
=
Q
r
u
(\mathbf{Q}\mathbf{Q}^T+\lambda\mathbf{I})\mathbf{p}_u=\mathbf{Q}\mathbf{r}_u
(QQT+λI)pu=Qru
p
u
=
(
Q
Q
T
+
λ
I
)
−
1
Q
r
u
\mathbf{p}_u=(\mathbf{Q}\mathbf{Q}^T+\lambda\mathbf{I})^{-1}\mathbf{Q}\mathbf{r}_u
pu=(QQT+λI)−1Qru
求出每一行
p
u
\mathbf{p}_u
pu就可以得到
P
\mathbf{P}
P
固定P则可以得到:
q
i
=
(
P
P
T
+
λ
I
)
−
1
P
r
i
\mathbf{q}_i=(\mathbf{P}\mathbf{P}^T+\lambda\mathbf{I})^{-1}\mathbf{P}\mathbf{r}_i
qi=(PPT+λI)−1Pri
梯度下降法
也可以通过梯度下降法求解。使用梯度下降算法:
1.求L的偏导
∂
L
∂
p
u
k
=
−
2
(
r
u
i
−
∑
k
=
1
K
p
u
k
q
k
i
)
q
k
i
+
2
λ
p
u
k
\frac{\partial L}{\partial p_{uk}}=-2(r_{ui}-\sum\limits_{k=1}^Kp_{uk}q_{ki})q_{ki}+2\lambda p_{uk}
∂puk∂L=−2(rui−k=1∑Kpukqki)qki+2λpuk
∂
L
∂
q
k
i
=
−
2
(
r
u
i
−
∑
k
=
1
K
p
u
k
q
k
i
)
p
u
k
+
2
λ
p
k
i
\frac{\partial L}{\partial q_{ki}}=-2(r_{ui}-\sum\limits_{k=1}^Kp_{uk}q_{ki})p_{uk}+2\lambda p_{ki}
∂qki∂L=−2(rui−k=1∑Kpukqki)puk+2λpki
转换成对向量求偏导
∂
L
∂
p
u
=
∑
i
2
(
p
u
T
q
i
−
r
u
i
)
q
i
+
2
λ
p
u
\frac{\partial L}{\partial \mathbf{p}_u}=\sum\limits_i2(\mathbf{p}_u^T\mathbf{q}_i-r_{ui})\mathbf{q}_i+2\lambda \mathbf{p}_u
∂pu∂L=i∑2(puTqi−rui)qi+2λpu
对
q
i
\mathbf{q}_i
qi类似
2.迭代更新
p
u
=
p
u
−
α
∂
L
∂
p
u
\mathbf{p}_u=\mathbf{p}_u-\alpha\frac{\partial L}{\partial \mathbf{p}_u}
pu=pu−α∂pu∂L
对
q
i
\mathbf{q}_i
qi类似
其中
α
\alpha
α为学习率,
λ
\lambda
λ为正则化系数。
代码实现
import numpy as np
import pandas as pd
# R:评分矩阵
# K:电影种类数
# epochs: 最大迭代次数
# alpha:学习率
# lamda:正则化系数
# P:用户潜在因子矩阵
# Q:电影潜在因子矩阵
# M:用户数
# N:电影数
def LFM( R, K=3, epochs=1000, alpha=0.0001, lamda=0.001 ):
# 初始化
M = len(R)
N = len(R[0])
P = np.random.rand(M, K)
Q = np.random.rand(K, N)
for epoch in range(epochs):
for u in range(M):
for i in range(N):
if R[u][i] > 0:
eui = np.dot( P[u,:], Q[:,i] ) - R[u][i]
for k in range(K):
P[u][k] = P[u][k] - alpha * ( 2 * eui * Q[k][i] + 2 * lamda * P[u][k] )
Q[k][i] = Q[k][i] - alpha * ( 2 * eui * P[u][k] + 2 * lamda * Q[k][i] )
R_ = np.dot( P, Q )
Loss = 0
for u in range(M):
for i in range(N):
if R[u][i] > 0:
Loss += ( np.dot( P[u,:], Q[:,i] ) - R[u][i] ) ** 2
# 加上正则化项
for k in range(K):
Loss += lamda * ( P[u][k] ** 2 + Q[k][i] ** 2 )
if Loss < 0.0001:
break
return P, Q.T, Loss
测试:
使用了上课的例子
R=[[5,4,4,0,5],[0,3,5,3,4],[5,2,0,2,3],
[0,2,3,1,2],[4,0,5,4,5],[5,3,0,3,5],
[3,2,3,2,0],[5,3,4,0,5],[4,2,5,4,0],
[5,0,5,3,4]]
R = np.array(R)
K = 5
epochs = 5000
alpha = 0.0003
lamda = 0.001
P, Q, Loss = LFM(R, K, epochs, alpha, lamda)
print(P)
print(Q)
print(Loss)
R_ = P.dot(Q.T)
print(R)
print(R_)
结果如下: