机器学习时常遇到一个问题:当数据并不彻底可分时,分类器得分不高。真实世界中的数据常常是这样:各类无心义数据和少许有意义数据混在一块儿,无心义数据又没什么规律,没法统一去除。好比说,对股票外汇市场受各类因素影响,预测第二天涨跌通常各算法效果都很差。虽然找不到通用的规则,却能在数据中探索到一些模式,好比十字星,孕线,三只乌鸦等组合仍是具备必定的预测性。
以前使用决策树时,就遇到过这种状况,虽然总体得分不高,但某些叶节点上纯度高(全是正例或全是反例)而且实例多,能够把该分枝拿出来当规则使用。虽然不能用它预测任意数据,但能够做为一个过滤器使用。html
规则模型和决策树同属逻辑模型,不一样的是决策树对正例反例一样重视,而规则只重视正例/反例其中一项。所以决策树呈现的是互斥关系,而规则模型容许重叠,结果也相对零散的规则列表。规则更像是在大量数据中挑选有意义的数据。
若是说树是精确模型,规则模型则是启发式策略(虽然通过修改也能覆盖全部实例,但通常不这么用)。它能够找到数据集中的一个子集,相对于所有数据,该子集有明显的意义。
规则模型多用于处理离散数据,如文本中查找频繁单词,提取摘要,分析购物信息等等。python
在实现上,咱们能够把规则当成树的变种,稍加修改,即是规则模型。具体有两种作法:一种是找规则,使其覆盖同质(全真或全假)的样本集(和树相似);另外一种是选定类别,找覆盖该类别实例样本的规则集。算法
规则是一个用途不少的算法,关于规则模型的文章很少,有的书把它纳入逻辑推理中,而非机器学习。咱们最多见的是关联规则,好比用Apriori实现的频繁项集挖掘算法。
最经典的是购物篮分析中啤酒、尿布的故事,即经过对购物清单的分析,发现看似毫无关系的啤酒和尿布常常在用户的购物篮中一块儿出现,经过挖掘出啤酒、尿布这个频繁项集,则当一个用户买了啤酒的时候能够为他推荐尿布,从而达到组合营销的目的。
下面将介绍两种基于关联规则的无监督学习算算法Apriori和FP-growth数组
Apriori这个单词在拉丁语中的意思是“来自之前”,也可拆开为a priori,即一次先验。算法的目标是找到出现频率高的简单规则。app
若是某个项是频繁的,那么它的全部子集也是频繁的。反之,若是一个项集是非频繁的,那么它的超集也是非频的。好比啤酒和尿布经常同时出现,则啤酒单独出现的机率也很高;若是这个地区的人极少喝啤酒,啤酒和尿布的组合也不会经常出现。机器学习
生成单个物品列表,去掉频率低于支持度的,再组合两个物品,去掉低于支持度的,以此类推,求出频繁项集,在频繁项集中抽取关联规则。
算法的输入是大量可能相关的数据组合,支持度,置信度。输出是频率项集或关联规则。性能
Apriori的优势是易于理解,缺点是算得慢,若是共有N件物品,计算量是2^N-1。在属性过多,或属性的状态过多时都会致使大量计算。学习
支持度:数据集中包含该项集的记录所占的比例
置信度:同时支持度/部分支持度(纯度)
频繁项集:常常同时出现的物品的集合
关联规则:两种物品间可能存在很强的关系,好比A,B同时出现,若是A->B,则A称为前件,B称为后件。若是A发生几率50%,而AB几率25%,则A不必定引起B,但若是AB发生几率为49%,则说明A->B。大数据
i. 功能
从屡次购物数据中取频率项集,并显示各组合的支持度code
ii. 代码
# -*- coding: utf-8 -*- from numpy import * def loadDataSet(): return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]] # 创建全部单项的集合,如购物中的物品集合 def createC1(dataSet): C1 = [] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) C1.sort() return list(map(frozenset, C1)) # D是数据,Ck是各个组的集合,返回知足支持率的组 def scanD(D, Ck, minSupport): ssCnt = {} # 字典 for tid in D: # 每条记录 for can in Ck: # 每一个组 if can.issubset(tid): if not can in ssCnt: # 把组放入字典 ssCnt[can]=1 else: ssCnt[can] += 1 numItems = float(len(D)) retList = [] supportData = {} for key in ssCnt: support = ssCnt[key]/numItems if support >= minSupport: retList.insert(0,key) # 把字典中的该项加入list supportData[key] = support return retList, supportData # 创建各个层次的组, 只创建不判断, k是组里元素的个数 def aprioriGen(Lk, k): retList = [] lenLk = len(Lk) for i in range(lenLk): for j in range(i+1, lenLk): L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2] L1.sort(); L2.sort() if L1==L2: #if first k-2 elements are equal retList.append(Lk[i] | Lk[j]) #set union return retList def apriori(dataSet, minSupport = 0.5): C1 = createC1(dataSet) D = list(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 if __name__ == '__main__': dataSet = loadDataSet() # 数据是二维数组,每项可看做如一次购物 L,suppData = apriori(dataSet, minSupport = 0.7) print(L) print(suppData)
iii. 结果
[[frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([2, 5])], []] {frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([2]): 0.75}
FP是Frequent Pattern的缩写,表明频繁模式。FP-growth比Apriori快,性能提升在两个数量级以上,在大数据集上表现更佳。
和Apriori屡次扫描原始数据相比,FP-Growth算法则只需扫描原始数据两遍,把数据存储在FP-Tree结构中。
与搜索树不一样的是,一个元素项能够在FP树中出现屡次,FP树会存储项集的出现频率。每一个项集会以路径的方式存储在树中,存在类似元素的集合会共享树的一部分,只当集合之间彻底不一样时,树才会分叉。
除了树,还有个索引表(Header table),把全部含相同元素的组织起来(link list),以便查找。
先构建FP树,而后从FP树中挖掘频繁项集
i. 收集数据
数据是五次购物的清单(记录),a,b,c,d…分别表明物品(item)
ii. 去除非频繁项l, i, o等,并排序
iii. 将记录依次加入树,并创建索引表(左侧框),粉色为添加第一次购物数据。
iv. 从下往上构造每一个item的条件模式基CPB(conditional pattern base)
顺着header table中item的链表,找出全部包含该item的前缀路径,这些前缀路径就是该item的条件模式基(CPB)
全部这些CPB的频繁度(计数)为该路径上item的频繁度(计数)
如包含p的其中一条路径是fcamp,该路径中p的频繁度为2,则该CPB fcam的频繁度为2
v. 构造条件FP-tree(conditional FP-tree)
累加每一个CPB上的item的频繁度(计数),过滤低于阈值的item,构建FP-tree
如m的CPB{<fca:2>, <fcab:1>},f:3, c:3, a:3, b:1, 阈值假设为3,则过滤掉b。
递归的挖掘每一个条件FP-tree,累加后缀频繁项集,直到找到FP-tree为空或者FP-tree只有一条路径。