NLTK学习之三:文本分类与构建基于分类的词性标注器

1 有监督的分类

1.1 分类相关概念

分类是为给定输入选择正确的类标签的任务。比如判断一封Email是否是垃圾邮件,确定一篇新闻的主题。
如果分类需要人工标准的标签进行训练,则称为有监督分类。有监督的学习使用下图的框架。
此处输入图片的描述
在框架中,特征是一个非常重要的概念。使用分类器首先要决定选择什么 样的特征,以及对特征进行编码。如果没有特征直接输入原始数据,则数据会十分离散而难以训练出有用的模型。

1.2 NLTK的分类器介绍

在NLTK中提供了NaiveBayesClassifier,DecisionTreeClassifier,MaxentClassifier三种类型的分类器。分类器都提供了类方法可以训练出一个分类器实例,有了这个实例,便能对新的样本进行分类预测,以及其进行准确度评测。

方法 说明
train(train_set) 类方法,用于生成一个分类器实例
classify(feature) 实例方法,基于训练的模型对输入特征进行分类
show_most_informative_features() 实例方法,显示训练过程中最有效的特性统计

nltk.classify包的工具类提供了下列的方法辅助训练及优化过程

方法 说明
accuracy(classifier,test_set) 评估分类器在测试集上的准确度
apply_feature(func,data) 将特征函数func应用到data上,类似于map操作

1.3 文本分类示例

下面基于nltk的movie_reviews语料库的正负向标注数据训练一个简单的分类器,用来预测评论的正负向。PS:语料库文本被分两类,pos与neg。
代码先统计出最常用2000个词,简单假设这些词的使用情况可以决定一篇评论的正负向情感。针对每篇文档,特征提取器计算其在这2000个词上出现的情况,若出现则在特性中标记为True,否则标记为False。

import random
import nltk
from nltk.corpus import movie_reviews

docs = [(list(movie_reviews.words(fileid)),category)
        for category in movie_reviews.categories()
        for fileid in movie_reviews.fileids(category)]
random.shuffle(docs)

all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
most_common_word = [word for (word,_) in all_words.most_common(2000)]

def doc_feature(doc):
    doc_words = set(doc)
    feature = {}
    for word in most_common_word:
        feature[word] = (word in doc_words)
    return feature

train_set = nltk.apply_features(doc_feature,docs[:100])
test_set = nltk.apply_features(doc_feature,docs[100:])

classifier = nltk.NaiveBayesClassifier.train(train_set)
print nltk.classify.accuracy(classifier,test_set) #0.735
classifier.show_most_informative_features()
'''
Most Informative Features
hard = True                neg : pos    =      8.6 : 1.0
worst = True               neg : pos    =      7.1 : 1.0
giant = True               neg : pos    =      7.1 : 1.0
unlike = True              pos : neg    =      6.8 : 1.0
entire = True              pos : neg    =      5.6 : 1.0
headed = True              neg : pos    =      5.6 : 1.0
gun = True                 neg : pos    =      5.6 : 1.0
attempt = True             neg : pos    =      5.2 : 1.0
small = True               pos : neg    =      5.0 : 1.0
'''

可以看到出现hard,worst,giant词的文档通常是neg的,出现entire,small词的文档通常是正向的。基于特征词的分类器在测试集上达到了73.5%的正确率。精心构造的对于当前任务透彻理解的特性,通常可以显著提高分类的正确性。

nltk.sentiment包提供了两个情感分析器的实现,使用示例见这里

类名 说明
SentimentAnalyzer 实现和上面代码的思想一致,只是简单做了些封装,支持传入不同的分类器和使用串联的多个特征提取器。可以直接使用此类进行中文的情感分析。
SentimentIntensityAnalyzer 实现基于《VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text》这篇文章,思想是基于词典对词进行打分,得到了个分值来表示句子情感的极性。这个类由于规则词典的词都是英文,如果想使用它处理中文,需要自己统计提取中文极性词并打分,以替换掉英文词典。

1.4 过拟合和欠拟合

特性如果选择的太多则会导致算法在训练集上正确率很高,而在测试集上较差的情况,此时就出现了过拟合。尤其是训练数据集较小的情况下更容易出现。因此要平衡特征的选取,以取得算法在未知数据上更好泛化能力。
过拟合的反面是欠拟合,即特征不足。此时通过对失败情况进行分析,改进特征提取器以重建分类器是提升模型准确性的一种方法。但要注意防止出现过拟合。

2 基于上下文的词性标注器

在上一篇文章中,主要介绍了N-gramTagger的词性标注器的使用方法,其主要思想是基于词的词性的历史出现次数进行推测。下面介绍实现一种基于分类器的词性标注器,它借助词本身,词的上下文,标注信息的上下文等特征来训练一个词性分类器,从而实现词性标注。

import nltk
from nltk.corpus import brown

def pos_feature_use_hist(sentence,i,history):
    features = {
        'suffix-1': sentence[i][-1:],
        'suffix-2': sentence[i][-2:],
        'suffix-3': sentence[i][-3:],
        'pre-word': 'START',
        'prev-tag': 'START'
    }
    if i>0:
        features['prev-word'] = sentence[i-1],
        features['prev-tag'] =  history[i-1]
    return features

class ContextPosTagger(nltk.TaggerI):
    def __init__(self,train):
        train_set = []
        for tagged_sent in train:
            untagged_sent = nltk.tag.untag(tagged_sent)
            history = []
            for i,(word,tag) in enumerate(tagged_sent):
                features = pos_feature_use_hist(untagged_sent,i,history)
                train_set.append((features,tag))
                history.append(tag)
        print train_set[:10]
        self.classifier = nltk.NaiveBayesClassifier.train(train_set)

    def tag(self,sent):
        history = []
        for i,word in enumerate(sent):
            features = pos_feature_use_hist(sent,i,history)
            tag = self.classifier.classify(features)
            history.append(tag)
        return zip(sent,history)

tagged_sents = brown.tagged_sents(categories='news')
size = int(len(tagged_sents)*0.8)
train_sents,test_sents = tagged_sents[0:size],tagged_sents[size:]

#tagger = nltk.ClassifierBasedPOSTagger(train=train_sents)  # 0.881
tagger = ContextPosTagger(train_sents)  #0.78
tagger.classifier.show_most_informative_features()
print tagger.evaluate(test_sents)

这个简单只考虑前一个词及前一个词的词性的标注器,在测试集上有约78%的准确度,尚没有上节的组合N-gram标注器的正确率高,这主要是例子中的特征提取器考虑十分简单的缘故。nltk默认实现了一个ClassifierBasedPOSTagger类,可以实现约88%的标注准确度。
例子中实现存在的问题是一旦一个词的词定下来,那么后面就无法修改,即使是后面发现前面判断出了错。解决这个问题有两种方法:一种是采用转换策略,即Brill标注器。另一种对所有的词性标记序列打分,选择总分最高的序列。隐含马尔可夫模型即实现此方法,此外更高级的如最大熵马尔可夫模型,线性条件随机场模型。

3 算法评估方法

3.1 精确度与召回率

对于分类算法在测试集上运行之后,数据会被分为下表的四类

预测\类别 正例 负例
分类为正 TP(真正例) FP(假正例)
分类为负 FN(假负例) TN(真负例)

准确度A:(TP+TN)/ALL 分类器正确分类的比例。
精确度P:TP/(TP+FP), 预测为正的样本中,有多少真的正样本
召回率R:TP/(TP+FN),测试集中的正样本,有多少被正确分类
F1评分:(2*P*R)/(P+R),R与P的调和平均数

3.2 混淆矩阵

对于多分类任务,可以使用混淆矩阵来分析错误分类的细分信息。混淆矩阵的元素m[i,j],表示正确的类别i,被预测为类别j的次数。

import nltk
gold = [1,2,3,4]
test = [1,3,2,4]
print nltk.ConfusionMatrix(gold,test)
'''output
  | 1 2 3 4 |
--+---------+
1 |<1>. . . |
2 | .<.>1 . |
3 | . 1<.>. |
4 | . . .<1>|
--+---------+
'''

4 分类器模型介绍

4.1 决策树分类器

决策树(decision tree)是一个树结构。其每个非叶节点表示一个特征属性上的测试,而每个叶节点存放一个类别。使用决策树进行决策的过程就是从根节点开始,测试待分类项中相应的特征属性,并按照其值选择输出分支,直到到达叶子节点,将叶子节点存放的类别作为决策结果。
下面是一棵形象的决策树:此处输入图片的描述

4.2 朴素贝叶斯分类器

由条件概率和乘法法则:

P(A|B)=P(AB)P(B)=P(B|A)P(A)P(B)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值