NLTK 初學指南(三):基於 WordNet 的語義關係表示法 — 上下位詞結構篇

Youngmi huang
PyLadies Taiwan
Published in
16 min readSep 4, 2018

--

本篇是『NLTK 初學指南 』的第三集,透過 WordNet 這個獨特的語義網絡,去找到字詞在整篇文本當中的上下位關係、同義詞、文法的處理(動詞時態、名詞單複數)等。

第一篇文:NLTK 初學指南(一):簡單易上手的自然語言工具箱−探索篇

第二篇文:NLTK 初學指南(二):由外而內,從語料庫到字詞拆解 — 上手篇

NLTK 全名是 Natural Language Tool Kit, 是一套基於 Python 的自然語言處理工具箱。在官方文件的說明十分友善,主要是以下這個網頁版電子書: Natural Language Processing with Python ,章節如下圖:

Outline of Natural Language Processing with Python

本篇為第二章節 Accessing Text Corpora and Lexical Resource 的後半段以及第三章節 Processing Raw Text ;如果喜歡實體工具書,也可以參考 O’Reilly 的版本。

WordNet — A Lexical Database for English

簡介

WordNet 是一個以英文為主的語義網絡,在普林斯頓大學認知科學實驗室的心理學教授 George A. Miller 的指導下建立和維護的是一個由「字詞 — 語義」建成的語義網絡,去儲存了字詞間的結構關係,透過上下位詞的關係建立數值化的特徵表示,像是字詞階層數、結構相似度等。以下是 「motorcar」 的例子,具體使用會在後面進行介紹。

WordNet 示意圖:以 motocar 出發的語義階層網絡

官方文件

在 WordNet 中,名詞,動詞,形容詞和副詞各自被組織成一個同義詞的集合 ( synsets ),每個同義詞集合都代表一個基本的語義概念,並且這些集合之間也由各種關係連接,形成網絡。

synset:同義詞集合

WordNet 將每一個字詞進行分組,而分組後得到的每一個具有相同意義的字詞組,就稱為 synset (同義詞集合)。同時,每一個 synset 都有一個簡短、概要的定義,記錄著不同 synset 的語義關係。

1. 找出同義詞集合

wn.synsets()

# 找synset,以 motorcar 為例
from nltk.corpus import wordnet as wn
wn.synsets('motorcar')
# Result
Synset('car.n.01')
# 也可以替換改找卡車的synset
wn.synsets('trunk')
# result
Synset('trunk.n.01'),
Synset('trunk.n.02'),
Synset('torso.n.01'),
Synset('luggage_compartment.n.01'),
Synset('proboscis.n.02')

在上面這個例子當中,car.n.01motorcar 這個字詞的 synset, n 代表了 car.n.01 是一個名詞類的 synset。

另外,「trunk」是多義詞的例子,一個多義詞會出現在它所代表每個意思的同義詞集合中。也就是說一個字詞與 synset 之間不一定是一對一的關係,如果這個字詞代表多種意義,就會有一個字詞,對應到多個 synset 的情況。

2. 找出同義詞

wn.synset('synsets id').lemma_names()

# synset 裡的字詞
wn.synset('car.n.01').lemma_names()
# Result
['car', 'auto', 'automobile', 'machine', 'motorcar']

在這個 synset 底下,有囊括了 carautoautomobilemachinemotorcar ,這些字詞可以視為是 motorcar 的同義詞。

如果今天要查找的字屬於多義詞,一個多義詞會有好幾個 synset,那麼該如何列舉所有的同義詞呢?

# 列舉多義字的同義字
for synset in wn.synsets('trunk'):
print(synset.lemma_names())
# Result:
['trunk', 'tree_trunk', 'bole']
['trunk']
['torso', 'trunk', 'body']
['luggage_compartment', 'automobile_trunk', 'trunk']
['proboscis', 'trunk']

3. 查詢 synset 的定義說明

wn.synset('synsets id').definiton()

# 查找 motorcar 所屬的 synset 定義
wn.synset('car.n.01').definition()
# Result
'a motor vehicle with four wheels; usually propelled by an internal combustion engine'
# 查找 trunk 所屬的 synset 定義
wn.synset('trunk.n.01').definition()
# Result
'the main stem of a tree; usually covered with bark; the bole is usually the part that is commercially useful for lumber'

以上的定義說明是 WordNet 拿來歸屬 synset 的依據,有點像是我們在做 label 問題時,所需要將邏輯紀錄的文件化說明。

4. hypernym/hyponym ( 上位詞與下位詞 )

wn.synset('synset id1').hypernyms()

wn.synset('synsets id').hyponyms()

對字詞做分類,是 WordNet 的一大特色與亮點,我們先看一個生物學上的例子,就可以更好理解上下位詞的意義。

界門綱目科屬種:是一個用生物分類學方法來對生物的物種分組和歸類的辦法。比如說:「人」使用這個分類方法由細類到大類排列如下:

Homo sapiens (智人) < Homo (人屬) < Hominids (人科) < Primates (靈長目) < Mammals (哺乳綱) < Chordates (脊索動物門) < Animals (動物界)

界門綱目科屬種:人類所屬分類的示意圖

如此我們就可以明顯看到「智人 — 人屬」的上下位關係。回到 WordNet 本身的特色:WordNet 是由「字詞 — 語義」建構而成的網絡,也可以理解成對字詞做分類的方法。以人的例子來看,「人屬」為「智人」的上位詞 ( hypernym ),而「智人」即為「人屬」的下位詞 ( hyponym )。

接下來,召喚前面出現過的 motorcar 在 WordNet 裡的架構圖,:

示意圖來自官方document,其實 artefact 應該是 artifact 才對。

可以看到 motor vehiclemotorcar 的上位詞 ,而hatch-backcompactgas guzzlermotorcar 的下位詞 ;上位詞所涵蓋的字詞數廣,分類是較粗的,下位詞是較細的分類。

# 尋找 motorcar 的上位詞組
motorcar = wn.synset('car.n.01')
types_of_motorcar = motorcar.hypernyms()
# 尋找 motorcar 的下位詞組
motorcar = wn.synset('car.n.01')
types_of_motorcar = motorcar.hyponyms()
# 找到下位詞組後後,再從 synset 找出單詞(以詞為中心)
sorted(lemma.name() for synset in types_of_motorcar for lemma in synset.lemmas())
# Result (僅列5項)
'electric',
'electric_automobile',
'electric_car',
'estate_car',
'gas_guzzler',
...

上下位詞不只可以找到上一位或是下一位,還可以找到 motorcar 這個詞的完整路徑(也就是上圖 motor vehicle 至 artefact 虛線所包含的 synsets + artifact 之上的上位詞 synsets ):

# 完整路徑(上位詞組再往上走)
motorcar = wn.synset('car.n.01')
motorcar.hypernym_paths()
# Result:兩組 path 的不同之處(以粗體表示)
[[Synset('entity.n.01'),
Synset('physical_entity.n.01'),
Synset('object.n.01'),
Synset('whole.n.02'),
Synset('artifact.n.01'),
Synset('instrumentality.n.03'),
Synset('container.n.01'),
Synset('wheeled_vehicle.n.01'),
Synset('self-propelled_vehicle.n.01'),
Synset('motor_vehicle.n.01'),
Synset('car.n.01')],
[Synset('entity.n.01'),
Synset('physical_entity.n.01'),
Synset('object.n.01'),
Synset('whole.n.02'),
Synset('artifact.n.01'),
Synset('instrumentality.n.03'),
Synset('conveyance.n.03'),
Synset('vehicle.n.01'),

Synset('wheeled_vehicle.n.01'),
Synset('self-propelled_vehicle.n.01'),
Synset('motor_vehicle.n.01'),
Synset('car.n.01')]]

那麼如何從 motorcar 這個階層直指最頂端的上位詞組 entity.n.01 呢?找到這個位置詞組的好處即可得知這個詞的最大分類。

# 直指頂端上位詞組
motorcar = wn.synset('car.n.01')
motorcar.root_hypernyms()
# Result
[Synset('entity.n.01')]

5. 找尋兩個 synset 之間的最低位共同詞組

wn.synset('synset id1').lowest_common_hypernyms('synset id2')

透過找尋 synset id1、以及 synset id2 的上位詞組,去找到一組最低位的共同上位詞組(有點像是尋找最小公倍數的概念),其應用是:共同分類的字詞表示。

官方文件是以三種鯨魚為例子,「露脊鯨」以及「小鬚鯨」最低位的共同上位詞組是 baleen_whale ,是「鬚鯨目」的意思,代表這兩種鯨魚隸屬於該分類底下。

# 以鯨魚為例
right = wn.synset("right_whale.n.01")
minke = wn.synset("minke_whale.n.01")
# 「露脊鯨」與「小鬚鯨」在上位詞組中最低位的詞組
right.lowest_common_hypernyms(minke)
# Result
[Synset('baleen_whale.n.01')]
Source: Atlantic (Northern) Right Whale
Source: Animal Legal Defense Fund Responds to Death of Kasatka the Orca
Source: Minke Whale

在自然語言處理的任務當中,在意的是個字詞、詞組之間的相同/相異程度,差異越小的會在越下位找,差異越大的會在越上位,才找到共同詞組,可以看以下例子:

# 露脊鯨 vs 虎鯨
orca = wn.synset("orca.n.01")
right.lowest_common_hypernyms(orca))
# Result
[Synset('whale.n.02')] //鯨目
# 露脊鯨 vs 陸龜
tortoise = wn.synset("tortoise.n.01")
right.lowest_common_hypernyms(tortoise)
# Result
[Synset('vertebrate.n.01')] //脊椎動物
# 露脊鯨 vs 小說
novel = wn.synset("novel.n.01")
right.lowest_common_hypernyms(novel)
# Result
[Synset('entity.n.01')] //實體

6. synset 之間的階層計算以及上下位結構的相似度

將字詞的上下位關係,轉為量化的數值表示:包含階層計算以及相似度。

wn.synset('synset id1').min_depth()

# 計算由當前 synset 而上的階層數
wn.synset('baleen_whale.n.01').min_depth()
wn.synset('whale.n.02').min_depth()
wn.synset('vertebrate.n.01').min_depth()
wn.synset('entity.n.01').min_depth()
# Result
14
13
8
0

wn.synset('synset id1').path_similarity()

# 上下位詞組結構的相似程度 (數字接近1代表path越像)
right.path_similarity(right) //露脊鯨和自己本身
right.path_similarity(minke) //露脊鯨和小鬚鯨
right.path_similarity(orca) //露脊鯨和虎鯨
right.path_similarity(tortoise) //露脊鯨和陸龜
right.path_similarity(novel) //露脊鯨和小說
# Result
1.0
0.25
0.16666666666666666
0.07692307692307693
0.043478260869565216

最後,補充兩個字詞處理的小技巧

在英文的世界裡,很常會遇到動詞的時態變化或是名詞的單複數變化等,「詞幹提取」、「詞性還原」即可進行字詞的預處理。(備註:使用上要視任務需求而定,若是詞性標註、或是專有名詞識別 ( NER ) 等可能較不適合進行這些前處理)

詞幹提取 ( Stemming )

通過一些簡單的規則,對字詞做初步處理,e.g. eat 這個單詞會有 eatingeateneats 等變化,在某些應用當中,我們是不需要特別區分 eateaten 之間的區別的,所以通常會用詞幹提取的方式,將這種語法上的變化歸類為相同的詞。

擁有基本規則的詞幹提取器是可以移除 -s/es-ing-ed的,像是 Porter StemmerLancaster Stemmer ;而較晚出現的 Snowball Stemmer,它是基於 Porter 的方式做改良,同時可以提取一些其他語言,像是法文、德文等。若想對詞幹提取這一塊進行更深入研究,可以參考 wiki

# 引用詞幹提取器
from nltk.stem import PorterStemmer
from nltk.stem import LancasterStemmer
from nltk.stem import SnowballStemmer
# 初始化
pst = PorterStemmer()
lst = LancasterStemmer()
snow = SnowballStemmer('english') //需定義語言
# 使用 (以Porter Stemmer為例)
pst.stem('eating')
pst.stem('eats')
# Result
eat
eat

詞性還原 ( Lemmatization )

有別於詞幹提取,詞性還原涵蓋了詞根的文法和變化形式,它運用了不同的標準化規則(像是上下文情境和詞性),來獲取相關的詞根 ( lemma ),將各種類型的詞的變形,歸成一致的形式。

# 詞性還原 
from nltk.stem import WordNetLemmatizer
wtlem = WordNetLemmatizer()
# 未指定詞性
wtlem.lemmatize('ate')
wtlem.lemmatize('eating')
# Result
ate
eating
# 指定詞性為動詞
wtlem.lemmatize('ate', 'v')
wtlem.lemmatize('eating', 'v')
# Result
eat
eat

小結

這次主要介紹的 WordNet ,練習後可以發現:其精髓是將字詞分類後以 「synset 」為中介,透過「synset」去做各種連結,像是尋找上下位詞組、計算字詞階層的相似度等。

這次在 NLTK 初學指南系列,介紹了從字詞出發的基本技巧、分詞與文章拆解、WordNet 語義網絡等主題。其實複雜的 NLP 任務的最底層,會與字詞表達方式文章拆解有關(e.g. 分詞、同異義詞處理、以及字詞在整篇文章的位置),因此,相信這些基本技巧上手之後,就能應用在 NLP 的相關任務上,若有興趣做得更細緻,就得在字詞表達與文章拆解上好好研究以及著墨了。

本篇程式碼已整理在 github,可參考 03_nltk_practice.ipynb 進行實作

資料來源

  1. Natural Language Processing with Python — Analyzing Text with the Natural Language Toolkit
  2. Accessing Text Corpora and Lexical Resources
  3. Processing Raw Text
  4. NLTK 基礎教程:用 NLTK 和 Python 庫構建機器學習應用
  5. 用Python也能輕鬆玩自然語言處理
  6. NLTK WordNet Lemmatizer: Shouldn’t it lemmatize all inflections of a word?
  7. 關於WordNet,我的一些用法和思路(二)

如果這篇文章有幫助到你,可以幫我在下方綠色的拍手圖示按5下,只要登入Google或FB,不需任何花費就能【免費支持】youmgmi 繼續創作。

--

--

Youngmi huang
PyLadies Taiwan

Participate in data science field, fascinated by something new. (Current: fraud risk modeling with ML/DL, Past: NLP)