第一章 中文語言的機器處理

1. 搭建NLTK環境

1.1 操作系統

本教程用的操作系統是Windows 10 x64(如果你使用的是32位系統,則應升級爲64位的操作系統;如果你安裝的是Windows 7或XP,則無需升級到Windows 10操作系統)。

1.2 Python開發環境

本教程使用的是當前最新版本的Python-3.7版本。

安裝方法,我是用過兩種。

第一種是標準方法,也就是到官網上下載最新版本。

第二種是使用 Anaconda跨平臺安裝包,可以到官網 https://www.continuum.io/downloads 上自行下載,部分教程可以參考本人博客相關內容。

編輯器用的是Pycharm。

1.3 安裝常用Python應用程序

  • 安裝數學運算包。

    pip install numpy
    conda install scipy

  • 安裝mysql數據庫工具包 。

  • 安裝Tornado網絡包 。

    pip install Tornado

  • 安裝NLTK開發環境。
    (1)安裝NLTK語言開發系統的基本指令。

    pip install nltk

    (2)下載常用語料庫:NLTK,執行如下代碼可下載相關的語料。

    import nltk
    nltk.download()

    出現如圖所示界面:
    在這裏插入圖片描述
    注意,如果在Pycharm中運行代碼。

    import nltk

    可能會報一系列錯誤,主要表示無法獲取模塊。然而這個時候感覺一切都是正確的(python用的是Anaconda的虛擬環境,Anaconda環境變量配置完整,cmd中執行上述代碼沒有問題),卻找不到正確方法。建議重啓電腦,清理電腦垃圾(360),在cmd上運行一遍,在覺得比較通暢的時候,再在Pycharm上再試一遍。如果沒有報錯,那就說明成功了。如果按照上面安裝nltk的參考鏈接,已經將nltk_data下載到了本地,結果是老是顯示網絡鏈接超時,只跳出來窗口,不顯式各種庫,只需點擊refresh,慢慢地各種庫就會顯示出來。
    這個在整合句法分析模塊用的到,現在也不必太過着急,可以回頭參考,免得事倍功半。

2. 整合中文分詞模塊

這裏將幾個開源的NLP系統整合到NLTK中。

本節主要介紹【中文分詞】部分。漢語自然語言處理的第一部分是中文分詞。 因爲中文不像英文那樣,實行分詞連寫,即詞與詞之間用空格分隔。在分析中文文本之前必須將一個漢字序列切分成一個一個單獨的詞。這個過程稱爲中文分詞(Chinese Word Segmentation)。

按照使用的算法不同,介紹兩大類中文分詞模塊

  • 基於條件隨機場(CRF)的中文分詞算法的開源系統。
  • 基於張華平NShort的中文分詞算法的開源系統。

2.1 安裝Ltp Python組件

國內使用CRF做中文分詞的開源系統主要爲哈工大的HIT LTP語言技術平臺。

(1)pyltp安裝。

pip install pyltp

然而失敗(我python3.5,3.7都試過了失敗),報錯原因:

c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe’ failed with exit status 2

解決辦法:
果斷放棄pip和conda方法,參考鏈接這位仁兄的解決辦法。

步驟1:添加VC路徑到用戶環境變量中,注意是用戶環境變量中,而不是系統環境變量中。在這裏插入圖片描述
變量名:VCINSTALLDIR (變量值爲vs安裝路徑下的VC,默認是這個)
變量值(我的):C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC

步驟2:
win+R運行cmd,執行命令:

set CL=/FI”%VCINSTALLDIR%\INCLUDE\stdint.h” %CL%

步驟3:
下載pyltp壓縮文件。下載鏈接:https://github.com/hit-scir/pyltp ,下載完成後,解壓到任意一個文件夾下,如pyltp-master。注意,解壓pyltp後所得到的文件夾中可能已經有一個名爲ltp的空文件夾:在這裏插入圖片描述
步驟4:
然後下載ltb壓縮文件。下載;鏈接:https://github.com/hit-scir/ltp ,下載完成後,解壓文件夾到任意一個文件夾中,如,ltp-master。在這裏插入圖片描述
步驟5:
將ltp-master該文件夾重命名爲ltp複製並覆蓋pyltp的ltp文件夾。

步驟6:
win+R打開CMD,cd到pyltp目錄下,然後輸入python setup.py install,如下所示(我用的是Anaconda,所以有第一句激活命令,如果不是Anaconda忽略第一句)。

(base) C:\Users\Administrator>activate Anaconda-Pycharm
(Anaconda-Pycharm) C:\Users\Administrator>d:
(Anaconda-Pycharm) D:>cd D:\Anaconda3\envs\Anaconda-Pycharm\pyltp-master
(Anaconda-Pycharm) D:\Anaconda3\envs\Anaconda-Pycharm\pyltp-master>python setup.py install

結果是!!!!(神啊,讓我成功吧!):
報錯:

ltp/src/utils/strutils.hpp(344): warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int’, possible loss of data
ltp/src/utils/sbcdbc.hpp(67): warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int’, possible loss of data
ltp/src/utils/sbcdbc.hpp(85): warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int’, possible loss of data
ltp/src/utils/smartmap.hpp(617): warning C4267: ‘initializing’: conversion from ‘size_t’ to ‘int32_t’, possible loss of data

patch\libs\python\src\converter\builtin_converters.cpp(51): error C2440: ‘return’: cannot convert from ‘const char *’ to ‘void *’

patch\libs\python\src\converter\builtin_converters.cpp(51): note: Conversion loses qualifiers
patch\libs\python\src\converter\builtin_converters.cpp(442): warning C4244: ‘initializing’: conversion from ‘Py_ssize_t’ to ‘int’, possible loss of data
error: command ‘C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_amd64\cl.exe’ failed with exit status 2

可以看的出來前面都是warning,可以忽略,但是這裏有一個報錯的信息:
patch\libs\python\src\converter\builtin_converters.cpp(51): error C2440: ‘return’: cannot convert from ‘const char *’ to ‘void *’

錯誤原因:類型轉換出錯。

解決辦法:(51)行加(void*),暴力解決,強制類型轉換。

#if PY_VERSION_HEX < 0x03000000
  void* convert_to_cstring(PyObject* obj)
  {
      return PyString_Check(obj) ? PyString_AsString(obj) : 0;
  }
#else
  void* convert_to_cstring(PyObject* obj)
  {
      return PyUnicode_Check(obj) ?  (void*) _PyUnicode_AsString(obj) : 0;       
  }
#endif

再來試一下:
結果是:成功!!!!

Installed d:\anaconda3\envs\anaconda-pycharm\lib\site-packages\pyltp-0.2.1-py3.7-win-amd64.egg
Processing dependencies for pyltp==0.2.1

Finished processing dependencies for pyltp==0.2.1

沒得塔西,沒得塔西

(2)部署語言模型庫。
從鏈接:https://pan.baidu.com/s/1DQObXdIkGyS_Ne_h7ixrZg 提取碼:40zu下載Ltp的模型文件,好幾種,我自己用v3.4.0,解壓在自己的某一個盤符下:在這裏插入圖片描述cws.model爲中文分詞模塊所需的語言模型,爲二進制文件。
fulluserdict.txt爲用戶可添加的外部詞典文件,貌似一開始並不存在,可以用戶在運行過程中慢慢添加,如下面的實例,結構如圖所示。

2.2 使用Ltp3.4進行中文分詞

(1)Ltp3.4 安裝成功之後,新建一個Python文件:

# -*- coding: utf-8 -*-
from pyltp import Segmentor # 導入ltp庫
segmentor = Segmentor() # 實例化分詞模塊
segmentor.load("D:/ltp_3.4/cws.model")# 加載分詞庫
words = segmentor.segment("在包含問題的所有解的解空間樹種,按照深度優先搜索的策略,從根節點出發深度探索解空間樹。")# ,調用分詞函數,設置分詞對象
print("|".join(words))# 打印分詞結果
segmentor.release()

執行結果如下:

在|包含|問題|的|所有|解|的|解|空間|樹種|,|按照|深度|優先|搜索|的|策略|,|從|根節點|出發|深度|探索|解|空間|樹|。

(2)分詞結果的處理。

觀察上述分詞結果,“解|空間”、“解|空間|樹”、“深度|優先”都可以看作一個完成的專有名詞:解空間、解空間樹、深度優先,所以說,分詞器劃分的【粒度過細】。

爲了獲得更精確的結果,可以將錯分的結果合併爲【專有名詞】。這就是分詞結果的後處理過程,即【一般外部用戶詞典的構成原理】。

# -*- coding: utf-8 -*-
from pyltp import Segmentor # 導入ltp庫
postdict={"解|空間":"解空間","深度|優先":"深度優先"}# 分詞後處理——矯正一些錯誤的結果
segmentor = Segmentor() # 實例化分詞模塊
segmentor.load("D:/ltp_3.4/cws.model")# 加載分詞庫
words = segmentor.segment("在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根節點出發深度探索解空間樹。")# 設置分詞對象
seg_sent = "|".join(words)# 分割後的分詞結果
print("分割後的分詞結果 :"+seg_sent)
for key in postdict:
    seg_sent=seg_sent.replace(key,postdict[key])
print("分詞後處理的分詞結果 : "+seg_sent)# 打印分詞後處理的分詞結果
segmentor.release()

分詞結果如下:

分割後的分詞結果 :在|包含|問題|的|所有|解|的|解|空間|樹|中|,|按照|深度|優先|搜索|的|策略|,|從|根節點|出發|深度|探索|解|空間|樹|。
分詞後處理的分詞結果 : 在|包含|問題|的|所有|解|的|解空間|樹|中|,|按照|深度優先|搜索|的|策略|,|從|根節點|出發|深度|探索|解空間|樹|。

(3)現在加入用戶詞典,詞典中登陸一些新詞,如解空間。
javascript

2.3 使用結巴分詞模塊

張華平NShort的中文分詞算法是目前最大規模中文分詞的主流算法。在商用領域,大多數搜索引擎公司都使用該算法做欸主要的分詞算法。具有算法原理簡單、容易理解、便於訓練、大規模分詞的效率高、模型支持增量擴展、模型佔用資源低等優勢。

結巴分詞的算法核心就是NShort中文分詞算法。

(1)結巴分詞庫的安裝。

pip install jieba

結巴分詞下載地址:https://github.com/fxsjy/jieba 下載完解壓到任一文件夾,如,D:\jieba-master,命令如下:

(base) C:\Users\Administrator>activate Anaconda-Pycharm
(Anaconda-Pycharm) C:\Users\Administrator>d:
(Anaconda-Pycharm) D:>cd D:\jieba-master
(Anaconda-Pycharm) D:\jieba-master>python setup.py install

(2)使用結巴分詞

# -*- coding: utf-8 -*-
import jieba # 導入結巴分詞庫

# 結巴分詞--全模式
sent = '在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根節點出發深度探索解空間樹。'
wordlist = jieba.cut(sent, cut_all=True)
print('|'.join(wordlist))
# 結巴分詞--精確切分
wordlist = jieba.cut(sent) # cut_all = False
print('|'.join(wordlist))
# 結巴分詞--搜索引擎模式
wordlist = jieba.cut_for_search(sent)
print('|'.join(wordlist))

分詞結果如下。

D:\Anaconda3\envs\Anaconda-Pycharm\python.exeD:/PycharmProjects/NLP/jieba_test.py
Building prefix dict from the default dictionary …
Dumping model to file cacheC:\Users\Administrator\AppData\Local\Temp\jieba.cache
在|包含|問題|的|所有|解|的|解空|空間|樹|中|||按照|深度|優先|搜索|的|策略|||從|根|節點|點出|出發|深度|探索|索解|解空|空間|樹||
在|包含|問題|的|所有|解|的|解|空間|樹中|,|按照|深度|優先|搜索|的|策略|,|從根|節點|出發|深度|探索|解|空間|樹|。
在|包含|問題|的|所有|解|的|解|空間|樹中|,|按照|深度|優先|搜索|的|策略|,|從根|節點|出發|深度|探索|解|空間|樹|。
Loading model cost 1.191 seconds.
Prefix dict has been built successfully.

進程已結束,退出代碼 0

結巴分詞的基礎詞庫較之Ltp分詞要少一些,標準詞典的詞彙量約有35萬個。上例的分詞結果顯示,一些專有名詞的切分缺乏足夠的精度,不僅出現粒度問題,還出現錯分問題(如上例中的“樹中”、“從根”)。由此可見分詞器的精度不僅受到算法的影響,更受到語言模型庫的規模影響。這一點對於NShort最短路徑算法尤其明顯。

(3)定義用戶詞典。
如,詞典文件userdict.txt

解空間 5 n
解空間樹 5 n
根節點 5 n
深度優先 5 n

詞典格式爲一個詞佔一行;每一行分爲三部分:詞與、詞頻和詞性,用空格隔開,順序不可顛倒。userdict.txt文件必須爲UTF-8編碼。

結巴分詞中的詞典詞頻設置,默認爲5。這個數越大,說明該字符串的概率越高,受內置詞典的干擾就越小;這個數越小,用戶詞典內的詞收到內置詞典的干擾越強,不能受到正確切分的概率就越大。用戶根據實際情況來設置這個值。

(4)使用用戶詞典。

# -*- coding: utf-8 -*-
import jieba # 導入結巴分詞庫

jieba.load_userdict("userdict.txt") #加載外部 用戶詞典
sent = '在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根節點出發深度探索解空間樹。'
# 結巴分詞--精確切分
wordlist = jieba.cut(sent) # cut_all = False
print('|'.join(wordlist))

分詞結果如下。

Building prefix dict from the default dictionary …
Loading model from cache C:\Users\Administrator\AppData\Local\Temp\jieba.cache
Loading model cost 1.104 seconds.
在|包含|問題|的|所有|解|的|解空間樹|中|,|按照|深度優先|搜索|的|策略|,|從|根節點|出發|深度|探索|解空間樹|。
Prefix dict has been built successfully.

3. 整合詞性標註模塊

【詞性標註】(Part-of-Speech Tagging或POS Tagging),又稱爲詞類標註,是指判斷處在一個句子中沒歌詞所扮演的語法角色。例如,表示人、事物、地點或抽象概念的名稱就是名詞;表示動作或狀態變換的詞爲動詞;用來描寫或修飾名詞性成分或表示概念的性質、狀態、特徵或屬性的詞稱爲形容詞,等等。

在漢語中,常用詞的詞性都不是固定的,也就是說,一個詞可能具有多個詞性。

本節主要介紹幾個中文詞性標註系統的應用。一般而言,中文的詞性標註算法比較統一,大多數使用【HMM】(隱馬爾科夫模型)或【最大熵算法】,如前文中的結巴分詞的詞性標註實現。爲了獲得更高的精度,也有使用【CRF算法】的,如Ltp3.4中的詞性標註。雖然分詞與詞性標註是兩個不同的模塊,但是在一般的工程應用中,語料的【中文分詞】和【詞性標註】通常同時完成。

目前流行的中文詞性標籤有兩大類:北大詞性標註集和賓州詞性標註集。兩大標註方式各有千秋,爲了更全面地反應中文標註的概況,我們使用Stanford大學的中文詞性標註模塊作爲另一箇中文詞性標註系統。

3.1 Ltp 3.4 詞性標註

# -*- coding: utf-8 -*-
from pyltp import Segmentor
from pyltp import Postagger
words = "在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根節點出發深度探索解空間樹。"
segmentor = Segmentor() # 實例化分詞模塊
segmentor.load("D:/ltp_3.4/cws.model")# 加載分詞庫
words = segmentor.segment(words)# 調用分詞函數
words = '|'.join(words)
sent = words.split('|')
postagger = Postagger() # 實例化詞性標註類
postagger.load("D:/ltp_3.4/pos.model")# 導入詞性標註模型
postags = postagger.postag(sent)
for word,postags in zip(sent,postags):
    print(word+"/"+postags)

詞性標註結果如下。

在/p
包含/v
問題/n
的/u
所有/b
解/n
的/u
解/v
空間/n
樹/n
中/nd
,/wp
按照/p
深度/n
優先/v
搜索/v
的/u
策略/n
,/wp
從/p
根節點/n
出發/v
深度/n
探索/v
解/v
空間/n
樹/n
。/wp

標註的標籤與原標籤用“/”分隔,每個標籤都有器語法的意義。例如,“n”表示名詞,“v”表示動詞。Ltp的詞性標註遵從給北大詞性標註規範。

3.2 安裝StanfordNLP並編寫Python接口類

下面介紹如何使用StanfordNLP系統來進行中文詞性標註。在使用之前,需要安裝jdk環境,Stanford-CoreNLP-3.6.0 版本需要安裝Java 8+的環境。(安裝jdk過程跳過)

安裝Stanford NLP的語言程序包。讀者可從http://stanfordnlp.github.io/CoreNLP/ 網頁下載全套語言處理模塊,或者我自己提供的下載地址https://nlp.stanford.edu/software/stanford-corenlp-full-2018-10-05.zip ,可以直接將鏈接複製到迅雷下載。
下載文件名爲 standford-corenlp-full-2018-10-05.zip,但其只攜帶了英文的語言模型包,中文部分的語言模型需要單獨下載,下載鏈接:https://stanfordnlp.github.io/CoreNLP/index.html#download 在這裏插入圖片描述
最好用迅雷下載,中文模型包大概1G大小。

先將stanford-corenlp-full-2018-10-05.zip解壓到盤符下一個名爲stanford-corenlp的文件夾下,如,D:/stanford-corenlp。

在這裏插入圖片描述

很長的文件夾內容,其中stanford-corenlp.jar爲主執行文件。

下載的中文模型文件Stanford-Chinese-corenlp-2018-10-05-models.jar解壓後的目錄結構如圖所示。

Stanford-Chinese-corenlp-2018-10-05-models.jar中的中文模型全部解壓到d:/stanford-corenlp中的models目錄下。其中,pos-tagger目錄下放置了詞性標註的中文模型。

讀者可以從http://nlp.stanford.edu/software/tagger.shtml 處下載stanford-postagger-full-2018-10-05.zip包,參考教程,並找到stanford-postagger.bat作爲執行命令行的參考腳本。

該工具包是用java寫的,所以使用該工具包有兩種方式:Java代碼和命令行調用。下面是解壓的文件夾。

在這裏插入圖片描述
(1)Java代碼方式。

