显示标签为“数据挖掘与机器学习算法”的博文。显示所有博文
显示标签为“数据挖掘与机器学习算法”的博文。显示所有博文

2012年12月25日星期二

数据挖掘六法

数据挖掘六法:1、关联,用于发现不同事件之间的关联性;2、序列,用于发现一定时间间隔内接连发生的事件;3、分类,分析具有类别的样本特点得到决定样本属于类别的规则;4、聚类,将样本聚成不同的组并进行描述;5、预测,根据样本的已知估算连续类型的变量;6、时序,分析随时间而变化的事件序列。

2012年11月17日星期六

从Java里调用R – JRI的设置方法


从Java里调用R – JRI的设置方法

JRI允许用户从Java里面调用R的功能,而Eclipse是目前最常用的Java开发环境。本文介绍在Eclipse里设置JRI的方法。
环境:
Windows 7 32bit
Eclipse 3.6
R 2.13.1
rJava 0.9-1
1.在R里安装rJava扩展包。JRI已经被包含在rJava里了。命令是: install.packages(“rJava”)。运行完成后rJava默认被安装在R的安装路径,如:C:\Program Files\R\R-2.13.1\library\rJava。
2.打开JRI的安装目录,如:C:\Program Files\R\R-2.13.1\library\rJava\jri,即可看到从Java里调用时需要使用的文件和目录。其中: examples中包含示例Java源文件,可以用来测试你的设置是否正确。jri.dll是需要使用的动态链接库,运行Java程序时会被用到。JRI.jar以及另两个jar文件是Java类库,编译Java源文件时需要用到。
3.我们现在在Eclipse里新建一个Java项目,然后把examples目录里的.java文件复制到这个项目里。
4.下面要设置运行环境。
4.1 首先使Java类能够编译。需要把上面提到的三个jar文件加到项目的类路径里。右键点击项目名,选择Properties,然后在左侧边栏中选择Java Build Path,然后在右侧tab里选择Libraries,然后选择Add External JARs…,在弹出的选择框里选择jri文件夹里的三个.jar文件,点确定。这时,这三个新文件应该会在界面上被列出来。点击OK退出项目属性界面后,Java类应该会被重新编译,所有文件应该能被编译通过了。
4.2 配置运行时的动态链接库。主要是两步:首先,包含jri.dll的文件夹必须在java.library.path里;其次,R.dll必须在运行路径下。在Eclipse项目里,右键点击rtest.java,在弹出菜单里选择”Run As…”,然后选择”Run Configurations …”,这时会出现对话框。在右边列出的tab中,选择Arguments这个tab,在VM Arguments里加入一行:-Djava.library.path=”C:\Program Files\R\R-2.13.1\library\rJava\jri”。然后tab里选择Environment,按New添加新变量,变量名为PATH,值是C:\Program Files\R\R-2.13.1\bin\i386。
配置Arguments的界面:
配置Environment的界面:
5.完成上一步后,运行刚才新建的配置即可。运行时的界面如下:

R程序调试–DEBUG R


R程序调试–DEBUG R

1、browser(): 在脚本文件适当的位置插入browser(),重新载入模块,运行,程序会在该行中断。命令:n: step; c: continue; where: print the call back; Q: quit; enter: last command;
2、traceback(): 打印上一次的出错信息;
3、定义一个全局变量捕捉中间数据信息,比如一个全局变量a,可以在函数内a<<-***来给全局变量赋值;
4、设定options(warn=1),即时提示warning信息,不设置则警告信息会在程序执行完毕之后才会输出;
5、try() 和tryCatch()是两个很好的处理error的函数;
6、warning()输出一个警告信息,stop()函数终止程序运行并退出,geterrmessage()得到最后一次出错信息。
Posted in ProgrammingTips.
Tagged with .

概念化”增强学习”(二)


概念化”增强学习”(二)

就像做通信的都绕不过信息论,要研究增强学习也跑不掉MDP。

增强学习的基础理论:马尔科夫决策过程(MDP)

通过MDP可以符号化地定义一个多状态带时延反馈的增强学习过程。一个MDP包括一个状态集合S,一个操作集合A,一个反馈函数R:S×A->R,以及一个状态迁移概率函数T(s,a,s’): S×A->S。可以结合前文的图1里来理解这里符号的意义。
这个模型的求解很简单,要输出的最优策略\pi其实是一个状态到操作的映射函数S->A,即在什么状态下应采用什么操作是确定的(根据短时平稳性假设,至少是短时确定的)。为得到这样的策略,我们首先需要定义在状态s时不同操作所能获得的期望收益,并把这个收益最大化。以下公式即定义了这个期望最大的收益(这里使用的是无限视野带衰减的目标函数):

获得这个最大收益时所采取的策略:

这两个迭代式,在T和R都已知的情况下,可以通过初始化,然后迭代计算使得结果收敛,过程跟pagerank的计算差不多。而根据是对V*进行迭代还是对\pi进行迭代又产生了值迭代和策略迭代两种算法。性能上稍有不同,结果是一致的。值迭代又可以演化出增强学习的代表性算法Q-learning。
MDP是一个很理想化的模型,它假设了我们事先对环境的完全了解(转移概率函数T及反馈函数R),而增强学习要解决的问题恰恰是对环境信息的未知。虽然MDP模型并不具有现实可用的意义,但通过它我们定义了一整套的符号系统以及问题解决的途径,极大地简化了对问题的描述与求解。在理论上解决了一些抽象层面的问题后(如收敛性、计算复杂度),要把它推广到现实的应用,只要着眼于解决对T及R函数的估计或替代即可。那些看似没有实用价值的数学模型的意义就在于此。

一般情况下MDP的求解

在现实应用中,MDP的环境模型(T及R函数)的参数是未知的,所以增强学习的过程可以化归为一个参数估计问题,只要能通过逐步得到的数据迭代地修正模型参数或算法参数,就可以直接利用通过MDP推导得到的结果。要实现这个目的,也有两种方式:model-based(学习模型的参数并把估计得到的模型应用于策略输出)和model-free(不断根据数据输入调整策略的输出,而无需估计具体的模型)的。两种实现方式各有千秋,一般来说model-free的方法需要更多的迭代步数才能收敛,但需要更少的环境知识,而model-based的方法则正好相反。由于这里主要是概念性的描述,欲求更多细节与方法实现的,可参看文后的文献。

增强学习潜在的应用场景

由于增强学习解决的是一类问题,所以它在自适应控制、机器人控制、博弈等等领域都有广泛的应用。从增强学习与有监督学习的对比可以看到,增强学习特别适用于那些没有启动数据的环境。搞推荐系统的朋友,这个时候你是不是瞬间就想到了冷启动问题。

博弈论的情景

以我粗浅的理解,可以把博弈论所讨论的问题理解为群体式的增强学习。每一个agent都与其它agent交互,并把对方的反馈视为环境的输入,agent必须学习在这个环境里最优的生存法则。比增强学习更为复杂的是,这个时候的环境是非稳定的,你在调整策略的同时,其它agent也在调整着自身的策略。一个著名的例子是囚徒困境的实验,多位社会精英为自己的agent设计出理想的增强学习型算法,但最后获胜的却是最简单的策略:以牙还牙。个中的奥妙颇是耐人寻味,在multi-agent博弈的环境里,非线性的因素是不可建模亦不可被预测到的,回归到最基本的策略往往能得到出人意表的效果,虽然它缺乏学习(实际也可能是群体学习筛选的结果),它却也省去了过拟合的烦恼。延伸来说,如果你不能掌控一个系统的复杂性,用启发式的策略做一些力所能及的事情,比你搭建一个同样复杂的系统试图去理解它是更为合理的。

结文

到此,增强学习的基本概念与模型算是摆在面前了,应该不会有下文了,将来如果有,也会结合着我自己的实践体会来写。虽说只是一个缩略版,这些模式对于实际要做的事情也没有直接的帮助,但我们需要这样的对问题建构的范式,以设计出符合自己系统需要的模型来。
参考文献:
【1】Reinforcement learning: A survey;  Kaelbling, L.P. and Littman, M.L. and Moore, A.W.; Arxiv preprint cs/9605103; 1996
关于作者
阿稳, 豆瓣, 算法工程师
推荐系统;数据挖掘;算法架构及实现的可扩展性;R环境编程
如果你的问题已经能从我的博客中得到解答,就最好不过了:http://www.wentrue.net/blog/

中国推荐社区ReSys的小站


中国推荐社区ReSys的小站

中国推荐社区:http://site.douban.com/106414/
这是一个豆瓣上的小站,现在还处于早期阶段,我们希望把它建设成为一个国内对互联网算法感兴趣的同学们(简称算法攻城师)交流的基地。现在暂时由文栋、xlvector、yoyo和我打理,欢迎大家加入。
由于处于建设初期,版块刚刚确立起来,内容还不甚完善,将来会有更多的内容更新上去,也欢迎大家在上面交流心得,讨论问题。
协同过滤版块,会转载一些范畴广泛的涉及互联网算法的博客文章,可由大家推荐产生,转载过来(保留回链),然后大家就该文进行交流,这样也方便把不同地方的精华内容汇聚到一起进行沟通,省了大家东奔西走之苦。
新产品新应用、学术动态、电子杂志、招聘广场各版块,顾名思义,都有其各自的职能,不过现在还有待开发,希望将来有会不同的版主来管理,大家可以积极申请。
小站的首页会用于将来线下活动的召集,活动反馈,讨论,内容建议等等。
希望与大家一同把这个基地建设好。
关于作者
阿稳, 豆瓣, 算法工程师
推荐系统;数据挖掘;算法架构及实现的可扩展性;R环境编程
如果你的问题已经能从我的博客中得到解答,就最好不过了:

少数人的智慧(The Wisdom of the Few)-基于专家的协同过滤(CF)


少数人的智慧(The Wisdom of the Few)

看 到这么个有吸引力的名字,你不会觉得它是一篇学术论文,但实际上,它是的。这是2009年Amatriain等人发表在ACM的一篇关于推荐系统的文章。从这个并不太学术的题 目,你大概可以意想到这里面并不会涉及太多繁琐的理论细节。实际上,如果你有一些关于推荐系统的背景,你可以毫无障碍的把它读下来,因为它就相当于一篇报 告文学一般好懂,但其中揭示的道理却并非如它显示出来的那么显浅,尽管文中的叙述不一定很完备,但这绝对是一个值得细细探讨的主题。
所谓少数人的智慧,实际指是的作者提出的基于专家的协同过滤(CF)在某些方面要优胜于传统的CF算法。这是一个很丰满的工作,其要点如下:
  • 专家用户与一般用户在行为模式上的差异;
  • 设计基于专家的CF推荐算法;
  • 通过两种评测方式,评估专家CF与传统CF在精度上的差异,最后还通过一个用户调查来作出用户体验层面的比较。
之 所以要提出专家CF的算法取代传统的CF,是基于传统CF的一些弊病,比如数据的稀疏性,数据噪声以及计算量的庞大等等,而正是这些数据上的原因导致传统 CF算法推荐多样性不足、推荐不准确以及推荐可扩展性不良好等种种问题。这里提出的专家CF算法目的并不在于在某些数学精度指标上压倒传统的CF算法,而 希冀能探究如下几个问题:
  • 一个庞大的用户集合的偏好是否可以通过一个比较小的用户集合的偏好预测出来;
  • 对于一个源数据集来说,另一个与之不同源的、无直接相关的数据集是否具有对它进行推荐的能力;
  • 分析专家的收藏是否可以用作普通用户的推荐;
  • 探讨专家CF是否能解决传统CF的一些难题。
首 先定义专家,他们必须是这样的一群人:在一个特定的领域内,他们能对该领域内的条目给出深思熟虑的、一致的、可靠的评价(打分)。在这篇文章里,作者并没 有详细地探讨如何从数据中发现一批领域专家,他们挑选的是一批来自从rottentomatoes.com爬取的现成的电影评论专家,这样可以使得他们讨 论的主题更为集中,而因为这些专家都是经过人工筛选的,所以,可以忽略因专家挑选算法的不足而给后续算法与分析带来的偏差。
数据集:
文 章中大部分的分析都是基于两个数据集:1、来自netflix的一个庞大的电影打分集;2、如上所描述的从rottentomatoes.com爬取筛选 的专家用户打分集。专家数据集有169个经过筛选的用户,他们的打分记录都大于等于250个。经过两个数据集中电影的匹配,剩下8000部两个数据集中都 出现过的电影,占原netflix全集电影的50%左右。
专家用户与一般用户在收藏行为上的差异:
这是一个很具有参考价值的分析,虽然作者并没有提供给我们自动挑选专家的方法,但通过这些分析结论,你可以对你自己通过算法或者人工得到的专家数据集的质量进行评估。所有关于专家用户与一般用户差异的结论都可以通过如下几张图得到。
图2:打分数量与数据稀疏性
图的解释:这是一个累积分布图(CDF),对两个数据集各作一条线。图2a中的每一个点(x, y)表示打分人数<=x的电影占电影总数的比例;类似地,图2b中的每一个点(x, y)表示打分记录<=x的用户占用户总数的比例。
结论:专家用户的打分比一般用户要多得多,数据集也要稠密得多,实际上,数据集2的稀疏系数约为0.07(用户评分矩阵中非0元素的比例),而数据集1的稀疏系数约为0.01。图2a中专家曲线在y上的截距为0.2,表示有20%的电影其实只有一个人打过分。
图3:平均评分的分布
结论1:图3a中,专家用户的曲线在高分段占有更多的电影,说明他们对好电影的认同更为一致;
结论2:图3b中,专家用户自己平均评分的变化范围不大,但对电影的覆盖面更广,即无论好片还是烂片,都有一定量的打分记录,而一般用户正好相反。说明专家用户的打分并不依赖于这是否好片,只是一个客观的评价,而一般用户并不倾向于收藏烂片。
图4:评分的标准差
结论:跟图2a一样的道理,图4a在y有一个0.2的截距。
我觉得存在的一个问题:除非专家们的收藏差异比较大,否则专家对电影意见的更为一致的特性并不会带来一个我们希望的分众性的效果。有时需要人为地引入关于专家收藏的差异性。
专家CF算法:
专 家CF算法跟传统的基于用户的CF算法基本是一致的,只是由原来的计算user-user的距离从而找出相近的user这个思路,改为计算user- expert的距离从而找出相近的expert。相似度的计算公式跟一般的公式稍有点不一样。见原文公式1,主要是把余弦距离乘以一个共同收藏比的因子,以添加 共同收藏数量这个因素的影响。得到相似度之后,会计算与用户相邻的专家,其中会引入两个参数作为阈值以保持推荐的质量(这种质量保证机制也导致了无法针对 某些条目计算预测分值,也就有了下面要说的“覆盖率”这一评价指标),详情可以看原文,这里不过多介绍。得到相似邻居之后对条目的预测评分跟传统的CF也 一样,这里也不多说了。
本文我着重介绍的是两点:专家用户的特征与专家用户好处。最初已经说了特征,下面讲好处。
作者使用了两种评估方法,评估了三种推荐方式的效果
三种推荐方式分别是:
Critics’s Choice:专家平均算法,计算每个专家用户对每个条目评分的平均值,以此作为对所有用户的预测值。
Expert-CF:专家CF,见上文所述。
Neighbor-CF:传统CF,见上文所述。
第一种评估方式是常见的MAE(Mean Absolute Error)
过 程大家应该很熟悉,把数据集分割成训练集与测试集,根据训练集计算的用户相似度,去预测测试集里的评分,得到误差的绝对值的平均,以及预测的覆盖程度(如 上所说,因为有质量保障机制,所以并非所有的user-item对都有预测评分)。结果如下表所示,专家CF比专家平均的MAE有很大的信息增益效果,虽然 比传统CF要差,但覆盖度却也比传统CF要大。
然而对每个用户的误差研究表明,专家CF逊于传统CF时通常集中于那些预测误差并不大的用户,而且差得也不多(0.1左右),可见对于预测准的用户来说,这点差别并不显著。而对于那些预测得并不那么准的用户,两者差别并不大,专家CF还稍稍好一点。
第二种评估方式是Top-N精度
跟 以往的计算Top-N推荐的精确度与召回率的方法不一样,这里把评估变成了一个分类问题,即根据一个设定的阈值delta,把计算出来的预测评分分成 recommendable(大于等于delta)与not-recommendable两个类别;同样把测试集里每个用户评分的条目根据delta分割 成两个类别,然后观察这个真实的类别划分与预测的类别划分之间的交叉情况(true positive与false positive),即可计算得到一个推荐精度的值。
当取delta=4时,专家CF的精度是0.76,传统CF的精度是0.85,两者有一定的差距,当取delta=3时,专家CF的精度是0.89,传统CF的精度是0.90,两者相差不远。即当认为大于等于平均水平(3分)的推荐为有效的话,两个算法的差别不大。
虽 然作为学术评测依据,大家都习惯用各种数字评测指标来说明自己的算法比起别人的有多大的优势,但这些数学上的指标到底跟用户体验有多大的关系实际上是没有 人知道的。为此作者又做了一个更现实的用户实验。这个实验是这样的,作者通过一个网站对招募来的57名用户进行预测与评价。首先要求这批用户对100部电 影按照自己的喜好进行打分,然后系统根据四种策略给每个人产生4组10个的推荐电影。这4种策略分别是:1.随机选取;2.根据专家打分平均从高往低 取;3.传统CF;4.专家CF。并要求用户对这给出的4组推荐分别作出评价,评价的问题是4个:1.对推荐结果的总体感觉;2.有没有你喜欢的推 荐;3.有没有你不喜欢的推荐;4.有没有惊喜。这4个问题的答案需以1-5(第一题)或1-4(其余三题)分值的形式给出。因为实际进行中每个用户在第 一步时评分的电影其实很少,平均每名用户评分14.5(虽然给出100部电影要求打分),这也正好切合了一个新用户面对推荐系统时的情况,也即冷启动。结 果令作者非常满意,几个问题里策略4专家CF都占有压倒性的优势,而策略3传统CF的表现只能跟策略2专家平均差不多,甚至更差。
最后的总结作者吹嘘了一番专家CF相比于传统CF的几大优势:
数据稀疏性:推荐数据集固有的数据稀疏问题会因为信息量不足而带来一些额外的问题,专家收藏的数据稀疏度要比全体用户收藏的稀疏程度要低,即有更多的可参考的信息。
噪声评分:数据集里面难免会存在一些噪声评分,无论用户是有意的还是无意的,甚至还有些故意捣乱的用户或spammer。而专家在这方面则可靠得多,而且个人意见也比较容易保持一致。
冷启动问题:这是专家CF的一大卖点。对于用户冷启动,由于数据稀疏性与噪声问题而造成的问题,在专家CF里得到了不错的解决。实验也证明了这一点。对于条目冷启动,由于专家更具有前瞻性,所以新条目更容易通过专家而进入到推荐池中。
可扩展性:如果直接使用基于用户相似度的CF算法进行推荐,在实际系统中几乎是不可行的,因为构造一个用户相似度矩阵是如此地庞大。而使用量要少得多的专家作为相似度矩阵的一个维度,矩阵的规模则现实得多。
隐私:这里还考虑了这样的一种可能性,即不需要你把数据传递到服务器,只需要把专家喜好传递到客户端,与你本地的收藏相匹配,然后服务器给你返回相应的推荐,避免了服务器记录你的收藏。
我补充一下,其实专家CF的对条目的覆盖面与多样性应该要更好一些,这跟专家收藏数量以及收藏的覆盖面更广这个特性有关。
看 这篇文章,更多的是看文中阐述的思想,虽然这可能并不是他们首创的,但毕竟他们作了一个很好的总结与分析。我一直在思索我们到底需要什么样的推荐,最近我觉得:至少在大部分的场合,我们需要的并不是与自己相似的用户的推荐,而是与自己相似的专家的推荐。无论是看书、看电影、买手机、买笔记本,那批“行内人 物”的观点往往是左右我们决定的主要因素。这个结论在个性化要求相对比较低的中国显得更为真实。
关于作者
阿稳, 豆瓣, 算法工程师
推荐系统;数据挖掘;算法架构及实现的可扩展性;R环境编程
如果你的问题已经能从我的博客中得到解答,就最好不过了:

【R Special】 函数式语言的面向对象机制实现


【R Special】 函数式语言的面向对象机制实现

好久没更新博客了,一方面因为工作里挖了太多的坑,要一个一个去填,另一方面也是因为这阵子把今年整年的旅行都给规划了一遍。然后突然心血来潮,要 给R写一个系列的文章,有部队的编排,感觉会比以前那样打游击要好。于己可以更有体系的总结过往实践过的知识,于他人,也方便特意或偶然来到这个博客的读 者查阅。

前言

翻过好些R的书籍,都是从入门到精通型,三章之内完全体现不出R的特点,更让人找不到理由为什么还要去学习这第k+1门语言。这样general的 介绍方式倒也情有可原,你不从基本数据结构和基本流程控制讲起又怎能唤起入门者的强烈共鸣呢。R的几篇官方文档倒是值得一看再看,但枯燥无味基本不属于普 通人类的阅读范畴。于是就想写一些简明易懂的R的special方面的东西,这个尝试会比较随性,想到哪写哪,哪天要是没有动力,说不定就烂尾了。
意想中这不是个入门系列,而是我个人偏好摘取的R的一些Feature汇集,具有一定程度的进阶及挑战性,甚至是编程思维和对问题建模方式层面的特点,也是R之所以为R的原因,所以定名为R Special。
粗略归纳自己在实践中思考过的或深或浅的内容,预计至少会有:R的基本数据类型及底层数据结构、向量化思维及实现、矩阵(密、疏)运算、函数式编程 特性、各种常用的统计分析、统一的分类模型、并行计算、如何用R来描述算法等等内容,第一篇就介绍函数式语言的面向对象机制可能不太合适,但正好这些天又 看回这部分内容,所以,无所谓了。

面向对象的函数式语言

以前我介绍R的六张面孔时 曾说过,R是面向对象的函数式语言,这是他特有的两张面孔。一般来说,函数式和面向对象这两个特性很难并存于同一种语言中。基于它lisp和 fp(function programming)的基因,R在融合这两个稍有对立的特性时其实更多地是体现函数式的一面,再通过一些附加的机制实现了面向对象中的继承及多态的特 性,这种实现只重实质不重形式,所以不要惊呼:“我熟悉的Class关键词呢”?接受它有助于你理解数学家们是怎么抽象地去解决一个工程问题的。
R的面向对象一方面体现在其内置的数据结构都表现为类这个概念。对于R,万物皆对象,类是对象的抽象描述,对象是类的实例化。R有一个class()函数,你可以给它传入任何变量(任何!包括这个函数本身),它会返回描述这个对象的类的名字。
R的面向对象另一方面体现在你可以利用它提供的面向对象的编码方式来描述自己的算法,组织自己的代码。为达到这个目的,R采用的并非如C++、 Java那样的层次型的类结构,而是利用了泛型函数(generic function)这个概念。泛型函数的OOP并不关心特定的语法结构,而专注于OOP最主要的问题——代码分派(dispatch),即:“调用什么代 码”以及“如何做出这一决策”。常见的OOP语言通过内化的继承及重载的方式来完成这一使命,重心在class;而R则通过定义一系列泛型函数,以及这些 函数在不同类型对象上的行为来达到这个目的,重心在method。前者结构严谨,但未免冗余笨重,系统越大缺陷越明显;后者抽象,但因其关注在函数本身而 成为与fp最完美的结合方式。

S3与S4的OOP系统

先看如下两段代码及其注释。
S3系统
1
2
3
4
5
6
7
8
9
10
11
12
> print   # 显示print这个函数的代码
 function (x, ...)
 UseMethod("print")  # 调用UseMethod这个函数相当于隐式地定义了print为泛型函数,同时这个函数担当了代码分派决策的角色
 
 > x <- c(1,2,3) > class(x)  # 任何对象都可以通过这个方法得到其class[1] "numeric"
 > print(x)  # print函数对numeric这个class的默认行为
 [1] 1 2 3
 > class(x) <- 'num'  # 可以随意修改某个对象的class名字 > class(x)
 [1] "num"
 > print.num <- function(x) {cat('this is my numeric:', x, '\n')} # 为num这个新classprint对应的method > print(x)  # 你猜到了,泛型函数的分派策略是寻找'print.%s'%class(x)这个函数(这里是python风格的string format)
 this is my numeric: 1 2 3
S4系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 定义一个类的名字及其成员变量(R中成为slot,通过@操作符选择)
 setClass('player', representation(name='character'))
 # contain关键词表示继承关系,prototype相当于初始化
 setClass('superstar', representation(club='character'),
 prototype=list(club='none', name='anonymous'), contain='player')
 # 因为print已经是一个generic函数,所以可以直接绑定method到某个类,否则需要先setGeneric才能setMethod
 setMethod('print', 'superstar',
 function(x, ...){
 cat(sprintf('%s is from %s\n', x@name, x@club))})
 # 通过new来生成类的一个实例
 ronaldo = new('superstar', name='ronaldo', club='RM')
 messi = new('superstar', name='messi', club='Barca')
 # 根据setMethod的绑定关系来分派
 print(ronaldo)
 print(messi)
 # 以上代码另存为一个文本文件,如object.R
 
> source('object.R')
 ronaldo is from RM
 messi is from Barca
可以看到,R提供了S3和S4这两种不同的机制来实现OOP编程,后起的S4兼容S3。
S3是R中最古老的class/method系统,绝大部分R自带的方法都是基于S3的泛型函数,如print, summary, plot。在这套系统里,class基本处于一个无足轻重的地位,因为你可以给任意对象指定任意一个名字来指代它的class,就如列表1的第10行所示 的那样。在这里,class只是一个名字而已,比人类起名字还随便。它不必有具体的数据结构、具体的事物含义,也无需声明、初始化、析构,更别说严谨的继 承重载结构,这对当初只看过C++和Java的OOP的我来说无疑是一枚深水炸弹啊。但这个名字所起的唯一作用也是最重要的作用——代码分派!
虽然已经完成任务,但S3仍略显随意和简陋,并非一个完全的面向对象的系统,所以后来又发展出一个完全平行的S4版本的class/method系 统来。因为R是个完全基于package的环境,所以所谓“自带”的S3系统其实来自于base这个基本包,而S4来自于methods这个包,但跟 base一样,这个包在R启动时会默认被装载,所以也可以认为是自带的。S4时代class的地位相对于S3有了很大的提升,可以通过setClass来 声明一个class,定义它的数据结构,以及进行完整性检验。泛型函数的声明也不再是S3那样的隐式实现,而是通过setGeneric来显式声明。 setMethod函数则实现了前两者的绑定。
你可以按自己的喜好选择任一个系统来完成你的OOP建模,在功能上它们不会有任何差别,互不干扰。官方建议新的项目应该使用更为严谨的S4系统,但很多人都还是喜欢用S3,理由是它够”quick and dirty”【2】。

使用R的OOP建模方式

无论是使用简单的S3还是更为规范化的S4,基于其泛型编程的侧重,在R环境下对你所面临的问题进行面向对象式的建模跟你以往的习惯会有所不同。首 先需要把你的问题中的“操作”抽象成已有的或现写的泛型函数,这就是generic function;然后定义你的问题中涉及的对象及其数据结构,给它们一个名字,这就是class;最后为每个需要泛型操作的对象实现一个方法,这就是 method(method即是泛型函数在某个class的具体实现)。

为什么要引入面向对象

