您的位置:首页 > 其它

nlp---使用NLTK进行建构词性标注器

2018-01-31 15:03 323 查看
自然语言是人类在沟通中形成的一套规则体系。规则有强有弱,比如非正式场合使用口语,正式场合下的书面语。要处理自然语言,也要遵循这些形成的规则,否则就会得出令人无法理解的结论。下面介绍一些术语的简单区别。
文法:等同于语法(grammar),文章的书写规范,用来描述语言及其结构,它包含句法和词法规范。
句法:Syntax,句子的结构或成分的构成与关系的规范。
词法:Lexical,词的构词,变化等的规范。

词性标注,或POS(Part Of Speech),是一种分析句子成分的方法,通过它来识别每个词的词性。
下面简要列举POS的tagset含意,详细可看nltk.help.brown_tagset()
标记词性示例
ADJ形容词new, good, high, special, big, local
ADV动词really, already, still, early, now
CONJ连词and, or, but, if, while, although
DET限定词the, a, some, most, every, no
EX存在量词there, there’s
MOD情态动词will, can, would, may, must, should
NN名词year,home,costs,time
NNP专有名词April,China,Washington
NUM数词fourth,2016, 09:30
PRON代词he,they,us
P介词on,over,with,of
TO词toto
UH叹词ah,ha,oops
VB 动词
VBD动词过去式made,said,went
VBG现在分词going,lying,playing
VBN过去分词taken,given,gone
WHwh限定词who,where,when,what

1 使用NLTK对英文进行词性标注

1.1 词性标注示例

import nltk

sent="I am going to Beijing tomorrow";
tokens = nltk.word_tokenize(sent)
taged_sent = nltk.pos_tag(tokens)
print taged_sent
#[('I', 'PRP'), ('am', 'VBP'), ('going', 'VBG'), ('to', 'TO'), ('Beijing', 'NNP'), ('tomorrow', 'NN'), ('.', '.')]
1
2
3
4
5
6
7

1.2 语料库的已标注数据

语料类提供了下列方法可以返回预标注数据。
方法说明
tagged_words(fileids,categories)返回标注数据,以词列表的形式
tagged_sents(fileids,categories)返回标注数据,以句子列表形式
tagged_paras(fileids,categories)返回标注数据,以文章列表形式

2 标注器

2.1 默认标注器

最简单的词性标注器是将所有词都标注为名词NN。这种标注器没有太大的价值。正确率很低。下面演示NLTK提供的默认标注器的用法。
import nltk
from nltk.corpus import brown

default_tagger = nltk.DefaultTagger('NN')
sents = 'I am going to Beijing.'
print default_tagger.tag(sents)
#[('I', 'NN'), ('am', 'NN'), ('going', 'NN'), ('to', 'NN'), ('Beijing', 'NN')]

tagged_sents = brown.tagged_sents(categories='news')
print default_tagger.evaluate(tagged_sents) #0.131304
1
2
3
4
5
6
7
8
9
10

2.2 基于规则的标注器

从默认标注器的评估来看,只有13%的正确率。为了改进这一效果,我们使用一些规则来提高正确率。比如对于ing结尾则柡注为VG,ed结尾则标注为VD。可以通过正则表达式标注器实现这个想法。
import nltk
from nltk.corpus import brown

pattern =[
(r'.*ing$','VBG'),
(r'.*ed$','VBD'),
(r'.*es$','VBZ'),
(r'.*\'s$','NN$'),
(r'.*s$','NNS'),
(r'.*', 'NN')  #未匹配的仍标注为NN
]
sents = 'I am going to Beijing.'

tagger = nltk.RegexpTagger(pattern)
print tagger.tag(nltk.word_tokenize(sents))
#[('I', 'NN'), ('am', 'NN'), ('going', 'VBG'), ('to', 'NN'), ('Beijing', 'VBG'), ('.', 'NN')]

tagged_sents = brown.tagged_sents(categories='news')
print default_tagger.evaluate(tagged_sents) #0.1875
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

2.3 基于查表的标注器

经过增加简单的规则,可以提升默认标注器的准确度,但仍不够好。为此我们统计一下部分高频词的词性,比如经常出现的100个词的词性。利用单个词的词性的统计知识来进行标注,这就是Unigram模型的思想。
import nltk
from nltk.corpus import brown

fdist = nltk.FreqDist(brown.words(categories='news'))
common_word = fdist.most_common(100)

