关于最小二乘法(LS)和交替最小二乘法(ALS)可以参考博客:https://blog.csdn.net/qq_37142346/article/details/80472088
交替最小二乘法(ALS)是统计分析中最常用的逼近计算的一种算法,其交替计算结果使得最终结果尽可能地逼近真实结果。而ALS的基础是最小二乘法(LS算法),LS算法是一种常用的机器学习算法,它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便的求得未知的数据,并使得这些求得的数据与实际数据之间误差的平法和为最小。
最小二乘法思想:对损失函数求偏导,然后再使偏导为0。
Rui:实际评分 mu:平均评分 bi:电影评分的偏置值 lamda1:正则参数 |Ru|:用户有过的评分数量
Rui:实际评分 mu:平均评分 bu:用户给出评分的偏置值 lamda2:正则参数 |Ri|:物品收到的评分数量
bu和bi分别属于用户和物品的偏置,因此他们从正则参数可以分别设置两个独立的参数。
通过最小二乘推导,我们最终分别得到了bu和bi的表达式,但他们的表达式中却又各自包含对方,因此这里我们将利用一种叫交替最小二乘的方法来计算他们的值:
- 计算其中一项,先固定其他未知参数,即看作其他未知参数为已知
- 如求bu时,将bi看作是已知;求bi时,将bu看作是已知;如此反复交替,不断更新二者的值,求得最终的结果。这就是交替最小二乘法(ALS)
实验数据:链接:https://pan.baidu.com/s/1n76-KsNs1cvgIcyV4YI3Rg 提取码:1314
import pandas as pd
import numpy as np
class BaseLine(object):
def __init__(self, number, reg_bu, reg_bi, columns=None):
"""
初始化最高迭代次数、正则参数等
:param number:
:param reg_bu:
:param reg_bi:
:param columns:
"""
if columns is None:
columns = ["uid", "iid", "rating"]
self.number = number
self.reg_bu = reg_bu
self.reg_bi = reg_bi
self.columns = columns
self.dataset = None
self.users_rating = None
self.items_rating = None
self.global_mean = None
self.bu = None
self.bi = None
def fit(self, dataset):
"""
:param dataset:
:return:
"""
self.dataset = dataset
# 用groupby进行‘行分组’,agg进行列的组合
self.users_rating = dataset.groupby('userId').agg([list]) # 用户评分数据
self.items_rating = dataset.groupby('movieId').agg([list]) # 物品评分数据
self.global_mean = dataset['rating'].mean() # 全局平均分
self.bu, self.bi = self.als()
def als(self):
"""
利用交替最小二乘法优化BaseLine损失
:return:bu, bi
"""
# 初始化bu、bi的值,设为0
bu = dict(zip(self.users_rating.index, np.zeros(len(self.users_rating))))
bi = dict(zip(self.items_rating.index, np.zeros(len(self.items_rating))))
print('进行 %d 次迭代以优化BaseLine损失' % self.number)
for i in range(self.number):
print("iter%d" % i)
# 不可以使用index=False消除索引
# 会报错ValueError: not enough values to unpack (expected 3, got 2)
for iid, uids, ratings in self.items_rating.itertuples(index=True):
_sum = 0
for uid, rating in zip(uids, ratings):
_sum += rating - self.global_mean - bu[uid]
bi[iid] = _sum / (self.reg_bi + len(uids))
for uid, iids, ratings in self.users_rating.itertuples(index=True):
_sum = 0
for iid, rating in zip(iids, ratings):
_sum += rating - self.global_mean - bi[iid]
bu[uid] = _sum / (self.reg_bu + len(iids))
return bu, bi
# 预测评分
def predict(self, uid, iid):
predict_rating = self.global_mean + self.bu[uid] + self.bi[iid]
return predict_rating
if __name__ == '__main__':
# 数据加载
dtype = [("userId", np.int32), ("movieId", np.int32), ("rating", np.float32)]
dataset = pd.read_csv("data/ratings.csv", usecols=range(3), dtype=dict(dtype))
bsl = BaseLine(20, 25, 15, ["userId", "movieId", "rating"])
bsl.fit(dataset)
print('Predict_Score: %f' % bsl.predict(1,1))