推荐系统surprise库教程

推荐系统surprise库官方文档解读

  • 安装时常见问题
  • Surprise的功能
  • 示例
    • 使用内置的数据集+交叉验证
    • 不使用交叉验证,只把数据集分割一次
    • 使用自己的数据集、不使用测试集
    • 自行指定训练集和测试集
  • 内置算法和参数设置
    • NormalPredictor算法
        • 示例代码
    • Baseline算法
      • ALS
        • 示例代码
      • SGD
        • 示例代码
    • KNNBasic算法
        • 示例代码
    • KNNWithMeans算法
        • 示例代码
    • KNNWithZScore算法
        • 示例代码
    • KNNBaseline算法
        • 示例代码
    • SVD算法
        • 示例代码
    • SVDpp算法
        • 示例代码
    • NMF算法
        • 示例代码
    • SlopeOne算法
        • 示例代码
    • CoClustering算法
  • Precision、Recall、MAP和NDCG的计算

安装时常见问题

安装还是常见的

pip install surprise

安装常见问题:出现报错(error: Microsoft Visual C++ 14.0 is required. Get it with “Microsoft Visual C++ Build Tools”: https://visualstudio.microsoft.com/downloads/)
解决方法:
①最笨的方法,下载所提示的对应的Visual Studio版本;
②核心思想,躲!在https://www.lfd.uci.edu/~gohlke/pythonlibs/上找到对应python版本的想要的库的whl包,然后pip install xx.whl进行安装,surprise库的shl文件在https://pypi.org/project/surprise/#files,不过可能还是躲不掉;
③对于2.7选手,可以在https://www.microsoft.com/en-us/download/details.aspx?id=44266上下载VCForPython27.msi以支持对用C写成的包的支持;

Surprise的功能

Surprise库非常适用于初学者了解推荐算法,其内置的功能包括:

  1. 内部实现了部分基础的推荐算法:KNN类算法(最基础的KNN算法、考虑了均值的KNNWithMeans、考虑了标准值的KNNWithZSore和考虑了baseline的KNNBaseline)、SVD类算法(SVD算法、SVDpp算法和NMF算法)、SlopeOne算法和co-clustering算法;
  2. 自动的多折交叉验证,如5折交叉验证;但同时也可以满足其他的设置,如将自行分割的训练集和测试集输入模型;
  3. 自动计算RMSE、MAE、MSE和FCP(Precision、Recall、F1-score、MAP和NDCG等指标没有内置,但可以根据输出自行编写,后文会给出,供参考)。

示例

本节会给出Surprise库使用的相关示例,读者可以根据自己的需要对示例的代码进行改写,从而实现自己所需的功能。

使用内置的数据集+交叉验证

from surprise import SVD
from surprise import Dataset
from surprise.model_selection import cross_validate
# 加载内置的ml100k数据集
data = Dataset.load_builtin('ml-100k')
# 使用SVD算法
algo = SVD()
# 使用五折交叉验证,使用cv参数设置几折,measures设置评价指标,verbose设置为True表示显示详细信息
cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=5, verbose=True)

不使用交叉验证,只把数据集分割一次

写成类似于sklearn中的常见写法

from surprise import SVD
from surprise import Dataset
from surprise import accuracy
from surprise.model_selection import train_test_split
data = Dataset.load_builtin('ml-100k')
# 类似于sklearn中的写法,将数据分割为75%
trainset, testset = train_test_split(data, test_size=.25)
algo = SVD()
# 不同上一个例子,这里使用fit和test函数
algo.fit(trainset)
predictions = algo.test(testset)
# 选用rmse指标
accuracy.rmse(predictions)

使用自己的数据集、不使用测试集

from surprise import SVD
from surprise import Dataset
from surprise import accuracy
from surprise import Reader
# 指定要读入的文件的格式,本例中每行三列,分别是用户、项目和评分,中间用空格隔开,若是用逗号或其他符号隔开,在sep参数中进行变化即可
reader = Reader(line_format='user item rating', sep=' ')
# 指定要读入的数据文件,本例中为test.txt
data = Dataset.load_from_file('test.txt', reader=reader)
# 把全部数据集都作为训练集
data = data.build_full_trainset()
algo = SVD()
algo.fit(trainset)
predictions = algo.test(testset)
accuracy.rmse(predictions)

