机器学习之Apriori

本来不想介绍这个算法的,因为这个算法太老了,但是有一次在国内某家在线旅游公司实习的时候发现他们也没有用到什么高大上的算法(说实话有点失望,公司中应用的并不是想象的那样,也许中有微软/谷歌这种级别的公司才能够研究高大上的算法吧),基本上逻辑回归/关联算法/协同过滤,最多加上一个SVD矩阵分解。好了,为了能够解释清楚这个算法,我还是从一个场景讲起:
在互联网商城中,用户会浏览到一系列的数据,并且购买一些商品,通常用户并不会单单买单个商品,比如:小张新买了一个苹果6s,那么正常的话还会买一个保护壳/保护膜/移动电源等等。关联算法就是找出这之中的关系,即用户买了商品A,那么继续购买商品B的可能性。很容易想到这个是用条件概率去计算,已知购买商品A的概率P(A),同时购买商品A和商品B的概率P(AB),那么P(B|A)=P(AB)/P(A),毫无疑问这是条件概率公式,这其中P(A)和P(B|A)有一个美称,叫做支持度(support),P(B|A)(confidence)叫做置信度,那么condifence(A→B)=support(AB)/support(A),很显然也许你会想到我们先从数据库中遍历一遍,计算出两个置信度,然后就能够计算出结果了,当这个值大于某一个门限值的时候,就可以说有足够的信心说明当有人购买商品A后,那么他就有可能购买商品B。但是这样子是一件粗暴的做法(很多算法都是简单,粗暴以我的了解比方说CNN算法就是的,实现不需要预处理,直接进行。。),还是举一个例子来说:我们打算找出{1,2,3,4,5}这5个商品之间的关联关系,那么就有1→2? 2→3? 1,2,3,4->5?…..学过高中数学的人都会明白一共有2^5种可能性存在,难道我要计算这么多次吗?好可怕?这尼玛能做吗?科学吗?。。。。Apriori算法由此诞生,就是为了解决这个问题而提出的,先看如下图:

上图中是4个商品,理论上是2^4种组合方式,Apriori是一个很聪明的算法,他不会这么粗暴,他是一个有内涵的绅士,他的策略是层层进行,嘘嘘渐进的完成上述工作,在解释之间,先说一个有趣的现象:如上图阴影部分表示支持度不达标的组合(比方说有些商品同时出现的次数比较少,那么就应该不去考虑),很显然{2,3}不频繁那么所有包含{2,3}的集合也不频繁({0,2,3},{1,2,3},{0,1,2,3}),利用这个方法可以减少很多计算量(前提是门限值取得要事宜,这个取值究竟是多少,我只能说呵呵。。。)
上述的问题可以用概率去描述,比如:
P(A)<Y=>P(AB)<Y?
是否成立,证明也很简单:
P(AB)=P(A)P(B|A)P(A)<Y
仅仅得到频繁集是不够的,还需要计算置信度,及A->B的可信程度,算法的实现上会设置一个门限值,大于这个值则可信,小于这个值则不可信。先来说一个有趣的事情:

上图所示,灰色部分是不可信部分,我们由012->3不可信,可以推断出12->02,02->13,01->23,2->013,1->023,0->123不可信,证明如下:
[

P(0123|012)<Y=>P(0123)P(012)<Y=>P(03|12)P(12)P(0|12)P(12)<Y=>P(03|12)<YP(0|12)Y.........=>P(13|02)Y.........=>P(23|01)Y.........=>P(013|2)Y...
]
利用上述方法可以降低程序的运算,具体如下:
input:data
C(1) = data //候选集合{0}{1}{2}{3}
L(1) = scan(C(1)) // 去除不满足支持度的候选集合
i=1
While Ci>0
C(i+1) = Gen(L(i)) // i代表每一个集合的长度
L(i+1) = scan(C(i+1)) // 去除不满足支持度的候选集合
i++
end
output: L
代码如下:

''' Created on Mar 24, 2011 Ch 11 code @author: Peter '''
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 map(frozenset, C1)#use frozen set so we
                            #can use it as a key in a dict 
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
    numItems = float(len(D))
    retList = []
    supportData = {}
    for key in ssCnt:
        support = ssCnt[key]/numItems
        if support >= minSupport:
            retList.insert(0,key)
        supportData[key] = support
    return retList, supportData
def aprioriGen(Lk, k): #creates Ck
    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 = 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)#scan DB to get Lk
        supportData.update(supK)
        L.append(Lk)
        k += 1
    return L, supportData
def generateRules(L, supportData, minConf=0.7):  #supportData is a dict coming from scanD
    bigRuleList = []
    for i in range(1, len(L)):#only get the sets with two or more items
        for freqSet in L[i]:
            print 'i', i, freqSet
            H1 = [frozenset([item]) for item in freqSet]
            if (i > 1):
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
            else:
                print freqSet,H1
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList         
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    prunedH = [] #create new list to return
    for conseq in H:
        conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence
        if conf >= minConf: 
            print freqSet-conseq,'-->',conseq,'conf:',conf
            brl.append((freqSet-conseq, conseq, conf))
            print 'conseq', conseq
            prunedH.append(conseq)
    return prunedH
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
    m = len(H[0])
    if (len(freqSet) > (m + 1)): #try further merging
        Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        if (len(Hmp1) > 1):    #need at least two sets to merge
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)

def pntRules(ruleList, itemMeaning):
    for ruleTup in ruleList:
        for item in ruleTup[0]:
            print itemMeaning[item]
        print " -------->"
        for item in ruleTup[1]:
            print itemMeaning[item]
        print "confidence: %f" % ruleTup[2]
        print       #print a blank line
# dataSet = loadDataSet()
# C1 = createC1(dataSet)
# print C1
# D = map(set, dataSet)
# print D
# L1,suppData0 = scanD(D,C1,0.5)
# print L1
dataSet = loadDataSet()
L,suppData=apriori(dataSet)
print L
print suppData
rules=generateRules(L,suppData, minConf=0.7)
print rules

你可能感兴趣的:(算法,python,机器学习,Apriori)