大数据时代开始流行推荐算法,所以作者写了一篇教程来介绍apriori推荐算法。
推荐算法大致分为:
- 基于物品和用户本身
- 基于关联规则
- 基于模型的推荐
基于物品和用户本身
代码语言:javascript复制基于物品和用户本身的,这种推荐引擎将每个用户和每个物品都当作独立的实体,预测每个用户对于每个物品的喜好程度,这些信息往往是用一个二维矩阵描述的。由于用户感兴趣的物品远远小于总物品的数目,这样的模型导致大量的数据空置,即我们得到的二维矩阵往往是一个很大的稀疏矩阵。同时为了减小计算量,我们可以对物品和用户进行聚类, 然后记录和计算一类用户对一类物品的喜好程度,但这样的模型又会在推荐的准确性上有损失。
基于关联规则
代码语言:javascript复制基于关联规则的推荐(Rule-based Recommendation):关联规则的挖掘已经是数据挖掘中的一个经典的问题,主要是挖掘一些数据的依赖关系,典型的场景就是“购物篮问题”,通过关联规则的挖掘,我们可以找到哪些物品经常被同时购买,或者用户购买了一些物品后通常会购买哪些其他的物品,当我们挖掘出这些关联规则之后,我们可以基于这些规则给用户进行推荐。
基于模型的推荐
代码语言:javascript复制基于模型的推荐(Model-based Recommendation):这是一个典型的机器学习的问题,可以将已有的用户喜好信息作为训练样本,训练出一个预测用户喜好的模型,这样以后用户在进入系统,可以基于此模型计算推荐。这种方法的问题在于如何将用户实时或者近期的喜好信息反馈给训练好的模型,从而提高推荐的准确度。
其实在现在的推荐系统中,很少有只使用了一个推荐策略的推荐引擎,一般都是在不同的场景下使用不同的推荐策略从而达到最好的推荐效果,例如 Amazon 的推荐,它将基于用户本身历史购买数据的推荐,和基于用户当前浏览的物品的推荐,以及基于大众喜好的当下比较流行的物品都在不同的区域推荐给用户,让用户可以从全方位的推荐中找到自己真正感兴趣的物品。探索推荐引擎内部的秘密,第 1 部分: 推荐引擎初探
Apriori算法 是一种最有影响力的 挖掘布尔关联规则 的频繁项集的算法,这个算法是属于上面第二条基于关联规则
推荐的算法,本文着重讲解该算法的计算。
按照网上最简单的例子来进行分析:
支持度support=P(AB),指的是事件A和事件B同时发生的概率。 置信度confidence=P(B|A)=P(AB)/P(A),指的是发生事件A的基础上发生事件B的概率。
大致步骤为:
扫描
->计数
->剪枝
->频繁集
->强规则
扫描
扫描候选项集,并将所有的种类汇总,上图可知有种类["A","B","C","D","E"]
计数
计数即统计种类的个数,上图可知:{'A': 2, 'C': 3, 'D': 1, 'E': 3, 'B': 3}
剪枝
剪枝就是把不满足支持度的部分给去掉,支持度自己设定,上图第一次剪枝去掉了D,那么数据库中的所有D也要被去掉,即下次计算不被包含
频繁集
第一次剪枝后就得到了第一个频繁集L1,L1还能继续压缩,所以继续重复前面的几个步骤,直到无法再压缩
前面这些步骤用python写了代码如下:
代码语言:javascript复制# !/usr/bin/python3.4
# -*- coding: utf-8 -*-
# 获取所有元素种类
def getkinds(array):
arr = []
for item in array:
for value in item:
if value in arr:
pass
else:
if value != " ":
arr.append(value)
return arr
# 候选集长度
def getcount(array, support):
# 第一次扫描
# C1
dict = {}
for item in array:
for key in item:
if key in dict.keys():
dict[key] = 1
else:
dict[key] = 1
# 第一次剪枝
newdict = judge_spport(dict, support)
# 第二次扫描
# 构造项集C2
# 两两组合
arr = []
kinds = getkinds(newdict.keys())
for m in range(0, len(kinds)):
for n in range(m 1, len(kinds)):
arr.append(kinds[m] " " kinds[n])
# 计数
dict = {}
for item in array:
for values in arr:
value = values.split(" ")
if value[0] in item and value[1] in item:
if values in dict:
dict[values] = 1
else:
dict[values] = 1
# print(dict)
# {'B A': 1, 'A E': 1, 'C E': 2, 'B E': 3, 'C B': 2, 'C A': 2}
# 第二次剪枝
newdict = judge_spport(dict, support)
# print(newdict)
# {'C E': 2, 'B E': 3, 'C B': 2, 'C A': 2}
# 第三次扫描
# 构造项集C2
# 两两组合
arr = []
kinds = getkinds(newdict.keys())
for m in range(0, len(kinds)):
for n in range(m 1, len(kinds)):
for k in range(n 1, len(kinds)):
arr.append(kinds[m] " " kinds[n] " " kinds[k])
# 计数
dict = {}
for item in array:
for values in arr:
value = values.split(" ")
if value[0] in item and value[1] in item and value[2] in item:
if values in dict:
dict[values] = 1
else:
dict[values] = 1
# print(dict)
# {'E B A': 1, 'E C A': 1, 'E B C': 2, 'B C A': 1}
# 第三次剪枝
newdict = judge_spport(dict, support)
# {'B E C': 2}
return newdict
# 剪枝
# 删除不符合支持度的key
def judge_spport(dict, support):
dic = dict.copy()
for key in dict.keys():
if dict[key] < support:
del dic[key]
return dic
if __name__ == '__main__':
support = 2
info = [["A", "C", "D"], ["B", "C", "E"], ["A", "B", "C", "E"], ["B", "E"]]
dict = getcount(info, support)
print(dict)
打印结果为:
代码语言:javascript复制{'C B E': 2}
强规则计算
上文可知频繁集{C,B,E}的非空真子集有{B,C},{B,E},{C,E},{B},{C}和{E},对应置信度如下:
代码语言:javascript复制B&&C->E confidence=2/2=100%
B&&E->C confidence=2/3=66%
C&&E->B confidence=2/2=100%
B ->C&&E confidence=2/3=66%
C ->B&&E confidence=2/3=66%
E ->B&&C confidence=2/3=66%
对于规则" If B and E then C",同时购买B和E的人中,有66.67%会购买C。
即置信度confidence=P(B|A)=P(AB)/P(A)
计算强规则的代码为:
代码语言:javascript复制# 计算强规则
def getconfidence(dict,array):
# 一一组合
kinds = getkinds(dict.keys())
arr = kinds
newdict = {}
for i in range(0,len(arr)):
denominator1 = 0
numerator1 = 0
denominator2 = 0
numerator2 = 0
for item in array:
if arr[i] in item:
denominator1 = 1
temp = getkinds(dict.keys())
temp.remove(arr[i])
if temp[0] in item and temp[1] in item:
numerator1 = 1
key1 = arr[i] "->" temp[0] " " temp[1]
for item in array:
temp = getkinds(dict.keys())
temp.remove(arr[i])
if temp[0] in item and temp[1] in item:
numerator2 = 1
if arr[i] in item:
denominator2 = 1
key2 = temp[0] " " temp[1] "->" arr[i]
if denominator1 == 0:
newdict[key1] = str(numerator1) "denominator1"
else:
newdict[key1] = str(numerator1) "/" str(denominator1)
if numerator2 == 0:
newdict[key2] = str(denominator2) "numerator2"
else:
newdict[key2] = str(denominator2) "/" str(numerator2)
return newdict
打印结果为:
代码语言:javascript复制{'B->E C': '2/3', 'B C->E': '2/2', 'E C->B': '2/2', 'E->B C': '2/3', 'C->B E': '2/3', 'B E->C': '2/3'}
结果解说
可以知道购买B和C的人很可能买E,买了E和C的人很可能买B,而其他的概率可能要小一些,例如购买了B的人,很可能买E和C。
源码