具體的代碼可以參看TaggerDemo.java和TaggerDemo2.java這兩個代碼,需要注意的是在工程中(例如:eclipse),工程需要將對應的“models”文件夾放置在該工程中,同時在該項目的“Build Path”中選擇“Configure Build Path”,點擊“Add External JARs”,將“stanford-postagger-3.9.2.jar”加入該項目中,接下來就是基於Demo改一下就可以使用。 當然,如果我們想要做一個可視化界面,可以將該工程導出(export)成一個可執行的jar(Runnable jar file),同時需要注意,我們需要在生成的可執行jar的路徑下把“models”文件夾也複製進來,否則,無法調用postagger。

(2)命令行方式。

這個可以參看文件夾的“README.txt"這個文件。裏面有好幾種方式,我們這次主要說明利用stanford-postagger.sh來進行。

其實這裏有一個小缺陷,假設一個場景:如果我們在一個路徑下調用了一個python腳本,我們在該python裏想要利用os。system(cmd)來調用該postagger.sh,但是該python腳本和該postagger.sh並不在一個路徑下,所以我們需要用一個String構造cmd命令,這樣很可能會用”../"或者“./”來表示路徑,但是我們需要調用該.sh又需要使用“./"這個命令,所以這是個小矛盾,注意例子中給的”./"只是一種調用.sh的方式,我們還可以用“sh tagger.sh ..."這樣的命令來進行調用。(表示這段內容,沒遇到過,下面我來解釋,我知道的)

在該文件夾中,點擊運行stanford-postagger-gui.bat,會出來一個Stanford詞性標註器,然而只有英文的。

在這裏插入圖片描述

現在需要用在命令行下,重新編輯bat文件, cd到D:/stanford-postagger目錄下,在D:/stanford-postagger下新建一個postext.txt文件,要求是utf-8格式(另存爲可以實現)。postext文件內容:

在 包含 問題 的 所有 解 的 解空間樹 中 ,按照 深度優先 搜索 的 策略 ,從 根節點 出發 深度 探索 解空間樹 。

執行過程如下:

(Anaconda-Pycharm) D:>cd d:/stanford-postagger
(Anaconda-Pycharm) d:\stanford-postagger>java -mx200m -classpath stanford-postagger.jar edu.stanford.nlp.tagger.maxent.MaxentTagger -model “models\chinese-distsim.tagger” -textFile postext.txt > result.txt

執行結果如下:

Loading default properties from tagger models\chinese-distsim.tagger
Loading POS tagger from models\chinese-distsim.tagger … done [1.5 sec].
Tagged 21 words at 262.50 words per second.

在result.txt文件中的內容如下:

在#VV 包含#VV 問題#NN 的#DEC 所有#DT 解#VV 的#DEC 解空間樹#NN 中#LC ,按照#NR 深度優先#AD 搜索#VV 的#DEC 策略#NN ,從#NN 根節點#NN 出發#VV 深度#JJ 探索#NN 解空間樹#VV 。#PU

在該過程中。

(Anaconda-Pycharm) d:\stanford-postagger>java -mx200m -classpath stanford-postagger.jar edu.stanford.nlp.tagger.maxent.MaxentTagger -model “models\chinese-distsim.tagger” -textFile postext.txt > result.txt

java -mx200m #調用java程序,以及設置最大的內存

可修改內容爲最大內存大小。

-cp stanford-postagger.jar # 調用的jar包,3.9版只有一個就是stanford-postagger.jar

可修改的內容stanford-postagger.jar包的路徑,也就是如果不cd到D:/stanford-postagger目錄下,在任一目錄下,需要填寫完整路徑。

-classpath “D:\stanford-postagger\stanford-postagger.jar”

或者

-cp “D:\stanford-postagger\stanford-postagger.jar;”

edu.stanford.nlp.tagger.maxent.MaxentTagger # 最大熵分類器
-model "models\\chinese-distsim.tagger" # 最大熵模型文件 ,對應-model %1

可修改內容爲%1,其模型文件在"D:/stanford-postagger/models/"路徑下,如果不是在stanford-postagger目錄下執行就需要,修改爲絕對路徑。

-textFile postext.txt > result.txt # 最大熵輸入文件,同時結果放到輸出文件

可修改內容爲postext.txt 和 result.txt路徑,如果這兩個文件不想放在執行命令的路徑下,就需要填寫絕對路徑。

-textFile D:\stanford-postagger\postext.txt > D:\result.txt

總的來說也就是可以如此修改。

(Anaconda-Pycharm) C:\Users\Administrator>java -mx200m -classpath “D:\stanford-postagger\stanford-postagger.jar” edu.stanford.nlp.tagger.maxent.MaxentTagger -model “D:\stanford-postagger\models\chinese-distsim.tagger” -textFile D:\stanford-postagger\postext.txt > D:\result.txt

結果爲:

Loading default properties from tagger D:\stanford-postagger\models\chinese-distsim.tagger
Loading POS tagger from D:\stanford-postagger\models\chinese-distsim.tagger … done [1.5 sec].
Tagged 21 words at 262.50 words per second.

爲了在NLTK中使用方便,新建一個 stanford.py,專門編寫了一個 StanfordCoreNLP 的若干個接口類,用於 Python 調用 Java 編寫的NLP詞性標註應用。 新建一個StanfordCoreNLP_Interface.py

import os

class StanfordCoreNLP_Interface(): # 所有StanfordNLP的父類

    def __init__(self,root):# 構造函數
        self.root = root # 獲取jar包根路徑
        self.inputpath = "D:/stanford-postagger/postext.txt" # 臨時中文輸入源文件路徑 -textFile %2 , 注意將文件格式設置爲utf-8
        self.jarlist = {"ejml-0.23.jar","javax.jason.jar","jollyday.jar","joda-time.jar","protobuf.jar","slf4j-api.jar","slf4j-simple.jar","stanford-corenlp-3.9.2.jar","xom.jar"}
        self.jarlistpath = ""
        self.buildjars() # 根據根目錄構建-classpath jarlistpath

    # 創建臨時文件存儲路徑
    def savefile(self, inputpath, sent):
        fp = open(inputpath, 'w', encoding='utf-8')
        # 注意這裏需要指定編碼格式爲utf-8,否則會報錯:
        # UnicodeDecodeError: 'gbk' codec can't decode byte 0xbd in position 98: illegal multibyte sequence
        fp.write(sent)
        fp.close()

    # 根據root路徑構建所有的jar包路徑 -classpath
    def buildjars(self):
        # D:/stanford-corenlp/ejml-0.23.jar;D:/stanford-corenlp/javax.jason.jar;....;
        for jar in self.jarlist:
            self.jarlistpath += self.root + jar +";"
        print(self.jarlistpath)


    def delfile(self,path): # 刪除臨時文件
        os.remove(path)

