3.6 规范化文本

在前面的程序例子中,我们在处理文本词汇前经常要将文本转换为小写,即set(w.lower() for w in text)。通过使用lower()我们将文本规范化为小写,这样一来 The 与 the 的区别被忽略。我们常常想比这走得更远,去掉所有的词缀以及提取词干的任务等。更进一步的步骤是确保结果形式是字典中确定的词,即叫做词形归并的任务。我们依次讨论这些。首先,我们需要定义我们将在本节中使用的数据:

  1. >>> raw = """DENNIS: Listen, strange women lying in ponds distributing swords
  2. ... is no basis for a system of government. Supreme executive power derives from
  3. ... a mandate from the masses, not from some farcical aquatic ceremony."""
  4. >>> tokens = word_tokenize(raw)

词干提取器

NLTK 中包括几个现成的词干提取器,如果你需要一个词干提取器,你应该优先使用它们中的一个,而不是使用正则表达式制作自己的词干提取器,因为 NLTK 中的词干提取器能处理的不规则的情况很广泛。Porter 和 Lancaster 词干提取器按照它们自己的规则剥离词缀。请看 Porter 词干提取器正确处理了词 lying(将它映射为 lie),而 Lancaster 词干提取器并没有处理好。

  1. >>> porter = nltk.PorterStemmer()
  2. >>> lancaster = nltk.LancasterStemmer()
  3. >>> [porter.stem(t) for t in tokens]
  4. ['DENNI', ':', 'Listen', ',', 'strang', 'women', 'lie', 'in', 'pond',
  5. 'distribut', 'sword', 'is', 'no', 'basi', 'for', 'a', 'system', 'of', 'govern',
  6. '.', 'Suprem', 'execut', 'power', 'deriv', 'from', 'a', 'mandat', 'from',
  7. 'the', 'mass', ',', 'not', 'from', 'some', 'farcic', 'aquat', 'ceremoni', '.']
  8. >>> [lancaster.stem(t) for t in tokens]
  9. ['den', ':', 'list', ',', 'strange', 'wom', 'lying', 'in', 'pond', 'distribut',
  10. 'sword', 'is', 'no', 'bas', 'for', 'a', 'system', 'of', 'govern', '.', 'suprem',
  11. 'execut', 'pow', 'der', 'from', 'a', 'mand', 'from', 'the', 'mass', ',', 'not',
  12. 'from', 'som', 'farc', 'aqu', 'ceremony', '.']

词干提取过程没有明确定义,我们通常选择心目中最适合我们的应用的词干提取器。如果你要索引一些文本和使搜索支持不同词汇形式的话,Porter 词干提取器是一个很好的选择(3.6 所示,它采用 面向对象 编程技术,这超出了本书的范围,字符串格式化技术将在3.9讲述,enumerate()函数将在4.2解释)。

  1. class IndexedText(object):
  2. def __init__(self, stemmer, text):
  3. self._text = text
  4. self._stemmer = stemmer
  5. self._index = nltk.Index((self._stem(word), i)
  6. for (i, word) in enumerate(text))
  7. def concordance(self, word, width=40):
  8. key = self._stem(word)
  9. wc = int(width/4) # words of context
  10. for i in self._index[key]:
  11. lcontext = ' '.join(self._text[i-wc:i])
  12. rcontext = ' '.join(self._text[i:i+wc])
  13. ldisplay = '{:>{width}}'.format(lcontext[-width:], width=width)
  14. rdisplay = '{:{width}}'.format(rcontext[:width], width=width)
  15. print(ldisplay, rdisplay)
  16. def _stem(self, word):
  17. return self._stemmer.stem(word).lower()

词形归并

WordNet 词形归并器只在产生的词在它的词典中时才删除词缀。这个额外的检查过程使词形归并器比刚才提到的词干提取器要慢。请注意,它并没有处理 lying,但它将 women 转换为 woman。

  1. >>> wnl = nltk.WordNetLemmatizer()
  2. >>> [wnl.lemmatize(t) for t in tokens]
  3. ['DENNIS', ':', 'Listen', ',', 'strange', 'woman', 'lying', 'in', 'pond',
  4. 'distributing', 'sword', 'is', 'no', 'basis', 'for', 'a', 'system', 'of',
  5. 'government', '.', 'Supreme', 'executive', 'power', 'derives', 'from', 'a',
  6. 'mandate', 'from', 'the', 'mass', ',', 'not', 'from', 'some', 'farcical',
  7. 'aquatic', 'ceremony', '.']

如果你想编译一些文本的词汇,或者想要一个有效词条(或中心词)列表,WordNet 词形归并器是一个不错的选择。

注意

另一个规范化任务涉及识别非标准词,包括数字、缩写、日期以及映射任何此类词符到一个特殊的词汇。例如,每一个十进制数可以被映射到一个单独的标识符0.0,每首字母缩写可以映射为AAA。这使词汇量变小,提高了许多语言建模任务的准确性。