自行指定训练集和测试集

from surprise import SVD
from surprise import Dataset
from surprise import accuracy
from surprise import Reader
from surprise.model_selection import PredefinedKFold
# 数据集在系统路径\data\下
files_dir = os.path.expanduser('~/data/')
# 训练集为u1.base、u2.base
train_file = files_dir + 'u%d.base'
# 测试集为u1.test、u2.test
test_file = files_dir + 'u%d.test'
# range(m,n)表示训练集和测试集文件的命名,因为本例中是从u1到u2,所以这里为range(1,3),其实就是定义一个列表,里面是一组组训练集和测试集文件,即[(训练集1,测试集1),(训练集2,测试集2)……]
folds_files = [(train_file % i, test_file % i) for i in range(1, 3)]
reader = Reader(line_format='user item rating', sep='\t')
data = Dataset.load_from_folds(folds_files, reader=reader)
pkf = PredefinedKFold()
algo = SVD()
# 因为本例中有两组训练集和测试集,所以出现两次结果
for trainset, testset in pkf.split(data):
    algo.fit(trainset)
    predictions = algo.test(testset)
    accuracy.rmse(predictions, verbose=True)
    accuracy.mae(predictions, verbose=True)

内置算法和参数设置

NormalPredictor算法

该算法即随机预测算法,假设测试集的评分满足正态分布,然后生成正态分布的随机数进行预测,正态分布 N ( μ ^ , σ ^ 2 ) N(\hat{\mu},\hat{\sigma}^2) N(μ^,σ^2)的参数均值和方差从训练集中得到。
μ ^ = 1 ∣ R t r a i n ∣ ∑ r u i ∈ R t r a i n r u i \hat{\mu}=\frac{1}{\vert R_{train}\vert}\sum_{r_{ui}\in R_{train}}r_{ui} μ^=Rtrain1ruiRtrainrui
σ ^ = ∑ r u i ∈ R t r a i n ( r u i − μ ^ ) 2 ∣ R t r a i n ∣ \hat{\sigma}=\sqrt{\sum_{r_{ui}\in R_{train}}\frac{(r_{ui}-\hat{\mu})^2}{\vert R_{train}\vert}} σ^=ruiRtrainRtrain(ruiμ^)2

示例代码

algo = NormalPredictor()

Baseline算法

Koren提出的baseline算法,不考虑用户的偏好
r u i ^ = μ + b u + b i \hat{r_{ui}}=\mu+b_u+b_i rui^=μ+bu+bi
对于未在训练集中出现的 u u u b u = 0 b_u=0 bu=0 b i b_i bi做类似处理)
参数设置
训练方法是使用交替最小二乘法(ALS)还是随机梯度下降(SGD)

ALS

  1. r e g i reg_i regi:使用ALS得到非精确解的分母上的衰减因子 λ i \lambda_i λi,默认为10
  2. r e g u reg_u regu:使用ALS得到非精确解的分母上的衰减因子 λ u \lambda_u λu,默认为15
  3. n _ e p o c h s n\_epochs n_epochs:ALS的迭代次数,默认为10
  4. v e r b o s e verbose verbose:是否输出训练的信息,默认为True,即输出,基本每个算法都有这个参数,后面的算法介绍时不再说明此参数

示例代码

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

SGD

  1. r e g reg reg:正则化率,默认为0.02
  2. l e a r n i n g _ r a t e learning\_rate learning_rate:学习速率,默认为0.005
  3. n _ e p o c h s n\_epochs n_epochs:ALS的迭代次数,默认为20

示例代码

bsl_options = {'method': 'sgd',
               'learning_rate': .00005,
               }
algo = BaselineOnly(bsl_options=bsl_options)

KNNBasic算法