新建一個StanfordPOSTagger_Interface.py

import os
from StanfordCoreNLP_Interface import StanfordCoreNLP_Interface
class StanfordPOSTagger_Interface(StanfordCoreNLP_Interface): # 詞性標註子類

    def __init__(self,root,modelpath): # 重寫構造函數
        StanfordCoreNLP_Interface.__init__(self, root)# 聲明StanfordCoreNLP構造函數,這樣就可以調用SCNLP的屬性了,如tempsrcpath。
        self.modelpath = modelpath # 模型文件路徑
        self.classfier = "edu.stanford.nlp.tagger.maxent.MaxentTagger" # 詞性標註主類
        self.delimiter = "/" # 標籤分隔符
        self.__buildcmd()

    def __buildcmd(self):# 將此方法定義爲私有

        self.cmdline = 'java -mx200m -classpath "'+self.jarlistpath +'" '+self.classfier+' -model "'+ self.modelpath + '" -tagSeparator ' + self.delimiter
        print(self.cmdline)

    # 標註文件
    def tagfile(self,sent,outpath):
        self.savefile(self.inputpath,sent)
        os.system(self.cmdline+' -textFile '+self.inputpath+' > ' +outpath)# 結果輸出到文件outpath中
        self.delfile(self.inputpath)

3.3 執行Stanford詞性標註

使用StanfordPOSTagger類來進行詞性標註。

from StanfordPOSTagger_Interface import StanfordPOSTagger_Interface

root  = 'D:/stanford-corenlp/'
modelpath = root + "models/pos-tagger/chinese-distsim/chinese-distsim.tagger"
st = StanfordPOSTagger_Interface(root,modelpath)
seg_sent = '在 包含 問題 的 所有 解 的 解空間 樹 中 , 按照 深度優先 搜索 的 策略 ,從 根節點  出發 深度 探索 解空間 樹 。 '
taglist = st.tagfile(seg_sent,"D:/result.txt")

查看D:/result.txt文件內容。

在/P 包含/VV 問題/NN 的/DEC 所有/DT 解/VV 的/DEC 解空間/NN 樹/NN 中/LC ,/PU 按照/P 深度優先/NN 搜索/NN 的/DEC 策略/NN ,從/NN 根節點/NN 出發/VV 深度/JJ 探索/NN 解空間/NN 樹/NN 。/PU

在StanfordPOSTagger的分詞結果中,“p”表示介詞;“NN”表示一般名詞;“LC”表示方位詞等,由於Ltp3.3和Stanford使用的詞性標籤不同,這裏對標註結果不做評估。後面章節中會對詞性標註做一個專門的評估。

4. 整合命名實體識別模塊

【命名實體識別】(Named Entity Recognition,NER)用於識別文本中具有特定意義 的實體,常見的實體主要包括人名、地名、機構名及其他專有名詞等。

本文將命名實體劃分在語義範疇的原因是,命名實體識別不僅需要標註詞的語法信息(名稱),更重要的是指示詞的語義信息(人名還是組織機構名等)。這裏所需要識別的命名實體一般不是指已知名詞(詞典種的登錄詞),而是指新詞(或稱未登錄詞)。

文本中新詞的湧現反映了人類詞彙的【能產性】。所謂能產性指基本詞(字)能夠構成其他新詞,但是這些新詞的產生並沒有規律性。應該說,不同的【義類】具有不同的規律;構成中國人名的統計規律,顯然區別於外文譯名,或其他專有名詞。新詞構成的概率按照義類的不同而有所不同。這是命名實體構成的普遍規律。
更具體地命名實體識別任務還要識別出文本中三大類(實體類、時間類和數字類)、七小類(人名、機構名、地名、時間、日期、貨幣和百分比)命名實體。

4.1 Ltp3.4命名實體識別

from pyltp import *

sent = "陝西 西安 是 一 座 古老 的 城市 。"
words = sent.split(" ")
postagger = Postagger()
postagger.load("D:/ltp_3.4/pos.model") # 導入詞性標註模塊
postags = postagger.postag(words)

recognizer = NamedEntityRecognizer()
recognizer.load("D:/ltp_3.4/ner.model") #導入命名標註模塊
netags = recognizer.recognize(words,postags)

for word,postag,netag in zip(words,postags,netags):
    print(word+"/"+postag+"/"+netag)

執行結果爲:

陝西/ns/B-Ns
西安/ns/E-Ns
是/v/O
一/m/O
座/q/O
古老/a/O
的/u/O
城市/n/O
。/wp/O

輸出例句中“/”爲分隔符,分隔符將結果按詞爲單位分爲三段。例如,第一段是詞“陝西”,第二段是詞性“ns”,第三段“B-Ns”就是識別的專有名詞。這裏的標籤“O”表示非專名,“S-Ns”表示地名。全部標籤在後米娜的章節中有詳細說明。

4.2 Stanford 命名實體識別

如果僅使用斯坦福的中文命名實體識別模塊(也稱爲NER),可從從http://nlp.stanford.edu/software/CRF-NER.shtml下載。與詞性標註的不同之處在於,命名實體識別模塊的程序和中文模型庫分開存放。應用程序可以從下圖所示處下載。在這裏插入圖片描述

斯坦福命名實體識別也可以通過Stanford-CoreNLP包運行。中文模型庫內使用了基於詞向量的語義相似度模型。運行該模型的前提條件需要首先進行中文分詞,然後將分詞結果(以空格分開的分詞文本)作爲輸入,再運行命名實體識別模塊進行輸出。
仿照詞性標註模塊,編寫一個Python命名實體類如下。

import os
from StanfordCoreNLP_Interface import StanfordCoreNLP_Interface
class StanfordNERTagger_Interface(StanfordCoreNLP_Interface):

    def __init__(self,root,modelpath):
        StanfordCoreNLP_Interface.__init__(self, root)
        self.modelpath = modelpath # 模型文件路徑
        self.classfier = "edu.stanford.nlp.ie.crf.CRFClassifier"
        self.__buildcmd()

    # 構建命令行
    def __buildcmd(self):
        self.cmdline = 'java -mx2000m -classpath "'+self.jarlistpath+'" '+self.classfier+' -model "'+self.modelpath+'"'

    # 標註文件,將標註的句子存儲文件
    def tagfile(self,sent,outpath):
        self.savefile(self.inputpath,sent)
        os.system(self.cmdline+' -textFile '+self.inputpath+' > '+outpath)
        self.delfile(self.inputpath)

