从大文本中提取 n 克

extracting n grams from huge text

例如我们有以下文字:

"Spark is a framework for writing fast, distributed programs. Spark solves similar problems as Hadoop MapReduce does but with a fast in-memory approach and a clean functional style API. ..."

我需要这篇文章的所有可能的部分,分别是一个词一个词,然后两个词两个,三个词三个,五到五个。 像这样:

ones : ['Spark', 'is', 'a', 'framework', 'for', 'writing, 'fast', 'distributed', 'programs', ...]

twos : ['Spark is', 'is a', 'a framework', 'framework for', 'for writing' ...]

threes : ['Spark is a', 'is a framework', 'a framework for', 'framework for writing', 'for writing fast', ...]

. . .

fives : ['Spark is a framework for', 'is a framework for writing', 'a framework for writing fast','framework for writing fast distributed', ...]

请注意,要处理的文本是超大文本(约100GB)。 我需要这个过程的最佳解决方案。可能应该多线程并行处理。

我不需要一次整个列表,它可以流式传输。

你写过代码吗?尝试使用谷歌搜索 N-gram generation 或查看此处:Computing N Grams using Python

您似乎想要生成 1-gram(单词列表),最多 5-gram。

我会一次做每一个,然后转到 n+1-gram。

如果您不需要一次整个列表,那么最好的选择应该是使用迭代器。 因此,我的解决方案如下所示:

import re
text = "Spark is a framework for writing fast, distributed programs. Spark solves similar problems as Hadoop MapReduce does but with a fast in-memory approach and a clean functional style API."
word_re = re.compile(r"\w+")
words = [text[word.start():word.end()] for word in word_re.finditer(text)]
ngrams = ((words[k] for k in xrange(j, j + i + 1)) for i in xrange(len(words)) for j in xrange(len(words) - i))
for ngram in ngrams:
    for word in ngram:
        print word,
    print

这会以所需的顺序为您提供所有需要的 ngram。 请注意,即使对于 ngram 本身,迭代器也是不可避免的,因为您的文本有 500G 这么大,而且您的大部分 "all possible sections" 都无法放入您的内存中。

另请注意,在您的情况下,您需要计算文本的长度并单独从中获取单词,因为您无法像我的代码示例那样将其保存在内存中。

首先,确保您的文件中有行,然后您就可以放心地逐行阅读 (discussed here):

with open('my100GBfile.txt') as corpus:
    for line in corpus:
        sequence = preprocess(line)
        extract_n_grams(sequence)

让我们假设您的语料库不需要任何特殊处理。我想您可以为您的文本找到合适的处理方式,我只想将其放入理想的标记中:

def preprocess(string):
    # do what ever preprocessing that it needs to be done
    # e.g. convert to lowercase: string = string.lower()
    # return the sequence of tokens
    return string.split()

我不知道你想用 n-gram 做什么。让我们假设你想把它们算作适合你记忆的语言模型(通常是这样,但我不确定 4 和 5 克)。简单的方法是使用现成的 nltk 库:

from nltk.util import ngrams

lm = {n:dict() for n in range(1,6)}
def extract_n_grams(sequence):
    for n in range(1,6):
        ngram = ngrams(sentence, n)
        # now you have an n-gram you can do what ever you want
        # yield ngram
        # you can count them for your language model?
        for item in ngram:
            lm[n][item] = lm[n].get(item, 0) + 1

理想情况下应该这样做。 您可以自定义参数 min_len 和 max_len 以满足您的需要。 应用排序函数也可以让您很好地了解哪些 n-gram 脱颖而出。

import nltk
from nltk.util import *
from nltk.collocations import *

content = "Spark is a framework for writing fast, distributed programs. Spark solves similar problems as Hadoop MapReduce does but with a fast in-memory approach and a clean functional style API. ..."
tokens = nltk.word_tokenize(content)
bgs = everygrams(tokens, min_len=users_minimium, max_len=users_maximum)
fdist_bg = nltk.FreqDist(bgs)
for k,v in fdist_bg.items():
        print k,v

并且由于您提到了并行执行,您可以使用 Spark MLib 和 Python

查看以下代码片段
from pyspark.ml.feature import NGram
wordDataFrame = sqlContext.createDataFrame([
(0, ["Hi", "I", "heard", "about", "Spark"]),
(1, ["I", "wish", "Java", "could", "use", "case", "classes"]),
(2, ["Logistic", "regression", "models", "are", "neat"])], ["label","words"])
ngram = NGram(inputCol="words", outputCol="ngrams")
ngramDataFrame = ngram.transform(wordDataFrame)
for ngrams_label in ngramDataFrame.select("ngrams", "label").take(3):
    print(ngrams_label)

Link 解决方案和 Spark 中的其他特征提取技术在这里:Spark MLib Feature extraction

希望对您有所帮助。干杯。 :)

我已经编写了一个 C 库来执行此操作:https://github.com/adriaan-pelzer/corpusToNgrams

基本上,我能想到的最高效的方法是:

  • 逐字符解析语料库。
  • 如果遇到单词结束字符(space、逗号、句号等),请将单词添加到 1-gram 数组中。
  • 重复 n = 2 到 maxN:
  • 如果 1-gram 数组的长度大于 n,则连接 1-gram 数组中的最后 n 个单词并将其添加到 n-gram 数组中。

以上可以用递归函数实现,只需要解析一次语料