最初我也疑惑,像R这种fp和统计混血的小孩为什么要穿上一身市面上流行的”面向对象“的外衣,以致于你在R里使用的所有自带的函数都实际上都是泛型函数,包括运算符。这里我不打算废话了,通过两个例子来说明这个机制所带来的好处。
常用的输出一般性统计量的函数summary是泛型函数
> summary(1:100) # 一个向量的一般统计量
Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
1.00   25.75   50.50   50.50   75.25  100.00
> summary(matrix(1:100, nrow=50))  # 一个矩阵的一般统计量(其实是按列计算)
V1              V2
Min.   : 1.00   Min.   : 51.00
1st Qu.:13.25   1st Qu.: 63.25
Median :25.50   Median : 75.50
Mean   :25.50   Mean   : 75.50
3rd Qu.:37.75   3rd Qu.: 87.75
Max.   :50.00   Max.   :100.00
> fit=lm(y~x, data.frame(x=1:100, y=1:100+rnorm(100)))
> summary(fit)
# 输出为这个线性模型的一系列拟合结果及参数,此处略。
#高级一点,分类器模型常用的predict函数也是泛型函数。
> predict
predict       predict.glm   predict.lm    predict.mlm   predict.poly
以上几个对predict进行了扩展的method可以保证lm, glm, mlm, poly这几个不同的分类器model的返回结果都可以通过一个统一的predict函数进行预测——多么优雅的抽象。
最后,不要赶潮流随便地就给你的项目引入面向对象,这不是值得夸耀的feature的一部分,而应该是让的你的feature更好地被实现的工具。 R引入了这个机制,使得不同的对象都可以使用一致的方法(数学家们这个时候肯定会洋洋得意地认为自己抽象出了一个算子——还记得算子吗亲),整个算法的描 述变得抽象、简洁又优雅,这才是最重要的。
【参考资料】
【1】使用 R 编写统计程序,第 3 部分: 可重用和面向对象编程: http://www.ibm.com/developerworks/cn/linux/l-r3.html
【2】Classes and Methods in R: http://www.biostat.jhsph.edu/~rpeng/docs/R-classes-scope.pdf
关于作者

R,不仅仅是一种语言


R,不仅仅是一种语言

本文原载于《程序员》杂志2010年第8期,因篇幅所限,有所删减,这里刊登的是全文。

简介:R是什么

工欲善其事,必先利其器,作为一个战斗在IT界第一线的工程师,C/C++、java、perl、python、ruby、php、javascript、erlang等等等等,你手中总有一把使用自如的刀,帮助你披荆斩棘。
应用场景决定知识的储备与工具的选择,反过来,无论你选择了什么样的工具,你一定会努力地把它改造成符合自己应用场景所需的那个样子。从这个道理来说,我选择了R[1]作为数据挖掘人员手中攻城陷池的那把云梯,并努力地把它改造成自己希望的那个样子。
关于R的一个比较准确的描述是:R是一门用于统计计算和作图的语言,它不单是一门语言,更是一个数据计算与分析的环境。统计计算领域有三大工具:SAS、SPSS、S,R正是受S语言和Scheme语言影响发展而来。其最主要的特点是免费、开源、各种各样的模块十分齐全,在R的综合档案网络CRAN中,提供了大量的第三方功能包,其内容涵盖了从统计计算到机器学习,从金融分析到生物信息,从社会网络分析到自然语言处理,从各种数据库各种语言接口到高性能计算模型,可以说无所不包,无所不容,这也是为什么R正在获得越来越多各行各业的从业人员喜爱的一个重要原因。
从R的普及来看,国外的普及度要明显好于国内,跟盗版windows的泛滥会影响linux在中国的普及一样的道理,破解的matlab与SPSS的存在也影响了R在中国的使用人群。但在国外高校的统计系,R几乎是一门必修的语言,具有统治性的地位。在工业界,作为互联网公司翘楚的google内部也有不少的工程使用R进行数据分析工作,这里[2]有一个google campus的讲课视频,内容就是用R作为工具来讲述数据挖掘的概念与算法。
随着近年来R使用者的增加,关于R的报道也屡有见于报端,如2009年初美国纽约时报就有一篇很好的报道:Data Analysts Captivated by R’s Power[3]。报道中述说了R的发展历史以及由于数据挖掘需求的增长而日益普及的现状,它虽源于S但其发展却远远地超过了S,已经成为高校毕业学生所选用的第二大工具语言,google与Pfizer的员工也介绍了R在自己公司中的应用。此外,报道中google首席经济学家Hal Varian说:R的最让人惊艳之处在于你可以通过修改它来做所有的事情,而你已经拥有大量可用的工具包,这无疑让你是站在巨人的肩膀上工作。
以下就R的几个主要应用场景以及我在实践中的经验对这个并不算主流的编程语言作一些介绍。

统计计算:R之最强项

R从它出生的第一天就是为了做统计计算的,那时它被定义为一个统计计算与作图的工具,虽然发展到现在它已经被赋予了越来越强大的功能,但现在R的开发人员里,还是以各个高校统计系的老师与学生为主,他们自然最了解自己最需要的是什么。
在统计计算中,我们常常需要根据样本数据作线性回归,得到一定的规律性,R中实现这个功能十分简单,以下是一个一元线性回归的例子:
x <- 1:10
y <- x+rnorm(10, 0, 1)
fit <- lm(y ~ x)
summary(fit)
注明一下,R里的“<-”符号意义为赋值,大多数情况下它可以用“=”号来代替,但某些特殊的场合不可以,本文会遵循“<-”这种官方使用的写法。这个例子的前两行准备了两列数据:自变量x与因变量y,第三行的函数lm即根据提供的样本数据进行线性回归计算,得到的模型结果可以用第四行打印出来。函数lm除了可以做这种简单的一元线性回归,还可以做多元线性回归,同时返回模型的各种统计量。
做统计的往往免不了要做各种各样的图形,R的另一个基本特点就是对图形的强大支持,以下代码展示了一个箱线图的作法,代码来自boxplot函数的manual,该图显示了几列数据的分位数、中值、均值、奇异点等信息及其对比位置。更详细的关于R的作图功能可以参看[4]。
boxplot(mpg~cyl,data=mtcars, main=”Car Milage Data”, xlab=”Number of Cylinders”, ylab=”Miles Per Gallon”)

