【问题标题】:Performance of Frequent Itemset mining频繁项集挖掘的性能
【发布时间】:2014-02-11 08:02:08
【问题描述】:

我已经实现了用于挖掘频繁项集的 apriori 算法,它对样本数据的工作正常,但是当我尝试为http://fimi.ua.ac.be/data/retail.dat 提供的零售数据集执行它时,它是大约 3mb 的数据,包含 88k 事务和 1600 个唯一项目,大约需要 29小时。我搜索了性能下降的原因,发现生成候选项目集的算法需要很长时间。任何人都可以帮助我如何提高性能还是这些是正常的算法行为。

【问题讨论】:

  • 请更清楚地说明您所做的以及您的期望。处理数据需要多长时间很大程度上取决于您使用的是哪种算法,以及您是如何实现它的。此外,如果可能的话,您可以写一个关于您正在处理的数据的结构和性质的简短描述。
  • 性能取决于实现。在最低支持 100 的情况下,我需要在此数据集上 15 秒(使用 ELKI 开发分支中的 APRIORI 和 i7 核心 CPU),并且 minsupp=50 在 58 秒内。我认为降低最低支持率没有多大意义。我还没有实现FPGrowth。那么你用了什么

标签: algorithm data-mining distributed-system apriori


【解决方案1】:

这取决于您的实施。实施 Apriori 时可以进行许多优化。

首先,如果您阅读 Agrawal 和 Srikant 的原始 Apriori 论文,您会注意到他们建议使用特殊的哈希树来有效地计算候选人的支持度。如果您想了解这个实现是如何工作的,SPMF 开源数据挖掘库中有一个使用 HashTree 实现的 Apriori 版本。它被命名为 AprioriHT。

另一个避免多次扫描数据库的优化被命名为 AprioriTID。每个项目集都用包含它的事务 id 集(tid 集)进行注释。那么当通过组合A和B两个项目集生成候选时,直接统计AUB的支持度而不扫描数据库,可以进行A和B的tid集的交集。为了进一步优化,可以实现tid sets位向量。此版本的 APriori 称为 AprioriTID。

算法Pascal中提出了另一种优化。就是利用形式概念分析中的生成器项集的概念,在不扫描数据库的情况下,利用生成器的概念来推断某些项集的支持阈值。

另一个优化是按字典顺序对 Apriori 中每个级别的项集进行排序,并按字典顺序对每个项集中的所有项进行排序。然后,在生成候选者时,您可以使用此排序来避免将所有项集相互比较以生成更大的候选者。

还有其他优化。在 FIMI 网站上,有各种不同优化的实现。

如果您想查看优化版本,可以查看我在SPMF data mining library in Java 中的实现。此外,在同一个网站上,您将获得更快算法的实现,例如 FPGrowth。

【讨论】:

    【解决方案2】:

    您使用的是什么算法,您的阈值是多少?

    如果您有满足最低支持的n 1-itemset,Apriori(和许多其他)必须考虑n*(n-1)/2 2-itemset。这当然会变得相当昂贵。在 Apriori 中,2 项集通常是最大和最昂贵的步骤(取决于您的设置,3 项集可能更糟,但通常您不会走那么远……)

    根据您的数据特征,Eclat 的表现可能会更好,也可能更差。 FP-growth 非常聪明,但似乎很难正确有效地实施。

    还存在无数种变体,有些是概率性的,可能会更快。

    但本质上实施和数据集/参数设置很重要。在同一组数据上,一种方法可能优于另一种方法;并且实现可能很容易产生 10 倍的性能差异。特别是对于 APRIORI,很多人并不完全了解所使用的修剪技巧,最终会做太多工作。

    对于您的示例数据集(不幸的是,如果没有解释数字的字典,这完全没有帮助),我的 APRIORI 在 minSupport=1000 的低端 Atom CPU 上大约 1 分钟内完成。找到的 4 个项目集是:

    32, 38, 39, 48: 1236
    32, 39, 41, 48: 1646
    36, 38, 39, 48: 1080
    38, 39, 41, 48: 1991
    38, 39, 48, 110: 1031
    38, 39, 48, 170: 1193
    

    但如前所述,参数对 APRIORI 的影响很大。很多。 APRIORI 在事务数量上的扩展性很好(可能超过了主内存的容量),但它会受到大量候选的影响,因此您需要将 minSupport 设置得足够高。

    使用 minSupport=1000:

    1-frequentItemsets (56)
    2-frequentItemsets (49)
    3-frequentItemsets (24)
    4-frequentItemsets (6)
    APRIORI runtime: 55401 ms
    

    使用 minSupport=500:

    1-frequentItemsets (185)
    2-frequentItemsets (191)
    3-frequentItemsets (79)
    4-frequentItemsets (13)
    5-frequentItemsets (0)
    APRIORI runtime: 136594 ms
    

    如您所见,运行时间增加了一倍多。原因是 1 项集增长了,产生了更多的 2 项集候选者。在 3 项和 4 项集上,差异不再那么大。但总的来说,在一个非常低端的 CPU 上运行 2 分钟还不错。

    将最低支持降至 250:

    1-frequentItemsets (587)
    2-frequentItemsets (640)
    3-frequentItemsets (273)
    4-frequentItemsets (54)
    5-frequentItemsets (4)
    APRIORI runtime: 954984 ms
    

    现在运行时开始爆炸。大约 16 分钟找到 5 项集:

    32, 38, 39, 41, 48: 448
    36, 38, 39, 41, 48: 334
    38, 39, 41, 48, 110: 346
    38, 39, 41, 48, 170: 413
    

    如您所见,38, 39, 41, 48 4 项集在该数据集中发挥着关键作用(但我们已经在第一次运行中发现了这一点)。我们现在还可以为38, 39, 41, 48 -> 32 提供置信度,这将是涉及5 个项目的最置信规则:大约22.5% 涉及前四个的事务还涉及项目32。但鉴于 AFAICT 这个数据集的数字的含义是未知的,我们不知道这个规则在实践中是否真的很有趣,或者只是一个玩具练习。

    达到 minSupport=100,运行时间进一步增长:

    1-frequentItemsets (1857)
    2-frequentItemsets (2785)
    3-frequentItemsets (1475)
    4-frequentItemsets (306)
    5-frequentItemsets (28)
    6-frequentItemsets (0)
    APRIORI runtime: 8256507 ms
    

    即两个多小时。大部分时间都花在了 2 个项目集上:有 170 万个候选者,其中 2785 个是频繁的。对于 3 项集,可以粗略估计只有几千个候选者,但不再是几百万了。我有一些计划,通过根据候选者的数量在两个代码路径之间切换来进一步改进实现。 ('''Update:''' 通过更简单的修改,我进一步将运行时间减少了 20 倍。所以,'''实现很重要''',它可以轻松地将 100 到 1000 倍或更多 -例如,当 APRIORI 剪枝未完全实现时)

    如果我有时间阅读 Eclat 算法并重新实现它,我可以用结果更新它。我相信这里会更快,FP-growth 也会如此。

    【讨论】:

    • 实际上我正在尝试为分布式系统实现先验算法,以便减少数据库扫描的数量。为此,我正在生成项目集中所有可能的项目组合,这些项目成为瓶颈。有没有分布式版本的 FIM 可用。
    • 永远不要生成所有可能的组合,这就是 any FIM 算法的重点。扫描数据库通常不是瓶颈(但它是要检查的组合数量!)。 FPgrowth 是一种特别聪明的算法,可以很好地用于分布式数据库并且只需 2 次数据库扫描。但是很难正确地实现树操作。
    【解决方案3】:

    有一个pretty efficient algorithm proposed by Karp Papadimtrioue Shanke,即在数据的单次遍历中找到候选对象(它基本上是为流处理而设计的),以便为任何theta 中的任何theta 找到频率至少为theta 的项目(0,1).

    高级算法:

    • 将元素收集到 PF 中,计算它们的出现次数
    • 每当遇到 1/θ 不同的元素时,将所有计数器减 1,并从 PF 中删除那些计数器为零的元素
    • 在 PF 中输出在此过程中幸存下来的元素

    该算法产生 1/Theta(最多)个候选者,并且它没有假阴性(不会遗漏任何候选者) - 但它确实有一些假阳性(一些候选者不常见)。

    为简单起见,假设 1/Theta 是一个整数。

    伪代码:

    PF = {} //empty dictionary
    for each element e:
       if PF.contains(e):
           PF[e] = PF[e] + 1
       else:
           PF[e] = 1
           if PF.size() == 1/Theta:
                 for each element k in PF:
                    PF[k] = PF[k] - 1
                    if PF[k] == 0:
                         remove k from PF
    When done, all elements in PF are your candidates.
    

    【讨论】:

      猜你喜欢
      • 2014-01-25
      • 2018-11-06
      • 2011-03-04
      • 2011-10-26
      • 2016-05-02
      • 1970-01-01
      • 1970-01-01
      • 2011-02-24
      • 2018-05-07
      相关资源
      最近更新 更多