5

我正在尝试将财务文件拆分为句子。我有大约 50.000 个包含纯英文文本的文档。总文件大小约为 2.6 GB。

我正在使用PunktSentenceTokenizer带有标准英语泡菜文件的 NLTK。我还通过提供额外的缩写对其进行了调整,但结果仍然不够准确。

由于 NLTK PunktSentenceTokenizer 基于 Kiss & Struk (2006) 的无监督算法,我正在尝试根据我的文档训练句子标记器,基于nltk punkt 的训练数据格式

import nltk.tokenize.punkt
import pickle
import codecs

tokenizer = nltk.tokenize.punkt.PunktSentenceTokenizer()
text = codecs.open("someplain.txt", "r", "utf8").read()
tokenizer.train(text)
out = open("someplain.pk", "wb")
pickle.dump(tokenizer, out)
out.close()

不幸的是,在运行代码时,我收到了一个错误,即内存不足。(主要是因为我首先将所有文件连接到一个大文件中。)

现在我的问题是:

  1. 如何批量训练算法,这会降低内存消耗吗?
  2. 我可以使用标准的英语泡菜文件并使用已经训练过的对象进行进一步的训练吗?

我在 Core I7 2600K 和 16GB RAM 机器上的 Windows 10 上使用 Python 3.6 (Anaconda 5.2)。

4

2 回答 2

2

我自己遇到这个问题后发现了这个问题。我想出了如何批量训练标记器,并将这个答案留给其他想要这样做的人。我能够PunktSentenceTokenizer在大约 12 小时内训练大约 200GB 的生物医学文本内容,一次内存占用不超过 20GB。不过,我想支持@colidyre 的建议,即PunktSentenceTokenizer在大多数情况下更喜欢其他工具。

有一个类PunktTrainer可以用来PunktSentenceTokenizer批量训练。

from nltk.tokenize.punkt import PunktSentenceTokenizer, PunktTrainer

假设我们有一个生成器,它产生一个训练文本流

texts = text_stream()

在我的例子中,生成器的每次迭代都会一次查询数据库中的 100,000 条文本,然后将所有这些文本连接在一起。

我们可以实例化 aPunktTrainer然后开始训练

trainer = PunktTrainer()
for text in texts:
    trainer.train(text)
    trainer.freq_threshold()

freq_threshold注意处理每个文本后对方法的调用。这通过清理不太可能影响未来训练的稀有令牌的信息来减少内存占用。

完成后,调用 finalize 训练方法。然后,您可以使用在训练期间找到的参数来实例化一个新的分词器。

trainer.finalize_training()
tokenizer = PunktSentenceTokenizer(trainer.get_params())

@colidyre 建议使用带有缩写的 spaCy。但是,可能很难提前知道哪些缩写会出现在您的文本域中。为了两全其美,您可以添加 Punkt 找到的缩写词。您可以通过以下方式获得一组这些缩写

params = trainer.get_params()
abbreviations = params.abbrev_types
于 2020-04-01T12:34:45.857 回答
1

源代码中所述:

Punkt 句子分词器

该分词器通过使用无监督算法为缩写词、搭配和开始句子的词建立模型,将文本划分为句子列表。它必须在目标语言的大量纯文本集合上进行训练,然后才能使用。

大型集合的真正含义还不是很清楚。在论文中,没有给出关于学习曲线的信息(当停止学习过程就足够了,因为已经看到了足够的数据)。那里提到了华尔街日报语料库(它大约有 3000 万字)。因此,您是否可以简单地修剪您的训练语料库并减少内存占用,这是非常不清楚的。

关于您的主题还有一个未解决的问题,说大约 200 GB RAM 及更多。如您所见,NLTK 可能没有很好地实现 Kiss & Struk (2006) 提出的算法。

我看不到如何批处理它,正如您在train()-method 的函数签名中看到的那样(NLTK 版本 3.3):

def train(self, train_text, verbose=False):
    """
    Derives parameters from a given training text, or uses the parameters
    given. Repeated calls to this method destroy previous parameters. For
    incremental training, instantiate a separate PunktTrainer instance.
    """

但可能还有更多问题,例如,如果您将给定版本 3.3 的签名与 git 标记版本 3.3 进行比较,则有一个新参数finalize可能会有所帮助,并指示可能的批处理或可能与已训练模型的合并:

def train(self, text, verbose=False, finalize=True):
    """
    Collects training data from a given text. If finalize is True, it
    will determine all the parameters for sentence boundary detection. If
    not, this will be delayed until get_params() or finalize_training() is
    called. If verbose is True, abbreviations found will be listed.
    """

无论如何,如果您想在操场级别之外进行句子标记化,我强烈建议不要使用 NLTK 的 Punkt Sentence Tokenizer。不过,如果您想坚持使用该标记器,我只是建议您也使用给定的模型,而不是训练新模型,除非您有一个具有巨大 RAM 内存的服务器。

于 2018-09-03T13:15:51.617 回答