基于Item的协同过滤算法实践(最简单的在线电影推荐系统)

2023-11-20

上一篇文章《基于用户的协同过滤算法实践》中,基于用户的相似度生成推荐列表,本文将基于Item的相似度阐述。

 

1 相似度

基于物品的协同过滤算法(简称ItemCF)给用户推荐那些和他们之前喜欢的物品相似的物品。不过ItemCF不是利用物品的内容计算物品之间相似度,而是利用用户的行为记录。

该算法认为,物品A和物品B具有很大的相似度是因为喜欢物品A的用户大都也喜欢物品B。这里蕴含一个假设,就是每个用户的兴趣都局限在某几个方面,因此如果两个物品属于同一个用户的兴趣列表,那么这两个物品可能就属于有限的几个领域。而如果两个物品同时出现在很多用户的兴趣列表,那么它们可能就属于同一领域,因而具有很大的相似度。

从上述概念出发,定义物品i和j的相似度为

物品相似度公式
 
其中,|N(i)||N(i)|是喜欢物品i的用户数,|N(i)⋂N(j)||N(i)⋂N(j)|是同时喜欢物品i和物品j的用户数。分母是惩罚物品i和j的权重,因此惩罚了热门物品和很多物品相似的可能性。

在ItemCF中,两个物品产生相似度是因为它们共同出现在很多用户的兴趣列表中。假设有这么一个用户,他是开书店的,并且买了当当网上 80% 的书准备用来自己卖。那么,
他的购物车里包含当当网 80% 的书。所以这个用户对于他所购买书的两两相似度的贡献应该远远小于一个只买了十几本自己喜欢的书的文学青年。
提出一个称为 IUF ( Inverse User Frequence ),即用户活跃度对数的倒数的参数,来修正物品相似度的计算公式。认为活跃用户对物品相似度的贡献应该小于不活跃的用户。

 

IUF

 

2 兴趣程度

在得到物品相似度之后,ItemCF通过以下公式计算用户u对未产生行为的物品j的感兴趣程度。

推荐程度计算公式
 
这里的N(u)N(u)是用户喜欢的物品集合,S(j,K)S(j,K)是和物品j最相似的K个物品的集合,wijwij是物品j和i的相似度,ruirui是用户u对物品j的兴趣评分(简单的,如果用户u对物品i有过行为,即可令ruirui=1)

对于已经得到的物品相似度矩阵w,按照以下公式对w进行按列归一化,不仅可以增加推荐的准确度,它还可以提高推荐的覆盖率和多样性。

归一化
 
假设物品分为两类—— A 和 B , A 类物品之间的相似度为 0.5 , B 类物品之间的相似度为 0.6 ,而 A 类物品和 B 类物品之间的相似度是 0.2 。在这种情况下,如果一个用户喜欢了 5个 A 类物品和 5 个 B 类物品,用 ItemCF 给他进行推荐,推荐的就都是 B 类物品,因为 B 类物品之间的相似度大。但如果归一化之后, A 类物品之间的相似度变成了 1 , B 类物品之间的相似度也是 1 ,那么这种情况下,用户如果喜欢 5 个 A 类物品和 5 个 B类物品,那么他的推荐列表中 A 类物品和 B 类物品的数目也应该是大致相等的。从这个例子可以看出,相似度的归一化可以提高推荐的多样性。
一般来说,热门的类其类内物品相似度一般比较大。如果不进行归一化,就会推荐比较热门的类里面的物品,而这些物品也是比较热门的。因此,推荐的覆盖率就比较低。相反,如果进行相似度的归一化,则可以提高推荐系统的覆盖率。

 

3 电影推荐代码

1 生成用户数据集: 用户, 兴趣程度, 物品

{用户i: {物品1:兴趣程度, 物品2:兴趣程度, 物品3:兴趣程度}}

 

2 物品被多少个用户购买

{物品1: 2, 物品2: 5, 物品3: 1}

 

物品--物品共现矩阵

{物品1 :{物品2: 2, 物品3: 1},

物品2: {物品1: 2, 物品3: 2, 物品4: 1}

......

}

 

3 物品相似度矩阵

物品1 : {物品2: (物品1 和 物品2 共现次数 ) / sqrt(物品1出现的次数 * 物品2出现的次数) }

 

代码说明:

代码中的文件u.data参考上一篇文章。

import math
from texttable import Texttable  
import importlib
import sys

