一.矩阵分解是什么?
度娘定义:矩阵分解(Matrix Factorization, MF)是将矩阵拆解为数个矩阵的乘积。
狐仙定义:矩阵分解(Matrix Factorization, MF)就是将一个大矩阵分解为两个或多个小矩阵。
二.矩阵分解干什么?
如上图所示,矩阵算法就是将用户和产品矩阵中的数据,分解成两个矩阵(用User矩阵和Item矩阵),两个矩阵相乘得到的结果就是预测评分。当我们要计算第i 个用户对第j 个item的预测评分时),我们就可以用User矩阵的第i行和Item矩阵的第j 列做内积,这个内积的值就是预测评分了。对于某个用户进行推荐时,我们把他的用户向量和所有item向量做内积,然后按内积从大到小排序,取出前K 个item,过滤掉历史item后推荐给用户。 那MF是如何从评分矩阵中分解出User矩阵和Item矩阵的呢?简单来说,MF把User矩阵和Item矩阵作为未知量,用它们表示出每个用户对每个item的预测评分,然后通过最小化预测评分跟已知实际评分的差异,学习出User矩阵和Item矩阵。
也就是说: 我们的目的就是利用已知的评分来预测出未知的评分。
三.矩阵算法推导
1.首先我们要定义一个类似于上图的评分矩阵,用
R
R
R表示,其维度为
N
?
M
N*M
N?M,也就是
R
R
R为
N
N
N行
M
M
M列矩阵。 然后我们将其分解
P
P
P矩阵与
Q
Q
Q矩阵,其中
P
P
P矩阵维度为
N
?
K
N*K
N?K,
Q
Q
Q矩阵维度为
K
?
M
K*M
K?M 于是我们可以得到
R
≈
R\approx
R≈
R
^
=
P
?
Q
\hat{R}=P*Q
R^=P?Q 对于
P
P
P,
Q
Q
Q 矩阵的解释,直观上,
P
P
P 矩阵是
N
N
N 个用户对
K
K
K 个主题的关系,
Q
Q
Q 矩阵是
K
K
K 个主题跟
M
M
M 个物品的关系,至于
K
K
K 个主题具体是什么,在算法里面
K
K
K 是一个参数,需要调节的,通常
10
~
100
10\sim100
10~100 之间。
2.对于
R
^
\hat{R}
R^矩阵:
r
^
i
j
=
p
i
T
q
j
=
∑
k
=
1
K
p
i
k
q
k
j
\hat{r}_{ij}=p_{i} ^{T}q_{j}=\sum_{k=1}^{K}p_{ik}q_{kj}
r^ij?=piT?qj?=∑k=1K?pik?qkj?
R
^
\hat{R}
R^与
R
R
R的维度相同,其中
r
^
i
j
\hat{r}_{ij}
r^ij?是
R
^
\hat{R}
R^第
i
i
i行第
j
j
j列的元素值。
3.求损失函数并更新变量: 使用原始的评分矩阵
R
R
R 与重新构建的评分矩阵
R
^
\hat{R}
R^ 之间的误差的平方作为损失函数,即:
e
i
j
2
=
(
r
i
j
?
r
^
i
j
)
2
=
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
2
e_{ij}^{2}=(r_{ij}-\hat{r}_{ij})^{2}=(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})^{2}
eij2?=(rij??r^ij?)2=(rij??∑k=1K?pik?qkj?)2 通过梯度下降法,更新变量: 求导:
?
?
p
i
k
e
i
j
2
=
?
2
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
q
k
j
=
?
2
e
i
j
q
k
j
\frac{?}{?_{p{ik}}}e_{ij}^{2}=-2(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})q_{kj}=-2e_{ij}q_{kj}
?pik???eij2?=?2(rij??k=1∑K?pik?qkj?)qkj?=?2eij?qkj?
?
?
q
k
j
e
i
j
2
=
?
2
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
p
i
k
=
?
2
e
i
j
p
i
k
\frac{?}{?_{q{kj}}}e_{ij}^{2}=-2(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})p_{ik}=-2e_{ij}p_{ik}
?qkj???eij2?=?2(rij??k=1∑K?pik?qkj?)pik?=?2eij?pik? 根据负梯度的方向更新变量:
p
i
k
′
=
p
i
k
?
α
?
?
p
i
k
e
i
j
2
=
p
i
k
+
2
α
e
i
j
q
k
j
p_{ik}'=p_{ik}-α\frac{?}{?{p_{ik}}}e_{ij}^{2}=p_{ik}+2αe_{ij}q_{kj}
pik′?=pik??α?pik???eij2?=pik?+2αeij?qkj?
q
k
j
′
=
q
k
j
?
α
?
?
q
k
j
e
i
j
2
=
q
k
j
+
2
α
e
i
j
p
i
k
q_{kj}'=q_{kj}-α\frac{?}{?{q_{kj}}}e_{ij}^{2}=q_{kj}+2αe_{ij}p_{ik}
qkj′?=qkj??α?qkj???eij2?=qkj?+2αeij?pik?
4.在损失函数中加入正则化惩罚项:通常在求解的过程中,为了能够有较好的泛化能力,会在损失函数中加入正则项,以对参数进行约束。加入正则项后的计算过程如下:
E
i
j
2
=
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
2
+
β
2
∑
k
=
1
K
(
p
i
k
2
+
q
k
j
2
)
E_{ij}^{2}=(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})^{2}+\frac{β}{2}\sum_{k=1}^{K}(p_{ik}^{2}+q_{kj}^{2})
Eij2?=(rij??k=1∑K?pik?qkj?)2+2β?k=1∑K?(pik2?+qkj2?)通过梯度下降法,更新变量:求导:
?
?
p
i
k
E
i
j
2
=
?
2
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
q
k
j
+
β
p
i
k
=
?
2
e
i
j
q
k
j
+
β
p
i
k
\frac{?}{?{p_{ik}}}E_{ij}^{2}=-2(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})q_{kj}+βp_{ik}=-2e_{ij}q_{kj}+βp_{ik}
?pik???Eij2?=?2(rij??k=1∑K?pik?qkj?)qkj?+βpik?=?2eij?qkj?+βpik?
?
?
q
k
j
E
i
j
2
=
?
2
(
r
i
j
?
∑
k
=
1
K
p
i
k
q
k
j
)
p
i
k
+
β
q
k
j
=
?
2
e
i
j
p
i
k
+
β
q
k
j
\frac{?}{?{q_{kj}}}E_{ij}^{2}=-2(r_{ij}-\sum_{k=1}^{K}p_{ik}q_{kj})p_{ik}+βq_{kj}=-2e_{ij}p_{ik}+βq_{kj}
?qkj???Eij2?=?2(rij??k=1∑K?pik?qkj?)pik?+βqkj?=?2eij?pik?+βqkj?根据负梯度的方向更新变量:
p
i
k
′
=
p
i
k
?
α
(
?
?
p
i
k
e
i
j
2
+
β
p
i
k
)
=
p
i
k
+
α
(
2
e
i
j
q
k
j
?
β
p
i
k
)
p_{ik}'=p_{ik}-α(\frac{?}{?{p_{ik}}}e_{ij}^{2}+βp_{ik})=p_{ik}+α(2e_{ij}q_{kj}-βp_{ik})
pik′?=pik??α(?pik???eij2?+βpik?)=pik?+α(2eij?qkj??βpik?)
q
k
j
′
=
q
k
j
?
α
(
?
?
q
k
j
e
i
j
2
+
β
q
k
j
)
=
q
k
j
+
α
(
2
e
i
j
p
i
k
?
β
q
k
j
)
q_{kj}'=q_{kj}-α(\frac{?}{?{q_{kj}}}e_{ij}^{2}+βq_{kj})=q_{kj}+α(2e_{ij}p_{ik}-βq_{kj})
qkj′?=qkj??α(?qkj???eij2?+βqkj?)=qkj?+α(2eij?pik??βqkj?)
- 算法终止:每次更新完
R
^
\hat{R}
R^ 后,计算一次
l
o
s
s
loss
loss 值,若
l
o
s
s
loss
loss 值非常小或者到达最大迭代次数,结束算法。于是就得到了我们最终的预测矩阵
R
^
\hat{R}
R^。
四.Python代码实现
import numpy as np
import math
import matplotlib.pyplot as plt
def Matrix_decomposition(R,P,Q,N,M,K,alpha=0.0002,beta=0.02):
Q = Q.T
loss_list = []
for step in range(5000):
for i in range(N):
for j in range(M):
if R[i][j] != 0:
error = R[i][j]
for k in range(K):
error -= P[i][k]*Q[k][j]
for k in range(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 range(N):
for j in range(M):
if R[i][j] != 0:
data = 0
for k in range(K):
data = data + P[i][k]*Q[k][j]
loss = loss + math.pow(R[i][j]-data,2)
for k in range(K):
loss = loss + beta/2*(P[i][k]*P[i][k]+Q[k][j]*Q[k][j])
loss_list.append(loss)
plt.scatter(step,loss)
if (step+1) % 1000 == 0:
print("loss={:}".format(loss))
if loss < 0.001:
print(loss)
break
plt.show()
return P,Q
if __name__ == "__main__":
N = 5
M = 4
K = 5
R = np.array([[5,3,0,1],
[4,0,0,1],
[1,1,0,5],
[1,0,0,4],
[0,1,5,4]])
print("初始评分矩阵:")
print(R)
P = np.random.rand(N,K)
Q = np.random.rand(M,K)
print("开始矩阵分解:")
P,Q = Matrix_decomposition(R,P,Q,N,M,K)
print("矩阵分解结束。")
print("得到的预测矩阵:")
print(np.dot(P,Q))
运行结果分析:
初始评分矩阵:
[[5 3 0 1]
[4 0 0 1]
[1 1 0 5]
[1 0 0 4]
[0 1 5 4]]
开始矩阵分解:
loss=8.727735279273917
loss=1.5983412898789944
loss=1.2664582185054736
loss=1.2241666331627166
loss=1.2166150584503614
矩阵分解结束。
得到的预测矩阵:
[[4.97903684 2.97131217 3.41300234 1.00412214]
[3.97892547 0.6736204 3.33839296 1.00341136]
[1.00662457 0.97898601 3.36790504 4.97254172]
[0.99649186 0.5035927 2.9660996 3.98258083]
[3.95311908 1.02446502 4.9821697 3.98566116]]
五.补充
可能有些小伙伴不理解矩阵乘法,这里我给大家在B站找了一个讲解视频, 链接放在这里了,矩阵分解的详解计算
|