推荐系统实践(一)----基于用户的协同过滤算法(UserCF)

2023-11-08

  随着信息技术和互联网的发展,人们逐渐从信息匮乏的时代走入了信息过载的时代。在这个时代,无论是信息消费者还是信息生产者都遇到了很大的挑战:如何从大量信息中找到自己感兴趣的信息是一件非常困难的事情,这个时候就需要推荐系统。推荐系统不需要用户提供明确的需求,而是通过分析用户的历史行为给用户的兴趣建模,从而主动给用户推荐能够满足他们兴趣和需求的信息。

  基于邻域的算法是推荐系统中最基本的算法,该算法不仅在学术界得到了深入研究,而且在业界得到了广泛应用。基于邻域的算法分为两大类,一类是基于用户的协同过滤算法,另一类是基于物品的协同过滤算法。这里我们主要讲解基于用户的协同过滤算法。

  • 原理
      俗话说“物以类聚、人以群分”,拿漫威英雄来说,如果 A A A喜欢钢铁侠、美国队长、死侍等, B B B 也都喜欢这些人物形象,但他还喜欢蜘蛛侠,那么很有可能 A A A 也喜欢蜘蛛侠这个人物。
      所以说,当一个用户 A A A 需要个性化推荐时,可以先找到和他兴趣相似的用户群体 B B B,然后把 B B B 喜欢的而 A A A 没有听说过的物品推荐给 A A A,这就是基于用户的系统过滤算法。

  • 流程

  1. 找到与目标用户相似的用户集合
  2. 找到这个集合中用户喜欢的且目标用户没有听说过的物品推荐给目标用户
  • 用户相似度计算

  协同过滤算法主要利用行为的相似度计算兴趣的相似度。给定用户 u u u 和用户 v v v ,令 N ( u ) N(u) N(u) 表示用户 u u u 感兴趣的物品集合,令 N ( v ) N(v) N(v) 为用户 v v v 感兴趣的物品集合。那么我们可以通过 J a c c a r d Jaccard Jaccard 公式或者余弦公式来计算用户 u u u, v v v 的相似程度:

       J a c c a r d Jaccard Jaccard公式:
w u v = ∣ N ( u ) ∩ N ( v ) ∣ ∣ N ( u ) ∪ N ( v ) ∣ {w_{uv}} = \frac{{|N(u) \cap N(v)|}}{{|N(u) \cup N(v)|}} wuv=N(u)N(v)N(u)N(v)
      余弦相似度公式:
w u v = ∣ N ( u ) ∩ N ( v ) ∣ ∣ N ( u ) ∣ ∣ N ( v ) ∣ {w_{uv}} = \frac{{|N(u) \cap N(v)|}}{{\sqrt {|N(u)||N(v)|} }} wuv=N(u)N(v) N(u)N(v)
   假设目前共有4个用户: A A A B B B C C C D D D;共有5个漫威英雄人物:死侍、钢铁侠、美国队长、黑豹、蜘蛛侠。用户与人物之间的爱好程度如下图所示:

用户 喜爱英雄人物
A 死侍、钢铁侠、美国队长
B 死侍、黑豹
C 钢铁侠、蜘蛛侠
D 蜘蛛侠、黑豹、美国队长

   存储的数据格式为:

   每列分别代表用户、英雄人物、评分。首先将处理数据集为我们需要的格式:
def load_data(filePath):   
    dataSet = {}
    with open(filePath, "r", encoding="utf-8") as  f:
    	for line in f:
       	    user, hero, rating = line.strip().split(",")
            dataSet .setdefault(user, {})
       	    dataSet [user][hero] = rating

    return dataSet 

   得到数据集格式如下:

	{'A': {'死侍': '1', '钢铁侠': '1', '美国队长': '1'}, 'B': {'死侍': '1', '黑豹': '1'}, 'C': {'钢铁侠': '1', '蜘蛛侠': '1'}, 'D': {'蜘蛛侠': '1', '黑豹': '1', '美国队长': '1'}}

   数据处理好之后,需要建立物品到用户的倒排表,对于每个物品都保存对该物品产生过行为的用户列表。倒排表为喜欢每个物品对应的用户,如下所示:

喜爱英雄人物 用户
死侍 A、B
钢铁侠 A、C
美国队长 A、D
黑豹 B、D
蜘蛛侠 C、D

   实现该功能的python方法:

	def calc_user_sim(dataSet):  # 建立物品-用户的倒排列表
	    item_users = dict()
	    for user, items in dataSet.items():
	        for movie in items:
	            if movie not in item_users:
	                item_users[movie] = set()
	            item_users[movie].add(user)
	      
	    print("物品-用户倒排列表: ", item_users)
	     
	 	return item_users

   结果如下:

	物品-用户倒排列表:  {'钢铁侠': {'C', 'A'}, '死侍': {'B', 'A'}, '美国队长': {'D', 'A'}, '黑豹': {'B', 'D'}, '蜘蛛侠': {'D', 'C'}}

   假设用户 A A A 和用户 B B B 同时属于倒排表中 K K K 个人物对应的用户列表,就有 C [ A ] [ B ] = K C[A][B]=K C[A][B]=K。从而,可以扫描倒排表中每个物品对应的用户列表,将用户列表中的两两用户对应的 C [ A ] [ B ] C[A][B] C[A][B] 加1,最终就可以得到所有用户之间不为0的稀疏矩阵 C [ A ] [ B ] C[A][B] C[A][B]:

A B C D
A 1 1 1
B 1 1
C 1 1
D 1 1 1

   建立好稀疏矩阵之后,对于人物死侍,将 W [ A ] [ B ] W[A][B] W[A][B] W [ B ] [ A ] W[B][A] W[B][A] 加1,对于钢铁侠,将 W [ A ] [ C ] W[A][C] W[A][C] W [ C ] [ A ] W[C][A] W[C][A] 加1,以此类推,扫描完所有人物后,我们可以得到最终的 W W W 矩阵,这里的 W W W 是余弦相似度中的分子部分,然后将 W W W 除以分母可以得到最终的用户兴趣相似度:

	def user_similarity(userSet):
	    C = dict()
	    N = dict()
	    for movie, users in userSet.items():
	        for u in users:
	            N.setdefault(u, 0)
	            N[u] += 1  # 每个商品下用户出现一次就加一次,就是计算每个用户一共购买的商品个数。
	            for v in users:
	                if u == v:
	                    continue
	                C.setdefault(u, {})
	                C[u].setdefault(v, 0)
	                C[u][v] += 1
	               
	    print("稀疏矩阵: ", C)
	    W = dict()
	    for u, related_users in C.items():
	        for v, cuv in related_users.items():
	            W.setdefault(u, {})
	            W[u].setdefault(v, 0)
	            W[u][v] = cuv / math.sqrt(N[u] * N[v])
	
	    print("用户相似度: ", W)
	    return W

   实现结果为:

稀疏矩阵:  {'C': {'A': 1, 'D': 1}, 'A': {'C': 1, 'B': 1, 'D': 1}, 'B': {'A': 1, 'D': 1}, 'D': {'A': 1, 'B': 1, 'C': 1}}
	
用户相似度:  {'C': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'A': {'C': 0.4082482904638631, 'B': 0.4082482904638631, 'D': 0.3333333333333333}, 'B': {'A': 0.4082482904638631, 'D': 0.4082482904638631}, 'D': {'A': 0.3333333333333333, 'B': 0.4082482904638631, 'C': 0.4082482904638631}}
A B C D
A 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1 1 3 * 3 \frac{1}{{\sqrt 3 {\text{*}}\sqrt 3 }} 3 *3 1
B 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1
C 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1
D 1 3 * 3 \frac{1}{{\sqrt 3 {\text{*}}\sqrt 3 }} 3 *3 1 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1 1 2 * 3 \frac{1}{{\sqrt 2 {\text{*}}\sqrt 3 }} 2 *3 1
  • 用户相似度改进
       如果两个用户都喜欢同一个物品,但这不能说明他们兴趣一定相似,比如我们小学的时候基本买过《新华字典》,但是是我们都对这个感兴趣。但如果两个用户都买过《python数据分析与挖掘实战》,那可以认为他们的兴趣比较相似,因为只有研究数据挖掘的人才会买这本书。所以换句话说,两个用户对冷门物品采取过同样的行为更能说明他们兴趣的相似度。因此又提出了如下公式,根据用户行为计算用户的兴趣相似度:
    w u v = ∑ i ∈ N ( u ) ∩ N ( v ) 1 log ⁡ 1 + ∣ N ( i ) ∣ ∣ N ( u ) ∣ ∣ N ( v ) ∣ {w_{uv}} = \frac{{\sum {i \in N(u) \cap N(v)} \frac{1}{{\log 1 + |N(i)|}}}}{{\sqrt {|N(u)||N(v)|} }} wuv=N(u)N(v) iN(u)N(v)log1+N(i)1
       分子中的倒数惩罚了用户u和用户v共同兴趣列表中热门物品对他们相似度的影响。N(i)是对物品i有过行为的用户集合,越热门,N(i)越大
	def user_similarity_update(userSet):
	    C = dict()
	    N = dict()
	    for movie, users in userSet.items():
	        for u in users:
	            N.setdefault(u, 0)
	            N[u] += 1
	            for v in users:
	                if u == v:
	                    continue
	                C.setdefault(u, {})
	                C[u].setdefault(v, 0)
	                C[u][v] += 1 / math.log(1 + len(users))
	               
	    print("稀疏矩阵: ", C)
	    W = dict()
	    for u, related_users in C.items():
	        for v, cuv in related_users.items():
	            W.setdefault(u, {})
	            W[u].setdefault(v, 0)
	            W[u][v] = cuv / math.sqrt(N[u] * N[v])
	
	    print("用户相似度: ", W)
	    return W

   实现结果为:

稀疏矩阵:  {'C': {'A': 0.9102392266268373, 'D': 0.9102392266268373}, 'A': {'C': 0.9102392266268373, 'B': 0.9102392266268373, 'D': 0.9102392266268373}, 'B': {'A': 0.9102392266268373, 'D': 0.9102392266268373}, 'D': {'A': 0.9102392266268373, 'B': 0.9102392266268373, 'C': 0.9102392266268373}}
用户相似度:  {'C': {'A': 0.37160360818355515, 'D': 0.37160360818355515}, 'A': {'C': 0.37160360818355515, 'B': 0.37160360818355515, 'D': 0.3034130755422791}, 'B': {'A': 0.37160360818355515, 'D': 0.37160360818355515}, 'D': {'A': 0.3034130755422791, 'B': 0.37160360818355515, 'C': 0.37160360818355515}}
  • 推荐

p ( u , i ) = ∑ v ∈ S ( u , K ) ∩ N ( i ) w u v r v i p(u,i) = \sum\limits_{v \in S(u,K) \cap N(i)} {{w_{uv}}{r_{vi}}} p(u,i)=vS(u,K)N(i)wuvrvi

   由上面步骤得到用户的兴趣相似度后, U s e r C F UserCF UserCF 算法会给用户推荐和他兴趣最相似的 K K K 个用户喜欢的物品。上面公式度量了 U s e r C F UserCF UserCF 算法中用户 u u u 对物品 i i i 的感兴趣程度:其中, S ( u , K ) S(u, K) S(u,K) 包含和用户 u u u 兴趣最接近的 K K K 个用户, N ( i ) N(i) N(i) 是对物品 i i i 有过行为的用户集合, w u v {w_{uv}} wuv 是用户 u u u 和用户 v v v 的兴趣相似度, r v i {r_{vi}} rvi 代表用户 v v v 对物品 i i i 的兴趣,因为使用的是单一行为的隐反馈数据,所以所有的 R v i = 1 Rvi=1 Rvi=1

	def recommend(user, train, W, K):
	    rvi = 1
	    rank = dict()
	    related_user=[]
	    interacted_items = train[user]
	
	    for co_user, item in W.items():
	        if co_user == user:
	            for user, score in item.items():
	                related_user.append((user, score))
	            break
	           
	    print("与user用户相似度: ", related_user)
	    for v, wuv in sorted(related_user, key=itemgetter(1), reverse=True)[0:K]:
	        for i in train[v]:
	            if i in interacted_items:
	                continue
	            if i not in rank.keys():
	                rank[i]=0
	            rank[i] += wuv * rvi
	           
	    print(rank)
	    return rank

   实现结果为:

