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.01code.n.02code.n.03code.v.01code.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.enciphercode.v.02.ciphercode.v.02.cyphercode.v.02.encryptcode.v.02.inscribecode.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

相似度比較

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

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

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 中的最短距離)。

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

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.stanfordnltk.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中國