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

11 篇文章 5 订阅

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)

对于贝叶斯分类器,假设某个样本集有n项特征(Feature),分别为F1、F2、…、Fn。现有m个类别(Category),分别为C1、C2、…、Cm。分类器就是计算出概率最大的那个分类,也就是求下面这个算式的最大值:
P(C|F1F2...Fn)=P(F1F2...Fn|C)P(C)P(F1F2...Fn)

由于 P(F1F2...Fn) 对于所有的类别都是相同的,可以省略,问题就变成了求 P(F1F2...Fn|C)P(C) 的最大值。
朴素贝叶斯分类器则是更进一步,假设所有特征都彼此独立,因此有
P(F1F2...Fn|C)P(C)=P(F1|C)P(F2|C)...P(Fn|C)P(C)

上式等号右边的每一项,都可以从统计资料中得到,由此就可以计算出每个类别对应的概率,从而找出最大概率的那个类。

4.3 最大熵分类器

在信息论中,熵表示离散随机事件的出现概率。一个系统越是有序,信息熵就越低;反之,一个系统越是混乱,信息熵就越高。下面给出一些定义:

4.3.1 熵

如果一个随机变量X的可能取值为X = {x1, x2,…, xk},其概率分布为P(X = xi) = pi(i = 1,2, …, n),则随机变量X的熵定义为:

H(X)=i=1kP(xi)log2P(xi)

4.3.2 条件熵

对于两个随机变量X,Y的联合分布,可以形成联合熵Joint Entropy,用H(X,Y)表示。

在随机变量X发生的前提下,随机变量Y发生所新带来的熵定义为Y的条件熵,用H(Y|X)表示,用来衡量在已知随机变量X的条件下随机变量Y的不确定性,定义为:

H(Y|X)=H(X,Y)H(X)

4.3.3 相对熵

又称互熵,交叉熵。设p(x)、q(x)是X中取值的两个概率分布,则p对q的相对熵是:

D(p||q)=xp(x)logp(x)q(x)=Ep(x)logp(x)q(x)

4.3.4 互信息

互信息:两个随机变量X,Y的互信息定义为X,Y的联合分布和各自独立分布乘积的相对熵,用I(X,Y)表示。

I(X,Y)=x,yp(x,y)logp(x,y)p(x)p(y)=H(X)+H(Y)H(X,Y)

4.3.5 最大熵模型

最大熵模型的本质,就是已知X,计算Y的概率,且尽可能让Y的概率最大(实践中,X可能是某单词的上下文信息,Y是该单词翻译成me,I,us、we的各自概率),从而根据已有信息,尽可能最准确的推测未知信息。模型表示为:

maxH(Y|X)=x,yP(x,y)logP(y|x)

求解最大熵参数的算法有GIS,IIS,MEGAM,TADM,可以在nltk.classify.maxent类中找到实现。

5 参考文献

1 贝叶斯分类应用:
http://www.ruanyifeng.com/blog/2013/12/naive_bayes_classifier.html
2 最大熵模型中的数学推导:
http://blog.csdn.net/v_july_v/article/details/40508465

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值