如何用Python處理自然語言?(Spacy與Word Embedding)

本文教你用簡(jiǎn)單易學(xué)的工業(yè)級(jí)Python自然語言處理軟件包Spacy,對(duì)自然語言文本做詞性分析、命名實(shí)體識(shí)別、依賴關(guān)系刻畫,以及詞嵌入向量的計(jì)算和可視化。

盲維

我總愛重復(fù)一句芒格愛說的話:

To the one with a hammer, everything looks like a nail. (手中有錘,看什么都像釘)

這句話是什么意思呢?

就是你不能只掌握數(shù)量很少的方法、工具。

否則你的認(rèn)知會(huì)被自己能力框住。不只是存在盲點(diǎn),而是存在“盲維”。

你會(huì)嘗試用不合適的方法解決問題(還自詡“一招鮮,吃遍天”),卻對(duì)原本合適的工具視而不見。

結(jié)果可想而知。

所以,你得在自己的工具箱里面,多放一些兵刃。

最近我又對(duì)自己的學(xué)生,念叨芒格這句話。

因?yàn)樗麄冮_始做實(shí)際研究任務(wù)的時(shí)候,一遇到自然語言處理(Natural Language Processing, NLP),腦子里想到的就是詞云、情感分析LDA主題建模。

為什么?

因?yàn)槲业膶诤凸娞?hào)里,自然語言處理部分,只寫過這些內(nèi)容。

你如果認(rèn)為,NLP只能做這些事,就大錯(cuò)特錯(cuò)了。

看看這段視頻,你大概就能感受到目前自然語言處理的前沿,已經(jīng)到了哪里。

當(dāng)然,你手頭擁有的工具和數(shù)據(jù),尚不能做出Google展示的黑科技效果。

但是,現(xiàn)有的工具,也足可以讓你對(duì)自然語言文本,做出更豐富的處理結(jié)果。

科技的發(fā)展,蓬勃迅速。

除了咱們之前文章中已介紹過的結(jié)巴分詞、SnowNLP和TextBlob,基于Python的自然語言處理工具還有很多,例如 NLTK 和 gensim 等。

我無法幫你一一熟悉,你可能用到的所有自然語言處理工具。

但是咱們不妨開個(gè)頭,介紹一款叫做 Spacy 的 Python 工具包。

剩下的,自己舉一反三。

工具

Spacy 的 Slogan,是這樣的:

Industrial-Strength Natural Language Processing. (工業(yè)級(jí)別的自然語言處理)

這句話聽上去,是不是有些狂妄???

不過人家還是用數(shù)據(jù)說話的。

數(shù)據(jù)采自同行評(píng)議(Peer-reviewed)學(xué)術(shù)論文:

看完上述的數(shù)據(jù)分析,我們大致對(duì)于Spacy的性能有些了解。

