您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Python

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

时间:2020-05-28 10:57:47  来源:  作者:

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

不知不觉写完了七篇文章来分析这个推荐算法库 surprise,基本上我们从头到尾所有代码都自己完成,并且可以成功 run 起来一个基于邻域的协同过滤算法。是哪个算法这件事其实我觉得不重要,surprise 支持的每个算法本身思路并不复杂,代码也不晦涩难懂,我们主要的目的是理解它的架构,学习框架各个部分的交互。

按理之前的文章写完后,这个系列就可以算是初步完成了。但是我自己回头再看的时候,发现七篇文章缺乏一个总体性的视角,这可能会导致有的同学读起来觉得前后不太连贯。

文章的顺序是按照我自己在学习源码过程中的顺序编排的,但是如果没有告诉读者我的思维过程,读者可能不能很好的理解这其中的关系。

所以这篇文章是想从一个整体的视角,以我当初的思路为主线进行介绍,观察并思考如何一步一步的让模型 run 起来。至于某些具体的细节部分,我们给出之前写的对应文章链接,大家可以回头再去看相应的文章来了解。

 

1 先搞个模型跑起来

我们首先从一个总体性的代码看一下,很简单的几行代码,开始我们的 surprise 之旅。

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

这里需要导入的部分,我都已经重写过了,但是大家可以在自己本地的代码上尝试一下,直接利用 surprise 库就可以运行一个简单的 KNN 算法,本质上也就是基于邻域的协同过滤算法。按照我在代码上标注出来的红线部分,分别给对应的四个 import 模块,前面加上 surpris. 的路径就可以从 surprise 中进行导入。

这里需要提一下,我用的是自己之前已经在 movielens 官网下载的数据集,大家可以自己直接下载,也可以在网上找一下教程,surprise 支持自动下载 movielens 的数据集。

接下来我们只看 surprise_code 函数,这个函数就是我们需要学习的所有内容,从这个例子开始,我们要去一步步深扒 surprise 的执行过程。

def surprise_code: reader = Reader(line_format="user item rating", sep=',', skip_lines=1) data = Dataset.load_from_file('./ml-latest-small/ratings.csv', reader)
 algo = KNNBasic perf = cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=2, verbose=0) for keys in perf: print(keys, ": ", perf[keys])

对核心的 surprise_code 函数,可以分为两个部分来看:数据载入,算法执行并检测结果。

数据载入,由 Reader 和 Dataset 两个类来提供功能,具体的思路是由 Reader 提供读取数据的格式,然后 Dataset 按照 Reader 的设置来完成对数据的载入。

算法执行并检测结果,这里由一个 cross_validate 来完成,提前导入需要执行的算法并实例化,然后将数据,算法,要检测的指标等都传入 cross_validate,它会完成对算法的训练拟合,然后进行预测结果,再对结果进行验证,最终返回目标的检测指标对应的结果。

所以我们可以看到直接调用接口是很容易跑起来一个模型的,仅这么几行简单的代码就可以将一个算法完整的运行起来。但是如果要深入到代码的执行细节上,就需要捋顺它们的关系,然后抽丝剥茧一点点展开。

在【第一篇文章:推荐实践(1):从零开始写一个自己的推荐算法库】中,我们将上面的算法执行并检测结果分成了两部分,也就是将整个工作流程划分为三部分:数据载入,算法设计,结果评估。这样子更加细化的一步当然没问题,逻辑上这样子也更容易理解。

现在捋顺了算法的执行思路以后,我们开始从数据集的载入开始去分析源码。

trick:在正式细节代码前,分享一些关于我学习源码的方法,不一定适合所有人,也不一定适合所有源码,仅供大家参考:

首先,要像我们一样完成一个非常简单的 demo,如这一小节的标题一样,先搞个模型跑起来。至少我们得会用它,能用起来,这样子我们才能分析下去。

其次,我们再捋顺模型的流程,划分为几个主要的部分,按照这个逻辑顺序,从最初的部分开始,尝试自己完成该模块的功能。导入自己写的部分,替代掉源码的那一部分,看看前面的 demo 能不能继续运行,检查结果是否一致。

最后,不要在分析的时候,拘泥于细节,先完成该模块的主要功能。如果这部分功能与其它模块相关,可以先导入相关的模块,直接使用。不要尝试一步将某一个模块写的尽善尽美。

这三个提到的内容,我们在接下来的文章可以再进一步体会。

 

2 从第一部分的数据载入开始

我们前面分析了数据载入部分,由 Reader 和 Dataset 两个类来提供功能。接下来要做的就是捋顺这部分内容,然后自己尝试写出来对应的模块,并且替代进去,看看我们前面的代码能不能继续正常运行。