class ItemBasedCF: 
    def __init__(self, train_file):
        self.train_file = train_file
        self.readData()

    """
    读取数据,处理成字典格式
    生成用户数据集: 用户,  兴趣程度, 物品
    {用户i: {物品1:兴趣程度, 物品2:兴趣程度, 物品3:兴趣程度}}
    """
    def readData(self):
        self.train = dict()
        #print('train_file:', self.train_file)
        for line in self.train_file:  
            #print('type(line):', type(line))
            user, score, item = line.strip().split(",")
            self.train.setdefault(user, {})
            self.train[user][item] = int(float(score))
    
    """
    N: {物品1: 2,  物品2:  5,  物品3: 1}
    C: {
        物品1 : {物品2: 2,  物品3: 1},  
        物品2 : {物品1: 2,  物品3: 2,  物品4: 1}
        }
    """
    def ItemSimilarity(self): 
        N = dict()  #物品被多少个不同用户购买
        C = dict()  #物品和物品的共现矩阵
        for user, items in self.train.items():
            #print('user:', user)
            #print('item:', items)
            for ikey in items.keys():  #ikey: {'a': 1, 'b': 1, 'd': 1}
                #print('ikey:', ikey)
                N.setdefault(ikey, 0)
                N[ikey] += 1
                
                C.setdefault(ikey, {})
                for jkey in items.keys(): #物品和物品的共现矩阵 jkey: {'a': 1, 'b': 1, 'd': 1}
                    if ikey==jkey:
                        continue
                    #print('jkey:', jkey)
                    C[ikey].setdefault(jkey, 0)
                    #C[ikey][jkey] += 1
                    C[ikey][jkey] += 1 / math.log( 1+len(items)*1.0 ) #用用户活跃度来修正相似度,len(items)来衡量用户活跃度
                      
        #print('N:', N)
        #print('C:', C)
        
        #根据N,C计算物品之间的相似度
        #根据C得到物品和物品共现的次数,根据N得到物品分别出现的次数
        self.W = dict()
        self.W_max = dict() #记录每一列的最大值
        for ikey, relateij in C.items():
            #print('ikey:', ikey)
            #print('relateij:', relateij)
            self.W.setdefault(ikey, {})
            for jkey, Wij in relateij.items():
                self.W_max.setdefault(jkey, 0.0) #初始化当列最大值为0
                self.W[ikey][jkey] = Wij / ( math.sqrt(N[ikey] * N[jkey]) ) #计算相似度    
                if self.W[ikey][jkey] > self.W_max[jkey]:
                    self.W_max[jkey] = self.W[ikey][jkey] #更新列中最大值
                    #print('jkey:', jkey, 'self.W_max:', self.W_max[jkey])
                    
        #归一化处理, Wij / Wij_max
        for ikey, relateij in C.items():
            for jkey, Wij in relateij.items():
                self.W[ikey][jkey] = self.W[ikey][jkey] / self.W_max[jkey]
            
                    
        #for k, v in self.W.items():
            #print(k + ':' + str(v))
            
    """
    函数功能:生成推荐列表
    函数参数:
        @user:需要推荐的用户
        @K   :取物品相似度矩阵前k物品
        @N   : 最多推荐N个物品
    """
    def Recommend(self, user, K=3, N=10):
        rank = dict() #推荐字典
        action_item = self.train[user] #获取用户user的物品和评分数据
        
        for item, score in action_item.items(): #item为用户购买的物品,score为评分   
            for j, Wj in sorted(self.W[item].items(), key=lambda x:x[1], reverse = True)[0:K]: #self.W[item].items()为物品item的相似度矩阵
                if j in action_item.keys():  #用户已经购买过的物品,不再推荐
                    continue
                rank.setdefault(j, 0)
                rank[j] += (Wj * score) #j为根据相似度推荐的物品,Wj为推荐的物品和用户的物品item的相似度,score为item的评分
        return sorted(rank.items(), key=lambda x:[1], reverse = True)[0:N]    
            
def readFile(filename):  
    contents = []  
    f = open(filename, "rb")  
    contents = f.readlines()  
    f.close()  
    return contents 

def getRatingInfo(ratings):  
    rates = [] 
    for line in ratings:  
        rate = line.split("\t".encode(encoding="utf-8"))  
        rate_str = str(int(rate[0])) +','+ str(int(rate[2])) +',' + str(int(rate[1])) 
        rates.append(rate_str)  
    return rates 

#获取电影的列表  
def getMovieList(filename):  
    contents = readFile(filename)  
    movies_info = {}  #dict
    for movie in contents:  
        single_info = movie.split("|".encode(encoding="utf-8"))  #把当前行按|分隔,保存成list
        movies_info[int(single_info[0])] = single_info[1:]  #将第0个元素作为key,第二个到最后作为value保存成字典dict返回
    return movies_info  

#uid_score_bid = ['A,1,a','A,1,b','A,1,d','B,1,b','B,1,c','B,1,e','C,1,c','C,1,d','D,1,b','D,1,c','D,1,d','E,1,a','E,1,d']

