Apriori算法 ??Apriori算法用于关联分析,其目标包括两个:发现频繁项集,发现关联规则。首先需要发现频繁项集,然后才能发现关联规则。本文Apriori部分的代码来自《机器学习实战》,有需要可以看看。
发现频繁项集 ??频繁项集指那些经常出现在一起的集合。若某个项集是频繁项集,则它的所有子集也是频繁的。反之,若一个项集是非频繁项集,则它的所有超集也是非频繁的。Apriori利用这个原理,避免计算非频繁项集及其所有超集的支持度。
??给定数据集,用Apriori发现频繁项集,只有一个输入参数:最小支持度。Apriori初始生成所有单个物品的候选集,然后扫描数据集,滤掉小于最小支持度的候选集,接着把剩下的集合进行组合,生成包含两个元素的候选集,如此持续下去,直到所有候选集被消去。
??频繁项集的量化指标是支持度,频繁项集必须满足支持度需求。
# 提取数据集的单元素集合(大小为1的候选集的集合)
def createC1(dataSet):
C1 = set()
for transaction in dataSet:
for item in transaction:
C1.add(item)
C1 = list(C1)
C1.sort()
# 用frozenset是为了能作为key
return map(lambda x:frozenset([x]), C1)# 计算Ck中每项的支持度并过滤
def scanD(D, Ck, minSupport):
ssCnt = {}
for tid in D:
for can in Ck:
if can.issubset(tid):
if not ssCnt.has_key(can):
ssCnt[can] = 1
else:
ssCnt[can] += 1
retList = [] # Lk
supportData = https://www.it610.com/article/{} # 支持度(出现比例)
for key in ssCnt:
support = ssCnt[key] / float(len(D))
if support>= minSupport:
retList.insert(0, key) # 阈值过滤
supportData[key] = support
return retList, supportData
??
createC1()
用于初始化,构建集合C1
,它的每个元素都是大小为1的候选集,顾名思义,Ck
的每个元素大小为k。scanD()
用于扫描数据集,判断C1
里的每一个候选集是否满足最小支持度需求,过滤掉,然后剩下的构成L1
,L1
中的元素后续会进行组合,构成C2
,然后过滤成L2
,如此循环往复。# 构造下一个候选集Ck
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: # 如果它们前k-2项相同
retList.append(Lk[i] | Lk[j]) # 合并
return retListdef apriori(dataSet, minSupport=0.5):
C1 = createC1(dataSet)
D = map(set, dataSet)
L1, supportData = https://www.it610.com/article/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
??
aprioriGen()
接收Lk
,创建候选集Ck
,比如输入{0}、{1}、{2},就会输出{0,1}、{0,2}、{1,2}。注意这里有一个判断,若输入的两个元素(集合)有k-2项相同才合并,这个做法的目的是减少遍历次数。主函数是apriori()
,它将持续生成Ck
,然后过滤生成Lk
,一直到无法继续为止。挖掘关联规则 ??关联规则源于频繁项集,任意一个频繁项集都能产生若干条关联规则,其中只有一部分成立。例如{“啤酒”,“奶粉”},可能有一条规则“啤酒”→“奶粉”,但是反之不一定成立。
??关联规则的量化指标是可信度,规则P→H的可信度定义为 support(P∪H)/support(P)。对于任何一个频繁项集,其产生的所有关联规则,都要计算可信度,把低可信度的规则去掉。然而有时候频繁项集的元素个数太多,可能产生很多关联规则,为了减少计算复杂度,利用这个规律:若某条规则不满足最小可信度需求,则该规则的所有子集也不满足最小可信度需求。
# 计算可信度
def calcConf(freqSet, H, supportData, br1, minConf=0.7):
prunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet - conseq]
if conf >= minConf: # 过滤
# print "{0} --> {1} conf:{2}".format(freqSet - conseq, conseq, conf)
br1.append((freqSet - conseq, conseq, conf))
prunedH.append(conseq)
return prunedHdef rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7):
m = len(H[0])
if len(freqSet) > m+1:
Hmp1 = aprioriGen(H, m+1)
Hmp1 = calcConf(freqSet, Hmp1, supportData, br1, minConf)
if len(Hmp1)>1:
rulesFromConseq(freqSet, Hmp1, supportData, br1, minConf)def generateRules(L, supportData, minConf=0.7):
bigRuleList = []
for i in range(1, len(L)):
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet]
if i>1:
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
else:
calcConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
??
generateRules()
是主函数,它有三个输入:频繁项集列表L
、支持度表supportData
和最小可信度minConf
,输出一个包含可信度的规则列表bigRuleList
。它会遍历L
中每一个频繁项集,对每个频繁项集创建只包含单个元素集合的列表H1
,如果每个项集只有1个元素,直接用calcConf()
计算可信度,否则用rulesFromConseq()
进行合并。示例:发现毒蘑菇的相似特征 ??Apriori还可以拿来做分类。我们不寻找所有的频繁项集,只对包含label的项集感兴趣。也就是说我们需要找到一些关联规则P→H,在H中包含label。这样我们就可以遍历所有样本,若P是样本的子集,就将对应的置信度作为样本是对应label的概率。当匹配的P很多时,可以取平均值。这种做法具有可解释性,可以理解为,“特征搭配为xxx和xxx时,平均有xx%的概率是正例”。
蘑菇数据可以在这里或这里找到。
为了保证label被包含在H中,需要对
calConf()
函数做一点改动。def calcConf(freqSet, H, supportData, br1, minConf=0.7):
prunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet - conseq]
if conf >= minConf:
# 仅当label被包含在H中
if "1" in conseq or "0" in conseq:
# print "{0} --> {1} conf:{2}".format(freqSet - conseq, conseq, conf)
br1.append((freqSet - conseq, conseq, conf))
prunedH.append(conseq)
return prunedH
主函数
import apriori
import time
import numpy as np# 读取训练集
with open("./data/agaricus_train.csv", "rb") as f:
dataSet = [line[:-1].split(',') for line in f.readlines()]# L中的每一个元素都至少在25%的样本中出现过
L, suppData = https://www.it610.com/article/apriori.apriori(dataSet, 0.25) # 阈值越小,越慢# 生成规则,每个规则的置信度至少是0.6
bigRuleList = apriori.generateRules(L, suppData, 0.6)# P→H,根据P集合的大小排序
bigRuleList = sorted(bigRuleList, key=lambda x:len(x[0]), reverse=True)# 读取测试集
with open("./data/agaricus_test.csv", "rb") as f:
dataSet = [line[:-1].split(',') for line in f.readlines()]
labels = np.array([int(x[0]) for x in dataSet])scores = []
for line in dataSet:
tmp = []
for item in bigRuleList:
if item[0].issubset(set(line)):
if "1" in item[1]:
tmp.append(float(item[2]))
# 因为是预测“为1的概率”,所以要用1减
if "0" in item[1]:
tmp.append(1 - float(item[2]))
scores.append(np.mean(tmp)) # 求取均值# 用置信度作为预测概率
scores = map(lambda x:x>0.5, scores)
scores = np.array(scores, dtype='int')
print sum(np.equal(scores, labels)), len(labels), sum(np.equal(scores, labels))/float(len(labels))
输出
1427 1611 0.8857852265673495
虽然88.6%的识别率很低,但将最小支持度进一步调低,应该还可以提高识别率,此外,也可以将这个置信度当做一维特征,而不是单纯作为预测概率。
Apriori的缺点 ??如上所见,Apriori是真的很慢,慢到令人发指。尽管它已经利用非频繁项集的超集非频繁的原理节省了不少计算量,但才不到1w行的mushroom数据,支持度选取25%,都要3分钟左右的处理时间。这是由于对于每一个 kk ,Apriori都要遍历一次整个数据集,而当支持度变低,满足条件的频繁项集变多,这个时间消耗就进一步增加。
完整实验代码 https://github.com/SongDark/Apriori
参考资料 【机器学习|Apriori算法与python实现】《机器学习实战》
数据挖掘十大算法之Apriori详解
Frequent Itemset Mining Dataset Repository
UCI mushroom
mushroom 分训练集测试集
推荐阅读
- paddle|动手从头实现LSTM
- 人工智能|干货!人体姿态估计与运动预测
- 推荐系统论文进阶|CTR预估 论文精读(十一)--Deep Interest Evolution Network(DIEN)
- Python专栏|数据分析的常规流程
- 读书笔记|《白话大数据和机器学习》学习笔记1
- Pytorch学习|sklearn-SVM 模型保存、交叉验证与网格搜索
- Python机器学习基础与进阶|Python机器学习--集成学习算法--XGBoost算法
- 深度学习|深度学习笔记总结
- 机器学习|机器学习Sklearn学习总结
- 机器学习|线性回归原理与python实现