2.1 句子分割

句子分割可以看作是一个标点符号的分类任务:每当我们遇到一个可能会结束一个句子的符号,如句号或问号,我们必须决定它是否终止了当前句子。

第一步是获得一些已被分割成句子的数据,将它转换成一种适合提取特征的形式:

  1. >>> sents = nltk.corpus.treebank_raw.sents()
  2. >>> tokens = []
  3. >>> boundaries = set()
  4. >>> offset = 0
  5. >>> for sent in sents:
  6. ... tokens.extend(sent)
  7. ... offset += len(sent)
  8. ... boundaries.add(offset-1)

在这里,tokens是单独句子标识符的合并列表,boundaries是一个包含所有句子边界词符索引的集合。下一步,我们需要指定用于决定标点是否表示句子边界的数据特征:

  1. >>> def punct_features(tokens, i):
  2. ... return {'next-word-capitalized': tokens[i+1][0].isupper(),
  3. ... 'prev-word': tokens[i-1].lower(),
  4. ... 'punct': tokens[i],
  5. ... 'prev-word-is-one-char': len(tokens[i-1]) == 1}

基于这一特征提取器,我们可以通过选择所有的标点符号创建一个加标签的特征集的列表,然后标注它们是否是边界标识符:

  1. >>> featuresets = [(punct_features(tokens, i), (i in boundaries))
  2. ... for i in range(1, len(tokens)-1)
  3. ... if tokens[i] in '.?!']

使用这些特征集,我们可以训练和评估一个标点符号分类器:

  1. >>> size = int(len(featuresets) * 0.1)
  2. >>> train_set, test_set = featuresets[size:], featuresets[:size]
  3. >>> classifier = nltk.NaiveBayesClassifier.train(train_set)
  4. >>> nltk.classify.accuracy(classifier, test_set)
  5. 0.936026936026936

使用这种分类器进行断句,我们只需检查每个标点符号,看它是否是作为一个边界标识符;在边界标识符处分割词列表。在2.1中的清单显示了如何可以做到这一点。

  1. def segment_sentences(words):
  2. start = 0
  3. sents = []
  4. for i, word in enumerate(words):
  5. if word in '.?!' and classifier.classify(punct_features(words, i)) == True:
  6. sents.append(words[start:i+1])
  7. start = i+1
  8. if start < len(words):
  9. sents.append(words[start:])
  10. return sents