#获取所有电影的列表  
if __name__ == '__main__':  
  
    importlib.reload(sys) 
    movies = getMovieList("u.item")  #movies为dict
    #print('movies:', movies)

    contents = []
    contents = readFile("u.data")
    rates = getRatingInfo(contents)

    Item = ItemBasedCF(rates)
    #print('train:', Item.train)

    Item.ItemSimilarity()
    recommend_dict = Item.Recommend('50', 5, 20) #k=5, N=20

    print('推荐列表:')
    recommend_list = []
    for k, v in recommend_dict:
        print(k + ':' + str(v))
        recommend_list.append(int(k))

    print(recommend_list)

    table = Texttable()  
    table.set_deco(Texttable.HEADER)  
    table.set_cols_dtype(['t', 't', 't'])  
    table.set_cols_align(["l", "l", "l"])  
    rows=[]  
    rows.append([u"movie name",u"release", u"from userid"])  
    for movie_id in recommend_list[:20]:  
        rows.append([movies[movie_id][0],movies[movie_id][1]," "])  
    table.add_rows(rows)  
    print (table.draw())  

运行结果:

 movie name                     release      from userid
=============================================================
Smilla's Sense of Snow (1997)       14-Mar-1997              
Private Parts (1997)                07-Mar-1997              
Trees Lounge (1996)                 11-Oct-1996              
Welcome to the Dollhouse (1995)     24-May-1996              
Kansas City (1996)                  16-Aug-1996              
City Hall (1996)                    16-Feb-1996              
Chamber, The (1996)                 11-Oct-1996              
Arrival, The (1996)                 31-May-1996              
Kama Sutra: A Tale of Love (1996)   07-Mar-1997              
Waiting for Guffman (1996)          31-Jan-1997              
Daytrippers, The (1996)             21-Mar-1997              
Kissed (1996)                       18-Apr-1997              
City of Lost Children, The (1995)   01-Jan-1995              
Swingers (1996)                     18-Oct-1996              
Mis�rables, Les (1995)              01-Jan-1995              
Georgia (1995)                      01-Jan-1995              
Cry, the Beloved Country (1995)     01-Jan-1995              
Street Fighter (1994)               01-Jan-1994              
Scarlet Letter, The (1995)          01-Jan-1995              
Kolya (1996)                        24-Jan-1997  

 

 

参考文章列表:

《推荐系统实践》——基于物品的协同过滤算法(代码实现)

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Item的协同过滤算法实践(最简单的在线电影推荐系统) 的相关文章

  • 正弦函数的频谱图(FFT)

    从理论上讲 正弦函数的傅里叶变换是冲击函数 它的幅值为原正弦信号幅值的1 2倍 即 若x t Acos t 则其频谱幅值最大值为A 2 如左图 但是 我们用matlab求出来的频谱图却不是左边这样的 而是右图 原因是 1 理论中的正弦信号是
  • 分布式时序相似查询初探

    时序数据 即随时间变化的数据 在人们的日常生活中无处不在 过去的近十年来 随着电子监控和智能穿戴等设备的普及 更是产生了海量的时序数据 例如 经过多年的发展 火力发电行业的数字化程度已经达到了很高的水平 以一台60万千瓦的中型火电机组为例

