协同过滤源代码在真实数据集上运行及优化

news2024/11/23 0:11:43

最近在做推荐算法相关研究,

先拿一个协同过滤代码练手。

网上找代码很容易,但是大多是讲原理的示例代码,在真实数据集上运行问题特别多。

以一个2w节点的开源数据集为例(baby.inter)

https://github.com/enoche/MMRec/

https://drive.google.com/drive/folders/13cBy1EA_saTUuXxVllKgtfci2A09jyaG?usp=sharing

列举一些我遇到的问题:

1.稀疏矩阵占用内存过大,使用coo_matrix可以极大减少占用

2.矩阵类型转换过程中维度经常出错,矩阵读取某列的方法和稀疏矩阵读某列的方法返回格式不同

3.使用多线程提速的过程中内存溢出,导致部分线程崩溃,运行结束后只得到一半结果。

4.获取结果的语句嵌套过多难以理解,例如:recommendations = unrated_items[np.argsort(-pred[unrated_items])][0:top_k]

5.格式转换不当导致内存溢出(64g内存的电脑啊,直接蓝屏了)

6.np.save np.savez不会用。

下面直接上代码

1.读取数据,这里数据的最后一位是训练集、验证集、测试集的划分标签。chatGPT写了几遍也不对,还是自己写的。

# 数据读取
def load_data(file_path):
    train_data_u = []
    train_data_i = []
    train_data_r = []
    val_data_u = []
    val_data_i = []
    val_data_r = []
    test_data_u = []
    test_data_i = []
    test_data_r = []

    with open(file_path) as file:
        next(file)  # 跳过第一行
        for line in file:
            line = line.strip().split("\t")
            userID = int(line[0])
            itemID = int(line[1])
            rating = float(line[2])
            x_label = line[4]
            
            if x_label == '0':
                train_data_u.append(userID)
                train_data_i.append(itemID)
                train_data_r.append(rating)
            elif x_label == '1':
                val_data_u.append(userID)
                val_data_i.append(itemID)
                val_data_r.append(rating)
            elif x_label == '2':
                test_data_u.append(userID)
                test_data_i.append(itemID)
                test_data_r.append(rating)

    num_users = max(train_data_u) + 1
    num_items = max(train_data_i) + 1
    
    train_data_matrix = coo_matrix((train_data_r, (train_data_u,train_data_i)), shape=(num_users, num_items))
    val_data_matrix = coo_matrix((val_data_r, (val_data_u,val_data_i)), shape=(num_users, num_items))
    test_data_matrix = coo_matrix((test_data_r, (test_data_u,test_data_i)), shape=(num_users, num_items))
    
    return train_data_matrix, val_data_matrix,test_data_matrix

2.训练模型,这里很多代码都只获取用户相似度,然后在预测过程中用户相似度乘评分矩阵得到相似用户的评分和。但是把矩阵乘法运算放在这里只需要计算1次,减少了约2w次的大规模矩阵乘法运算。

函数返回的矩阵每行表示某个用户对所有物品的评分。(这里把每个矩阵的维度都列举出来就能分析清楚了,我只是做笔记,懒得写。)矩阵每列表示一个商品。

# 模型训练
def train_model(data_matrix):
    similarity_matrix = cosine_similarity(data_matrix, dense_output=False)
    pred=similarity_matrix.dot(data_matrix)
    print(pred)
    return pred

3.预测用户评分,这里注意tocsr(),被坑了好久,coo_matrix可以直接进行点乘运算,但是不能做切片运算,切片出来某一行也和普通矩阵索引出来某行不一样。

然后就是非常复杂的排序运算,这里实在无法优化,也是整个代码最耗时的部分,因为要执行大约2w次。