机器学习:让你的数据发挥它应有的作用

机器学习、数据挖掘领域面临着一些抽象自大量现实生活的问题,比如关联规则挖掘、聚类、分类这三大问题。作为一个完备的工程计算包,R毫无疑问对它们都提供了足够的支持。
关联规则问题源于“买了这件商品的顾客还买了什么”这个问题,现在已经广泛应用于客户行为分析以及互联网用户行为分析中。关联规则挖掘领域最经典的算法为apriori,R的第三方包arules[5],就是专门用于做关联规则挖掘的。以下例子需要你已经安装了arules包。
library(arules)
data <- paste(“item1,item2″,”item1″,”item2,item3″, sep=”\n”)
write(data, file = “demo_basket”)
tr <- read.transactions(“demo_basket”, format = “basket”, sep=”,”)
data(“Adult”)
rules <- apriori(Adult, parameter = list(supp = 0.5, conf = 0.9, target = “rules”))
最后一行的apriori函数接受一个transaction对象的输入,输出关联规则对象rules,为方便起见,这里用于计算的transaction对象Adult是通过第5行从arules包中现成载入进来的,第2~4行说明了怎么从一个文本文件中读入数据并生成一个transaction对象。
聚类算法使用最广泛的高效算法无疑是kmeans,R在其默认载入的stats包中就包含了这个函数,以下是一个来自kmean说明文档的例子:
x <- rbind(matrix(rnorm(100, sd = 0.3), ncol = 2), matrix(rnorm(100, mean = 1, sd = 0.3), ncol = 2))
cl <- kmeans(x, 2)
plot(x, col = cl$cluster)
points(cl$centers, col = 1:2, pch = 8, cex=2)
代码第1行生成两组两维的正态分布的数据,第一组均值为0,第二组均值为1,两组数据方差都为0.3。第2行对该数据进行聚类,第3和第4行把聚类结果画出来。
分类器是模式识别领域的研究主题,也是人类认知活动的中心。多年来的学术研究积累下来很多种类型的分类器,而其中比较靠谱的分类器基本都能在R中找到对应的实现。诸多分类器中以svm最为著名,它也被一些人称为是单分类器的王道。以下是一个利用svm对著名的iris数据集进行分类的过程,运行该例子需要你已经安装了e1071这个包[6]。
library(e1071)
data(iris)
x <- subset(iris, select = -Species)
y <- iris$Species
model <- svm(x, y)
summary(model)
pred <- predict(model, x)
table(pred, y)
第5行代码调用svm函数,计算由x作为特征y作为类别标签的分类器模型,第7行把模型应用于原数据进行预测。
以上例子的演示并非想让各位读者当场学会各个不同领域中这些功能函数的用法,而是一方面展示一些实际的R代码以及它解决问题的方式,另一方面说明了R在这些常见的机器学习领域的积累。在R帮助下去解决这些或许不是我们专业的问题,可以省去我们大量重复造轮子的精力,写出来的代码也足够的短小精悍,节省时间之余也让你对自己算法逻辑的全局一览无余。

高性能计算:向量化与并行/分布计算

作为现代数据挖掘人员从业者,可能第一个需要关心的是所使用工具的可伸缩性(scalability),具体来说就是在面对大数据量场景时的计算能力。
一个拥有高性能计算能力的计算包,首先它必须能充分利用历史上积累下来的那些著名的数值计算包,比如blas、lapack;另一方面,它必须具有良好的可扩展性,即它必须方便开发人员并行化自己的算法,很幸运这些特性R都具备了。
类似于R、scilab与matlab那样的工程计算包,通常都会以向量化计算(Vectorization)作为其基本的计算特点(即使python的numpy包也是如此),因为向量化的处理方式是现代大型计算机的基本特性,在计算机领域,无论硬件还是软件,都提供了对向量化的支持,硬件上如Intel的MMX, SSE等指令集都提供了对向量化的支持,更多可以看到wikipedia上的介绍[7]。软件上如blas等著名的计算包,天然地就可以对向量化的命令自动实施并行计算。
所谓向量化,是一种特殊的并行计算的方式,相比于一般程序在同一时间只执行一个操作的方式,它可以在同一时间执行多次操作,通常是对不同的数据执行同样的一个或一批指令,或者说把指令应用于一个数组/向量。以下列出R中经常使用几种向量化运算,都是十分稀松平常的操作,但它们本质上都是同时对一批数据应用相同的操作,所以都可以经过向量化处理方式的改造:
  • 向量取值,如:V[1:10]
  • 向量赋值,如:V[1:10] <- seq(1,10)
  • lapply,类似于python里的map函数:lapply(A, mean)
  • 矩阵运算:A + B;A %*% B
向量化因其在计算过程中数据的前后不依赖的特点,是并行计算的天然先驱,一个用向量化实现的算法,必定是一个可以高度并行化的算法。正因为这个原因,在利用R写脚本的时候,都要尽量利用向量化的思想来设计自己的算法,尽可能少地使用循环结构。一旦你的程序都是或大都是基于向量化的,除了当时获得来自于计算机软硬件上的优化外,将来某一天数据量膨胀使得计算成为瓶颈时,你就可以极为方便地把原来的算法并行化。
正如我们所知,CRAN包括了各种你能想像得到的工具包,当然也有不少并行计算的包,这些包被归纳在R高性能计算相关的包列表中[8]。
关于R的向量化及并行计算更详细的内容可以参考我的一篇博客[9]。

编写接口与工具包:最有用的包必定是你写的那一个

