前面介绍的机器学习算法均为监督学习方法,即“对于输入数据X能预测变量Y”,下面学习几个非监督学习算法,即回答“从数据X中能发现什么”问题,这里需要回答的X方面的问题可能是“构成X的最佳5个数据簇有哪些”或者“X中哪三个特征最频繁地一起出现”等。简单的聚类方法(包括k均值聚类)就不赘述了,直接介绍两个关系分析算法:Apriori算法,FP-growth算法。
从大规模数据中寻找物品间的隐含关系被称为关联分析,或者关联规则学习。这些关系可以有两种形式:频繁项集,关联规则。
显然,暴力搜索法是不可行的,下面介绍一种高效的算法 —— Apriori算法。
Apriori原理是说如果某个相集是频繁的,那么它的所有子集也是频繁的。
关键是其逆否命题:如果一个相集是非频繁集,那么它的所有超集也是非频繁的。
显然,此原理可以避免相集数目的指数增长,减少在数据集上进行检查的集合的数目。
算法流程可以概括为
根据数据集构建初始集合C_1 当集合中项的个数k大于0时: 对C_k以最小支持度进行过滤,生成频繁项集集合L_k 对L_k中元素相互组合,生成候选项集集合C_k+1
我们在来看步骤“对L_k中元素相互组合,生成候选项集集合C_k+1”,假设共有M个长度为k的项集,机器学习实战中采用下述方法将项集两两组合:满足两两中前k-1个元素相同,只后一个元素要求前一条记录的商品名称小于后一条记录的商品名称,这样是为了避免重复组合,求它们的并集得到长度为k+1的准频繁项集。可见,这个过程最多共有C(M,2)中组合,这对于M很大的情况显然并不理想。其实,可以采用Apriori原理先对L_k进行预处理,再进行元素组合得到C_k+1。
具体说来,对L_k中的每个项集,观察该项集的任意k-1长度的子项集是否包含在L_k-1中,如果没有,说明该子项集为非频繁项集,那么根据Apriori原理,其超项集必然也不是频繁项集,因此,L-k中的该项集应该被删除。
可见Apriori算法有个最大的问题就是要产生大量准频繁项集或者说候选集,效率不高,并且要多次扫描数据库,在后面的FP-growth算法将避免了这两个个问题。
前面给出了频繁项集的量化定义,即它满足最小支持度要求。对于关联规则,这种量化指标成为可信度。
Apriori原理应用到关联规则的结论是:假设规则{0,1,2}到{3}不满足最小可信度要求,那么任何前件(左部)为{0,1,2,}的自己的规则都不会满足最小可信度要求。
可以看到,Apriori算法在每次增加频繁集项的大小时,都要重新扫描整个数据集,当数据集很大时,这会显著降低频繁项集发现的速度。后面较少的FP-growth算法则只需要对数据集进行两次遍历,能够显著加快频繁项集的速度
### helper functions def loadDataSet(): return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]] # create the initial itemset C1 def createC1(dataSet): C1 = [] for tran in dataSet: for item in tran: if not [item] in C1: C1.append([item]) C1.sort() return map(frozenset, C1) # scan the dataSet to filter C_k into L_k def scanD(D, Ck, minSupport): ssCnt = {} for tran in D: for can in Ck: if can.issubset(tran): if not ssCnt.has_key(can): ssCnt[can] = 1 else: ssCnt[can] += 1 numItems = float(len(D)) # L_k retList = [] # a dict combine C_k with its support supportData = {} for key in ssCnt: support = ssCnt[key]/numItems if support >= minSupport: retList.insert(0,key) supportData[key] = support return retList, supportData ### Apriori algorithm to find the frequent item sets # create the C_k # actually the Lk in function denotes L_k-1 def aprioriGen(Lk,k): retList = [] lenLk = len(Lk) for ii in range(lenLk): for jj in range(ii+1, lenLk): L1 = list(Lk[ii])[:k-2] L1.sort() L2 = list(Lk[jj])[:k-2] L2.sort() if L1 == L2 : retList.append(Lk[ii] | Lk[jj]) return retList def apriori(dataSet, minSupport=0.5): C1 = createC1(dataSet) D = map(set, dataSet) L1,supportData = scanD(D, C1, minSupport) L = [L1] k = 2 while (len(L[k-2]) > 0): Ck = aprioriGen(L[k-2], k) Lk, supK = scanD(D, Ck, minSupport) supportData.update(supK) L.append(Lk) k += 1 return L, supportData ### Explore the association rules