def predict(ratings,pred, user_id, top_k):

    pred1=pred.tocsr()[user_id].toarray()[0]
    ratings1=ratings.tocsr()[user_id].toarray()[0]

    unrated_items = np.where(ratings1 == 0)[0]
    sort_index=np.argsort(-pred1[unrated_items])
    sort_items=unrated_items[sort_index]
    # 根据预测评分生成推荐列表
    recommendations =sort_items [0:top_k]
    return recommendations

4.评价函数,没什么说的,gpt写的直接拿来用

# 评估指标
def evaluate_recommendations(recommendations, test_matrix, k):
    num_users, num_items = test_matrix.shape
    recall = 0.0
    ndcg = 0.0
    precision = 0.0
    map_value = 0.0
    
    for user_id in range(num_users):
        test_ratings = test_matrix[user_id]
        if np.sum(test_ratings) > 0:
            relevant_items = np.where(test_ratings > 0)[0]
            
            recall += len(set(recommendations[user_id]) & set(relevant_items)) / len(relevant_items)
            
            dcg = 0.0
            idcg = np.log2(2)
            for i in range(k):
                item_id = recommendations[user_id][i]
                if item_id in relevant_items:
                    dcg += 1 / np.log2(i + 2)
            
            ndcg += dcg / idcg
            
            precision += len(set(recommendations[user_id]) & set(relevant_items)) / k
            
            ap = 0.0
            for i in range(k):
                item_id = recommendations[user_id][i]
                if item_id in relevant_items:
                    ap += len(set(recommendations[user_id]) & set(relevant_items)) / (i + 1)
            ap /= len(relevant_items)
            
            map_value += ap
    
    recall /= num_users
    ndcg /= num_users
    precision /= num_users
    map_value /= num_users
    
    return recall, ndcg, precision, map_value

5.主函数,写主函数一部分是c语言的习惯,另一部分是随时可以用return打断,否则当执行到thread.join时代码无法ctrl+c打断,只能直接关控制台。(哦,我都是记事本写代码,在控制台执行,没有IDE)

def printTime():
    print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}]")


def main():        
    print('start')
    printTime()    
    # 数据路径
    file_path = "..\\MMRec-master\\data\\baby\\baby.inter"

    # 加载数据
    train_matrix, val_matrix,test_matrix = load_data(file_path)

    # 训练
    pred = train_model(train_matrix)
    print(pred.toarray()[0])
    # 生成推荐
    top_k = 10
    recommendations = {}
    recommendations[0]=(predict(train_matrix,pred,0, top_k))
    print(recommendations[0])
    print(len(recommendations))

    #return
    # 创建一个线程列表
    threads = []
    num_threads=16
    # 将用户数量按照线程数进行划分
    user_per_thread = test_matrix.shape[0] // num_threads
    # 定义一个函数,用于并行计算推荐结果
    def calculate_recommendations(start, end):
        print(start,end)
        for user_id in range(start, end):
            recommendations[user_id] = predict(train_matrix, pred, user_id, top_k)#2.82G
            if(user_id%500==0):
                print(user_id)
                
    for i in range(num_threads):
        start = i * user_per_thread
        end = (i+1) * user_per_thread
        
        # 创建并启动一个新线程
        t = threading.Thread(target=calculate_recommendations, args=(start, end))
        t.start()
        
        threads.append(t)

    # 等待所有线程完成
    for t in threads:
        t.join()
       
    start=  int( test_matrix.shape[0] // num_threads)*num_threads
    end=test_matrix.shape[0]
    calculate_recommendations(start, end)
      
    printTime()
    np.savez('data.npz',recommendations=recommendations,test_matrix=test_matrix)
    '''data=np.load('data.npz',allow_pickle=True)
    recommendations=data['recommendations'].item();
    test_matrix=data['test_matrix'].item();'''

    # 评估指标
    recall, ndcg, precision, map_value = evaluate_recommendations(recommendations, test_matrix.toarray(), top_k)
    printTime()
    print("Recall =", recall)
    print("NDCG =", ndcg)
    print("Precision =", precision)
    print("MAP =", map_value)
    
main()

6.上面的np.savez是因为在花费了11个小时运行完代码后在评分环节可能崩溃,为了避免重新运行11个小时,第一时间保存结果。

后记:

这个代码在单线程第一次跑通时占用2.8g内存,11个小时。

后来4个线程,2.8*4+2.8+2=16,刚好让一台16g内存的电脑随时崩溃,4个子线程,1个主线程,2g系统占用。

后来换了一台电脑8个线程占用1个半小时,内存占用25g。

现在这版同时开16个线程,只需要20秒,

我去挑战更大的数据集了。

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

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

相关文章

13.XXL-JOB应用

XXL-JOB应用 1.介绍 XXL-JOB是大众点评员工徐雪里于2015年发布的分布式任务调度平台 2.XXL-JOB与Quartz的关系 老版本依赖Quartz的定时任务触发,在V2.1.0版本开始移除了Quartz 3.官方地址 官方文档:https://www.xuxueli.com/xxl-job/gitee&#xf…

【云原生】初识Docker,安装以及了解操作命令

一、为什么要使用容器? 背景:以前开发、测试、生产为不同的环境,痛点是发现开发测试以后没问题,但是在生产环境无法运行。给测试、开发、运维人员造成了大量的工作。最终结果是发版更新速度也跟不上,效率低 我认为使…

如何制作自己的实景中国视频地图?

让每一个人都有自己的地图! 我们在《水经微图Web版1.5.0发布》一文中,提到了水经微图(简称“微图”)Web版新增了视频气泡的功能。 现在,我们为你分享一下如何基于此功能,制作一个属于自己的实景中国视频地…

Final Cut Pro v10.7.1中文版 专业级视频剪辑软件 兼容M

Final Cut Pro 是 macOS平台上最好的视频剪辑软件,基于Cocoa编写,支持多路多核心处理器,支持GPU加速,支持后台渲染,可编辑从标清到4K的各种分辨率视频,ColorSync管理的色彩流水线则可保证全片色彩的一致性。…

vue常用指令(v-on)

一、v-on 指令 作用: 为元素绑定事件, 比如: v-on:click,可以简写为 click“方法”绑定的方法定义在 VUE实例的, method属性中 二、代码演示 1、v-on绑定点击事件 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&quo…

如何做好一个信息系统项目经理,一个项目经理的个人体会和经验总结(三)

前言 今天我们继续聊聊在 项目开发阶段&#xff0c;项目经理需要做好的事情 &#x1f603; 二、项目开发阶段&#xff08;续&#xff09; 4. 控制好项目开发质量 要控制好项目开发质量&#xff0c;主要是依赖测试&#xff0c;好的产品都是靠不断地测试&#xff0c;不断地试…

yum仓库和NFS文件共享服务

一、yum仓库简介&#xff1a; 1.yum仓库简介&#xff1a; yum是一个基于RPM包&#xff08;是Red-Hat Package Manager红帽软件包管理器的缩写&#xff09;构建的软件更新机制&#xff0c;能够自动解决软件包之间的依赖关系。解决了日常工作中的大量查找安装依赖包的时间 为什…

Vue2中CesiumV1.113.0加载离线地图

Vue2中CesiumV1.113.0加载离线地图&#xff0c;本文以天地图为例。 1.使用nodejs获取天地图 新建nodejsdownmap项目文件夹&#xff0c;初始化项目 npm init -y src/index.js // An highlighted block var Bagpipe require(bagpipe) var fs require("fs"); var r…

【python应用】—利用python构造邮件、解析邮件、发送邮件、接收邮件(email、smtplib、imaplib模块)

文章目录 一、前言1、邮件构成二、email模块1、email模块的Message类Message类常用方法2、email.mine模块:构建电子邮件信息MIMEBase类实现3、email.parser模块:解析电子邮件信息4、email.header模块:丰富、解析邮件头5、email.utils模块:其他工具6、email.iterators模块:…

小程序学习-21

目前小程序分包大小有以下限制&#xff1a; 整个小程序所有分包大小不超过 20M单个分包/主包大小不能超过 2M 独立分包&#xff1a;"independent": true

若依前后台分离vue项目放开前台页面拦截配置

若依前后台分离vue项目放开前台页面拦截配置 使用场景某些页面不需要权限就能直接访问的页面 , 例如做个单点登录之类的。这里只需要修改2处即可 ssologin.vue代码 <template> </template> <script> export default {name: "SsoLogin",data() {r…

Python + Selenium —— ActionChains动作链!

当你需要执行复杂的操作时&#xff0c;比如将一个元素按住拖动到另一个元素上去&#xff0c;需要移动鼠标然后点击并按下键盘某个按键等等。 当然&#xff0c;在 Web 页面上&#xff0c;这种操作好像比较少。 但是&#xff0c;如果遇到了怎么办呢&#xff1f;这就需要用到 Ac…

JAVAEE初阶 网络编程(三)

TCP回显服务器 一. TCP的API二. TCP回显服务器的代码分析三. TCP回显服务器代码中存在的问题四. TCP回显服务器代码五. TCP客户端的代码六.TCP为基准的回显服务器的执行流程 一. TCP的API 二. TCP回显服务器的代码分析 这的clientSocket并不是表示用户端的层面东西&#xff0c;…

Git--基本操作介绍(2)

Git 常用的是以下 6 个命令&#xff1a;git clone、git push、git add 、git commit、git checkout、git pull. 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff1a;暂存区/缓存区local repository&#xff1a;版本库或本地仓库remote repository&#xf…

Leetcode—25.K 个一组翻转链表【困难】

2023每日刷题&#xff08;八十二&#xff09; Leetcode—25.K 个一组翻转链表 算法思想 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val…

论文精读--ResNet

ResNet论文 撑起计算机视觉半边天的ResNet【论文精读】_哔哩哔哩_bilibili Abstract Deeper neural networks are more difficult to train. We present a residual learning framework to ease the training of networks that are substantially deeper than those used pre…

代码随想录算法训练营第四十二天|01背包问题、01背包问题(滚动数组)、416. 分割等和子集

题目&#xff1a;01背包问题 文章链接&#xff1a;代码随想录 视频链接&#xff1a;LeetCode:背包问题 题目链接&#xff1a;卡码题目链接 图释&#xff1a; //二维dp数组实现 #include <bits/stdc.h> using namespace std;int n, bagweight;// bagweight代表行李箱空…

​​快速排序(四)——挖坑法,前后指针法与非递归

目录 ​一.前言 二.挖坑法 三.前后指针法 四.递归优化 五.非递归 六.结语 一.前言 本文我们接着上篇文章的重点快排&#xff0c;现在继续讲解对快排优化的挖坑法&#xff0c;前后指针法以及非递归方法&#xff0c;下面是上篇文章快排链接&#xff1a;https://mp.csdn.net…

如何用AirServer进行手机投屏?,Airserver 永久激活注册码

AirServer一款投屏神器&#xff0c;可以帮你轻松地将iPhone、iPad投屏到Mac。是不是经常看到游戏主播用AirServer投屏&#xff1f;此外&#xff0c;AirServer也是视频Up主必备工具之一&#xff01;用来录制演示教程不错。除了实现单个手机投屏到电脑或荧幕。如果你有多画面投屏…

Python - 【Socket】消息粘包处理demo(一)

一. 前言 在网络编程中&#xff0c;粘包是指TCP协议在传输过程中&#xff0c;多条数据被合并成一条数据发送或者一条数据被拆分成多个数据发送的现象。 二. 粘包问题的常规处理方法&#xff1a; 使用固定长度的包头 可以在发送数据前先发送一个固定长度的包头&#xff0c;包…