一个开源软件的最强大之处在于大量从业人员的贡献,R最让人激奋,进而选择它作为工作平台的一个重要原因则是庞大而无所不包的的CRAN,在那里几乎能找到所有你能想像得到的与分析研究相关的工具包,可以说丝毫不逊色于perl的CPAN。之所以拥有一个如此强大的第三方支持,一方面在于R本身在统计计算与计算能力方面的支持,另一方面则在于开发一个R扩展是如此地容易,以致于每一个使用R作为自己常用工具的人,都会按捺不住强烈的冲动要写一个自己的包,以满足工作需要。如果自己的这个包感觉写得不错,又为很多人所需要,就可以提交到CRAN。这是造成CRAN如此庞大的原因,但同时也造成了CRAN的软件包良萎不全。但大多数情况下,这些包都会是你的得力助手,特别是那些著名而广为使用的包,如果觉得它们不满足你的需要,那么放心地对它们进行修改吧,因为它们都是开源的。
下面展示一个简单的R扩展包的制作过程:
1、生成包结构:新建一个目录mypkg,同时作为包名,在mypkg中新建几个目录与文件,mypkg的目录结构如下图所示。R自带的函数package.skeleton可以自动帮你生成这些目录,但它需要一些现成的函数对象或文件作启动,为了顺序说明整个过程,这里没有使用。
2、目录说明:必需的是DESCRIPTION文件、man目录和R目录,剩下的都是可选的。DESCRIPTION文件描述包的meta信息;R目录下面存放R脚本文件,里面的函数可导出作为包函数库提供给外部使用;如果要在包里放一些试验数据,可以放在data目录里,常用是以csv格式存放,在R终端里data(***)可以载入,这里留空;man目录是R的帮助文档,有一定的格式要求,这里也留空,生成包时会有一些警告,可以不用管;src存放c/c++/fortran源代码,必须同时放置Makefile或Makevars文件指导编译程序工作,这里留空;zzz.R可以在载入包时做一些事情,这里也留空。
3、添加功能:DESCRIPTION文件的内容可以参考任意一个R包对应文件的写法,依样把信息修改成自己相应的信息即可。以下只写一个简单的R函数作为说明,在R目录下添加一个名为helloword.R的文件,文件内容如下:
helloword <- function(x, y)
{
return(x*y)
}
4、安装:在命令行中运行R CMD build mypkg,会编译生成一个mypkg_0.1.tar.gz安装包,其中的数字是我在DESCRIPTION里写的版本号;运行R CMD INSTALL mypkg,就可以把包安装到系统里。
5、试验:运行R,进入R终端;library(mypkg),载入刚制作的包;search(),可以看到mypkg包已经被载入;在R终端运行helloworld(2,3),返回6,试验成功。
一个具有一定功能的包就这样做好了,是不是很简单。如果有其它需要,只要往R目录或src目录添加文件,然后重新生成并安装就可以了。R与c/c++之间的接口调用也十分方便,限于篇幅,无法更仔细地说明,更详细的内容可以参考我的几篇博客[10-13]。

R在中国的发展

R在中国的普及现在并不十分地广泛,主要还是学校及研究机构在使用,但近年来随着R的声名鹊起,也已经有越来越多各个领域的工业界从业人员选择R作为自己的工作平台,其中统计之都[14]是一个国内R用户的聚焦地。今年的6月份在人民大学举行了第3届R语言会议,从前三届会议的人员组成来看,R的中国用户群一直呈现较大的增长趋势,用户分布的领域也越来越丰富。第三届R语言会议参会者人来源可以从会议纪要中看到[15]。相信随着数据挖掘广为各个公司接受,R也会走近工业界的各行各业中。

R在豆瓣中的应用

有一段时间,我一直在寻找介乎于matlab与系统语言(如C, Fortran)的中间物,希望它既能拥有系统语言的高性能,又能方便数据挖掘人员的日常工作,于是我找到了R,这不仅是一门语言,它更是一个理想的计算环境。它一方面方便我对新算法原型的构建、调试、评测,另一方面并没有让我失去系统级语言的计算优势,甚至在实现并行计算方面拥有了更多的选择。现在我使用R编写我们自己的工具包,进行算法原型构造、矩阵运算、并行算法等离线应用,为相似性计算、推荐系统等上层应用提供底层的支持。

一个R写的协同过滤推荐的例子

最后用一个R实现的协同过滤推荐的例子来结束本文,协同过滤是推荐系统中一个基本的算法,详细内容可以参考这里[16]。由于大量地采用了向量化的计算方式(包括各种矩阵运算),所以算法的实现相当简洁,有可能是史上代码最少的协同过滤推荐引擎 :-)
data <- read.table(‘data.dat’, sep=’,', header=TRUE)
user <- unique(data$user_id)
subject <- unique(data$subject)
uidx <- match(data$user, user)
iidx <- match(data$subject, subject)
M <- matrix(0, length(user), length(subject))
i <- cbind(uidx, iidx)
M[i] <- 1
mod <- colSums(M^2)^0.5
MM <- M %*% diag(1/mod)
S <- crossprod(MM)
R <- M %*% S
R <- apply(R, 1, FUN=sort, decreasing=TRUE, index.return=TRUE)
k <- 5
res <- lapply(R, FUN=function(r)return(subject[r$ix[1:k]]))
write.table(paste(user, res, sep=’:'), file=’result.dat’,
quote=FALSE, row.name=FALSE, col.name=FALSE)
代码我就不细加注释了,有兴趣了解其原理的同学可以看这里[16]。

参考:

关于作者
阿稳, 豆瓣, 算法工程师
推荐系统;数据挖掘;算法架构及实现的可扩展性;R环境编程
如果你的问题已经能从我的博客中得到解答,就最好不过了: