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. 对 Watchbog Botnet 渗透过程和 Payload 的分析

    漏洞利用 CVE-2018-1000861 https://jenkins.io/security/advisory/2018-12-05/ Watchbog在做什么? Watchbog僵尸网络为其所 ...

  2. bootstrap-table和bootstrap-switch

    {% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta ...

  3. 2019年Java面试题基础系列228道(4)

    1.Java 中能创建 volatile 数组吗? 能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组.我的意思是,如果改变引用指向的数组,将会受到 vo ...

  4. [转帖]Apache Kylin 概述

    Apache Kylin 概述 https://www.cnblogs.com/xiaodf/p/11671095.html 1 Kylin是什么 今天,随着移动互联网.物联网.AI等技术的快速兴起, ...

  5. SpringBoot项目集成PageHelper使用

    SpringBoot项目集成PageHelper使用 一.开始 ​ 地址:https://github.com/pagehelper/Mybatis-PageHelper ​ 在spring boot ...

  6. css拓展

    自定义居中 .second-listleft{/*固定位置*/ position: relative; float: left; width: 25%; height: 100%; backgroun ...

  7. 在vue中使用ElementUI

    完整引用ElementUI: 安装:在需要使用到的vue项目目录下,使用npm下载安装: npm/cnpm i element-ui -S/--save <!-- 引入样式 --> < ...

  8. AtomicIntegerFieldUpdater和AtomicInteger

    为什么有了AtomicInteger还需要AtomicIntegerFieldUpdater? 当需要进行原子限定的属性所属的类会被创建大量的实例对象, 如果用AtomicInteger, 每个实例里 ...

  9. hdu 6185 递推+矩阵快速幂

    思路:考虑全部铺满时,前2列的放法.有如下5种情况:(转自http://blog.csdn.net/elbadaernu/article/details/77825979 写的很详细 膜一下)  假设 ...

  10. python之基础总结(飞机大战)

    一.学习python有一段时间了,总体上手还是挺好的,但是有些东西还是和Java存在着一定的区别,这里主要是通过学习,然后自己去编写一个案例.从中学习到的一些东西,这里分享出来,如果存在不正确的地方还 ...