模式识别是自然语言处理的一个核心部分。
6.1有监督分类
分类:是为给定的输入选择正确的类标签任务。
有监督分类:如果分类的基础基于包含每个输入正确标签的训练语料。
有监督分类的使用框架图如下:
性别鉴定
让我们以性别鉴定这个简单的例子,再次解释上述图形的流程。
背景:男女的名字是有一定背景的,以此为背景来解释。在这里我们以每个名字的最后一个字母为特征来标志是否为男女。
1、确定特征集
def gender_features(word): return {'last_letter':word[-1]}
这个函数返回的字典被成为特征集。
2、准备一个例子和对应类标签链表
from nltk.corpus import names import random >>>names= ([(name, 'male') for name in names.words('male.txt')] + ... [(name, 'female') for namein names.words('female.txt')]) >>>random.shuffle(names)
3、提取特征集
featuresets=[(gender_feature(n),g) for (n,g) in names]
4、把特征集分成训练集和测试集
>>>train_set, test_set = featuresets[500:], featuresets[:500]
>>>classifier = nltk.NaiveBayesClassifier.train(train_set)
5、使用分类器
>>>classifier.classify(gender_features('Neo')) 'male' >>>classifier.classify(gender_features('Trinity')) 'female'
到这里就知道了,应该怎么使用分类器。
我们可以评价此分类器:
>>>print nltk.classify.accuracy(classifier, test_set) 0.758
我们还可以查看,哪些特征对于区分名字的性别是最有效的。
>>>classifier.show_most_informative_features(5) MostInformative Features last_letter = 'a' female : male = 38.3 : 1.0 last_letter = 'k' male: female = 31.4 : 1.0 last_letter = 'f' male: female = 15.3: 1.0 last_letter = 'p' male: female = 10.6 : 1.0 last_letter = 'w' male: female = 10.6 : 1.0
这些比例被称为似然比。
注意,在处理大型语料库的时候,如果要建立测试集和训练集,为了节省内存,可以使用下面的办法:
>>>from nltk.classifyimport apply_features >>>train_set = apply_features(gender_features, names[500:]) >>>test_set = apply_features(gender_features, names[:500])
选取正确的特征
能够选取正确的特征,对一个模型的影响非常重要。
在构造特征的时候,我们可以选取很多特征,例如,还是在性别鉴定的例子上:
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(j)':1,'has(d)': False,'count(b)': 0,...}
需要注意的是,如果选取的特征过多,会高度依赖这些特征,当运作在小训练集上会出现问题,精确度会降低。这个问题被叫做过拟合。
>>>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.748
其实,在找到特征集之后,还需要错误分析,来完善特征集。
我们可以把一个特征集分成训练集、开发测试集和最终用来评估的测试集。
>>>train_names = names[1500:] >>>devtest_names= names[500:1500] >>>test_names = names[:500]
文档分类
#读取文档 >>>from nltk.corpusimport movie_reviews >>>documents= [(list(movie_reviews.words(fileid)), category) ... for categoryin movie_reviews.categories() ... for fileid in movie_reviews.fileids(category)] >>>random.shuffle(documents) #设置分类器,特征为每个词是否在一个特定文档中 all_words = nltk.FreqDist(w.lower()for win movie_reviews.words()) word_features= all_words.keys()[:2000] def document_features(document): document_words= set(document) features = {} for wordin word_features: features['contains(%s)' %word]= (word in document_words) return features >>>print document_features(movie_reviews.words('pos/cv957_8737.txt')) {'contains(waste)':False, 'contains(lot)': False,...} #训练一个特征集 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) � 0.81 >>>classifier.show_most_informative_features(5) � MostInformative Features contains(outstanding) = True pos: neg = 11.1: 1.0 contains(seagal) = True neg: pos = 7.7: 1.0 contains(wonderfully) = True pos: neg = 6.8 : 1.0 contains(damon)= True pos: neg = 5.9 : 1.0 contains(wasted) =True neg : pos = 5.8: 1.0
探索上下文语境
例如,我们可能会遇到一个词,fly,如果在他前面的词是a,那么我们就可以确定他是一个名词而不是一个动词。
现在我们设计一个词性分类器,来检查他出现的上下文,以便决定应该怎样分配词性标记。
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
然后,来生成一个特征集,然后把这个集合分成训练集和测试集。
>>>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) >>>nltk.classify.accuracy(classifier,test_set)
利用这样的标注器,可以提高准确率。
序列分类
为了捕捉相关的分类任务之间的依赖关系,我们可以使用联合分类器模型,收集有关输入,选择合适标签。
在词性标注的例子中,各种不同的序列分类器模型可以被用来为一个给定的句子中的所有的词共同选择词性标签。
一种序列分类器策略,称为连续分类或贪婪序列分类:是为第一个输入找到最有可能的类标签,然后使用这个问题的答案帮助寻找到下一个输入的最佳标签。这个过程可以不断的重复,直到所有的输入都被贴上标签。
下面是一个连续分类的进行词性标注的例子:
def pos_features(sentence,i, history): features = {"suffix(1)": sentence[i][-1:], "suffix(2)": sentence[i][-2:], "suffix(3)": sentence[i][-3:]} if i ==0: features["prev-word"] = "<START>" features["prev-tag"] = "<START>" else: features["prev-word"] = sentence[i-1] features["prev-tag"] = history[i-1] return features
classConsecutivePosTagger(nltk.TaggerI): def __init__(self, train_sents): train_set = [] for tagged_sent in train_sents: untagged_sent = nltk.tag.untag(tagged_sent) history = [] for i, (word, tag) in enumerate(tagged_sent): featureset = pos_features(untagged_sent,i, history) train_set.append( (featureset, tag) ) history.append(tag) self.classifier = nltk.NaiveBayesClassifier.train(train_set) def tag(self, sentence): history = [] for i, wordin enumerate(sentence): featureset = pos_features(sentence,i, history) tag = self.classifier.classify(featureset) history.append(tag) return zip(sentence, history) >>>tagged_sents = brown.tagged_sents(categories='news') >>>size = int(len(tagged_sents) *0.1) >>>train_sents, test_sents = tagged_sents[size:], tagged_sents[:size] >>>tagger = ConsecutivePosTagger(train_sents) >>>print tagger.evaluate(test_sents) 0.79796012981
其他序列分类方法
隐马尔可夫模型:类似于连续分类器。他不光看输入也看已经预测标记的历史。为所有可能的序列打分,选择总得分最高的序列。