总结
本系列是机器学习课程的系列课程,主要介绍机器学习中关联规则和协同过滤。
参考
机器学习(三):Apriori算法(算法精讲)
Apriori 算法 理论 重点
【手撕算法】【Apriori】关联规则Apriori原理、代码实现
FPGROWTH算法详解
MovieLens:一个常用的电影推荐系统领域的数据集
23张图,带你入门推荐系统
本门课程的目标
完成一个特定行业的算法应用全过程:
懂业务+会选择合适的算法+数据处理+算法训练+算法调优+算法融合
+算法评估+持续调优+工程化接口实现
机器学习定义
关于机器学习的定义,Tom Michael Mitchell的这段话被广泛引用:
对于某类任务T和性能度量P,如果一个计算机程序在T上其性能P随着经验E而自我完善,那么我们称这个计算机程序从经验E中学习。
关联规则
啤酒与尿布
“啤酒与尿布”
的故事相信很多人都听过,年轻爸爸去超市购买尿布时,经常会买点啤酒犒劳自己。因此,沃尔玛将这两种商品进行了捆绑销售,最终获得了更好的销量。
“啤酒与尿布”的故事
这个故事背后的理论依据就是 “推荐算法”,因为尿布和啤酒经常出现在同一个购物车中,那么向购买尿布的年轻爸爸推荐啤酒确实有一定道理。
关联规则算法
获得啤酒与尿布的关系的一种算法就是关联规则算法
:
1.关联规则推荐算法:这种算法基于关联规则挖掘的技术。它通过分析用户行为数据中的项集之间的关联关系,找出频繁项集和关联规则,然后根据这些规则进行推荐。比如,根据用户购买商品的历史记录,可以挖掘出购买商品之间的关联规则,然后根据规则推荐其他相关商品给用户。
关联规则算法最开始是面向购物篮分析问题:
如何在消费者购买了特定商品
,比如PC机和一台数码相机后,作为销售人员的你针对该消费者已购买的商品进行分析(购物篮分析
),可以继续给该消费者推荐什么产品
,该消费者才能更感兴趣。
关联规则算法
可以帮助我们在大量历史销售数字
中发现“已有的多数客户在购买PC机和数码相机后,还经常购买哪些产品”这样的一个规律
。
关联规则
就是通过发现顾客放入“购物篮”中的不同商品之间的关联
,分析顾客的购物习惯,而物品见的某种联系我们称为关联
。
这种关联的发现可以帮助零售商了解哪些商品频繁的被顾客同时购买
,从而帮助他们开发更好的营销策略。
关联规则 (Association Rules,又称 Basket Analysis)
是形如X→Y的蕴涵式
,
其中, X和Y
分别称为关联规则的先导(antecedent或left-hand-side, LHS)
和后继(consequent或right-hand-side, RHS)
。
在这当中,关联规则X→Y
,利用其支持度和置信度
从大量数据中挖掘出有价值的数据项之间的相关关系。
关联规则解决的常见问题如:“如果一个消费者购买了产品A,那么他有多大机会购买产品B?”
以及“如果他购买了产品C和D,那么他还将购买什么产品?”
关联规则定义:
假设
I = {I1,I2,。。。Im}
是包含所有商品(item)的集合,
包含k个项
的项集称为k项集(k-itemset)
。
给定一个交易数据库D
,其中每个事务(Transaction)T是I的非空子集
,即每一个交易都与一个唯一的标识符TID(Transaction ID)对应。
关联规则挖掘的目的
即通过已发生的事务数据,找到其中有效关联性较高的项集
所构成的规则。
那么,如何度量关联规则的有效性及关联性
呢?
首先,该关联规则本身所对应的商品应当具有一定的普遍推荐价值
,即支持度较高
;关联规则在D中的支持度(support)
是D中事务同时包含X、Y的百分比,即概率
;
其次该规则的发生应当具有一定的可能性
,即置信度较高
。置信度(confidence)
是D中事务已经包含X的情况下,包含Y的百分比,即条件概率
。
如果满足最小支持度阈值min_support
和最小置信度阈值min_confidence
,则认为关联规则是重要的。
当一个项集(XY)的支持度大于等于min_support,这个项集就被称为频繁项集(Frequent Itemset)
。
当以频繁项集(XY)构成的关联规则(X→Y)的置信度大于等于min_confidence,这个关联规则就被称为强关联规则
。强关联规则也是关联规则挖掘的最终产出。
关联规则挖掘过程主要包含两个阶段:
第一阶段必须先从资料集合中找出所有的频繁项集(Frequent Itemsets),
第二阶段再由这些高频项目组中产生强关联规则(Association Rules)。
举个栗子🌰说明下
TID | 牛奶 | 面包 | 尿布 | 啤酒 | 鸡蛋 | 可乐 |
---|---|---|---|---|---|---|
1 | 1 | 1 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 1 | 1 | 1 | 0 |
3 | 1 | 0 | 1 | 1 | 0 | 1 |
4 | 1 | 1 | 1 | 1 | 0 | 0 |
5 | 1 | 1 | 1 | 0 | 0 | 1 |
上表格是顾客购买记录的数据库D,包含
5个事务, 即D=5,有5个订单
。
项集I={牛奶,面包,尿布,啤酒,鸡蛋}
。
若给定最小支持度in_support=0.5,最小置信度min_confidence=0.6
,
考虑一个二项集:{牛奶,面包}
,
事务1,4,5同时包含牛奶和面包,那么说明包含牛奶和面包的有3个事务
,即X∩Y=3,支持度(X∩Y)/D=3/5=0.6>min_support
,则{牛奶,面包}是一个频繁项集
。
对于关联规则(牛奶→面包)
,在数据库D中4个事务是包含牛奶的,即X=4
, 因而置信度(X∩Y)/X=3/4=0.75>min_confidence
,则认为购买牛奶和购买面包之间存在强关联
。
关联规则算法Apriori实现
Apriori算法实现原理
R.Agrawal 和 R. Srikant于1994年在文献中提出了Apriori算法,该算法的描述如下图所示:
candidate itemsets候选项集合
frequent itemsets频繁项集合
1)令k = 1
2)统计每个k项集的支持度,并找出频繁k项集
3)利用频繁k项集生成候选k+1项集
4)令k=k+1,重复第 2)步
// 尺寸为k的候选项目集
C_k:Candidate itemsets of size k
//大小为k的频繁项目集
L_k:frequent itemsets of size k
L1={frequent 1-itemsets}; // 大小为1的频繁项目集
// k从1开始,频繁项目集不为0
for (k=1;L_k≠0;k++)
// 从 L_k频繁项目集 中生成 C_k+1候选项目集
C_k+1=GenerateCandidates(L_k)
// 对于每一个数据库D中的事务t
for each transaction t in database do
// 包含在t中的C_k+1中的候选者的增量计数
increment count of candidates in C_k+1 that are contained in t
endfor
// 在 C_k+1中的候选集中找到大于最小支持度的作为L_K+1频繁候选项集
L_k+1=candidates in C_k+i with support >= min_sup
endfor
return U_kL_k;
举例🌰:说明下
Apriori算法实例—产生频繁项集
Apriori算法实例—产生关联规则
Apriori算法思想总结
FPGROWTH算法
Apriori的挑战及改进方案
挑战
多次数据库扫描
巨大数量的候补项集
繁琐的支持度计算
改善Apriori: 基本想法
减少扫描数据库的次数
减少候选项集的数量
简化候选项集的支持度计算
具体参考这个:
FPGROWTH算法详解
手写完整代码
# 1.构建候选1项集C1
def createC1(dataSet):
# 获取数据集中所有不重复的项
c1 = list(set([y for x in dataSet for y in x]))
c1.sort() # 排序
# 将每个项转换为集合形式
c2 = [[x] for x in c1]
# 使用frozenset以便作为字典键使用
return list(map(frozenset, c2))
# 将候选集Ck转换为频繁项集Lk
# D:原始数据集
# Cn: 候选集项Ck
# minSupport:支持度的最小值
def scanD(D, Ck, minSupport):
# 候选集计数字典
ssCnt = {}
# 遍历每条交易记录
for tid in D:
# 遍历每个候选项集
for can in Ck:
# 如果候选项集是当前交易记录的子集
if can.issubset(tid):
# 统计候选项集出现次数
if can not in ssCnt.keys(): ssCnt[can] = 1
else: ssCnt[can] += 1
# 计算总交易数
numItems = float(len(D))
Lk = [] # 候选集项Cn生成的频繁项集Lk
supportData = {} # 候选集项Cn的支持度字典
# 计算候选项集的支持度
for key in ssCnt:
support = ssCnt[key] / numItems
# 如果支持度大于等于最小支持度,加入频繁项集
if support >= minSupport:
Lk.append(key)
# 记录所有候选项集的支持度
supportData[key] = support
return Lk, supportData
# 连接操作,将频繁Lk-1项集通过拼接转换为候选k项集
def aprioriGen(Lk_1, k):
Ck = []
lenLk = len(Lk_1)
for i in range(lenLk):
# 获取前k-2个项
L1_list = list(Lk_1[i])
L1 = L1_list[:k - 2]
L1.sort()
for j in range(i + 1, lenLk):
# 获取另一个项集的前k-2个项
L2_list = list(Lk_1[j])
L2 = list(Lk_1[j])[:k - 2]
L2.sort()
# 前k-2个项相同时,将两个集合合并
if L1 == L2:
Ck.append(Lk_1[i] | Lk_1[j])
return Ck
# Apriori算法主函数
def apriori(dataSet, minSupport=0.5):
# 生成候选1项集
C1 = createC1(dataSet)
# 扫描数据集,生成频繁1项集
L1, supportData = scanD(dataSet, C1, minSupport)
L = [L1] # 存储所有频繁项集
k = 2
# 循环生成更高阶的频繁项集
while (len(L[k-2]) > 0):
Lk_1 = L[k-2]
# 生成候选k项集
Ck = aprioriGen(Lk_1, k)
print("ck:", Ck)
# 扫描数据集,生成频繁k项集
Lk, supK = scanD(dataSet, Ck, minSupport)
supportData.update(supK)
print("lk:", Lk)
L.append(Lk)
k += 1
return L, supportData
# 生成关联规则
# L: 频繁项集列表
# supportData: 包含频繁项集支持数据的字典
# 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
# 计算是否满足最小可信度
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
prunedH = [] # 满足最小置信度的后件集合
# 用每个conseq作为后件
for conseq in H:
# 计算前件支持度
P_A = supportData[freqSet.difference(conseq)]
# 计算置信度
conf = supportData[freqSet] / P_A
if conf >= minConf:
print(freqSet - conseq, '-->', conseq, 'conf:', conf)
# 将规则加入结果列表(前件, 后件, 置信度)
brl.append((freqSet - conseq, conseq, conf))
prunedH.append(conseq)
return prunedH
# 对规则进行评估
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
m = len(H[0]) # 后件长度
if (len(freqSet) > (m + 1)):
# 生成更高阶的后件候选集
Hmp1 = aprioriGen(H, m + 1)
# 计算置信度
Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
if (len(Hmp1) > 0):
# 递归处理更高阶后件
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
# 测试数据集
dataset = [['土豆', '尿不湿', '啤酒'], ['巧克力', '牛奶', '土豆', '啤酒'], ['牛奶', '尿不湿', '啤酒'],
['巧克力', '尿不湿', '啤酒'], ['巧克力', '啤酒']]
# 运行Apriori算法
L, supportData = apriori(dataset, minSupport=0.5)
# 生成关联规则
rules = generateRules(L, supportData, minConf=0.7)
# 打印所有关联规则
for e in rules:
print(e)
运行输出如下:
ck: [frozenset({‘尿不湿’, ‘啤酒’}), frozenset({‘巧克力’, ‘啤酒’}), frozenset({‘尿不湿’, ‘巧克力’})]
lk: [frozenset({‘尿不湿’, ‘啤酒’}), frozenset({‘巧克力’, ‘啤酒’})]
ck: []
lk: []
frozenset({‘尿不湿’}) --> frozenset({‘啤酒’}) conf: 1.0
frozenset({‘巧克力’}) --> frozenset({‘啤酒’}) conf: 1.0
(frozenset({‘尿不湿’}), frozenset({‘啤酒’}), 1.0)
(frozenset({‘巧克力’}), frozenset({‘啤酒’}), 1.0)
调包完成关联规则
efficient_apriori 包实现
# 安装:pip install efficient-apriori==2.0.5
from efficient_apriori import apriori
# 测试数据集
dataset = [['土豆', '尿不湿', '啤酒'], ['巧克力', '牛奶', '土豆', '啤酒'], ['牛奶', '尿不湿', '啤酒'],
['巧克力', '尿不湿', '啤酒'], ['巧克力', '啤酒']]
freqItemSet, rules = apriori(dataset, 0.5, 0.7)
print(freqItemSet)
print(rules)
输出如下:
{1: {(‘尿不湿’,): 3, (‘啤酒’,): 5, (‘巧克力’,): 3}, 2: {(‘啤酒’, ‘尿不湿’): 3, (‘啤酒’, ‘巧克力’): 3}}
[{尿不湿} -> {啤酒}, {巧克力} -> {啤酒}]
fpgrowth_py 包实现
Apriori算法效率比较低,建议在使用的时候直接使用基于Apriori算法开发的FP-growth算法,fpgrowth_py 实现代码如下:
# 安装:pip install fpgrowth_py==1.0.0
from fpgrowth_py import fpgrowth
dataset = [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
freqItemSet, rules = fpgrowth(dataset, 0.5, 0.7)
print(freqItemSet)
print(rules)
dataset = [['土豆', '尿不湿', '啤酒'], ['巧克力', '牛奶', '土豆', '啤酒'], ['牛奶', '尿不湿', '啤酒'],
['巧克力', '尿不湿', '啤酒'], ['巧克力', '啤酒']]
freqItemSet, rules = fpgrowth(dataset, 0.5, 0.7)
print(freqItemSet)
print(rules)
输出如下:
[{1}, {1, 3}, {3}, {2, 3}, {2}, {5}, {3, 5}, {2, 3, 5}, {2, 5}]
[[{1}, {3}, 1.0], [{2, 3}, {5}, 1.0], [{3, 5}, {2}, 1.0], [{2}, {5}, 1.0], [{5}, {2}, 1.0]][{‘尿不湿’}, {‘啤酒’, ‘尿不湿’}, {‘巧克力’}, {‘啤酒’, ‘巧克力’}, {‘啤酒’}]
[[{‘尿不湿’}, {‘啤酒’}, 1.0], [{‘巧克力’}, {‘啤酒’}, 1.0]]