2012-12-30 75 views
29

我正在使用NLTK來分析一些經典文本,並且我正忙於按句子標記文本。例如,這裏是我得到的一個片段來自Moby Dick如何調整NLTK語句分詞器

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? 
----- 
" 
''' 

我不指望十全十美這裏,考慮到梅爾維爾的語法是有點過時,但NLTK應該能夠處理終端雙引號和「太太」這樣的標題。由於標記器是無監督訓練算法的結果,但是,我無法弄清楚如何修改它。

任何人都有更好的句子tokenizer的建議?我更喜歡一種簡單的啓發式方法,我可以進行攻擊,而不必去訓練我自己的解析器。

回答

40

您需要提供縮寫的列表標記生成器,像這樣:

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('."', '. "') 
+0

啊,很高興知道。奇怪的是,如果我通過你的解決方案在我的問題中運行完整的句子,這是行不通的。任何想法爲什麼? –

+0

只是在答案中增加了更多信息。 – vpekar

+3

我通常避免'感謝'的評論,但這裏真的是在這裏:謝謝! – Private

7

您可以通過將realign_boundaries參數設置爲True來告訴PunktSentenceTokenizer.tokenize方法在句子的其餘部分包含「終端」雙引號。有關示例,請參閱下面的代碼。

我不知道一個乾淨的方法來防止像Mrs. Hussey這樣的文本被分成兩句話。然而,這裏是一個黑客,其

  • 軋液的Mrs. Hussey所有出現Mrs._Hussey
  • 然後拆分文本句子sent_tokenize.tokenize
  • 然後爲每個句子,unmangles Mrs._HusseyMrs. 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?" 
+0

正是我需要的 - 謝謝! –

+0

更新:這個答案的合併部分與上面的那個 –

2

所以我也有類似的問題,並嘗試了vpekar的解決方案以上。

也許我是某種邊緣情況,但是我在應用替換後觀察到了相同的行爲,但是,當我嘗試用放在他們之前的引號替換標點符號時,我得到了我正在查找的輸出。大概缺乏對工作重點的遵守不如將原始報價保留爲單個句子那麼重要。

更清楚:

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

如果MLA是很重要的,雖然你總是可以回去的地方它計數逆轉這些變化。

24

您可以修改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'。有關其他分詞器參數的詳細信息,請參閱the relevant documentation.

+1

這應該是最佳答案。如果您只是創建一個新的標記器,您將無法獲得英語標記器的所有現有功能。 –

+0

它似乎沒有爲我工作,而最佳答案。 – Alter