关于最小二乘法(LS)和交替最小二乘法(ALS)可以参考博客:https://blog.csdn.net/qq_37142346/article/details/80472088

    交替最小二乘法(ALS)是统计分析中最常用的逼近计算的一种算法,其交替计算结果使得最终结果尽可能地逼近真实结果。而ALS的基础是最小二乘法(LS算法),LS算法是一种常用的机器学习算法,它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便的求得未知的数据,并使得这些求得的数据与实际数据之间误差的平法和为最小。

    最小二乘法思想:对损失函数求偏导,然后再使偏导为0。

    Rui:实际评分 mu:平均评分 bi:电影评分的偏置值 lamda1:正则参数 |Ru|:用户有过的评分数量

    bu.png

    Rui:实际评分 mu:平均评分 bu:用户给出评分的偏置值 lamda2:正则参数 |Ri|:物品收到的评分数量

    bi.png
    bubi分别属于用户和物品的偏置,因此他们从正则参数可以分别设置两个独立的参数。

    通过最小二乘推导,我们最终分别得到了bubi的表达式,但他们的表达式中却又各自包含对方,因此这里我们将利用一种叫交替最小二乘的方法来计算他们的值:

    • 计算其中一项,先固定其他未知参数,即看作其他未知参数为已知
    • 如求bu时,将bi看作是已知;求bi时,将bu看作是已知;如此反复交替,不断更新二者的值,求得最终的结果。这就是交替最小二乘法(ALS)

    实验数据:链接:https://pan.baidu.com/s/1n76-KsNs1cvgIcyV4YI3Rg 提取码:1314

    1. import pandas as pd
    2. import numpy as np
    3. class BaseLine(object):
    4. def __init__(self, number, reg_bu, reg_bi, columns=None):
    5. """
    6. 初始化最高迭代次数、正则参数等
    7. :param number:
    8. :param reg_bu:
    9. :param reg_bi:
    10. :param columns:
    11. """
    12. if columns is None:
    13. columns = ["uid", "iid", "rating"]
    14. self.number = number
    15. self.reg_bu = reg_bu
    16. self.reg_bi = reg_bi
    17. self.columns = columns
    18. self.dataset = None
    19. self.users_rating = None
    20. self.items_rating = None
    21. self.global_mean = None
    22. self.bu = None
    23. self.bi = None
    24. def fit(self, dataset):
    25. """
    26. :param dataset:
    27. :return:
    28. """
    29. self.dataset = dataset
    30. # 用groupby进行‘行分组’,agg进行列的组合
    31. self.users_rating = dataset.groupby('userId').agg([list]) # 用户评分数据
    32. self.items_rating = dataset.groupby('movieId').agg([list]) # 物品评分数据
    33. self.global_mean = dataset['rating'].mean() # 全局平均分
    34. self.bu, self.bi = self.als()
    35. def als(self):
    36. """
    37. 利用交替最小二乘法优化BaseLine损失
    38. :return:bu, bi
    39. """
    40. # 初始化bu、bi的值,设为0
    41. bu = dict(zip(self.users_rating.index, np.zeros(len(self.users_rating))))
    42. bi = dict(zip(self.items_rating.index, np.zeros(len(self.items_rating))))
    43. print('进行 %d 次迭代以优化BaseLine损失' % self.number)
    44. for i in range(self.number):
    45. print("iter%d" % i)
    46. # 不可以使用index=False消除索引
    47. # 会报错ValueError: not enough values to unpack (expected 3, got 2)
    48. for iid, uids, ratings in self.items_rating.itertuples(index=True):
    49. _sum = 0
    50. for uid, rating in zip(uids, ratings):
    51. _sum += rating - self.global_mean - bu[uid]
    52. bi[iid] = _sum / (self.reg_bi + len(uids))
    53. for uid, iids, ratings in self.users_rating.itertuples(index=True):
    54. _sum = 0
    55. for iid, rating in zip(iids, ratings):
    56. _sum += rating - self.global_mean - bi[iid]
    57. bu[uid] = _sum / (self.reg_bu + len(iids))
    58. return bu, bi
    59. # 预测评分
    60. def predict(self, uid, iid):
    61. predict_rating = self.global_mean + self.bu[uid] + self.bi[iid]
    62. return predict_rating
    63. if __name__ == '__main__':
    64. # 数据加载
    65. dtype = [("userId", np.int32), ("movieId", np.int32), ("rating", np.float32)]
    66. dataset = pd.read_csv("data/ratings.csv", usecols=range(3), dtype=dict(dtype))
    67. bsl = BaseLine(20, 25, 15, ["userId", "movieId", "rating"])
    68. bsl.fit(dataset)
    69. print('Predict_Score: %f' % bsl.predict(1,1))