cfdist = nltk.ConditionalFreqDist(brown.tagged_words(categories='news'))
table= dict((word, cfdist[word].max()) for (word, _) in common_word)

uni_tagger = nltk.UnigramTagger(model=table,backoff=nltk.DefaultTagger('NN'))
print uni_tagger.evaluate(tagged_sents) #0.5817
1
2
3
4
5
6
7
8
9
10
11
只利用前100个词的历史统计数据便能获得58%的正确率,加大这个词的数量更可以继续提升标注的正确率,当为8000时可以达到90%的正确率。这里我们对不在这100个词的其他词统一回退到默认标注器。

3 训练N-gram标注器

3.1 一般N-gram标注

在上一节中,已经使用了1-Gram,即Unigram标注器。考虑更多的上下文,便有了2/3-gram,这里统称为N-gram。注意,更长的上正文并不能带来准确度的提升。 
除了向N-gram标注器提供词表模型,另外一种构建标注器的方法是训练。N-gram标注器的构建函数如下:__init__(train=None, model=None, backoff=None),可以将标注好的语料作为训练数据,用于构建一个标注器。
import nltk
from nltk.corpus import brown

brown_tagged_sents = brown.tagged_sents(categories='news')
train_num = int(len(brown_tagged_sents) * 0.9)
x_train =  brown_tagged_sents[0:train_num]
x_test =   brown_tagged_sents[train_num:]
tagger = nltk.UnigramTagger(train=x_train)
print tagger.evaluate(x_test)  #0.81
1
2
3
4
5
6
7
8
9
对于UniGram,使用90%的数据进行训练,在余下10%的数据上测试的准确率为81%。如果改为BiGram,则正确率会下降到10%左右。

3.2 组合标注器

可以利用backoff参数,将多个组合标注器组合起来,以提高识别精确率。
import nltk
from nltk.corpus import brown
pattern =[
(r'.*ing$','VBG'),
(r'.*ed$','VBD'),
(r'.*es$','VBZ'),
(r'.*\'s$','NN$'),
(r'.*s$','NNS'),
(r'.*', 'NN')  #未匹配的仍标注为NN
]
brown_tagged_sents = brown.tagged_sents(categories='news')
train_num = int(len(brown_tagged_sents) * 0.9)
x_train =  brown_tagged_sents[0:train_num]
x_test =   brown_tagged_sents[train_num:]

t0 = nltk.RegexpTagger(pattern)
t1 = nltk.UnigramTagger(x_train, backoff=t0)
t2 = nltk.BigramTagger(x_train, backoff=t1)
print t2.evaluate(x_test)  #0.863
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
从上面可以看出,不需要任何的语言学知识,只需要借助统计数据便可以使得词性标注做的足够好。  
对于中文,只要有标注语料,也可以按照上面的过程训练N-gram标注器。

4 更进一步

nltk.tag.BrillTagger实现了基于转换的标注,在基础标注器的结果上,对输出进行基于规则的修正,实现更高的准确度。

5 示例:中文标注器的训练

下面基于Unigram训练一个中文词性标注器,语料使用网上可以下载得到的人民日报98年1月的标注资料。百度网盘链接
# coding=utf-8
import nltk
import json

lines = open('9801.txt').readlines()
all_tagged_sents = []

for line in lines:
line = line.decode('utf-8')
sent = line.split()
tagged_sent = []
for item in sent:
pair = nltk.str2tuple(item)
tagged_sent.append(pair)

if len(tagged_sent)>0:
all_tagged_sents.append(tagged_sent)

train_size = int(len(all_tagged_sents)*0.8)
x_train = all_tagged_sents[:train_size]
x_test = all_tagged_sents[train_size:]

tagger = nltk.UnigramTagger(train=x_train,backoff=nltk.DefaultTagger('n'))

tokens = nltk.word_tokenize(u'我 认为 不丹 的 被动 卷入 不 构成 此次 对峙 的 主要 因素。')
tagged = tagger.tag(tokens)
print json.dumps(tagged,encoding='UTF-8', ensure_ascii=False)
#["我", "R"], ["认为", "V"], ["不丹", "n"], ["的", "U"], ["被动", "A"], ["卷入", "V"], ["不", "D"], ["构成", "V"], ["此次", "R"], ["对峙", "V"], ["的", "U"], ["主要", "B"], ["因素。", "n"]
print tagger.evaluate(x_test) #0.871
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
简单的训练,标注器便取得了87.1%的成绩,还是不错的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: