深度学习实战案例:基于 AutoRec 构建电影推荐系统( 附 PyTorch 版代码)

news2025/1/19 17:20:02

文章目录

    • 技术交流
    • 前言
    • AutoRec 模型介绍
    • 损失函数
    • 基于 AutoRec 的推荐过程
    • 实验对比
    • 消融实验
    • 代码实践
    • 总结
    • 参考

本文要介绍的 AutoRec 模型是由澳大利亚国立大学在2015年提出的,它将自编码器(AutoEncoder)的思想与协同过滤(Collaborative Filter)的思想结合起来,提出了一种单隐层的简单神经网络推荐模型。

可以说这个模型的提出,拉开了使用深度学习解决推荐系统问题的序幕,为复杂深度学习网络的构建提供了思路。

原论文只有2页,非常简洁明了,比较适合作为深度学习推荐系统的入门模型来学习,原文地址在这里。

技术交流

技术要学会分享、交流,不建议闭门造车。一个人走的很快、一堆人可以走的更远。

本文来自星球技术群粉丝分享整理,文章源码、数据、技术交流,均可加交流群获取,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、添加微信号:pythoner666,备注:来自CSDN +备注来意
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:加群

前言

本文会介绍AutoRec模型的基本原理,包括网络模型、损失函数、推荐过程、实验结果等,并且会给出基于PyTorch的代码。

AutoRec 模型介绍

AutoRec模型跟MLP(多层感知器)类似,是一个标准的3层(包含输入层)神经网络,只不过它结合其结合了自编码器(AutoEncoder)和协同过滤(Collaborative Filtering)的思想。其实再确切一点说,AutoRec模型就是一个标准的自编码器结构,它的基本原理是利用协同过滤中的共现矩阵,完成物品向量或者用户向量的自编码。再利用自编码结果得到用户对所有物品的评分,结果通过排序之后就可以用于物品推荐。

这里先简要地介绍一下自编码器:

自编码器是一种无监督的数据维度压缩和数据特征表达方法,它是神经网络的一种,经过训练后能尝试将输入复制到输出。自编码器由编码器和解码器组成,结构如下:

下面是AutoRec的整体模型框图:

基于物品的AutoRec模型,可以看到整个模型只有3层,蓝色的圆点代表的是隐层神经元,红色方框代表的是模型的输入

损失函数

首先给出自编码器的损失函数,如下:

AutoRec 模型的损失函数中考虑到了对参数的限制,因此加入了L2正则来防止过拟合,损失函数变化为:

基于 AutoRec 的推荐过程

在这里插入图片描述

实验对比

作者分别在MovieLens 1M和10M、以及Netflix数据上进行了对比实验,评测指标为RMSE,即均方根误差。分别与U-RBM、I-RBM、BiasedMF、LLORMA算法进行了对比。结果如下:

对比实验结果1

对比实验结果2

作者还做了消融实验,验证选择不同的激活函数对最终实验结果的影响。

消融实验

除此之外,还评测了不同隐层神经元数量对实验结果的影响, 可以看到随着隐层神经元数量的增加,RMSE稳步下降。

代码实践

代码基于 PyTorch 编写,主要包含数据预处理和加载文件dataloader.py,网络模型定义network.py,训练器trainer.py,以及测试文件autorec_test.py。

数据预处理部分比较简单,测试的数据为 MovieLens 1M 数据集,主要定义了共现矩阵,并且将数据集划分为训练集和测试集。

部分代码如下:

import torch
import numpy as np
import torch.utils.data as Data

def dataProcess(filename, num_users, num_items, train_ratio):
    fp = open(filename, 'r')
    lines = fp.readlines()

    num_total_ratings = len(lines)

    user_train_set = set()
    user_test_set = set()
    item_train_set = set()
    item_test_set = set()

    train_mask_r = np.zeros((num_users, num_items))
    test_mask_r = np.zeros((num_users, num_items))

    
    random_perm_idx = np.random.permutation(num_total_ratings)
    
    train_idx = random_perm_idx[0:int(num_total_ratings * train_ratio)]
    test_idx = random_perm_idx[int(num_total_ratings * train_ratio):]

    ''' Train '''
    for itr in train_idx:
        line = lines[itr]
        user, item, rating, _ = line.split("::")
        user_idx = int(user) - 1
        item_idx = int(item) - 1
        train_r[user_idx][item_idx] = int(rating)
        train_mask_r[user_idx][item_idx] = 1

        user_train_set.add(user_idx)
        item_train_set.add(item_idx)

    ''' Test '''
    for itr in test_idx:
        line = lines[itr]
        user, item, rating, _ = line.split("::")
        user_idx = int(user) - 1
        item_idx = int(item) - 1
        test_r[user_idx][item_idx] = int(rating)
        test_mask_r[user_idx][item_idx] = 1

        user_test_set.add(user_idx)
        item_test_set.add(item_idx)

    return train_r, train_mask_r, test_r, test_mask_r, user_train_set, item_train_set, user_test_set, item_test_set

def Construct_DataLoader(train_r, train_mask_r, batchsize):
    torch_dataset = Data.TensorDataset(torch.from_numpy(train_r), torch.from_numpy(train_mask_r))
    return Data.DataLoader(dataset=torch_dataset, batch_size=batchsize, shuffle=True)

网络模型部分代码比较简单,基本就是两个全连接层外加一个Sigmoid激活函数就搞定。

代码如下:

import torch
import numpy as np
import torch.nn as nn

class AutoRec(nn.Module):
    """
    基于物品的AutoRec模型
    """
    def __init__(self, config):
        super(AutoRec, self).__init__()
        self._num_items = config['num_items']
        self._hidden_units = config['hidden_units']
        self._lambda_value = config['lambda']
        self._config = config

        
        self._encoder = nn.Sequential(
            nn.Linear(self._num_items, self._hidden_units),
            nn.Sigmoid()
        )
        
        self._decoder = nn.Sequential(
            nn.Linear(self._hidden_units, self._num_items)
        )

    def forward(self, input):
        return self._decoder(self._encoder(input))

    def loss(self, res, input, mask, optimizer):
        cost = 0
        temp = 0

        cost += ((res - input) * mask).pow(2).sum()
        rmse = cost

        for i in optimizer.param_groups:
            
            for j in i['params']:
                if j.data.dim() == 2:
                    temp += torch.t(j.data).pow(2).sum()

        cost += temp * self._config['lambda'] * 0.5
        return cost, rmse

    def recommend_user(self, r_u, N):
        """
        :param r_u: 单个用户对所有物品的评分向量
        :param N: 推荐的商品个数
        """
        
        predict = self.forward(torch.from_numpy(r_u).float())
        predict = predict.detach().numpy()
        indexs = np.argsort(-predict)[:N]
        return indexs

    def recommend_item(self, user, test_r, N):
        """
        :param r_u: 所有用户对物品i的评分向量
        :param N: 推荐的商品个数
        """
        
        recommends = np.array([])

        for i in range(test_r.shape[1]):
            predict = self.forward(test_r[:, i])
            recommends.append(predict[user])

        
        indexs = np.argsot(-recommends)[:N]
        
        return recommends[indexs]

    def evaluate(self, test_r, test_mask_r, user_test_set, user_train_set, item_test_set, item_train_set):
        test_r_tensor = torch.from_numpy(test_r).type(torch.FloatTensor)
        test_mask_r_tensor = torch.from_numpy(test_mask_r).type(torch.FloatTensor)

        res = self.forward(test_r_tensor)

        for user in unseen_user_test_list:
            for item in unseen_item_test_list:
                if test_mask_r[user, item] == 1:
                    res[user, item] = 3

        mse = ((res - test_r_tensor) * test_mask_r_tensor).pow(2).sum()
        RMSE = mse.detach().cpu().numpy() / (test_mask_r == 1).sum()
        RMSE = np.sqrt(RMSE)
        print('test RMSE : ', RMSE)

    def saveModel(self):
        torch.save(self.state_dict(), self._config['model_name'])

    def loadModel(self, map_location):
        state_dict = torch.load(self._config['model_name'], map_location=map_location)
        self.load_state_dict(state_dict, strict=False)

测试代码主要是包含了模型训练,随机挑选了3个用户并推荐5个商品,以及在测试集上评估RMSE指标等。
代码如下:

import torch
from AutoRec.trainer import Trainer
from AutoRec.network import AutoRec
from AutoRec.dataloader import dataProcess