最基础的KNN算法,可分为user-based KNN和item-based KNN
user-based KNN的公式
r u i ^ = ∑ v ∈ N i k ( u ) s i m ( u , v ) ⋅ r v i ∑ v ∈ N i k ( u ) s i m ( u , v ) \hat{r_{ui}} = \frac {\sum_{v\in N_i^k(u)} sim(u,v)\cdot r_{vi}} {\sum_{v\in N_i^k(u)}sim(u,v)} rui^=vNik(u)sim(u,v)vNik(u)sim(u,v)rvi
item-based KNN的公式
r u i ^ = ∑ j ∈ N u k ( i ) s i m ( i , j ) ⋅ r u j ∑ j ∈ N u k ( i ) s i m ( i , j ) \hat{r_{ui}} = \frac {\sum_{j\in N_u^k(i)} sim(i,j)\cdot r_{uj}} {\sum_{j\in N_u^k(i)}sim(i,j)} rui^=jNuk(i)sim(i,j)jNuk(i)sim(i,j)ruj
8. k k k:设置的邻居的个数,默认为40
9. m i n _ k min\_k min_k:最少的邻居的个数,如果合适的邻居达不到设置的最小邻居值,则使用全局平均值进行预测,默认为1
10. s i m _ o p t i o n s sim\_options sim_options中的 n a m e name name:使用的计算相似度的函数,默认为MSD,也可设置为cosine或pearson_baseline
11. s i m _ o p t i o n s sim\_options sim_options中的 u s e r _ b a s e d user\_based user_based:默认为True,即使用user-based KNN,若设置为True,则使用item-based KNN
12. s i m _ o p t i o n s sim\_options sim_options中的 m i n _ s u p p o r t min\_support min_support:相似度达到该值,才能进入邻居的选择范围,无默认值
13. s i m _ o p t i o n s sim\_options sim_options中的 s h r i n k a g e shrinkage shrinkage:当相似函数选择为pearson_baseline,用该参数设置是否衰减,默认为100

示例代码

sim_options = {'name': 'cosine',
               'user_based': False  # compute  similarities between items
               }
algo = KNNBasic(k=10, sim_options=sim_options)
sim_options = {'name': 'pearson_baseline',
               'shrinkage': 0  # no shrinkage
               }
algo = KNNBasic(k=10, sim_options=sim_options)

KNNWithMeans算法

在KNNBasic算法的基础上,考虑用户均值或项目均值
r ^ u i = μ u + ∑ v ∈ N i k ( u ) sim ( u , v ) ⋅ ( r v i − μ v ) ∑ v ∈ N i k ( u ) sim ( u , v ) \hat{r}_{ui} = \mu_u + \frac{ \sum\limits_{v \in N^k_i(u)} \text{sim}(u, v) \cdot (r_{vi} - \mu_v)} {\sum\limits_{v \in N^k_i(u)} \text{sim}(u, v)} r^ui=μu+vNik(u)sim(u,v)vNik(u)sim(u,v)(rviμv)

r ^ u i = μ i + ∑ j ∈ N u k ( i ) sim ( i , j ) ⋅ ( r u j − μ j ) ∑ j ∈ N u k ( i ) sim ( i , j ) \hat{r}_{ui} = \mu_i + \frac{ \sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot (r_{uj} - \mu_j)} {\sum\limits_{j \in N^k_u(i)} \text{sim}(i, j)} r^ui=μi+jNuk(i)sim(i,j)jNuk(i)sim(i,j)(rujμj)
参数设置与KNNBasic类似

示例代码

sim_options = {'name': 'cosine',
               'user_based': False  # compute  similarities between items
               }
algo = KNNWithMeans(k=10, sim_options=sim_options)

KNNWithZScore算法

引入Z-Score的思想
r ^ u i = μ u + σ u ∑ v ∈ N i k ( u ) sim ( u , v ) ⋅ ( r v i − μ v ) / σ v ∑ v ∈ N i k ( u ) sim ( u , v ) \hat{r}_{ui} = \mu_u + \sigma_u \frac{ \sum\limits_{v \in N^k_i(u)} \text{sim}(u, v) \cdot (r_{vi} - \mu_v) / \sigma_v} {\sum\limits_{v \in N^k_i(u)} \text{sim}(u, v)} r^ui=μu+σuvNik(u)sim(u,v)vNik(u)sim(u,v)(rviμv)/σv

