1.一元標注器(Unigram Tagging)
一元標注器利用一種簡單的統計算法,對每個標注符分配最有可能的標記。例如:它將分配標記JJ給詞frequent,因為frequent用作形容詞更常見。一元標注器的行為與查找標注器相似,建立一元標注器的技術,稱為訓練。在下面的代碼例子中,“訓練”一個一元標注器,用它來標注一個句子,然后進行評估。
1 >>> from nltk.corpus import brown 2 >>> brown_tagged_sents=brown.tagged_sents(categories='news') //‘news’類別下,已經被標記的句子 3 >>> brown_sents=brown.sents(categories='news') //‘news’類別下,未被標記的句子 4 >>> import nltk 5 >>> unigram_tagger=nltk.UnigramTagger(brown_tagged_sents) //用已經被標記的句子訓練一元標注器 6 >>> unigram_tagger.tag(brown_sents[2007]) //用生成的一元標注器去標記新的句子 7 [('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'), ('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('ty 8 pe', 'NN'), (',', ','), ('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'), ('so', 'QL'), ('that', 'CS'), ('en 9 trance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')] 10 >>> unigram_tagger.evaluate(brown_tagged_sents) //評估標注器的性能 11 0.9349006503968017 12 >>>
上面代碼中,使用unigram_tagger.tag(brown_sents[2007]) 來標記的是brwon_sents的第2008個句子,因為brown_sents中以句子為單位,每個句子以詞list的形式存在,所以必須制定目標標記的句子,而不能一次性標記多個句子。
>>> brown_sents [['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', 'Friday', 'an', 'investigation', 'of', "Atlanta's", 'recent', 'primary', 'election', ' produced', '``', 'no', 'evidence', "''", 'that', 'any', 'irregularities', 'took', 'place', '.'], ['The', 'jury', 'further', 'said', 'in', 't erm-end', 'presentments', 'that', 'the', 'City', 'Executive', 'Committee', ',', 'which', 'had', 'over-all', 'charge', 'of', 'the', 'election ', ',', '``', 'deserves', 'the', 'praise', 'and', 'thanks', 'of', 'the', 'City', 'of', 'Atlanta', "''", 'for', 'the', 'manner', 'in', 'which ', 'the', 'election', 'was', 'conducted', '.'], ...] >>>
通過在初始化標注器時指定已標注的句子數據作為參數來訓練一元標注器。訓練過程中涉及檢查每個詞的標記,將所有詞的最可能標記存儲在一個字典里面,這個字典存儲在標注器內部。
2.分離訓練和測試數據
在一些數據上訓練標注器,必須注意不要在相同的數據上測試。如果一個標注器只是單純地去記憶它的訓練數據,而不試圖建立一般的模型,測試結果會更好,但在標注新的文本時不起作用。相反,我們應該分割數據,90%為訓練數據,其余10%為測試數據。
1 >>> size=int(len(brown_tagged_sents)*0.9) 2 >>> size 3 4160 4 >>> train_sents=brown_tagged_sents[:size] 5 >>> test_sents=brown_tagged_sents[size:] 6 >>> unigram_tagger=nltk.UnigramTagger(train_sents) 7 >>> unigram_tagger.evaluate(test_sents) 8 0.8124190172430977 9 >>>
顯然得分更糟糕了,但是對這種標注器是無用的情況有了更好的了解。
3.一般的N-gram的標注
當基於unigrams處理語言 處理任務時,可使用上下文中的項目。標注時,只考慮當前的標識符,而不考慮其他上下文。給定一個模型,最好是為每個詞標注其先驗的最可能的標記。這意味着將使用相同的標記標注詞。n-gram標注器是ungram標注器的一般化,它的上下文是當前詞和它前面n-1個標識符的詞性標記。
1-gram標注器(unigram tagger)是一元標注器的另一個名稱:即用於標注上下文是標識符本身的標識符。2-gram標注器也稱為二元標注器(bigram taggers), 3-gram標注器也稱為三元標注器(trigram taggers).
NgramTagger 類使用一個已標注的訓練語料庫來確定每個上下文中哪個詞性標記最有可能。下面的例子中,我們看到n-gram標注器的一個特殊情況,即bigram標注器,首先訓練它,然后用它來標注未標注的句子。
1 >>> bigram_tagger=nltk.BigramTagger(train_sents) 2 >>> bigram_tagger.tag(brown_sents[2007]) 3 [('Various', 'JJ'), ('of', 'IN'), ('the', 'AT'), ('apartments', 'NNS'), ('are', 'BER'), ('of', 'IN'), ('the', 'AT'), ('terrace', 'NN'), ('ty 4 pe', 'NN'), (',', ','), ('being', 'BEG'), ('on', 'IN'), ('the', 'AT'), ('ground', 'NN'), ('floor', 'NN'), ('so', 'CS'), ('that', 'CS'), ('en 5 trance', 'NN'), ('is', 'BEZ'), ('direct', 'JJ'), ('.', '.')] 6 >>> unseen_sent=brown_sents[4203] 7 >>> bigram_tagger.tag(unseen_sent) 8 [('The', 'AT'), ('population', 'NN'), ('of', 'IN'), ('the', 'AT'), ('Congo', 'NP'), ('is', 'BEZ'), ('13.5', None), ('million', None), (',', 9 None), ('divided', None), ('into', None), ('at', None), ('least', None), ('seven', None), ('major', None), ('``', None), ('culture', None), 10 ('clusters', None), ("''", None), ('and', None), ('innumerable', None), ('tribes', None), ('speaking', None), ('400', None), ('separate', No 11 ne), ('dialects', None), ('.', None)] 12 >>> bigram_tagger.evaluate(test_sents) 13 0.10276088906608193 14 >>>
注意,bigram標注器能夠標注訓練中它看到過的句子中的所有詞,但對一個沒見過的句子卻不行。只要遇到一個新詞就無法給它分配標記。它不能標注下面的詞,即使在訓練過程中看到過的,因為在訓練過程中從來沒有見過他前面有None標記的詞。因此,標注器也無法標注句子的其余部分。它的整體准確度得分非常低,從上面運行結果來看只有0.1左右。
當n 越大時,上下文的特異性就會增加,要標注的數據中包含訓練數據中不存在的上下文的幾率也增大。這被稱為數據稀疏問題,在NLP中是相當普遍的。因此,研究結果的精度和覆蓋范圍之間需要有一個權衡。
N-gram標注器不應該考慮跨越句子邊界的上下文,因此,nltk的標注器被涉及用於句子鏈表,一個句子是一個詞鏈表。在一個句子的開始,tn-1和前面的標記被設置為None。
4.組合標注器
解決精度和覆蓋范圍之間權衡的一個辦法是盡可能地使用更精確的算法,但卻在很多時候卻遜於覆蓋范圍更廣的算法。例如:可以按如下方式組合bigram標注器,unigram標注器和一個默認標注器。
- 嘗試使用bigram標注器標注標識符
- 如果bigram標注器無法找到標記,嘗試unigram標注器。
- 如果unigram標注器也無法找到標記,使用默認標注器。
大多數nltk標注器允許指定回退標注器。回退標注器自身可能也有回退標注器。
1 >>> t0=nltk.DefaultTagger('NN') 2 >>> t1=nltk.UnigramTagger(train_sents, backoff=t0) 3 >>> t2=nltk.BigramTagger(train_sents, backoff=t1) 4 >>> t2.evaluate(test_sents) 5 0.8466061995415131 6 >>>
注意:在標注器初始化時要指定回退標注器,從而訓練時才能利用回退標注器。於是,如果在上下文中bigram標注器將分配與它的unigram回退標注器一樣的標記,那么bigram標注器丟棄訓練實例。這樣可以保持盡可能小的bigram標注器模型。可以進一步確定的是標注器需要保存上下文多個實例。例如:nltk.BigramTagger(sents, cutoff=2, backoff=t1)將丟棄那些只出現一次或兩次的上下文。
5.標注生詞
標注生詞的方法是回退到正則表達式標注器或默認標注器。這些都無法利用上下文。因此,如果標注器遇到詞blog,但訓練過程中沒有看到過,它會分配相同的標記,不論這個詞出現的上下文是the blog 還是to blog。
基於上下文標注生詞的方法是限制標注器的詞匯表為最頻繁的n個詞。訓練時,unigram標注器可能會將UNK標注名詞。然而,n-gram標注器會檢測其他標記的上下文。例如:如果前面的詞是to(標注未TO),那么UNK可能會被標注為一個動詞。
6.存儲標注器
原因:在大語料庫中訓練標注器可能需要花費大量時間,而且沒有必要重復訓練標注器。
解決方案:將一個訓練好的標注器保存到文件中供以后重復使用。
實例:將標注器t2保存到文件t2.pkl。
7.性能限制
調查標注器性能的方法:
- 根據經驗
- 研究它的錯誤
訓練數據中的歧義可產生標注器性能的上限。有時更多的上下文能解決這些歧義。然而,在其他情況下,只有參考語法或現實世界的知識才能解決歧義。盡管存在缺陷,但詞性標注在利用統計方法進行自然語言處理的發展過程中起到了核心作用。