随机推荐

  • c#复习题(简答题:基础+面向对象)

    1 简答题 填写程序 要求如下 输入三角形三条边 先判断是否可以构成三角形 如果可以 则求三角形的周长和面积 否则报错 提示 输入的三个数构成三角形三边的条件 每条边长均大于0 并且任意两边之和大于第三边 代码编写 static void
  • 从zookeeper官方文档系统学习zookeeper

    从zookeeper官方文档系统学习zookeeper 1 zookeeper 2 zookeeper 文档 3 zookeeper 单机版 3 1 配置 3 2 启动 3 3 验证 4 zookeeper 集群版 4 1 配置 4 2 启
  • centos7 Jumpserver堡垒机部署以及使用详情

    一 跳板机 堡垒机的概念 1 跳板机 跳板机就是一台服务器 运维人员在使用管理服务器的时候 必须先连接上跳板机 然后才能去操控内网中的服务器 才能登录到目标设备上进行维护和操作 跳板机的缺点 仅仅实现服务器登录安全 但是没有实现对于运维人员
  • ChatGPT-Sorry, you have been blocked 解决办法

    使用无痕模式登录 已谷歌浏览器举例 右上点打开性的无痕模式 再访问地址 如下 可以继续登录了
  • Hadoop生态概述及常见报错

    Hadoop Hadoop是一个开源框架来存储和处理大型数据在分布式环境中 它包含两个模块 一个是MapReduce 另外一个是Hadoop分布式文件系统 HDFS MapReduce 它是一种并行编程模型在大型集群普通硬件可用于处理大型结
  • 亲测有效win10系统QQ音乐无法安装

    本人使用win10专业版时 因为一些原因 多次下载qq音乐都无法进入安装界面 找了很多原因都无法解决 最后在网上找到好像是因为火绒的原因 于是关闭火绒 再次尝试安装 遂成功 实在不行可以卸载火绒安全后重启电脑 再从官网重新下载QQ音乐安装包
  • 数据预处理-独热编码(One-Hot)

    1 部分特征如人的性别有男女 国家有中国 美国 法国等 并不是连续的 而是离散的 无序的 通常我们需要对其进行特征数字化 2 假如某个样本 某个人 他的特征是这样的 男 中国 乒乓球 我们可以用 0 0 4 来表示 但是这样的特征处理并不能
  • BeautifulSoup基本用法总结

    BeautifulSoup是Python的一个库 最主要的功能就是从网页爬取我们需要的数据 BeautifulSoup将html解析为对象进行处理 全部页面转变为字典或者数组 相对于正则表达式的方式 可以大大简化处理过程 0x01 安装 建
  • Leetcode 106. 从中序与后序遍历序列构造二叉树

    文章目录 题目 代码 9 18 首刷自解 题目 Leetcode 106 从中序与后序遍历序列构造二叉树 代码 9 18 首刷自解 class Solution public unordered map
  • UE4文字显示乱码“字字字字字字字字”的解决办法

    键盘win R 搜索fonts 2 滑到最底下右键复制 宋体常规简体字 3 复制到ue4项目的字体文件夹中 如下 注意在外部文件处复制 4 回到项目界面 此时右下角会有个弹窗提示是否确认导入 点击导入 然后会弹一个 字体样式导入选项 弹框
  • openGauss学习笔记-63 openGauss 数据库管理-资源池化架构

    文章目录 openGauss学习笔记 63 openGauss 数据库管理 资源池化架构 openGauss学习笔记 63 openGauss 数据库管理 资源池化架构 本文档主要介绍资源池化架构下的一些最佳实践和使用注意事项 用于支撑对相
  • go Cobra命令行工具入门

    简介 Github https github com spf13 cobra Star 26 5K Cobra是一个用Go语言实现的命令行工具 并且现在正在被很多项目使用 例如 Kubernetes Hugo和Github CLI等 通过使
  • Failed to set locale, defaulting to C.UTF-8解决方法

    CentOS 8Linux系统提示 Failed to set locale defaulting to C UTF 8 这是由于没有配置正确的语言环境导致的 Linux百科 使用root账户登录你的CentOS操作系统 然后执行两条命令
  • 现阶段计算机网络技术专业人才培养的发展对策

    确立具有高职特色的人才培养目标 在市场经济的条件下 人才培养首先要适应市场需求 以市场行业的需求为导 向制定人才培养目标 学校人才培养是否能满足社会需求 可以通过学生在对口行 业及相关领域的就业情况来衡量 高职教育培养高技能应用型人才 与研
  • Objective-C中的封装、继承、多态、分类

    封装 封装最好理解了 封装是面向对象的特征之一 是对象和类概念的主要特性 封装 也就是把客观事物封装成抽象的类 并且类可以把自己的数据和方法只让可信的类或者对象操作 对不可信的进行信息隐藏 继承 面向对象编程 OOP 语言的一个主要功能就是
  • 测试工具73款

    我们将本文的软件测试工具分为4类 1 Web应用测试工具 2 网站安全测试工具 3 跨浏览器测试工具 4 移动应用测试工具 注 工具排名没有任何意义 1 Web应用测试工具 我们列出了一些在Web应用程序上执行性能 负载和压力测试的关键工具
  • 开源项目MiniWord .NET Word 操作由Word模板和数据简单、快速生成文件

    MiniWord NET Word 介绍 MiniWord NET Word模板引擎 藉由Word模板和数据简单 快速生成文件 image Getting Started 安装 nuget link https www nuget org
  • ubuntu18.04命令安装ros2

    ROS2官方文档 本教程为apt get命令安装方式 官网教程有点问题 借鉴一下大佬的安装方式 文章目录 1 安装ROS2 1 1 安装秘钥相关指令 1 2 授权秘钥 1 3 添加ROS2软件源 1 4 安装 2 设置环境 可选但是推荐 2
  • vue路由器学习(个人学习笔记四)

    目录 友情提醒 第一章 路由简介 1 1 什么是路由 1 2 安装路由插件 第二章 自定义路由器 2 1 创建路由器文件index js文件 2 2 index js文件中配置路由信息 第三章 使用路由器 3 1 在main js文件中将路
  • 基于Item的协同过滤算法实践(最简单的在线电影推荐系统)

    上一篇文章 基于用户的协同过滤算法实践 中 基于用户的相似度生成推荐列表 本文将基于Item的相似度阐述 1 相似度 基于物品的协同过滤算法 简称ItemCF 给用户推荐那些和他们之前喜欢的物品相似的物品 不过ItemCF不是利用物品的内容