机器学习——协同过滤算法(CF)

news2024/12/23 12:57:44

机器学习——协同过滤算法(CF)

文章目录

  • 前言
  • 一、基于用户的协同过滤
    • 1.1. 原理
    • 1.2. 算法步骤
    • 1.3. 代码实现
  • 二、基于物品的协同过滤
    • 2.1. 原理
    • 2.2. 算法步骤
    • 2.3. 代码实现
  • 三、比较与总结
  • 四、实例解析
  • 总结


前言

协同过滤算法是一种常用的推荐系统算法,它基于用户的历史行为和其他用户的行为进行推荐(即物以类聚,人以群分的意思)
其原理主要分为两种类型:基于用户的协同过滤和基于物品的协同过滤。
本文将简单介绍一下协同过滤算法


在这里插入图片描述

一、基于用户的协同过滤

1.1. 原理

该算法基于用户之间的相似性来进行推荐。首先,计算用户之间的相似度,常用的相似度度量方法有余弦相似度、Pearson相关系数等。然后,根据相似度高的用户的行为(包括用户对物品的评分、点击、购买等行为),为目标用户推荐未曾接触过的物品。

1.2. 算法步骤

  1. 首先创建一个用户-物品评分矩阵,将用户的评分数据表示为一个矩阵,行表示用户,列表示物品

  2. 对数据进行去中心化处理。可以通过减去每个用户的平均评分来实现去中心化。

  3. 计算用户之间的相似度,可以使用余弦相似度公式计算两个用户之间的相似度:
    c o s ( θ ) = ∑ i = 1 n ( x i ∗ y i ) ∑ i = 1 n ( x i ) 2 ∗ ∑ i = 1 n ( y i ) 2 cos(\theta ) = \frac{\sum_{i=1}^{n}(x_{i} * y_{i})}{\sqrt{\sum_{i=1}^{n}(x_{i})^{2}}*\sqrt{\sum_{i=1}^{n}(y_{i})^{2}}} cos(θ)=i=1n(xi)2 i=1n(yi)2 i=1n(xiyi)

  4. 选择与目标用户最相似的k个用户

  5. 根据这k个用户的行为,为目标用户推荐未曾接触过的物品。可以通过加权平均计算得到目标用户对未曾接触过的物品的评分,然后按照评分进行排序,推荐评分较高的物品

1.3. 代码实现

import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 假设得到了一个有缺失值的用户-物品评分矩阵(评分越高,表示此用户对该物品的喜好程度越高)
data = pd.DataFrame({
    "one": [4, np.nan, 2, np.nan],
    "two": [np.nan, 4, np.nan, 5],
    "three": [5, np.nan, 2, np.nan],
    "four": [3, 4, np.nan, 3],
    "five": [5, np.nan, 1, np.nan],
    "six": [np.nan, 5, np.nan, 5],
    "seven": [np.nan, np.nan, np.nan, 4]
}, index=list('ABCD'))
print(data)
   one  two  three  four  five  six  seven
A  4.0  NaN    5.0   3.0   5.0  NaN    NaN
B  NaN  4.0    NaN   4.0   NaN  5.0    NaN
C  2.0  NaN    2.0   NaN   1.0  NaN    NaN
D  NaN  5.0    NaN   3.0   NaN  5.0    4.0

# first: 寻找与A最相似的其它用户  ; second: 预测A对未接触的物品(即原来评分为NaN的物品,如two)的评分,从而做出是否推荐的判断


# !!!对缺失值的处理要适当(想象一下,如果直接将缺失值填充为0,则会引入负面评价,因为此时0并不是一个中性值,甚至可以说是一个最小的值,直接拉低了评分),所以我们要先对这个矩阵进行去中心化,再将缺失值填充为0
# 去中心化(每一个用户的每一个项目的评分减去均值) , 这样每一个用户的项目的均值就为0了,那么0就是一个中性值,不会引入负面评价
new_data = data.apply(lambda x: x - x.mean(), axis=1)
print(new_data)
        one       two     three      four      five       six  seven
A -0.250000       NaN  0.750000 -1.250000  0.750000       NaN    NaN
B       NaN -0.333333       NaN -0.333333       NaN  0.666667    NaN
C  0.333333       NaN  0.333333       NaN -0.666667       NaN    NaN
D       NaN  0.750000       NaN -1.250000       NaN  0.750000  -0.25


#计算余弦相似度
"""
from sklearn.metrics.pairwise import cosine_similarity
sim_AB = cosine_similarity(new_data.loc["A", :].fillna(0).values.reshape(1, -1), new_data.loc["B",].fillna(0).values.reshape(1, -1))  
print(sim_AB)
sim_AC = cosine_similarity(new_data.loc["A", :].fillna(0).values.reshape(1, -1), new_data.loc["C",].fillna(0).values.reshape(1, -1))
print(sim_AC)
sim_AD = cosine_similarity(new_data.loc["A", :].fillna(0).values.reshape(1, -1), new_data.loc["D",].fillna(0).values.reshape(1, -1))
print(sim_AD)
"""
#这样一个一个计算太麻烦了,可定义一个函数来循环遍历
Result = {}
def yonhu_similar(mubiao,data):
    for i in range(data.shape[0]):
        bijiao = data.index[i]
        sim = cosine_similarity(new_data.loc[mubiao, :].fillna(0).values.reshape(1, -1), new_data.loc[bijiao,].fillna(0).values.reshape(1, -1))
        result = f"sim_{mubiao}{bijiao}"
        Result[result] = sim

yonhu_similar("A",data= data)

#print(Result)

#根据相似度大小排序(降序排)
sorted_Result = sorted(Result.items(),key= lambda x : x[1],reverse=True)
yonhu_sorted_Result = [i[0] for i in sorted_Result ]
print(sorted_Result)
print(yonhu_sorted_Result)
[('sim_AA', array([[1.]])), ('sim_AD', array([[0.56818182]])), ('sim_AB', array([[0.30772873]])), ('sim_AC', array([[-0.24618298]]))]
['sim_AA', 'sim_AD', 'sim_AB', 'sim_AC']
#可见用户A与用户D最相似(除自身外)



#预测A对two商品的评分,从而做出是否推荐的判断
A_two =(sim_AD*data.loc["D","two"] + sim_AB * data.loc["B",'two']) /(sim_AD + sim_AB)
print(A_two)
[[4.64867562]]

#预测A对six商品的评分,从而做出是否推荐的判断
A_six =(sim_AD*data.loc["D","six"] + sim_AB * data.loc["B",'six']) /(sim_AD + sim_AB)
print(A_six)
[[5.]]
#同理这里也可以写一个循环来遍历,然后将结果存于一个列表中

从结果可看出A用户关于未曾接触的物品two和six的评分都挺高的,也就说明可能适合向用户A推送这两件物品的信息。
(因为评分矩阵太小的缘故,结果得到评分的差异不是非常明显,但是基础的原理是差不多的,可以想象一下当物品与用户数都极多时,在这样大的数据运算下,系统基于相似用户推送给你的物品是从"千军万马“中筛选出来的评分较高的,这过滤掉了绝大多数物品)

二、基于物品的协同过滤

2.1. 原理

该算法基于物品之间的相似性来进行推荐。首先,计算物品之间的相似度,然后,根据用户历史行为中评价高的物品,为用户推荐与这些物品相似的物品

2.2. 算法步骤

  1. 准备用户的历史行为数据,包括用户对物品的评分、点击、购买等行为。通常使用评分矩阵,然后去中心化
  2. 根据用户的历史行为数据,计算物品之间的相似度(可以根据实际,选择合适的相似度计算方法,如余弦相似度)
  3. 计算得到目标用户对未曾接触过的物品的预测评分
  4. 根据与用户历史行为中评价高的物品相似的物品,为用户推荐未曾接触过的物品。

2.3. 代码实现

import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
data = pd.DataFrame({
    "u1":[2,None,1,None,3],
    "u2": [None,3,None,4,None],
    "u3": [4,None,5,4,None],
    "u4": [None,3,None,4,None],
    "u5": [5,None,4,None,5]
},index= ["S1","S2","S3","S4","S5"])
print(data)
     u1   u2   u3   u4   u5
S1  2.0  NaN  4.0  NaN  5.0
S2  NaN  3.0  NaN  3.0  NaN
S3  1.0  NaN  5.0  NaN  4.0
S4  NaN  4.0  4.0  4.0  NaN
S5  3.0  NaN  NaN  NaN  5.0
#index 为物品,columns 为用户

#目标: 预测u3 对 S5的评分:

#数据去中心化,消除用户之间的评分偏差,从而提高预测评分的准确性。
data_center = data.apply(lambda x : x- x.mean(),axis= 1)
#print(data_center)

## 计算物品S5与其它物品的相似度
sim = []
for i in range(len(data_center)):
    sim_li = cosine_similarity(np.nan_to_num(data_center.iloc[-1].values).reshape(1,-1),
                               np.nan_to_num(data_center.iloc[i].values).reshape(1,-1)
                               )
    sim.append(sim_li)

print(sim)
[array([[0.98198051]]), array([[0.]]), array([[0.72057669]]), array([[0.]]), array([[1.]])]


# 计算u3 对 S5的预测评分
u3_s5 = (sim[0]*data_center.loc["S1","u3"] + sim[2] * data_center.loc["S3","u3"]) / (sim[0]+sim[2])
print(u3_s5)
[[0.89764267]]

补充:
在协同过滤算法中,使用去中心化后的评分矩阵进行预测时,可以得到更准确的相似度和预测评分,从而提高推荐的准确性。
然而,去中心化操作也可能会导致一些评分变为负值,这可能需要在实际应用中进行处理。

三、比较与总结

  1. 目标不同:顾名思义,基于用户的协同过滤算法的目标是找到与目标用户兴趣相似的其他用户,并根据这些用户的行为来推荐物品。而基于物品的协同过滤算法的目标是找到与目标物品相似的其他物品,并根据这些物品的评分来推荐给用户。

  2. 在相似度计算时,基于物品的协同过滤算法要对用户-物品评分矩阵进行转置

  3. 推荐效果不同:前者在用户数较多时能为新用户提供更好的推荐效果,或者在物品数较多时能提供更好的效果。

四、实例解析

这是一个电影推荐实例

import pandas as pd
import numpy as np

# 读取用户数据
users = pd.read_csv(r"D:\movielens\users.dat", sep="::", header=None,
                    names=["user_id", "gender", "age", "occupation", "zip"], engine="python")
# 读取评分数据
ratings = pd.read_csv(r"D:\movielens\ratings.dat", sep="::", header=None,
                      names=["user_id", "movie_id", "rating", "timestamp"], engine="python")
# 读取电影数据
movies = pd.read_csv(r"D:\BaiduNetdiskDownload\movielens\movies.dat", sep="::", header=None,
                     names=["movie_id", "title", "genres"], engine="python", encoding="ISO-8859-1")

# 数据透视,得到一个评分矩阵
data = pd.pivot_table(ratings, index="user_id", columns="movie_id", values="rating")

print(data.info())
<class 'pandas.core.frame.DataFrame'>
Int64Index: 6040 entries, 1 to 6040
Columns: 3706 entries, 1 to 3952
dtypes: float64(3706)
memory usage: 170.8 MB
None


"""
# 假如为user_id 为 1 的用户推荐电影
"""

# 去中心化
new_data = data.apply(lambda x: x - x.mean(), axis=1)
# print(new_data.head())

# 计算每一个用户与user_id 为1的相似度
from sklearn.metrics.pairwise import cosine_similarity
sim_cos = []
for i in range(len(new_data)):
    sim = cosine_similarity(new_data.iloc[0].fillna(0).values.reshape(1, -1),
                            new_data.iloc[i].fillna(0).values.reshape(1, -1))
    sim_cos.append(sim)

# print(sim_cos)
sim_cos = [x[0][0] for x in sim_cos]
# print(sim_cos)
data = data.assign(sim = sim_cos)
# print(data)
data = data.sort_values(by= "sim",ascending=False)
# print(data)
data_top5 = data.iloc[1:6].copy()
print(data_top5)
movie_id    1   2   3   4   5   6  ...  3948  3949  3950  3951  3952       sim
user_id                            ...                                        
1337      NaN NaN NaN NaN NaN NaN  ...   NaN   NaN   NaN   NaN   NaN  0.189242
379       NaN NaN NaN NaN NaN NaN  ...   NaN   NaN   NaN   NaN   NaN  0.159893
5404      5.0 NaN NaN NaN NaN NaN  ...   NaN   NaN   NaN   NaN   NaN  0.155154
49        5.0 NaN NaN NaN NaN NaN  ...   NaN   NaN   NaN   NaN   NaN  0.148455
2607      NaN NaN NaN NaN NaN NaN  ...   NaN   NaN   NaN   NaN   NaN  0.148105

[5 rows x 3707 columns]


print("=========================")

#将这五位用户都没看过的电影删除
data_top5.dropna(how ="all",axis = 1,inplace = True )  
print(((~data_top5.iloc[:,0].isnull()).astype("int")) * data_top5.sim)    #将nan值设置为0,非nan值设置为1

#预测评分
have_pre = data_top5[data_top5.columns[:-1]].apply(lambda x: ((x*data_top5.sim).sum())/(((~x.isnull()).astype("int"))*(data_top5.sim)).sum())
#print(have_pre)
haven_seen = data.columns[~data.iloc[0,:].isnull()][:-1]  #得到用户1已经看过电影的ID
#print(haven_seen)

#得到用户一未看过的电影
pre_movie_id = set(have_pre.index) - set(haven_seen)
pre_movie = have_pre[have_pre.index.isin(pre_movie_id)]

#推荐前十个电影
recommend= pre_movie.sort_values(ascending= False)[:10] 
#print(recommend.index)
movies_top10 = movies[movies.movie_id.isin(recommend.index)]
print(movies_top10)
      movie_id                                title                  genres
911        923                  Citizen Kane (1941)                   Drama
920        932        Affair to Remember, An (1957)                 Romance
1040      1054                Get on the Bus (1996)                   Drama
1052      1066               Shall We Dance? (1937)  Comedy|Musical|Romance
1063      1079          Fish Called Wanda, A (1988)                  Comedy
1064      1080  Monty Python's Life of Brian (1979)                  Comedy
1068      1084              Bonnie and Clyde (1967)             Crime|Drama
1108      1124                On Golden Pond (1981)                   Drama
1899      1968           Breakfast Club, The (1985)            Comedy|Drama
1928      1997                 Exorcist, The (1973)                  Horror





#看一下该用户已经看过的电影的排名前十
have_see_love = data.sort_values(by= 1,axis= 1,ascending= False).columns[:10]
have_see_love_top10 = movies[movies.movie_id.isin(have_see_love)]
print(have_see_love_top10)
      movie_id  ...                                genres
0            1  ...           Animation|Children's|Comedy
47          48  ...  Animation|Children's|Musical|Romance
1009      1022  ...          Animation|Children's|Musical
1250      1270  ...                         Comedy|Sci-Fi
1768      1836  ...                                 Drama
1892      1961  ...                                 Drama
1959      2028  ...                      Action|Drama|War
2286      2355  ...           Animation|Children's|Comedy
2735      2804  ...                          Comedy|Drama
3036      3105  ...                                 Drama

[10 rows x 3 columns]



#可以发现推荐给用户的电影大多数是一些Drama和Comedy,这与该用户已看过的电影类型较为相似,(这个推荐系统应该是较为合理的)

总结

本文简单介绍了一下协同过滤算法:基于用户和基于物品的协同过滤算法,然后比较了一下这两个的区别,最后介绍了一个电影推荐实例解析。

故常无欲,以观其妙;常有欲,以观其徼

–2023-9-11 筑基篇

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

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

相关文章

清理 Ubuntu 系统的 4 个简单步骤

清理 Ubuntu 系统的 4 个简单步骤 现在&#xff0c;试试看这 4 个简单的步骤&#xff0c;来清理你的 Ubuntu 系统吧。 这份精简指南将告诉你如何清理 Ubuntu 系统以及如何释放一些磁盘空间。 如果你的 Ubuntu 系统已经运行了至少一年&#xff0c;尽管系统是最新的&#xff0c;…

2003-2022年黄河流域TCI、VCI、VHI、TVDI逐年1km分辨率数据集

摘要 黄河流域大部分属于干旱、半干旱气候,先天水资源条件不足,是中国各大流域中受干旱影响最为严重的流域。随着全球环境和气候变化,黄河流域的干旱愈加频繁,对黄河流域的干旱监测研究已经成为当下的热点。本数据集基于MODIS植被和地表温度产品,通过对逐年数据进行去云、…

Mendix使用Upload image新增修改账户头像

学习Mendix中级文档&#xff0c;其中有个管理我的账号功能&#xff0c;确保账号主任可以修改其头像&#xff0c;接下来记录如何实现账户头像的上传和修改。根据文档的步骤实现功能&#xff5e;&#xff5e; 新建GeneralExtentions模块&#xff0c;给GeneralExtentions添加两个模…

MapTR v2文章研读

MapTR v2论文来了&#xff0c;本文仅介绍v2相较于v1有什么改进之处&#xff0c;如果想了解v1版本的论文细节&#xff0c;可见链接。 相较于maptr&#xff0c;maptr v2改进之处&#xff1a; 在分层query机制中引进解耦自注意力机制&#xff0c;有效降低了内存消耗&#xff1b;…

Spring中如何解决循环依赖问题

一、什么是循环依赖 循环依赖也叫循环引用&#xff0c;是指bean之间形成相互依赖的关系&#xff0c;由此&#xff0c;bean对象在属性注入时便会产生循环。这种循环依赖会导致编译器无法编译代码&#xff0c;从而无法运行程序。为了避免循环依赖&#xff0c;我们在开发过程中需…

视频号视频下载工具有那些?我们怎么下载视频号里面的视频

本篇文章给大家谈谈视频号视频下载工具&#xff0c;以及视频号视频如何下载?对应的知识点&#xff0c;希望对各位有所帮助。 视频号里面的视频可以下载吗&#xff1f; 视频号官方首先是不提供下载功能的&#xff0c;但是很多第三方可以提供视频号的视频下载功能。 早期版本视…

【力扣每日一题】2023.9.12 课程表Ⅳ

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 今天是课程表系列题目的最后一题&#xff0c;因为我在题库里找不到课程表5了&#xff0c;所以今天的每日一题就是最后一个课程表了。 题…

小节5:Python列表list常用操作

1、对列表的基本认知&#xff1a; 列表list&#xff0c;是可变类型。比如&#xff0c;append()函数会直接改变列表本身&#xff0c;往列表里卖弄添加元素。所以&#xff0c;list_a list_a.append(123)就是错误的。如果想删除列表中的元素&#xff0c;可以用remove()函数&…

基于微信小程序的宠物寄养平台,附源码、数据库

1. 简介 本文正是基于微信小程序开发平台&#xff0c;针对宠物寄养的需求,本文设计出一个包含寄养家庭分类、寄养服务管理、宠物档案、交流论坛的微信小程序,以此帮助宠物寄养的实现,促进宠物寄养工作的进展。 2 开发技术 微信小程序的运行环境分为渲染层和逻辑层&#xff0…

仿照Everything实现的文件搜索工具--SearchEverything

一、项目介绍 项目名称&#xff1a;SearchEverything 项目简介&#xff1a;SearchEverything是仿照Everything实现的一款桌面级的文件搜索软件,它是Everything的增强版&#xff0c;支持跨平台的使用。 项目功能&#xff1a; 1.选择文件夹后&#xff0c;多线程扫描文件夹下的…

学会这个技能,写字楼立马高级起来!

在当今现代化社会中&#xff0c;写字楼已成为商业和行政活动的中心。成千上万的人们每天涌入这些高楼大厦&#xff0c;从事各种各样的工作&#xff0c;以实现公司和组织的目标。然而&#xff0c;与这种繁忙的办公环境一样&#xff0c;也带来了一系列的安全挑战和管理难题。 随着…

【大数据之Kafka】十一、Kafka消费者及消费者组案例

1 独立消费者案例&#xff08;订阅主题&#xff09; &#xff08;1&#xff09;需求&#xff1a;创建一个独立消费者&#xff0c;消费 first 主题中数据。 &#xff08;2&#xff09;分析&#xff1a; 注意&#xff1a;在消费者 API 代码中必须配置消费者组 id。命令行启动消…

算法通关村第13关【青铜】| 数字与数学基础问题

数字统计专题 1.数组元素积的符号 思路&#xff1a;每回碰到负数就取反 class Solution {public int arraySign(int[] nums) {int res nums[0];if(nums[0]>0){res 1;}else if(nums[0]<0){res -1;}else{return res;}for(int i 1;i<nums.length;i){if(nums[i]<…

Linux基本认识

一、Linux基本概念 Linux 内核最初只是由芬兰人林纳斯托瓦兹&#xff08;Linus Torvalds&#xff09;在赫尔辛基大学上学时出于个人爱好而编写的。 Linux 是一套免费使用和自由传播的类 Unix 操作系统&#xff0c;是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多…

地下管网实时水位监测用什么设备好?

地下排水管网是城市重要基础设施生命线之一&#xff0c;主要用于排放雨水、地表水和废水&#xff0c;以维护城市的安全运行。然而&#xff0c;在极端天气事件发生时&#xff0c;排水系统可能会面临压力巨大&#xff0c;导致排水不畅引发城市内涝。通过对管网水位实时监测&#…

Java集合大总结——Collection集合

Collection集合的整理 1、List&#xff0c;Set&#xff0c;Queue&#xff0c;Map四者的区别集合底层数据结构梳理2、关于集合的的选用2.1 为什么使用集合3、List接口3.1 ArrayList 和 Array&#xff08;数组&#xff09;的区别&#xff1f;3.1 LinkedList 为什么不能实现Random…

基于python+txt的学生成绩管理系统

基于pythontxt的学生成绩管理系统 一、系统介绍二、效果展示三、其他系统实现四、获取源码 一、系统介绍 录入学生信息查找学生信息删除学生信息修改学生信息排序统计学生信息显示所有学生信息 基于python的学生成绩管理系统&#xff0c;具备基本的增删改查功能&#xff0c;包…

2023-9-12 完全背包问题

题目链接&#xff1a;完全背包问题 初版(时间复杂度拉满) #include <iostream> #include <algorithm>using namespace std;const int N 1010;int n, m; int v[N], w[N]; int f[N][N];int main() {cin >> n >> m;for(int i 1; i < n; i ) cin >…

AntDB数据库参加ACDU中国行杭州站,分享数据库运维实践与经验

关于ACDU 和中国行: ACDU是由墨天轮社区举办的中国数据库联盟的品牌活动之一&#xff0c;在线下汇集数据库领域的行业知名人士&#xff0c;共同探讨数据库前沿技术及其应用&#xff0c;促进行业发展和创新的平台&#xff0c;也为开发者们提供友好交流的机会。 AntDB作为具有技术…

Kafka 基于 S3 的数据导出、导入、备份、还原、迁移方案

在系统升级或迁移时&#xff0c;用户常常需要将一个 Kafka 集群中的数据导出&#xff08;备份&#xff09;&#xff0c;然后在新集群或另一个集群中再将数据导入&#xff08;还原&#xff09;。通常&#xff0c;Kafka集群间的数据复制和同步多采用 Kafka MirrorMaker&#xff0…