轉載自:http://www.zmonster.me/2016/06/08/use-stanford-nlp-package-in-nltk.html
NLTK 與 Stanford NLP
NLTK 是一款著名的 Python 自然語言處理(Natural Language Processing, NLP)工具包,在其收集的大量公開數據集、模型上提供了全面、易用的接口,涵蓋了分詞、詞性標注(Part-Of-Speech tag, POS-tag)、命名實體識別(Named Entity Recognition, NER)、句法分析(Syntactic Parse)等各項 NLP 領域的功能。
Stanford NLP 是由斯坦福大學的 NLP 小組開源的 Java 實現的 NLP 工具包,同樣對 NLP 領域的各個問題提供了解決辦法。
斯坦福大學的 NLP 小組是世界知名的研究小組,如果能將 NLTK 和 Stanford NLP 這兩個工具包結合起來使用,那自然是極好的!在 2004 年 Steve Bird 在 NLTK 中加上了對 Stanford NLP 工具包的支持,通過調用外部的 jar 文件來使用 Stanford NLP 工具包的功能。
從 NLTK 的 commit 歷史中可以找到相應的提交記錄:
commit e1372fef56bfb88d02fdb6c0ea88474d5f414a38 Author: Steven Bird <stevenbird1@gmail.com> Date: Tue Aug 3 12:20:20 2004 +0000 added Stanford svn/trunk@2088
現在的 NLTK 中,通過封裝提供了 Stanford NLP 中的以下幾個功能:
- 分詞
- 詞性標注
- 命名實體識別
- 句法分析,依存句法分析
安裝和配置
NLTK 3.2 之后加入了用於中文分詞的 StanfordSegmenter 這個類,作者是知名 NLP 博主 52nlp,見 相關文章。而 NLTK 3.1 及之前則只有以下幾個類:
- 分詞: StanfordTokenizer
- 詞性標注: StanfordPOSTagger
- 命名實體識別: StanfordNERTagger
- 句法分析: StanfordParser
- 依存句法分析: StanfordDependencyParser, StanfordNeuralDependencyParser
方便起見,本文以 NLTK 3.2 這個版本來說明如何進行相關的安裝和配置,3.1 及之前的版本基本相同。
注意事項
需要注意這么幾點:
- Stanford NLP 工具包自 2014 年 10 月后(大概是 version 3.5.0)需要 Java 8 及之后的版本,如果出錯請檢查 Java 版本
- 下面的配置過程都以 Stanford NLP 3.6.0 為例,如果使用的是其他版本,請注意替換相應的文件名
- 下面的配置過程以 NLTK 3.2 為例,如果使用 NLTK 3.1,需要注意該舊版本中 StanfordSegmenter 未實現,其余大致相同
-
下面的配置過程是針對不同的接口分別講述各自如何配置,根據來自 NLTK 的源代碼,分別是
- nltk/tokenize/stanford.py
- nltk/tag/stanford.py
- nltk/parse/stanford.py
如果不想了解這些細節,可以參考 NLTK 官方 wiki 頁面上的內容,但需要注意的是,StanfordSegmenter 和 StanfordNeuralDependencyParser 這兩者的配置和其他的都不一樣,而 wiki 頁面上並未覆蓋到這部分內容。
- 事實上,也可以完全不進行環境變量設置,但這就需要在每次調用的時候手動指定參數
StanfordSegmenter
- 從 http://nlp.stanford.edu/software/segmenter.html 處下載 stanford-segmenter-2015-12-09.zip (version 3.6.0)
- 將 stanford-segmenter-2015-12-09.zip 解壓, 並將解壓目錄中的 stanford-segmenter-3.6.0.jar 拷貝為 stanford-segmenter.jar
-
將 stanford-segmenter.jar 和 slf4j-api.jar 加入到 CLASSPATH 中去
例:
export STANFORD_SEGMENTER_PATH="$HOME/stanford/segmenter" export CLASSPATH="$CLASSPATH:$STANFORD_SEGMENTER_PATH/stanford-segmenter.jar:$STANFORD_SEGMENTER_PATH/slf4j-api.jar"
之所以要將 stanford-segmenter.jar 和 slf4j-api.jar 加入到 CLASSPATH 中,是因為在 StanfordSegmenter 的實現中顯式依賴了這兩個文件,並且優先在 CLASSPATH 中尋找這兩個文件。如果在 CLASSPATH 中找不到 stanford-segmenter.jar ,則會在環境變量 STANFORD_SEGMENTER 指定的路徑中尋找;同樣的,如果找不到 slf4j-api.jar ,則會在環境變量 SLF4J 指定的路徑中尋找。其他幾個類也有同樣的依賴設置,為了統一管理,可以將所有依賴的 jar 文件都加入到 CLASSPATH 中去,當然分別為不同的 jar 文件設置不同的環境變量也是可以的。
除了設置環境變量,也可以通過函數參數來傳入依賴的 jar 文件的准確路徑,此時將會忽略環境變量設置。
StanfordTokenizer
- 從 http://nlp.stanford.edu/software/tagger.html 中下載 stanford-postagger-full-2015-12-09.zip (version 3.6.0)
- 將 stanford-postagger-full-2015-12-09.zip 解壓
-
將解壓目錄中的 stanford-postagger.jar 加入到 CLASSPATH 中,或者設置到環境變量 STANFORD_POSTAGGER 中
export STANFORD_POSTAGGER_PATH="$HOME/stanford/postagger" export CLASSPATH="$CLASSPATH:$STANFORD_POSTAGGER_PATH/stanford-postagger.jar"
或
export STANFORD_POSTAGGER="$HOME/stanford/postagger/stanford-postagger.jar"
StanfordNERTagger 和 StanfordPOSTagger
在 NLTK 里,StanfordNERTagger 和 StanfordPOSTagger 都繼承自 StanfordTagger ,在設置上有共同之處,因此放到一起來講一下。
- 從 http://nlp.stanford.edu/software/CRF-NER.html 處下載 stanford-ner-2015-12-09.zip(version 3.6.0)
- 從 http://nlp.stanford.edu/software/tagger.html 中下載 stanford-postagger-full-2015-12-09.zip (version 3.6.0)
- 將 stanford-ner-2015-12-09.zip 和 stanford-postagger-full-2015-12-09.zip 都解壓
-
將解壓后目錄中的 stanford-ner.jar 和 stanford-postagger.jar 加入到 CLASSPATH 中去,和 StanfordTokenizer 不一樣,這兩個類都只從 CLASSPATH 中尋找對應的 jar 文件(所以為了統一我建議都添加到 CLASSPATH 中去)
export STANFORD_NER_PATH="$HOME/stanford/ner" export STANFORD_POSTAGGER_PATH="$HOME/stanford/postagger" export CLASSPATH="$CLASSPATH:$STANFORD_NER_PATH/stanford-ner.jar:$STANFORD_POSTAGGER_PATH/stanford-postagger.jar"
同時將 stanford-ner-2015-12-09.zip 解壓后目錄中的 classifiers 目錄和 stanford-postagger-full-2015-12-09.zip 解壓后目錄中的 models 目錄加入到環境變量 STANFORD_MODELS 中去
export STANFORD_MODELS="$STANFORD_NER_PATH/classifiers:$STANFORD_POSTAGGER_PATH/models"
StanfordParser, StanfordDependencyParser
StanfordParser 和 StanfordDependencyParser 都繼承自 GenericStanfordParser ,使用 stanford-parser.jar 來提供句法分析功能。
- 從 http://nlp.stanford.edu/software/lex-parser.html 處下載 stanford-parser-full-2015-12-09.zip (version 3.6.0)
-
將下載的壓縮包解壓,並將其中的 stanford-parser.jar 和 stanford-parser-3.6.0-models.jar(這個在不同版本中名稱會不一樣) 都加入到 CLASSPATH 中
export STANFORD_PARSER_PATH="$HOME/stanford/parser" export CLASSPATH="$CLASSPATH:$STANFORD_PARSER_PATH/stanford-parser.jar:$STANFORD_PARSER_PATH/stanford-parser-3.6.0-models.jar"
或者將 stanford-parser.jar 加入到環境變量 STANFORD_PARSER 中,將 stanford-parser-3.6.0-models.jar 加入到環境變量 STANFORD_MODELS 中
export STANFORD_PARSER="$STANFORD_PARSER_PATH/stanford-parser.jar" export STANFORD_MODELS="$STANFORD_MODELS:$STANFORD_PARSER_PATH/stanford-parser-3.6.0.models.jar"
StanfordNeuralDependencyParser
StanfordNeuralDependencyParser 雖然也繼承自 GenericStanfordParser,並且用來進行句法分析,但它使用的 Stanford CoreNLP 中的功能和模型,不依賴 Stanford Parser 這個(子)工具包。
- 從 http://stanfordnlp.github.io/CoreNLP/ 處下載 stanford-corenlp-full-2015-12-09.zip
-
將下載的壓縮包解壓,並將其中的 stanford-corenlp-3.6.0.jar 和 stanford-corenlp-3.6.0-models.jar 加入到 CLASSPATH 中去
export STANFORD_CORENLP_PATH="$HOME/stanford-corenlp-full-2015-12-09" export CLASSPATH="$CLASSPATH:$STANFORD_CORENLP_PATH/stanford-corenlp-3.6.0.jar:$STANFORD_CORENLP_PATH/stanford-corenlp-3.6.0-models.jar"
或者可以更簡單地將解壓目錄設置為環境變量 STANFORD_CORENLP 的值
export STANFORD_CORENLP=$STANFORD_CORENLP_PATH
基本使用
使用 StanfordSegmenter 和 StanfordTokenizer 進行分詞
StanfordSegmenter 是 52nlp 實現的對 Stanford Segmenter 的封裝,用來進行中文分詞。
# coding: utf-8 from nltk.tokenize import StanfordSegmenter segmenter = StanfordSegmenter( path_to_sihan_corpora_dict="/home/linusp/stanford/segmenter/data/", path_to_model="/home/linusp/stanford/segmenter/data/pku.gz", path_to_dict="/home/linusp/stanford/segmenter/data/dict-chris6.ser.gz" ) res = segmenter.segment(u"北海已成為中國對外開放中升起的一顆明星") print type(res) print res.encode('utf-8')
<type 'unicode'> 北海 已 成為 中國 對外開放 中 升起 的 一 顆 明星
StanfordSegmenter 的初始化參數說明:
-
path_to_jar: 用來定位 stanford-segmenter.jar ,在設置了 CLASSPATH 的情況下,該參數可留空
注: 其他所有 Stanford NLP 接口都有 path_to_jar 這個參數,同樣在設置了環境變量的情況下可以留空,后面不再另加說明。
- path_to_slf4j: 用來定位 slf4j-api.jar ,在設置了 CLASSPATH 或者 SLF4J 這個環境變量的情況下,該參數可留空
- path_to_sihan_corpora_dict: 設定為 stanford-segmenter-2015-12-09.zip 解壓后目錄中的 data 目錄,話說這個參數名真是讓人摸不着頭腦
- path_to_model: 用來指定用於中文分詞的模型,在 stanford-segmenter-2015-12-09 的 data 目錄下,有兩個可用模型 pkg.gz 和 ctb.gz
需要注意的是,使用 StanfordSegmenter 進行中文分詞后,其返回結果並不是 list ,而是一個字符串,各個漢語詞匯在其中被空格分隔開。
StanfordTokenizer 可以用來進行英文的分詞,使用起來比較簡單
# coding: utf-8 from nltk.tokenize import StanfordTokenizer tokenizer = StanfordTokenizer() sent = "Good muffins cost $3.88\nin New York. Please buy me\ntwo of them.\nThanks." print tokenizer.tokenize(sent)
[u'Good', u'muffins', u'cost', u'$', u'3.88', u'in', u'New', u'York', u'.', u'Please', u'buy', u'me', u'two', u'of', u'them', u'.', u'Thanks', u'.']
使用 StanfordNERTagger 進行命名實體識別
所謂命名實體識別,是用來識別並標注文本中的人名、地名、組織機構名等單元,這些單元既是 "命名實體"。
# coding: utf-8 from nltk.tag import StanfordNERTagger eng_tagger = StanfordNERTagger('english.all.3class.distsim.crf.ser.gz') print eng_tagger.tag('Rami Eid is studying at Stony Brook University in NY'.split())
[(u'Rami', u'PERSON'), (u'Eid', u'PERSON'), (u'is', u'O'), (u'studying', u'O'), (u'at', u'O'), (u'Stony', u'ORGANIZATION'), (u'Brook', u'ORGANIZATION'), (u'University', u'ORGANIZATION'), (u'in', u'O'), (u'NY', u'O')]
StanfordNERTagger 在初始化時需要指定所使用的模型,在 stanford-ner-2015-12-09.zip 解壓后的 classifiers 目錄中,有幾個可用的英語 NER 模型:
/home/linusp/stanford/ner/classifiers/ ├── english.all.3class.distsim.crf.ser.gz ├── english.all.3class.distsim.prop ├── english.conll.4class.distsim.crf.ser.gz ├── english.conll.4class.distsim.prop ├── english.muc.7class.distsim.crf.ser.gz ├── english.muc.7class.distsim.prop ├── example.serialized.ncc.ncc.ser.gz └── example.serialized.ncc.prop
如果需要進行中文的命名實體識別,則可以在 Stanford Named Entity Recognizer 頁面的 Models 一節找到中文模型的下載鏈接,下載得到 stanford-chinese-corenlp-2015-12-08-models.jar ,解壓后將 edu/stanford/nlp/models/ner/ 目錄下的 chinese.misc.distsim.crf.ser.gz 和 chinese.misc.distsim.prop 復制到模型目錄下(stanford-ner-2015-12-09/classifiers)即可。
# coding: utf-8 from nltk.tag import StanfordNERTagger chi_tagger = StanfordNERTagger('chinese.misc.distsim.crf.ser.gz') sent = u'北海 已 成為 中國 對外開放 中 升起 的 一 顆 明星' for word, tag in chi_tagger.tag(sent.split()): print word.encode('utf-8'), tag
北海 GPE 已 O 成為 O 中國 GPE 對外開放 O 中 O 升起 O 的 O 一 O 顆 O 明星 O
使用 StanfordPOSTagger 進行詞性標注
所謂詞性標注,是根據句子中的上下文信息,給句中每個詞確定一個最為合適的詞性標記,比如動詞、名詞、人稱代詞等。
和 StanfordNERTagger 一樣,StanfordPOSTagger 需要的輸入也是一個已經分好詞的句子,下面是一個英文的詞性標注實例:
from nltk.tag import StanfordPOSTagger eng_tagger = StanfordPOSTagger('english-bidirectional-distsim.tagger') print eng_tagger.tag('What is the airspeed of an unladen swallow ?'.split())
[(u'What', u'WP'), (u'is', u'VBZ'), (u'the', u'DT'), (u'airspeed', u'NN'), (u'of', u'IN'), (u'an', u'DT'), (u'unladen', u'JJ'), (u'swallow', u'VB'), (u'?', u'.')]
如果之前配置時下載的是 stanford-postagger-full-xxxx-xx-xx.zip ,在解壓后,其中的 models 目錄是包含有兩個中文模型的,分別是 chinese-distsim.tagger 和 chinese-nodistsim.tagger ,可以直接使用。
# coding: utf-8 from nltk.tag import StanfordPOSTagger chi_tagger = StanfordPOSTagger('chinese-distsim.tagger') sent = u'北海 已 成為 中國 對外開放 中 升起 的 一 顆 明星' for _, word_and_tag in chi_tagger.tag(sent.split()): word, tag = word_and_tag.split('#') print word.encode('utf-8'), tag
北海 NR 已 AD 成為 VV 中國 NR 對外開放 NN 中 LC 升起 VV 的 DEC 一 CD 顆 M 明星 NN
這個中文的詞性標注輸出的結果有點奇怪……
使用 StanfordParser 進行句法分析
句法分析在分析單個詞的詞性的基礎上,嘗試分析詞與詞之間的關系,並用這種關系來表示句子的結構。實際上,句法結構可以分為兩種,一種是短語結構,另一種是依存結構。前者按句子順序來提取句法結構,后者則按詞與詞之間的句法關系來提取句子結構。這里說的句法分析得到的是短語結構。
from nltk.parse.stanford import StanfordParser eng_parser = StanfordParser(model_path=u'edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz') print list(eng_parser.parse("the quick brown fox jumps over the lazy dog".split()))
[Tree('ROOT', [Tree('NP', [Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['quick']), Tree('JJ', ['brown']), Tree('NN', ['fox'])]), Tree('NP', [Tree('NP', [Tree('NNS', ['jumps'])]), Tree('PP', [Tree('IN', ['over']), Tree('NP', [Tree('DT', ['the']), Tree('JJ', ['lazy']), Tree('NN', ['dog'])])])])])])]
得到的結果是一個 list, 其中的元素是 Tree 類型的,在上面這個例子中,這個 list 的長度是 1 ,調用 Tree 的 draw 方法可以將句法樹繪制出來。
要進行中文的句法分析,只要指定好中文的模型就好,可用的中文模型有兩個,分別是 'edu/stanford/nlp/models/lexparser/chinesePCFG.ser.gz' 和 'edu/stanford/nlp/models/lexparser/chineseFactored.ser.gz',依然拿 "_北海 已 成為 中國 對外開放 中 升起 的 一 顆 明星_" 這句話作為例子,得到的句法樹如下所示。
使用 StanfordDependencyParser 進行依存句法分析
見上一節,依存句法分析得到的是句子的依存結構。
from nltk.parse.stanford import StanfordDependencyParser eng_parser = StanfordDependencyParser(model_path=u'edu/stanford/nlp/models/lexparser/englishPCFG.ser.gz') res = list(eng_parser.parse("the quick brown fox jumps over the lazy dog".split())) for row in res[0].triples(): print row
((u'fox', u'NN'), u'det', (u'the', u'DT')) ((u'fox', u'NN'), u'amod', (u'quick', u'JJ')) ((u'fox', u'NN'), u'amod', (u'brown', u'JJ')) ((u'fox', u'NN'), u'dep', (u'jumps', u'NNS')) ((u'jumps', u'NNS'), u'nmod', (u'dog', u'NN')) ((u'dog', u'NN'), u'case', (u'over', u'IN')) ((u'dog', u'NN'), u'det', (u'the', u'DT')) ((u'dog', u'NN'), u'amod', (u'lazy', u'JJ'))
繪制出來的依存句法結構如下圖所示。
中文的依存句法分析同理,在初始化時使用中文模型即可,不再贅述。
StanfordNeuralDependencyParser 的使用與 StanfordDependencyParser 一樣,但是在本人的機器上執行非常耗時,即使是對一些簡單句子,所以這里就不略過不講了。