推荐实践(4):从KNNBasic() 了解整个算法部分的结构梳理

谢谢朋友们的陪伴来一块儿学习,这周的文章咱们了解一下 KNNBasic() 算法如何实现,以及搞清楚整个算法的结构梳理。node

首先在 surprise 中,全部的算法都从一个基类 AlgoBase() 中继承而来,这个类设置了你们公有的功能函数的接口,如 fit() 函数用来让算法对数据集进行拟合,predict() 函数对给定的 user 和 item 进行评分预测。算法

图片 algo base 类ruby

以 KNN 系列算法为例,首先定义一个名为 knns 的库,其中包含了多个 KNN 系列的算法,例如 KNNBasic(),KNNWithMeans(),KNNWithZScore() 等。微信

咱们在前面推荐理论系列文章中介绍了基于邻域的思路,KNN 算法本质上就是基于邻域的协同过滤方法,而这些变种则是对类似度的不一样应用,好比对类似度进行归一化,对用户的平均打分标准进行归一化等。机器学习

考虑到 KNN 有两种对称的子方法:基于用户的协同过滤和基于物品的协同过滤。两种方法在计算上是对称的,因此 KNN 系列的方法有一个基类是 SymmetricAlgo(),这个类继承了前面提到的 AlgoBase() 类,是全部 KNN 类的父类。函数

class SymmetricAlgo(AlgoBase): """This is an abstract class aimed to ease the use of symmetric algorithms.
A symmetric algorithm is an algorithm that can can be based on users or on items indifferently, e.g. all the algorithms in this module.
When the algo is user-based x denotes a user and y an item. Else, it's reversed. """
def __init__(self, sim_options={}, verbose=True, **kwargs):
AlgoBase.__init__(self, sim_options=sim_options, **kwargs) self.verbose = verbose
def fit(self, trainset):
AlgoBase.fit(self, trainset)
ub = self.sim_options['user_based'] self.n_x = self.trainset.n_users if ub else self.trainset.n_items self.n_y = self.trainset.n_items if ub else self.trainset.n_users self.xr = self.trainset.ur if ub else self.trainset.ir self.yr = self.trainset.ir if ub else self.trainset.ur
return self
def switch(self, u_stuff, i_stuff): """Return x_stuff and y_stuff depending on the user_based field."""
if self.sim_options['user_based']: return u_stuff, i_stuff else: return i_stuff, u_stuff

咱们能够看到 SymmetricAlgo() 类中重写了 fit() 函数,在父函数 fit() 函数的基础上,还加上了一个功能就是判断是基于用户类似仍是基于物品类似。学习

获得判断结果 ub 后,fit() 函数利用 ub 的值对数据集从新进行处理,至关于经过这个函数后,咱们不用再区分基于用户或者基于物品的区别,而只须要直接对获得的 self.n_x,self.n_y 进行处理。测试

这里咱们能够看到还有一个 switch() 函数,内容就是根据 self.sim_options['user_based'] 的值来选择是否翻转给定的两组输入。其实和前面的 fit() 函数思想一致,只不过一个是为了训练集的数据处理,一个是为了测试集的数据处理。this

接下来咱们以 KNNBasic() 函数为例,介绍一下算法实现的具体思路,具体的计算方法本周的文章就不深刻,主要是捋清楚算法运行的流程和结构。spa

class KNNBasic(SymmetricalAlgo): """ Args: k(int): The (max) number of neighbors to take into account for aggregation (see :ref:`this note <actual_k_note>`). Default is ``40``. min_k(int): The minimum number of neighbors to take into account for aggregation. If there are not enough neighbors, the prediction is set to the global mean of all ratings. Default is ``1``. sim_options(dict): A dictionary of options for the similarity measure. See :ref:`similarity_measures_configuration` for accepted options. verbose(bool): Whether to print trace messages of bias estimation, similarity, etc. Default is True. """ def __init__(self, k=40, min_k=1, sim_options={}, verbose=True, **kwargs): SymmetricalAlgo.__init__(self, sim_options=sim_options, verbose=verbose, **kwargs) self.k = k self.min_k = min_k
def fit(self, trainset): SymmetricalAlgo.fit(self, trainset) 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.')
x, y = self.switch(u, i)
neighbors = [(self.sim[x, x2], r) for x2, r in self.yr[y]] k_neighbors = heapq.nlargest(self.k, neighbors, key=lambda t: t[0])
sum_sim = sum_ratings = actual_k = 0 for (sim, r) in k_neighbors: if sim > 0: sum_sim += sim sum_ratings += sim*r actual_k += 1
if actual_k < self.min_k: raise PredictionImpossible('Not enough neighbors')
est = sum_ratings / sum_sim
details = {'actual_k': actual_k} return est, details

首先能够看到在这个子类中再次重写了 fit() 函数,从 fit() 函数内部经过 self 去调用父类中的 self.compute_similarities() 函数,这个函数被定义在 AlgoBase() 类中,这样子对算法自己而言只须要直接调用 fit() 就能够完成拟合过程,将具体的计算抽象了出去。

而 self.sim 则是返回的计算出来的类似性矩阵,给出了用户两两之间的类似性。

再看 estimate() 函数须要输入目标用户 u 和目标评分项 i,最终返回一个预测的评分结果。在进行预测以前先调用父类的 switch() 函数,对基于用户仍是基于物品进行肯定。

具体的计算方式则是按照给定的 k 值,获得与输入的给定用户 u 最类似的 k 个用户,具体的形式是一个列表,列表中有 k 个元组,每一个元组有两项,分别是类似用户和该用户对物品 i 的评分。

经过获得最类似的 k 个用户及其评分,目标用户对目标物品评分的预测就很简单了,最直接的就是让和每一个用户的类似性乘以该用户的评分,最终求和取平均则是预测结果。

经过上面的函数进行替代目标的 KNNBasic(),咱们的模型能够完整的运行一个 demo。

总结

这篇文章很简单,可是思路很清晰,能够有效的看清源码中的调用结构。下篇文章咱们再完善基类 AlgoBase() 的各个方法,将来再继续丰富其它各种算法,欢迎朋友们和我一块儿交流学习,共同进步。

推荐实践(1):从零开始写一个本身的推荐算法库

推荐实践(2):数据集的载入与切割

推荐实践(3):调用算法接口实现一个 demo


若是喜欢做者,请关注我吧 ❤️

本文分享自微信公众号 - 机器学习与推荐系统(ml-recsys)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索