从大文件中访问n-gram频率

时间:2013-02-21 10:27:01

标签: performance nlp large-files n-gram memory-efficient

我对自然语言处理和java编程很新。我有一个非常大的文本文件,包含ngrams和相关频率(aaprox.250 mb)。我需要在程序运行时获得给定ngram的频率值。 ngram频率在文件中提供如下(仅示例):

the quick 445
quick brown 458
brown fox 11
fox jumped 123

我尝试在启动时通过填充哈希集来读取文件...但是对于18mb文件(使用System.currentTimeMillis()测试)花了将近1500毫秒。现在我正在考虑对n-gram计数进行排序并将250mb文件分成小块并填充列表并通过在单独的索引中索引文件集并引用它来按需获取频率。

但是,我不确定是否还有其他更简单或更有效的方法。如果有更好的方法,请告诉我。 (如果没有使用任何脚本或库,那就更好了......)。谢谢大家。

2 个答案:

答案 0 :(得分:2)

我同意@mbatchkarov的说法,加载时间通常不是最重要的优化目标。但是运行时通常与内存占用量密切相关(内存访问速度很慢,因此您可以在缓存中使用的工作集越多越好)。

将每个bigram映射到整数计数(可能是在java.util.HashMap中)的初始方法是明智的,但是内存非常密集。您的计数文件包含数百万个bigrams,每个都必须表示为单独的字符串。这些字符串消耗(至少)大约40个字节的内存,每个计数需要一个Integer对象 - 大多数JVM实现中大约20个字节。我粗略的背后猜测使数据结构超过了千兆字节。

但是你可以做得更好,通过观察一个二元组只出现在你的文件(和你的数据结构)中一次,大多数单个单词会重复多次 - 你可以在不重复存储的情况下离开。

我将从一个从单词到整数索引的映射开始 - 例如,从您的示例中,= 0,quick = 1,brown = 2,依此类推。我不知道你的词典的大小,但是频繁的英语单词的典型映射可能有几十或几十万个单词。所以String存储必须更小。

要存储计数,您可以将这些整数字索引组合成复合键,并将该键用于地图。一种简单的“组合”方法只是对第一个单词的索引进行位移,而在第二个单词中对OR进行位移。

在伪代码中:

HashMap<String, Integer> lexicon = new HashMap<String, Integer>();

// Iterate through the file, mapping each word to 
for (String file line) {
  ... Parse out word1 and word2
  if (!lexicon.containsKey(word1)) {
      lexicon.put(word1, lexicon.size());
  }
  if (!lexicon.containsKey(word2)) {
      lexicon.put(word2, lexicon.size());
  }
}

现在,再次遍历文件,将计数添加到单独的计数图中。

HashMap<Long, Integer> countMap = new HashMap<Long, Integer>();

for (String file line) {
    ... Parse out word1, word2, and count
    int i1 = lexicon.get(word1);
    int i2 = lexicon.get(word2);
    long key = (i1 << 32) | i2;
    countMap.put(key, count);
}

访问bigram计数类似于映射它 - 查找两个单词的索引,创建键,并在计数映射中查找。这应该会大大减少你的存储空间。但我会更进一步,并使用FastUtilTrove等类型特定的地图替换通用HashMaps。原始数据结构将消除数据映射中每个Long和Integer的大约12-20字节的开销。

上面的伪代码假定您对单词索引使用32位整数,并将它们组合成64位长整数。如果你的词典足够小,你可以使用16位短路和32位整数,并节省更多空间。

编辑:我应该清楚,如果你想要实现一个完整的n-gram语言模型(trigram,4-gram等),那么有更高效的表示,并且n-gram模型可以很好地处理几个图书馆(我建议您查看OpenGRMLingpipe)。但上面的伪代码是一种简单而有效的方法来做一个简单的二元模型。

答案 1 :(得分:2)

查看BerkeleyLM这是一个处理ngrams的特殊库。