文章目录

  • Surprise库
  • 1. 加载数据模块
  • 2. 模型训练前的数据划分模块
  • 2.1 交叉验证数据划分
  • 2.2 训练集测试集划分
  • 3 构建算法模块
  • 3.1 记号说明
  • 3.2 基于统计的算法
  • 3.3 基于近邻(协同过滤)的方法
  • 3.3.1 [相似度计算模块](https://surprise.readthedocs.io/en/stable/similarities.html)
  • 3.3.2 [预测算法](https://surprise.readthedocs.io/en/stable/knn_inspired.html)
  • 3.4 基于矩阵分解的方法
  • 4. 交叉验证和超参数优化
  • 5.结果分析模块


Surprise库

Surprise(Simple Python Recommendation System Engine)是scikit系列的一个基于Python的构建和分析推荐系统的工具库。下面按照构建推荐系统并进行分析的流程梳理一遍涉及到的模块。

1. 加载数据模块

关于加载数据主要有surprise.Dataset读取数据模块和surprise.Reader数据解析模块。数据来源有以下四种:

  1. 加载库内置的数据:Dataset.load_builtin(name),内建数据有‘ml-100k’, ‘ml-1m’和 ‘jester’,默认为’ml-100k’。
from surprise import SVD
from surprise import Dataset
from surprise import accuracy
from surprise.model_selection import KFold

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

# define a cross-validation iterator
kf = KFold(n_splits=3)

algo = SVD()

for trainset, testset in kf.split(data):

    # train and test algorithm.
    algo.fit(trainset)
    predictions = algo.test(testset)

    # Compute and print Root Mean Squared Error
    accuracy.rmse(predictions, verbose=True)
  1. pandas.DataFrame加载数据:Dataset.load_from_df(df, reader)dfpandas.DataFrame格式,三列对应user_iditem_idratingreader为读取文件时的格式,是一个Reader类,在这里只需要设置rating_scale评分范围。
import pandas as pd

from surprise import Dataset
from surprise import Reader

# Creation of the dataframe. Column names are irrelevant.
ratings_dict = {'itemID': [1, 1, 1, 2, 2],
                'userID': [9, 32, 2, 45, 'user_foo'],
                'rating': [3, 2, 4, 3, 1]}
df = pd.DataFrame(ratings_dict)

# A reader is still needed but only the rating_scale param is requiered.
reader = Reader(rating_scale=(1, 5))

# The columns must correspond to user id, item id and ratings (in that order).
data = Dataset.load_from_df(df[['userID', 'itemID', 'rating']], reader)
  • Reader:读取外部数据的时候需要用到,surprise.reader.Reader(name, line_format, sep, rating_scale, skip_line)
  • name:可以选择内建数据集名称,使用时会忽略其他参数
  • line_format:定义每行格式,默认空格分割;
  • sep:设置分隔符;
  • rating_scale:设置rating范围,格式为元组;
  • skip_line:默认为0。
  1. 从一个文件加载数据:Dataset.load_from_file(file_path, reader)file_path为数据集路径,reader为文件解析类,包括每行格式、分隔符、评分范围等。
from surprise import Dataset
from surprise import Reader

# path to dataset file
file_path = os.path.expanduser('~/.surprise_data/ml-100k/ml-100k/u.data')

# As we're loading a custom dataset, we need to define a reader. In the
# movielens-100k dataset, each line has the following format:
# 'user item rating timestamp', separated by '\t' characters.
reader = Reader(line_format='user item rating timestamp', sep='\t')

data = Dataset.load_from_file(file_path, reader=reader)
  1. 针对数据已经划分好的情况,加载多个文件:Dataset.load_from_folds(folds_file, reader)
from surprise import SVD
from surprise import Dataset
from surprise import Reader
from surprise import accuracy
from surprise.model_selection import PredefinedKFold

# path to dataset folder
files_dir = os.path.expanduser('~/.surprise_data/ml-100k/ml-100k/')

# This time, we'll use the built-in reader.
reader = Reader('ml-100k')

# folds_files is a list of tuples containing file paths:
# [(u1.base, u1.test), (u2.base, u2.test), ... (u5.base, u5.test)]
train_file = files_dir + 'u%d.base'
test_file = files_dir + 'u%d.test'
folds_files = [(train_file % i, test_file % i) for i in (1, 2, 3, 4, 5)]

data = Dataset.load_from_folds(folds_files, reader=reader)
pkf = PredefinedKFold()

algo = SVD()

for trainset, testset in pkf.split(data):

    # train and test algorithm.
    algo.fit(trainset)
    predictions = algo.test(testset)

    # Compute and print Root Mean Squared Error
    accuracy.rmse(predictions, verbose=True)

2. 模型训练前的数据划分模块

这部分方法都位于surprise.model_selection.split部分。

2.1 交叉验证数据划分

方法名

说明

KFold(n_splits=5, random_state=None, shuffle=True)

基本的K折交叉验证迭代器

LeaveOneOut(n_splits=5, random_state=None, min_n_ratings=0)

留一法

PredefinedKFold

使用Dataset.load_from_folds加载数据时使用

RepeatedKFold(n_splits=5, n_repeats=10, random_state=None)

使用不同的随机数重复n次k折交叉验证

ShuffleSplit(n_splits=5, test_size=0.2, train_size=None, random_state=None, shuffle=True)

随机训练集和测试集的基本交叉验证迭代器

2.2 训练集测试集划分

train_test_split(data, test_size=0.2, train_size=None, random_state=None, shuffle=True)


3 构建算法模块

3.1 记号说明

符号

说明

评分集

, ,

训练集,测试集,预测集

user集合

,

user

item集合

,

item

对item i有评分的user集合

同时对item i和j有评分的user集合

user u评分过的item集合

user u和v共同评分过的item集合

user u对item i的真实评分

user u对item i的估计评分

user u对item i的baseline评分,

所有评分的均值

user u给出的所有评分的均值

打给item i的所有评分的均值

user u给出的所有评分的标准差

item i收到的所有评分的标准差

pesq的python库 python surprise库_推荐系统_24

给item i有评分的user u的k个近邻user集合,使用相似度指标计算得到

pesq的python库 python surprise库_推荐系统_24

user u打过分的item i的k个近邻item集合,使用相似度指标计算得到

3.2 基于统计的算法

基于surprise.prediction_algorithms模块

  • 随机预测:random_pred.NormalPredictor 假设训练集评分服从正态分布,根据训练集数据进行最大似然估计得到评分的均值pesq的python库 python surprise库_推荐系统_24和标准差pesq的python库 python surprise库_surprise库用法_27,构建正态分布pesq的python库 python surprise库_pesq的python库_28,由正态分布产生预测结果。
    pesq的python库 python surprise库_surprise库用法_29
    pesq的python库 python surprise库_surprise库使用方法_30
  • baseline:baseline_only.BaselineOnly(bsl_options={}, verbose=True) 假设函数为pesq的python库 python surprise库_surprise库使用方法_31,则需要最小化pesq的python库 python surprise库_推荐系统_32,使用Stochastic Gradient Descent (SGD)Alternating Least Squares(ALS)进行求解。
    使用SGD时,损失函数为pesq的python库 python surprise库_surprise库教程_33参数有:

参数

说明

默认值

reg

损失函数的正则化参数

0.02

learning_rate

学习率

0.005

n_epochs

迭代次数

20

print('Using SGD')
bsl_options = {'method': 'sgd',
               'learning_rate': .00005,
               'n_epochs': 20
               }
algo = BaselineOnly(bsl_options=bsl_options)

使用ALS时,损失函数为pesq的python库 python surprise库_推荐系统_35参数有:

参数

说明

默认值

reg_u

item的正则化参数

15

reg_i

user的正则化参数

10

n_epochs

ALS过程的迭代次数

10

bsl_options = {'method': 'als',
               'n_epochs': 5,
               'reg_u': 12,
               'reg_i': 5
               }
algo = BaselineOnly(bsl_options=bsl_options)

3.3 基于近邻(协同过滤)的方法

3.3.1 相似度计算模块

sim_options={name, user_based, min_support ,shrinkage}

  • name表示相似度计算方法,有cosine, msd, pearson, pearson_baseline四种;
  • Cosine similarity
    pesq的python库 python surprise库_surprise库教程_38
  • Mean Squared Difference similarity
    pesq的python库 python surprise库_pesq的python库_39
    pesq的python库 python surprise库_surprise库用法_40
  • Pearson similarity
    pesq的python库 python surprise库_surprise库用法_41
  • Pearson baseline similarity:使用baseline pesq的python库 python surprise库_surprise库用法_42取代mean
    pesq的python库 python surprise库_pesq的python库_43
    当评分矩阵十分稀疏时,使用下式计算以减少过拟合
    pesq的python库 python surprise库_surprise库教程_44
  • user_based表示是否是基于用户的相似度,True表示基于用户的相似度,False表示基于物品的相似度;
  • min_support表示认为两个user或item具有相似性时具有的最少相同数量的item或user,数目小于min_support的user或item会被认为相似性为0;
  • shrinkage是在name=pearson_baseline时有用,默认值为100。
3.3.2 预测算法

knns.KNNBasic(k=40, min_k=1, sim_options={}, verbose=True):基本的基于物品or用户的协同过滤
pesq的python库 python surprise库_surprise库用法_45
knns.KNNWithMeans(k=40, min_k=1, sim_options={}, verbose=True):考虑用户/物品偏好的协同过滤
pesq的python库 python surprise库_pesq的python库_46
knns.KNNWithZScore(k=40, min_k=1, sim_options={}, verbose=True)考虑用户/物品偏好,并对用户/物品进行z-score归一化
pesq的python库 python surprise库_surprise库教程_47
knns.KNNBaseline(k=40, min_k=1, sim_options={}, bsl_options={}, verbose=True)基于baseline的协同过滤
pesq的python库 python surprise库_pesq的python库_48

3.4 基于矩阵分解的方法

  1. Simon Funk2016年参加Netflix Prize期间在博客公开了一个算法Funk-SVD,也称为Latent Factor Model。
    假设函数为pesq的python库 python surprise库_surprise库用法_49,等同于概率矩阵分解Probabilistic Matrix Factorization
  2. matrix_factorization.SVD,考虑偏置项的LFM,也称为bias-SVD
    假设函数为pesq的python库 python surprise库_pesq的python库_50
    构造损失函数为
    pesq的python库 python surprise库_surprise库教程_51
    带入假设函数得pesq的python库 python surprise库_surprise库使用方法_52
    使用SGD的迭代公式如下:
    pesq的python库 python surprise库_surprise库使用方法_53
    pesq的python库 python surprise库_surprise库使用方法_54
    pesq的python库 python surprise库_pesq的python库_55
    pesq的python库 python surprise库_surprise库用法_56
    其中pesq的python库 python surprise库_surprise库用法_57是学习速率,pesq的python库 python surprise库_pesq的python库_58是正则化系数。
    算法参数:

参数名

说明

默认值

n_factor

设置的factor数目

100

n_epochs

SGD算法的迭代次数

20

biased

是否使用bias-SVD,True使用bias-SVD,False使用SVD

True

lr_all

所有参数的学习速率,也可以针对不同的参数设置不同的学习速率

0.005

reg_all

所有参数的正则化系数,也可以针对不同的参数设置不同的正则化系数

0.02

返回pesq的python库 python surprise库_surprise库教程_59pesq的python库 python surprise库_推荐系统_60pesq的python库 python surprise库_surprise库使用方法_61pesq的python库 python surprise库_pesq的python库_62

  1. matrix_factorization.SVDpp 前面的LFM模型并没有显式地考虑用户的历史行为对用户评分预测的影响。Koren在Netflix Prize比赛中提出了一个模型,将用户历史评分的物品加入到了LFM模型中,结合了基于item的邻域方法(item-CF)和LFM,称为SVD++。(论文:Factor in the Neigbhorhood: Scalable and Accurate Collaborative Filtering)
    假设函数为:
    pesq的python库 python surprise库_surprise库使用方法_63
    将物品相似度矩阵pesq的python库 python surprise库_pesq的python库_64同样进行分解,得:
    pesq的python库 python surprise库_surprise库使用方法_65
    为了缓解过拟合,令pesq的python库 python surprise库_surprise库使用方法_66,得
    pesq的python库 python surprise库_surprise库用法_67
    后面构造损失函数,以及算法的超参数与bias-SVD相似。
  2. matrix_factorization.NMFNon-negative Matrix Factorization (NMF),一种基于非负矩阵分解的协同过滤算法。假设函数为pesq的python库 python surprise库_pesq的python库_50,与SVD的区别在于pesq的python库 python surprise库_pesq的python库_69pesq的python库 python surprise库_surprise库使用方法_70的值在任何时候都是非负的。相关论文有:

参数名

说明

默认值

n_factor

设置的factor数目

15

n_epochs

SGD算法的迭代次数

50

biased

是否使用偏置项

False

reg_pu

的正则化系数

0.06

reg_qi

的正则化系数

0.06

reg_bu

的正则化系数,biased=True时有效

0.02

reg_bi

的正则化系数,biased=True时有效

0.02

lr_bu

的学习速率,biased=True时有效

0.005

lr_bi

的学习速率,biased=True时有效

0.005

init_low

factor随机初始化的下界,必须大于等于0

0

init_high

factor随机初始化的上界

1

4. 交叉验证和超参数优化

validation.cross_validate(algo, data, measures=[u'rmse', u'mae'], cv=None, return_train_measures=False, n_jobs=1, pre_dispatch=u'2*n_jobs', verbose=False) 对于给定的算法和数据运行交叉验证过程,返回正确性和花费时间的报告。

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')

# We'll 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)

search.GridSearchCV(algo_class, param_grid, measures=[u'rmse', u'mae'], cv=None, refit=False, return_train_measures=False, n_jobs=1, pre_dispatch=u'2*n_jobs', joblib_verbose=0):网格搜索交叉验证

from surprise import SVD
from surprise import Dataset
from surprise.model_selection import GridSearchCV

# Use movielens-100K
data = Dataset.load_builtin('ml-100k')

param_grid = {'n_epochs': [5, 10], 'lr_all': [0.002, 0.005],
              'reg_all': [0.4, 0.6]}
gs = GridSearchCV(SVD, param_grid, measures=['rmse', 'mae'], cv=3)

gs.fit(data)

# best RMSE score
print(gs.best_score['rmse'])

# combination of parameters that gave the best RMSE score
print(gs.best_params['rmse'])

5.结果分析模块