如何分批训练NLTK PunktSentenceTokenizer?

时间:2018-09-03 12:41:22

标签: python nltk nltk-trainer

我正在尝试将财务文件拆分为句子。我有约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. 我可以使用标准的英语泡菜文件,并对已经训练好的对象进行进一步的训练吗?

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

source code中所述:

  

朋克句子标记器

     

此分词器将文本分为句子列表   通过使用无监督算法来建立缩写模型   单词,搭配和以句子开头的单词。 必须是   在目标语言上接受了大量明文的培训   在可以使用之前。

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

在您的主题上还有一个open issue,上面写着大约200 GB RAM和更多内容。如您所见,NLTK可能不是Kiss&Strunk(2006)提出的算法的良好实现。

我看不到如何对其进行批处理,正如您在train()方法的函数签名(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句子标记化器。不过,如果您要坚持使用该令牌生成器,我只建议您也使用给定的模型,而不要训练新模型,除非您的服务器具有大量RAM内存。

相关问题