损失函数
J
(
θ
)
=
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
u
−
b
i
)
2
+
λ
∗
(
∑
u
b
u
2
+
∑
i
b
i
2
)
J(theta)=sum_{u,iin R}(r_{ui}-mu-b_u-b_i)^2+lambda*(sum_u b_u{^2}+sum_i b_i{^2} )
J(θ)=u,i∈R∑(rui−μ−bu−bi)2+λ∗(u∑bu2+i∑bi2)
对损失函数求偏导
∂
J
(
θ
)
∂
b
u
=
−
2
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
u
−
b
i
)
+
2
λ
∗
b
u
frac{partial{J(theta)}}{partial{b_u}}=-2sum_{u,iin R}(r_{ui}-mu-b_u-b_i)+2lambda * b_u
∂bu∂J(θ)=−2u,i∈R∑(rui−μ−bu−bi)+2λ∗bu
另偏导等于0,可得:
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
u
−
b
i
)
=
λ
∗
b
u
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
i
)
=
∑
u
,
i
∈
R
b
u
+
λ
∗
b
u
sum_{u,iin R}(r_{ui}-mu-b_u-b_i)=lambda * b_u \ sum_{u,iin R}(r_{ui}-mu-b_i)=sum_{u,iin R} b_u+lambda * b_u
u,i∈R∑(rui−μ−bu−bi)=λ∗buu,i∈R∑(rui−μ−bi)=u,i∈R∑bu+λ∗bu
为了简化公式,令
∑
u
,
i
∈
R
b
u
≈
∣
R
(
u
)
∣
∗
b
u
sum_{u,iin R} b_u approx |R(u)|*b_u
∑u,i∈Rbu≈∣R(u)∣∗bu,
∣
R
(
u
)
∣
|R(u)|
∣R(u)∣是
u
u
u总的评分次数,可得:
b
u
=
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
i
)
∣
R
(
u
)
∣
+
λ
1
b_u=frac{sum_{u,iin R}(r_{ui}-mu-b_i)}{|R(u)|+lambda_1}
bu=∣R(u)∣+λ1∑u,i∈R(rui−μ−bi)
同理可得:
b
i
=
∑
u
,
i
∈
R
(
r
u
i
−
μ
−
b
u
)
∣
R
(
i
)
∣
+
λ
2
b_i=frac{sum_{u,iin R}(r_{ui}-mu-b_u)}{|R(i)|+lambda_2}
bi=∣R(i)∣+λ2∑u,i∈R(rui−μ−bu)
其中
∣
R
(
i
)
∣
|R(i)|
∣R(i)∣是
i
i
i总的被评分次数
通过原理推导,我们得到了 b u b_u bu和 b i b_i bi的表达式,他们的表达式中又各自包含着对方,可以用交替最小二乘的方法来计算他们的值。
整体代码放在https://github.com/Kitten-Rec/HeiMa/code
关键代码如下def als(self):
"""
随机梯度下降优化bu, bi
:return: bu, bi
"""
# 初始化参数 bu, bi 全部设置为0
bu = dict(zip(self.user_ratings.index, np.zeros(len(self.user_ratings.index))))
bi = dict(zip(self.item_ratings.index, np.zeros(len(self.item_ratings.index))))
# 交替最小二乘法更新参数
for epoch in range(self.number_epochs):
print("Epoch: %d" % epoch)
for iid, uid_list, real_rating_list in self.item_ratings.itertuples(index=True):
sum = 0
for uid, real_rating in zip(uid_list, real_rating_list):
sum += real_rating - self.global_mean - bu[uid]
bi[iid] = sum / (self.lambda2 * len(uid_list))
for uid, iid_list, real_rating_list in self.user_ratings.itertuples(index=True):
sum = 0
for iid, real_rating in zip(iid_list, real_rating_list):
sum += real_rating - self.global_mean - bi[iid]
bu[uid] = sum / (self.lambda1 * len(iid_list))
return bu, bi