与user用户相似度:  [('C', 0.4082482904638631), ('B', 0.4082482904638631), ('D', 0.3333333333333333)]
{'蜘蛛侠': 0.4082482904638631, '黑豹': 0.4082482904638631}

   选取 K = 2 K=2 K=2,与用户 A A A最相似的两个用户为 B B B C C C,可以看出相对这两个用户, A A A对蜘蛛侠、黑豹没有过行为,因此可以把这两个人物推荐给用户 A A A。根据 U s e r C F UserCF UserCF 算法,用户 A A A 对蜘蛛侠、黑豹的兴趣是:

p ( A , 蜘 蛛 侠 ) = w A C = 0.4082482904638631 p(A,蜘蛛侠) = {{\text{w}}_{AC}} = 0.4082482904638631 p(A,)=wAC=0.4082482904638631
p ( A , 黑 豹 ) = w A B = 0.4082482904638631 p(A,黑豹) = {{\text{w}}_{AB}} = 0.4082482904638631 p(A,)=wAB=0.4082482904638631

  • 代码分析
    基于用户的协同过滤算法(UserCF)
  • 总结
       但是基于用户的协同过滤算法有一些缺点: 随着用户数目越来越大,计算用户兴趣相似度矩阵将越来越困难,其运算时间复杂度和空间复杂度的增长和用户数的增长非常快; 时效性较差。用户有新行为,推荐结果很难立即变化,所以针对这些,又提出了另一种基于物品的协同过滤算法。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

推荐系统实践(一)----基于用户的协同过滤算法(UserCF) 的相关文章

随机推荐

  • How to Control Power Switch Rush Current

    原文链接 https community cadence com cadence blogs 8 b lp posts how to control power switch rush current While there are mul
  • mysql列转行

    原表select from test table 列转行select sheng substring index substring index b shi a help topic id 1 1 as shi from mysql hel
  • C#怎么测试静态方法?我给出了2种方案

    问题 假设有一个方法需要判断当前小时范围 代码如下 public class Class1 public bool SomeMethod var hour DateTime Now Hour if hour gt 9 hour lt 12
  • 遥感影像深度学习样本对制作教程3——从GEE下载训练数据

    关注公众号GeodataAnalysis 回复20230505获取示例数据和代码 这三章的代码都放在一起 上手运行一下代码更容易弄懂 遥感数据多种多样 存储格式各异 处理起来很麻烦 比如很多MODIS数据都是采用HDF格式存储的 在制作深度
  • 模板编程:模板完全特例化

    模板有类模板和函数模板 类模板存在偏特例化 和完全特例化 类模板 类模板完全特例化 类模板偏特化 函数模板只有完全特例化 函数模板完全特特化重点 需要在特例化版本前面加template lt gt 告诉编译器 这个函数是对模板进行特例化 特
  • stm32F1的 PA13/PA14/PA15/PB3/PB4 作为普通引脚使用

    代码链接 https blog csdn net Mark md article details 107411081
  • AS3 通过方法名称 进行调用

    package public class ObjectBinder public var targetInstance public function ObjectBinder targetInstance this targetInsta
  • 运维常用工具

    操作系统 Centos Ubuntu Redhat suse Freebsd 网站服务 nginx apache lighttpd php tomcat resin 数据 库 MySQL MariaDB PostgreSQL DB中间件 m
  • Android Handler被弃用,那么以后怎么使用Handler,或者类似的功能

    Android API30左右 Android应用在使用传统写法使用Handler类的时候会显示删除线 并提示相关的方法已经被弃用 不建议使用 Handler handler new Handler Override public void
  • PyQt5学习记录----案例1实践

    案例1 创建多个用于信息提示的QLabel 要求 1 凡是提示的QLabel控件 都需设置 字体大小 25px 字体颜色 灰色 边框圆角 8px 2 信息提示分多个级别 正常 normal 绿色边框及字体 警告 warning 黄色边框及字
  • 前端技术搭建井字游戏(内含源码)

    The sand accumulates to form a pagoda 写在前面 功能介绍 页面搭建 样式设置 逻辑部分 写在前面 上周我们实通过前端基础实现了飞机大战游戏 今天还是继续按照我们原定的节奏来带领大家完成一个井字游戏游戏
  • QT vector转QVector(来自stackflow)

    std vector
  • el-popover弹出框更新位置刷新定位

    问题背景 当使用el popover弹框提示 又需要同时加载数据时 就会出现位置错位的现象 原因不言而喻 插件先计算了el popover的位置 然后加载数据又改变了el popover的宽高 如下 问题解决 模拟触发元素被点击 this
  • Python接口自动化测试之pytest:(一)参数化执行

    Pytest是非常流行的Python测试框架 适用于单元测试 UI测试 接口测试 一 一个简单的Pytest测试例子 Pytest可以在命令行模式下直接使用命令执行测试脚本 执行Pytest命令后将会自动匹配到以test开头或结尾的文件 并
  • python入门教程之爬虫的概述及简单实践练习

    文章目录 一 我们先了解用户获取网络数据的方式 二 简单了解网页源代码的组成 1 web基本的编程语言 2 使用浏览器查看网页源代码 三 爬虫概述 1 认识爬虫 2 python爬虫 3 爬虫分类 4 爬虫应用 5 爬虫是一把双刃剑 6 p
  • arcgis10.2 SDE (sqlserver)安装及使用

    1 下载 链接地址 http pan baidu com s 1DsMEe 提取密码 3in6 2 安装 3 数据库的安装与配置 数据库的安装不赘述 需要说明白的是 如果是安装多了多个数据库 数据库实例的名字一般是 主机名 实例名 可以通过
  • 与服务器的连接以获取信息,怎么从服务器获取数据库连接

    怎么从服务器获取数据库连接 内容精选 换一换 创建弹性云服务器 请参见 弹性云服务器用户指南 该弹性云服务器用于连接云数据库RDS实例 需要与目标实例处于同一虚拟私有云内 正确配置安全组 使得弹性云服务器可以通过 连接地址 访问云数据库RD
  • dga 分析

    02n 0iy6gn3ozzwmyu 7i43n9qil1g1z2 com0e527eaf 5ec5 4623 9fe9 e459583acd72 com0fmgm1cuu7h1279dghgka0ltg com0gqo9jx0ir0rjy
  • javascript 贪心算法说明

    贪心算法 贪心算法遵循一种近似解决问题的技术 期盼通过每个阶段的局部最优选择 当前最好的解 从而达到全局的最优 全局最优解 最少硬币找零问题 最少硬币找零是给出要找零的钱数 以及可以用硬币的额度数量 找出有多少种找零方法 如 美国面额硬币有
  • 推荐系统实践(一)----基于用户的协同过滤算法(UserCF)

    随着信息技术和互联网的发展 人们逐渐从信息匮乏的时代走入了信息过载的时代 在这个时代 无论是信息消费者还是信息生产者都遇到了很大的挑战 如何从大量信息中找到自己感兴趣的信息是一件非常困难的事情 这个时候就需要推荐系统 推荐系统不需要用户提供