之前我们看了几个例子,那里文档已经按类别标记。使用这些语料库,我们可以建立分类器。自动给新文档添加适当的类别标签。
首先我们构造一个标记了相应类别的文档清单,对于这个例子,我选择了nltk中的电影评论语料库,将每个评论分为正面或者负面。
import random
from nltk.corpus import movie_reviews
documents = [(list(movie_reviews.words(fileid)),category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)]
random.shuffle(documents)
接下来我们定义一个特征提取器,这样分类器就会知道哪些方面的数据应注意。
对于文档主题识别,我们可以为每个词定义一个特征表示该文档是否包含这个词。
为了限制分类器需要处理的特征的数目,我们一开始构建一个整个语料中前2000个最频繁词的链表,然后定义一个特征提取器。简单的检查这些词是否在一个给定的文档中。
import nltk
all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
word_features = list(all_words.keys())[:2000]
def document_features(document):
documents_words = set(document)
features = {}
for word in word_features:
features['contains(%s)'%word] = (word in documents_words)
return features
现在我们已经定义好了我们的特征提取器,可以用它来训练一个分类器,为新的电影评论添加标签。
为了检查产生的分类器的可靠性,我们在测试集上计算其准确性。然后我们使用 show_most_informative_features()来找出哪些是分类器发现最有信息量的。
训练和测试一个分类器进行文档分类:
featuresets = [(document_features(d),c) for (d,c) in documents]
train_set,test_set = featuresets[100:],featuresets[:100]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(nltk.classify.accuracy(classifier,test_set))
print(classifier.show_most_informative_features(5))
输出结果:准确度0.86 (neg消极的,pos积极的)
可看到这个语料中,例如:提到Justin的评论中 负面的是正面的9倍。
之前我们建立过正则表达式标注器,通过查找词内部的组成,为词选择词性标记。但是这个是手工的,我们这里可以训练一个分类器来算出哪个后缀最有信息量。
那我们先找出最常见的后缀:
import nltk
from nltk.corpus import brown
suffix_fdist = nltk.FreqDist()
for word in brown.words():
word = word.lower()
suffix_fdist[word[-1:]] +=1
suffix_fdist[word[-2:]] +=1
suffix_fdist[word[-3:]] +=1
common_suffixes = list(suffix_fdist.keys())[:100]
print(common_suffixes)
# 输出频率最高的100个后缀(最后一个字母,两个,以及三个)
输出:[‘e’, ‘he’, ‘the’, ‘n’, ‘on’, ‘ton’, ‘y’, ‘ty’, ‘nty’,…‘urt’, ‘dge’, ‘od’]
接下来:我们定义一个特征提取函数,检查给定的单词的这些后缀。
def pos_features(word):
features={}
for suffix in common_suffixes:
features['endswith(%s)'%suffix] = word.lower().endwith(suffix)
return features
特征提取函数就像一个有色眼镜一样,强调我们的数据中的某些属性(颜色),并使其无法看到其他属性。分类器在决定如何进行标注时,会完全依赖他们强调的属性。在这个情况下,分类器将只基于一个给定的词拥有(如果有)哪个常见的后缀的信息来做决定。
现在我们已经定义了一个自己的特征提取器,可以用他来训练一个新的决策树的分类器。
tagged_words = brown.tagged_words(categories = 'news')
featuresets = [(pos_features(n),g) for (n,g) in tagged_words]
size = int(len(featuresets) * 0.1)
train_set,test_set = featuresets[size:],featuresets[:size]
classifier = nltk.DecisionTreeClassifier.train(train_set)
print(nltk.classify.accuracy(classifier, test_set))
print(classifier.classify(pos_features('cats')))
探索上下文语境(Exploiting Context)
上下文语境特征往往提供关于正确标记的强大线索——例如:标注词fly,如果知道它前面的词是“a”将使我们能够确定它是一个名词,而不是一个动词。如果前面的词是“to”显然它是一个动词。所以今天我们构造的词性分类器。
一个词性分类器,它的特征检测器检查一个词出现的上下文以便决定应该分配的词性标记。特别的,前面的词被作为一个特征。
def pos_features(sentence, i):
features = {"suffix(1)": sentence[i][-1:],
"suffix(2)": sentence[i][-2:],
"suffix(3)": sentence[i][-3:]}
if i == 0:
features["prev-word"] = "<START>"
else:
features["prev-word"] = sentence[i-1]
return features
print(brown.sents()[0])
输出[u’The’, u’Fulton’, u’County’, u’Grand’, … u’place’, u’.’]
pos_features(brown.sents()[0], 8)
输出{‘suffix(3)’: u’ion’, ‘prev-word’: u’an’, ‘suffix(2)’: u’on’, ‘suffix(1)’: u’n’}
tagged_sents = brown.tagged_sents(categories='news')
featuresets = []
for tagged_sent in tagged_sents:
untagged_sent = nltk.tag.untag(tagged_sent)
for i, (word, tag) in enumerate(tagged_sent):
featuresets.append( (pos_features(untagged_sent, i), tag) )
size = int(len(featuresets) * 0.1)
train_set, test_set = featuresets[size:], featuresets[:size]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(nltk.classify.accuracy(classifier, test_set))
输出 0.77
利用上下文特征可以特高我们的词性标注器的性能,例如:分类器学到一个词跟在large或 gubernatorial后面,那他极有可能是名词。