早在小學時我們就學過名詞荤牍、動詞氧敢、形容詞和副詞之間的差異。這些“詞類”不是閑置的文法家的發(fā)明另凌,而是對許多語言處理任務都有用的分類谱轨。正如我們將看到的,這些分類源于對文本中詞的分布的簡單的分析吠谢。
將單詞按它們的詞性分類并進行相應地標注的過程土童,稱為詞語性質標注、詞性標注或簡稱標注工坊。詞性也稱為詞類或詞匯類別献汗。用于特定任務的標記的集合被稱為一個標記集。我們在本章的重點是運用標記和自動標注文本王污。
一罢吃、使用詞性標注器
text = nltk.word_tokenize("And now for something completely different")
nltk.pos_tag(text)
>> [('And', 'CC'),
('now', 'RB'),
('for', 'IN'),
('something', 'NN'),
('completely', 'RB'),
('different', 'JJ')]
and是CC,并列連詞昭齐;now和completely是RB尿招,副詞;for是IN阱驾,介詞就谜;something是NN,名詞里覆;different是JJ丧荐,形容詞
text = nltk.word_tokenize("They refuse to permit us to obtain the refuse permit")
nltk.pos_tag(text)
>> [('They', 'PRP'),
('refuse', 'VBP'),
('to', 'TO'),
('permit', 'VB'),
('us', 'PRP'),
('to', 'TO'),
('obtain', 'VB'),
('the', 'DT'),
('refuse', 'NN'),
('permit', 'NN')]
請注意:refuse和permit都以現(xiàn)在時動詞(VBP)和名詞(NN)形式出現(xiàn)。例如refUSE是一個動詞租谈,意為“拒絕”篮奄,而REFuse是一個名詞捆愁,意為“垃圾”(即它們不是同音詞)。因此窟却,我們需要知道正在使用哪一個詞以便能正確讀出文本昼丑。
匯類別如“名詞”和詞性標記如NN,看上去似乎有其用途夸赫,但好像卻不是那么容易讓人明白它們的用途菩帝。
text = nltk.Text(word.lower() for word in nltk.corpus.brown.words())
text.similar('woman')
>> man time day year car moment world house family child country boy
state job place way war girl work word
text.similar('bought')
>> made said done put had seen found given left heard was been brought
set got that took in told felt
text.similar('over')
>> in on to of and for with from at by that into as up out down through
is all about
text.similar('the')
>> a his this their its her an that our any all one these my in your no
some other and
上面是一個查找相似詞的程序,可以觀察到茬腿,搜索woman找到名詞呼奢;搜索bought找到的大部分是動詞;搜索over一般會找到介詞切平;搜索the找到幾個限定詞握础。一個標注器能夠正確識別一個句子的上下文中的這些詞的標記,例如The woman bought over $150,000 worth of clothes悴品。
一個標注器還可以為我們對未知詞的認識建模禀综,例如我們可以根據(jù)詞根scrobble猜測scrobbling可能是一個動詞,并有可能發(fā)生在he was scrobbling這樣的上下文中苔严。
二定枷、標注語料庫
1. 表示已標注的標識符
按照 NLTK 的約定,一個已標注的標識符使用一個由標識符和標記組成的元組來表示届氢。 我們可以使用函數(shù) str2tuple()從表示一個已標注的標識符的標準字符串創(chuàng)建一個這樣的特殊元組:
tagged_token = nltk.tag.str2tuple('fly/NN')
tagged_token
>> ('fly', 'NN')
2. 讀取已標注的語料庫
只要語料庫包含已標注的文本欠窒,NLTK的語料庫接口都將有一個tagged_words()方法,使用該方法退子,我們就可以得到 [('The', 'AT'), ('Fulton', 'NP-TL'), ...]
這種形式的文章岖妄。
nltk.corpus.brown.tagged_words()
語料類一共提供了下列方法可以返回預標注數(shù)據(jù):
并非所有的語料庫都采用同一組標記;看前面提到的標記集的幫助函數(shù)和readme()方法中的文檔絮供。最初衣吠,我們想避免這些標記集的復雜化茶敏,所以我們使用一個內置的到“通用標記集“的映射:
nltk.corpus.brown.tagged_words(tagset='universal')
如果語料庫也被分割成句子壤靶,將有一個tagged_sents()方法將已標注的詞劃分成句子,而不是將它們表示成一個大列表惊搏。也就是說贮乳,詞還是以(詞,POS)的形式出現(xiàn)恬惯,但是會以一個句子一個列表的形式進行存放向拆。
3. 各類詞性
名詞:一般指的是人、地點酪耳、事情或概念浓恳,例如:女人刹缝、蘇格蘭、圖書颈将、情報梢夯。名詞可能 出現(xiàn)在限定詞和形容詞之后,可以是動詞的主語或賓語
動詞:是用來描述事件和行動的詞晴圾,例如:fall 和 eat颂砸。在一個句子中,動詞通常表示涉及一個或多個名詞短語所指示物的關系死姚。
形容詞:形容詞修飾名詞人乓,可以作為修飾符(如:the la rge pizza中的large)或謂語(如:the pizza is large)。英語形容詞可以有內部結構(如:t he falling stocks 中的 fall+ing)都毒。
副詞:副詞修飾動詞色罚,指定時間、方式账劲、地點或動詞描述的事件的方向(如:the stocks fell quickly 中的 quickly)保屯。副詞也可以修飾的形容詞(如:Mary’ s teacher was really nice 中的 really)。
英語中還有幾個封閉的詞類涤垫,如介詞姑尺,冠詞(也常稱為限定詞)(如:the,a)蝠猬,情態(tài)動 詞(如:should切蟋,may),人稱代詞 (如:she榆芦,they)柄粹。每個詞典和語法對這些詞的分類都不同。
三匆绣、使用 Python 字典映射詞及其屬性
略
四驻右、自動標注
1. 默認標注器
為每個標識符分配同樣的標記:
# 尋找最可能的標記
tags = [tag for (word, tag) in brown.tagged_words(categories='news')]
nltk.FreqDist(tags).max()
>> 'NN'
raw = 'I do not like eggs and ham, I do not like them Sam I am'
tokens = nltk.word_tokenize(raw)
default_tagger = nltk.DefaultTagger('NN')# 創(chuàng)建一個將所有詞都標注成 NN 的標注器
print(default_tagger.tag(tokens)) # 調用tag()方法進行標注
print(default_tagger.evaluate(brown_tagged_sents)) # 使用默認
默認的標注器給每一個單獨的詞分配標記,即使是之前從未遇到過的詞崎淳。碰巧的是堪夭,一旦我們處理了幾千詞的英文文本之后,大多數(shù)新詞都將是名詞拣凹。正如我們將看到的森爽,這意味 著,默認標注器可以幫助我們提高語言處理系統(tǒng)的穩(wěn)定性嚣镜。
2. 正則表達式標注器
正則表達式標注器基于匹配模式分配標記給標識符爬迟。
例如:我們可能會猜測任一以 ed 結尾的詞都是動詞過去分詞,任一以's 結尾的詞都是名詞所有格菊匿「杜唬可以用一個正則表達式的列表表示這些:
>>> patterns = [
... (r'.*ing$', 'VBG'), # gerunds
... (r'.*ed$', 'VBD'), # simple past
... (r'.*es$', 'VBZ'), # 3rd singular present
... (r'.*ould$', 'MD'), # modals
... (r'.*\'s$', 'NN$'), # possessive nouns
... (r'.*s$', 'NNS'), # plural nouns
... (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers
... (r'.*', 'NN') # nouns (default)
... ]
regexp_tagger = nltk.RegexpTagger(patterns)
print(regexp_tagger.tag(tokens))
print(regexp_tagger.evaluate(brown_tagged_sents))
3. 查詢標注器
找出 n 個最頻繁的詞计福,存儲它們最有可能的標記。 然后就可以使用這個信息作為“查找標注器”(NLTK UnigramTagger)的模型徽职。不在頻繁詞中的詞分配默認標記 NN棒搜。
換句話說,先使用查找表活箕,如果它不能指定一個標記就使用默認標注器力麸,這個過程叫做回退。
fd = nltk.FreqDist(brown.words(categories="news"))
cfd = nltk.ConditionalFreqDist(brown.tagged_words(categories="news"))
most_freq_words = fd.most_common(100)
likely_tags = dict((word, cfd[word].max()) for (word,times) in most_freq_words)
baseline_tagger = nltk.UnigramTagger(model=likely_tags, backoff=nltk.DefaultTagger('NN'))
print(baseline_tagger.tag(tokens))
print(baseline_tagger.evaluate(brown_tagged_sents))
五育韩、N-gram 標注
1. 一元標注( Unigram Tagging)
一元標注器基于一個簡單的統(tǒng)計算法:對每個標識符分配這個獨特的標識符最有可能的標記克蚂。
例如: 它將分配標記 JJ 給詞 frequent ,因為 frequent 用作一個形容詞(例 如:a frequent word)比用作一個動詞(例如:I frequent this cafe)更常見筋讨。
from nltk.corpus import brown
brown_tagged_sents = brown.tagged_sents(categories='news')
brown_sents = brown.sents(categories='news')
unigram_tagger = nltk.UnigramTagger(brown_tagged_sents)
unigram_tagger.tag(brown_sents[2007])
2. 一般的 N-gram 的標注
一個 n-gram 標注器是一個 unigram 標注器的一般化埃叭,它的上下文是當前詞和它前面 n- 1 個標識符的詞性標記。如下圖所示:要選擇的標記是圓圈里的 tn悉罕,灰色陰影的是上下文赤屋。
在上面的n-gram 標注器的例子中,讓 n= 3壁袄,也就是說尘执,考慮當前詞的前兩 個詞的標記欧啤。一個 n-gram 標注器挑選在給定的上下文中最有可能的標記。
# 一般的N-gram標注器(代碼為二元)
size = int(len(brown_tagged_sents)*0.9)
train_sents = brown_tagged_sents[:size]
test_sents = brown_tagged_sents[size+1:]
bigram_tagger = nltk.BigramTagger(train_sents)
print(bigram_tagger.tag(tokens))
print(bigram_tagger.evaluate(test_sents))
3. 組合標注器
解決精度和覆蓋范圍之間的權衡的一個辦法是盡可能的使用更精確的算法,但卻在很多 時候落后于具有更廣覆蓋范圍的算法饭庞。例如:可以按如下方式組合 bigram 標注器奈籽、uni gram 標注器和一個默認標注器:
- 嘗試使用二元標注器標注標識符痛悯。
- 如果二元標注器無法找到一個標記鸵鸥,嘗試一元標注器。
- 如果一元標注器也無法找到一個標記萄凤,使用默認標注器室抽。
大多數(shù)NLTK標注器允許指定一個回退標注器∶遗回退標注器自身可能也有一個回退標注器:
t0 = nltk.DefaultTagger('NN')
t1 = nltk.UnigramTagger(train_sents,backoff=t0)
t2 = nltk.BigramTagger(train_sents,backoff=t1)
t2.evaluate(test_sents)
注意:對于例如trigram的標注器會使用前兩個標識符的詞性坪圾,但有時會遇到前兩個詞是上一句話的最后一個單詞和句子結尾的標點符號。通常颤难,前一句結尾的詞的類別和下一句的開頭的詞性沒有什么關系神年,為了避免這種跨句子邊界標注的問題,常通過已標記的tagged_sents(也就是按句子進行處理的語料)來訓練標注器行嗤。
六、標注生詞
標注生詞的方法仍然是回退到一個正則表達式標注器或一個默認標注器垛耳。這些都無法利用上下文栅屏。因此飘千,如果標注器遇到詞blog,訓練過程中沒有看到過栈雳,它會分配相同的標記护奈,不論這個詞出現(xiàn)的上下文是the blog還是to blog。我們怎樣才能更好地處理這些生詞哥纫,或詞匯表以外的項目霉旗?
一個有用的基于上下文標注生詞的方法是限制一個標注器的詞匯表為最頻繁的 n 個詞, 使用 5.3 節(jié)中的方法替代每個其他的詞為一個特殊的詞 UNK蛀骇。訓練時厌秒,一個 unigram 標注器可能會學到 UNK 通常是一個名詞。然而擅憔,n-gram 標注器會檢測它的一些其他標記中的上下文鸵闪。例如:如果前面的詞是 to(標注為 TO),那么 UNK 可能會被標注為一個動詞暑诸。
七蚌讼、存儲標注器
在大語料庫上訓練一個標注器可能需要大量的時間。沒有必要在每次我們需要的時候訓練一個標注器个榕,很容易將一個訓練好的標注器保存到一個文件以后重復使用篡石。讓我們保存我們的標注器t2到文件t2.pkl。
# 保存
from pickle import dump
output = open('t2.pkl', 'wb')
dump(t2, output, -1)
output.close()
# 加載
from pickle import load
input = open('t2.pkl', 'rb')
tagger = load(input)
input.close()
八西采、 基于轉換的標注
n-gram標注器的一個潛在的問題是它們的n-gram表(或語言模型)的大小夏志。如果使用各種語言技術的標注器部署在移動計算設備上,在模型大小和標注器準確性之間取得平衡是很重要的苛让。使用回退標注器的n-gram標注器可能存儲trigram和bigram表沟蔑,這是很大的稀疏陣列,可能有數(shù)億條條目狱杰。
第二個問題是關于上下文瘦材。n-gram標注器從前面的上下文中獲得的唯一的信息是標記,雖然詞本身可能是一個有用的信息源仿畸。n-gram模型使用上下文中的詞的其他特征為條件是不切實際的食棕。在本節(jié)中,我們考察Brill標注错沽,一種歸納標注方法簿晓,它的性能很好,使用的模型只有n-gram標注器的很小一部分千埃。
Brill標注是一種基于轉換的學習憔儿,以它的發(fā)明者命名。一般的想法很簡單:猜每個詞的標記放可,然后返回和修復錯誤谒臼。在這種方式中朝刊,Brill標注器陸續(xù)將一個不良標注的文本轉換成一個更好的。與n-gram標注一樣蜈缤,這是有監(jiān)督的學習方法拾氓,因為我們需要已標注的訓練數(shù)據(jù)來評估標注器的猜測是否是一個錯誤。然而底哥,不像n-gram標注咙鞍,它不計數(shù)觀察結果,只編制一個轉換修正規(guī)則列表趾徽。
Brill標注的的過程通常是與繪畫類比來解釋的续滋。假設我們要畫一棵樹,包括大樹枝附较、樹枝吃粒、小枝、葉子和一個統(tǒng)一的天藍色背景的所有細節(jié)拒课。不是先畫樹然后嘗試在空白處畫藍色徐勃,而是簡單的將整個畫布畫成藍色,然后通過在藍色背景上上色“修正”樹的部分早像。以同樣的方式僻肖,我們可能會畫一個統(tǒng)一的褐色的樹干再回過頭來用更精細的刷子畫進一步的細節(jié)。Brill標注使用了同樣的想法:以大筆畫開始卢鹦,然后修復細節(jié)臀脏,一點點的細致的改變。
讓我們看看下面的例子:
The President said he will ask Congress to increase grants to states for vocation al rehabilitation.
(總統(tǒng)表示他將要求國會增加撥款給各州用于職業(yè)康復冀自。)
我們將學習到兩個規(guī)則的運作:
- 當前面的詞是 TO 時揉稚,替換 NN 為 VB
- 當下一個標記是 NNS 時,替換 TO 為 IN
下圖說明了這一過程熬粗,首先使用 unigram 標注器標注搀玖, 然后運用規(guī)則修正錯誤:
在此表中,我們看到兩個規(guī)則驻呐。所有這些規(guī)則由以下形式的模板產(chǎn)生:“ 替換 T1 為 T2在上下文 C 中灌诅。” 典型的上下文是之前或之后的詞的內容或標記含末,或者當前詞的兩到三個詞 范圍內出現(xiàn)的一個特定標記猜拾。在其訓練階段,T1佣盒,T2 和 C 的標注器猜測值創(chuàng)造出數(shù)以千計 的候選規(guī)則挎袜。每一條規(guī)則都根據(jù)其凈收益打分:它修正的不正確標記的數(shù)目減去它錯誤修改的正確標記的數(shù)目。
Brill 標注器的另一個有趣的特性:規(guī)則是語言學可解釋的。與采用潛在的巨大的 n-gra m 表的 n-gram 標注器相比宋雏,我們并不能從直接觀察這樣的一個表中學到多少東西芜飘,而 Brill 標注器學到的規(guī)則可以务豺。
Brill標注器演示:標注器有一個“X→Y如果前面的詞是Z”的形式的模板集合磨总;這些模板中的變量是創(chuàng)建“規(guī)則”的特定詞和標記的實例;規(guī)則的得分是它糾正的錯誤例子的數(shù)目減去正確的情況下它誤報的數(shù)目笼沥;除了訓練標注器蚪燕,演示還顯示了剩余的錯誤:
nltk.tag.brill.demo()
Training Brill tagger on 80 sentences...
Finding initial useful rules...
Found 6555 useful rules.
B |
S F r O | Score = Fixed - Broken
c i o t | R Fixed = num tags changed incorrect -> correct
o x k h | u Broken = num tags changed correct -> incorrect
r e e e | l Other = num tags changed incorrect -> incorrect
e d n r | e
------------------+-------------------------------------------------------
12 13 1 4 | NN -> VB if the tag of the preceding word is 'TO'
8 9 1 23 | NN -> VBD if the tag of the following word is 'DT'
8 8 0 9 | NN -> VBD if the tag of the preceding word is 'NNS'
6 9 3 16 | NN -> NNP if the tag of words i-2...i-1 is '-NONE-'
5 8 3 6 | NN -> NNP if the tag of the following word is 'NNP'
5 6 1 0 | NN -> NNP if the text of words i-2...i-1 is 'like'
5 5 0 3 | NN -> VBN if the text of the following word is '*-1'
...
>>> print(open("errors.out").read())
left context | word/test->gold | right context
--------------------------+------------------------+--------------------------
| Then/NN->RB | ,/, in/IN the/DT guests/N
, in/IN the/DT guests/NNS | '/VBD->POS | honor/NN ,/, the/DT speed
'/POS honor/NN ,/, the/DT | speedway/JJ->NN | hauled/VBD out/RP four/CD
NN ,/, the/DT speedway/NN | hauled/NN->VBD | out/RP four/CD drivers/NN
DT speedway/NN hauled/VBD | out/NNP->RP | four/CD drivers/NNS ,/, c
dway/NN hauled/VBD out/RP | four/NNP->CD | drivers/NNS ,/, crews/NNS
hauled/VBD out/RP four/CD | drivers/NNP->NNS | ,/, crews/NNS and/CC even
P four/CD drivers/NNS ,/, | crews/NN->NNS | and/CC even/RB the/DT off
NNS and/CC even/RB the/DT | official/NNP->JJ | Indianapolis/NNP 500/CD a
| After/VBD->IN | the/DT race/NN ,/, Fortun
ter/IN the/DT race/NN ,/, | Fortune/IN->NNP | 500/CD executives/NNS dro
s/NNS drooled/VBD like/IN | schoolboys/NNP->NNS | over/IN the/DT cars/NNS a
olboys/NNS over/IN the/DT | cars/NN->NNS | and/CC drivers/NNS ./.
# 基于轉換的標注器
from nltk.tbl.template import Template
from nltk.tag.brill import Pos, Word
from nltk.tag import untag, RegexpTagger, BrillTaggerTrainer
size = int(len(brown_tagged_sents)*0.9)
train_sents = brown_tagged_sents[:size]
test_sents = brown_tagged_sents[size+1:]
# baseline
unigram_tagger = nltk.UnigramTagger(train_sents)
# 建立轉換的標注器
#clear any templates created in earlier tests
Template._cleartemplates()
templates = [Template(Pos([-1])), Template(Pos([-1]), Word([0]))]
#construct a BrillTaggerTrainer
tt = BrillTaggerTrainer(unigram_tagger, templates, trace=3)
tagger1 = tt.train(train_sents, max_rules=10)
# 查看規(guī)則
print(tagger1.rules()[1:3])
print(tagger1.evaluate(test_sents))
九、中文標注小demo
例子來自于 zzulp 處奔浅,基于Unigram訓練一個中文詞性標注器馆纳,語料使用網(wǎng)上可以下載得到的人民日報98年1月的標注資料:語料下載
# coding=utf-8
import nltk
lines = open('199801.txt', "r", encoding='utf-8').readlines()
all_tagged_sents = []
for line in lines:
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(tagged)
print(tagger.evaluate(x_test)) #0.871
十汹桦、如何確定一個詞的分類
1. 形態(tài)學線索
一個詞的內部結構可能為這個詞分類提供有用的線索鲁驶。舉例來說:-ness是一個后綴,與形容詞結合產(chǎn)生一個名詞舞骆,如happy → happiness, ill → illness钥弯。如果我們遇到的一個以-ness結尾的詞,很可能是一個名詞督禽。同樣的脆霎,-ment是與一些動詞結合產(chǎn)生一個名詞的后綴,如govern → government和establish → establishment狈惫。
英語動詞也可以是形態(tài)復雜的睛蛛。例如,一個動詞的現(xiàn)在分詞以-ing結尾胧谈,表示正在進行的還沒有結束的行動(如falling, eating)忆肾。-ing后綴也出現(xiàn)在從動詞派生的名詞中,如the falling of the leaves(這被稱為動名詞)菱肖。
2. 句法線索
另一個信息來源是一個詞可能出現(xiàn)的典型的上下文語境客冈。例如,假設我們已經(jīng)確定了名詞類蔑滓。那么我們可以說郊酒,英語形容詞的句法標準是它可以立即出現(xiàn)在一個名詞前,或緊跟在詞be或very后键袱。
3. 語義線索
一個詞的意思對其詞匯范疇是一個有用的線索燎窘。例如:名詞的眾所周知的一個定 義是根據(jù)語義的:“一個人、地方或事物的名稱蹄咖『纸。”在現(xiàn)代語言學,詞類的語義標準受到懷疑, 主要是因為它們很難規(guī)范化蚜迅。然而舵匾,語義標準鞏固了我們對許多詞類的直覺,使我們能夠在不熟悉的語言中很好的猜測詞的分類谁不。
例如:如果我們都知道荷蘭語詞 verjaardag 的意思與 英語詞 birthday 相同坐梯,那么我們可以猜測 verjaardag 在荷蘭語中是一個名詞。然而刹帕,一些修 補是必要的:雖然我們可能翻譯 zij is vandaag jarig as it’s her birthday today吵血,詞 jarig 在荷蘭語中實際上是形容詞,與英語并不完全相同偷溺。