autorec_config = \
{
    'train_ratio': 0.9,
    'num_epoch': 200,
    'batch_size': 100,
    'optimizer': 'adam',
    'adam_lr': 1e-3,
    'l2_regularization':1e-4,
    'num_users': 6040,
    'num_items': 3952,
    'hidden_units': 500,
    'lambda': 1,
    'device_id': 2,
    'use_cuda': False,
    'data_file': '../Data/ml-1m/ratings.dat',
    'model_name': '../Models/AutoRec.model'
}

if __name__ == "__main__":
    
    
    
    train_r, train_mask_r, test_r, test_mask_r, \
    user_train_set, item_train_set, user_test_set, item_test_set = \
        dataProcess(autorec_config['data_file'], autorec_config['num_users'], autorec_config['num_items'], autorec_config['train_ratio'])
    
    autorec = AutoRec(config=autorec_config)
    
    autorec.loadModel(map_location=torch.device('cpu'))
    
    print("用户1推荐列表: ",autorec.recommend_user(test_r[0], 5))
    print("用户2推荐列表: ",autorec.recommend_user(test_r[9], 5))
    print("用户3推荐列表: ",autorec.recommend_user(test_r[23], 5))

    autorec.evaluate(test_r, test_mask_r, user_test_set=user_test_set, user_train_set=user_train_set, \
                     item_test_set=item_test_set, item_train_set=item_train_set)

测试结果如下:

总结

AutoRec 模型是深度学习方法用于推荐系统中的开山之作,它使用了一个单隐层的自编码器来泛化用户和物品评分,使模型具有一定的泛化和表达能力,稍微增加模型的复杂度,表现效果将非常明显。

参考

  • 《深度学习推荐系统》-- 王喆
  • https://blog.csdn.net/quiet_girl/article/details/84401029
  • https://zhuanlan.zhihu.com/p/163673436
  • https://zhuanlan.zhihu.com/p/159087297
  • https://github.com/NeWnIx5991/AutoRec-for-CF/blob/master/autorec.py

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

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

相关文章

GitHub 创建 Pull Request 将代码提交至别人的仓库

GitHub 创建 Pull Request 将代码提交至别人的仓库 1 Forking the repository 1.1 About forks (关于 forks) A fork is a new repository that shares code and visibility settings with the original upstream repository. A fork 是一个新的存储库,它与原 ups…

U-Boot 命令使用

进入 uboot 的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前 uboot 所 支持的命令,如图 所示: 我们输入“help(或?) 命令名”既可以查看命令的详细用法,以“bootz”这 个命令为例,我们输…

4种吃子跳棋

目录 一,双玩家吃子跳棋 玻璃跳棋 大人物跳棋 二,单玩家吃子跳棋 智力游戏67跳棋(5) 一个挑战 跳瓶盖 欢乐跳跳棋 三,单玩家多目吃子跳棋——Hopping dots 1,Hopping dots 2,规则 3…

说过的话就一定要办到 - redo日志

一、什么是redo日志? 如果我们只在内存的 Buffer Pool 中修改了页面,假设在事务提交后突然发生了某个故障,导致内存中的数据都失效了,那么这个已经提交了的事务对数据库中所做的更改也就跟着丢失了,这会导致事务会失去…

火力全开,重新定义蓝牙耳机!新一代南卡OE Pro不入耳式蓝牙耳机震撼来袭

中国专业声学品牌Nank南卡,在近期推出了南卡OE Pro不入耳蓝牙耳机,是业内首款功能配置齐全的蓝牙耳机,以创新开放式听音方式,让更多人感受到不入耳开放式耳机带来的魅力之处。据了解,有不少媒体猜测,南卡OE…

工作面试老大难 - 锁

一、概述 为保证数据的一致性和完整性,需要对 事务间并发操作进行控制 ,因此产生了 锁 。锁冲突 也是影响数据库 并发访问性能 的一个重要因素。所以锁对数据库而言显得尤其重要,也更加复杂。 二、并发问题 MySQL并发事务访问相同记录 &am…

硬件设计--DAPLINK设计

1 参考网站 1、打造属于你自己的STM32下载器调试器--------DAPLink 2、ARMmebed官方开源代码DAPLink 3、ARMmebed官方开源代码DAPLink github加速网站 4、ARMmebed官方开源硬件旧版 5、ARMmebed官方开源硬件新版 6、自制DAPLink – ARM官方源码以及STM32F103C8T6 7、如何做一个…

软件测试之测试名词解释

1. 白盒测试,英文是white-box testing 是通过程序的源代码进行测试而不使用用户界面。这种类型的测试需要从代码句法发现内部代码在算法,溢出,路径,条件等等中的缺点或者错误,进而加以修正。 2. 黑盒测试,英…

word脚标【格式:第X页(共X页)】

不得不吐槽一下这个论文,真的我好头疼啊。我又菜又不想改。但是还是得爬起来改 (是谁大半夜不能睡觉加班加点改格式啊) 如何插入页码。 格式、要求如下: 操作步骤: ①双击页脚,填好格式,宋体小四和居中都…

除了 Swagger,这个开源 API 管理工具生成文档更高效

提起 Swagger,经常接触接口开发的朋友,一定知道并且都熟练使用了。 Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上,并且…

VMware ESXi 8.0U1 发布 - 领先的裸机 Hypervisor

请访问原文链接:https://sysin.org/blog/vmware-esxi-8-u1/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 2023-04-18, VMware vSphere 8.0U1 发布。 详见:VMware vSphere 8 Update 1 新增功能 产品简…

ai智能写作助手-ai自动写作软件

为什么要用ai智能写作工具 在数字化时代,AI(人工智能)技术已经被广泛应用于各种领域,其中之一是写作。AI智能写作工具是利用自然语言处理技术和机器学习算法来生成高质量的文章、博客、新闻稿等。这些工具不仅提供了便捷、高效的…

校园网策列及思路

解决思路一: 适合以下情况也是我现在的学校校园网大概情况: 内网不认证情况下可以互联,除了几个常见端口封闭(目前已知3389),要联网必须认证登录,而且一个号最多两台设备(甚至有时候…

FANUC机器人DCS功能基本介绍

FANUC机器人DCS功能基本介绍 1. 定义 DCS(Dual Check Safety)位置/速度检查功能;利用机器人控制柜中两个独立CPU进行电机速度和位置数据的检查,实时检查位置和速度误差,并通过两个独立的通道关闭电机电源。 DCS功能可以有效地节约占地面积。 DCS功能可以防止机器人工具…

工业通讯应用中主流的常用协议Modbus协议

智联物联技术分享,本期为大家介绍工业通讯常用的主流协议Modbus协议。 Modbus协议的前身叫做Mod协议,常被用于Modicon公司的PLC控制器中,后来Modicon被Schneider收购后随之改名为我们如今所熟悉的modbus协议,现如今广泛应用在物联…

【高级数据结构】红黑树

本文整理红黑树学习过程中的知识点和底层代码实现。 目录 基本概念1、介绍2、应用3、性质 实现红黑树1、原理2、操作1)查找2)插入3)删除 和其他相似结构的对比1、二叉搜索树(BST)2、AVL树1)例子2&#xff0…

Dubbo+Zookeeper 实现服务远程调用

文章目录 一、Dubbo 架构图二、Zookeeper 注册中心三、SpringBoot 整合 Dubbo3.1 添加依赖3.2 配置服务端3.3 配置消费端3.4 启动测试 四、Dubbo-admin 管理中心4.1 部署服务端4.2 部署前端4.3 访问控制台 提示:以下是本篇文章正文内容,Java 系列学习将会…

Visual Assist X安装失败解决办法

最近重装了VS2017,在重装之前卸载了VA助手,但是等到装好VS再去装VA助手时,总是提示以下错误信息: Visual Assist Installer : An error was reported by Visual Studio VSIXInstaller. See the next window for access to its err…

ElasticJob

官网 :: ElasticJob ElasticJob 是面向互联网生态和海量任务的分布式调度解决方案,由两个相互独立的子项目 ElasticJob-Lite 和 ElasticJob-Cloud 组成。 它通过弹性调度、资源管控、以及作业治理的功能,打造一个适用于互联网场景的分布式调度解决方案&…

如何给厂区做导航地图?智能工厂导航地图解决方案公司

如何给厂区做导航地图?在智慧园区中,基于园区的电子地图地图使用的重要性越来越凸显。但目前在园区信息化应用形式中,广泛缺乏专业电子地图的使用,主要原因是:一是地图系统(GIS)实现繁复,与其他展会业务系统…