【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

这篇具有很好参考价值的文章主要介绍了【推荐系统入门到项目实战】(三):矩阵分解和ALS算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法


  • 🌸个人主页:JOJO数据科学
  • 📝个人介绍:统计学top3高校统计学硕士在读
  • 💌如果文章对你有帮助,欢迎✌关注、👍点赞、✌收藏、👍订阅专栏
  • ✨本文收录于【推荐系统入门到项目实战】本系列主要分享一些学习推荐系统领域的方法和代码实现。

引言

之前我们介绍了推荐系统的基础框架。了解了基础的推荐系统算法。我们首先来回顾一下推荐算法的常见方法。主要分为两大类

  • 1.基于内容的推荐
  • 2.基于协同过滤的推荐

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

而基于协同过滤的推荐是推荐系统的主流思想之一。其中矩阵分解是一个重要的模块。本文我们来讨论一下矩阵分解ALS的原理与实现。

在正式介绍矩阵分解之前,我们先来讨论一下一些基础的问题

什么是隐语义模型:

用户与物品之间存在着某些隐含的联系。通过隐含特征(Latent Factor) 联系用户兴趣和物品,基于用户行为的自动聚类。我们可以指定隐特征的个数,粒度可粗(隐特征少),可细(隐特征多)。计算物品属于每个隐特征的权重,但是可解释性差。

有了基础的前置知识,我们来看看正式来看看矩阵分解吧

矩阵分解(MF)

首先,我们的目标是:为用户找到其感兴趣的item推荐给他

矩阵分解要做的是预测出矩阵中缺失的评分,使得预测评分能反映用户的喜欢程度。可以把预测评分最高的前K个电影推荐给用户了。

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

其中,只有评分矩阵是已知的,我们要做的是如何预测出User矩阵和Item矩阵。这是一个最优化问题,我们的目标是使得User矩阵×Item矩阵与评分矩阵中已知的评分差异最小。

现在我们来看一个具体的例子,用矩阵表示收集到的用户行为数据,一共12个用户,9部电影并对其进行了评分。

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

现在假设我们得到的矩阵分解结果如下:user矩阵和item矩阵

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

  • 观察User矩阵:用户的观影爱好体现在User向量上,例如用户1,最喜欢动作片

  • 观察Item矩阵,电影的风格也会体现在Item向量上。例如红海行动、战狼2,这些都是动作片

  • 矩阵分解用user向量和item向量的内积去拟合评分矩阵中该user对该item的评分,内积的大小反映了user对item的喜欢程度。内积大匹配度高,内积小匹配度低。

  • 隐含特征个数k,k越大,隐类别分得越细,计算量越大。

然后根据得到的两个矩阵,进行矩阵乘法,可以得到电影的预测评分

某个用户u对电影i的预测评分 = User向量和Item向量的内积

评分值越大,表示用户喜欢该电影的可能性越大,该电影就越值得推荐给用户

那么我们如何进行矩阵分解呢?

我们先来看看矩阵分解的目标函数

矩阵分解的目标函数

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

其中:

  • r u i r_{ui} rui实际评分
  • x u x_u xu用户矩阵(user-matrix)
  • y i y_i yi物品矩阵(item-matrix)
  • λ \lambda λ正则化系数

我们的目标是找出使得上述目标函数最小的 x u x_u xu y i y_i yi。首先我们明确的是,上述我们要解决的是显示评分问题。

显性评分

ALS, Alternative Least Square, ALS,交替最小二乘法

Step1,固定Y 优化X

如下所示

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

Step2,固定X 优化Y,和step1一样

重复Step1和2,直到X 和Y 收敛。每次固定一个矩阵,优化另一个矩阵,都是最小二乘问题。

隐性评分

除了针对显式评分矩阵,ALS还可以对隐式矩阵进行分解。很多时候,用户的行为也会反映其对某种商品的偏好,但是这种行为是一直隐性的,没有具体的评分值。例如,将评分看成行为的强度,比如浏览次数,阅读时间

r u i > 0 r_{ui}>0 rui>0时,用户u对商品i有行为

r u i = 0 r_{ui}=0 rui=0时,用户u对商品i没有行为
p u i = 1 , r u i > 0 p u i = 0 , r u i = 0 p_{ui}=1,r_{ui}>0\\p_{ui}=0,r_{ui}=0 pui=1,rui>0pui=0,rui=0

p u i p_{ui} pui 称为用户偏好:

当用户u 对物品i 有过行为,认为用户u 对物品i感兴趣, p u i = 1 p_{ui}=1 pui=1

当用户u 对物品i 没有过行为,认为用户u 对物品i 不感兴趣, p u i = 0 p_{ui}=0 pui=0

对隐式矩阵进行分解:

引入置信度:

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

r u i r_{ui} rui>0时, c u i 与 r u i 线性递增 c_{ui}与r_{ui}线性递增 cuirui线性递增

r u i = 0 r_{ui}=0 rui=0时, c u i = 1 c_{ui}=1 cui=1,也就是 c u i c_{ui} cui最小值为1

此时目标函数目标函数

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

将目标函数转化为矩阵形式,并进行求导

Step1,固定Y优化X

求解得

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

Λu 为用户u 对所有物品的置信度 c u i c_{ui} cui 构成的对角阵

Step2,固定X优化Y

同理,求解得

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

Λi 为所有用户对物品i 的偏好的置信度构成的对角矩阵

置信度c的其他选择: c u , i = 1 + α l o g ( r u , i ) c_{u,i}=1+\alpha log(r_{u,i}) cu,i=1+αlog(ru,i).
当r为负的:

  • c u , i = α × ∣ I u ∣ c_{u,i} = \alpha \times |I_u| cu,i=α×Iu

代码实现

下面我们来看一下如何使用ALS算法进行求解。

import pandas as pd
import numpy as np
import random
from collections import defaultdict

class ALS(object):
    def __init__(self):
        self.user_ids = None
        self.item_ids = None
        self.user_ids_dict = None
        self.item_ids_dict = None
        self.user_matrix = None
        self.item_matrix = None
        self.user_items = None
        self.shape = None
        self.rmse = None


    def _process_data(self, X):
        """ 将评分矩阵X转化为稀疏矩阵
            输入参数X:
                X {list} -- 2d list with int or float(user_id, item_id, rating)
            输出结果:
                dict -- {user_id: {item_id: rating}}
                dict -- {item_id: {user_id: rating}}
        """        
        self.user_ids = tuple((set(map(lambda x: x[0], X))))
        self.user_ids_dict = dict(map(lambda x: x[::-1], enumerate(self.user_ids)))
     
        self.item_ids = tuple((set(map(lambda x: x[1], X))))
        self.item_ids_dict = dict(map(lambda x: x[::-1], enumerate(self.item_ids)))
     
        self.shape = (len(self.user_ids), len(self.item_ids))
     
        ratings = defaultdict(lambda: defaultdict(int))
        ratings_T = defaultdict(lambda: defaultdict(int))
        for row in X:
            user_id, item_id, rating = row
            ratings[user_id][item_id] = rating
            ratings_T[item_id][user_id] = rating
     
        err_msg = "Length of user_ids %d and ratings %d not match!" % (len(self.user_ids), len(ratings))
        assert len(self.user_ids) == len(ratings), err_msg
     
        err_msg = "Length of item_ids %d and ratings_T %d not match!" % (len(self.item_ids), len(ratings_T))
        assert len(self.item_ids) == len(ratings_T), err_msg
        return ratings, ratings_T

     
    def _users_mul_ratings(self, users, ratings_T):
        """ 用户矩阵(稠密) 与 评分矩阵(稀疏)相乘
            The result(items) is a k * n matrix, n stands for number of item_ids.
            Arguments:
                users {Matrix} -- k * m matrix, m stands for number of user_ids.
                ratings_T {dict} -- The items ratings by users.
                {item_id: {user_id: rating}}
            Returns:
                Matrix -- Item matrix.
        """
        def f(users_row, item_id):
            user_ids = iter(ratings_T[item_id].keys())
            scores = iter(ratings_T[item_id].values())
            col_nos = map(lambda x: self.user_ids_dict[x], user_ids)
            _users_row = map(lambda x: users_row[x], col_nos)
            return sum(a * b for a, b in zip(_users_row, scores))
     
        ret = [[f(users_row, item_id) for item_id in self.item_ids] for users_row in users.data]
        return Matrix(ret)

    def _items_mul_ratings(self, items, ratings):
        """ item矩阵(稠密)与评分矩阵(稀疏)相乘
        The result(users) is a k * m matrix, m stands for number of user_ids.
        Arguments:
            items {Matrix} -- k * n matrix, n stands for number of item_ids.
            ratings {dict} -- The items ratings by users.
            {user_id: {item_id: rating}}
        Returns:
            Matrix -- User matrix.
        """
        def f(items_row, user_id):
            item_ids = iter(ratings[user_id].keys())
            scores = iter(ratings[user_id].values())
            col_nos = map(lambda x: self.item_ids_dict[x], item_ids)
            _items_row = map(lambda x: items_row[x], col_nos)
            return sum(a * b for a, b in zip(_items_row, scores))
     
        ret = [[f(items_row, user_id) for user_id in self.user_ids] for items_row in items.data]
        return Matrix(ret)

    # 生成随机矩阵
    def _gen_random_matrix(self, n_rows, n_colums):
        #print(n_colums, ' ', n_rows)
        #data = [[random() for _ in range(n_colums)] for _ in range(n_rows)]
        #d = 2
        data = np.random.rand(n_rows, n_colums)
        return Matrix(data)


    # 计算RMSE
    def _get_rmse(self, ratings):
            m, n = self.shape
            mse = 0.0
            n_elements = sum(map(len, ratings.values()))
            for i in range(m):
                for j in range(n):
                    user_id = self.user_ids[i]
                    item_id = self.item_ids[j]
                    rating = ratings[user_id][item_id]
                    if rating > 0:
                        user_row = self.user_matrix.col(i).transpose
                        item_col = self.item_matrix.col(j)
                        rating_hat = user_row.mat_mul(item_col).data[0][0]
                        square_error = (rating - rating_hat) ** 2
                        mse += square_error / n_elements
            return mse ** 0.5

    # 模型训练
    def fit(self, X, k, max_iter=10):
        ratings, ratings_T = self._process_data(X)
        self.user_items = {k: set(v.keys()) for k, v in ratings.items()}
        m, n = self.shape
     
        error_msg = "Parameter k must be less than the rank of original matrix"
        assert k < min(m, n), error_msg
     
        self.user_matrix = self._gen_random_matrix(k, m)
     
        for i in range(max_iter):
            if i % 2:
                items = self.item_matrix
                self.user_matrix = self._items_mul_ratings(
                    items.mat_mul(items.transpose).inverse.mat_mul(items),
                    ratings
                )
            else:
                users = self.user_matrix
                self.item_matrix = self._users_mul_ratings(
                    users.mat_mul(users.transpose).inverse.mat_mul(users),
                    ratings_T
                )
            rmse = self._get_rmse(ratings)
            print("Iterations: %d, RMSE: %.6f" % (i + 1, rmse))
     
        self.rmse = rmse

    # Top-n推荐,用户列表:user_id, n_items: Top-n
    def _predict(self, user_id, n_items):
        users_col = self.user_matrix.col(self.user_ids_dict[user_id])
        users_col = users_col.transpose
     
        items_col = enumerate(users_col.mat_mul(self.item_matrix).data[0])
        items_scores = map(lambda x: (self.item_ids[x[0]], x[1]), items_col)
        viewed_items = self.user_items[user_id]
        items_scores = filter(lambda x: x[0] not in viewed_items, items_scores)
     
        return sorted(items_scores, key=lambda x: x[1], reverse=True)[:n_items]

    # 预测多个用户
    def predict(self, user_ids, n_items=10):
        return [self._predict(user_id, n_items) for user_id in user_ids]

def format_prediction(item_id, score):
    return "item_id:%d score:%.2f" % (item_id, score)

def load_movie_ratings(file_name):
    f = open(file_name)
    lines = iter(f)
    col_names = ", ".join(next(lines)[:-1].split(",")[:-1])
    print("The column names are: %s." % col_names)
    data = [[float(x) if i == 2 else int(x)
             for i, x in enumerate(line[:-1].split(",")[:-1])]
            for line in lines]
    f.close()

    return data

print("使用ALS算法") 
model = ALS()
# 数据加载
X = load_movie_ratings('MovieLens/ratings_small.csv')
# 运行max_iter次
model.fit(X, k=3, max_iter=2)
"""
X = np.array([[1,1,1], [1,2,1], [2,1,1], [2,3,1], [3,2,1], [3,3,1], [4,1,1], [4,2,1],
              [5,4,1], [5,5,1], [6,4,1], [6,6,1], [7,5,1], [7,6,1], [8,4,1], [8,5,1], [9,4,1], [9,5,1],
              [10,7,1], [10,8,1], [11,8,1], [11,9,1], [12,7,1], [12,9,1]])
# 运行max_iter次
model.fit(X, k=3, max_iter=20)
"""

print("对用户进行推荐")
user_ids = range(1, 13)
# 对用户列表user_ids,进行Top-n推荐
predictions = model.predict(user_ids, n_items=2)
print(predictions)
for user_id, prediction in zip(user_ids, predictions):
    _prediction = [format_prediction(item_id, score) for item_id, score in prediction]
    print("User id:%d recommedation: %s" % (user_id, _prediction))


使用ALS算法
The column names are: userId, movieId, rating.
Iterations: 1, RMSE: 3.368115
Iterations: 2, RMSE: 0.393883
对用户进行推荐
[[(593, 0.14406097730654768), (1198, 0.14358259103963905)], [(318, 2.059017427702032), (260, 1.923258591494868)], [(260, 1.2102151320443484), (2571, 1.0787785902899518)], [(318, 3.686989610157476), (593, 3.6010162010338984)], [(296, 2.0071412668514), (318, 1.8489213945544887)], [(593, 0.6176565451297263), (1198, 0.5291764793134027)], [(296, 2.1991835206619252), (593, 1.7899558400858702)], [(608, 2.3390885141087048), (1, 2.2529122594512856)], [(296, 0.8503498053957818), (1198, 0.8349697071072679)], [(593, 0.8445440715644625), (296, 0.7889019418410842)], [(356, 0.5262573842686891), (318, 0.5049187078106891)], [(593, 0.407095000056158), (3578, 0.40234869950041907)]]
User id:1 recommedation: ['item_id:593 score:0.14', 'item_id:1198 score:0.14']
User id:2 recommedation: ['item_id:318 score:2.06', 'item_id:260 score:1.92']
User id:3 recommedation: ['item_id:260 score:1.21', 'item_id:2571 score:1.08']
User id:4 recommedation: ['item_id:318 score:3.69', 'item_id:593 score:3.60']
User id:5 recommedation: ['item_id:296 score:2.01', 'item_id:318 score:1.85']
User id:6 recommedation: ['item_id:593 score:0.62', 'item_id:1198 score:0.53']
User id:7 recommedation: ['item_id:296 score:2.20', 'item_id:593 score:1.79']
User id:8 recommedation: ['item_id:608 score:2.34', 'item_id:1 score:2.25']
User id:9 recommedation: ['item_id:296 score:0.85', 'item_id:1198 score:0.83']
User id:10 recommedation: ['item_id:593 score:0.84', 'item_id:296 score:0.79']
User id:11 recommedation: ['item_id:356 score:0.53', 'item_id:318 score:0.50']
User id:12 recommedation: ['item_id:593 score:0.41', 'item_id:3578 score:0.40']

可以看出我们得到了预测的结果

但是上面这种方法只是最简单的对一个原始评分矩阵进行了分解。但是没有考虑到有些电影平均评分很高(或很低),我们称之为热门电影冷门电影,俗称烂片)

例如,肖申克的救赎,大家对它的评分一般都不会很低。(我给了9.8分,太好看了)

【推荐系统入门到项目实战】(三):矩阵分解和ALS算法

有的用户也会比较偏向性的给所有电影较高的评分,我们称为热心用户(我自己瞎起的名字,大家理解就行)在这种情况下,这类用户对所有的电影可能都会给一个较高的评分。但是我们刚刚上面的方法没有办法识别出这种关系,下面我们介绍一些其他的方法。

总结

矩阵分解(MF) 是一种隐语义模型,它通过隐类别匹配用户和item来做推荐。
矩阵分解的基本思想是对原有的评分矩阵R进行降维,分成了两个小矩阵:User矩阵和Item矩阵,User矩阵每一行代表一个用户的向量,Item矩阵的每一列代表一个item的向量。将User矩阵和Item矩阵的维度降低到隐类别个数的维度。

根据用户的行为,矩阵分解又可以分为显式矩阵分解和隐式矩阵

  • 在显式MF中,用户向量和物品向量的内积拟合的是用户对物品的实际评分

  • 在隐式MF中,用户向量和物品向量的内积拟合的是用户对物品的偏好(0或1),拟合的强度由置信度控制,置信度又由行为的强度决定。

主要的两大应用场景

  • 评分预测(Rating Prediction)

主要用于评价网站,比如用户给自己看过的电影评多少分(MovieLens),或者用户给自己看过的书籍评价多少分(Douban)。矩阵分解技术主要应用于评分预测问题。(这种情况下评分矩阵往往存在严重的稀疏性问题)

  • Top-N推荐(Item Ranking)

常用于购物网站,拿不到显式评分,通过用户的隐式反馈为用户提供一个可能感兴趣的Item列表。排序任务,需要排序模型进行建模。

在本文中,我们使用ALS进行求解矩阵分解。但是实现这种方法的缺点是没有办法反应整体平均的情况,下一章我们将继续讨论baseline算法slopeOne算法。文章来源地址https://www.toymoban.com/news/detail-463857.html

到了这里,关于【推荐系统入门到项目实战】(三):矩阵分解和ALS算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 推荐系统 | 基础推荐模型 | 矩阵分解模型 | 隐语义模型 | PyTorch实现

    基础推荐模型——传送门 : 推荐系统 | 基础推荐模型 | 协同过滤 | UserCF与ItemCF的Python实现及优化 推荐系统 | 基础推荐模型 | 矩阵分解模型 | 隐语义模型 | PyTorch实现 推荐系统 | 基础推荐模型 | 逻辑回归模型 | LS-PLM | PyTorch实现 推荐系统 | 基础推荐模型 | 特征交叉 | FM | FFM |

    2023年04月09日
    浏览(41)
  • 【推荐算法】协同过滤算法代码(pyspark | ALS)

    【推荐算法】协同过滤算法介绍_MachineCYL的博客-CSDN博客 上文介绍了协同过滤算法的原理,接下来我介绍一下协同过滤算法的代码实现。 下面我就开始介绍用pyspark中的ALS(交替最小二乘矩阵分解)来实现协同过滤代码。 ALS算法是2008年以来,用的比较多的协同过滤算法。它已

    2024年02月06日
    浏览(31)
  • LightFM:一款开源推荐系统框架,可以轻松实现大规模矩阵分解,快速、高效地处理大型矩阵

    作者:禅与计算机程序设计艺术 LightFM 是由 Yelp 开发的一款开源推荐系统框架,可以轻松实现大规模矩阵分解。该项目基于 TensorFlow 和 Keras 框架,可以快速、高效地处理大型矩阵。它具有以下特点: 提供了一种简单的方法来训练矩阵分解模型,即通过定义项间的交互矩阵和用

    2024年02月10日
    浏览(33)
  • 机器学习中高维组合特征的处理方法+推荐系统使用矩阵分解为用户推荐的原理解析,《百面机器学习》学习笔记

    为了提高复杂关系的拟合能力,在特征工程中经常会把一阶离散特征进行组合,构成高阶组合特征。 假设有A B两组特征,C为受到A B两种特征影响的因素,且对特征A来说,其有 A i , i ∈ [ 0 , 1 ] {A^i,iin [0,1]} A i , i ∈ [ 0 , 1 ] 两种特征取值。同时,对于特征B来说,其有 B j , j ∈

    2024年02月05日
    浏览(33)
  • 机器学习实战教程(四):从特征分解到协方差矩阵:详细剖析和实现PCA算法

    方差和标准差的原理和实例演示,请参考 方差 方差(Variance)是度量一组数据的分散程度。方差是各个样本与样本均值的差的平方和的均值: 标准差 标准差是数值分散的测量。 标准差的符号是 σ (希腊语字母 西格马,英语 sigma) 公式很简单:方差的平方根。 协方差 通俗

    2024年02月02日
    浏览(39)
  • 计算机毕业设计 基于协同过滤算法的体育商品推荐系统的设计与实现 Java实战项目 附源码+文档+视频讲解

    博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟 ——————————

    2024年02月07日
    浏览(43)
  • 「从零入门推荐系统」19:H&M推荐系统代码实战案例

    作者 | gongyouliu 编辑 | gongyouliu 我们在上一章中利用Netflix prize数据集讲解了最基础、最简单的一些推荐系统召回、排序算法,大家应该对怎么基于Python实现推荐算法有了一些基本的了解了。接着上一章的思路,本章我们会基于一个更复杂、更近代一点的数据集来实现一些我

    2024年02月07日
    浏览(23)
  • 机器学习实战:Python基于SVD奇异值分解进行矩阵分解(八)

    1.1 奇异值分解 奇异值分解( Singular Value Decomposition,SVD )是一种重要的矩阵分解技术,它可以将一个矩阵分解为三个矩阵的乘积,分别为左奇异矩阵、奇异值矩阵和右奇异矩阵。SVD 的原理可以描述如下: 对于任意 m × n m times n m × n 的矩阵 A A A ,它的 SVD 分解为: A = U $

    2024年02月02日
    浏览(44)
  • 推荐系统简介+算法详解+项目介绍

    1、推荐系统目的 信息过载 让用户更快更好的获取到自己需要的内容 内容更快更好的推送到喜欢它的用户手中 让网站(平台)更有效的保留用户资源 即好的推荐系统–让三方共赢 2、推荐系统的应用 个性化音乐、电影视频、社交网络、个性化阅读、证券理财、个性化旅游、

    2024年02月06日
    浏览(39)
  • 对称矩阵的三对角分解(Lanzos分解算法)-MINRES算法预热

    这篇博客看完以后接着看下一篇博客添加链接描述专门介绍MINRES算法实现就容易了 首先介绍Lanczos分解,Lanzos把对称矩阵转换为一个三对角对称矩阵。考虑三对角对称矩阵如下,考虑正交分解 T = Q T A Q T = Q^T A Q T = Q T A Q T = ( α 1 β 1 0 ⋯ 0 0 β 1 α 2 β 2 0 ⋯ 0 0 β 2 α 3 β 3 ⋯ 0

    2024年02月03日
    浏览(191)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包