ContentBased算法的思想非常简单:根据用户过去喜欢的物品(本文统称为 item),为用户推荐和他过去喜欢的物品相似的物品。而关键就在于这里的物品相似性的度量,这才是算法运用过程中的核心。

CB的过程一般包括以下三步:

物品表示(Item Representation):为每个item抽取出一些特征(也就是item的content了)来表示此item;

特征学习(Profile Learning):利用一个用户过去喜欢(及不喜欢)的item的特征数据,来学习出此用户的喜好特征(profile);

生成推荐列表(Recommendation Generation):通过比较上一步得到的用户profile与候选item的特征,为此用户推荐一组相关性最大的item。

代码中,初始化步骤如下:

1、得到moviesDF,包括movie_id,title,genres三列;得到ratingsDF,包括user_id,movie_id,rating和timestamp。

2、得到item_cate,cate_item分别代表item中不同种类的得分(平均)以及每个种类下item得分的倒排。

3、得到self.up,形式是userid:[(category,ratio),(category1,ratio1)],代表每个用户对cate的评分。

重点有以下方法:

  • get_up(self,score_thr=4.0,topK=5)

    选出评分>score_thr的item代表用户的倾向,对时间进行加权得到time_score,具体公式为:\(time\_score=round(\frac{1}{1+(max\_ts-ts)/(24*60*60*100)},3)\),代表最近的时间点评分的item时间权重越大。根据用户对item的评分,评分的时间权重以及item下的cate权重最终得到每位用户topK的cate分数(并进行归一化)

  • recommend(self, userID, K=10)

    根据用户的cate分数得到每一个cate下top的item,作为对用户的推荐。

实际上,这里使用电影类别作为item的特征数据,来表示用户的喜好特征(profile),根据用户profile与候选item在特征下的分数,为此用户推荐一组相关性最大的item。

全部代码如下所示:

