如何构建自己的预测算法¶
本页介绍如何使用 Surprise 构建自定义预测算法。
基础知识¶
想亲手实践一下吗?太棒了。
创建自己的预测算法非常简单:算法只是一个派生自 AlgoBase
的类,它有一个 estimate
方法。这个方法是由 predict()
方法调用的。它接受一个 **内部** 用户 ID 和一个 **内部** 项目 ID(参见 此说明),并返回估计评分 \(\hat{r}_{ui}\)
examples/building_custom_algorithms/most_basic_algorithm.py
¶from surprise import AlgoBase, 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,无论用户和项目是什么。
如果你想存储关于预测的额外信息,你也可以返回一个包含指定详细信息的字典
def estimate(self, u, i):
details = {
'info1' : 'That was',
'info2' : 'easy stuff :)'
}
return 3, details
这个字典将作为 details
字段存储在 prediction
中,并可用于 后续分析。
fit
方法¶
现在,让我们创建一个稍微聪明一点的算法,它预测训练集(trainset)中所有评分的平均值。由于这是一个不依赖于当前用户或项目的常量值,我们宁愿一次性计算好。这可以通过定义 fit
方法来实现
examples/building_custom_algorithms/most_basic_algorithm2.py
¶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)
这样的表达式。
trainset
属性¶
一旦基类 fit()
方法返回,所有关于当前训练集(评分值等)所需的信息都存储在 self.trainset
属性中。这是一个 Trainset
对象,包含许多对预测有用的属性和方法。
为了说明其用法,让我们创建一个算法,它预测所有评分的平均值、用户平均评分和项目平均评分之间的平均值
examples/building_custom_algorithms/mean_rating_user_item.py
¶ 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
异常。你需要先导入它
from surprise import PredictionImpossible
这个异常会被 predict()
方法捕获,估计值 \(\hat{r}_{ui}\) 会根据可以重写的 default_prediction()
方法设置。默认情况下,它返回训练集(trainset)中所有评分的平均值。
使用相似度和基线¶
如果你的算法使用了相似性度量或基线估计,你需要在 __init__
方法中接受 bsl_options
和 sim_options
作为参数,并将它们传递给基类。关于如何使用这些参数,请参见 使用预测算法 部分。
方法 compute_baselines()
和 compute_similarities()
可以在 fit
方法中调用(或在其他任何地方)。
examples/building_custom_algorithms/.with_baselines_or_sim.py
¶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 unknown.")
# 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(f"user {v} with sim {sim_uv:1.2f}")
# ... Aaaaand return the baseline estimate anyway ;)
bsl = self.trainset.global_mean + self.bu[u] + self.bi[i]
return bsl
欢迎随意探索 prediction_algorithms 包的源代码,以了解可以实现哪些功能。