什么是推荐系统?

互联网的信息越来越多,用户面对大量数据信息的时候,无法获得对自己真正有用的部分,造成信息超载问题。

推荐系统就是解决信息超载的一个办法。

推荐系统依据用户的历史行为、社交关系、兴趣点等信息去判断用户当前需要或感兴趣物品/服务的应用。

推荐是去预测用户对某个他没有使用过的物品/服务的喜欢程度(一般是打分机制),物品/服务可以是电影、书、音乐、新闻等;

推进系统核心任务主要是把用户和信息关联起来,

  • 对用户而言:推荐系统可以帮助用户找到喜欢的物品/服务, 帮忙进行决策,发现用户可能喜欢的新事物
  • 对商家而言:可以给用户提供个性化的服务,提高用户信任度,关注。增加营利收益。

背景介绍

推荐问题的发展历史

1994年, Minncsota, GroupLcns 研究组论文

  • 提出 “协同过滤” 的概念
  • 推荐问题的形式化
  • 影响深远

目前已经广泛应用到很多商业应用系统

尤其是网络购物平台: 亚马逊,京东,淘宝等

Google新闻: 38%的点击量来自推荐等等。

新闻:今日头条

视频:抖音推荐短视频

推荐系统的输入(三元组)

user(用户) + item(物品/服务) + review(用户对物品的评价)

User & user Profile(用户画像)

  • 描述一个用户的 “个性”
  • 两种构建 (用户画像)
  1. 与item Profile 类似,如用户的性别、年龄、年收入、活跃时间
  • 难以与item 建立具体的联系
  • 隐私问题
  • 很少直接使用
  1. 利用item Profile 构建User Profile
  • 信息检索(IR)

Item(物品) & Item Profile(物品对应特征)

  • 电影: 类别、导演、主演、国家等
  • 新闻: 标题、本文、关键词、时间等

Review (user 对 item 的评价)

  • 最简单的Review:打分(Rating)
  • 一般是 1 - 5 的星级
  • 其他Review:
  • 评论
  • 评分
  • 标签

推荐系统的输出

  • 推荐列表( TOP-N)
  • 按照排序给出对该用户的推荐
  • 推荐理由
  • 与 IR(信息索引) 系统不同
  • 基于统计
  • 比如购买了某物品的用户有90%也购买了该物品。(找图)
  • 该物品在某类别中人气最高
  • 重要性
  • 解决推荐的合理性问题
  • 受到越来越多的重视

应用场景

  • 亚马逊个性化推荐
  • 亚马逊相关推荐
  • 电影和视频网站
  • 个性化音乐网络电台
  • 抖音视频的推荐
  • 网易云音乐
  • Facebook
  • 其他
  • 个性化阅读
  • 基于位置的服务
  • 个性化邮件
  • 个性化广告

基本原理

利用用户行为数据
  • 用户行为在个性化推荐系统中一般分为两种
  1. 显性反馈行为
  • 用户明确表示对物品喜好的行为
  1. 隐性反馈行为
  • 指那些不能明确反应用户喜好的行为(比如:页面浏览)
  • 协同过滤算法
  • 协同过滤: 用户通过不断与网页互动,使自己的推荐列表能够不断过滤掉自己不敢兴趣的物品,从而越来越满足自己的 需求。
  • 基于用户的协同过滤算法(UserCF): 依据跟他兴趣相似的其他用户,推荐其他用户喜欢的物品。
  • 基于物品的协同过滤算法(ItemCF): 依据他之前(喜欢的物品)推荐相似的物品
  • UserCF推荐步骤
  • (先找到和他有相同兴趣的其他用户):
    比如下面的图: 分为 用户 A、B、C、D。 物品: a、b、c、d


    找出对某个物品有共同兴趣的用户,比如对物品a 有兴趣的有用户A和用户B

    采用余弦相识度公式:

越接近 1 ,相似度越高 ,A 和 B只有a ,1个,总共 3 × 2 个组合
基于Hadoop的旅游景点推荐系统的设计与实现选题背景及意义 基于hadoop的电影推荐系统_数据
然后 UserCF 算法会给用户推荐和他感兴趣最相近的 K 个用户喜欢的物品

基于Hadoop的旅游景点推荐系统的设计与实现选题背景及意义 基于hadoop的电影推荐系统_surprise_02

参数说明:u 用户, i 物品

1. S(u, K): 包含和用户u 兴趣最接近的 K 个用户
		2. N(i): 对物品 i 有过行为的用户集合
		3. Wuv : 用户u 和 用户 v 的兴趣相似度
		4. Rvi: 代表用户v 对物品 i 的兴趣
  1. 基于图的推荐算法
  2. 二分图又称作二部图,是图论中的一种特殊模型。
利用用户标签数据
  • 通过一些特征联系用户和物品,给用户推荐那些具有用户喜欢的特征的物品
利用上下文信息
  • 用户所处的上下文(context), 包括用户访问推荐系统的时间、地点、心情等,对于提高推荐系统的推荐效果是非常重要的。
利用社交网络
  • 依据调查结果显示,90%的用户相信朋友对他们的推荐,70%的用户相信网上其他用户对广告商品的评论。
MovieLens 数据集
  • MovieLens 包含了电影推荐服务的5星评分和文本标记数据

使用 jupyter notebook分析MovieLens 数据集

导入需要的模块

import numpy as np
import pandas as pd
import matplotlib.pylab as plt

读取对应的数据集显示

header = ['user_id', 'item_id', 'rating', 'timestamp']
src_data = pd.read_csv('ml-100k/u.data', sep='\t', names = header)
src_data.head()

按user_id 排序

user_data = pd.DataFrame(src_data)
user_data.sort_values(by='user_id')

对应的数据集

# 用户信息: 用户ID,年龄,性别,职业,邮政编码
u_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']
users = pd.read_csv('ml-100k/u.user', sep='|', names=u_cols, encoding='latin-1')

# 评分信息: 用户ID,电影ID,评分,时间戳
r_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
ratings = pd.read_csv('ml-100k/u.data', sep='\t', names=r_cols, encoding='latin-1')

# 电影信息:电影ID,电影标题,发布时间,上映时间,IMBD地址
m_cols = ['movie_id', 'title', 'release_date', 'video_release_date', 'imbd_url']
movies = pd.read_csv('ml-100k/u.item', sep='|', names=m_cols, usecols=range(5), encoding='latin-1')

把用户,物品,评分这些表结合

# 把这些数据融合
movie_ratings = pd.merge(users, ratings)
merge_data = pd.merge(movie_ratings, movies)
merge_data.head()

找出评论次数最多的电影
安装电影标题分为不同的组(groups), 并且用size()函数得到
每一部电影的个数(即每部电影被评论的次数),按照从大到小排序,取最大的前20部电影

title_size = merge_data.groupby('title').size()
sort_val = title_size.sort_values(ascending=False)[:20]
print(sort_val)

评分最高的十部电影
按照电影名称分组
取出至少被评论过100次的电影按照平均评分从大到小排序,取最大的10部电影

movie_stats = merge_data.groupby('title')
# 使用agg 函数通过一个字典{’rating':[np.size, np.mean]}来按照key即rating
rating_agg = movie_stats.agg({'rating': [np.size, np.mean]})
# 依据布尔(true, false)排序
take_100 = rating_agg['rating']['size'] >= 100
# print(take_100)
sort_raring = rating_agg[take_100].sort_values([('rating', 'mean')], ascending=False)
sort_raring[:10]

直方图查看用户的年龄分布

plt.hist(users.age, bins=30, color='red', alpha=0.75)
plt.xlabel('age')
plt.ylabel('count of users')
plt.show()

用户年龄分组

# 将用户年龄分组
labels = ['10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79']
merge_data['age_group'] = pd.cut(merge_data.age, range(10, 81, 10), right=False, labels=labels)
merge_data[['age', 'age_group']].drop_duplicates()[:10]
# 每个年龄段用户评分偏好
merge_data.groupby('age_group').agg({'rating':[np.size, np.mean]})

推荐系统框架: surprise

摘自官网surprise简要概述:

Surprise was designed with the following purposes in mind:

大体上是说,推荐系统被设计时,充分考虑了用户控制,减轻数据集的处理,可以使用内置的数据集(Movielens, Jester), 提供了大量的基本算法,能更容易实现新的想法, 提供算法分析、模型评估、比较算法性能的工具。一个简单的Python 推荐系统引擎库

安装方法
pip install numpy
pip install scikit-surprise

也可以 anaconda 安装:

conda install -c conda-forge scikit-surprise

也可以从git clone github官网安装最新版本

pip install numpy cython
git clone https://github.com/NicolasHug/surprise.git
cd surprise
python setup.py install

基本算法

算法类名

描述

random_pred.NormalPredictor

依据训练集的分布特征随机给预测值

baseline_only.BaselineOnly

给定用户和Item, 给出基于baseline的估计值

knns.KNNBasic

最基础的协同过滤

knns.KNNWithMeans

将每个用户评分的均值考虑在内的协同过滤实现

knns.KNNWithZScore

一种基本的协同过滤算法,考虑到每个用户的Z分数标准化。

knns.KNNBaseline

考虑基线评级的协同过滤

matrix_factorization.SVD

SVD(奇异值)实现

matrix_factorization.SVDpp

SVD++

matrix_factorization.NMF

基于矩阵分解的协同过滤

slope_one.SlopeOne

一个简单但精确的协同过滤算法

co_clustering.CoClustering

基于协同聚类的协同过滤算法

交叉验证迭代器

KFold

一个基本的交叉验证迭代器。

RepeatedKFold

重复KFold交叉验证。

ShuffleSplit

具有随机顺练集和测试集的基本交叉验证迭代器。

LeaveOneOut

交叉验证迭代器,其中每个用户在测试集合中只有一个评级。

PredefinedKFold

使用load_from_folds 方法加载数据集时的交叉验证迭代器。

相识度准则

cosine

计算所有用户(或物品)对之间的余弦相似度。