r ^ u i = μ i + σ i ∑ j ∈ N u k ( i ) sim ( i , j ) ⋅ ( r u j − μ j ) / σ j ∑ j ∈ N u k ( i ) sim ( i , j ) \hat{r}_{ui} = \mu_i + \sigma_i \frac{ \sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot (r_{uj} - \mu_j) / \sigma_j} {\sum\limits_{j \in N^k_u(i)} \text{sim}(i, j)} r^ui=μi+σijNuk(i)sim(i,j)jNuk(i)sim(i,j)(rujμj)/σj
参数设置与KNNBasic类似

示例代码

sim_options = {'name': 'cosine',
               'user_based': False  # compute  similarities between items
               }
algo = KNNWithZScore(k=10, sim_options=sim_options)

KNNBaseline算法

和KNNWithMeans的区别在于,用的不是均值而是bias
r ^ u i = b u i + ∑ v ∈ N i k ( u ) sim ( u , v ) ⋅ ( r v i − b v i ) ∑ v ∈ N i k ( u ) sim ( u , v ) \hat{r}_{ui} = b_{ui} + \frac{ \sum\limits_{v \in N^k_i(u)} \text{sim}(u, v) \cdot (r_{vi} - b_{vi})} {\sum\limits_{v \in N^k_i(u)} \text{sim}(u, v)} r^ui=bui+vNik(u)sim(u,v)vNik(u)sim(u,v)(rvibvi)

r ^ u i = b u i + ∑ j ∈ N u k ( i ) sim ( i , j ) ⋅ ( r u j − b u j ) ∑ j ∈ N u k ( i ) sim ( i , j ) \hat{r}_{ui} = b_{ui} + \frac{ \sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot (r_{uj} - b_{uj})} {\sum\limits_{j \in N^k_u(i)} \text{sim}(i, j)} r^ui=bui+jNuk(i)sim(i,j)jNuk(i)sim(i,j)(rujbuj)
参数设置与KNNBasic类似

示例代码

sim_options = {'name': 'cosine',
               'user_based': False  # compute  similarities between items
               }
algo = KNNBaseline(k=10, sim_options=sim_options)

SVD算法

