Linux中國

進階教程:用 Python 和 NLTK 進行 NLP 分析

在 之前的文章 里,我介紹了 自然語言處理 natural language processing (NLP)和賓夕法尼亞大學研發的 自然語言處理工具包 Natural Language Toolkit (NLTK)。我演示了用 Python 解析文本和定義 停頓詞 stopword 的方法,並介紹了 語料庫 corpus 的概念。語料庫是由文本構成的數據集,通過提供現成的文本數據來輔助文本處理。在這篇文章里,我將繼續用各種語料庫對文本進行對比和分析。

這篇文章主要包括以下部分:

  • 詞網 WordNet 和 同義詞集 synset
  • 相似度比較 Similarity comparison
  • 樹 Tree 和 樹庫 treebank
  • 命名實體識別 Named entity recognition

詞網和同義詞集

詞網 WordNet 是 NLTK 里的一個大型辭彙資料庫語料庫。詞網包含各單詞的諸多 認知同義詞 cognitive synonyms (認知同義詞常被稱作「 同義詞集 synset 」)。在詞網裡,名詞、動詞、形容詞和副詞,各自被組織成一個同義詞的網路。

詞網是一個很有用的文本分析工具。它有面向多種語言的版本(漢語、英語、日語、俄語和西班牙語等),也使用多種許可證(從開源許可證到商業許可證都有)。初代版本的詞網由普林斯頓大學研發,面向英語,使用 類 MIT 許可證 MIT-like license 。

因為一個詞可能有多個意義或多個詞性,所以可能與多個同義詞集相關聯。每個同義詞集通常提供下列屬性:

屬性 定義 例子
名稱 Name 此同義詞集的名稱 單詞 code 有 5 個同義詞集,名稱分別是 code.n.01、 code.n.02、 code.n.03、code.v.01 和 code.v.02
詞性 POS 此同義詞集的詞性 單詞 code 有 3 個名詞詞性的同義詞集和 2 個動詞詞性的同義詞集
定義 Definition 該詞作對應詞性時的定義 動詞 code 的一個定義是:(計算機科學)數據或計算機程序指令的 象徵性排列 symbolic arrangement
例子 Example 使用該詞的例子 code 一詞的例子:We should encode the message for security reasons
詞元 Lemma 與該詞相關聯的其他同義詞集(包括那些不一定嚴格地是該詞的同義詞,但可以大體看作同義詞的);詞元直接與其他詞元相關聯,而不是直接與 單詞 word 相關聯 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
反義詞 Antonym 意思相反的詞 詞元 encode.v.01.encode 的反義詞是 decode.v.01.decode
上義詞 Hypernym 該詞所屬的一個範疇更大的詞 code.v.01 的一個上義詞是 tag.v.01
分項詞 Meronym 屬於該片語成部分的詞 computer 的一個分項詞是 chip
總項詞 Holonym 該詞作為組成部分所屬的詞 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(&apos;code&apos;)
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(&apos;code.n.01.code&apos;), Lemma(&apos;code.n.01.codification&apos;)]
Antonyms: []
Hypernyms: [Synset(&apos;written_communication.n.01&apos;)]
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(&apos;code.n.03.code&apos;), Lemma(&apos;code.n.03.computer_code&apos;)]
Antonyms: []
Hypernyms: [Synset(&apos;coding_system.n.01&apos;)]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []

...

Name code.v.02
POS: v
Definition: convert ordinary language into code
Examples: [&apos;We should encode the message for security reasons&apos;]
Lemmas: [Lemma(&apos;code.v.02.code&apos;), Lemma(&apos;code.v.02.encipher&apos;), Lemma(&apos;code.v.02.cipher&apos;), Lemma(&apos;code.v.02.cypher&apos;), Lemma(&apos;code.v.02.encrypt&apos;), Lemma(&apos;code.v.02.inscribe&apos;), Lemma(&apos;code.v.02.write_in_code&apos;)]
Antonyms: []
Hypernyms: [Synset(&apos;encode.v.01&apos;)]
Instance Hpernyms: []
Part Holonyms: []
Part Meronyms: []

同義詞集 synset 和 詞元 lemma 在詞網裡是按照樹狀結構組織起來的,下面的代碼會給出直觀的展現:

def hypernyms(synset):
    return synset.hypernyms()

synsets = wordnet.synsets(&apos;soccer&apos;)
for synset in synsets:
    print(synset.name() + " tree:")
    pprint(synset.tree(rel=hypernyms))
    print()