msd

计算所有用户(或物品)对之间的均方差异相似度

pearson

计算所有用户(或物品)对之间的Pearson相关系数。

pearson_baseline

计算所有用户(或项目)对之间的(缩小的)Pearson相关系数,使用基线进行居中而不是平均值。

精度评估

rmse

计算RMSE (均方根误差).

mae

计算MAE (平均绝对误差).

fcp

计算 FCP (协调对的分数).

自带三个数据集

  • movielens-100k 数据集:1700部电影的1000名用户获得100,000评级。 发布于4/1998。
  • movielens-1m 数据集: 4000部电影的6000名用户获得100万评级。 2003年2月发布。
  • Jester 数据集:在线笑话评分。

上面的都可以在各自的网站下载到,都分有时间段收集的数据集。

加载数据集

Dataset.load_builtin

加载内置数据集。

Dataset.load_from_file

从(自定义)文件加载数据集。

Dataset.load_from_folds

加载数据集,其中折叠(用于交叉验证)由某些文件预定义。

Dataset.folds

生成器函数迭代数据集的折叠。

DatasetAutoFolds.split

将数据集拆分为折叠以供接下来的交叉验证。

surprise官方实例

from surprise import SVD
from surprise import Dataset
from surprise.model_selection import cross_validate

# Load the movielens-100k dataset (download it if needed).
data = Dataset.load_builtin('ml-100k')

# Use the famous SVD algorithm.
algo = SVD()

# Run 5-fold cross-validation and print results.
cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=5, verbose=True)

Output

评估5次分裂的算法SVD的RMSE,MAE。 Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std RMSE 0.9311 0.9370 0.9320 0.9317 0.9391 0.9342 0.0032 MAE 0.7350 0.7375 0.7341 0.7342 0.7375 0.7357 0.0015 Fit time 6.53 7.11 7.23 7.15 3.99 6.40 1.23 Test time 0.26 0.26 0.25 0.15 0.13 0.21 0.06

  • 但是官网自带的大量实例,只能看到算法评估效果。

如何计算某一个用户(user)对某一个物品(item)的评分?

根据预测结果,获取TOP-N的函数
def get_top_n(predictions, n = 10):
    top_n = defaultdict(list)
    
    # uid: 用户ID
    # iid: 物品ID
    # est: 估计得分
    for uid, iid, true_r, est, _ in predictions:
        top_n[uid].append((iid, est))
        
    # 为每一个用户都寻找 K 个得分最高的item
    for uid, user_ratings in top_n.items():
        user_ratings.sort(key=lambda x: x[1], reverse=True)
        top_n[uid] = user_ratings[:n]
        
    return top_n
自带 ml-100k数据TOP-N 推荐
每一个用户评分最高的10个物品Item
# 首先在movielens数据集上训练SVD算法。
data = Dataset.load_builtin('ml-100k')
# 转换,这样才能获取数据集详细信息
trainset = data.build_full_trainset()
algo = SVD()
algo.fit(trainset)

testset = trainset.build_anti_testset()
predictions = algo.test(testset)

top_n = get_top_n(predictions, n = 10)

for uid, user_ratings in top_n.items():
    print(uid, [iid for (iid, _) in user_ratings])
在自己的数据上做TOP-N推荐
from surprise import SVD
from surprise import Dataset, print_perf, Reader
from surprise.model_selection import cross_validate
import os

# 指定文件所在路径,mydata是自己制作的数据集
file_path = os.path.expanduser('mydata.csv')
# sep: 指定数据集以什么方式去读
reader = Reader(line_format='user item rating', sep=',')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
trainset = data.build_full_trainset()

#训练SVD算法
algo = SVD()
algo.fit(trainset)
测试用户获取第一个用户分别对第1,2,3个item的得分
testset = [('1', '1', 0),
          ('1', '2', 0),
          ('1', '3', 0),
]
predictions = algo.test(testset)

我们如何使用协同过滤?

基于用户的协同过滤算法(UserCF):
from surprise import KNNWithMeans
from surprise import Dataset, print_perf, Reader
from surprise.model_selection import cross_validate
import os

# 指定文件所在路径
file_path = os.path.expanduser('mydata.csv')
reader = Reader(line_format='user item rating timestamp', sep='\t')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
trainset = data.build_full_trainset()
# 读取数据
header = ['user', 'item', 'rating', 'timestamp']
src_data = pd.read_csv('mydata.csv', sep='\t', names = header)
user_data = pd.DataFrame(src_data)
user_data.sort_values(by='user').head()

# user-base CF 得分计算
algo = KNNWithMeans(k=50, sim_options={'user_based': True})
algo.fit(trainset)

uid = str(5)
iid = str(1)
pred = algo.predict(uid, iid)
pred
基于物品的协同过滤算法(ItemCF)
# item-based CF 得分计算
# 取最相似的用户计算时,只取最相似的k个
algo = KNNWithMeans(k=50, sim_options={'user_based': False})
algo.fit(trainset)
uid = str(1)
iid = str(1)

pred = algo.predict(uid, iid)
pred