频繁项集挖掘之apriori和fp-growth
Apriori和fp-growth是频繁项集(frequent itemset mining)挖掘中的两个经典算法,虽然都是十几年前的,但是理解这两个算法对数据挖掘和学习算法都有很大好处。在理解这两个算法之前,应该先了解频繁项集挖掘是做什么用的。
频繁项集挖掘是关联规则挖掘中的首要的子任务。关联规则挖掘是要找出一个数据集上,满足一定条件的项集。这些项的集合能构成 形如蕴含式"A=>B"这样的“规则”。这个"=>"符号是通过一些条件来定义的,如果没有条件那当然所有的项组合都能形成这样的关系。频繁程度就是一种要求,也就是AB共同次数出现了超过阈值。 A和B能否形成"=>"规则, 就要根据定义来算,那首先就得把A、B需要的条件挖出来,也就是频繁项集挖掘要做的。
关联规则定义比较容易能搜到,频繁项集挖掘简单的说就是:给定一个项列表list = {A,B,C,...},一个数据集D的每条记录都是list 的子集,要找出数据集中频繁共同出现次数超过阈值t,也就是支持度,的所有组合。
这个挖掘其实不好做,因为结果可能是list 中所有项的组合,有2^|list|个可能,搜索空间是个组合爆炸的空间。看下图,先别看红字红线:
要弄好这件事不仅需要有效减小搜索空间,而且对每个可能的搜索都必须快速完成。所以频繁项集挖掘在算法实践和编码实现上就要有非常强的技巧。我们就来深入学习apriori和fp-growth中的搜索方式和技巧。这两个算法很容易找到完整的步骤,这里会更注重里面一些精彩之处,但是可能书写不会那么规范,建议和完整算法对照来读。
先来看看apriori。Apriori采用广度优先的搜索方式,缩小搜索空间用到了一个称为apriori的性质。Apriori性质是这么说的:频繁项集的所有非空子集必然也是频繁的。这是很显然的,比如 同时包含项AB的记录条数肯定比只包含A的记录少。这条性质反过来也可以这么说:如果一个项集是非频繁的,那么它的超集必然也是非频繁的。
算法过程如下:
输入:数据集D,支持度minsup
输出:满足支持度的所有项集的集合L
L1 = 发现1-项集(D);
for (k=2;Lk-1 ≠Φ ;k++) {
Ck = 连接剪枝生成(Lk-1, minsup)
扫描D,为Ck中每个项集c 统计 c.count
保留Lk ={c ∈ Ck|c.count≥min_sup}
L = L ∪ Lk
}
Return L
其中算法精华在于 连接剪枝生成(Lk-1, minsup) 这一步, 包含连接步和剪枝步两个动作:
1. 连接步:长度k-1的项连接成长度k的项;具体就是对两个k-1长的项L1和L2,必须满足前k-2个项都相同才能连接,最后把L1和L2剩下的最后一项加起来,形成k的长度的项。
2. 剪枝步:k项连接完成后,检查其所有k-1子集是否是频繁的,如果是,才保留作为候选项。
可以通过一张截图来演示一下apriori的过程:
对应第一张图,连接步是从第k层的项集,向下扩展一层的候选项集,剪枝步能够通过apriori性质过滤掉那些肯定非频繁的项集。
Apriori的框架其实很小,但是可以想象得到主要的两个步骤: 连接+剪枝(也就是候选集生成),以及,候选集统计是很耗费时间的。
剪枝步也需要对每个k-候选项集的k-1子集都进行一次检测,也很耗费时间;统计频繁次数是必须的,因此需要扫描数据库,经历I/O。那么有必要剪枝,直接统计会不会更好呢,虽然没有试验过,但我估计还是剪枝以后减少候选集的统计更划算。而这两个耗时的步骤在实现上如果能使用到技巧,对算法时间影响最直接。比如剪枝步中k-1候选项集需要逐一向已有的k-1频繁项集查询,这用什么数据结构最好?又如扫描数据库的时候是否能过进行一些压缩,相同的记录进行合并减少遍历次数,以及过滤掉对统计没用的记录?
面对apriori的问题,感觉Fp-growth突然间就冒出来了,它是一个挖掘方式和apriori完全不一样的算法,直接看可能不那么像apriori直观,因为算法一开始就介绍了它采用的数据结构和挖掘方式。所以我们先对比下apriori和fp-growth的差异在哪,再介绍它的算法。
简单的说apriori是先产生一批候选项集,再通过原数据集去过滤非频繁项集:先找A、B、C,检查一下通过了,再找AB、AC、AB,检查又通过了,再到ABC... 这样的广度优先的方式。而fp-growth是先从数据集中找频繁的项,再从包含这个频繁项的子数据集中找其他的频繁项,把它们俩连起来也肯定是频繁的:先找A,再在找包含A的子数据库里,找到B,就得到AB是频繁,再再包含AB的子数据库里,找到C,ABC就是频繁了。
在了解了fp-growth的大致思路以后,我们就能介绍它采用的数据结构和算法了。
首先fp-growth采用了一个叫fp-tree 的数据结构去压缩存储数据集,放到内存里,这样以后过问原数据集的事就不必经过IO了。
Fp-tree主要是一种前缀树,和字典树(trie)接近,并且节点把项的次数也记录下了。字符的顺序有所不同,字典树用的是字母表顺序,fp-tree (frequent pattern tree)用的是字母表的频率降序,这样的好处是出现次数多的项作为共享前缀的概率也大,fp-tree的压缩率就高(后面还会提到),根据apriori性质,非频繁的项没用,因此fp-tree上可以没有它们。
根据前面提到fp-growth步骤,需要找数据库上包含某个项的子数据库,不能从树根开始搜索,因此为了方便,需要把fp-tree中所有枝干、叶子上相同的项全串一起,这样项从一个起点开始,向树根遍历,就能得到包含这个项的子数据库了。这些起点和串起相同节点的链就是fp-tree的另一个部分:头表和兄弟链。头表包含树上所有的单项,并是兄弟链的起点,那么fp-tree不仅完整记录了数据库里所需的信息,还能找到对任一项找到包含了它的子数据库。
有了fp-tree,挖掘频繁项集就变得直观了。首先是压缩数据库,过滤非频繁项,得到一棵fp-tree 1号,对于一个项,比如A,通过兄弟链,遍历树找出 包含A的子树(库),又称A的条件模式树(库),英文原文叫condition pattern tree(base)。然后把这个子库当做一个新的数据库2号,过滤2号库非频繁项,建立一个小点的fp-tree 2号,那么那个A与这个2号树里的所有项,连起来肯定也是频繁的;比如有B,同理把B的条件树找出,也建立个fp-tree 3号,就能得到AB和3号树上的项连起来也肯定是频繁的。这个过程递归完成,建立不出条件子树递归就跳出去。
算法包含两个部分:
1. 是建立fp-tree:扫描一遍数据库,得到每个项的支持度,排序并过滤非频繁项;再扫描一次数据库,每条记录按顺序排序,添加到fp-tree上。
2. 调用算法FP_growth(FP-tree, null)。
Function Fp_growth(FP-tree, a){
if(fp-tree 是单条路径){
对路径上的组合b, 都连接a,输出b∪ a
}else{
For each 项ai in 头表{
输出一个模式b= ai∪ a,其支持度 support =ai.support
构造b的条件模式库,然后构造b的条件模式树 Treeb;
If (Treeb 不为空){
调用算法FP_growth(Treeb,b )
}
}
}
FP_growth是个递归算法,期间需要反复遍历树和构建fp-tree。fp-growth中判断单路径部分可以不要,最后实际结果其实是和下面部分是一样的,但是直接计算单路径产生所有组合会便捷很多。另外一点,fp-tree要按支持度降序的顺序的好处有几点?前面说了可以提高共享前缀的可能,提高压缩率,树小了,遍历的步数还能减小,寻找最优压缩的顺序是个NP难问题,因此选这个办法能有个比较好的压缩率足够了。
fp-growth虽然号称不产生候选集,但是实际上候选集产生已经在寻找条件子库的时候隐隐产生了,剪掉非频繁候选项的时候是通过建树步骤中的第一小步完成的。
fp-growth在实现上也可以有很多技巧,比如寻找条件子树的时候,同一条路径会被遍历很多次,如何有效避免(后来han自己提出,遇到扫把型的fp-tree,即上面是单路径、下面分叉的,可以把单路径所有的组合分别连接到下面的部分挖掘结果上输出,那就不用遍历上面的单路径了) 另外树上节点用什么数据结构保存指向子孙节点的指针,能同时兼顾查询时间和空间?
最后我们总结一下apriori和fp-growth之间的联系和差异。
初读fp-growth算法,估计都感觉不到它和apriori有什么关系,但我个人猜测fp-growth是从apriori的统计候选集太耗时的那里 改良开始的,希望实现候选项集的更快速的计算支持度,最后就彻底的改变的搜索频繁项集的方式。我觉得两个算法的最根本的差异在于apriori是以搜索项集组合的空间作为基础,通过数据库来对照。而fp-growth是以数据库为基础,在里面寻找项集是否频繁,表现为搜索方式一个是广度优先一个是深度优先。。
apriori的那剪枝步和统计支持度在fp-growth上就是不断的建fp-tree和遍历。而前者的统计需要经过的IO,后者已经压缩到内存了;但fp-growth不是在所有数据集上都比apriori强,比如在稀疏的数据集上,fp-tree每个节点可能包含非常多子孙,因此保存子孙节点的指针也是很大开销,fp-tree本来就是通过压缩使得数据集能被内存容纳,结果导致最后fp-tree起不到压缩效果适得其反。优化实现的apriori在稀疏数据集上也往往比fp-growth要快。
这里fp-growth在大部分地方是完胜了apriori,后面很多改进都是基于深度优先的思想,并且更注重实现上的技巧。现在我们也没必要去费太多精力去改进这两个算法了,因为频繁项集挖掘是个组合爆炸的时间复杂度。在2003 2004年ICDM举办过两个workshop就是专门比谁实现的频繁项集挖掘最好(搜"FIMI 03",网站里有很多的源码)。在这里想多提一点,数据挖掘中,没有算法能在所有数据集上PK掉其他算法。因此我们应该了解一种任务的多种算法,看看它们为什么和如何在不同的数据集上体现出自己的优势,这样,通过比较我们不仅能更好的理解和掌握它们的精华,更能在当我们遇到新的数据集的时候,选取合适算法甚至做出针对性的优化措施。
频繁项集挖掘之apriori和fp-growth的更多相关文章
- 海量数据挖掘MMDS week2: 频繁项集挖掘 Apriori算法的改进:非hash方法
http://blog.csdn.net/pipisorry/article/details/48914067 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...
- 海量数据挖掘MMDS week2: 频繁项集挖掘 Apriori算法的改进:基于hash的方法
http://blog.csdn.net/pipisorry/article/details/48901217 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...
- 海量数据挖掘MMDS week2: Association Rules关联规则与频繁项集挖掘
http://blog.csdn.net/pipisorry/article/details/48894977 海量数据挖掘Mining Massive Datasets(MMDs) -Jure Le ...
- 频繁项集挖掘之Aprior和FPGrowth算法
频繁项集挖掘的应用多出现于购物篮分析,现介绍两种频繁项集的挖掘算法Aprior和FPGrowth,用以发现购物篮中出现频率较高的购物组合. 基础知识 项:“属性-值”对.比如啤酒2罐. 项集:项的集 ...
- FP-growth算法发现频繁项集(一)——构建FP树
常见的挖掘频繁项集算法有两类,一类是Apriori算法,另一类是FP-growth.Apriori通过不断的构造候选集.筛选候选集挖掘出频繁项集,需要多次扫描原始数据,当原始数据较大时,磁盘I/O次数 ...
- Python两步实现关联规则Apriori算法,参考机器学习实战,包括频繁项集的构建以及关联规则的挖掘
.caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px so ...
- 手推FP-growth (频繁模式增长)算法------挖掘频繁项集
一.频繁项集挖掘为什么会出现FP-growth呢? 原因:这得从Apriori算法的原理说起,Apriori会产生大量候选项集(就是连接后产生的),在剪枝时,需要扫描整个数据库(就是给出的数据),通过 ...
- 关联规则—频繁项集Apriori算法
频繁模式和对应的关联或相关规则在一定程度上刻画了属性条件与类标号之间的有趣联系,因此将关联规则挖掘用于分类也会产生比较好的效果.关联规则就是在给定训练项集上频繁出现的项集与项集之间的一种紧密的联系.其 ...
- 【Storm】Storm实战之频繁二项集挖掘
一.前言 针对大叔据实时处理的入门,除了使用WordCount示例之外,还需要相对更深入点的示例来理解Storm,因此,本篇博文利用Storm实现了频繁项集挖掘的案例,以方便更好的入门Storm. 二 ...
随机推荐
- MySQL 开启远程访问权限
1.登陆mysql数据库 mysql -u root -p 查看user表 mysql> use mysql;Database changedmysql> select host,u ...
- android textview settext卡顿深层次原因
最近在公司项目里面发现listview里面的textview在调用settext函数的时候非常耗时,当时都有点不敢相信,这是因为如果你把textview设置成wrap_content,则每次调用set ...
- vue组件介绍
https://www.cnblogs.com/Leo_wl/p/5863185.html vue.js说说组件 什么是组件:组件是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重 ...
- 2018.07.12 atcoder Choosing Points(数学分析好题)
传送门 一句话题意:给出n,d1,d2" role="presentation" style="position: relative;">n,d ...
- arduino 驱动电调
#include <TimerOne.h> #define PPMPIN 7 ; //0-9 ; void setup() { // put your setup code here, t ...
- SPSS-比较均值-独立样本T检验 案例解析
在使用SPSS进行单样本T检验时,很多人都会问,如果数据不符合正太分布,那还能够进行T检验吗?而大样本,我们一般会认为它是符合正太分布的,在鈡型图看来,正太分布,基本左右是对称的,一般具备两个参数,数 ...
- 如何将word中的图片和文字导入自己的博客中
目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...
- 字符串"k:1“” 处理成字典 {'k':1,'k1':2....}
1.有字符串"k:1|k1:2|k2:3|k3:4" 处理成字典 {'k':1,'k1':2....} #第一种方法 s1 = "k:1|k1:2|k2:3|k3:4&q ...
- Hdu1342 Lotto 2017-01-18 17:12 44人阅读 评论(0) 收藏
Lotto Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submiss ...
- 几个经典的数学库之一学习---VCGlib(1)
1. VCG Libary是Visulization and Computer Graphics Libary(可视化与计算机图形学库)的缩写,是一个开源的C++模板库,用于三角网格和四面体网格的控制 ...