surprise框架之如何建立你自己的预测算法(翻译)

本节将会向你展示如何通过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
21
from 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
5
def 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
22
class 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
13
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

说明:注意,在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
33
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)

# 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包源代码,以了解可以做什么。