6.1 监督分类
本章问题
1.我们应该如何识别语言数据中对特征分类非常重要的特殊特征?
2.我们如何构建可以用来自动执行语言处理任务的语言模型?
3.我们能从这些模型中学习到关于语言的什么内容?
分类任务的例子?
1.决定邮件是不是垃圾邮件
2.决定一个文章是哪个话题区域下的?例如:运动,科技,政治
3.确定一个出现的单词"bank"是不是指"river bank"还是指一个金融机构(银行),还是指倾斜,还是将…存入银行
如果分类器是基于包含每个输入都有正确标签的训练语料库构建的,则称为有监督分类器。
6.1.1性别鉴定
我们看到男性和女性的名字有一些独特的特点。
以a、e和i结尾的名字很可能是女性,而以k、o、r、s和t结尾的名字很可能是男性。
让我们构建一个分类器来更精确地建模这些差异。
输出名字的最后一个字母作为性别特征
def gender_features(word):
return {'last_letter': word[-1]}
print(gender_features('Shrek'))
打乱带标签的名字列表
labeled_names = ([(name, 'male') for name in names.words('male.txt')]
+[(name, 'female') for name in names.words('female.txt')])
random.shuffle(labeled_names)
获取特征集,后面得作为训练集,前面的作为测试集,用贝叶斯分类器训练,随机给一个名字测试分类器是否正确
featuresets = [(gender_features(n), gender) for (n, gender) in labeled_names]
train_set, test_set = featuresets[500:], featuresets[:500]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(classifier.classify(gender_features('Neo'))) #male
print(classifier.classify(gender_features('Trinity'))) #female
测试分类器的正确率
print(nltk.classify.accuracy(classifier, test_set)) #0.762
通过分类器得到对性别最有效,最有区分度的前五个字母
classifier.show_most_informative_features(5)
在处理大型语料库时,构造一个包含每个实例特性的列表可能会占用大量内存。
在这些情况下,使用nltk.classification.apply_features函数,它返回一个对象,
该对象的作用类似于列表,但不将所有特性集存储在内存中
train_set = apply_features(gender_features, labeled_names[500:])
test_set = apply_features(gender_features, labeled_names[:500])
6.1.2 选择正确的特征
通常情况下,分类器是通过反复试验的过程构建的,由与问题相关的信息的直觉指导。通常从“kitchen sink”方法开始,包括您能想到的所有特性,然后检查哪些特性实际上是有用的。
分析词的特征,一个单词中每个字母包含几个,是否包含
def gender_features2(name):
features = {}
features["first_letter"] = name[0].lower()
features["last_letter"] = name[-1].lower()
for letter in 'abcdefghijklmnopqrstuvwxyz':
features["count({})".format(letter)] = name.lower().count(letter)
features["has({})".format(letter)] = (letter in name.lower())
return features
print(gender_features2('John'))
然而,对于给定的学习算法,您应该使用的特性的数量通常是有限制的——如果您提供了太多的特性,那么该算法将更有可能依赖于您的训练数据的特性,而这些特性不能很好地推广到新样本中。这个问题被称为过度拟合,给features2计算accuracy
一旦选择了一组初始特征,一种非常有效的细化特征集的方法是错误分析。首先,我们选择一个开发集,其中包含用于创建模型的语料库数据。然后将这个开发集细分为训练集和开发测试集。
训练集用于训练模型,开发测试集用于进行误差分析。测试集用于我们对系统的最终评估。
由于下面讨论的原因,重要的是我们使用一个单独的开发测试集来进行错误分析,而不仅仅是使用测试集。语料库数据划分为不同子集的方法如1.3所示。
将语料库划分为适当的数据集之后,我们使用训练集训练一个模型,然后在开发测试集上运行它。
train_names = labeled_names[1500:]
devtest_names = labeled_names[500:1500]
test_names = labeled_names[:500]
train_set = [(gender_features(n), gender) for (n, gender) in train_names]
devtest_set = [(gender_features(n), gender) for (n, gender) in devtest_names]
test_set = [(gender_features(n), gender) for (n, gender) in test_names]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(nltk.classify.accuracy(classifier, devtest_set)) #0.758
使用dev-test集,我们可以生成一个分类器在预测姓名性别时产生的错误列表:
6.1.3文档分类
获取电影评论里的所有文件
documents = [([movie_reviews.words(fileid)], category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)]
random.shuffle(documents)
电影评论里所有词中用的最多的前2000个词
all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
word_features = [all_words][:2000]
某一个文件里是否包含前2000个词
def document_features(documents):
document_words = documents
features = {}
for word in word_features:
features['contains({})'.format(word)] = (word in document_words)
return features
print(document_features(movie_reviews.words('D:/nltk_data/corpora/movie_reviews/neg/cv000_29416.txt')))
6.1.4词性标注
前100个高频后缀(1,2,3一共3个长度)
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 = [suffix for (suffix, count) in suffix_fdist.most_common(100)]
给定一个词,看它的后缀是不是在那100个高频的后缀里
def pos_features(word):
features = {}
for suffix in common_suffixes:
features['endswith({})'.format(suffix)] = word.lower().endswith(suffix)
return features
获取brown news类别下有标签的词的特征集合(是否以那100个高频后缀结尾&这个词的词性)
tagged_words = brown.tagged_words(categories='news')
featuresets = [(pos_features(n), g) for (n,g) in tagged_words]
取前1/10做测试集,后面的数据做训练集,然后用决策树训练得到分类器
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’))) #0.6270512182993535
print(classifier.pseudocode(depth=4))