如何将文本文件拆分为 word2vec/gensim 的句子

How to split a text file into sentences for word2vec/gensim

我已经将大约 40MB 的英文维基百科提取为纯文本。我会用它来构建带有 gensim 的 word2vec 模型。为此,我需要先将其分成句子。我怎样才能做到这一点?我试过了:

from __future__ import unicode_literals, print_function
import spacy
from spacy.lang.en import English 
nlp = spacy.load('en_core_web_sm')
nlp.max_length = 47084146
ftest = open("test_02", "r")
raw_test = ftest.read().replace("\n", " ")
sentences = [i for i in nlp(raw_test).sents] 

f = open("sentences.txt", "w")

for sent in sentences:
    f.write(str(sent)+"\n")
f.write("\n")
f.close()

但这失败了:MemoryError: Unable to allocate 34.8 GiB for an array with shape (9112793, 8, 64, 2) and data type float32

我不知道为什么要使用这么多内存!

我该怎么做?


Traceback (most recent call last):
  File "../../processwiki.py", line 8, in <module>
    sentences = [i for i in nlp(raw_test).sents] 
  File "/mnt/storage/home/user/.local/lib/python3.7/site-packages/spacy/language.py", line 449, in __call__
    doc = proc(doc, **component_cfg.get(name, {}))
  File "nn_parser.pyx", line 233, in spacy.syntax.nn_parser.Parser.__call__
  File "nn_parser.pyx", line 274, in spacy.syntax.nn_parser.Parser.predict
  File "nn_parser.pyx", line 287, in spacy.syntax.nn_parser.Parser.greedy_parse
  File "/mnt/storage/home/user/.local/lib/python3.7/site-packages/thinc/neural/_classes/model.py", line 167, in __call__
    return self.predict(x)
  File "/mnt/storage/home/user/.local/lib/python3.7/site-packages/thinc/neural/_classes/model.py", line 131, in predict
    y, _ = self.begin_update(X, drop=None)
  File "_parser_model.pyx", line 243, in spacy.syntax._parser_model.ParserModel.begin_update
  File "_parser_model.pyx", line 300, in spacy.syntax._parser_model.ParserStepModel.__init__
  File "_parser_model.pyx", line 425, in spacy.syntax._parser_model.precompute_hiddens.__init__
  File "/mnt/storage/home/user/.local/lib/python3.7/site-packages/spacy/_ml.py", line 183, in begin_update
    Yf = self._add_padding(Yf)
  File "/mnt/storage/home/user/.local/lib/python3.7/site-packages/spacy/_ml.py", line 214, in _add_padding
    Yf_padded = self.ops.xp.vstack((self.pad, Yf))
  File "<__array_function__ internals>", line 6, in vstack
  File "/mnt/storage/software/languages/anaconda/Anaconda3-2020.02-tflow-2.2.0/lib/python3.7/site-packages/numpy/core/shape_base.py", line 283, in vstack
    return _nx.concatenate(arrs, 0)
  File "<__array_function__ internals>", line 6, in concatenate
MemoryError: Unable to allocate 34.8 GiB for an array with shape (9112793, 8, 64, 2) and data type float32

问题是,test_02 的内容是一次性处理的,中间数据结构不适合内存。分块处理应该可以解决问题。例如,如果句子从不在行之间拆分,则渐进处理将如下所示:

with  open("test_02", "r") as ftest, open("sentences.txt", "w") as f:
    for line in ftest:
        for sent in nlp(line).sents:
            f.write(str(sent)+"\n")

由于句子可以跨越多行,您可能需要使用不同的拆分策略 test_02,例如通过拆分双换行符而不是 for line in ftest.read().split('\n\n') 但很可能即使这种天真的方法也能正常工作。

MemoryError 的近因是将很多不需要的东西加载到内存中。

首先将整个test_02文件作为一个巨大的字符串放入可寻址内存中。 (一定要有足够的内存,因为很多人这一步都会失败!)

用 spaces 替换所有换行符——为什么,确切地说? – 然后可能会暂时将内存需求加倍,作为 .replace() returns 的副本。 (不过,令人惊讶的是,你还没有耗尽内存。)

然后,要求 Spacy 解析大量文本——Spacy 的解析本身通常是一个复杂的步骤——触发了 Spacy 代码深处的错误。 (但是,如果运气好或扩展系统内存没有触发错误,您的下一行可能会尝试创建所有 .sents 的堆内列表。)

好消息是:您可能不需要执行任何这些操作。尽管 gensim 的参数名称为 Word2Vecsentences,但它实际上并不需要合法的句子。它只需要文本,其中每个文本都是一个字符串标记列表。 (这些可以是很多段落的完整 articles/docs,或者其他任何内容。word2vec 的一些使用甚至会留下标点符号作为伪词。)

你的 test_02 文件,如果它已经被标记化为你想要的 'words' ,其中每一行都是一个合理大小的文本并且单词由单个 space 分隔,可能几乎已经准备好传递给 Word2Vec。尝试...

from gensim.models.word2vec import LineSentence
corpus = LineSentence('test_02')

..然后将 corpus 作为 sentences 参数提供给 Word2Vec。如果实际上您的 test_02 需要更复杂的 preprocessing/tokenization,请先执行此操作,如果您需要那种复杂程度,则可以使用 Spacy,然后创建一个文件,每行一个文本并以 space 分隔令牌。

在任何时候都没有必要将整个语料库加载到单个堆内字符串或列表中;它可以而且几乎总是应该根据需要从磁盘流式传输,以避免消耗太多主内存。