本节介绍一下如何搭建自己的模型
搭建自己的预测系统非常简单,使用一个从AlgoBase衍生过来的算法,并且含有estimate方法。
使用内置的user id和item id, 返回预测的评分值r^ui:
import os
from surprise import Reader
from surprise import AlgoBase
from surprise import Dataset
from surprise.model_selection import cross_validate
from surprise import KNNBaseline
class MyOwnAlgorithm(AlgoBase):
def __init__(self):
KNNBaseline.__init__(self)
def estimate(self, u, i):
return 3
file_path = os.path.expanduser('ml-100k/u.data')
reader = Reader(line_format='user item rating timestamp', sep=' ')
data = Dataset.load_from_file(file_path, reader=reader)
algo = MyOwnAlgorithm()
cross_validate(algo, data, verbose = True)
Evaluating RMSE, MAE of algorithm MyOwnAlgorithm on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 1.2502 1.2488 1.2378 1.2408 1.2430 1.2441 0.0047
MAE (testset) 1.0095 1.0059 0.9937 1.0000 0.9992 1.0017 0.0055
Fit time 0.00 0.02 0.02 0.02 0.02 0.01 0.01
Test time 0.07 0.07 0.07 0.07 0.07 0.07 0.00
{'test_rmse': array([1.25015999, 1.24879942, 1.23780047, 1.24084649, 1.24304063]),
'test_mae': array([1.0095 , 1.0059 , 0.99365, 1. , 0.99925]),
'fit_time': (0.0,
0.01707291603088379,
0.017518997192382812,
0.017546653747558594,
0.017040252685546875),
'test_time': (0.07319474220275879,
0.07066011428833008,
0.07072019577026367,
0.0691843032836914,
0.07066011428833008)}
现在这个算法很简单,只能返回3,没有考虑users和items的关系
如果想要增加预测的信息,可以返回一个包含详细信息的字典,这个字典将会保存到 prediction 中去,details可以在稍后的分析中使用
def estimate(self, u, i):
details = {'info1' : 'That was',
'info2' : 'easy stuff :)'}
return 3, details
现在让我们设计一个稍微聪明的算法,可以预测trainset的平均评分值,只一个不以来user和item的常数值,我们可以通过fit函数一次计算所有的评分。
可以在交叉验证过程中 cross_validate 会调用fit函数,在做任何事的时候,要首先调用fit函数,fit函数返回的是self,所以可以使用algo.fit(trainset).test(testset).
class MyOwnAlgorithm(AlgoBase):
def __init__(self):
AlgoBase.__init__(self)
def fit(self, trainset):
AlgoBase.fit(self, trainset)
self.the_mean = np.mean([r for (_, _, r) in
self.trainset.all_ratings()])
return self
def estimate(self, u, i):
return self.the_mean
trainset属性
当fit()函数返回时,我们需要的所有的关于trainset的信息都会保存在self.trainset属性中,它是一个Trainset对象,包含许多关于预测的参数和方法。
为了展示它的使用方法,让我们来设计一个算法来计算所有评分的平均值,user的平均评分值和item的平均评分值。
def estimate(self, u, i):
sum_means = self.trainset.global_mean
div = 1
if self.trainset.knows_user(u):
sum_means += np.mean([r for (_, r) in self.trainset.ur[u]])
div += 1
if self.trainset.knows_item(i):
sum_means += np.mean([r for (_, r) in self.trainset.ir[i]])
div += 1
return sum_means / div
判断算法是否得到prediction是至关重要的,如果prediction是不可用的,可以输出PredictionImpossible异常,
需要导入 from surprise import PredictionImpossible
这个异常会被predict()函数调用,预测值r^ui会通过default_prediction()[可以重载]来计算,返回的是trainset所有评分的平均值。
使用相似度和baselines
如果算法想要使用相似度度量或者baseline预测,需要在
init 方法中添加 bsl_options 和 sim_options 两个参数,将它们传递给Base Class,
compute_baselines() 和 compute_similarities()方法可以在fit()方法或者在其他地方中调用。
class MyOwnAlgorithm(AlgoBase):
def __init__(self, sim_options={}, bsl_options={}):
AlgoBase.__init__(self, sim_options=sim_options,
bsl_options=bsl_options)
def fit(self, trainset):
AlgoBase.fit(self, trainset)
self.bu, self.bi = self.compute_baselines()
self.sim = self.compute_similarities()
return self
def estimate(self, u, i):
if not (self.trainset.knows_user(u) and self.trainset.knows_item(i)):
raise PredictionImpossible('User and/or item is unkown.')
neighbors = [(v, self.sim[u, v]) for (v, r) in self.trainset.ir[i]]
neighbors = sorted(neighbors, key=lambda x: x[1], reverse=True)
print('The 3 nearest neighbors of user', str(u), 'are:')
for v, sim_uv in neighbors[:3]:
print('user {0:} with sim {1:1.2f}'.format(v, sim_uv))