Deeplearning Algorithms tutorial

谷歌的人工智能位于全球前列,在图像识别、语音识别、无人驾驶等技术上都已经落地。而百度实质意义上扛起了国内的人工智能的大旗,覆盖无人驾驶、智能助手、图像识别等许多层面。苹果业已开始全面拥抱机器学习,新产品进军家庭智能音箱并打造工作站级别Mac。另外,腾讯的深度学习平台Mariana已支持了微信语音识别的语音输入法、语音开放平台、长按语音消息转文本等产品,在微信图像识别中开始应用。全球前十大科技公司全部发力人工智能理论研究和应用的实现,虽然入门艰难,但是一旦入门,高手也就在你的不远处! AI的开发离不开算法那我们就接下来开始学习算法吧!

FP-growth算法(FP-Growth Algorithm)

FP-growth算法是基于Apriori原理的,通过将数据集存储在FP(Frequent Pattern)树上发现频繁项集,但不能发现数据之间的关联规则。FP-growth算法只需要对数据库进行两次扫描,而Apriori算法在求每个潜在的频繁项集时都需要扫描一次数据集,所以说Apriori算法是高效的。其中算法发现频繁项集的过程是:

  • 1.构建FP树;
  • 2.从FP树中挖掘频繁项集。

构建FP树

FP表示的是频繁模式,其通过链接来连接相似元素,被连起来的元素可以看成是一个链表。将事务数据表中的各个事务对应的数据项按照支持度排序后,把每个事务中的数据项按降序依次插入到一棵以 NULL为根节点的树中,同时在每个结点处记录该结点出现的支持度。

FP-growth算法的流程为:首先构造FP树,然后利用它来挖掘频繁项集。在构造FP树时,需要对数据集扫描两边,第一遍扫描用来统计频率,第二遍扫描至考虑频繁项集。下面举例对FP树加以说明。

假设存在的一个事务数据样例为,构建FP树的步骤如下:

事务ID 事务中的元素
001 r,z,h,j,p
002 z,y,x,w,v,u,t,s
003 z
004 r,x,n,o,s
005 y,r,x,z,q,t,p
006 y,z,x,e,q,s,t,m

 结合Apriori算法中最小支持度的阈值,在此将最小支持度定义为3,结合上表中的数据,那些不满足最小支持度要求的将不会出现在最后的FP树中,据此构建FP树,并采用一个头指针表来指向给定类型的第一个实例,快速访问FP树中的所有元素,构建的带头指针的FP树如下:

FP-growth算法(FP-Growth Algorithm) - 图1

结合绘制的带头指针表的FP树,对表中数据进行过滤,排序如下:

事务ID 事务ID 过滤和重排序后的事务
001 r,z,h,j,p z,r
002 z,y,x,w,v,u,t,s z,x,y,s,t
003 z z
004 r,x,n,o,s x,s,r
005 y,r,x,z,q,t,p z,x,y,r,t
006 y,z,x,e,q,s,t,m z,x,y,s,t

在对数据项过滤排序了之后,就可以构建FP树了,从NULL开始,向其中不断添加过滤排序后的频繁项集。过程可表示为:

FP-growth算法(FP-Growth Algorithm) - 图2

因而我们可以实现FP树的构建,我们知道,在第二次扫描数据集时会构建一棵FP树,并采用一个容器来保存树。首先创建一个类来保存树的每一个节点:

  1. from numpy import *
  2. class treeNode:
  3. def __init__(self, nameValue, numOccur, parentNode):
  4. self.name = nameValue
  5. self.count = numOccur
  6. self.nodeLink = None
  7. self.parent = parentNode #needs to be updated
  8. self.children = {}
  9. def inc(self,numOccur):
  10. self.count += numOccur
  11. def disp(self,ind = 1):
  12. print ' '*ind,self.name,' ',self.count
  13. for child in self.children.values():
  14. child.disp(ind+1)
  15. '''
  16. #test
  17. rootNode = treeNode('pyramid',9,None)
  18. rootNode.children['eye'] = treeNode('eye',13,None)
  19. a = rootNode.disp()
  20. print a
  21. '''

FP-growth算法(FP-Growth Algorithm) - 图3

这样,FP树对应的数据结构就建好了,现在就可以构建FP树了,FP树的构建函数如下:

  1. #FP构建函数
  2. def createTree(dataSet,minSup = 1):
  3. headerTable = {}
  4. for trans in dataSet:
  5. for item in trans:
  6. headerTable[item] = headerTable.get(item,0) + dataSet[trans]#记录每个元素项出现的频度
  7. for k in headerTable.keys():
  8. if headerTable[k] < minSup:
  9. del(headerTable[k])
  10. freqItemSet = set(headerTable.keys())
  11. if len(freqItemSet) == 0:#不满足最小值支持度要求的除去
  12. return None,None
  13. for k in headerTable:
  14. headerTable[k] = [headerTable[k],None]
  15. retTree = treeNode('Null Set',1,None)
  16. for tranSet,count in dataSet.items():
  17. localD = {}
  18. for item in tranSet:
  19. if item in freqItemSet:
  20. localD[item] = headerTable[item][0]
  21. if len(localD) > 0:
  22. orderedItems = [v[0] for v in sorted(localD.items(),key = lambda p:p[1],reverse = True)]
  23. updateTree(orderedItems,retTree,headerTable,count)
  24. return retTree,headerTable
  25. def updateTree(items, inTree, headerTable, count):
  26. if items[0] in inTree.children:
  27. inTree.children[items[0]].inc(count)
  28. else:
  29. inTree.children[items[0]] = treeNode(items[0], count, inTree)
  30. if headerTable[items[0]][1] == None:
  31. headerTable[items[0]][1] = inTree.children[items[0]]
  32. else:
  33. updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
  34. if len(items) > 1:
  35. updateTree(items[1::], inTree.children[items[0]], headerTable, count)
  36. def updateHeader(nodeToTest, targetNode):
  37. while (nodeToTest.nodeLink != None):
  38. nodeToTest = nodeToTest.nodeLink
  39. nodeToTest.nodeLink = targetNode

在应用示例之前还需要一个真正的数据集,结合之前的数据自定义数据集:

  1. def loadSimpDat():
  2. simpDat = [['r', 'z', 'h', 'j', 'p'],
  3. ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
  4. ['z'],
  5. ['r', 'x', 'n', 'o', 's'],
  6. ['y', 'r', 'x', 'z', 'q', 't', 'p'],
  7. ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
  8. return simpDat
  9. def createInitSet(dataSet):
  10. retDict = {}
  11. for trans in dataSet:
  12. retDict[frozenset(trans)] = 1
  13. return retDict

结果:

  1. simpDat = loadSimpDat()
  2. initSet = createInitSet(simpDat)
  3. myFPtree,myHeaderTab = createTree(initSet,3)
  4. a = myFPtree.disp()
  5. print a

这样就构建了FP树,接下来就是使用它来进行频繁项集的挖掘。

从FP树中挖掘频繁项集

在构建了FP树之后,就可以抽取频繁项集了,这里的思想和Apriori算法大致类似,首先从氮元素项集合开始,然后在此基础上逐步构建更大的集合。大致分为三个步骤:

  • 1.从FP树中获得条件模式基;
  • 2.利用条件模式基,构建一个条件FP树;
  • 3.迭代重复1和2,直到树包含一个元素项为止。 首先,获取条件模式基。条件模式基是以所查找元素项为结尾的路径集合,表示的是所查找的元素项与树根节点之间的所有内容。结合构建FP树绘制的图,r的前缀路径就是{x,s}、{z,x,y}和{z},其中的每条前缀路径都与一个计数值有关,该计数值表示的是每条路径上r的数目。 为了得到这些前缀路径,结合之前所得到的头指针表,头指针表中包含相同类型元素链表的起始指针,根据每一个元素项都可以上溯到这棵树直到根节点为止。该过程对应的如下:

FP-growth算法(FP-Growth Algorithm) - 图4

  1. def ascendTree(leafNode, prefixPath): #ascends from leaf node to root
  2. if leafNode.parent != None:
  3. prefixPath.append(leafNode.name)
  4. ascendTree(leafNode.parent, prefixPath)
  5. def findPrefixPath(basePat, treeNode): #treeNode comes from header table
  6. condPats = {}
  7. while treeNode != None:
  8. prefixPath = []
  9. ascendTree(treeNode, prefixPath)
  10. if len(prefixPath) > 1:
  11. condPats[frozenset(prefixPath[1:])] = treeNode.count
  12. treeNode = treeNode.nodeLink
  13. return condPats
  14. #test
  15. simpDat = loadSimpDat()
  16. initSet = createInitSet(simpDat)
  17. myFPtree,myHeaderTab = createTree(initSet,3)
  18. a = myFPtree.disp()
  19. b = findPrefixPath('x',myHeaderTab['x'][1])
  20. print b

运行示例,与所给数据一致。接下来就可以创建条件FP树了。对于每一个频繁项,都需要创建一棵条件FP树,使用刚才创建的条件模式基作为输入,采用相同的建树代码来构建树,相应的递归发现频繁项、发现条件模式基和另外的条件树。对应的递归查找频繁项集的函数如下:

  1. def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
  2. bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]#(sort header table)
  3. for basePat in bigL:
  4. newFreqSet = preFix.copy()
  5. newFreqSet.add(basePat)
  6. freqItemList.append(newFreqSet)
  7. condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
  8. myCondTree, myHead = createTree(condPattBases, minSup)
  9. if myHead != None:
  10. mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

结合之前的数据验证发现没有错误。

应用示例

  1. # -*- coding: utf-8 -*
  2. # 树节点
  3. class treeNode:
  4. def __init__(self, nameValue, numOccur, parentNode):
  5. self.name = nameValue
  6. self.count = numOccur # 计数值
  7. self.nodeLink = None # 连接相似变量
  8. self.parent = parentNode #needs to be updated
  9. self.children = {}
  10. def inc(self, numOccur):
  11. self.count += numOccur
  12. # 递归输出树节点
  13. def disp(self, ind=1):
  14. print ' '*ind, self.name, ' ', self.count
  15. for child in self.children.values():
  16. child.disp(ind+1)
  17. # 根据 dataSet 创建 FP-growth 树
  18. # 参数 minSup 指的是最小支持度
  19. def createTree(dataSet, minSup=1):
  20. headerTable = {}
  21. # 遍历所有元素项, 记录其出现次数
  22. # 内容类似为 {'t': 3, 'w': 1, 'v': 1, 'y': 3, 'x': 4, 'z': 5}
  23. for trans in dataSet:
  24. for item in trans:
  25. headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
  26. # 移除未达到最小支持度的元素项
  27. for k in headerTable.keys():
  28. if headerTable[k] < minSup:
  29. del(headerTable[k])
  30. freqItemSet = set(headerTable.keys())
  31. if len(freqItemSet) == 0: return None, None # 没有达到最小支持度的元素项, 返回
  32. # 格式化后格式为:
  33. # {'t': [3, None], 'y': [3, None], 'x': [4, None], 'z': [5, None]}
  34. for k in headerTable:
  35. headerTable[k] = [headerTable[k], None]
  36. retTree = treeNode('Null Set', 1, None) # 只包含空值的根节点
  37. for tranSet, count in dataSet.items(): # count 都初始化为 1
  38. localD = {}
  39. for item in tranSet: # 取满足最小支持度的元素项
  40. if item in freqItemSet:
  41. localD[item] = headerTable[item][0]
  42. # 对每个项集中的元素项, 按支持度从大到小排序
  43. if len(localD) > 0:
  44. orderedItems = [v[0] for v in sorted(localD.items(),
  45. key=lambda p: p[1], reverse=True)]
  46. updateTree(orderedItems, retTree, headerTable, count) # 填充树
  47. return retTree, headerTable # 返回树与各个元素的头指针
  48. # 使用 items 使树生长, FP-growth 的来历
  49. # inTree 为树的根节点
  50. def updateTree(items, inTree, headerTable, count):
  51. # 如果 items 的第一个元素是 树的第一层子节点, 则将此子节点对应的计算 加1
  52. if items[0] in inTree.children:
  53. inTree.children[items[0]].inc(count)
  54. else: # 如果此节点不存在, 则给树添加新子节点
  55. inTree.children[items[0]] = treeNode(items[0], count, inTree)
  56. # 更新头指针表
  57. if headerTable[items[0]][1] == None:
  58. headerTable[items[0]][1] = inTree.children[items[0]]
  59. else:
  60. updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
  61. # 对除去第一个元素之外的 items 递归建树
  62. if len(items) > 1:
  63. updateTree(items[1::], inTree.children[items[0]], headerTable, count)
  64. # 更新头指针链表, targetNode 接到 nodeToTest 所指链表的最后
  65. # 这里不用递归, 因为递归在链表长度过长时, 可能会栈溢出
  66. def updateHeader(nodeToTest, targetNode):
  67. while (nodeToTest.nodeLink != None):
  68. nodeToTest = nodeToTest.nodeLink
  69. nodeToTest.nodeLink = targetNode
  70. # 迭代上溯整棵树
  71. # 即将叶节点到根节点的所有元素都存到 prefixPath 中
  72. def ascendTree(leafNode, prefixPath):
  73. if leafNode.parent != None:
  74. prefixPath.append(leafNode.name)
  75. ascendTree(leafNode.parent, prefixPath)
  76. # 按给定元素项生成一个条件模式基
  77. # 遍历头指针链表, 对每一个元素都向根节点上溯, 记录找到的前缀式及元素出现次数
  78. def findPrefixPath(basePat, treeNode): #treeNode comes from header table
  79. condPats = {}
  80. # 遍历以 treeNode 为头节点的头指针链表
  81. while treeNode != None:
  82. prefixPath = []
  83. ascendTree(treeNode, prefixPath)
  84. # 保存前缀及其次数
  85. if len(prefixPath) > 1:
  86. condPats[frozenset(prefixPath[1:])] = treeNode.count
  87. treeNode = treeNode.nodeLink
  88. return condPats
  89. # 递归查找频繁项集
  90. # 参 freqItemList 返回频繁项集列表
  91. def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
  92. # 头指针按其元素项出现的频率从小到大进行排序
  93. bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]
  94. for basePat in bigL: #start from bottom of header table
  95. newFreqSet = preFix.copy() # 生成一个频繁项集
  96. newFreqSet.add(basePat)
  97. # 创造条件基
  98. freqItemList.append(newFreqSet) # 繁频项集添加到参数中, 以待最后返回
  99. condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
  100. # 创建条件 FP 树
  101. myCondTree, myHead = createTree(condPattBases, minSup)
  102. # 递归挖掘条件 FP 树
  103. if myHead != None:
  104. mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)
  105. # 载入数据
  106. def loadSimpDat():
  107. simpDat = [['r', 'z', 'h', 'j', 'p'],
  108. ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
  109. ['z'],
  110. ['r', 'x', 'n', 'o', 's'],
  111. ['y', 'r', 'x', 'z', 'q', 't', 'p'],
  112. ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
  113. return simpDat
  114. # 初始化每个项集的次数为 1
  115. def createInitSet(dataSet):
  116. retDict = {}
  117. for trans in dataSet:
  118. retDict[frozenset(trans)] = 1
  119. return retDict
  120. if __name__ == "__main__":
  121. # 建树
  122. simpDat = loadSimpDat()
  123. initSet = createInitSet(simpDat)
  124. myFPtree, myHeaderTab = createTree(initSet, 3) # 3 为最小支持度
  125. myFPtree.disp() # 显示树
  126. # 根据树挖掘 频繁项集
  127. freqItems = []
  128. mineTree(myFPtree, myHeaderTab, 3, set([]), freqItems) # 3 为最小支持度
  129. print freqItems