code.n.01 tree:
[Synset(&apos;code.n.01&apos;),
 [Synset(&apos;written_communication.n.01&apos;),
   ...

code.n.02 tree:
[Synset(&apos;code.n.02&apos;),
 [Synset(&apos;coding_system.n.01&apos;),
   ...

code.n.03 tree:
[Synset(&apos;code.n.03&apos;),
   ...

code.v.01 tree:
[Synset(&apos;code.v.01&apos;),
 [Synset(&apos;tag.v.01&apos;),
   ...

code.v.02 tree:
[Synset(&apos;code.v.02&apos;),
 [Synset(&apos;encode.v.01&apos;),
   ...

詞網並沒有涵蓋所有的單詞和其信息(現今英語有約 17,0000 個單詞,最新版的 詞網 涵蓋了約 15,5000 個),但它開了個好頭。掌握了「詞網」的各個概念後,如果你覺得它辭彙少,不能滿足你的需要,可以轉而使用其他工具。或者,你也可以打造自己的「詞網」!

自主嘗試

使用 Python 庫,下載維基百科的 「open source」 頁面,並列出該頁面所有單詞的 同義詞集 synset 和 詞元 lemma 。

相似度比較

相似度比較的目的是識別出兩篇文本的相似度,在搜索引擎、聊天機器人等方面有很多應用。

比如,相似度比較可以識別 football 和 soccer 是否有相似性。

syn1 = wordnet.synsets(&apos;football&apos;)
syn2 = wordnet.synsets(&apos;soccer&apos;)

# 一個單詞可能有多個 同義詞集,需要把 word1 的每個同義詞集和 word2 的每個同義詞集分別比較
for s1 in syn1:
    for s2 in syn2:
        print("Path similarity of: ")
        print(s1, &apos;(&apos;, s1.pos(), &apos;)&apos;, &apos;[&apos;, s1.definition(), &apos;]&apos;)
        print(s2, &apos;(&apos;, s2.pos(), &apos;)&apos;, &apos;[&apos;, s2.definition(), &apos;]&apos;)
        print("   is", s1.path_similarity(s2))
        print()
Path similarity of:
Synset(&apos;football.n.01&apos;) ( 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&apos;s goal ]
Synset(&apos;soccer.n.01&apos;) ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents&apos; goal ]
   is 0.5

Path similarity of:
Synset(&apos;football.n.02&apos;) ( n ) [ the inflated oblong ball used in playing American football ]
Synset(&apos;soccer.n.01&apos;) ( n ) [ a football game in which two teams of 11 players try to kick or head a ball into the opponents&apos; goal ]
   is 0.05

兩個詞各個同義詞集之間 路徑相似度 path similarity 最大的是 0.5,表明它們關聯性很大( 路徑相似度 path similarity 指兩個詞的意義在 上下義關係的辭彙分類結構 hypernym/hypnoym taxonomy 中的最短距離)。

那麼 code 和 bug 呢?這兩個計算機領域的詞的相似度是:

Path similarity of:
Synset(&apos;code.n.01&apos;) ( n ) [ a set of rules or principles or laws (especially written ones) ]
Synset(&apos;bug.n.02&apos;) ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.1111111111111111
...
Path similarity of:
Synset(&apos;code.n.02&apos;) ( n ) [ a coding system used for transmitting messages requiring brevity or secrecy ]
Synset(&apos;bug.n.02&apos;) ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.09090909090909091
...
Path similarity of:
Synset(&apos;code.n.03&apos;) ( n ) [ (computer science) the symbolic arrangement of data or instructions in a computer program or the set of such instructions ]
Synset(&apos;bug.n.02&apos;) ( n ) [ a fault or defect in a computer program, system, or machine ]
   is 0.09090909090909091

這些是這兩個詞各同義詞集之間 路徑相似度 path similarity 的最大值,這些值表明兩個詞是有關聯性的。

NLTK 提供多種 相似度計分器 similarity scorers ,比如:

  • path_similarity
  • lch_similarity
  • wup_similarity
  • res_similarity
  • jcn_similarity
  • lin_similarity

要進一步了解這些 相似度計分器 similarity scorers ,請查看 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)

要把文本轉換成樹狀結構,你必須定義一個 語法 grammar 。這個例子里用的是一個基於 Penn Treebank tags 的簡單語法。

# A simple grammar to create tree
grammar = "NP: {<JJ><NN>}"

然後用這個 語法 grammar 創建一顆 樹 tree :

# Create tree
parser = nltk.RegexpParser(grammar)
tree = parser.parse(words_tagged)
pprint(tree)

運行上面的代碼,將得到:

Tree(&apos;S&apos;, [(&apos;I&apos;, &apos;PRP&apos;), (&apos;love&apos;, &apos;VBP&apos;), Tree(&apos;NP&apos;, [(&apos;open&apos;, &apos;JJ&apos;), (&apos;source&apos;, &apos;NN&apos;)])])

你也可以圖形化地顯示結果。

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 號

樹庫 treebank 指由許多預先標註好的 樹 tree 構成的語料庫。現在已經有面向多種語言的樹庫,既有開源的,也有限定條件下才能免費使用的,以及商用的。其中使用最廣泛的是面向英語的賓州樹庫。賓州樹庫取材於 華爾街日報 Wall Street Journal 。NLTK 也包含了賓州樹庫作為一個子語料庫。下面是一些使用 樹庫 treebank 的方法:

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:
[&apos;Pierre&apos;, &apos;Vinken&apos;, &apos;,&apos;, &apos;61&apos;, &apos;years&apos;, &apos;old&apos;, &apos;,&apos;, ...]
3914 sentences:
[[(&apos;Pierre&apos;, &apos;NNP&apos;), (&apos;Vinken&apos;, &apos;NNP&apos;), (&apos;,&apos;, &apos;,&apos;), (&apos;61&apos;, &apos;CD&apos;), (&apos;years&apos;, &apos;NNS&apos;), (&apos;old&apos;, &apos;JJ&apos;), (&apos;,&apos;, &apos;,&apos;), (&apos;will&apos;, &apos;MD&apos;), (&apos;join&apos;, &apos;VB&apos;), (&apos;the&apos;, &apos;DT&apos;), (&apos;board&apos;, &apos;NN&apos;), (&apos;as&apos;, &apos;IN&apos;), (&apos;a&apos;, &apos;DT&apos;), (&apos;nonexecutive&apos;, &apos;JJ&apos;), (&apos;director&apos;, &apos;NN&apos;), ...]

查看一個句子里的各個 標籤 tags :

sent0 = tagged_sents[0]
pprint(sent0)
[(&apos;Pierre&apos;, &apos;NNP&apos;),
 (&apos;Vinken&apos;, &apos;NNP&apos;),
 (&apos;,&apos;, &apos;,&apos;),
 (&apos;61&apos;, &apos;CD&apos;),
 (&apos;years&apos;, &apos;NNS&apos;),
...

定義一個 語法 grammar 來把這個句子轉換成樹狀結構:

grammar = &apos;&apos;&apos;
    Subject: {<NNP><NNP>}
    SubjectInfo: {<CD><NNS><JJ>}
    Action: {<MD><VB>}
    Object: {<DT><NN>}
    Stopwords: {<IN><DT>}
    ObjectInfo: {<JJ><NN>}
    When: {<NNP><CD>}
&apos;&apos;&apos;
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")

樹 trees 和 樹庫 treebanks 的概念是文本分析的一個強大的組成部分。

自主嘗試

使用 Python 庫,下載維基百科的 「open source」 頁面,將得到的文本以圖形化的樹狀結構展現出來。

命名實體識別

無論口語還是書面語都包含著重要數據。文本處理的主要目標之一,就是提取出關鍵數據。幾乎所有應用場景所需要提取關鍵數據,比如航空公司的訂票機器人或者問答機器人。 NLTK 為此提供了一個 命名實體識別 named entity recognition 的功能。

這裡有一個代碼示例:

sentence = &apos;Peterson first suggested the name "open source" at Palo Alto, California&apos;

驗證這個句子里的 人名 name 和 地名 place 有沒有被識別出來。照例先預處理:

import nltk

words = nltk.word_tokenize(sentence)
pos_tagged = nltk.pos_tag(words)

運行 命名實體標註器 named-entity tagger :

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
  &apos;&apos;/&apos;&apos;
  at/IN
  (FACILITY Palo/NNP Alto/NNP)
  ,/,
  (GPE California/NNP))

上面的結果里,命名實體被識別出來並做了標註;只提取這個 樹 tree 里的命名實體:

print("Recognized named entities:")
for ne in ne_tagged:
    if hasattr(ne, "label"):
        print(ne.label(), ne[0:])
Recognized named entities:
PERSON [(&apos;Peterson&apos;, &apos;NNP&apos;)]
FACILITY [(&apos;Palo&apos;, &apos;NNP&apos;), (&apos;Alto&apos;, &apos;NNP&apos;)]
GPE [(&apos;California&apos;, &apos;NNP&apos;)]

圖形化地顯示:

ne_tagged.draw()

![NLTK Treebank tree](/data/attachment/album/202107/21/115645ghz7j75kwzls2gj5.jpg "NLTK Treebank tree")

NLTK 內置的 命名實體標註器 named-entity tagger ,使用的是賓州法尼亞大學的 Automatic Content Extraction(ACE)程序。該標註器能夠識別 組織機構 ORGANIZATION 、人名 PERSON 、地名 LOCATION 、設施 FACILITY 和 地緣政治實體 geopolitical entity 等常見 實體 entites 。

NLTK 也可以使用其他 標註器 tagger ,比如 Stanford Named Entity Recognizer. 這個經過訓練的標註器用 Java 寫成,但 NLTK 提供了一個使用它的介面(詳情請查看 nltk.parse.stanford 或 nltk.tag.stanford)。

自主嘗試

使用 Python 庫,下載維基百科的 「open source」 頁面,並識別出對 開源 open source 有影響力的人的名字,以及他們為 開源 open source 做貢獻的時間和地點。

高級實踐

如果你準備好了,嘗試用這篇文章以及此前的文章介紹的知識構建一個 超級結構 superstructure 。

使用 Python 庫,下載維基百科的 「Category: Computer science page」,然後:

  • 找出其中頻率最高的 單詞 unigrams 、二元搭配 bigrams 和 三元搭配 trigrams ,將它們作為一個關鍵詞列表或者技術列表。相關領域的學生或者工程師需要了解這樣一份列表裡的內容。
  • 圖形化地顯示這個領域裡重要的人名、技術、日期和地點。這會是一份很棒的信息圖。
  • 構建一個搜索引擎。你的搜索引擎性能能夠超過維基百科嗎?

下一步?

自然語言處理是 應用構建 application building 的典型支柱。NLTK 是經典、豐富且強大的工具集,提供了為現實世界構建有吸引力、目標明確的應用的工作坊。

在這個系列的文章里,我用 NLTK 作為例子,展示了自然語言處理可以做什麼。自然語言處理和 NLTK 還有太多東西值得探索,這個系列的文章只是幫助你探索它們的切入點。

如果你的需求增長到 NLTK 已經滿足不了了,你可以訓練新的模型或者向 NLTK 添加新的功能。基於 NLTK 構建的新的自然語言處理庫正在不斷湧現,機器學習也正被深度用於自然語言處理。

via: https://opensource.com/article/20/8/nlp-python-nltk

作者:Girish Managoli 選題:lujun9972 譯者:tanloong 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的郵箱地址不會被公開。 必填項已用 * 標註

    這個站點使用 Akismet 來減少垃圾評論。了解你的評論數據如何被處理。

    More in:Linux中國

    Linux中國

    捐贈 Let&apos;s Encrypt,共建安全的互聯網

    隨著 Mozilla、蘋果和谷歌對沃通和 StartCom 這兩家 CA 公司處罰落定,很多使用這兩家 CA 所簽發證書的網站紛紛尋求新的證書籤發商。有一個非盈利組織可以為大家提供了免費、可靠和安全的 SSL 證書服務,這就是 Let's Encrypt 項目。現在,它需要您的幫助
    Linux中國

    關於Linux防火牆iptables的面試問答

    Nishita Agarwal是Tecmint的用戶,她將分享關於她剛剛經歷的一家公司(印度的一家私人公司Pune)的面試經驗。在面試中她被問及許多不同的問題,但她是iptables方面的專家,因此她想分享這些關於iptables的問題和相應的答案給那些以後可能會進行相關面試的人。 所有的問題和相應的答案都基於Nishita Agarwal的記憶並經過了重寫。 嗨,朋友!我叫Nishita Agarwal。我已經取得了理學學士學位,我的專業集中在UNIX和它的變種(BSD,Linux)。它們一直深深的吸引著我。我在存儲方面有1年多的經驗。我正在尋求職業上的變化,並將供職於印度的P
    Linux中國

    Lets Encrypt 已被所有主流瀏覽器所信任

    旨在讓每個網站都能使用 HTTPS 加密的非贏利組織 Lets Encrypt 已經得了 IdenTrust的交叉簽名,這意味著其證書現在已經可以被所有主流的瀏覽器所信任。從這個裡程碑事件開始,訪問者訪問使用了Lets Encrypt 證書的網站不再需要特別配置就可以得到 HTTPS 安全保護了。 Lets Encrypt 的兩個中級證書 ...