#-*-coding:utf-8-*-
"""
author:jamest
date:20190405
content based function
"""
import pandas as pd
import numpy as np
import time
import os class contentBased:
def __init__(self,rating_file,item_file):
if not os.path.exists(rating_file) or not os.path.exists(item_file):
print('the file not exists')
return
self.moviesDF = pd.read_csv(item_file, index_col=None, sep='::', header=None, names=['movie_id', 'title', 'genres'])
self.ratingsDF = pd.read_csv(rating_file, index_col=None, sep='::', header=None, names=['user_id', 'movie_id', 'rating', 'timestamp'])
self.item_cate, self.cate_item = self.get_item_cate()
self.up = self.get_up() def get_item_cate(self,topK = 10):
"""
Args:
topK:nums of items in cate_item
Returns:
item_cate:a dic,key:itemid ,value:ratio
cate_item:a dic:key:cate vale:[item1,item2,item3]
"""
movie_rating_avg = self.ratingsDF.groupby('movie_id')['rating'].agg({'item_ratings_mean': np.mean}).reset_index()
movie_rating_avg.head()
items = movie_rating_avg['movie_id'].values
scores = movie_rating_avg['item_ratings_mean'].values #得到item的平均评分
item_score_veg = {}
for item, score in zip(items, scores):
item_score_veg[item] = score #得到item中不同种类的得分
item_cate = {}
items = self.moviesDF['movie_id'].values
genres = self.moviesDF['genres'].apply(lambda x: x.split('|')).values
for item, genres_lis in zip(items, genres):
radio = 1 / len(genres_lis)
item_cate[item] = {}
for genre in genres_lis:
item_cate[item][genre] = radio recode = {}
for item in item_cate:
for genre in item_cate[item]:
if genre not in recode:
recode[genre] = {}
recode[genre][item] = item_score_veg.get(item, 0) # 不同种类item的倒排
cate_item = {}
for cate in recode:
if cate not in cate_item:
cate_item[cate] = []
for zuhe in sorted(recode[cate].items(), key=lambda x: x[1], reverse=True)[:topK]:
cate_item[cate].append(zuhe[0]) return item_cate, cate_item def get_time_score(self,timestamp,fix_time_stamp):
"""
Args:
timestamp:the timestamp of user-item
fix_time_stamp:the max timestamp of the timestamps
Returns:
a time_score:fixed range in (0,1]
"""
total_sec = 24*60*60
delta = (fix_time_stamp-timestamp)/total_sec/100
return round(1/(1+delta),3) def get_up(self,score_thr=4.0,topK=5):
"""
Args:
score_thr:select the score>=score_thr of ratingsDF
topK:the number of item in up
Returns:
a dic,key:userid ,value[(category,ratio),(category1,ratio1)]
"""
ratingsDF = self.ratingsDF[self.ratingsDF['rating'] > score_thr]
fix_time_stamp = ratingsDF['timestamp'].max()
ratingsDF['time_score'] = ratingsDF['timestamp'].apply(lambda x: self.get_time_score(x,fix_time_stamp)) users = ratingsDF['user_id'].values
items = ratingsDF['movie_id'].values
ratings = ratingsDF['rating'].values
scores = ratingsDF['time_score'].values recode = {}
up = {}
for userid, itemid, rating, time_score in zip(users, items, ratings, scores):
if userid not in recode:
recode[userid] = {} for cate in self.item_cate[itemid]:
if cate not in recode[userid]:
recode[userid][cate] = 0
recode[userid][cate] += rating * time_score * self.item_cate[itemid][cate]
for userid in recode:
if userid not in up:
up[userid] = []
total_score = 0
for zuhe in sorted(recode[userid].items(), key=lambda x: x[1], reverse=True)[:topK]:
up[userid].append((zuhe[0], zuhe[1]))
total_score += zuhe[1]
for index in range(len(up[userid])):
up[userid][index] = (up[userid][index][0], round(up[userid][index][1] / total_score, 3))
return up def recommend(self, userID, K=10):
"""
Args:
userID: the user to recom
K: the num of recom item
Returns:
a dic,key:userID ,value:recommend itemid
"""
if userID not in self.up:
return
recom_res = {}
if userID not in recom_res:
recom_res[userID] = [] for zuhe in self.up[userID]:
cate, ratio = zuhe
num = int(K * ratio) + 1
if cate not in self.cate_item:
continue
rec_list = self.cate_item[cate][:num]
recom_res[userID] += rec_list
return recom_res if __name__ == '__main__':
moviesPath = '../data/ml-1m/movies.dat'
ratingsPath = '../data/ml-1m/ratings.dat'
usersPath = '../data/ml-1m/users.dat'
recom_res = contentBased(ratingsPath,moviesPath).recommend(userID=1,K=30)
print('content based result',recom_res)

参考:

推荐系统概述(一)

Github

个性化召回算法实践(四)——ContentBased算法的更多相关文章

  1. 个性化排序算法实践(五)——DCN算法

    wide&deep在个性化排序算法中是影响力比较大的工作了.wide部分是手动特征交叉(负责memorization),deep部分利用mlp来实现高阶特征交叉(负责generalizatio ...

  2. 个性化排序算法实践(三)——deepFM算法

    FM通过对于每一位特征的隐变量内积来提取特征组合,最后的结果也不错,虽然理论上FM可以对高阶特征组合进行建模,但实际上因为计算复杂度原因,一般都只用到了二阶特征组合.对于高阶特征组合来说,我们很自然想 ...

  3. 个性化召回算法实践(一)——CF算法

    协同过滤推荐(Collaborative Filtering Recommendation)主要包括基于用户的协同过滤算法与基于物品的协同过滤算法. 下面,以movielens数据集为例,分别实践这两 ...

  4. 个性化排序算法实践(四)——GBDT+LR

    本质上GBDT+LR是一种具有stacking思想的二分类器模型,所以可以用来解决二分类问题.这个方法出自于Facebook 2014年的论文 Practical Lessons from Predi ...

  5. 个性化召回算法实践(三)——PersonalRank算法

    将用户行为表示为二分图模型.假设给用户\(u\)进行个性化推荐,要计算所有节点相对于用户\(u\)的相关度,则PersonalRank从用户\(u\)对应的节点开始游走,每到一个节点都以\(1-d\) ...

  6. 个性化召回算法实践(二)——LFM算法

    LFM算法核心思想是通过隐含特征(latent factor)联系用户兴趣和物品,找出潜在的主题和分类.LFM(latent factor model)通过如下公式计算用户u对物品i的兴趣: \[ P ...

  7. 个性化排序算法实践(一)——FM算法

    因子分解机(Factorization Machine,简称FM)算法用于解决大规模稀疏数据下的特征组合问题.FM可以看做带特征交叉的LR. 理论部分可参考FM系列,通过将FM的二次项化简,其复杂度可 ...

  8. 个性化排序算法实践(二)——FFM算法

    场感知分解机(Field-aware Factorization Machine ,简称FFM)在FM的基础上进一步改进,在模型中引入类别的概念,即field.将同一个field的特征单独进行one- ...

  9. [迷宫中的算法实践]迷宫生成算法——递归分割算法

    Recursive division method        Mazes can be created with recursive division, an algorithm which wo ...

随机推荐

  1. Nginx负载均衡-如何自定义URL中的hash key

    "例如请求的url为http://www.a.com/{path_var1}/{path_var2}path_var1和path_var2是两个path variable如果现在只想根据pa ...

  2. Django 插件之 Xadmin实现富文本编辑器

    此文为前一篇文章的续写: Django 插件之 Xadmin Ueditor 介绍 UEditor 是由百度 web 前端研发部开发所见即所得富文本 web 编辑器,具有轻量,可定制,注重用户体验等特 ...

  3. JIRA数据库的迁移,从HSQL到MYSQL/Oracle

    Jira数据库迁移,从HSQL到MYSQL 通过JIRA管理员登录,进入“管理员页面”,“系统”--“导入&导出”,以XML格式备份数据. 在MySQL中创建Schema,命名为jira 关闭 ...

  4. 《ucore lab1 exercise6》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 题目:完善中断初始化和处理 请完成编码工作和回答如下问题: 中断描述符表(也可简称为保护模式下的中断向量表)中一个表项占多少字节?其中哪几位代表 ...

  5. Scala 面向对象编程之Trait

    将trait作为接口使用 // Scala中的Triat是一种特殊的概念 // 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 // 在triat中可以定义抽象方 ...

  6. WebApi如何传递参数

    一 概述 一般地,我们在研究一个问题时,常规的思路是为该问题建模:我们在研究相似问题时,常规思路是找出这些问题的共性和异性.基于该思路,我们如何研究WebApi参数传递问题呢? 首先,从参数本身来说, ...

  7. Educational Codeforces Round 61 (Div.2)

    A.(c1=0&&c3>0)||(c1!=c4) #include<cstdio> #include<cstring> #include<algor ...

  8. 第一章 Java的IO演进之路

    Unix中5种IO模型 就网络通信而言,一次数据读入可以分为两个阶段,首先等待数据从网络中到达,到达后需要复制到内核的缓冲区中,第二个阶段是从内核的缓冲区复制到进程的缓冲区,复制到进程的缓冲区才算读取 ...

  9. iis 经典模式和集成模式

    IIS7.0中的Web应用程序有两种配置模式:经典模式和集成模式.两者区别大家可以参考下,根据实际情况选用.  经典模式是为了与之前的版本兼容,使用ISAPI扩展来调用ASP.NET运行库,原先运行于 ...

  10. 关于utf8mb4的使用

    针对mysql数据库存储一些特殊字符或者emoji的字符,所需要的编码类型.实际上基于efcore框架的情况下,codefirst自动迁移生成的数据库的默认编码格式,就是utf8mb4,以前的时候记得 ...