频繁项集挖掘之apriori和fp-growth

匿名 (未验证) 提交于 2019-12-03 00:22:01

要弄好这件事不仅需要有效减小搜索空间,而且对每个可能的搜索都必须快速完成。所以频繁项集挖掘在算法实践和编码实现上就要有非常强的技巧。我们就来深入学习apriori和fp-growth中的搜索方式和技巧。这两个算法很容易找到完整的步骤,这里会更注重里面一些精彩之处,但是可能书写不会那么规范,建议和完整算法对照来读。


算法过程如下:

输入:数据集D,支持度minsup

发现1-项集(D);

c


可以通过一张截图来演示一下apriori的过程:



对应第一张图,连接步是从第k层的项集,向下扩展一层的候选项集,剪枝步能够通过apriori性质过滤掉那些肯定非频繁的项集。


剪枝步也需要对每个k-候选项集的k-1子集都进行一次检测,也很耗费时间;统计频繁次数是必须的,因此需要扫描数据库,经历I/O。那么有必要剪枝,直接统计会不会更好呢,虽然没有试验过,但我估计还是剪枝以后减少候选集的统计更划算。而这两个耗时的步骤在实现上如果能使用到技巧,对算法时间影响最直接。比如剪枝步中k-1候选项集需要逐一向已有的k-1频繁项集查询,这用什么数据结构最好?又如扫描数据库的时候是否能过进行一些压缩,相同的记录进行合并减少遍历次数,以及过滤掉对统计没用的记录?


面对apriori的问题,感觉Fp-growth突然间就冒出来了,它是一个挖掘方式和apriori完全不一样的算法,直接看可能不那么像apriori直观,因为算法一开始就介绍了它采用的数据结构和挖掘方式。所以我们先对比下apriori和fp-growth的差异在哪,再介绍它的算法。

在了解了fp-growth的大致思路以后,我们就能介绍它采用的数据结构和算法了。

Fp-tree主要是一种前缀树,和字典树(trie)接近,并且节点把项的次数也记录下了。字符的

根据前面提到fp-growth步骤,需要找数据库上包含某个项的子数据库,不能从树根开始搜索,因此为了方便,需要把fp-tree中所有枝干、叶子上相同的项全串一起,这样项从一个起点开始,向树根遍历,就能得到包含这个项的子数据库了。这些起点和串起相同节点的链就是fp-tree的另一个部分:头表和兄弟链。头表包含树上所有的单项,并是兄弟链的起点,那么fp-tree不仅完整记录了数据库里所需的信息,还能找到对任一项找到包含了它的子数据库。




算法包含两个部分:

FP_growth是个递归算法,期间需要反复遍历树和构建fp-tree。fp-growth中判断单路径部分可以不要,最后实际结果其实是和下面部分是一样的,但是直接计算单路径产生所有组合会便捷很多。另外一点,fp-tree要按支持度降序的顺序的好处有几点?前面说了可以提高共享前缀的可能,提高压缩率,树小了,遍历的步数还能减小,寻找最优压缩的顺序是个NP难问题,因此选这个办法能有个比较好的压缩率足够了。

fp-growth虽然号称不产生候选集,但是实际上候选集产生已经在寻找条件子库的时候隐隐产生了,剪掉非频繁候选项的时候是通过建树步骤中的第一小步完成的。


最后我们总结一下apriori和fp-growth之间的联系和差异。

我觉得两个算法的最根本的差异在于apriori是以搜索项集组合的空间作为基础,通过数据库来对照。而fp-growth是以数据库为基础,在里面寻找项集是否频繁,表现为搜索方式一个是广度优先一个是深度优先。

apriori的那剪枝步和统计支持度在fp-growth上就是不断的建fp-tree和遍历。而前者的统计需要经过的IO,后者已经压缩到内存了;但fp-growth不是在所有数据集上都比apriori强,比如在稀疏的数据集上,fp-tree每个节点可能包含非常多子孙,因此保存子孙节点的指针也是很大开销,fp-tree本来就是通过压缩使得数据集能被内存容纳,结果导致最后fp-tree起不到压缩效果适得其反。优化实现的apriori在稀疏数据集上也往往比fp-growth要快。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!