本节将会向你展示如何通过surprise建立一个个性化的预测算法。
基础
想自己来试一下吗?
构建你自己的预测算法是很简单的:每一个算法都只是从AlgoBase类中派生出来的,它有一个自己的被称作是predict()的评估方法。它需要传递进去 一个inner user id , 一个inner item id ,查看这里,返回的是一个评估率 r^ui:
实例代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21from surprise import AlgoBase
from surprise import Dataset
from surprise.model_selection import cross_validate
class MyOwnAlgorithm(AlgoBase):
def __init__(self):
# Always call base method before doing anything.
AlgoBase.__init__(self)
def estimate(self, u, i):
return 3
data = Dataset.load_builtin('ml-100k')
algo = MyOwnAlgorithm()
cross_validate(algo, data, verbose=True)
这个算法是我们可以想到的最糟糕的:它只是预测评分为3,而不管用户和项目。
如果你想存储关于预测的额外信息,你也可以返回一个带有给定细节的字典:1
2
3
4
5def estimate(self, u, i):
details = {'info1' : 'That was',
'info2' : 'easy stuff :)'}
return 3, details
这个字典会被作为details字段存储到prediction中,以便后面分析用到。
fit 方法
我们现在来试一下构建一个稍微聪明点的算法——他能预测所有训练集中的评分数据的平均值。
因为他只是一个不依赖于现有的用户或u物品的常量,所以,我们可以只计算一次。通过定义fit方法即可完成:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class MyOwnAlgorithm(AlgoBase):
def __init__(self):
# Always call base method before doing anything.
AlgoBase.__init__(self)
def fit(self, trainset):
# Here again: call base method before doing anything.
AlgoBase.fit(self, trainset)
# Compute the average rating. We might as well use the
# trainset.global_mean attribute ;)
self.the_mean = np.mean([r for (_, _, r) in
self.trainset.all_ratings()])
return self
def estimate(self, u, i):
return self.the_mean
fit方法被称为例如 通过在交叉验证过程的每个折叠处的cross_validate函数(但是您也可以自己调用它)。 在做任何事之前,你应该调用基类fit()方法。
说明:fit()方法返回self,所以,这也说明可以使用类似于algo.fit(trainset).test(testset) 的表达式。
训练集的属性
当基类的fit()方法返回后,你需要了解的关于现有的训练集的信息都存储在self.trainset的属性中了。它是一个有很多有关预测的属性和方法的对象。
举例说明它的用法,我们会做一个预测所有评分的均值、所有用户的评分均值和物品评分的平均值的均值。(有语病)
代码:1
2
3
4
5
6
7
8
9
10
11
12
13def 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
说明:注意,在fit方法中计算所有用户的均值可能是一个更好的主意,从而避免多次重复相同的计算。
何时进行预测
是否能进行预测是你来决定的。如果你觉得可以进行预测了,那就抛出PredictionImpossible异常,但是你要先导入这个:1
from surprise import PredictionImpossible
这个异常能被predict()方法捕获,估计量r^ui将被传递到全局变量——评分u的均值上。
使用相似度量和基准估计
如果你的算法使用相似度度量或者基准估计,那么你需要将bsl_options和sim_options传递进去作为初始化方法init的参数,并沿着Base class 进行传递。在这节查看如何使用使用这些参数。
compute_baselines()方法和compute_similarities()方法能被fit方法回调(或者说其他的任何地方):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class 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)
# Compute baselines and similarities
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.')
# Compute similarities between u and v, where v describes all other
# users that have also rated item i.
neighbors = [(v, self.sim[u, v]) for (v, r) in self.trainset.ir[i]]
# Sort these neighbors by similarity
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))
# ... Aaaaand return the baseline estimate anyway ;)
随意探索一下prediction_algorithms包源代码,以了解可以做什么。