经典的SVD算法
r ^ u i = μ + b u + b i + q i T p u \hat{r}_{ui} = \mu + b_u + b_i + q_i^Tp_u r^ui=μ+bu+bi+qiTpu
损失函数为
∑ r u i ∈ R t r a i n ( r u i − r ^ u i ) 2 + λ ( b i 2 + b u 2 + ∣ ∣ q i ∣ ∣ 2 + ∣ ∣ p u ∣ ∣ 2 ) \sum_{r_{ui} \in R_{train}} \left(r_{ui} - \hat{r}_{ui} \right)^2 + \lambda\left(b_i^2 + b_u^2 + ||q_i||^2 + ||p_u||^2\right) ruiRtrain(ruir^ui)2+λ(bi2+bu2+qi2+pu2)
优化公式为
b u ← b u + γ ( e u i − λ b u ) b_u \leftarrow b_u + \gamma (e_{ui} - \lambda b_u) bubu+γ(euiλbu)
b i ← b i + γ ( e u i − λ b i ) b_i \leftarrow b_i + \gamma (e_{ui} - \lambda b_i) bibi+γ(euiλbi)
p u ← p u + γ ( e u i ⋅ q i − λ p u ) p_u \leftarrow p_u + \gamma (e_{ui} \cdot q_i - \lambda p_u) pupu+γ(euiqiλpu)
q i ← q i + γ ( e u i ⋅ p u − λ q i ) q_i \leftarrow q_i + \gamma (e_{ui} \cdot p_u - \lambda q_i) qiqi+γ(euipuλqi)
14. n _ f a c t o r s n\_factors n_factors:隐因子的数量,默认为100
15. n _ e p o c h s n\_epochs n_epochs:迭代次数,默认为20
16. b i a s e d biased biased:默认为True,即使用SGD,如果为False,则使用MF算法也就是PMF算法
17. i n i t _ m e a n init\_mean init_mean:p和q两个向量的初始值由正态分布生成,均值参数由该参数设置,默认为0
18. i n i t _ s t d _ d e v init\_std\_dev init_std_dev:p和q两个向量的初始值由正态分布生成,标准差参数由该参数设置,默认为0.1
19. l r _ a l l lr\_all lr_all:可由该参数直接设置所有学习速率的值,默认为0.005
20. r e g _ a l l reg\_all reg_all:可由该参数直接设置所有正则化系数的值,默认为0.02
21. l r _ b u lr\_bu lr_bu:设置 b u b_u bu的学习速率,可覆盖 l r _ a l l lr\_all lr_all,默认未设置
22. l r _ b i lr\_bi lr_bi:设置 b i b_i bi的学习速率,可覆盖 l r _ a l l lr\_all lr_all,默认未设置
23. l r _ p u lr\_pu lr_pu:设置 p u p_u pu的学习速率,可覆盖 l r _ a l l lr\_all lr_all,默认未设置
24. l r _ q i lr\_qi lr_qi:设置 q i q_i qi的学习速率,可覆盖 l r _ a l l lr\_all lr_all,默认未设置
25. r e g _ b u reg\_bu reg_bu:设置 b u b_u bu的学习速率,可覆盖 r e g _ a l l reg\_all reg_all,默认未设置
26. r e g _ b i reg\_bi reg_bi:设置 b i b_i bi的学习速率,可覆盖 r e g _ a l l reg\_all reg_all,默认未设置
27. r e g _ p u reg\_pu reg_pu:设置 p u p_u pu的学习速率,可覆盖 r e g _ a l l reg\_all reg_all,默认未设置
28. r e g _ q i reg\_qi reg_qi:设置 q i q_i qi的学习速率,可覆盖 r e g _ a l l reg\_all reg_all,默认未设置
29. r a n d o m _ s t a t e random\_state random_state:随机种子设置,默认未设置,可设置为一个整数,即可在多次试验时得到相同结果(在相同的训练集和测试集的情况下)

示例代码

algo = SVD(n_factors=5, n_epochs=20, lr_all=0.007, reg_all=0.002, verbose=False, init_mean=0.1, init_std_dev=0)

SVDpp算法

依然是Koren提出的,考虑了隐性反馈的SVDpp算法
r ^ u i = μ + b u + b i + q i T ( p u + ∣ I u ∣ − 1 2 ∑ j ∈ I u y j ) \hat{r}_{ui} = \mu + b_u + b_i + q_i^T\left(p_u + |I_u|^{-\frac{1}{2}} \sum_{j \in I_u}y_j\right) r^ui=μ+bu+bi+qiTpu+Iu21jIuyj
和SVD相比,多了两个参数
30. l r _ y j lr\_yj lr_yj:设置 y j y_j yj的学习速率,可覆盖 l r _ a l l lr\_all lr_all,默认未设置
31. r e g _ y j reg\_yj reg_yj:设置 y j y_j yj的学习速率,可覆盖 r e g _ a l l reg\_all reg_all,默认未设置

示例代码

algo = SVDpp(n_factors=5, n_epochs=20, lr_all=0.007, reg_all=0.002, verbose=False, init_mean=0.1, init_std_dev=0)

NMF算法

非负矩阵分解,即要求p矩阵和q矩阵都是正的
r ^ u i = q i T p u , \hat{r}_{ui} = q_i^Tp_u, r^ui=qiTpu,
和SVD相比,多了两个参数
32. i n i t _ l o w init\_low init_low:设置初始值的下限,默认为0
33. i n i t _ h i g h init\_high init_high:设置初始值的上限,默认为1

示例代码

algo = NMF(n_factors=5, n_epochs=20, lr_all=0.007, reg_all=0.002, verbose=False, init_mean=0.1, init_std_dev=0)

SlopeOne算法

r ^ u i = μ u + 1 ∣ R i ( u ) ∣ ∑ j ∈ R i ( u ) dev ( i , j ) \hat{r}_{ui} = \mu_u + \frac{1}{ |R_i(u)|} \sum\limits_{j \in R_i(u)} \text{dev}(i, j) r^ui=μu+Ri(u)1jRi(u)dev(i,j)
dev ( i , j ) = 1 ∣ U i j ∣ ∑ u ∈ U i j r u i − r u j \text{dev}(i, j) = \frac{1}{ |U_{ij}|}\sum\limits_{u \in U_{ij}} r_{ui} - r_{uj} dev(i,j)=Uij1uUijruiruj

示例代码

algo = SlopeOne()

CoClustering算法

r ^ u i = C u i ‾ + ( μ u − C u ‾ ) + ( μ i − C i ‾ ) \hat{r}_{ui} = \overline{C_{ui}} + (\mu_u - \overline{C_u}) + (\mu_i- \overline{C_i}) r^ui=Cui+(μuCu)+(μiCi)

  1. n _ c l t r _ u n\_cltr\_u n_cltr_u:用户类的数量,默认为3
  2. n _ c l t r _ i n\_cltr\_i n_cltr_i:项目类的数量,默认为3
  3. n _ e p o c h s n\_epochs n_epochs:迭代次数,默认为20
  4. r a n d o m _ s t a t e random\_state random_state:随机种子设置,默认未设置,可设置为一个整数,即可在多次试验时得到相同结果(在相同的训练集和测试集的情况下)

Precision、Recall、MAP和NDCG的计算

#!/usr/bin/python
# -*- coding: utf-8 -*-
from surprise import KNNBasic
from surprise import Dataset
import pandas as pd
from surprise import Reader
import numpy as np
from surprise.model_selection import KFold
import math

num_item = 80
reader = Reader(line_format='user item rating', sep=',')
data = Dataset.load_from_file('rating2.txt', reader=reader)
kf = KFold(n_splits=5)
sim_options = {'name': 'cosine',
               'user_based': False
               }
algo = KNNBasic(sim_options=sim_options, verbose=False)
precision = 0.0
recall = 0.0
map = 0.0
ndcg = 0.0
topk = 3
for trainset, testset in kf.split(data):
    algo.fit(trainset)
    fenmu = pd.DataFrame(np.array(testset)[:, 0]).drop_duplicates().shape[0]
    real = [[] for i in range(fenmu)]
    sor = [[] for i in range(fenmu)]
    hit = 0
    score = 0.0
    dcg = 0.0
    dic = {}
    m = 0
    for i in range(len(testset)):
        if int(testset[i][0]) not in dic:
            dic[int(testset[i][0])] = m
            m += 1
            ls = []
            real[m - 1].append(int(testset[i][1]))
            for j in range(num_item):
                uid = str(testset[i][0])
                iid = str(j)
                pred = algo.predict(uid, iid)
                ls.append([pred[3], j])
            ls = sorted(ls, key=lambda x: x[0], reverse=True)
            for s in range(topk):
                sor[m-1].append(int(ls[s][1]))
        else:
            real[dic[int(testset[i][0])]].append(int(testset[i][1]))
    for i in range(fenmu):
        idcg = 0.0
        ap_score = 0.0
        ap = 0.0
        cg = 0.0
        for y in range(topk):
            if sor[i][y] in real[i]:
                ap_score += 1
                ap += ap_score / (y + 1)
                cg += 1 / math.log((y + 2), 2)
        score += ap / min(len(real[i]), topk)
        for z in range(int(ap_score)):
            idcg += 1 / math.log((z + 2), 2)
        if idcg > 0:
            dcg += cg / idcg
        recall += ap_score / (len(real[i]) * fenmu)
        precision += ap_score / (topk * fenmu)
    map += float(score) / fenmu
    ndcg += float(dcg) / fenmu
print 'precision ' + str(precision / 5.0)
print 'recall ' + str(recall / 5.0)
print 'map ' + str(map / 5.0)
print 'ndcg ' + str(ndcg / 5.0)

你可能感兴趣的:(推荐系统)