進階教程:用 Python 和 NLTK 進行 NLP 分析
在 之前的文章 里,我介紹了 自然語言處理 (NLP)和賓夕法尼亞大學研發的 自然語言處理工具包 (NLTK)。我演示了用 Python 解析文本和定義 停頓詞 的方法,並介紹了 語料庫 的概念。語料庫是由文本構成的數據集,通過提供現成的文本數據來輔助文本處理。在這篇文章里,我將繼續用各種語料庫對文本進行對比和分析。
這篇文章主要包括以下部分:
- 詞網 和 同義詞集
- 相似度比較
- 樹 和 樹庫
- 命名實體識別
詞網和同義詞集
詞網 是 NLTK 里的一個大型辭彙資料庫語料庫。詞網包含各單詞的諸多 認知同義詞 (認知同義詞常被稱作「 同義詞集 」)。在詞網裡,名詞、動詞、形容詞和副詞,各自被組織成一個同義詞的網路。
詞網是一個很有用的文本分析工具。它有面向多種語言的版本(漢語、英語、日語、俄語和西班牙語等),也使用多種許可證(從開源許可證到商業許可證都有)。初代版本的詞網由普林斯頓大學研發,面向英語,使用 類 MIT 許可證 。
因為一個詞可能有多個意義或多個詞性,所以可能與多個同義詞集相關聯。每個同義詞集通常提供下列屬性:
屬性 | 定義 | 例子 |
---|---|---|
名稱 | 此同義詞集的名稱 | 單詞 code 有 5 個同義詞集,名稱分別是 code.n.01 、 code.n.02 、 code.n.03 、code.v.01 和 code.v.02 |
詞性 | 此同義詞集的詞性 | 單詞 code 有 3 個名詞詞性的同義詞集和 2 個動詞詞性的同義詞集 |
定義 | 該詞作對應詞性時的定義 | 動詞 code 的一個定義是:(計算機科學)數據或計算機程序指令的 象徵性排列 |
例子 | 使用該詞的例子 | code 一詞的例子:We should encode the message for security reasons |
詞元 | 與該詞相關聯的其他同義詞集(包括那些不一定嚴格地是該詞的同義詞,但可以大體看作同義詞的);詞元直接與其他詞元相關聯,而不是直接與 單詞 相關聯 | code.v.02 的詞元是 code.v.02.encipher 、code.v.02.cipher 、code.v.02.cypher 、code.v.02.encrypt 、code.v.02.inscribe 和 code.v.02.write_in_code |
反義詞 | 意思相反的詞 | 詞元 encode.v.01.encode 的反義詞是 decode.v.01.decode |
上義詞 | 該詞所屬的一個範疇更大的詞 | code.v.01 的一個上義詞是 tag.v.01 |
分項詞 | 屬於該片語成部分的詞 | computer 的一個分項詞是 chip |
總項詞 | 該詞作為組成部分所屬的詞 | window 的一個總項詞是 computer screen |
同義詞集還有一些其他屬性,在 <你的 Python 安裝路徑>/Lib/site-packages
下的 nltk/corpus/reader/wordnet.py
,你可以找到它們。
下面的代碼或許可以幫助理解。
這個函數:
from nltk.corpus import wordnet
def synset_info(synset):
print("Name", synset.name())
print("POS:", synset.pos())
print("Definition:", synset.definition())
print("Examples:", synset.examples())
print("Lemmas:", synset.lemmas())
print("Antonyms:", [lemma.antonyms() for lemma in synset.lemmas() if len(lemma.antonyms()) > 0])
print("Hypernyms:", synset.hypernyms())
print("Instance Hypernyms:", synset.instance_hypernyms())
print("Part Holonyms:", synset.part_holonyms())
print("Part Meronyms:", synset.part_meronyms())
print()
synsets = wordnet.synsets('code')
print(len(synsets), "synsets:")
for synset in synsets:
synset_info(synset)
將會顯示:
5 synsets:
Name code.n.01
POS: n
Definition: a set of rules or principles or laws (especially written ones)
Examples: []
Lemmas: [Lemma('code.n.01.code'), Lemma('code.n.01.codification')]
Antonyms: []
Hypernyms: [Synset('written_communication.n.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []
...
Name code.n.03
POS: n
Definition: (computer science) the symbolic arrangement of data or instructions in a computer program or the set of such instructions
Examples: []
Lemmas: [Lemma('code.n.03.code'), Lemma('code.n.03.computer_code')]
Antonyms: []
Hypernyms: [Synset('coding_system.n.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []
...
Name code.v.02
POS: v
Definition: convert ordinary language into code
Examples: ['We should encode the message for security reasons']
Lemmas: [Lemma('code.v.02.code'), Lemma('code.v.02.encipher'), Lemma('code.v.02.cipher'), Lemma('code.v.02.cypher'), Lemma('code.v.02.encrypt'), Lemma('code.v.02.inscribe'), Lemma('code.v.02.write_in_code')]
Antonyms: []
Hypernyms: [Synset('encode.v.01')]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []
同義詞集 和 詞元 在詞網裡是按照樹狀結構組織起來的,下面的代碼會給出直觀的展現:
def hypernyms(synset):
return synset.hypernyms()
synsets = wordnet.synsets('soccer')
for synset in synsets:
print(synset.name() + " tree:")
pprint(synset.tree(rel=hypernyms))
print()
code.n.01 tree:
[Synset('code.n.01'),
[Synset('written_communication.n.01'),
...
code.n.02 tree:
[Synset('code.n.02'),
[Synset('coding_system.n.01'),
...
code.n.03 tree:
[Synset('code.n.03'),
...
code.v.01 tree:
[Synset('code.v.01'),
[Synset('tag.v.01'),
...
code.v.02 tree:
[Synset('code.v.02'),
[Synset('encode.v.01'),
...
詞網並沒有涵蓋所有的單詞和其信息(現今英語有約 17,0000 個單詞,最新版的 詞網 涵蓋了約 15,5000 個),但它開了個好頭。掌握了「詞網」的各個概念後,如果你覺得它辭彙少,不能滿足你的需要,可以轉而使用其他工具。或者,你也可以打造自己的「詞網」!
自主嘗試
使用 Python 庫,下載維基百科的 「open source」 頁面,並列出該頁面所有單詞的 同義詞集 和 詞元 。
相似度比較
相似度比較的目的是識別出兩篇文本的相似度,在搜索引擎、聊天機器人等方面有很多應用。
比如,相似度比較可以識別 football
和 soccer
是否有相似性。
syn1 = wordnet.synsets('football')
syn2 = wordnet.synsets('soccer')
# 一個單詞可能有多個 同義詞集,需要把 word1 的每個同義詞集和 word2 的每個同義詞集分別比較
for s1 in syn1:
for s2 in syn2:
print("Path similarity of: ")
print(s1, '(', s1.pos(), ')', '[', s1.definition(), ']')
print(s2, '(', s2.pos(), ')', '[', s2.definition(), ']')
print(" is", s1.path_similarity(s2))
print()
Path similarity of:
Synset('football.n.01') ( n ) [ any of various games played with a ball (round or oval) in which two teams try to kick or carry or propel the ball into each other's goal ]
Synset('soccer.n.01') ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents' goal ]
is 0.5
Path similarity of:
Synset('football.n.02') ( n ) [ the inflated oblong ball used in playing American football ]
Synset('soccer.n.01') ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents' goal ]
is 0.05
兩個詞各個同義詞集之間 路徑相似度 最大的是 0.5,表明它們關聯性很大( 路徑相似度 指兩個詞的意義在 上下義關係的辭彙分類結構 中的最短距離)。
那麼 code
和 bug
呢?這兩個計算機領域的詞的相似度是:
Path similarity of:
Synset('code.n.01') ( n ) [ a set of rules or principles or laws (especially written ones) ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
is 0.1111111111111111
...
Path similarity of:
Synset('code.n.02') ( n ) [ a coding system used for transmitting messages requiring brevity or secrecy ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
is 0.09090909090909091
...
Path similarity of:
Synset('code.n.03') ( n ) [ (computer science) the symbolic arrangement of data or instructions in a computer program or the set of such instructions ]
Synset('bug.n.02') ( n ) [ a fault or defect in a computer program, system, or machine ]
is 0.09090909090909091
這些是這兩個詞各同義詞集之間 路徑相似度 的最大值,這些值表明兩個詞是有關聯性的。
NLTK 提供多種 相似度計分器 ,比如:
- path_similarity
- lch_similarity
- wup_similarity
- res_similarity
- jcn_similarity
- lin_similarity
要進一步了解這些 相似度計分器 ,請查看 WordNet Interface 的 Similarity 部分。
自主嘗試
使用 Python 庫,從維基百科的 Category: Lists of computer terms 生成一個術語列表,然後計算各術語之間的相似度。
樹和樹庫
使用 NLTK,你可以把文本表示成樹狀結構以便進行分析。
這裡有一個例子:
這是一份簡短的文本,對其做預處理和詞性標註:
import nltk
text = "I love open source"
# Tokenize to words
words = nltk.tokenize.word_tokenize(text)
# POS tag the words
words_tagged = nltk.pos_tag(words)
要把文本轉換成樹狀結構,你必須定義一個 語法 。這個例子里用的是一個基於 Penn Treebank tags 的簡單語法。
# A simple grammar to create tree
grammar = "NP: {<JJ><NN>}"
然後用這個 語法 創建一顆 樹 :
# Create tree
parser = nltk.RegexpParser(grammar)
tree = parser.parse(words_tagged)
pprint(tree)
運行上面的代碼,將得到:
Tree('S', [('I', 'PRP'), ('love', 'VBP'), Tree('NP', [('open', 'JJ'), ('source', 'NN')])])
你也可以圖形化地顯示結果。
tree.draw()
![NLTK Tree](/data/attachment/album/202107/21/115644oswzxowwad5ldxww.jpg "NLTK Tree")
這個樹狀結構有助於準確解讀文本的意思。比如,用它可以找到文本的 主語:
subject_tags = ["NN", "NNS", "NP", "NNP", "NNPS", "PRP", "PRP$"]
def subject(sentence_tree):
for tagged_word in sentence_tree:
# A crude logic for this case - first word with these tags is considered subject
if tagged_word[1] in subject_tags:
return tagged_word[0]
print("Subject:", subject(tree))
結果顯示主語是 I
:
Subject: I
這是一個比較基礎的文本分析步驟,可以用到更廣泛的應用場景中。 比如,在聊天機器人方面,如果用戶告訴機器人:「給我媽媽 Jane 預訂一張機票,1 月 1 號倫敦飛紐約的」,機器人可以用這種分析方法解讀這個指令:
動作: 預訂
動作的對象: 機票
乘客: Jane
出發地: 倫敦
目的地: 紐約
日期: (明年)1 月 1 號
樹庫 指由許多預先標註好的 樹 構成的語料庫。現在已經有面向多種語言的樹庫,既有開源的,也有限定條件下才能免費使用的,以及商用的。其中使用最廣泛的是面向英語的賓州樹庫。賓州樹庫取材於 華爾街日報 。NLTK 也包含了賓州樹庫作為一個子語料庫。下面是一些使用 樹庫 的方法:
words = nltk.corpus.treebank.words()
print(len(words), "words:")
print(words)
tagged_sents = nltk.corpus.treebank.tagged_sents()
print(len(tagged_sents), "sentences:")
print(tagged_sents)
100676 words:
['Pierre', 'Vinken', ',', '61', 'years', 'old', ',', ...]
3914 sentences:
[[('Pierre', 'NNP'), ('Vinken', 'NNP'), (',', ','), ('61', 'CD'), ('years', 'NNS'), ('old', 'JJ'), (',', ','), ('will', 'MD'), ('join', 'VB'), ('the', 'DT'), ('board', 'NN'), ('as', 'IN'), ('a', 'DT'), ('nonexecutive', 'JJ'), ('director', 'NN'), ...]
查看一個句子里的各個 標籤 :
sent0 = tagged_sents[0]
pprint(sent0)
[('Pierre', 'NNP'),
('Vinken', 'NNP'),
(',', ','),
('61', 'CD'),
('years', 'NNS'),
...
定義一個 語法 來把這個句子轉換成樹狀結構:
grammar = '''
Subject: {<NNP><NNP>}
SubjectInfo: {<CD><NNS><JJ>}
Action: {<MD><VB>}
Object: {<DT><NN>}
Stopwords: {<IN><DT>}
ObjectInfo: {<JJ><NN>}
When: {<NNP><CD>}
'''
parser = nltk.RegexpParser(grammar)
tree = parser.parse(sent0)
print(tree)
(S
(Subject Pierre/NNP Vinken/NNP)
,/,
(SubjectInfo 61/CD years/NNS old/JJ)
,/,
(Action will/MD join/VB)
(Object the/DT board/NN)
as/IN
a/DT
(ObjectInfo nonexecutive/JJ director/NN)
(Subject Nov./NNP)
29/CD
./.)
圖形化地顯示:
tree.draw()
![NLP Treebank image](/data/attachment/album/202107/21/115645kama1atb5maab93a.jpg "NLP Treebank image")
樹 和 樹庫 的概念是文本分析的一個強大的組成部分。
自主嘗試
使用 Python 庫,下載維基百科的 「open source」 頁面,將得到的文本以圖形化的樹狀結構展現出來。
命名實體識別
無論口語還是書面語都包含著重要數據。文本處理的主要目標之一,就是提取出關鍵數據。幾乎所有應用場景所需要提取關鍵數據,比如航空公司的訂票機器人或者問答機器人。 NLTK 為此提供了一個 命名實體識別 的功能。
這裡有一個代碼示例:
sentence = 'Peterson first suggested the name "open source" at Palo Alto, California'
驗證這個句子里的 人名 和 地名 有沒有被識別出來。照例先預處理:
import nltk
words = nltk.word_tokenize(sentence)
pos_tagged = nltk.pos_tag(words)
運行 命名實體標註器 :
ne_tagged = nltk.ne_chunk(pos_tagged)
print("NE tagged text:")
print(ne_tagged)
print()
NE tagged text:
(S
(PERSON Peterson/NNP)
first/RB
suggested/VBD
the/DT
name/NN
``/``
open/JJ
source/NN
''/''
at/IN
(FACILITY Palo/NNP Alto/NNP)
,/,
(GPE California/NNP))
上面的結果里,命名實體被識別出來並做了標註;只提取這個 樹 里的命名實體:
print("Recognized named entities:")
for ne in ne_tagged:
if hasattr(ne, "label"):
print(ne.label(), ne[0:])
Recognized named entities:
PERSON [('Peterson', 'NNP')]
FACILITY [('Palo', 'NNP'), ('Alto', 'NNP')]
GPE [('California', 'NNP')]
圖形化地顯示:
ne_tagged.draw()
![NLTK Treebank tree](/data/attachment/album/202107/21/115645ghz7j75kwzls2gj5.jpg "NLTK Treebank tree")
NLTK 內置的 命名實體標註器 ,使用的是賓州法尼亞大學的 Automatic Content Extraction(ACE)程序。該標註器能夠識別 組織機構 、人名 、地名 、設施 和 地緣政治實體 等常見 實體 。
NLTK 也可以使用其他 標註器 ,比如 Stanford Named Entity Recognizer. 這個經過訓練的標註器用 Java 寫成,但 NLTK 提供了一個使用它的介面(詳情請查看 nltk.parse.stanford 或 nltk.tag.stanford)。
自主嘗試
使用 Python 庫,下載維基百科的 「open source」 頁面,並識別出對 開源 有影響力的人的名字,以及他們為 開源 做貢獻的時間和地點。
高級實踐
如果你準備好了,嘗試用這篇文章以及此前的文章介紹的知識構建一個 超級結構 。
使用 Python 庫,下載維基百科的 「Category: Computer science page」,然後:
- 找出其中頻率最高的 單詞 、二元搭配 和 三元搭配 ,將它們作為一個關鍵詞列表或者技術列表。相關領域的學生或者工程師需要了解這樣一份列表裡的內容。
- 圖形化地顯示這個領域裡重要的人名、技術、日期和地點。這會是一份很棒的信息圖。
- 構建一個搜索引擎。你的搜索引擎性能能夠超過維基百科嗎?
下一步?
自然語言處理是 應用構建 的典型支柱。NLTK 是經典、豐富且強大的工具集,提供了為現實世界構建有吸引力、目標明確的應用的工作坊。
在這個系列的文章里,我用 NLTK 作為例子,展示了自然語言處理可以做什麼。自然語言處理和 NLTK 還有太多東西值得探索,這個系列的文章只是幫助你探索它們的切入點。
如果你的需求增長到 NLTK 已經滿足不了了,你可以訓練新的模型或者向 NLTK 添加新的功能。基於 NLTK 構建的新的自然語言處理庫正在不斷湧現,機器學習也正被深度用於自然語言處理。
via: https://opensource.com/article/20/8/nlp-python-nltk
作者:Girish Managoli 選題:lujun9972 譯者:tanloong 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive