一篇长文学懂入门推荐算法库:surprise( 三 )


说这一段的意思是想告诉大家,在阅读源码,或者仿写源码时,需要把握住自己在这一步骤的核心思路,屏蔽掉暂时不重要,或者对当前步骤不是很关键的内容 。哪怕只是调用各个接口来完成自己当前步骤的任务,只要你把握住了它们的交互关系,处理流程就 OK 。
 
4 再补充忽略的算法部分打开上面的 fit_and_score 函数,我们可以看到对 algo 的使用只有两句代码,一个是训练阶段的 algo.fit(trainset),一个是测试阶段的 algo.test(testset) 。前者是对算法在训练集上进行拟合,而后者是对算法在测试集上进行测试 。
针对 knn 的算法而言,算法的实现上是比较简单的 。主要包括了一个 fit 方法,和一个 estimate 方法 。这里主要是需要对类间的继承关系进行梳理 。具体的实现细节可以看【第四篇文章:推荐实践(4):从KNNBasic() 了解整个算法部分的结构梳理】 。
在 surprise 中,所有的算法类都继承于一个父类:algo_base,这个类中抽象出来了一些子类都容易用到的方法,有的给出了具体的实现,有的只是抽象出了一个接口,如 fit 方法,在 algo_base 中和其子类 knn 中都有定义 。具体的 algo_base 的组成可以在【第五篇文章:推荐实践(5):Algo_base() 类的功能介绍】中进行了解 。
这里帮助大家再进一步梳理算法类之间的关系 。对 algo_base 而言,其是一切 surprise 中的算法的父类 。那么以 knn 算法为例,这里就不是由 knn 直接继承 algo_base 了,而是先有一个 SymmetricalAlgo 类继承 algo_base,然后由对应的 knn 类继承 SymmetricalAlgo 类 。
这里的 SymmetricalAlgo 的主要工作是处理 knn 中经常需要考虑的一个问题 。基于用户还是基于物品的协同过滤 。SymmetricalAlgo 中主要有两个方法,一个是前面一直提到的 fit 方法,这里的 fit 方法主要是对 n_x 和 n_y 等做出调整,判断是基于用户相似性还是基于物品相似性,然后调整对应的数据指标 。另外一个方法则是 switch 方法,顾名思义,switch 方法同样是调整对应的指标,调整的依据则是判断是基于用户相似性还是物品相似性,调整后的结果用来在测试时使用 。
这里用语言描述可能稍微有点绕口,大家看一下【第四篇文章:推荐实践(4):从KNNBasic() 了解整个算法部分的结构梳理】结合源码与文档,就可以非常容易理解了 。
关于其它的算法我们就不一一展开去分析了,授人以鱼不如授人以渔,通过这一个算法的分析,我们就可以明白如何分析算法的具体实现了,也就可以很容易的了解其它算法的功能组成 。
在解决了算法部分的问题后,我们了解了算法之间类的继承关系,以及父类提供的接口和可以使用的方法 。接下来即使再写其它算法,我们也可以仿照这个思路,保证这些接口的基础上,实现自己的算法代码 。
 
5 指标测量和数据集的格式我们在前面第三部分提到,对于预测结果如何进行指标计算的内容可以暂时忽略,这里我们就补充这一部分内容 。之所以选择在这里进行补充,我们可以看到,通过前面几步,已经搭建起来了一个基本完整的 demo 。其中只有少量内容调用了源码,对预测结果的指标计算就是其中一个 。
指标测量部分唯一需要注意的是预测结果的返回形式,也就是前面 Algo_base 中 test 方法返回的结果:predictions 。
然后就是对计算的几类指标的定义需要了解:MSE,RMSE,MAE,FCP 。前三类都是比较常见的指标,FCP 在源码中给出了一篇 paper 作为 reference,其中的定义也很清晰,我们参考 paper 中的定义便可 。
具体的计算方法知道了以后,代码的实现就是很简单的了 。利用 numpy 可以快速的计算出想要的结果,具体的指标定义,代码实现的介绍,以及关于一些其它常见的指标该如何测量我也提供了一些思考,都可以在【第六篇文章:推荐实践(6):accuracy()--surprise 支持哪些指标测量呢?】中看到 。感兴趣的朋友可以借鉴一下 。
至此,整个 demo 可以运行完毕 。但是还有一个前面留下的坑需要填一下,就是前面提到对于数据集的格式的问题 。在前面提到的时候,我们说这些内容可以忽略过去 。但是,其实在 surprise 中对数据格式的定义还是很值得学习的 。
surprise 定义了一个 Trainset 类,用来储存所有与数据集相关的内容 。比如用户数量,物品数量,评分数量等比较简单的内容,以及将数据集中的 user ID 转化为新定义的数据结构中的内部编号 inner ID,获取全局平均评分等稍复杂的功能 。
通过定义一个数据集的类,可以对数据集进行一次处理,然后需要相应指标时只需直接调用 。剩下了很多的运行时间,而且让代码更简洁 。具体的进一步的分析可以阅读【第七篇文章:推荐实践(7):trainset.Trainset() 通过调整数据集让代码更优雅】 。


推荐阅读