但是我選用它,不僅僅是因?yàn)樗肮I(yè)級(jí)別”的性能,更是因?yàn)樗峁┝吮憬莸挠脩粽{(diào)用接口,以及豐富、詳細(xì)的文檔。

僅舉一例。

上圖是Spacy上手教程的第一頁。

可以看到,左側(cè)有簡(jiǎn)明的樹狀導(dǎo)航條,中間是詳細(xì)的文檔,右側(cè)是重點(diǎn)提示。

僅安裝這一項(xiàng),你就可以點(diǎn)擊選擇操作系統(tǒng)、Python包管理工具、Python版本、虛擬環(huán)境和語言支持等標(biāo)簽。網(wǎng)頁會(huì)動(dòng)態(tài)為你生成安裝的語句。

這種設(shè)計(jì),對(duì)新手用戶,很有幫助吧?

Spacy的功能有很多。

從最簡(jiǎn)單的詞性分析,到高階的神經(jīng)網(wǎng)絡(luò)模型,五花八門。

篇幅所限,本文只為你展示以下內(nèi)容:

  • 詞性分析
  • 命名實(shí)體識(shí)別
  • 依賴關(guān)系刻畫
  • 詞嵌入向量的近似度計(jì)算
  • 詞語降維和可視化

學(xué)完這篇教程,你可以按圖索驥,利用Spacy提供的詳細(xì)文檔,自學(xué)其他自然語言處理功能。

我們開始吧。

環(huán)境

請(qǐng)點(diǎn)擊這個(gè)鏈接http://t.cn/R35fElv),直接進(jìn)入咱們的實(shí)驗(yàn)環(huán)境。

對(duì),你沒看錯(cuò)。

不需要在本地計(jì)算機(jī)安裝任何軟件包。只要有一個(gè)現(xiàn)代化瀏覽器(包括Google Chrome, Firefox, Safari和Microsoft Edge等)就可以了。全部的依賴軟件,我都已經(jīng)為你準(zhǔn)備好了。

打開鏈接之后,你會(huì)看見這個(gè)頁面。

不同于之前的 Jupyter Notebook,這個(gè)界面來自 Jupyter Lab。

你可以將它理解為 Jupyter Notebook 的增強(qiáng)版,它具備以下特征:

  • 代碼單元直接鼠標(biāo)拖動(dòng);
  • 一個(gè)瀏覽器標(biāo)簽,可打開多個(gè)Notebook,而且分別使用不同的Kernel;
  • 提供實(shí)時(shí)渲染的Markdown編輯器;
  • 完整的文件瀏覽器;
  • CSV數(shù)據(jù)文件快速瀏覽
  • ……

圖中左側(cè)分欄,是工作目錄下的全部文件。

右側(cè)打開的,是咱們要使用的ipynb文件。

根據(jù)咱們的講解,請(qǐng)你逐條執(zhí)行,觀察結(jié)果。

我們說一說樣例文本數(shù)據(jù)的來源。

如果你之前讀過我的其他自然語言處理方面的教程,應(yīng)該記得這部電視劇。

對(duì),就是"Yes, Minister"。

出于對(duì)這部80年代英國喜劇的喜愛,我還是用維基百科上"Yes, Minister"的介紹內(nèi)容,作為文本分析樣例。

下面,我們就正式開始,一步步執(zhí)行程序代碼了。

我建議你先完全按照教程跑一遍,運(yùn)行出結(jié)果。

如果一切正常,再將其中的數(shù)據(jù),替換為你自己感興趣的內(nèi)容。

之后,嘗試打開一個(gè)空白 ipynb 文件,根據(jù)教程和文檔,自己敲代碼,并且嘗試做調(diào)整。

這樣會(huì)有助于你理解工作流程和工具使用方法。

實(shí)踐

我們從維基百科頁面的第一自然段中,摘取部分語句,放到text變量里面。

text = "The sequel, Yes, Prime Minister, ran from 1986 to 1988. In total there were 38 episodes, of which all but one lasted half an hour. Almost all episodes ended with a variation of the title of the series spoken as the answer to a question posed by the same character, Jim Hacker. Several episodes were adapted for BBC Radio, and a stage play was produced in 2010, the latter leading to a new television series on UKTV Gold in 2013."

顯示一下,看是否正確存儲(chǔ)。

text
'The sequel, Yes, Prime Minister, ran from 1986 to 1988. In total there were 38 episodes, of which all but one lasted half an hour. Almost all episodes ended with a variation of the title of the series spoken as the answer to a question posed by the same character, Jim Hacker. Several episodes were adapted for BBC Radio, and a stage play was produced in 2010, the latter leading to a new television series on UKTV Gold in 2013.'

沒問題了。

下面我們讀入Spacy軟件包。

import spacy

我們讓Spacy使用英語模型,將模型存儲(chǔ)到變量nlp中。

nlp = spacy.load('en')

下面,我們用nlp模型分析咱們的文本段落,將結(jié)果命名為doc。

doc = nlp(text)

我們看看doc的內(nèi)容。

doc
The sequel, Yes, Prime Minister, ran from 1986 to 1988. In total there were 38 episodes, of which all but one lasted half an hour. Almost all episodes ended with a variation of the title of the series spoken as the answer to a question posed by the same character, Jim Hacker. Several episodes were adapted for BBC Radio, and a stage play was produced in 2010, the latter leading to a new television series on UKTV Gold in 2013.

好像跟剛才的text內(nèi)容沒有區(qū)別呀?不還是這段文本嗎?

別著急,Spacy只是為了讓我們看著舒服,所以只打印出來文本內(nèi)容。

其實(shí),它在后臺(tái),已經(jīng)對(duì)這段話進(jìn)行了許多層次的分析。

不信?

我們來試試,讓Spacy幫我們分析這段話中出現(xiàn)的全部詞例(token)。

for token in doc:
    print('"' + token.text + '"')

你會(huì)看到,Spacy為我們輸出了一長(zhǎng)串列表。

"The"
"sequel"
","
"Yes"
","
"Prime"
"Minister"
","
"ran"
"from"
"1986"
"to"
"1988"
"."
"In"
"total"
"there"
"were"
"38"
"episodes"
","
"of"
"which"
"all"
"but"
"one"
"lasted"
"half"
"an"
"hour"
"."
"Almost"
"all"
"episodes"
"ended"
"with"
"a"
"variation"
"of"
"the"
"title"
"of"
"the"
"series"
"spoken"
"as"
"the"
"answer"
"to"
"a"
"question"
"posed"
"by"
"the"
"same"
"character"
","
"Jim"
"Hacker"
"."
"Several"
"episodes"
"were"
"adapted"
"for"
"BBC"
"Radio"
","
"and"
"a"
"stage"
"play"
"was"
"produced"
"in"
"2010"
","
"the"
"latter"
"leading"
"to"
"a"
"new"
"television"
"series"
"on"
"UKTV"
"Gold"
"in"
"2013"
"."

你可能不以為然——這有什么了不起?

英語本來就是空格分割的嘛!我自己也能編個(gè)小程序,以空格分段,依次打印出這些內(nèi)容來!

別忙,除了詞例內(nèi)容本身,Spacy還把每個(gè)詞例的一些屬性信息,進(jìn)行了處理。

下面,我們只對(duì)前10個(gè)詞例(token),輸出以下內(nèi)容:

  • 文本
  • 索引值(即在原文中的定位)
  • 詞元(lemma)
  • 是否為標(biāo)點(diǎn)符號(hào)
  • 是否為空格
  • 詞性
  • 標(biāo)記
for token in doc[:10]:
    print("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}".format(
        token.text,
        token.idx,
        token.lemma_,
        token.is_punct,
        token.is_space,
        token.shape_,
        token.pos_,
        token.tag_
    ))

結(jié)果為:

The 0   the False   False   Xxx DET DT
sequel  4   sequel  False   False   xxxx    NOUN    NN
,   10  ,   True    False   ,   PUNCT   ,
Yes 12  yes False   False   Xxx INTJ    UH
,   15  ,   True    False   ,   PUNCT   ,
Prime   17  prime   False   False   Xxxxx   PROPN   NNP
Minister    23  minister    False   False   Xxxxx   PROPN   NNP
,   31  ,   True    False   ,   PUNCT   ,
ran 33  run False   False   xxx VERB    VBD
from    37  from    False   False   xxxx    ADP IN

看到Spacy在后臺(tái)默默為我們做出的大量工作了吧?

下面我們不再考慮全部詞性,只關(guān)注文本中出現(xiàn)的實(shí)體(entity)詞匯。

for ent in doc.ents:
    print(ent.text, ent.label_)
1986 to 1988 DATE
38 CARDINAL
one CARDINAL
half an hour TIME
Jim Hacker PERSON
BBC Radio ORG
2010 DATE
UKTV Gold ORG
2013 DATE

在這一段文字中,出現(xiàn)的實(shí)體包括日期、時(shí)間、基數(shù)(Cardinal)……Spacy不僅自動(dòng)識(shí)別出了Jim Hacker為人名,還正確判定BBC Radio和UKTV Gold為機(jī)構(gòu)名稱。

如果你平時(shí)的工作,需要從海量評(píng)論里篩選潛在競(jìng)爭(zhēng)產(chǎn)品或者競(jìng)爭(zhēng)者,那看到這里,有沒有一點(diǎn)兒靈感呢?

執(zhí)行下面這段代碼,看看會(huì)發(fā)生什么:

from spacy import displacy
displacy.render(doc, style='ent', jupyter=True)

如上圖所示,Spacy幫我們把實(shí)體識(shí)別的結(jié)果,進(jìn)行了直觀的可視化。不同類別的實(shí)體,還采用了不同的顏色加以區(qū)分。

把一段文字拆解為語句,對(duì)Spacy而言,也是小菜一碟。

for sent in doc.sents:
    print(sent)
The sequel, Yes, Prime Minister, ran from 1986 to 1988.
In total there were 38 episodes, of which all but one lasted half an hour.
Almost all episodes ended with a variation of the title of the series spoken as the answer to a question posed by the same character, Jim Hacker.
Several episodes were adapted for BBC Radio, and a stage play was produced in 2010, the latter leading to a new television series on UKTV Gold in 2013.

注意這里,doc.sents并不是個(gè)列表類型。

doc.sents
<generator at 0x116e95e18>

所以,假設(shè)我們需要從中篩選出某一句話,需要先將其轉(zhuǎn)化為列表。

list(doc.sents)
[The sequel, Yes, Prime Minister, ran from 1986 to 1988.,
 In total there were 38 episodes, of which all but one lasted half an hour.,
 Almost all episodes ended with a variation of the title of the series spoken as the answer to a question posed by the same character, Jim Hacker.,
 Several episodes were adapted for BBC Radio, and a stage play was produced in 2010, the latter leading to a new television series on UKTV Gold in 2013.]

下面要展示的功能,分析范圍局限在第一句話。

我們將其抽取出來,并且重新用nlp模型處理,存入到新的變量newdoc中。

newdoc = nlp(list(doc.sents)[0].text)

對(duì)這一句話,我們想要搞清其中每一個(gè)詞例(token)之間的依賴關(guān)系。

for token in newdoc:
    print("{0}/{1} <--{2}-- {3}/{4}".format(
        token.text, token.tag_, token.dep_, token.head.text, token.head.tag_))
The/DT <--det-- sequel/NN
sequel/NN <--nsubj-- ran/VBD
,/, <--punct-- sequel/NN
Yes/UH <--intj-- sequel/NN
,/, <--punct-- sequel/NN
Prime/NNP <--compound-- Minister/NNP
Minister/NNP <--appos-- sequel/NN
,/, <--punct-- sequel/NN
ran/VBD <--ROOT-- ran/VBD
from/IN <--prep-- ran/VBD
1986/CD <--pobj-- from/IN
to/IN <--prep-- from/IN
1988/CD <--pobj-- to/IN
./. <--punct-- ran/VBD

很清晰,但是列表的方式,似乎不大直觀。

那就讓Spacy幫我們可視化吧。

displacy.render(newdoc, style='dep', jupyter=True, options={'distance': 90})

結(jié)果如下:

這些依賴關(guān)系鏈接上的詞匯,都代表什么?

如果你對(duì)語言學(xué)比較了解,應(yīng)該能看懂。

不懂?查查字典嘛。

跟語法書對(duì)比一下,看看Spacy分析得是否準(zhǔn)確。

前面我們分析的,屬于語法層級(jí)。

下面我們看語義。

我們利用的工具,叫做詞嵌入(word embedding)模型。

之前的文章《如何用Python從海量文本抽取主題?》中,我們提到過如何把文字表達(dá)成電腦可以看懂的數(shù)據(jù)。

文中處理的每一個(gè)單詞,都僅僅對(duì)應(yīng)著詞典里面的一個(gè)編號(hào)而已。你可以把它看成你去營業(yè)廳辦理業(yè)務(wù)時(shí)領(lǐng)取的號(hào)碼。

它只提供了先來后到的順序信息,跟你的職業(yè)、學(xué)歷、性別統(tǒng)統(tǒng)沒有關(guān)系。

我們將這樣過于簡(jiǎn)化的信息輸入,計(jì)算機(jī)對(duì)于詞義的了解,也必然少得可憐。

例如給你下面這個(gè)式子:

? - woman = king - queen

只要你學(xué)過英語,就不難猜到這里大概率應(yīng)該填寫“man”。

但是,如果你只是用了隨機(jī)的序號(hào)來代表詞匯,又如何能夠猜到這里正確的填詞結(jié)果呢?

幸好,在深度學(xué)習(xí)領(lǐng)域,我們可以使用更為順手的單詞向量化工具——詞嵌入(word embeddings )。

如上圖這個(gè)簡(jiǎn)化示例,詞嵌入把單詞變成多維空間上面的向量。

這樣,詞語就不再是冷冰冰的字典編號(hào),而是具有了意義。

使用詞嵌入模型,我們需要Spacy讀取一個(gè)新的文件。

nlp = spacy.load('en_core_web_lg')

為測(cè)試讀取結(jié)果,我們讓Spacy打印“minister”這個(gè)單詞對(duì)應(yīng)的向量取值。

print(nlp.vocab['minister'].vector)

可以看到,每個(gè)單詞,用總長(zhǎng)度為300的浮點(diǎn)數(shù)組成向量來表示。

順便說一句,Spacy讀入的這個(gè)模型,是采用word2vec,在海量語料上訓(xùn)練的結(jié)果。

我們來看看,此時(shí)Spacy的語義近似度判別能力。

這里,我們將4個(gè)變量,賦值為對(duì)應(yīng)單詞的向量表達(dá)結(jié)果。

dog = nlp.vocab["dog"]
cat = nlp.vocab["cat"]
apple = nlp.vocab["apple"]
orange = nlp.vocab["orange"]

我們看看“狗”和“貓”的近似度:

dog.similarity(cat)
0.80168545

嗯,都是寵物,近似度高,可以接受。

下面看看“狗”和“蘋果”。

dog.similarity(apple)
0.26339027

一個(gè)動(dòng)物,一個(gè)水果,近似度一下子就跌落下來了。

“狗”和“橘子”呢?

dog.similarity(orange)
0.2742508

可見,相似度也不高。

那么“蘋果”和“橘子”之間呢?

apple.similarity(orange)
0.5618917

水果間近似度,遠(yuǎn)遠(yuǎn)超過水果與動(dòng)物的相似程度。

測(cè)試通過。

看來Spacy利用詞嵌入模型,對(duì)語義有了一定的理解。

下面為了好玩,我們來考考它。

這里,我們需要計(jì)算詞典中可能不存在的向量,因此Spacy自帶的similarity()函數(shù),就顯得不夠用了。

我們從scipy中,找到相似度計(jì)算需要用到的余弦函數(shù)。

from scipy.spatial.distance import cosine

對(duì)比一下,我們直接代入“狗”和“貓”的向量,進(jìn)行計(jì)算。

1 - cosine(dog.vector, cat.vector)
0.8016855120658875

除了保留數(shù)字外,計(jì)算結(jié)果與Spacy自帶的similarity()運(yùn)行結(jié)果沒有差別。

我們把它做成一個(gè)小函數(shù),專門處理向量輸入。

def vector_similarity(x, y):
    return 1 - cosine(x, y)

用我們自編的相似度函數(shù),測(cè)試一下“狗”和“蘋果”。

vector_similarity(dog.vector, apple.vector)
0.2633902430534363

與剛才的結(jié)果對(duì)比,也是一致的。

我們要表達(dá)的,是這個(gè)式子:

? - woman = king - queen

我們把問號(hào),稱為 guess_word

所以

guess_word = king - queen + woman

我們把右側(cè)三個(gè)單詞,一般化記為 words。編寫下面函數(shù),計(jì)算guess_word取值。

def make_guess_word(words):
    [first, second, third] = words
    return nlp.vocab[first].vector - nlp.vocab[second].vector + nlp.vocab[third].vector

下面的函數(shù)就比較暴力了,它其實(shí)是用我們計(jì)算的 guess_word 取值,和字典中全部詞語一一核對(duì)近似性。把最為近似的10個(gè)候選單詞打印出來。

def get_similar_word(words, scope=nlp.vocab):

    guess_word = make_guess_word(words)

    similarities = []

    for word in scope:
        if not word.has_vector:
            continue

        similarity = vector_similarity(guess_word, word.vector)
        similarities.append((word, similarity))


    similarities = sorted(similarities, key=lambda item: -item[1])
    print([word[0].text for word in similarities[:10]])

好了,游戲時(shí)間開始。

我們先看看:

? - woman = king - queen

即:

guess_word = king - queen + woman

輸入右側(cè)詞序列:

words = ["king", "queen", "woman"]

然后執(zhí)行對(duì)比函數(shù):

get_similar_word(words)

這個(gè)函數(shù)運(yùn)行起來,需要一段時(shí)間。請(qǐng)保持耐心。

運(yùn)行結(jié)束之后,你會(huì)看到如下結(jié)果:

['MAN', 'Man', 'mAn', 'MAn', 'MaN', 'man', 'mAN', 'WOMAN', 'womAn', 'WOman']

原來字典里面,“男人”(man)這個(gè)詞匯有這么多的變形啊。

但是這個(gè)例子太經(jīng)典了,我們嘗試個(gè)新鮮一些的:

? - England = Paris - London

即:

guess_word = Paris - London + England

對(duì)你來講,絕對(duì)是簡(jiǎn)單的題目。左側(cè)國別,右側(cè)首都,對(duì)應(yīng)來看,自然是巴黎所在的法國(France)。

問題是,Spacy能猜對(duì)嗎?

我們把這幾個(gè)單詞輸入。

words = ["Paris", "London", "England"]

讓Spacy來猜:

get_similar_word(words)
['france', 'FRANCE', 'France', 'Paris', 'paris', 'PARIS', 'EUROPE', 'EUrope', 'europe', 'Europe']

結(jié)果很令人振奮,前三個(gè)都是“法國”(France)。

下面我們做一個(gè)更有趣的事兒,把詞向量的300維的高空間維度,壓縮到一張紙(二維)上,看看詞語之間的相對(duì)位置關(guān)系。

首先我們需要讀入numpy軟件包。

import numpy as np

我們把詞嵌入矩陣先設(shè)定為空。一會(huì)兒慢慢填入。

embedding = np.array([])

需要演示的單詞列表,也先空著。

word_list = []

我們?cè)俅巫孲pacy遍歷“Yes, Minister”維基頁面中摘取的那段文字,加入到單詞列表中。注意這次我們要進(jìn)行判斷:

  • 如果是標(biāo)點(diǎn),丟棄;
  • 如果詞匯已經(jīng)在詞語列表中,丟棄。
