关联规则(一):Apriori算法和FP Tree算法原理总结

关联规则可以说得上是数据挖掘领域最广为人知的一类算法了,起码对于我来说是这样的,在大三时候第一次接触数据挖掘领域就是Apriori算法了,后来又断断续续地接触到了FP Tree算法。现在因为工作的原因,需要进一步了解频繁序列模式挖掘算法PrefixSpan。

刘建平老师之前总结过这3个算法。因此我在这里转载过来刘老师的文章,在我理解的角度上,补充一些知识点。

本篇博客先介绍Apriori算法和FP Tree算法,因为这2个算法都是挖掘频繁项集的,接下来的一篇博客介绍PrefixSpan算法,该算法的目的与前面2个不同,它是用于挖掘带有时间序列的频繁项集的。

一、Apriori算法

Apriori算法是常用的用于挖掘出数据关联规则的算法,它用来找出数据值中频繁出现的数据集合,找出这些集合的模式有助于我们做一些决策。比如在常见的超市购物数据集,或者电商的网购数据集中,如果我们找到了频繁出现的数据集,那么对于超市,我们可以优化产品的位置摆放,对于电商,我们可以优化商品所在的仓库位置,达到节约成本,增加经济效益的目的。下面我们就对Apriori算法做一个总结。

1.1 频繁项集的评估标准

什么样的数据才是频繁项集呢?也许你会说,这还不简单,肉眼一扫,一起出现次数多的数据集就是频繁项集吗!的确,这也没有说错,但是有两个问题,第一是当数据量非常大的时候,我们没法直接肉眼发现频繁项集,这催生了关联规则挖掘的算法,比如Apriori, PrefixSpan, CBA。第二是我们缺乏一个频繁项集的标准。比如10条记录,里面A和B同时出现了三次,那么我们能不能说A和B一起构成频繁项集呢?因此我们需要一个评估频繁项集的标准。

常用的频繁项集的评估标准有支持度,置信度提升度三个。

支持度就是几个关联的数据在数据集中出现的次数占总数据集的比重。或者说几个数据关联出现的概率。如果我们有两个想分析关联性的数据X和Y,则对应的支持度为:
S u p p o r t ( X , Y ) = P ( X Y ) = n u m b e r ( X Y ) n u m ( A l l S a m p l e s ) Support(X,Y) = P(XY) = \frac{number(XY)}{num(All Samples)} Support(X,Y)=P(XY)=num(AllSamples)number(XY)

以此类推,如果我们有三个想分析关联性的数据X,Y和Z,则对应的支持度为:
S u p p o r t ( X , Y , Z ) = P ( X Y Z ) = n u m b e r ( X Y Z ) n u m ( A l l S a m p l e s ) Support(X,Y,Z) = P(XYZ) = \frac{number(XYZ)}{num(All Samples)} Support(X,Y,Z)=P(XYZ)=num(AllSamples)number(XYZ)

一般来说,支持度高的数据不一定构成频繁项集,但是支持度太低的数据肯定不构成频繁项集

置信度体现了一个数据出现后,另一个数据出现的概率,或者说数据的条件概率。如果我们有两个想分析关联性的数据X和Y,X对Y的置信度为
C o n f i d e n c e ( X ⇐ Y ) = P ( X ∣ Y ) = P ( X Y ) / P ( Y ) Confidence(X \Leftarrow Y) = P(X|Y)=P(XY)/P(Y) Confidence(XY)=P(XY)=P(XY)/P(Y)

以此类推到多个数据的关联置信度,比如对于三个数据X,Y,Z,则X对于Y和Z的置信度为:
C o n f i d e n c e ( X ⇐ Y Z ) = P ( X ∣ Y Z ) = P ( X Y Z ) / P ( Y Z ) Confidence(X \Leftarrow YZ) = P(X|YZ)=P(XYZ)/P(YZ) Confidence(XYZ)=P(XYZ)=P(XYZ)/P(YZ)

举个例子,在购物数据中,纸巾对应鸡爪的置信度为40%,支持度为1%。则意味着在购物数据中,总共有1%的用户既买鸡爪又买纸巾;同时买鸡爪的用户中有40%的用户购买纸巾。

提升度表示含有Y的条件下,同时含有X的概率,与X总体发生的概率之比,即:
L i f t ( X ⇐ Y ) = P ( X ∣ Y ) / P ( X ) = C o n f i d e n c e ( X ⇐ Y ) / P ( X ) Lift(X \Leftarrow Y) = P(X|Y)/P(X) = Confidence(X \Leftarrow Y) / P(X) Lift(XY)=P(XY)/P(X)=Confidence(XY)/P(X)

提升度体现了X和Y之间的关联关系, 可以设定提升度大于1, X ⇐ Y X \Leftarrow Y XY是有效的强关联规则, 提升度小于等于1则X⇐Y是无效的强关联规则 。一个特殊的情况,如果X和Y独立,则有 L i f t ( X ⇐ Y ) = 1 Lift(X \Leftarrow Y) = 1 Lift(XY)=1,因为此时 P ( X ∣ Y ) = P ( X ) P(X|Y) = P(X) P(XY)=P(X)

一般来说,要选择一个数据集合中的频繁数据集,则需要自定义评估标准。最常用的评估标准是用自定义的支持度,或者是自定义支持度和置信度的一个组合

1.2 Apriori算法思想

对于Apriori算法,我们使用支持度来作为我们判断频繁项集的标准。Apriori算法的目标是找到最大的K项频繁集。这里有两层意思,首先,我们要找到符合支持度标准的频繁集。但是这样的频繁集可能有很多。第二层意思就是我们要找到最大个数的频繁集。比如我们找到符合支持度的频繁集AB和ABE,那么我们会抛弃AB,只保留ABE,因为AB是2项频繁集,而ABE是3项频繁集。那么具体的,Apriori算法是如何做到挖掘K项频繁集的呢?

Apriori算法采用了迭代的方法,先搜索出候选1项集及对应的支持度,剪枝去掉低于支持度的1项集,得到频繁1项集。然后对剩下的频繁1项集进行连接,得到候选的频繁2项集,筛选去掉低于支持度的候选频繁2项集,得到真正的频繁二项集,以此类推,迭代下去,直到无法找到频繁k+1项集为止,对应的频繁k项集的集合即为算法的输出结果。

可见这个算法还是很简洁的,第 i i i次的迭代过程包括扫描计算候选频繁 i i i项集的支持度,剪枝得到真正频繁 i i i项集和连接生成候选频繁 i + 1 i+1 i+1项集三步。

我们下面这个简单的例子看看:

CSDN图标

我们的数据集D有4条记录,分别是134,235,1235和25。现在我们用Apriori算法来寻找频繁k项集,最小支持度设置为50%,即至少有2条。首先我们生成候选频繁1项集,包括我们所有的5个数据并计算5个数据的支持度,计算完毕后我们进行剪枝,数据4由于支持度只有25%被剪掉。我们最终的频繁1项集为1235,现在我们链接生成候选频繁2项集,包括12,13,15,23,25,35共6组。此时我们的第一轮迭代结束。

进入第二轮迭代,我们继续扫描数据集计算候选频繁2项集的支持度,接着进行剪枝,由于12和15的支持度只有25%而被筛除,得到真正的频繁2项集,包括13,23,25,35。现在我们链接生成候选频繁3项集,123, 125,135和235共4组,这部分图中没有画出。通过计算候选频繁3项集的支持度,我们发现123,125和135的支持度均为25%,因此接着被剪枝,最终得到的真正频繁3项集为235一组。由于此时我们无法再进行数据连接,进而得到候选频繁4项集,最终的结果即为频繁3三项集235。

注意这里关于L2–>C3是不需要计算的,这是因为在L2–>C3过程中有一个trick: Apriori在生成频繁项集是有规定的:
只有项集的前k-1项是一样的才会拼接计算是不是k+1的频繁项集。比如在L2中,这就意味着只有前2-1=1项一样,才会拼接形成3项频繁项集。对应到文中:L2{1,3}、{2,3}、{2,5}、{3,5} 如果是想拼接{1,3}、{2,3},按照约定,这里第一位分别是1和2,因为不同,所以就直接不用计算了。{2,5},{3,5}也是同样的道理,最后这里只生成了{2,3,5}。

那么,按照上述思路会丢失频繁项吗?

不会。注意:Any subset of a frequent itemset must be frequent,即频繁项集的子集一定是频繁的,那么逆否命题就是如果一个项集不是频繁的,则它的所有超集也不是频繁的。这里,假设没有约束,{1,3}{2,3}可以生成频繁项集,这有{1,2,3}。因为{1,2,3}是频繁项集,所以他的子项集都是频繁的,就应该有{1,3}、{2,3}、{1,2},而{1,2}是不频繁的。所以这里就直接不需要计算{1,2,3}的支持度。

1.3 Apriori算法流程

下面我们对Apriori算法流程做一个总结。

输入:数据集合 D D D,支持度阈值 α \alpha α
输出:最大的频繁k项集

1)扫描整个数据集,得到所有出现过的数据,作为候选频繁1项集。 k = 1 k=1 k=1,频繁0项集为空集。

2)挖掘频繁k项集

        a) 扫描数据计算候选频繁k项集的支持度

        b) 去除候选频繁k项集中支持度低于阈值的数据集,得到频繁k项集。如果得到的频繁k项集为空,则直接返回频繁k-1项集的集合作为算法结果,算法结束。如果得到的频繁k项集只有一项,则直接返回频繁k项集的集合作为算法结果,算法结束。

        c) 基于频繁k项集,连接生成候选频繁k+1项集。

3) 令 k = k + 1 k=k+1 k=k+1,转入步骤2。

从算法的步骤可以看出,Apriori算法每轮迭代都要扫描数据集,因此在数据集很大,数据种类很多的时候,算法效率很低

1.4 Apriori算法总结

Apriori算法是一个非常经典的频繁项集的挖掘算法,很多算法都是基于Apriori算法而产生的,包括FP-Tree,GSP, CBA等。这些算法利用了Apriori算法的思想,但是对算法做了改进,数据挖掘效率更好一些,因此现在一般很少直接用Apriori算法来挖掘数据了,但是理解Apriori算法是理解其它Apriori类算法的前提,同时算法本身也不复杂,因此值得好好研究一番。

二、FP Tree算法原理总结

作为一个挖掘频繁项集的算法,Apriori算法需要多次扫描数据,I/O是很大的瓶颈。为了解决这个问题,FP Tree算法(也称FP Growth算法)采用了一些技巧,无论多少数据,只需要扫描两次数据集,因此提高了算法运行的效率。下面我们就对FP Tree算法做一个总结。

2.1 FP Tree数据结构

为了减少I/O次数,FP Tree算法引入了一些数据结构来临时存储数据。这个数据结构包括三部分,如下图所示:

CSDN图标

第一部分是一个项头表。里面记录了所有的1项频繁集出现的次数,按照次数降序排列。比如上图中B在所有10组数据中出现了8次,因此排在第一位,这部分好理解。第二部分是FP Tree,它将我们的原始数据集映射到了内存中的一颗FP树,这个FP树比较难理解,它是怎么建立的呢?这个我们后面再讲。第三部分是节点链表。所有项头表里的1项频繁集都是一个节点链表的头,它依次指向FP树中该1项频繁集出现的位置。这样做主要是方便项头表和FP Tree之间的联系查找和更新,也好理解。

下面我们讲项头表和FP树的建立过程。

2.2 项头表的建立

FP树的建立需要首先依赖项头表的建立。首先我们看看怎么建立项头表。

我们第一次扫描数据,得到所有频繁1项集的的计数。然后删除支持度低于阈值的项,将1项频繁集放入项头表,并按照支持度降序排列。接着第二次也是最后一次扫描数据,将读到的原始数据剔除非频繁1项集,并按照支持度降序排列。

上面这段话很抽象,我们用下面这个例子来具体讲解。我们有10条数据,首先第一次扫描数据并对1项集计数,我们发现O,I,L,J,P,M, N都只出现一次,支持度低于20%的阈值,因此他们不会出现在下面的项头表中。剩下的A,C,E,G,B,D,F按照支持度的大小降序排列,组成了我们的项头表。

接着我们第二次扫描数据,对于每条数据剔除非频繁1项集,并按照支持度降序排列。比如数据项ABCEFO,里面O是非频繁1项集,因此被剔除,只剩下了ABCEF。按照支持度的顺序排序,它变成了ACEBF。其他的数据项以此类推。为什么要将原始数据集里的频繁1项数据项进行排序呢?这是为了我们后面的FP树的建立时,可以尽可能的共用祖先节点

通过两次扫描,项头表已经建立,排序后的数据集也已经得到了,下面我们再看看怎么建立FP树。

CSDN图标

2.3 FP Tree的建立

有了项头表和排序后的数据集,我们就可以开始FP树的建立了。开始时FP树没有数据,建立FP树时我们一条条的读入排序后的数据集,插入FP树,插入时按照排序后的顺序,插入FP树中,排序靠前的节点是祖先节点,而靠后的是子孙节点如果有共用的祖先,则对应的公用祖先节点计数加1。插入后,如果有新节点出现,则项头表对应的节点会通过节点链表链接上新节点。直到所有的数据都插入到FP树后,FP树的建立完成。

似乎也很抽象,我们还是用第二节的例子来描述。

首先,我们插入第一条数据ACEBF,如下图所示。此时FP树没有节点,因此ACEBF是一个独立的路径,所有节点计数为1, 项头表通过节点链表链接上对应的新增节点。

CSDN图标

接着我们插入数据ACG,如下图所示。由于ACG和现有的FP树可以有共有的祖先节点序列AC,因此只需要增加一个新节点G,将新节点G的计数记为1。同时A和C的计数加1成为2。当然,对应的G节点的节点链表要更新

CSDN图标

同样的办法可以更新后面8条数据,如下8张图。由于原理类似,这里就不多文字讲解了,大家可以自己去尝试插入并进行理解对比。相信如果大家自己可以独立的插入这10条数据,那么FP树建立的过程就没有什么难度了。

CSDN图标
CSDN图标
CSDN图标
CSDN图标
CSDN图标
CSDN图标
CSDN图标
CSDN图标

2.4 FP Tree的挖掘

把FP树建立起来后,怎么去挖掘频繁项集呢?

下面我们讲如何从FP树里挖掘频繁项集。得到了FP树和项头表以及节点链表,我们首先要从项头表的底部项依次向上挖掘。对于项头表对应于FP树的每一项,我们要找到它的条件模式基。所谓条件模式基是以我们要挖掘的节点作为叶子节点所对应的FP子树。得到这个FP子树,我们将子树中每个节点的的计数设置为叶子节点的计数,并删除计数低于支持度的节点。从这个条件模式基,我们就可以递归挖掘得到频繁项集了。

实在太抽象了。还是以上面的例子来讲解。我们看看先从最底下的F节点开始,我们先来寻找F节点的条件模式基,由于F在FP树中只有一个节点,因此候选就只有下图左所示的一条路径,对应{A:8,C:8,E:6,B:2, F:2}。我们接着将所有的祖先节点计数设置为叶子节点的计数,即FP子树变成{A:2,C:2,E:2,B:2, F:2}。一般我们的条件模式基可以不写叶子节点,因此最终的F的条件模式基如下图右所示。

CSDN图标

通过它,我们很容易得到F的频繁2项集为{A:2,F:2}, {C:2,F:2}, {E:2,F:2}, {B:2,F:2}。递归合并二项集,得到频繁三项集为{A:2,C:2,F:2},{A:2,E:2,F:2},…还有一些频繁三项集,就不写了。当然一直递归下去,最大的频繁项集为频繁5项集,为{A:2,C:2,E:2,B:2,F:2}

F挖掘完了,我们开始挖掘D节点。D节点比F节点复杂一些,因为它有两个叶子节点,因此首先得到的FP子树如下图左。我们接着将所有的祖先节点计数设置为叶子节点的计数,即变成{A:2, C:2,E:1 G:1,D:1, D:1}。此时E节点和G节点由于在条件模式基里面的支持度低于阈值,被我们删除,最终在去除低支持度节点并不包括叶子节点后D的条件模式基为{A:2, C:2}。通过它,我们很容易得到D的频繁2项集为{A:2,D:2}, {C:2,D:2}。递归合并二项集,得到频繁三项集为{A:2,C:2,D:2}。D对应的最大的频繁项集为频繁3项集。

CSDN图标

同样的方法可以得到B的条件模式基如下图右边,递归挖掘到B的最大频繁项集为频繁4项集{A:2, C:2, E:2,B:2}。

CSDN图标

继续挖掘G的频繁项集,挖掘到的G的条件模式基如下图右边,递归挖掘到G的最大频繁项集为频繁4项集{A:5, C:5, E:4,G:4}。

CSDN图标

E的条件模式基如下图右边,递归挖掘到E的最大频繁项集为频繁3项集{A:6, C:6, E:6}。

CSDN图标

C的条件模式基如下图右边,递归挖掘到C的最大频繁项集为频繁2项集{A:8, C:8}。

CSDN图标

至于A,由于它的条件模式基为空,因此可以不用去挖掘了。

至此我们得到了所有的频繁项集,如果我们只是要最大的频繁K项集,从上面的分析可以看到,最大的频繁项集为5项集。包括{A:2, C:2, E:2,B:2,F:2}。

通过上面的流程,相信大家对FP Tree的挖掘频繁项集的过程也很熟悉了。

2.5 FP Tree算法归纳

这里我们对FP Tree算法流程做一个归纳。FP Tree算法包括三步:

1)扫描数据,得到所有频繁1项集的的计数。然后删除支持度低于阈值的项,将1项频繁集放入项头表,并按照支持度降序排列。

2)扫描数据,将读到的原始数据剔除非频繁1项集,并按照支持度降序排列。

3)读入排序后的数据集,插入FP树,插入时按照排序后的顺序,插入FP树中,排序靠前的节点是祖先节点,而靠后的是子孙节点。如果有共用的祖先,则对应的公用祖先节点计数加1。插入后,如果有新节点出现,则项头表对应的节点会通过节点链表链接上新节点。直到所有的数据都插入到FP树后,FP树的建立完成。

4)从项头表的底部项依次向上找到项头表项对应的条件模式基。从条件模式基递归挖掘得到项头表项项的频繁项集。

5)如果不限制频繁项集的项数,则返回步骤4所有的频繁项集,否则只返回满足项数要求的频繁项集

2.6 FP tree算法总结

FP Tree算法改进了Apriori算法的I/O瓶颈,巧妙的利用了树结构。利用内存数据结构以空间换时间是常用的提高算法运行时间瓶颈的办法。

在实践中,FP Tree算法是可以用于生产环境的关联算法,而Apriori算法则做为先驱,起着关联算法指明灯的作用。除了FP Tree,像GSP,CBA之类的算法都是Apriori派系的。

参考文献

【1】Apriori算法原理总结

【2】FP Tree算法原理总结
本文转自以上2篇博客。

  • 16
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值