传统推荐模型(二)协同过滤的进化——矩阵分解算法

news2024/11/26 19:44:41

传统推荐模型(二)协同过滤的进化——矩阵分解算法

针对协同过滤算法的头部效应较明显、泛化能力较弱的问题,矩阵分解算法被提出。矩阵分解在协同过滤算法中“共现矩阵”的基础上,加人了隐向量的概念,加强了模型处理稀疏矩阵的能力,针对性地解决了协同过滤存在的主要问题。

1、矩阵分解算法原理

协同过滤算法找到用户可能喜欢的视频的方式很直接,即基于用户的观看历史,找到跟目标用户A 看过同样视频的相似用户,然后找到这些相似用户喜欢看的其他视频,推荐给目标用户A。

在这里插入图片描述

矩阵分解算法则期望为每一个用户和视频生成一个隐向量,将用户和视频定位到隐向量的表示空间上,距离相近的用户和视频表明兴趣特点接近,在推荐过程中,就应该把距离相近的视频推荐给目标用户。例如,如果希望为上图中的用户 Dave 推荐视频可以发现离 Dave 的用户向量最近的两个视频向量分别是“Ocean’s 11”和“The Lion King”,那么可以根据向量距离由近到远的顺序生成 Dave 的推荐列表。

在“矩阵分解”的算法框架下,用户和物品的隐向量是通过分解协同过滤生成的共现矩阵得到的。

在这里插入图片描述

矩阵分解算法将mxn维的共现矩阵R分解为mxk维的用户矩阵U和kxn维的物品矩阵V相乘的形式。其中 m 是用户数量,n 是物品数量,k是隐向量的维度。k 的大小决定了隐向量表达能力的强弱。

k 的取值越小,隐向量包含的信息越少,模型的泛化程度越高;反之,k 的取值越大,隐向量的表达能力越强但泛化程度相应降低。此外,k 的取值还与矩阵分解的求解复杂度直接相关。

基于用户矩阵U和物品矩阵V,用户u对于物品i的预估评分:

在这里插入图片描述

2、矩阵分解(MF)

(1) 特征值分解(Eigen Decomposition)

特征分解(Eigen decomposition),又称谱分解(Spectral decomposition)是将矩阵分解为由其特征值和特征向量表示的矩阵之积的方法。

在这里插入图片描述

特征值分解可以得到特征值特征向量特征值表示的是这个特征到底有多重要,而特征向量表示这个特征是什么,可以将每一个特征向量理解为一个线性的子空间,我们可以利用这些线性的子空间干很多的事情。不过,特征值分解也有很多的局限,比如说变换的矩阵必须是方阵,那这也是后来又引入了奇异值分解的原因之一。

特征值分解只能作用于方阵,显然不适用于分解用户-物品矩阵。

(2) 奇异值分解(Singular Value Decomposition,SVD)

SVD是一种常用的降维数据压缩方法,核心是矩阵因子分解,即用因子分解的方式近似地表示原始矩阵,这种近似是在平方损失意义下的最优近似。

下面奇异值分解的定义、证明、计算及案例,来源于李航老师的《统计学习方法》。

奇异值分解的定义

在这里插入图片描述

在这里插入图片描述

奇异值分解例子:

在这里插入图片描述

矩阵的奇异值分解不是唯一的。

任意给定一个实矩阵,奇异值分解一定存在。

奇异值分解定理的证明

在这里插入图片描述

证明过程:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

奇异值分解的计算步骤

1、构造n阶实对称矩阵W

W = (A.T)A

2、计算W的特征值和特征向量

在这里插入图片描述

3、求得n阶正交矩阵V(利用上述求得的特征向量

在这里插入图片描述

4、求对角矩阵(利用上述求得的特征值

在这里插入图片描述

5、求解m阶正交矩阵(利用上述求得的V和对角矩阵)

在这里插入图片描述

在这里插入图片描述

奇异值分解的计算案例

1、构造n阶实对称矩阵W

在这里插入图片描述

2、计算W的特征值和特征向量

在这里插入图片描述

在这里插入图片描述

3、求得n阶正交矩阵V(利用上述求得的特征向量

在这里插入图片描述

4、求对角矩阵(利用上述求得的特征值

在这里插入图片描述

5、求解m阶正交矩阵(利用上述求得的V和对角矩阵)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

上面的算法和例题只是为了说明计算的过程,并不是实际应用中的算法。可以看出,奇异值分解算法关键在于 ATA 的特征值的计算。实际应用的奇异值分解算法是通过求 ATA 的特征值进行,但不直接计算 ATA。按照这个思路产生了许多矩阵奇异值分解的有效算法。

奇异值分解的缺点

1、传统的SVD分解,会要求原始矩阵是密的,而我们这里的这种矩阵一般情况下是非常稀疏的,如果想用奇异值分解,就必须对缺失的元素进行填充,而一旦补全,空间复杂度就会非常高,且补的不一定对。
2、然后就是SVD分解计算复杂度非常高,而我们的用户-物品矩阵非常大,所以基本上无法使用。

传统奇异值分解也不适用于解决大规模稀疏矩阵的矩阵分解问题,因此,梯度下降法成了进行矩阵分解的主要方法。

(3) Basic SVD(LFM、Funk SVD)

Funk-SVD, 也称Latent Factor Model(LFM)。 Funk-SVD的思想很简单: 把求解两个矩阵的参数问题转换成一个【最优化】问题,可以通过训练集里面的观察值利用最小化来学习用户矩阵和物品矩阵。

我们知道,如果有了用户矩阵和物品矩阵的话,我们就知道了如果想计算用户对物品的评分,只需要

在这里插入图片描述

我们有真实的r(u,i),我们可以随机初始化一个用户矩阵 U 和一个物品矩阵 V,我们就可以计算一个猜测的r^(u,i),那么这个猜测的和真实值之间就会有一个误差,有了误差,我们就可以计算出总的误差平方和,有了损失,我们就可以想办法进行训练,把SSE降到最小,那么我们的两个矩阵参数就可以算出来。所以就把这个问题转成了最优化的的问题。

预测函数

在这里插入图片描述

损失函数(误差平方和)

在这里插入图片描述

优化目标

在这里插入图片描述

梯度下降

求梯度

在这里插入图片描述

梯度更新

在这里插入图片描述

1、首先先初始化这两个参数矩阵U和V
2、通过两个隐向量乘积得到预测值pred
3、根据label和pred计算损失
4、通过梯度下降的方式,更新两个隐向量的值
5、未评过分的那些样本当做测试集,通过两个隐向量就可以得到测试集的label值这样就填充完了矩阵,下一步就可以进行推荐了
import random
import math


class LFM(object):

    def __init__(self, rating_data, F, alpha=0.1, lmbd=0.1, max_iter=500):
        """
        :param rating_data: rating_data是[(user,[(item,rate)]]类型
        :param F: 隐因子个数
        :param alpha: 学习率
        :param lmbd: 正则化
        :param max_iter:最大迭代次数
        """
        self.F = F
        self.P = dict()  # R=PQ^T,代码中的Q相当于博客中Q的转置
        self.Q = dict()
        self.alpha = alpha
        self.lmbd = lmbd
        self.max_iter = max_iter
        self.rating_data = rating_data

        '''随机初始化矩阵P和Q'''
        for user, rates in self.rating_data:
            self.P[user] = [random.random() / math.sqrt(self.F)
                            for x in range(self.F)]
            for item, _ in rates:
                if item not in self.Q:
                    self.Q[item] = [random.random() / math.sqrt(self.F)
                                    for x in range(self.F)]
        print(self.P)

    def train(self):
        """
        随机梯度下降法训练参数P和Q
        :return:
        """

        for step in range(self.max_iter):  # 遍历次数max_iter
            for user, rates in self.rating_data:
                for item, rui in rates:
                    hat_rui = self.predict(user, item)  # 预测得分
                    err_ui = rui - hat_rui  # 计算误差
                    for f in range(self.F):  # 梯度下降更新
                        self.P[user][f] += self.alpha * (err_ui * self.Q[item][f] - self.lmbd * self.P[user][f])
                        self.Q[item][f] += self.alpha * (err_ui * self.P[user][f] - self.lmbd * self.Q[item][f])
            self.alpha *= 0.9  # 每次迭代步长要逐步缩小

    def predict(self, user, item):
        """
        :param user:
        :param item:
        :return:
        预测用户user对物品item的评分
        """
        return sum(self.P[user][f] * self.Q[item][f] for f in range(self.F))


if __name__ == '__main__':
    '''用户有A B C,物品有a b c d'''
    rating_data = list()
    rate_A = [('a', 2.0), ('b', 1.0)]
    rating_data.append(('A', rate_A))
    rate_B = [('b', 1.0), ('c', 1.0)]
    rating_data.append(('B', rate_B))
    rate_C = [('c', 1.0), ('d', 1.0)]
    rating_data.append(('C', rate_C))

    lfm = LFM(rating_data, 2)
    lfm.train()
    for item in ['a', 'b', 'c', 'd']:
        print(item, lfm.predict('A', item))  # 计算用户A对各个物品的喜好程度

(4) RSVD(增加正则项)

RSVD是在Basic SVD目标函数的基础上,中加入正则化参数(加入惩罚项),以防止过拟合。

预测函数

在这里插入图片描述

目标函数(损失函数+正则项)

在这里插入图片描述

梯度下降

求梯度

在这里插入图片描述

梯度更新

在这里插入图片描述

(5) RSVD进一步优化(消除用户和物品打分的偏差)

  • 由于不同用户的打分体系不同(比如在 5分为满分的情况下,有的用户认为打 3 分已经是很低的分数了,而有的用户认为打 1 分才是比较差的评价 )
  • 不同物品的衡量标准也有所区别(比如电子产品的平均分和日用品的平均分差异有可能比较大 )

为了消除用户和物品打分的偏差(Bias ),常用的做法是在矩阵分解时加入用户和物品的偏差向量。

预测函数

在这里插入图片描述

目标函数(损失函数+正则项)

在这里插入图片描述

梯度下降

求梯度

在这里插入图片描述

梯度更新

在这里插入图片描述

3、矩阵分解优缺点

优点

相比协同过滤,矩阵分解有如下非常明显的优点:

(1)泛化能力强,在一定程度上解决了数据稀疏问题。

  • 显然,协同过滤在应对稀疏矩阵时,准确率很低。举个例子:在一个共现矩阵中,大部分物品只有少数用户评分了,那么计算相似性的时候,很难找到准确的相似。而在矩阵分解中,比如梯度下降中,我们是不需要关注缺失值的,我们只需要利用已知的评分,去拟合出我们的两个矩阵然后就可以通过这两个矩阵,去预测缺失值

  • 协同过滤中,它只利用了用户自身物品自身的信息,矩阵分解中,挖掘了用户与物品的潜在信息,可以看作是对用户和物品“打标签”,用户和物品画像都得到了丰宫,提高了泛化能力

(2)空间复杂度低。

协同过滤,需要存储庞大的
[用户-用户 相似度] m×m矩阵,[物品-物品 相似度] n×n矩阵
矩阵分解,只需要存储
[用户隐向量] m×k,[物品隐向量]n×k

(3)更好的扩展性和灵活性
产生的隐向量,便于与其他特征进行组合和拼接便于与深度学习网络进行无缝结合

缺点

  • 与协同过滤一样,矩阵分解同样不方便加入用户、物品和上下文相关的特征,这使得矩阵分解丧失了利用很多有收信息的机会

  • 同时在缺乏用户历史行为时,无法进行有效的推荐。

为了解决这问题,逻辑回归模型及其后续发展出的因子分解机等模型,凭借其天然的融合不同特征的能力,逐渐在推荐系统领域得到更广泛的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/180897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态顺序表——简单的增删查改

前言 :从这篇博客开始,我会进行数据结构(用C语言实现)有关内容的记录与分享。对于我们而言,数据结构的知识难度较大并且十分重要,希望我的分享给各位带来一些帮助。而今天要分享的就是数据结构中最简单的知识——顺序表的增删查改…

11.Java方法的综合练习题大全-双色球彩票系统,数字的加密和解密等试题

本篇文章是Java方法的专题练习,从第五题开始难度增大,涉及大厂真题,前四道题目是基础练习,友友们可有目的性的选择学习😘💕 文章目录前言一、数组的遍历1.注意点:输出语句的用法2.题目正解二、数组最大值三、判断是否存在四、复制数组五、案例一:卖飞机票…

【学习笔记之数据结构】二叉树(一)

二叉树的概念: 二叉树是一种树的度不大于2的树,也就是它的节点的度都是小于等于2的。二叉树的子树有左右之分,左右的次序不能颠倒,因此二叉树是一个有序树。任意的二叉树都由空树、只有根节点、只有左子树、只有右子树、左右子树均…

一个简单的自托管图片库HomeGallery

什么是 HomeGallery ? HomeGallery 是一个自托管的开源 Web 图片库,用于浏览个人照片和视频,其中包括标记、对移动端友好和 AI 驱动的图像和面部发现功能。 HomeGallery 的独特功能是自动 相似图像/反向图像搜索功能 和 无数据库架构 &#x…

实验三、8人智力竞赛抢答电路设计

实验三 8人智力竞赛抢答电路设计 实验目的 设计一个能支持八路抢答的智力竞赛抢答器;主持人按下开始抢答的按键后,有短暂的报警声提示抢答人员抢答开始且指示灯亮表示抢答进行中;在开始抢答后数码管显8秒倒计时;有抢答人员按下抢…

Linux企业应用现状

一、Linux在服务器领域的发展 随着开源软件在世界范围内影响力日益增强,Linux服务器操作系统在整个服务器操作系统市场格局中占据了越来越多的市场份额,已经形成了大规模市场应用的局面。并且保持着快速的增长率。尤其在政府、金融、农业、交通、电信等国…

linux 网络编程socket

前言 socket(套接字)是linux下进程间通信的一种方式,通常使用C-S(客户端-服务端)的方式通信,它可以是同一主机下的不同进程间通信或者不同主机的进程通信。 socket是夹在应用层和TCP/UDP协议层间的软件抽象…

机器自动翻译古文拼音 - 将进酒拼音版本,译文拼音版本

写了一个程序,用来给佛经和古诗加上拼音,并处理多音字和排版,顺便加上翻译。 定期翻译一些,给老人和小孩子用。 将进酒 君不见,黄河之水天上来,奔流到海不复回。 君不见,高堂明镜悲白发&…

Servlet介绍及其概念

Servlet介绍及其概念一、Web基础二、编写HTTP Server,打印Hello,World三、Servlet的出现1. 思考上述HTTP服务器的问题2. 实现代码重用,简化开发过程3. 实现最简单的Servlet4. 导入依赖5. pom.xml文件6. Servlet版本问题7. 整个Servlet工程结构四、运行Se…

Windows卸载与清除工具 “ Geek 与 CCleaner ”

前言 📜“作者 久绊A” 专注记录自己所整理的Java、web、sql等,IT技术干货、学习经验、面试资料、刷题记录,以及遇到的问题和解决方案,记录自己成长的点滴 目录 前言 一、Geek的简介 1、大概介绍 2、详细介绍 二、Geek的下载 1、…

千峰Ajax【fetch和promise】

promise基础 <script>// Promise构造函数var q new Promise(function (resolve, reject) {//异步setTimeout(function () {// 成功// resolve(["111", "222", "333"]);// 失败reject("error");}, 2000);});// q是promise对象q…

利用系统函数与堆栈快速定位app关键代码

string.trim 这个还是比较关键的&#xff0c;没准可以从这里得到加密方式&#xff0c;或者挖到sql注入&#xff0c;文件上传等漏洞。进一步利用可以打印堆栈来用 Java.perform(function(){function showStack(){console.log(Java.use("android.util.Log").getStack…

TCP/IP网络编程——基于 TCP 的服务端/客户端(下)

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 5 章 基于 TCP 的服务端/客户端&#xff08;2&#xff09;5.1 回声客户端的完美实现5.1.1 回声服务器没有问题&#xff0c;只有回声客户端有问题&#xff1f;5.1.2 回声客户端问题的解决办法5.1.3 如果问题不…

chrome插件开发时使用import

问题描述 在进行chrome插件开发时&#xff0c;我们有时会希望把一些公共的方法包装成一个模块&#xff0c;例如发送网络请求的方法&#xff0c;然后在其他js文件中import然后调用&#xff0c;但是在实际操作时&#xff0c;遇到了这样的问题&#xff1a; 控制台报错cannot use …

Vistual Studio Code 安装与配置C/C++环境

1. 下载VScode 2. 安装cpptools工具 3. 下载MinGW 4. 配置环境变量 5. 使用简单的.cpp文件配置C环境 6. 运行 注&#xff1a;本文所有的地址配置要根据读者的实际情况来&#xff0c;不要照文章复制&#xff01;&#xff01;&#xff01; 下载VScode 下载链接&#xff1a;https…

浏览器调用本地DLL的方法

要在浏览器中调用本地DLL&#xff0c;常见的方法是使用插件。但是为了安全&#xff0c;现在有的浏览器对插件开发做了限制&#xff0c;不让插件调用外部DLL。比如说Chrome&#xff0c;为了调用外部的DLL&#xff0c;我们只能使用早期的chrome版本。 还有一种方法就是在电脑上安…

linux编辑器的使用(gcc,g++)

前言 gcc/g是一个编译器。 我们程序的翻译有四个步骤1.预处理(头文件展开&#xff0c;条件编译&#xff0c;宏替换&#xff0c;去注释)2.编译(c语言汇编语言)3.汇编(汇编->可重定位目标二进制文件&#xff0c;不可以被执行的&#xff0c;bin.obj)----只是把我们自己的代码进…

下载Windows ISO镜像的方法 (超详细 适合新手入门)

前言 &#x1f4dc;“作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴 目录 前言 一、镜像介绍 1、大概介绍 2、详细介绍 二、下载Window…

Solon 1.12.4 发布

一个更现代感的 Java "生态型"应用开发框架&#xff1a;更快、更小、更自由。不是 Spring&#xff0c;没有 Servlet&#xff0c;也无关 JavaEE&#xff1b;新兴独立的开放生态 &#xff08;已有150来个生态插件&#xff09; 。主框架仅 0.1 MB。 相对于 Spring Boot…

JVM学习总结,全面介绍运行时数据区域、各类垃圾收集器的原理使用、内存分配回收策略

参考资料&#xff1a;《深入理解Java虚拟机》第三版 文章目录一&#xff0c;运行时数据区域&#xff08;基础重中之重&#xff09;二&#xff0c;垃圾收集器与内存分配策略1&#xff09;对象已死2&#xff09;再谈引用3&#xff09;对象回收4&#xff09;内存分代收集理论&…