使用 Python+spaCy 進行簡易自然語言處理

lsvih發表於2019-03-03

使用 Python+spaCy 進行簡易自然語言處理

簡介

自然語言處理(NLP)是人工智慧領域最重要的部分之一。它在許多智慧應用中擔任了關鍵的角色,例如聊天機器人、正文提取、多語翻譯以及觀點識別等應用。業界 NLP 相關的公司都意識到了,處理非結構文字資料時,不僅要看正確率,還需要注意是否能快速得到想要的結果。

NLP 是一個很寬泛的領域,它包括了文字分類、實體識別、機器翻譯、問答系統、概念識別等子領域。在我最近的一篇文章中,我探討了許多用於實現 NLP 的工具與元件。在那篇文章中,我更多的是在描述NLTK(Natural Language Toolkit)這個偉大的庫。

在這篇文章中,我會將 spaCy —— 這個現在最強大、最先進的 NLP python 庫分享給你們。


內容提要

  1. spaCy 簡介及安裝方法
  2. spaCy 的管道與屬性

    • Tokenization
    • 詞性標註
    • 實體識別
    • 依存句法分析
    • 名詞短語
  3. 整合詞向量計算

  4. 使用 spaCy 進行機器學習
  5. 與 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

Shivam Bansal 是一位資料科學家,在 NLP 與機器學習領域有著豐富的經驗。他樂於學習,希望能解決一些富有挑戰性的分析類問題。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章