Python NLTK学习6(创建词性标注器)除特别注明外,本站所有文章均为刘杰的个人网站原创 转载请注明出处: http://www.burnelltek.com/blog/60740e24d2f
2017-03-28 20:40
921 查看
除特别注明外,本站所有文章均为刘杰的个人网站原创
转载请注明出处: http://www.burnelltek.com/blog/60740e24d2f711e6841d00163e0c0e36
本系列博客为学习《用Python进行自然语言处理》一书的学习笔记。
默认标注器为每个单词分配同样的标记,尽管很平庸,但它也是有作用的,先看示例:
结果为:
[('You', 'NN'), ('are', 'NN'), ('a', 'NN'), ('good', 'NN'), ('man', 'NN'), (',', 'NN'), ('but', 'NN'), ('i', 'NN'), ('do', 'NN'), ("n't", 'NN'), ('love', 'NN'), ('you', 'NN'), ('!', 'NN')]
DefaultTagger的构造函数接受一个标记字符串作为参数,生成一个默认标注器对象,从结果可以看到默认标注器把所有的词都标记为NN。
DefaultTagger::tag(tokens):对指定的单词列表进行标记,返回被标记后的单词列表。
DefaultTagger::evaluete(tagged_sents):使用已经被标记的句子评价标注器,返回正确率0~1.0。
结果为:
0.13089484257215028
我们可以看到我们自己创建的默认标注器正确率仅为0.13。
默认标注器对所有单词使用同一个标记,准确率太低,我们可以考虑指定不同的单词为不同的标记,先看一个示例:
结果为:
0.45578495136941344
这次我们创建的标注器的正确率为0.45,比我们的默认标注器要好很多。UnigramTagger类的构造函数接受一个(单词-标记)字典作为模型,可以直接生成一个标注器。事实上UnigramTagger和DefaultTagger类都继承自TaggerI,TaggerI具有tag和evaluete方法,所以UnigramTagger也具有tag和evaluete方法。
因为我们只指定了100个单词的标记,我们来看看我们创建的标注器对未指定的单词是如何标记的。
结果为:
[('You', None), ('are', 'BER'), ('a', 'AT'), ('good', None), ('man', None), (',', ','), ('but', 'CC'), ('i', None), ('do', None), ("n't", None), ('love', None), ('you', None), ('!', None)]
许多词被分配为None标签,这是因为它们不包含在100单词中。针对这种情况,我们可以给它们一个默认标记。换句话说我们要先使用查找表,如果它不能指定一个标记就使用默认标注器,这个过程叫做回退。
结果为:
0.5817769556656125
我们可以看到正确率提升了
如果我们增大单词数量,则正确率还会提升。
结果为:
0.6789983491457326
一元标注器基于一个简单的统计算法:对每个单词分配这个单词最有可能的标记。一元标注器的行为就像一个查询标注器,但是它不需要我们提供model,我们只需要提供训练样本也就是被标记的句子列表,标注器会使用这些样本进行训练,将所有词的最可能标记存储在一个字典里,例子如下:
结果为:
0.9349006503968017
这个结果比我们前面的查询标注器要好很多,并且一元标注器不需要我们自己去统计每个单词最有可能的标记。
然而使用同一个数据集作为训练集和测试集不是一个好的做法,如果我们训练出来的标注器过拟合我们也不能知道,接下来我们要分离训练集和测试集,我们把数据集的90%作为训练集,10%作为测试集。
结果为:
0.9353630649241612
0.8115219774743347
我们可以看到一元标注器在测试集上的正确率为0.81
尽管我们对每个单词分配了这个标识符最有可能的标记,但是在不同的上下文环境中,单词是有可能是其他的标记的。所以一个单词的标记不仅仅和它自身有关系,还可能和它的前一个单词或者更前面的单词有关系。二元标注器就是一个可以考虑单词自身以及前一个单词的标注器。
结果为:
0.7890434263872471
0.10186384929731884
二元标注器会考查一个单词本身和它前一个单词的标记,如果遇到一个新词,那么二元标注器就没法标记它,并且还会导致接下来的单词都没法标记,所以我们会看到二元标注器在测试集上正确率很低。
前面我们对查询标注器设置了一个回退标注器(默认标注器),事实上大部分的NLTK标注器都可以设置回退标注器,这样我们就可以把二元标注器、一元标注器、默认标注器组合起来得到一个组合标注器,例如我们可以按照下列方式组合:
尝试使用bigram标注器标注单词。
如果bigram标注器无法找到一个标记,尝试unigram标注器。
如果unigram标注器无法找到一个标记,使用默认标注器。
代码如下:
结果为:
0.9735641453364413
0.8459085019435861
从结果我们可以看到相比于一元标注器,我们的组合标注器还是有提高的。
TaggerI::tag(tokens):对指定的单词列表进行标记,返回被标记后的单词列表
TaggerI::evaluete(tagged_sents):使用已经被标记的句子评价标注器,返回正确率0~1.0
DefaultTagger, UnigramTagger, BigramTagger都继承至TaggerI
DefaultTagger
默认标注器的构造函数接受一个标记作为参数,生成标注器
UnigramTagger
一元标注器接受被标记的句子列表作为参数,生成标注器
BigramTagger
二元标注器接受被标记的句子列表作为参数,生成标注器
Python NLTK学习1(Text对象)
Python NLTK学习2(FreqDist对象)
Python NLTK学习3(语料库)
Python NLTK学习4(条件频率分布)
Python NLTK学习5(词性标注)
Python NLTK学习6(创建词性标注器)
Python NLTK学习7(对中文昵称进行性别分类)
Python NLTK学习8(正则表达式分块器)
Python NLTK学习9(评估分类器的方法)
Python NLTK学习10(评估分块器)
转载请注明出处: http://www.burnelltek.com/blog/60740e24d2f711e6841d00163e0c0e36
本系列博客为学习《用Python进行自然语言处理》一书的学习笔记。
默认标注器
默认标注器为每个单词分配同样的标记,尽管很平庸,但它也是有作用的,先看示例:import nltk raw = "You are a good man, but i don't love you!" tokens = nltk.word_tokenize(raw) default_tagger = nltk.DefaultTagger('NN') tagged_words = default_tagger.tag(tokens) print(tagged_words)
结果为:
[('You', 'NN'), ('are', 'NN'), ('a', 'NN'), ('good', 'NN'), ('man', 'NN'), (',', 'NN'), ('but', 'NN'), ('i', 'NN'), ('do', 'NN'), ("n't", 'NN'), ('love', 'NN'), ('you', 'NN'), ('!', 'NN')]
DefaultTagger的构造函数接受一个标记字符串作为参数,生成一个默认标注器对象,从结果可以看到默认标注器把所有的词都标记为NN。
DefaultTagger::tag(tokens):对指定的单词列表进行标记,返回被标记后的单词列表。
DefaultTagger::evaluete(tagged_sents):使用已经被标记的句子评价标注器,返回正确率0~1.0。
from nltk.corpus import brown tagged_sents = brown.tagged_sents(categories='news') print(default_tagger.evaluate(tagged_sents))
结果为:
0.13089484257215028
我们可以看到我们自己创建的默认标注器正确率仅为0.13。
查询标注器
默认标注器对所有单词使用同一个标记,准确率太低,我们可以考虑指定不同的单词为不同的标记,先看一个示例:# 对新闻文本进行频率分布,找出新闻文本最常用的100个单词 fd = nltk.FreqDist(brown.words(categories='news')) most_common_pairs = fd.most_common(100) most_common_words = [i[0] for i in most_common_pairs] # 对标记后的新闻文本进行条件频率分布,这样我们就可以找到指定单词最多的标记是哪一个 cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories='news')) # 找出最常用的100个单词的最多标记 likely_tags = dict((word, cfd[word].max()) for word in most_common_words) # 使用(单词-标记)字典作为模型,生成查询标注器 baseline_tagger = nltk.UnigramTagger(model=likely_tags) tagged_sents = brown.tagged_sents(categories='news') print(baseline_tagger.evaluate(tagged_sents))
结果为:
0.45578495136941344
这次我们创建的标注器的正确率为0.45,比我们的默认标注器要好很多。UnigramTagger类的构造函数接受一个(单词-标记)字典作为模型,可以直接生成一个标注器。事实上UnigramTagger和DefaultTagger类都继承自TaggerI,TaggerI具有tag和evaluete方法,所以UnigramTagger也具有tag和evaluete方法。
因为我们只指定了100个单词的标记,我们来看看我们创建的标注器对未指定的单词是如何标记的。
raw = "You are a good man, but i don't love you!" tokens = nltk.word_tokenize(raw) print(baseline_tagger.tag(tokens))
结果为:
[('You', None), ('are', 'BER'), ('a', 'AT'), ('good', None), ('man', None), (',', ','), ('but', 'CC'), ('i', None), ('do', None), ("n't", None), ('love', None), ('you', None), ('!', None)]
许多词被分配为None标签,这是因为它们不包含在100单词中。针对这种情况,我们可以给它们一个默认标记。换句话说我们要先使用查找表,如果它不能指定一个标记就使用默认标注器,这个过程叫做回退。
# 使用默认标注器作为回退 baseline_tagger2 = nltk.UnigramTagger(model=likely_tags, backoff=nltk.DefaultTagger('NN')) tagged_sents = brown.tagged_sents(categories='news') print(baseline_tagger2.evaluate(tagged_sents))
结果为:
0.5817769556656125
我们可以看到正确率提升了
如果我们增大单词数量,则正确率还会提升。
# 对新闻文本进行频率分布,找出新闻文本最常用的500个单词 fd = nltk.FreqDist(brown.words(categories='news')) most_common_pairs = fd.most_common(500) most_common_words = [i[0] for i in most_common_pairs] # 对标记后的新闻文本进行条件频率分布,这样我们就可以找到指定单词最多的标记是哪一个 cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories='news')) # 找出最常用的500个单词的最多标记 likely_tags = dict((word, cfd[word].max()) for word in most_common_words) # 使用(单词-标记)字典作为模型,生成查询标注器 baseline_tagger = nltk.UnigramTagger(model=likely_tags, backoff=nltk.DefaultTagger('NN')) tagged_sents = brown.tagged_sents(categories='news') print(baseline_tagger.evaluate(tagged_sents))
结果为:
0.6789983491457326
一元标注器
一元标注器基于一个简单的统计算法:对每个单词分配这个单词最有可能的标记。一元标注器的行为就像一个查询标注器,但是它不需要我们提供model,我们只需要提供训练样本也就是被标记的句子列表,标注器会使用这些样本进行训练,将所有词的最可能标记存储在一个字典里,例子如下:import nltk from nltk.corpus import brown tagged_sents = brown.tagged_sents(categories='news') # 生成一元标注器 unigram_tagger = nltk.UnigramTagger(train=tagged_sents) print(unigram_tagger.evaluate(tagged_sents))
结果为:
0.9349006503968017
这个结果比我们前面的查询标注器要好很多,并且一元标注器不需要我们自己去统计每个单词最有可能的标记。
然而使用同一个数据集作为训练集和测试集不是一个好的做法,如果我们训练出来的标注器过拟合我们也不能知道,接下来我们要分离训练集和测试集,我们把数据集的90%作为训练集,10%作为测试集。
tagged_sents = brown.tagged_sents(categories='news') size = int(len(tagged_sents) * 0.9) train_sets = tagged_sents[:size] test_sets = tagged_sents[size:] # 生成一元标注器 unigram_tagger = nltk.UnigramTagger(train=train_sets) print(unigram_tagger.evaluate(train_sets)) print(unigram_tagger.evaluate(test_sets))
结果为:
0.9353630649241612
0.8115219774743347
我们可以看到一元标注器在测试集上的正确率为0.81
二元标注器
尽管我们对每个单词分配了这个标识符最有可能的标记,但是在不同的上下文环境中,单词是有可能是其他的标记的。所以一个单词的标记不仅仅和它自身有关系,还可能和它的前一个单词或者更前面的单词有关系。二元标注器就是一个可以考虑单词自身以及前一个单词的标注器。tagged_sents = brown.tagged_sents(categories='news') size = int(len(tagged_sents) * 0.9) train_sets = tagged_sents[:size] test_sets = tagged_sents[size:] # 生成二元标注器 bigram_tagger = nltk.BigramTagger(train=train_sets) print(bigram_tagger.evaluate(train_sets)) print(bigram_tagger.evaluate(test_sets))
结果为:
0.7890434263872471
0.10186384929731884
二元标注器会考查一个单词本身和它前一个单词的标记,如果遇到一个新词,那么二元标注器就没法标记它,并且还会导致接下来的单词都没法标记,所以我们会看到二元标注器在测试集上正确率很低。
组合标注器
前面我们对查询标注器设置了一个回退标注器(默认标注器),事实上大部分的NLTK标注器都可以设置回退标注器,这样我们就可以把二元标注器、一元标注器、默认标注器组合起来得到一个组合标注器,例如我们可以按照下列方式组合:尝试使用bigram标注器标注单词。
如果bigram标注器无法找到一个标记,尝试unigram标注器。
如果unigram标注器无法找到一个标记,使用默认标注器。
代码如下:
import nltk from nltk.corpus import brown # 划分训练集和测试集 tagged_sents = brown.tagged_sents(categories='news') size = int(len(tagged_sents) * 0.9) train_sets = tagged_sents[:size] test_sets = tagged_sents[size:] # 训练标注器,并把它们组合起来 t0 = nltk.DefaultTagger('NN') t1 = nltk.UnigramTagger(train=train_sets, backoff=t0) t2 = nltk.BigramTagger(train=train_sets, backoff=t1) # 查看标注器性能 print(t2.evaluate(train_sets)) print(t2.evaluate(test_sets))
结果为:
0.9735641453364413
0.8459085019435861
从结果我们可以看到相比于一元标注器,我们的组合标注器还是有提高的。
总结
TaggerI::tag(tokens):对指定的单词列表进行标记,返回被标记后的单词列表TaggerI::evaluete(tagged_sents):使用已经被标记的句子评价标注器,返回正确率0~1.0
DefaultTagger, UnigramTagger, BigramTagger都继承至TaggerI
DefaultTagger
默认标注器的构造函数接受一个标记作为参数,生成标注器
UnigramTagger
一元标注器接受被标记的句子列表作为参数,生成标注器
BigramTagger
二元标注器接受被标记的句子列表作为参数,生成标注器
其他章节链接
Python NLTK学习1(Text对象)Python NLTK学习2(FreqDist对象)
Python NLTK学习3(语料库)
Python NLTK学习4(条件频率分布)
Python NLTK学习5(词性标注)
Python NLTK学习6(创建词性标注器)
Python NLTK学习7(对中文昵称进行性别分类)
Python NLTK学习8(正则表达式分块器)
Python NLTK学习9(评估分类器的方法)
Python NLTK学习10(评估分块器)
相关文章推荐
- SilverLight学习笔记--如何在xaml文件中操作用户在后台代码定义的类(2)--示例篇:创建一个登录控件(原创)(转载本文请注明出处)
- 转载本站文章请注明作者和出处 奇葩一朵朵 – http://www.cnblogs.com/season-huang/ ,请勿用于任何商业用途
- python框架之django使用系列教程(1),原创,转载请注明出处
- Python NLTK学习5(词性标注)
- 动态表单——个人心得,转载请注明出处:http://blog.csdn.net/coolwzjcool
- 10) 泛型工厂方法创建实例ClassRegister [原创,泛型编程,自由下载转载,需注明出处]
- 王鸿飞原创作品转载请注明出处《软件工程(C编码实践篇)》MOOC课程http://mooc.study.163.com/course/USTC-10000
- CSS预处理器——Sass、LESS和Stylus实践【未删减版】著作权归作者所有。 商业转载请联系作者获得授权,非商业转载请注明出处。 原文: http://www.w3cplus.com/css/
- http://blog.csdn.net/w00w12l/article/details/8143591?locationNum=14&fps=1 转载请注明出处 Android异步处理系列文章索引
- 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://minilinux.blog.51cto.com/4499123/1309779
- mysql查看数据库和表的占用空间大小 作者:@狮子XL 本文为作者原创,转载请注明出处:http://www.cnblogs.com/iiiiher/p/5853133.html 目录 mysq
- 6) 创建一元函数/二元函数functionCreater [原创,泛型编程,自由下载转载,需注明出处]
- 李望 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-10000290
- 数据的游戏:冰与火--转载本站文章请注明作者和出处 酷壳 – CoolShell.cn ,请勿用于任何商业用途
- c++builder实现文件拖拽与U盘复制---YYB原创(转载请注明出处)
- S3C2450自动升级[原创作品,转载请注明出处]
- 淘宝网的实习生笔试题以及经历--2011 4 1(转载请注明出处,即原创网址)
- 刚子扯扯蛋:说下百度对网站原创文章的个人感受
- 【原创,转载注明出处】Android SD卡排错
- 博客文章是原创还是转载的个人建议