【问题标题】:large-scale document co-occurrence analysis大规模文档共现分析
【发布时间】:2014-02-01 02:58:30
【问题描述】:

我有大约 1000 个文件。每个包含大约 20,000 个文档。我还有一个大约 1,000,000 个单词的列表。

我想计算每个单词与其他单词一起出现的次数。所以,有一个大小为 1M X 1M 的稀疏矩阵。

为了加快计算速度,我正在通过执行以下操作分别处理每个文件:

1- 我机器中的每个核心都在处理单个文件并输出以下格式的文件

WordId1 WordId2 Frequency 

2-在做完每个文件后,我将1000个文件合并为一个文件。

这是我目前的方法,但它需要很长时间才能做到,我认为应该有很多有效的方法,所以欢迎您的 cmets。

【问题讨论】:

  • 你对共现的定义是什么? (两个词出现在同一个文档中?两个词在文档中彼此相邻出现?...?)
  • @meriton 相同的文档。
  • 您计算它们在文档中出现的次数,还是两者都出现任意次数时只计算为 1 或 0?
  • 如果你有单个文档,W1 遇到 2 次,W2 - 3 次,频率表是多少?
  • 这样的关系称为对称的

标签: java multithreading performance processing-efficiency


【解决方案1】:

您在这里遇到了复杂性的基本定律。您正在尝试为大量单词处理大量文档并从中生成大量数据集。

它总是会很慢。

一些可能会加快速度的事情:

  1. 忘记一百万个单词的列表。相反,只需接受您在文本中找到的任何单词,您以后可以随时过滤它们。如果您确实需要过滤列表,请确保列表采用适当的形式(例如 HashSet),以便您快速检查。

  2. 这类事情更有可能是 IO 限制而不是 CPU 限制,因此请尝试在快速 SSD 驱动器上运行它 - 或者如果文件足够小,请设置一个 RAM 磁盘并从中运行它。进行一些监控以确定瓶颈所在。

  3. 正如您已经确定的那样,对每组文件的处理是非常并行的,因此您可以考虑将其分布在多个内核以及多台机器上。

尝试一下(数据库的开销实际上可能会使其变慢): 而不是在最后进行合并,您可以只编译结果以在内存中一起处理一个文档。完成处理后,将单批插入到数据库中。然后,数据库将允许您动态查询结果,使用 sum() 等来查找每个单词组合的总数。这实际上为您提供了比平面文件更灵活/更有用的结果,并且避免了单独的合并步骤。

【讨论】:

    【解决方案2】:

    我想你可以通过仔细处理细节来获得合理的性能。有问题的部分似乎是内存。有了足够的内存,就可以避免写出和合并。

    在处理单个文档时,如果相应的单词存在,则可以将其转换为BitSet

    你的关系是对称的,所以我希望你只存储(a, b, count)a < b

    你需要像Multiset<Pair<String, String>> 这样的东西来计数,但是还有更多的内存保护结构。您的单词已编号,因此每个单词都可以用int 表示,一对可以用long 表示。所以也许像LongIntHashMap 这样的东西会做。您需要并发性,因此您可以将原子用于条目或将映射划分为N 部分(通过一些散列,N 大于核心数)并同步。在 AtomicIntegerArray 之上构建一些东西应该很容易。

    你没有说你的结果是否有可能适合内存,但如果是这样,它可能会导致巨大的加速。

    要求的解释

    字符串的编号从 0 到 100 万,适合 int。两个这样的数字一起放入一个long,可以用作TLongIntHashMap 的键。对于您识别所有相关字符串对的每个文档,获取相应的longs 并增加TLongIntHashMap 中的值。

    这里,只有增量需要在锁定下完成。由于这种锁定会阻碍并发性,我建议使用多个映射,每个映射都有自己的锁。递增可以分组,这样一个锁就可以完成多个操作。

    更好的解决方案可能是每个单词使用一个TIntIntHashMap。想象一下,您将在文档中找到的所有单词(表示为ints)放入一个集合中。然后就可以这样循环了

    for (int w1 : words) {
        getLock(w1).lock();
        TIntIntHashMap map = getMap(w1);
        for (int w2 : words) {
            if (isLess(w1, w2) map.increment(w2);
        }
        getLock(w1).unlock();
    }
    

    这里,isLess 是一个任意反对称非自反关系,用于避免同时存储 (a, b)(b, a)。虽然简单地w1 < w2 会做,但它会导致相当不平衡的值(getMap(0) 可能很大,getMap(1000000) 可能是空的)。使用((w1 - w2) ^ ((w1 + w2) << 31)) < 0 应该可以。

    【讨论】:

    • 你能解释一下 LongIntHashMap 是怎么做的吗?
    • @DotNet:已添加说明。与此同时,我有了更多的想法,尤其是在内存不够的情况下。
    【解决方案3】:

    我做了一些这样的统计,我把工作分成两步

    step1:多线程计数:计算每对的分区id并直接输出相应的分区文件(partition_id =(对的md5)/partition_count,分区过程是关键点),(我试过hash_map统计数据(当 size 大于 thread_hold 时,将 map_data 输出到文件中,这样可以节省很多磁盘空间,并且我将输出文件放在不同的磁盘中,这样可以大大加快处理速度)

    step2:多线程合并:使用map合并step1输出的count(这个过程在内存中完成,如果内存不足,选择更大的partition_count)

    注意:mapreduce 很简单,step1 是map 短语,step2 是reduce 短语,关键过程是partiotion 过程,对应hadoop 中reduce 过程之前的partition 部分

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-10
      • 2011-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-07
      相关资源
      最近更新 更多