40

我正在使用 NLTK 分析一些经典文本,并且遇到了逐句标记文本的麻烦。例如,这是我从Moby Dick获得的片段:

import nltk
sent_tokenize = nltk.data.load('tokenizers/punkt/english.pickle')

'''
(Chapter 16)
A clam for supper? a cold clam; is THAT what you mean, Mrs. Hussey?" says I, "but
that's a rather cold and clammy reception in the winter time, ain't it, Mrs. Hussey?"
'''
sample = 'A clam for supper? a cold clam; is THAT what you mean, Mrs. Hussey?" says I, "but that\'s a rather cold and clammy reception in the winter time, ain\'t it, Mrs. Hussey?"'

print "\n-----\n".join(sent_tokenize.tokenize(sample))
'''
OUTPUT
"A clam for supper?
-----
a cold clam; is THAT what you mean, Mrs.
-----
Hussey?
-----
" says I, "but that\'s a rather cold and clammy reception in the winter time, ain\'t it, Mrs.
-----
Hussey?
-----
"
'''

考虑到 Melville 的语法有点过时,我不希望这里完美,但 NLTK 应该能够处理终端双引号和像“Mrs.”这样的标题。但是,由于标记器是无监督训练算法的结果,我不知道如何修补它。

有人对更好的句子标记器有建议吗?我更喜欢我可以破解的简单启发式算法,而不必训练自己的解析器。

4

4 回答 4

50

您需要向标记器提供缩写列表,如下所示:

from nltk.tokenize.punkt import PunktSentenceTokenizer, PunktParameters
punkt_param = PunktParameters()
punkt_param.abbrev_types = set(['dr', 'vs', 'mr', 'mrs', 'prof', 'inc'])
sentence_splitter = PunktSentenceTokenizer(punkt_param)
text = "is THAT what you mean, Mrs. Hussey?"
sentences = sentence_splitter.tokenize(text)

现在的句子是:

['is THAT what you mean, Mrs. Hussey?']

更新:如果句子的最后一个单词附有撇号或引号(如Hussey?'),这不起作用。因此,解决此问题的一种快速而简单的方法是将空格放在撇号和句子结尾符号 (.!?) 后面的引号之前:

text = text.replace('?"', '? "').replace('!"', '! "').replace('."', '. "')
于 2012-12-31T15:08:35.377 回答
41

您可以修改 NLTK 的预训练英语句子标记器,通过将它们添加到集合中来识别更多缩写词_params.abbrev_types。例如:

extra_abbreviations = ['dr', 'vs', 'mr', 'mrs', 'prof', 'inc', 'i.e']
sentence_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
sentence_tokenizer._params.abbrev_types.update(extra_abbreviations)

请注意,必须指定缩略语而不包括最后句点,但要包括任何内部句点,如上'i.e'所示。有关其他分词器参数的详细信息,请参阅相关文档。

于 2014-08-19T04:52:51.587 回答
9

您可以通过将参数设置为来告诉该PunktSentenceTokenizer.tokenize方法在句子的其余部分中包含“终端”双引号。有关示例,请参见下面的代码。realign_boundariesTrue

我不知道一个干净的方法来防止文本Mrs. Hussey被分成两个句子。但是,这是一个 hack

  • 破坏所有出现的Mrs. Husseyto Mrs._Hussey
  • 然后将文本拆分成句子sent_tokenize.tokenize
  • 然后对于每个句子,解开Mrs._Hussey回到Mrs. Hussey

我希望我知道一个更好的方法,但这可能会在紧要关头起作用。


import nltk
import re
import functools

mangle = functools.partial(re.sub, r'([MD]rs?[.]) ([A-Z])', r'\1_\2')
unmangle = functools.partial(re.sub, r'([MD]rs?[.])_([A-Z])', r'\1 \2')

sent_tokenize = nltk.data.load('tokenizers/punkt/english.pickle')

sample = '''"A clam for supper? a cold clam; is THAT what you mean, Mrs. Hussey?" says I, "but that\'s a rather cold and clammy reception in the winter time, ain\'t it, Mrs. Hussey?"'''    

sample = mangle(sample)
sentences = [unmangle(sent) for sent in sent_tokenize.tokenize(
    sample, realign_boundaries = True)]    

print u"\n-----\n".join(sentences)

产量

"A clam for supper?
-----
a cold clam; is THAT what you mean, Mrs. Hussey?"
-----
says I, "but that's a rather cold and clammy reception in the winter time, ain't it, Mrs. Hussey?"
于 2012-12-31T12:55:50.057 回答
3

所以我遇到了类似的问题,并在上​​面尝试了 vpekar 的解决方案。

也许我的是某种边缘情况,但我在应用替换后观察到相同的行为,但是,当我尝试用放在它们之前的引号替换标点符号时,我得到了我正在寻找的输出。据推测,缺乏对 MLA 的遵守不如将原始引用保留为一个句子重要。

为了更清楚:

text = text.replace('?"', '"?').replace('!"', '"!').replace('."', '".')

如果 MLA 很重要,尽管您总是可以返回并在任何重要的地方扭转这些变化。

于 2014-06-04T23:10:36.860 回答