執行代碼如下。

from StanfordNERTagger_Interface import StanfordNERTagger_Interface

root  = 'D:/stanford-corenlp/'
modelpath = root + 'models/ner/chinese.misc.distsim.crf.ser.gz'
st = StanfordNERTagger_Interface(root,modelpath)
seg_sent = '歐洲 東部 的 羅馬尼亞 , 首都 是 布加勒斯特 。 '
taglist = st.tagfile(seg_sent,"ner_test.txt")

輸出結果爲ner_test.txt,打開該文件可以看到執行結果爲。

歐洲/LOCATION 東部/O 的/O 羅馬尼亞/GPE ,/O 首都/O 是/O 布加勒斯特/GPE 。/O

Stanford 命名實體識別不需要詞性標註,因此輸出僅分爲兩段,標註集也比價簡單。這裏標籤“O”表示非專名,“LOC”表示地名。其他標籤會在後面章節中再做講解。

5. 整合句法解析模塊

【句法分析】(syntactic analysis)是根據給定的語法體系自動推導處句子的語法結構,分析句子所包含的語法單元和這些語法單元之間的關係,將句子轉化爲一個結構化的語法樹。

目前句法分析有兩種不同的理論:一種是【短語結構語法】;另一種是【依存語法】。

其中比較突出的是Ltp3.4中文句法分析系統,使用依存句法複分析理論。

除此之外,最著名的句法解析器是Stanford 句法解析器。截至2015年,Stanford句法數包含了如下三大主要解析器。

  • PCFG概率解析器。是一個高度優化的詞彙化PCFG依存解析器。該解析器使用A*算法,是一個隨機上下文無關文法解析器。除英語之外,該解析器還包含一箇中文版本,使用賓州中文樹庫訓練。解析器的輸出格式包含依存關係輸出和短語結構樹輸出。
  • Shift-Reduce解析器。爲了提高PCFG概率解析器的性能,Stanford提供了一個基於移進–歸約算法(Shift-Reduce)的高性能解析器。其性能遠高於任何PCFG解析器,而且精度上比其他任何版本(包括RNN)的解析器都更精準。
  • 神經網絡依存解析器。神經網絡依存解析器是深度學習算法在句法解析器中的一個重要應用。它通過中心詞和修飾詞之間的依存關係來構建出句子的句法樹。有關此方面的研究是目前NLP的研究重點。

本節延續第3節的系統,使用Ltp3.4和Stanford Parser來進行中文的文本解析。因爲Stanford的句法解析器比較多,這裏僅實現比較有代表性的PCFG解析器。

5.1 Ltp 3.4 句法依存樹

繼續使用Ltp3.4中文命名實體識別模塊。句法解析模塊的文件名爲parser.model。
爲了簡化,下面使用已經分好詞的單句進行演示。
新建名爲ltp_NER.py的文件。

import nltk
from nltk.tree import Tree # 導入nltk tree結構
from nltk.grammar import DependencyGrammar # 導入依存句法包
from nltk.parse import *
from pyltp import * # 導入ltp應用包
import re

sent = "陝西 西安 是 一 座 古老 的 城市 。"
# sent = "羅馬尼亞 的 首都 是 布加勒斯特 。 "
words = sent.split(" ")

postagger = Postagger()
postagger.load("D:/ltp_3.4/pos.model") # 導入詞性標註模塊
postags = postagger.postag(words)
print(len(postags))

parser = Parser() # 將詞性標註和分詞結果都加入分析器中進行句法分析
parser.load("D:/ltp_3.4/parser.model")
arcs = parser.parse(words,postags)

print(type(arcs))
arclen = len(arcs)
print(arclen)
conll = ""
for i in range(arclen): # 構建Conll標準的數據結構
    if arcs[i].head == 0:
        arcs[i].relation = "ROOT"
    conll += "\t"+words[i]+"("+postags[i]+")"+"\t"+postags[i]+"\t"+str(arcs[i].head)+"\t"+arcs[i].relation+"\n"
print(conll)
conlltree = DependencyGraph(conll) # 轉換爲句法依存圖
tree = conlltree.tree() # 構建樹結構
tree.draw() # 顯示輸出的樹

顯示的結果爲

9
<class ‘pyltp.VectorOfParseResult’>
9
陝西(ns) ns 2 ATT
西安(ns) ns 3 SBV
是(v) v 0 ROOT
一(m) m 5 ATT
座(q) q 8 ATT
古老(a) a 8 ATT
的(u) u 6 RAD
城市(n) n 3 VOB
。(wp) wp 3 WP

在這裏插入圖片描述
上面的代碼中的文本 “陝西 西安 是 一 座 古老 的 城市 。”,而如果換成是文本 "羅馬尼亞 的 首都 是 布加勒斯特 。 " 就會報錯。報錯內容如下。

0
<class ‘pyltp.VectorOfParseResult’>
Traceback (most recent call last):
0
File “D:/PycharmProjects/NLP/ltp_syntacticAnalysis.py”, line 30, in <module>
tree = conlltree.tree() # 構建樹結構
File “D:\Anaconda3\envs\Anaconda-Pycharm\lib\site-packages\nltk\parse\dependencygraph.py”, line 413, in tree
word = node[‘word’]
TypeError: ‘NoneType’ object is not subscriptable
進程已結束,退出代碼 1

錯誤意思是詞性標註的內容爲空,句法解析的內容更爲空,所以導致無法對空對象生成句法依存樹。錯誤原因可能是ltp對於這句話的部分詞彙的未收錄(猜的),導致無法詞性標註,以及句法依存。

如上面正確的結果所示,將單句"陝西 西安 是 一 座 古老 的 城市 。"的解析結果給出如下結論:句法樹是一棵依存關係樹,根節點爲其謂語動詞“是”,主語是“西安”,“陝西”是修飾“西安”的定語,句子的賓語是“一座古老的城市”。

5.2 Stanford Parser 類

如果僅使用斯坦福的中文句法解析模塊(也稱爲Parser),可從http://nlp.stanford.edu/software/lex-parser.shtml下載。語命名實體識別相同,Parser模塊地程序和中文模型庫也分開存放,但都在一處下載。應用程序和模型都可從圖中所示的截圖處下載。

這裏仍舊使用stanford-corenlp中的模型和程序。在圖目錄列表中對應不同的句法解析器有不同的模型目錄,分別爲:lexparser、parser和srparser。其中PCFG概率解析器模型爲lexparser;Shift-Reduce解析器模型爲srparser;神經網絡依存解析器模型爲parser。以lexparser爲例,下圖所示的5個模型分別用於lexparser解析器的調用,最常用的庫是chinesePCFG.ser.gz文件。該文件支持高度精確的詞彙解析器。
圖1 Stanford句法依存模型文件
最後,仿照前兩節,編寫一個python的StanfordParser接口類如下。
新建StanfordParser_Interface.py

from StanfordCoreNLP_Interface import StanfordCoreNLP_Interface
import os
class StanfordParser(StanfordCoreNLP_Interface):
    def __init__(self,modelpath,jarpath,opttype):
        StanfordCoreNLP_Interface.__init__(self,jarpath)
        self.modelpath = modelpath # 模型文件路徑
        self.classfier = "edu.stanford.nlp.parser.lexparser.LexicalizedParser"
        self.opttype = opttype
        self.__buildcmd()

    # 構建命令行
    def __buildcmd(self):
        self.cmdline = 'java -mx500m -classpath "'+self.jarlistpath+'" '+self.classfier+' -outputFormat "'+self.opttype+'" '+self.modelpath+' '

     # 解析句子輸出到文件
    def tagfile(self,sent,outputpath):
        self.savefile(self.inputpath,sent)
        os.system(self.cmdline+self.inputpath+' > '+outputpath)
        fp = open(outputpath,'r',encoding='utf-8')
        result = fp.read()
        print(type(result))
        print(result)
        self.delfile(self.inputpath)
        return result

5.3 Stanford 短語結構樹

以短語結構的方式輸出,內容如下。

from nltk.tree import Tree # 導入nltk庫
from stanford import *
from StanfordParser_Interface import StanfordParser

root = "D:/stanford-corenlp/"
modelpath = root+'models/lexparser/chinesePCFG.ser.gz'
opttype = 'penn' # 賓州樹庫格式
parser = StanfordParser(modelpath,root,opttype)
seg_sent = "羅馬尼亞 的 首都 是 布加勒斯特 。 "
print(type(seg_sent))
result = parser.tagfile(seg_sent,"result.txt")
print(result)
tree = Tree.fromstring(result)
tree.draw()

輸出結果如下:

(ROOT
  (IP
    (NP
      (DNP
        (NP (NR 羅馬尼亞))
        (DEG 的))
      (NP (NN 首都)))
    (VP (VC 是)
      (NP (NR 布加勒斯特)))
    (PU 。)))

在這裏插入圖片描述
如圖所示,短語結構樹的葉子節點是每個詞的詞形,詞形上一級節點是詞性,再上一級節點是由多個詞構成的短語組塊,“NP”這裏表示名詞性短語,“DNP”是由NP+“的”構成的短語結構,“VP”是動詞短語,“IP”是簡單句。

5.4 Stanford 依存句法樹

以依存句法的方式輸出,內容如下。

from StanfordParser_Interface import StanfordParser
root  = "D:/stanford-corenlp/"
modelpath = root+'models/lexparser/chinesePCFG.ser.gz'
opttype = 'typedDependencies' # "penn,typedDependencies"
parser = StanfordParser(modelpath,root,opttype)
result = parser.tagfile("羅馬尼亞 的 首都 是 布加勒斯特 。 ",'result.txt')
print(result)

輸出結果如下。

nmod:assmod(首都-3, 羅馬尼亞-1)
case(羅馬尼亞-1, 的-2)
nsubj(布加勒斯特-5, 首都-3)
cop(布加勒斯特-5, 是-4)
root(ROOT-0, 布加勒斯特-5)
punct(布加勒斯特-5, 。-6)

Stanford依存樹庫的標註系統與Ltp的標註系統不同,具有更爲複雜的依存結構。“root”是根節點,一般對應一個謂語(VP+NP),這裏對應着的“是”,它是一個“cop”——系表助動詞結構。因此,用它後面的NP結構作爲根節點,剩下的部分就很容易了。“nsubj”表示名詞性主語,在這個主語的內部,“首都”被“羅馬尼亞”修飾,“assmod”表示關聯修飾,“的”依存於“羅馬尼亞”,“case”表示句法依賴。

這個結果與Ltp的分析結果略有不同,這是由標註規範的不一致導致的。有關標註規範的更多細節,第 6 章將給出具體說明。

6. 整合語義角色標註模塊

語義角色標註(Semantic Role Labeling,SRL)來源於20世紀60年代美國語言學家菲爾墨提出的格語法理論。在菲爾墨看來,格關係是句子深層結構中的名詞和謂語動詞之間的一種固定不變的語義結構古關係(謂詞——論元關係),而這種關係和具體語言中的表層結構上的語法結構沒有一一對應關係。換句話說,由同樣爲此——論元結構支配的,但語法結構不同的句子,其語義應該是相同的。

該理論是在句子語義理解上的一個重要突破。基於此理論,語義角色標註就發展起來了,併成爲句子語義分析的一種重要方式。它採用“謂詞——論元角色”的結構形式,標註句法成分相對於給定謂語動詞的語義角色,每個語義角色被賦予一定的語義。

美國賓州大學已經開發處一個對使用價值的表示語義命題庫,成爲PropBank。在本書的後續章節將會對該主題庫做一個全面和深入的分析。

語義角色標註系統已經處於NLP系統的末端,其精度和效率都收前面幾個模塊的影響,所以,當前系統的精度都不高,在中文良玉還沒有投入商業應用過的成功案例,本節介紹的是Ltp3.4中文語義角色標註系統。

報錯 1

labeller.load(os.path.join(MODELDIR,“pisrl.model”))# 加載語義角色標註模型
RuntimeError: incompatible native format - size of long

解決辦法

在Ltp版本爲3.4.0的模型文件,其中一個文件pisrl.model,如在windows系統下不可用,可以到 此鏈接 下載支持windows的語義角色標註模型文件pisrl_win.model,放到ltp_3.4的目錄下。也可以到此鏈接http://ltp.ai/download.html下找到。

報錯 2

roles = labeller.label(words,postags,netags,arcs)# 設置命名標註對象

報錯內容如下。

Traceback (most recent call last):
  File "D:/PycharmProjects/NLP/ltp_SRL.py", line 33, in <module>
    roles = labeller.label(wordlist,postags,netags,arcs)# 設置命名標註對象
Boost.Python.ArgumentError: Python argument types in
    SementicRoleLabeller.label(SementicRoleLabeller, list, VectorOfString, VectorOfString, VectorOfParseResult)
did not match C++ signature:
    label(struct SementicRoleLabeller {lvalue}, class boost::python::list, class boost::python::list, class std::vector<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > >)
    label(struct SementicRoleLabeller {lvalue}, class boost::python::list, class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >, class std::vector<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > >)
    label(struct SementicRoleLabeller {lvalue}, class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >, class boost::python::list, class std::vector<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > >)
    label(struct SementicRoleLabeller {lvalue}, class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >, class std::vector<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::allocator<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > >, class std::vector<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > >)

這個報錯的內容極其可恨啊,我想了4個小時終於得以解決,我在網上找了很長時間,都是直接跳過這個問題的解決辦法不回答,或者沒有遇到這個問題。

問題原因

根據原書(《NLP漢語自然語言原理處理與實踐》)上的代碼我進行重現,然而,這裏面有一個巨大的坑,待我慢慢道來。

首先是前面2.1節處的步驟3中的提到的pyltp安裝包目錄,這裏再提供一次圖片。
在這裏插入圖片描述
這個文件中src文件夾中有一個至關重要的文件pyltp.cpp這是一個C++類型的文件,是wrap.cpp文件。想學的話參考 此鏈接,但這不是重點。文件中有一段內容 line 285~344 ,關於SementicRoleLabeller,如下。

struct SementicRoleLabeller {
  SementicRoleLabeller()
    : loaded(false) {}

  void load(const std::string& model_path) {
    loaded = (srl_load_resource(model_path) == 0);
  }

  std::vector<SementicRole> label(
      const std::vector<std::string>& words,
      const std::vector<std::string>& postags,
      const std::vector<ParseResult>& parse
      ) {
    std::vector<SementicRole> ret;

    // Some trick
    std::vector<ParseResult> tmp_parse(parse);
    for (std::size_t i = 0; i < tmp_parse.size(); ++ i) {
      tmp_parse[i].first --;
    }
    if (!loaded) {
      std::cerr << "SRL: Model not loaded!" << std::endl;
    } else {
      srl_dosrl(words, postags, tmp_parse, ret);
    }
    return ret;
  }

  std::vector<SementicRole> label(
      const std::vector<std::string>& words,
      const boost::python::list& postags,
      const std::vector<ParseResult>& parse
      ) {
    return label(words, py_list_to_std_vector<std::string>(postags), parse);
  }

  std::vector<SementicRole> label(
      const boost::python::list& words,
      const std::vector<std::string>& postags,
      const std::vector<ParseResult>& parse
      ) {
    return label(py_list_to_std_vector<std::string>(words), postags, parse);
  }

  std::vector<SementicRole> label(
      const boost::python::list& words,
      const boost::python::list& postags,
      const std::vector<ParseResult>& parse
      ) {
    return label(py_list_to_std_vector<std::string>(words), py_list_to_std_vector<std::string>(postags), parse);
  }

  void release() {
    if (loaded) {
      srl_release_resource();
    }
  }

  bool loaded;
};

其中有四個重構函數,注意了,參數都是(words,postags,parse),我們在執行roles = labeller.label(words,postags,netags,arcs)# 設置命名標註對象的過程中就是調用這個Wrap代碼,然而參數不對應,這就是爲什麼錯了(心裏一萬個FUCK,這誰寫的Wrap,不寫完整,真FUCK,認真點好不好???絕對不會再用Ltp做NLP工作,FUCK)。

解決辦法

刪除netags這個參數。

執行代碼如下。

import os
from pyltp import *
MODELDIR = "D:/ltp_3.4/"
sentence = "歐洲東部的羅馬尼亞,首都是布加勒斯特,也是一座世界性的城市。"

segmentor = Segmentor() # 實例化分詞模塊
segmentor.load(os.path.join(MODELDIR,"cws.model"))# 加載專有名詞詞典
print(os.path.join(MODELDIR,"cws.model"))
words = segmentor.segment(sentence)# 設置分詞對象
print(words)

wordlist = list(words) # 生成器變爲列表元素

postagger = Postagger() # 實例化詞性標註模塊
postagger.load(os.path.join(MODELDIR,"pos.model"))# 加載詞性標註模型
postags = postagger.postag(words)# 設置詞性標註對象
print(postags)

parser = Parser()# 實例化句法分析模塊
parser.load(os.path.join(MODELDIR,'parser.model'))# 加載句法分析模型
arcs = parser.parse(words,postags)# 設置句法分析對象
print(arcs)

recognizer = NamedEntityRecognizer()#實例化命名標註模塊
recognizer.load(os.path.join(MODELDIR,"ner.model"))# 加載命名標註模型
netags = recognizer.recognize(words,postags)# 設置命名標註對象
print(netags)

labeller = SementicRoleLabeller()# 實例化語義角色標註模塊
labeller.load(os.path.join(MODELDIR,"pisrl_win.model"))# 加載語義角色標註模型
print(os.path.join(MODELDIR,"pisrl_win.model"))
roles = labeller.label(words,postags,arcs)# 設置命名標註對象
print(roles)

# 輸出標註結果
for role in roles:
    print('rel:',wordlist[role.index]) # 謂詞
    for arg in role.arguments:
        if arg.range.start != arg.range.end:
            print(arg.name,"".join(wordlist[arg.range.start:arg.range.end]))
        else:
            print(arg.name,wordlist[arg.range.start])

執行結果如下。

rel: 是
A0 歐洲東部的羅馬尼亞
A0 首都
A1 布加勒斯特
rel: 是
A0 歐洲東部的羅馬尼亞
A0 首都
ADV 也
A1 一座世界性的

這裏“rel”標籤表示的是謂詞,“A0”指動作的施事,“A1”指動作的受事。關於其他標籤,再後面章節會專門講解。

7. 結語

本章節終於暫時結束,千里之行,始於足下,歡迎評論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章