- 原文地址:Natural Language Processing Made Easy – using SpaCy (in Python)
- 原文作者:Shivam Bansal
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:lsvih
- 校對者:yzgyyang,sqrthree
使用 Python+spaCy 進行簡易自然語言處理
簡介
自然語言處理(NLP)是人工智慧領域最重要的部分之一。它在許多智慧應用中擔任了關鍵的角色,例如聊天機器人、正文提取、多語翻譯以及觀點識別等應用。業界 NLP 相關的公司都意識到了,處理非結構文字資料時,不僅要看正確率,還需要注意是否能快速得到想要的結果。
NLP 是一個很寬泛的領域,它包括了文字分類、實體識別、機器翻譯、問答系統、概念識別等子領域。在我最近的一篇文章中,我探討了許多用於實現 NLP 的工具與元件。在那篇文章中,我更多的是在描述NLTK(Natural Language Toolkit)這個偉大的庫。
在這篇文章中,我會將 spaCy —— 這個現在最強大、最先進的 NLP python 庫分享給你們。
內容提要
- spaCy 簡介及安裝方法
-
spaCy 的管道與屬性
- Tokenization
- 詞性標註
- 實體識別
- 依存句法分析
- 名詞短語
-
整合詞向量計算
- 使用 spaCy 進行機器學習
- 與 NLTK 和 CoreNLP 對比
1. spaCy 簡介及安裝方法
1.1 簡介
spaCy 由 cython(Python 的 C 語言擴充,旨在讓 python 程式達到如同 C 程式一樣的效能)編寫,因此它的執行效率非常高。spaCy 提供了一系列簡潔的 API 方便使用者使用,並基於已經訓練好的機器學習與深度學習模型實現底層。
1.2 安裝
spaCy 及其資料和模型可以通過 pip 和安裝工具輕鬆地完成安裝。使用下面的命令在電腦中安裝 spaCy:
sudo pip install spacy複製程式碼
如果你使用的是 Python3,請用 “pip3” 代替 “pip”。
或者你也可以在 這兒 下載原始碼,解壓後執行下面的命令安裝:
python setup.py install複製程式碼
在安裝好 spacy 之後,請執行下面的命令以下載所有的資料集和模型:
python -m spacy.en.download all複製程式碼
一切就緒,現在你可以自由探索、使用 spacy 了。
2. spaCy 的管道(Pipeline)與屬性(Properties)
spaCy 的使用,以及其各種屬性,是通過建立管道實現的。在載入模型的時候,spaCy 會將管道建立好。在 spaCy 包中,提供了各種各樣的模組,這些模組中包含了各種關於詞彙、訓練向量、語法和實體等用於語言處理的資訊。
下面,我們會載入預設的模組(english-core-web 模組)。
import spacy
nlp = spacy.load(“en”)複製程式碼
“nlp” 物件用於建立 document、獲得 linguistic annotation 及其它的 nlp 屬性。首先我們要建立一個 document,將文字資料載入進管道中。我使用了來自貓途鷹網的旅店評論資料。這個資料檔案可以在這兒下載。
document = unicode(open(filename).read().decode(`utf8`))
document = nlp(document)複製程式碼
這個 document 現在是 spacy.english 模型的一個 class,並關聯上了許多的屬性。可以使用下面的命令列出所有 document(或 token)的屬性:
dir(document)
>> [ `doc`, `ents`, … `mem`]複製程式碼
它會輸出 document 中各種各樣的屬性,例如:token、token 的 index、詞性標註、實體、向量、情感、單詞等。下面讓我們會對其中的一些屬性進行一番探究。
2.1 Tokenization
spaCy 的 document 可以在 tokenized 過程中被分割成單句,這些單句還可以進一步分割成單詞。你可以通過遍歷文件來讀取這些單詞:
# document 的首個單詞
document[0]
>> Nice
# document 的最後一個單詞
document[len(document)-5]
>> boston
# 列出 document 中的句子
list(document.sents)
>> [ Nice place Better than some reviews give it credit for.,
Overall, the rooms were a bit small but nice.,
...
Everything was clean, the view was wonderful and it is very well located (the Prudential Center makes shopping and eating easy and the T is nearby for jaunts out and about the city).]複製程式碼
2.2 詞性標註(POS Tag)
詞性標註即標註語法正確的句子中的詞語的詞性。這些標註可以用於資訊過濾、統計模型,或者基於某些規則進行文字解析。
來看看我們的 document 中所有的詞性標註:
# 獲得所有標註
all_tags = {w.pos: w.pos_ for w in document}
>> {97: u`SYM`, 98: u`VERB`, 99: u`X`, 101: u`SPACE`, 82: u`ADJ`, 83: u`ADP`, 84: u`ADV`, 87: u`CCONJ`, 88: u`DET`, 89: u`INTJ`, 90: u`NOUN`, 91: u`NUM`, 92: u`PART`, 93: u`PRON`, 94: u`PROPN`, 95: u`PUNCT`}
# document 中第一個句子的詞性標註
for word in list(document.sents)[0]:
print word, word.tag_
>> ( Nice, u`JJ`) (place, u`NN`) (Better, u`NNP`) (than, u`IN`) (some, u`DT`) (reviews, u`NNS`) (give, u`VBP`) (it, u`PRP`) (creit, u`NN`) (for, u`IN`) (., u`.`)複製程式碼
來看一看 document 中的最常用詞彙。我已經事先寫好了預處理和文字資料清洗的函式。
#一些引數定義
noisy_pos_tags = [“PROP”]
min_token_length = 2
#檢查 token 是不是噪音的函式
def isNoise(token):
is_noise = False
if token.pos_ in noisy_pos_tags:
is_noise = True
elif token.is_stop == True:
is_noise = True
elif len(token.string) <= min_token_length:
is_noise = True
return is_noise
def cleanup(token, lower = True):
if lower:
token = token.lower()
return token.strip()
# 評論中最常用的單詞
from collections import Counter
cleaned_list = [cleanup(word.string) for word in document if not isNoise(word)]
Counter(cleaned_list) .most_common(5)
>> [( u`hotel`, 683), (u`room`, 652), (u`great`, 300), (u`sheraton`, 285), (u`location`, 271)]複製程式碼
2.3 實體識別
spaCy 擁有一個快速實體識別模型,這個實體識別模型能夠從 document 中找出實體短語。它能識別各種型別的實體,例如人名、位置、機構、日期、數字等。你可以通過“.ents”屬性來讀取這些實體。
下面讓我們來獲取我們 document 中所有型別的命名實體:
labels = set([w.label_ for w in document.ents])
for label in labels:
entities = [cleanup(e.string, lower=False) for e in document.ents if label==e.label_]
entities = list(set(entities))
print label,entities複製程式碼
2.4 依存句法分析
spaCy 最強大的功能之一就是它可以通過呼叫輕量級的 API 來實現又快又準確的依存分析。這個分析器也可以用於句子邊界檢測以及區分短語塊。依存關係可以通過“.children”、“.root”、“.ancestor”等屬性讀取。
# 取出所有句中包含“hotel”單詞的評論
hotel = [sent for sent in document.sents if `hotel` in sent.string.lower()]
# 建立依存樹
sentence = hotel[2] for word in sentence:
print word, `: `, str(list(word.children))
>> A : [] cab : [A, from]
from : [airport, to]
the : []
airport : [the]
to : [hotel]
the : [] hotel :
[the] can : []
be : [cab, can, cheaper, .]
cheaper : [than] than :
[shuttles]
the : []
shuttles : [the, depending]
depending : [time] what : []
time : [what, of] of : [day]
the : [] day :
[the, go] you :
[]
go : [you]
. : []複製程式碼
解析所有居中包含“hotel”單詞的句子的依存關係,並檢查對於 hotel 人們用了哪些形容詞。我建立了一個自定義函式,用於分析依存關係並進行相關的詞性標註。
# 檢查修飾某個單詞的所有形容詞
def pos_words (sentence, token, ptag):
sentences = [sent for sent in sentence.sents if token in sent.string]
pwrds = []
for sent in sentences:
for word in sent:
if character in word.string:
pwrds.extend([child.string.strip() for child in word.children
if child.pos_ == ptag] )
return Counter(pwrds).most_common(10)
pos_words(document, `hotel`, “ADJ”)
>> [(u`other`, 20), (u`great`, 10), (u`good`, 7), (u`better`, 6), (u`nice`, 6), (u`different`, 5), (u`many`, 5), (u`best`, 4), (u`my`, 4), (u`wonderful`, 3)]複製程式碼
2.5 名詞短語(NP)
依存樹也可以用來生成名詞短語:
# 生成名詞短語
doc = nlp(u`I love data science on analytics vidhya`)
for np in doc.noun_chunks:
print np.text, np.root.dep_, np.root.head.text
>> I nsubj love
data science dobj love
analytics pobj on複製程式碼
3. 整合詞向量
spaCy 提供了內建整合的向量值演算法,這些向量值可以反映詞中的真正表達資訊。它使用 GloVe 來生成向量。GloVe 是一種用於獲取表示單詞的向量的無監督學習演算法。
讓我們建立一些詞向量,然後對其做一些有趣的操作吧:
from numpy import dot
from numpy.linalg import norm
from spacy.en import English
parser = English()
# 生成“apple”的詞向量
apple = parser.vocab[u`apple`]
# 餘弦相似性計算函式
cosine = lambda v1, v2: dot(v1, v2) / (norm(v1) * norm(v2))
others = list({w for w in parser.vocab if w.has_vector and w.orth_.islower() and w.lower_ != unicode("apple")})
# 根據相似性值進行排序
others.sort(key=lambda w: cosine(w.vector, apple.vector))
others.reverse()
print "top most similar words to apple:"
for word in others[:10]:
print word.orth_
>> apples iphone f ruit juice cherry lemon banana pie mac orange複製程式碼
4. 使用 spaCy 對文字進行機器學習
將 spaCy 整合進機器學習模型是非常簡單、直接的。讓我們使用 sklearn 做一個自定義的文字分類器。我們將使用 cleaner、tokenizer、vectorizer、classifier 元件來建立一個 sklearn 管道。其中的 tokenizer 和 vectorizer 會使用我們用 spaCy 自定義的模組構建。
from sklearn.feature_extraction.stop_words import ENGLISH_STOP_WORDS as stopwords
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import accuracy_score
from sklearn.base import TransformerMixin
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
import string
punctuations = string.punctuation
from spacy.en import English
parser = English()
# 使用 spaCy 自定義 transformer
class predictors(TransformerMixin):
def transform(self, X, **transform_params):
return [clean_text(text) for text in X]
def fit(self, X, y=None, **fit_params):
return self
def get_params(self, deep=True):
return {}
# 進行文字清洗的實用的基本函式
def clean_text(text):
return text.strip().lower()複製程式碼
現在讓我們使用 spaCy 的解析器和一些基本的資料清洗函式來建立一個自定義的 tokenizer 函式。值得一提的是,你可以用詞向量來代替文字特徵(使用深度學習模型效果會有較大的提升)
#建立 spaCy tokenizer,解析句子並生成 token
#也可以用詞向量函式來代替它
def spacy_tokenizer(sentence):
tokens = parser(sentence)
tokens = [tok.lemma_.lower().strip() if tok.lemma_ != "-PRON-" else tok.lower_ for tok in tokens]
tokens = [tok for tok in tokens if (tok not in stopwords and tok not in punctuations)] return tokens
#建立 vectorizer 物件,生成特徵向量,以此可以自定義 spaCy 的 tokenizer
vectorizer = CountVectorizer(tokenizer = spacy_tokenizer, ngram_range=(1,1)) classifier = LinearSVC()複製程式碼
現在可以建立管道,載入資料,然後執行分類模型了。
# 建立管道,進行文字清洗、tokenize、向量化、分類操作
pipe = Pipeline([("cleaner", predictors()),
(`vectorizer`, vectorizer),
(`classifier`, classifier)])
# Load sample data
train = [(`I love this sandwich.`, `pos`),
(`this is an amazing place!`, `pos`),
(`I feel very good about these beers.`, `pos`),
(`this is my best work.`, `pos`),
("what an awesome view", `pos`),
(`I do not like this restaurant`, `neg`),
(`I am tired of this stuff.`, `neg`),
("I can`t deal with this", `neg`),
(`he is my sworn enemy!`, `neg`),
(`my boss is horrible.`, `neg`)]
test = [(`the beer was good.`, `pos`),
(`I do not enjoy my job`, `neg`),
("I ain`t feelin dandy today.", `neg`),
("I feel amazing!", `pos`),
(`Gary is a good friend of mine.`, `pos`),
("I can`t believe I`m doing this.", `neg`)]
# 建立模型並計算準確率
pipe.fit([x[0] for x in train], [x[1] for x in train])
pred_data = pipe.predict([x[0] for x in test])
for (sample, pred) in zip(test, pred_data):
print sample, pred
print "Accuracy:", accuracy_score([x[1] for x in test], pred_data)
>> (`the beer was good.`, `pos`) pos
(`I do not enjoy my job`, `neg`) neg
("I ain`t feelin dandy today.", `neg`) neg
(`I feel amazing!`, `pos`) pos
(`Gary is a good friend of mine.`, `pos`) pos
("I can`t believe I`m doing this.", `neg`) neg
Accuracy: 1.0複製程式碼
5. 和其它庫的對比
Spacy 是一個非常強大且具備工業級能力的 NLP 包,它能滿足大多數 NLP 任務的需求。可能你會思考:為什麼會這樣呢?
讓我們把 Spacy 和另外兩個 python 中有名的實現 NLP 的工具 —— CoreNLP 和 NLTK 進行對比吧!
支援功能表
功能 | Spacy | NLTK | Core NLP |
---|---|---|---|
簡易的安裝方式 | Y | Y | Y |
Python API | Y | Y | N |
多語種支援 | N | Y | Y |
分詞 | Y | Y | Y |
詞性標註 | Y | Y | Y |
分句 | Y | Y | Y |
依存性分析 | Y | N | Y |
實體識別 | Y | Y | Y |
詞向量計算整合 | Y | N | N |
情感分析 | Y | Y | Y |
共指消解 | N | N | Y |
速度:主要功能(Tokenizer、Tagging、Parsing)速度
庫 | Tokenizer | Tagging | Parsing |
---|---|---|---|
spaCy | 0.2ms | 1ms | 19ms |
CoreNLP | 2ms | 10ms | 49ms |
NLTK | 4ms | 443ms | – |
準確性:實體抽取結果
庫 | 準確率 | Recall | F-Score |
---|---|---|---|
spaCy | 0.72 | 0.65 | 0.69 |
CoreNLP | 0.79 | 0.73 | 0.76 |
NLTK | 0.51 | 0.65 | 0.58 |
結束語
本文討論了 spaCy —— 這個基於 python,完全用於實現 NLP 的庫。我們通過許多用例展示了 spaCy 的可用性、速度及準確性。最後我們還將其餘其它幾個著名的 NLP 庫 —— CoreNLP 與 NLTK 進行了對比。
如果你能真正理解這篇文章要表達的內容,那你一定可以去實現各種有挑戰的文字資料與 NLP 問題。
希望你能喜歡這篇文章,如果你有疑問、問題或者別的想法,請在評論中留言。
作者介紹:
Shivam Bansal 是一位資料科學家,在 NLP 與機器學習領域有著豐富的經驗。他樂於學習,希望能解決一些富有挑戰性的分析類問題。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃。