【问题标题】:How to train NLTK PunktSentenceTokenizer batchwise?如何批量训练 NLTK PunktSentenceTokenizer?
【发布时间】:2023-03-11 14:08:01
【问题描述】:

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

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

由于 NLTK PunktSentenceTokenizer 基于 Kiss & Strunk (2006) 的无监督算法,我正在尝试根据我的文档训练句子标记器,基于 training data format for 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. 我可以使用标准的英语 pickle 文件并使用已训练的对象进行进一步训练吗?

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

【问题讨论】:

  • 另外:最好使用with-statement来打开文件,见docs

标签: python nltk nltk-trainer


【解决方案1】:

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

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

from nltk.tokenize.punkt import PunktSentenceTokenizer, PunktTrainer

假设我们有一个生成器来生成训练文本流

texts = text_stream()

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

我们可以实例化一个PunktTrainer,然后开始训练

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

【讨论】:

    【解决方案2】:

    source code中所述:

    Punkt 句子分词器

    此分词器将文本划分为句子列表 通过使用无监督算法建立缩写模型 单词、搭配和开始句子的单词。 应该是 在目标语言的大量纯文本上进行训练 在它可以使用之前。

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

    还有一个open issue 与您的主题相关,其中提到了 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 进行比较,there 是一个新参数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 内存。

    【讨论】:

    • 感谢您的建议。我不知道最终确定的机会。我会试试的。由于您不推荐使用 nltk 进行句子分割,您对如何完成此任务有什么建议吗?你能推荐任何库或方法吗?
    • @JumpinMD 你可以使用spaCy,例如,如果你想用 Python 轻松解决问题,不费力气,但强度和更多功能。只需使用(依赖)解析器加载语料库,然后您就可以简单地迭代句子(自动检测)。这只是众多解决方案中的一种,对于您的需求可能有点过分。但它速度很快,并且可以投入生产。如果您只想将句子标记化作为预处理步骤,您可以使用例如斯坦福的分词器(但还有很多分词器 -> 搜索引擎)。
    • 我已经尝试过 spaCy 但无法添加其他缩写。没有它,spaCy 就非常糟糕。你知道怎么做吗?
    • 这离题了...但我认为您可以将vocabulary(包含缩写)调整为tokenization taskThis 对你来说可能也很有趣。
    • @JumpinMD 你找到解决办法了吗?
    猜你喜欢
    • 2011-06-21
    • 2019-04-02
    • 2016-05-18
    • 2017-12-05
    • 2018-12-06
    • 1970-01-01
    • 2015-07-10
    • 2014-04-21
    • 1970-01-01
    相关资源
    最近更新 更多