大家早安,不知知覺竟然已經完成鐵人賽的三分之一,真的是太快了。快到我的存稿都沒了從今天開始大概率每天都是最新鮮的文章了XDD 希望我可以順利完成。
回到正題,前兩天講解了為什麼需要斷詞跟怎麼實作斷詞。但是斷詞結束就是全部了嗎?因為一般來說斷詞次後端的運作不是靠原本就建好的字典就是靠字跟字組合在一起的機率去進行(例:「他的背包」中,「他」跟「的」比「的」跟「背」結合的機率還高,而且「背」跟「包」結合機率也比「的」跟「背」結合的機率高。因此會斷成「他 / 的 / 背包」或「他的 / 背包」),所以對於電腦來說這些小單位的文字仍舊只是長相不同的字串而已,沒有什麼意義。如果我們想讓他初步對這些字串有些了解,就必須做詞性標註(Part of Speech Tagging, POS Tagging)。大家應該對詞性都不陌生,畢竟國高中國文選擇題一天到晚在考,但是為什麼我們會需要詞性標註呢?第一個原因是他可以做為一個讓機器學習的標籤,帶有資訊。第二個原因是他能夠幫我們明確字詞的讀音,請看下面的例子:
record (n.)
record (v.)
語言
夏蟲語冰
record這個詞當作名詞使用的時候要唸「咧口 /ˈrek.ɚd/」,當動詞的時候要唸「裡扣 /rɪˈkɔːrd/」;「語」這個字在當名詞的時候唸「ㄩˇ」,當動詞的時候則是要唸「ㄩˋ」。這些東西應該都是我們可以清楚分辨的。但不管是他們當中的哪一個,對機器來說都長得一模一樣,並沒有辦法透過外表來辨別,即便是我們也一樣,如果他們在沒有上下文的時候單獨出現,我們也無法判斷讀音。這種時候就需要詞性標註的輔助來幫電腦透過詞性規則來區辨這些詞的讀音。
第三個我們需要詞性標註的原因是他可以幫忙更準確地拆解句子中的結構。詞性在句法學上是由不同詞彙在一個句子中所處的位置和跟周邊詞彙關係之差異整理出來的分類。舉例來說,當record出現在一個句子裡面的時候,因為它前面是the,而the後面指有可能是名詞或形容詞,所以我們可以知道他在這邊一定是做為動詞使用。讓我們在看一下下面的例子:
你有去日本過過年嗎?
我最愛的小被被被拿去洗了
如果這樣的句子出現的時候,單純根據字詞之間共同出現頻率的高低來判斷怎麼斷詞就會比較困難,勢必需要透過前後的詞性來幫忙電腦做決定。最後一個需要斷詞的原因是,它可以幫助語意理解:
拿這條毒蛇的毒毒毒蛇,毒蛇會不會被毒毒死?
因為我們知道「的」後面會加名詞,所以可以確定這個地方的第一個「毒」是對人有害的東西。再接下來的「毒」是動詞的可能性較高,所以它的意思應該是拿毒去攻擊其他人或動物。就這樣依序推斷下來就能加深電腦對句子的語意理解。
說了這麼多,詞性標註到底是怎麼做到的呢?在深度學習的模型開始出現之前,大部分的詞性標註使用的都是統計領域中的條件機率原理來計算。這裡面又以HMM(Hidden Markov Model,隱藏馬可夫模型)最廣為使用,所以我會介紹HMM是怎麼應用在詞性標註上面的。But!因為前面已經囉嗦太多東西了,今天會先接續前兩天的內容,用那些介紹過的套件進一步示範怎麼用他們做詞性標註給大家看,HMM 的介紹就留到明天再做吧~
NLTK
import nltk
nltk.download("all")
sentence = "This is a book about how an airplane is made."
token = nltk.word_tokenize(sentence)
tag = nltk.pos_tag(token, tagset = "universal")
print(tag)
使用colab的缺點就是時間一到,google就會把暫存在上面的資料直接刪掉,所以我們每次使用NLTK
的時候都要重新下載裡面的套件。下載完之後,NLTK
最簡單的詞性標註方法就是在斷好詞的基礎上直接使用pos_tag()
這個功能。這邊需要特別注意的地方是,因為詞性標註跟左鄰右舍有很大的關係,我們不能把停止詞拿掉,不然會讓結果變得亂七八糟。然後為了讓標註不要太過複雜,這裡加上了tagset
這個參數讓他使用最國際通用標註方法進行(對沒錯,根據製作標註工具的團體不同,標註名稱跟方式會出現各種差異)。接下來讓我們看看結果。
# 輸出
[('This', 'DET'), ('is', 'VERB'), ('a', 'DET'), ('book', 'NOUN'), ('about', 'ADP'), ('how', 'ADV'), ('an', 'DET'), ('airplane', 'NOUN'), ('is', 'VERB'), ('made', 'VERB'), ('.', '.')]
大致上看起來沒有什麼問題,但畢竟句子比較簡單,遇到複雜一點的文章時,基本上是不可能做到100%正確啦XDD(可能就會需要用到NLTK
裡面其他訓練詞性標註工具的功能來讓他變好。)
Jieba
import jieba
import jieba.posseg as pseg
ch_sentence = "這是示範用的句子。"
tag = pseg.cut(ch_sentence)
用jieba
做詞性標註之後,回傳的是generator(生成器)的資料型態,這種資料型態不能直接印出來,需要我們做一些特殊措施。因為生成器的概念比較複雜一點,這邊沒有辦法細講。下面的程式碼大家就想像是依序從tag
裡面把分類為word底下的東西跟分類為tag底下的東西依序拿出來就好。
for word, flag in tag:
print('%s | %s' % (word, flag))
# 輸出
這 | r
是 | v
一顆 | m
蘋果 | n
。 | x
Jieba
裡面使用的是製作者自己定義的標籤,所以如果要對照標註結果對應到的是什麼詞性,可以到這裡確認。
CkipTagger
!pip install ckiptagger
from ckiptagger import data_utils
data_utils.download_data_gdown("./")
from ckiptagger import WS, POS
ws = WS("./data")
pos = POS("./data")
跟昨天一樣前置作業不能少,只是這次要多引進POS
這個套件來幫我們做斷詞詞性標註。
ckip_token = ws([ch_sentence])
ckip_tag = pos(ckip_token)
for i in range(len(ckip_tag[0])):
print(ckip_token[0][i] + "|" + ckip_tag[0][i])
因為兩個工具會分別回傳斷詞跟單純詞性標註的結果,所以我們要把它們手動合在一起。
# 輸出
這|Nep
是|SHI
一|Neu
顆|Nf
蘋果|Na
。|PERIODCATEGORY
其實就是一個很簡單的步驟而已,然後中研院的標籤可以對照這邊。
今天的教學就走一個比較簡單的路線,因為明天的HMM會很複雜,所以決定先讓腦袋放一天假了XDD 大家明天見~