for token in doc:
    if not(token.is_punct) and not(token.text in word_list):
        word_list.append(token.text)

看看生成的結(jié)果:

word_list
['The',
 'sequel',
 'Yes',
 'Prime',
 'Minister',
 'ran',
 'from',
 '1986',
 'to',
 '1988',
 'In',
 'total',
 'there',
 'were',
 '38',
 'episodes',
 'of',
 'which',
 'all',
 'but',
 'one',
 'lasted',
 'half',
 'an',
 'hour',
 'Almost',
 'ended',
 'with',
 'a',
 'variation',
 'the',
 'title',
 'series',
 'spoken',
 'as',
 'answer',
 'question',
 'posed',
 'by',
 'same',
 'character',
 'Jim',
 'Hacker',
 'Several',
 'adapted',
 'for',
 'BBC',
 'Radio',
 'and',
 'stage',
 'play',
 'was',
 'produced',
 'in',
 '2010',
 'latter',
 'leading',
 'new',
 'television',
 'on',
 'UKTV',
 'Gold',
 '2013']

檢查了一下,一長(zhǎng)串(63個(gè))詞語列表中,沒有出現(xiàn)標(biāo)點(diǎn)。一切正常。

下面,我們把每個(gè)詞匯對(duì)應(yīng)的空間向量,追加到詞嵌入矩陣中。

for word in word_list:
    embedding = np.append(embedding, nlp.vocab[word].vector)

看看此時(shí)詞嵌入矩陣的維度。

embedding.shape
(18900,)

可以看到,所有的向量?jī)?nèi)容,都被放在了一個(gè)長(zhǎng)串上面。這顯然不符合我們的要求,我們將不同的單詞對(duì)應(yīng)的詞向量,拆解到不同行上面去。

embedding = embedding.reshape(len(word_list), -1)

再看看變換后詞嵌入矩陣的維度。

embedding.shape
(63, 300)

63個(gè)詞匯,每個(gè)長(zhǎng)度300,這就對(duì)了。

下面我們從scikit-learn軟件包中,讀入TSNE模塊。

from sklearn.manifold import TSNE

我們建立一個(gè)同名小寫的tsne,作為調(diào)用對(duì)象。

tsne = TSNE()

tsne的作用,是把高維度的詞向量(300維)壓縮到二維平面上。我們執(zhí)行這個(gè)轉(zhuǎn)換過程:

low_dim_embedding = tsne.fit_transform(embedding)

現(xiàn)在,我們手里擁有的 low_dim_embedding ,就是63個(gè)詞匯降低到二維的向量表示了。

我們讀入繪圖工具包。

import matplotlib.pyplot as plt
%pylab inline

下面這個(gè)函數(shù),用來把二維向量的集合,繪制出來。

如果你對(duì)該函數(shù)內(nèi)容細(xì)節(jié)不理解,沒關(guān)系。因?yàn)槲疫€沒有給你系統(tǒng)介紹過Python下的繪圖功能。

好在這里我們只要會(huì)調(diào)用它,就可以了。

def plot_with_labels(low_dim_embs, labels, filename='tsne.pdf'):
    assert low_dim_embs.shape[0] >= len(labels), "More labels than embeddings"
    plt.figure(figsize=(18, 18))  # in inches
    for i, label in enumerate(labels):
        x, y = low_dim_embs[i, :]
        plt.scatter(x, y)
        plt.annotate(label,
                 xy=(x, y),
                 xytext=(5, 2),
                 textcoords='offset points',
                 ha='right',
                 va='bottom')
    plt.savefig(filename)

終于可以進(jìn)行降維后的詞向量可視化了。

請(qǐng)執(zhí)行下面這條語句:

plot_with_labels(low_dim_embedding, word_list)

你會(huì)看到這樣一個(gè)圖形。

請(qǐng)注意觀察圖中的幾個(gè)部分:

  • 年份
  • 同一單詞的大小寫形式
  • Radio 和 television
  • a 和 an

看看有什么規(guī)律沒有?

我發(fā)現(xiàn)了一個(gè)有意思的現(xiàn)象——每次運(yùn)行tsne,產(chǎn)生的二維可視化圖都不一樣!

不過這也正常,因?yàn)檫@段話之中出現(xiàn)的單詞,并非都有預(yù)先訓(xùn)練好的向量。

這樣的單詞,被Spacy進(jìn)行了隨機(jī)化等處理。

因此,每一次生成高維向量,結(jié)果都不同。不同的高維向量,壓縮到二維,結(jié)果自然也會(huì)有區(qū)別。

問題來了,如果我希望每次運(yùn)行的結(jié)果都一致,該如何處理呢?

這個(gè)問題,作為課后思考題,留給你自行解答。

細(xì)心的你可能發(fā)現(xiàn)了,執(zhí)行完最后一條語句后,頁面左側(cè)邊欄文件列表中,出現(xiàn)了一個(gè)新的pdf文件。

這個(gè)pdf,就是你剛剛生成的可視化結(jié)果。你可以雙擊該文件名稱,在新的標(biāo)簽頁中查看。

看,就連pdf文件,Jupyter Lab也能正確顯示。

下面,是練習(xí)時(shí)間。

請(qǐng)把ipynb出現(xiàn)的文本內(nèi)容,替換為你感興趣的段落和詞匯,再嘗試運(yùn)行一次吧。

源碼

執(zhí)行了全部代碼,并且嘗試替換了自己需要分析的文本,成功運(yùn)行后,你是不是很有成就感?

你可能想要更進(jìn)一步挖掘Spacy的功能,并且希望在本地復(fù)現(xiàn)運(yùn)行環(huán)境與結(jié)果。

沒問題,請(qǐng)使用這個(gè)鏈接http://t.cn/R35MIKh)下載本文用到的全部源代碼和運(yùn)行環(huán)境配置文件(Pipenv)壓縮包。

如果你知道如何使用github,也歡迎用這個(gè)鏈接http://t.cn/R35MEqk)訪問對(duì)應(yīng)的github repo,進(jìn)行clone或者fork等操作。

當(dāng)然,要是能給我的repo加一顆星,就更好了。

謝謝!

小結(jié)

本文利用Python自然語言處理工具包Spacy,非常簡(jiǎn)要地為你演示了以下NLP功能:

  • 詞性分析
  • 命名實(shí)體識(shí)別
  • 依賴關(guān)系刻畫
  • 詞嵌入向量的近似度計(jì)算
  • 詞語降維和可視化

希望學(xué)過之后,你成功地在工具箱里又添加了一件趁手的兵器。

愿它在以后的研究和工作中,助你披荊斬棘,馬到成功。

加油!

討論

你之前做過自然語言處理項(xiàng)目嗎?使用過哪些工具包?除了本文介紹的這些基本功能外,你覺得還有哪些NLP功能是非常基礎(chǔ)而重要的?你是如何學(xué)習(xí)它們的呢?歡迎留言,把你的經(jīng)驗(yàn)和思考分享給大家,我們一起交流討論。

喜歡請(qǐng)點(diǎn)贊。還可以微信關(guān)注和置頂我的公眾號(hào)“玉樹芝蘭”(nkwangshuyi)。

如果你對(duì)數(shù)據(jù)科學(xué)感興趣,不妨閱讀我的系列教程索引貼《如何高效入門數(shù)據(jù)科學(xué)?》,里面還有更多的有趣問題及解法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容