第06章 学习分类文本
# -*- coding: utf-8 -*-
import nltk, re, pprint
from nltk import word_tokenize
6.1 有监督分类
如果分类的建立基于包含每个输入的正确标签的训练语料,被称为有监督分类。
- (a)在训练过程中,特征提取器用来将每一个输入值转换为特征集。这些特征集捕捉每个输入中应被用于对其分类的基本信息。特征集与标签的配对被送入机器学习算法,生成模型。
- (b)在预测过程中,相同的特征提取器被用来将未见过的输入转换为特征集。之后,这些特征集被送入模型产生预测标签。
性别鉴定
男性和女性的名字有一些鲜明的特点。以a,e 和i 结尾的很可能是女性,而以k,o,r,s 结尾的很可能是男性。让我们建立一个分类器更精确地模拟这些差异。
#定义一个特征提取器
def gender_features(word):
'''
这个函数返回的字典被称为特征集,映射特征名称到它们的值。
'''
return {
'last_letter': word[-1]}
gender_features('Shrek')
{'last_letter': 'k'}
准备一个例子和对应类标签的链表
from nltk.corpus import names
import random
names = ([(name, 'male') for name in names.words('male.txt')] +
[(name, 'female') for name in names.words('female.txt')])
random.shuffle(names)
接下来,我们使用特征提取器处理名称数据,并划分特征集的结果链表为一个训练集和一个测试集。训练集用于训练一个新的“朴素贝叶斯”分类器。
featuresets = [(gender_features(n), g) for (n,g) in names]
train_set, test_set = featuresets[500:], featuresets[:500]
classifier = nltk.NaiveBayesClassifier.train(train_set)
classifier.classify(gender_features('Neo'))
'male'
classifier.classify(gender_features('Trinity'))
'female'
print(nltk.classify.accuracy(classifier, test_set))
0.762
检查分类器,确定哪些特征对于区分名字的性别是最有效的。
classifier.show_most_informative_features(5)
Most Informative Features
last_letter = 'a' female : male = 33.4 : 1.0
last_letter = 'k' male : female = 32.2 : 1.0
last_letter = 'v' male : female = 17.5 : 1.0
last_letter = 'f' male : female = 15.3 : 1.0
last_letter = 'p' male : female = 11.9 : 1.0
使用函数nltk.classify.apply_features,返回一个行为像一个链表而不会在内存存储所有特征集的对象
from nltk.classify import apply_features
train_set = apply_features(gender_features, names[500:])
test_set = apply_features(gender_features, names[:500])
选择正确的特征
例6-1. 一个特征提取器,过拟合性别特征。这个特征提取器返回的特征集包括大量指定的特征,从而导致对于相对较小的名字语料库过拟合。
def gender_features2(name):
features = {
}
features["firstletter"] = name[0].lower()
features["lastletter"] = name[-1].lower()
for letter in 'abcdefghijklmnopqrstuvwxyz':
features["count(%s)" % letter] = name.lower().count(letter)
features["has(%s)" % letter] = (letter in name.lower())
return features
gender_features2('John')
{'count(a)': 0,
'count(b)': 0,
'count(c)': 0,
'count(d)': 0,
'count(e)': 0,
'count(f)': 0,
'count(g)': 0,
'count(h)': 1,
'count(i)': 0,
'count(j)': 1,
'count(k)': 0,
'count(l)': 0,
'count(m)': 0,
'count(n)': 1,
'count(o)': 1,
'count(p)': 0,
'count(q)': 0,
'count(r)': 0,
'count(s)': 0,
'count(t)': 0,
'count(u)': 0,
'count(v)': 0,
'count(w)': 0,
'count(x)': 0,
'count(y)': 0,
'count(z)': 0,
'firstletter': 'j',
'has(a)': False,
'has(b)': False,
'has(c)': False,
'has(d)': False,
'has(e)': False,
'has(f)': False,
'has(g)': False,
'has(h)': True,
'has(i)': False,
'has(j)': True,
'has(k)': False,
'has(l)': False,
'has(m)': False,
'has(n)': True,
'has(o)': True,
'has(p)': False,
'has(q)': False,
'has(r)': False,
'has(s)': False,
'has(t)': False,
'has(u)': False,
'has(v)': False,
'has(w)': False,
'has(x)': False,
'has(y)': False,
'has(z)': False,
'lastletter': 'n'}
当运作在小训练集上时尤其会有过拟合问题
featuresets = [(gender_features2(n), g) for (n,g) in names]
train_set, test_set = featuresets[500:], featuresets[:500]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(nltk.classify.accuracy(classifier, test_set))
0.774
一旦初始特征集被选定,完善特征集的一个非常有成效的方法是错误分析。首先,我们选择一个开发集,包含用于创建模型的语料数据。然后将这种开发集分为训练集和开发测试集。
train_names = names[1500:]
devtest_names = names[500:1500]
test_names = names[:500]
train_set = [(gender_features(n), g) for (n,g) in train_names]
devtest_set = [(gender_features(n), g) for (n,g) in devtest_names]
test_set = [(gender_features(n), g) for (n,g) in test_names]
classifier = nltk.NaiveBayesClassifier.train(train_set)
print(nltk.classify.accuracy(classifier, devtest_set))
0.753
使用开发测试集,我们可以生成一个分类器预测名字性别时的错误列表。
errors = []
for (name, tag) in devtest_names:
guess = classifier.classify(gender_features(name))
if guess != tag:
errors.append( (tag, guess, name) )
for (tag,