当我们到了这一步的时候,首先就是打开这两个模块,看一看代码,了解一下它们的功能。在【第二篇文章:推荐实践(2):数据集的载入与切割】中,对这两部分的内容进行了仔细分析,我在这里就总体性的介绍一下,不深入到代码细节上去了。

reader = Reader(line_format="user item rating", sep=',', skip_lines=1)

对于这个 Reader 类,主要的功能是设置一个读取器。从 Reader 的使用也可以看出来,要求的输入是每行的格式,每行的分隔符,要忽略的行数。

从这个类实例时的输入上,我们可以判断出来,这个 Reader 类的作用是构造一个读取器对象 reader,这个读取器 reader 包含了一些如何去读数据的属性。比如 reader 知道每行的数据是按照 “user item rating” 来分布的,知道每行数据由符号 "," 分割开,知道第一行的数据应该被跳过。

所以我们在构建了这个 reader 以后,就可以将它传给 Dataset 类,来辅助我们从数据集中,按照我们想要的格式读取出来数据内容。

data = Dataset.load_from_file('./ml-latest-small/ratings.csv', reader)

由于这里我们选择了使用自己已经下载的数据集,调用的就是 Dataset.load_from_file 方法。可以看到的是,这个方法的输入有两个参数,第一个是数据集的路径,第二个就是刚刚实例化的读取器 reader。

所以这个 load_from_file 在读取数据时,就会按照 reader 的定义的格式来读取,最终返回一个自定义的数据格式。其实如果看了代码,我们可以看到这里返回的数据格式是:dataset.DatasetAutoFolds,但是正如我们前面说的,对于源码不要陷入细节。我们知道这两步对原始的数据集文件进行了处理,得到了后续可以处理的数据格式,就 OK 啦。

 

3 进行结果交叉验证

在完成数据集的载入以后,我们选择的是利用 cross_validate 执行算法并交叉检验。这一部分的内容,我们分为两部分去介绍。

首先忽略掉算法的实现,直接调用算法的接口。这也是一个很实用的 trick,适当的时候忽略掉一些代码实现,即使你接下来要用到它,也可以直接调源码的接口。所以我们这里忽略了 KNN 算法的实现,直接调用它来实现训练拟合以及后续在测试集上的预测。

那么 cross_validate 里面是什么呢?我们看一下 validate 中的内容:

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

validate 中有两个函数,分别是 cross_validate 和 fit_and_score。我们简单的介绍一下它们的功能,让大家可以继续没有障碍的阅读当前这篇文章,至于具体的代码和功能分析可以看【第三篇文章:推荐实践(3):调用算法接口实现一个 demo】。

algo = KNNBasicperf = cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=2, verbose=0)

可以看到 cross_validate 是被调用的外部接口,很容易可以猜到,fit_and_score 是在 cross_validate 中被调用的。

对 cross_validate 而言,它的输入有算法对象,数据集,需要测量的指标,交叉验证的次数等。这里简单的介绍一下它的内部逻辑。它对输入的数据 data,分成 cv 份,然后每次选择其中一份作为测试集,其余的作为训练集。在数据集划分完后,对它们分别调用 fit_and_score,去进行算法拟合。

这里注意一个小细节,对数据集的划分不是静态全部划分完,然后分别在数据集上进行训练和验证,而是利用输入的 data 构造一个生成器,每次抛出一组划分完的结果。

对 fit_and_score 函数,它对输入的算法在输入的训练集上进行拟合,然后在输入的测试集上进行验证,再计算需要的指标。

在进行到这里的时候,同样忽略掉对预测结果进行指标测量的步骤,直接调用 surprise 中的 accuracy 来进行处理。当然到后面我们会补充这些内容,这里要注意的重点是如何进行交叉验证。

这一部分内容的核心是,在有了我们第 2 节输入的数据后,该如何进行数据集的划分以及如何进行算法的训练和验证。所以我们关注的重点是数据集在进行 k 折交叉验证时如何划分,又如何调用接口完成算法在数据集上的训练和测试。

说这一段的意思是想告诉大家,在阅读源码,或者仿写源码时,需要把握住自己在这一步骤的核心思路,屏蔽掉暂时不重要,或者对当前步骤不是很关键的内容。哪怕只是调用各个接口来完成自己当前步骤的任务,只要你把握住了它们的交互关系,处理流程就 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() 通过调整数据集让代码更优雅】。

 

总结

这篇文章本身算是一篇疏导性的总结,结合之前的文章,大家可以自己复现出来一个 surprise 中的 knn 算法,而且其它部分的接口也介绍的非常清晰。对于想要在 surprise 上继续学习其它算法源码的朋友,可以轻松的按照我们之前的分析基础,继续自己的学习;对于想要进行魔改,加入一些自己想要的算法的朋友,目前的介绍也已经清晰的解释了各个接口,大家对应来封装自己的算法就可以了。

另外一方面,本篇文章也从如何阅读源码的角度为大家分享了一些我自己的经验,或许有些地方可以帮助到大家。从如何开始阅读源码,到从一个小 demo 逐渐剖析,暂时忽略掉一些不重要的模块,一步步的完成自己的代码对源码的替代,以写代读。

更严格的讲,这种以写代读比较适合代码量不超过一万行的小型库。这种级别的代码量,我们可以通过自己完整的写一遍来加深理解。但是更高量级的代码量,就不太适合写了,还是以梳理逻辑架构为主了。



Tags:surprise   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
不知不觉写完了七篇文章来分析这个推荐算法库 surprise,基本上我们从头到尾所有代码都自己完成,并且可以成功 run 起来一个基于邻域的协同过滤算法。是哪个算法这件事其实我...【详细内容】
2020-05-28  Tags: surprise  点击:(38)  评论:(0)  加入收藏
▌简易百科推荐
大家好,我是菜鸟哥,今天跟大家一起聊一下Python4的话题! 从2020年的1月1号开始,Python官方正式的停止了对于Python2的维护。Python也正式的进入了Python3的时代。而随着时间的...【详细内容】
2021-12-28  菜鸟学python    Tags:Python4   点击:(1)  评论:(0)  加入收藏
学习Python的初衷是因为它的实践的便捷性,几乎计算机上能完成的各种操作都能在Python上找到解决途径。平时工作需要在线学习。而在线学习的复杂性经常让人抓狂。费时费力且效...【详细内容】
2021-12-28  风度翩翩的Python    Tags:Python   点击:(1)  评论:(0)  加入收藏
Python 是一个很棒的语言。它是世界上发展最快的编程语言之一。它一次又一次地证明了在开发人员职位中和跨行业的数据科学职位中的实用性。整个 Python 及其库的生态系统使...【详细内容】
2021-12-27  IT资料库    Tags:Python 库   点击:(2)  评论:(0)  加入收藏
菜单驱动程序简介菜单驱动程序是通过显示选项列表从用户那里获取输入并允许用户从选项列表中选择输入的程序。菜单驱动程序的一个简单示例是 ATM(自动取款机)。在交易的情况下...【详细内容】
2021-12-27  子冉爱python    Tags:Python   点击:(4)  评论:(0)  加入收藏
有不少同学学完Python后仍然很难将其灵活运用。我整理15个Python入门的小程序。在实践中应用Python会有事半功倍的效果。01 实现二元二次函数实现数学里的二元二次函数:f(x,...【详细内容】
2021-12-22  程序汪小成    Tags:Python入门   点击:(32)  评论:(0)  加入收藏
Verilog是由一个个module组成的,下面是其中一个module在网表中的样子,我只需要提取module名字、实例化关系。module rst_filter ( ...); 端口声明... wire定义......【详细内容】
2021-12-22  编程啊青    Tags:Verilog   点击:(8)  评论:(0)  加入收藏
运行环境 如何从 MP4 视频中提取帧 将帧变成 GIF 创建 MP4 到 GIF GUI ...【详细内容】
2021-12-22  修道猿    Tags:Python   点击:(6)  评论:(0)  加入收藏
面向对象:Object Oriented Programming,简称OOP,即面向对象程序设计。类(Class)和对象(Object)类是用来描述具有相同属性和方法对象的集合。对象是类的具体实例。比如,学生都有...【详细内容】
2021-12-22  我头秃了    Tags:python   点击:(9)  评论:(0)  加入收藏
所谓内置函数,就是Python提供的, 可以直接拿来直接用的函数,比如大家熟悉的print,range、input等,也有不是很熟,但是很重要的,如enumerate、zip、join等,Python内置的这些函数非常...【详细内容】
2021-12-21  程序员小新ds    Tags:python初   点击:(5)  评论:(0)  加入收藏
Hi,大家好。我们在接口自动化测试项目中,有时候需要一些加密。今天给大伙介绍Python实现各种 加密 ,接口加解密再也不愁。目录一、项目加解密需求分析六、Python加密库PyCrypto...【详细内容】
2021-12-21  Python可乐    Tags:Python   点击:(8)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条