Python知識分享網(wǎng) - 專業(yè)的Python學(xué)習網(wǎng)站 學(xué)Python,上Python222
自然語言處理庫spaCy使用指北
匿名網(wǎng)友發(fā)布于:2023-07-29 15:49:29
(侵權(quán)舉報)
(假如點擊沒反應(yīng),多刷新兩次就OK!)

 

1 背景介紹與spaCy安裝

 

1.1 自然語言處理簡介

自然語言處理(Natural Language Processing,簡稱NLP)是一門研究人類語言與計算機之間交互的領(lǐng)域,旨在使計算機能夠理解、解析、生成和處理人類語言。NLP結(jié)合了計算機科學(xué)、人工智能和語言學(xué)的知識,通過各種算法和技術(shù)來處理和分析文本數(shù)據(jù)。近年來,隨著深度學(xué)習技術(shù)的發(fā)展,神經(jīng)網(wǎng)絡(luò)模型在自然語言處理(NLP)領(lǐng)域取得了重大的突破。其中,循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)、長短時記憶網(wǎng)絡(luò)(LSTM)和Transformer等模型都發(fā)揮了關(guān)鍵作用。這些模型為NLP任務(wù)帶來了更好的性能和效果,推動了NLP的發(fā)展和應(yīng)用。

NLP主要知識結(jié)構(gòu)如下圖所示,圖片來自網(wǎng)絡(luò)。

自然語言處理庫spaCy使用指北 圖1

NLP的應(yīng)用非常廣泛,涵蓋了多個領(lǐng)域,如機器翻譯、信息提取、文本分類、情感分析、自動摘要、問答系統(tǒng)、語音識別和語音合成等。以下是NLP常用的技術(shù)和方法:

分詞:將連續(xù)的文本分割成有意義的詞語或標記,是許多NLP任務(wù)的基礎(chǔ)。

詞性標注:為文本中的每個詞語賦予其相應(yīng)的詞性,如名詞、動詞、形容詞等。

句法分析:分析句子的語法結(jié)構(gòu),識別出句子中的短語、修飾語和依存關(guān)系等。

語義分析:理解文本的意義和語義關(guān)系,包括命名實體識別、語義角色標注和語義解析等。

機器翻譯:將一種語言的文本自動翻譯成另一種語言。

文本分類:將文本按照預(yù)定義的類別進行分類,如垃圾郵件分類、情感分類等。

信息提取:從結(jié)構(gòu)化和非結(jié)構(gòu)化文本中提取出特定的信息,如實體關(guān)系抽取、事件抽取等。

問答系統(tǒng):通過對用戶提出的問題進行理解和回答,提供準確的答案或相關(guān)信息。

情感分析:識別和分析文本中的情感傾向,如正面、負面或中性情感。

文本生成:使用NLP技術(shù)生成自然語言文本,如自動摘要生成、對話系統(tǒng)和機器作文等。

在眾多自然語言處理庫中,spaCy庫提供了超過73種語言的支持,并為25種語言提供了訓(xùn)練代碼。該庫提供了一系列簡單易用的模型和函數(shù)接口,包括分詞、詞性標注等功能。用戶還可以使用PyTorch、TensorFlow等框架在spaCy創(chuàng)建自定義模型,以滿足特定需求。spaCy支持的語言模型見spaCy-models

事實上,有一些自然語言處理開源庫,例如HuggingFace的Transformers、PaddleNLPNLTK,相較于spaCy來說更為專業(yè)且性能更好。然而,對于簡單的應(yīng)用而言,spaCy更為適合,因為它具有簡單易用、功能全面,同時也提供了大量面向多語言預(yù)訓(xùn)練模型的優(yōu)點。此外,隨著以GPT-3為代表的語言大模型在自然語言處理領(lǐng)域取得了巨大的突破和成功,原本一些自然語言處理庫在精度上實際不如語言大模型。然而,使用語言大模型需要龐大的推理資源,而在對精度要求不高的場景中,使用spaCy這類小巧的自然語言處理庫依然是很合適的選擇。

 

1.2 spaCy安裝

spaCy采用采用模塊和語言模塊一起安裝的模式。spaCy的設(shè)計目標之一是模塊化和可定制性。它允許用戶僅安裝必需的模塊和語言數(shù)據(jù),以減少安裝的整體大小和減輕資源負擔。如果使用spaCy的模型,需要通過pip安裝模型所需的模型包來使用預(yù)訓(xùn)練模型。這是因為spaCy的模型包含了訓(xùn)練后的權(quán)重參數(shù)和其他必要的文件,這些文件在安裝時被存儲在特定位置,而不是以單個文件的形式存在。如果需要進行模型訓(xùn)練和gpu運行則需要選定對應(yīng)的安裝包。將模塊和語言模塊一起安裝,可以簡化spaCy的配置過程。用戶無需單獨下載和配置語言數(shù)據(jù),也不需要手動指定要使用的語言模型。這樣可以減少用戶的工作量和安裝過程中的潛在錯誤。但是可定制性就很弱了,所以spaCy適合精度要求不高的簡單使用,工程應(yīng)用選擇其他大型自然語言處理庫更加合適。

為了實現(xiàn)這一目標,spaCy提供了配置化的安裝指令選擇頁面供用戶使用。安裝指令選擇頁面地址為spaCy-usage。下圖展示了本文的安裝配置項,本文采用了最簡單的cpu推理模式。

 

自然語言處理庫spaCy使用指北  圖2

spaCy安裝完畢后,運行以下代碼即可判斷spaCy及相對應(yīng)的語言模型是否安裝成功。

 

# jupyter notebook環(huán)境去除warning
import warnings
warnings.filterwarnings("ignore")
import spacy
spacy.__version__

 

'3.6.0'

 

import spacy
 
# 加載已安裝的中文模型
nlp = spacy.load('zh_core_web_sm')
 
# 執(zhí)行一些簡單的NLP任務(wù)
doc = nlp("早上好!")
for token in doc:
    # token.text表示標記的原始文本,token.pos_表示標記的詞性(part-of-speech),token.dep_表示標記與其他標記之間的句法依存關(guān)系
    print(token.text, token.pos_, token.dep_)

 

早上 NOUN nmod:tmod
好 VERB ROOT
! PUNCT punct

 

 

2 spaCy快速入門

該部分內(nèi)容和圖片主要來自于spacy-101的總結(jié)。spaCy提供的主要函數(shù)模塊分為以下模塊,接下來分別對這些模塊進行介紹。

自然語言處理庫spaCy使用指北 圖3

 

2.1 分詞

在處理過程中,spaCy首先對文本進行標記,即將其分段為單詞、標點符號等Token。這是通過應(yīng)用每種語言特有的規(guī)則來實現(xiàn)的。Token表示自然語言文本的最小單位。每個Token都代表著文本中的一個原子元素,通常是單詞或標點符號。

 

import spacy
 
nlp = spacy.load("zh_core_web_sm")
# 使對文本進行一鍵處理
doc = nlp("南京長江大橋是金陵四十景之一!")
# 遍歷doc中的每個Token
for token in doc:
    print(token.text)

 

南京
長江
大橋
是
金陵
四十
景
之一
!

 

import spacy
 
nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
for token in doc:
    print(token.text)

 

Apple
is
looking
at
buying
U.K.
startup
for
$
1
billion

 

對于中文分詞有時會出現(xiàn)專有名詞被拆分,比如南京長江大橋被拆分為南京、長江、大橋。我們可以添加自定義詞典來解決該問題,但是要注意的是自定義詞典添加只針對某些語言模型。

 

import spacy
 
nlp = spacy.load("zh_core_web_sm")
# 添加自定義詞匯
nlp.tokenizer.pkuseg_update_user_dict(["南京長江大橋","金陵四十景"])
 
doc = nlp("南京長江大橋是金陵四十景之一!")
for token in doc:
    print(token.text)

 

南京長江大橋
是
金陵四十景
之一
!

 

 

2.2 詞性標注與依存關(guān)系

spaCy在分詞后,會對句子中每個詞進行詞性標注以及確定句子中單詞之間的語法關(guān)系,要注意這些關(guān)系具體范圍取決于所使用的模型。示例代碼如下所示:

 

import spacy
 
nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
 
# token.text: 單詞的原始形式。
# token.lemma_: 單詞的基本形式(或詞干)。例如,“running”的詞干是“run”。
# token.pos_: 單詞的粗粒度的詞性標注,如名詞、動詞、形容詞等。
# token.tag_: 單詞的細粒度的詞性標注,提供更多的語法信息。
# token.dep_: 單詞在句子中的依存關(guān)系角色,例如主語、賓語等。
# token.shape_: 單詞的形狀信息,例如,單詞的大小寫,是否有標點符號等。
# token.is_alpha: 這是一個布爾值,用于檢查token是否全部由字母組成。
# token.is_stop: 這是一個布爾值,用于檢查token是否為停用詞(如“the”、“is”等在英語中非常常見但通常不包含太多信息的詞)。
for token in doc:
    print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
            token.shape_, token.is_alpha, token.is_stop)

 

Apple Apple PROPN NNP nsubj Xxxxx True False
is be AUX VBZ aux xx True True
looking look VERB VBG ROOT xxxx True False
at at ADP IN prep xx True True
buying buy VERB VBG pcomp xxxx True False
U.K. U.K. PROPN NNP dobj X.X. False False
startup startup NOUN NN advcl xxxx True False
for for ADP IN prep xxx True True
$ $ SYM $ quantmod $ False False
1 1 NUM CD compound d False False
billion billion NUM CD pobj xxxx True False

 

在上述代碼中pos_所用是常見的單詞詞性標注。tag_所支持的詞性標注及解釋如下:

 

# 獲取所有詞性標注(tag_)和它們的描述
tag_descriptions = {tag: spacy.explain(tag) for tag in nlp.get_pipe('tagger').labels}
# 打印詞性標注(tag_)及其描述
# print("詞性標注 (TAG) 及其描述:")
# for tag, description in tag_descriptions.items():
#     print(f"{tag}: {description}")

 

dep__所支持的依存關(guān)系及解釋如下:

 

# 獲取所有依存關(guān)系標注和它們的描述
par_descriptions = {par: spacy.explain(par) for par in nlp.get_pipe('parser').labels}
# print("依存關(guān)系及其描述:")
# for par, description in par_descriptions.items():
#     print(f"{par}: {description}")

 

 

2.3 命名實體識別

命名實體識別(Named Entity Recognition, 簡稱NER)是自然語言處理中的一項基礎(chǔ)任務(wù),應(yīng)用范圍非常廣泛。 NER是指識別文本中具有特定意義或者指代性強的實體,通常包括人名、地名、機構(gòu)名、日期時間、專有名詞等。spaCy使用如下:

 

import spacy
 
nlp = spacy.load("zh_core_web_sm")
# 添加自定義詞匯
nlp.tokenizer.pkuseg_update_user_dict(["東方明珠"])
 
# 自定義詞匯可能不會進入實體識別。
doc = nlp("東方明珠是一座位于中國上海市的標志性建筑,建造于1991年,是一座高度為468米的電視塔。")
for ent in doc.ents:
    # 實體文本,開始位置,結(jié)束位置,實體標簽
    print(ent.text, ent.start_char, ent.end_char, ent.label_)
 

 

中國上海市 9 14 GPE
1991年 24 29 DATE
468米 36 40 QUANTITY

 

如果想知道所有的實體以及其對應(yīng)的含義,可以執(zhí)行以下代碼:

 

# 獲取命名實體標簽及其含義
entity_labels = nlp.get_pipe('ner').labels
 
# 打印輸出所有命名實體及其含義
for label in entity_labels:
    print("{}: {}".format(label,spacy.explain(label)))

 

CARDINAL: Numerals that do not fall under another type
DATE: Absolute or relative dates or periods
EVENT: Named hurricanes, battles, wars, sports events, etc.
FAC: Buildings, airports, highways, bridges, etc.
GPE: Countries, cities, states
LANGUAGE: Any named language
LAW: Named documents made into laws.
LOC: Non-GPE locations, mountain ranges, bodies of water
MONEY: Monetary values, including unit
NORP: Nationalities or religious or political groups
ORDINAL: "first", "second", etc.
ORG: Companies, agencies, institutions, etc.
PERCENT: Percentage, including "%"
PERSON: People, including fictional
PRODUCT: Objects, vehicles, foods, etc. (not services)
QUANTITY: Measurements, as of weight or distance
TIME: Times smaller than a day
WORK_OF_ART: Titles of books, songs, etc.

 

 

2.4 詞向量與相似性

詞向量是自然語言處理中一種重要的表示方式,它將單詞映射為實數(shù)向量。這種表示方式能夠捕捉單詞之間的語義關(guān)系,并將語義信息轉(zhuǎn)化為計算機能夠處理的數(shù)值形式。

傳統(tǒng)的自然語言處理方法往往將文本表示為離散的符號,例如獨熱編碼或者詞袋模型。然而,這種方法忽略了單詞之間的語義相似性,而且維度過高,造成稀疏性問題。相比之下,詞向量通過將每個單詞映射到連續(xù)的向量空間中,可以更好地捕捉單詞之間的語義關(guān)系,并且降低了特征空間的維度,使得文本處理更加高效。通過計算兩個詞向量之間的距離或夾角可以衡量詞向量的相似性。

提取句子中每一個詞的詞向量代碼如下:

 

import spacy
 
# 加載中文模型"zh_core_web_sm"
nlp = spacy.load("zh_core_web_sm")
 
# 對給定文本進行分詞和詞性標注
tokens = nlp("東方明珠是一座位于中國上海市的標志性建筑!")
 
# 遍歷分詞后的每個詞語
for token in tokens:
    # 輸出詞語的文本內(nèi)容、是否有對應(yīng)的向量表示、向量范數(shù)和是否為未登錄詞(Out-of-vocabulary,即不在詞向量詞典中的詞)
    print(token.text, token.has_vector, token.vector_norm, token.is_oov)

 

東方 True 11.572288 True
明珠 True 10.620552 True
是 True 12.337883 True
一 True 12.998204 True
座位 True 10.186406 True
于 True 13.540245 True
中國 True 12.459145 True
上海市 True 12.004954 True
的 True 12.90457 True
標志性 True 13.601862 True
建筑 True 10.46621 True
! True 12.811246 True

 

如果想得到某個句子或者某個詞的詞向量,代碼如下:

 

# 該詞向量沒有歸一化
tokens.vector.shape

 

(96,)

 

spaCy提供了similarity函數(shù)以計算兩個文本向量的相似度。
示例代碼如下:

 

import spacy
 
# 為了方便這里使用小模型,推薦使用更大的模型
nlp = spacy.load("zh_core_web_sm")  
doc1 = nlp("東方明珠是一座位于中國上海市的標志性建筑")
doc2 = nlp("南京長江大橋是金陵四十景之一!")
 
# 計算兩個文本的相似度
print(doc1, "<->", doc2, doc1.similarity(doc2))

 

東方明珠是一座位于中國上海市的標志性建筑 <-> 南京長江大橋是金陵四十景之一! 0.5743045135827821

 

在上面代碼中相似度計算方式默認使用余弦相似度。余弦相似度范圍為0到1,數(shù)值越高表明詞向量越相似。一般來說詞向量相似度使用spacy通用模型準確度可能很低,可以嘗試使用專用模型或者自行訓(xùn)練模型,spacy推薦使用sense2vec來計算模型相似度。

此外如果僅僅使用spacy提取文本向量,可以用numpy手動計算文本相似度,代碼如下:

 

import numpy as np
import spacy
nlp = spacy.load("zh_core_web_sm")  
doc1 = nlp("東方明珠是一座位于中國上海市的標志性建筑")
doc2 = nlp("南京長江大橋是金陵四十景之一!")
# 獲取doc1和doc2的詞向量
vec1 = doc1.vector
vec2 = doc2.vector
 
# 使用NumPy計算相似度得分,np.linalg.norm(vec1)就是doc1.vector_norm
similarity_score = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
 
print(doc1, "<->", doc2,similarity_score)

 

東方明珠是一座位于中國上海市的標志性建筑 <-> 南京長江大橋是金陵四十景之一! 0.5743046

 

 

3 spaCy結(jié)構(gòu)體系

 

3.1 spaCy處理流程

當在一個文本上調(diào)用nlp模型時,spaCy首先對文本進行分詞處理,生成一個Doc對象。接著,Doc對象將在幾個不同的步驟中進行處理。訓(xùn)練好的處理流程通常包括詞性標注器、依存句法解析器和實體識別器等處理組件。這些組件相互獨立,每個處理流程組件都會返回處理后的Doc對象,然后將其傳遞給下一個組件。
最終生成的Doc對象是一個包含了所有單詞和標點符號的序列,每個單詞被表示為Token對象。每個Token對象包含了單詞本身的內(nèi)容、詞性標注、詞形還原后的形式等信息。以下圖片解釋了使用spaCy進行文本處理的過程。

自然語言處理庫spaCy使用指北  圖4

如上圖所示,模型管道中所涉及到的模塊主要取決于該模型的結(jié)構(gòu)和訓(xùn)練方式。其中分詞tokenizer是一個特殊的組件且獨立于其他組件之外,這是因為其他組件模塊在調(diào)用前都會先調(diào)用tokenizer以對字符串進行分詞。所有支持主要的模塊如下,這些模塊的使用已在前一章進行介紹。

自然語言處理庫spaCy使用指北  圖5

一個spacy的模型所支持的文本處理組件查看方式如下:

 

import spacy
 
# 加載中文模型"zh_core_web_sm"
nlp = spacy.load("zh_core_web_sm")
# 查看所支持的組件
nlp.pipe_names

 

['tok2vec', 'tagger', 'parser', 'attribute_ruler', 'ner']

 

基于以下代碼可以控制組件的選擇和使用,以加快執(zhí)行速度:

 

# 加載不包含命名實體識別器(NER)的管道
nlp = spacy.load("zh_core_web_sm", exclude=["ner"])
# 查看所支持的組件
nlp.pipe_names

 

['tok2vec', 'tagger', 'parser', 'attribute_ruler']

 

# 只啟用tagger管道
nlp = spacy.load("zh_core_web_sm",enable=[ "tagger"])
nlp.pipe_names

 

['tagger']

 

# 加載詞性標注器(tagger)和依存句法解析器(parser),但不啟用它們
nlp = spacy.load("zh_core_web_sm", disable=["tagger", "parser"],)
# 禁用某些組件
nlp.disable_pipe("ner")
nlp.pipe_names

 

['tok2vec', 'attribute_ruler']

 

 

3.2 spaCy工程結(jié)構(gòu)

spaCy中的中心數(shù)據(jù)結(jié)構(gòu)是Language類、Vocab和Doc對象。Language類用于處理文本并將其轉(zhuǎn)換為Doc對象。它通常存儲為一個名為nlp的變量。Doc對象擁有令牌序列及其所有注釋。通過在Vocab中集中字符串、詞向量和詞法屬性。這些主要類和對象的介紹如下所示:

自然語言處理庫spaCy使用指北  圖6

常用模塊的介紹如下:

Doc

Doc是spaCy中一個重要的對象,它代表了一個文本文檔,并包含了文本中的所有信息,如單詞、標點、詞性、依賴關(guān)系等。可以通過spaCy的Language對象對文本進行處理,得到一個Doc對象。

DocBin

DocBin 是用于高效序列化和反序列化Doc對象的數(shù)據(jù)結(jié)構(gòu),以在不同的過程中保存和加載Doc對象。使用代碼如下:

 

# 導(dǎo)入所需的庫
import spacy
from spacy.tokens import DocBin
 
# 加載英文預(yù)訓(xùn)練模型
nlp = spacy.load("en_core_web_sm")
 
# 定義待處理文本
texts = ["This is sentence 1.", "And this is sentence 2."]
 
# 將每個文本轉(zhuǎn)化為Doc對象,用nlp處理并保存到docs列表中
docs = [nlp(text) for text in texts]
 
# 創(chuàng)建一個新的DocBin對象,用于保存文檔數(shù)據(jù),并啟用存儲用戶數(shù)據(jù)的功能
docbin = DocBin(store_user_data=True)
 
# 將每個Doc對象添加到DocBin中
for doc in docs:
    docbin.add(doc)
 
# 將DocBin保存到文件中
with open("documents.spacy", "wb") as f:
    f.write(docbin.to_bytes())
 
# 從文件中加載DocBin
with open("documents.spacy", "rb") as f:
    bytes_data = f.read()
 
# 從字節(jié)數(shù)據(jù)中恢復(fù)加載DocBin對象
loaded_docbin = DocBin().from_bytes(bytes_data)
 
# 使用nlp.vocab獲取詞匯表,并通過DocBin獲取所有加載的文檔
loaded_docs = list(loaded_docbin.get_docs(nlp.vocab))
 
# 輸出加載的文檔
loaded_docs

 

[This is sentence 1., And this is sentence 2.]

 

Example

Example用于訓(xùn)練spaCy模型,它包含了一個輸入文本(Doc)和其對應(yīng)的標注數(shù)據(jù)。關(guān)于spaCy模型的訓(xùn)練,見:spaCy-training

Language

Language是spaCy的核心對象之一,它負責處理文本的預(yù)處理、詞性標注、句法分析等任務(wù)??梢酝ㄟ^spacy.load()來加載一個具體的語言模型,獲取對應(yīng)的Language對象。

Lexeme

Lexeme 是一個單詞在詞匯表中的表示,它包含了關(guān)于該單詞的各種信息,如詞性、詞頻等。示例代碼如下

 

nlp = spacy.load("en_core_web_sm")
 
# 定義一個單詞
word = "hello"
 
# 獲取單詞對應(yīng)的詞元(lexeme)
lexeme = nlp.vocab[word]
 
# 打印詞元的文本內(nèi)容、是否為字母(alphabetical)
# 是否為停用詞(stopword)\是否為字母(is_alpha),是否為數(shù)字(is_digit),是否為標題(is_title),語言(lang_)
print(lexeme.text, lexeme.is_alpha, lexeme.is_stop, lexeme.is_alpha, lexeme.is_digit, lexeme.is_title, lexeme.lang_)

 

hello True False True False False en

 

事實上對于Lexeme,只要可能,spaCy就會嘗試將數(shù)據(jù)存儲在一個詞匯表Vocab中,該詞匯表將由多個模型共享。為了節(jié)省內(nèi)存,spaCy還將所有字符串編碼為哈希值。

自然語言處理庫spaCy使用指北  圖7

如下所示,不同模型下“coffee”的哈希值為3197928453018144401。但是注意的是只是spaCy這樣做,其他自然語言處理庫不一定這樣做。

 

import spacy
 
nlp = spacy.load("zh_core_web_sm")
doc = nlp("I love coffee")
print(doc.vocab.strings["coffee"])  # 3197928453018144401
print(doc.vocab.strings[3197928453018144401])  # 'coffee'

 

3197928453018144401
coffee

 

import spacy
 
nlp = spacy.load("en_core_web_sm")
doc = nlp("I love coffee")
print(doc.vocab.strings["coffee"])  # 3197928453018144401
print(doc.vocab.strings[3197928453018144401])  # 'coffee'

 

3197928453018144401
coffee

 

Span

Span 是一個連續(xù)的文本片段,可以由一個或多個Token組成。它通常用于標記實體或短語。

 

nlp = spacy.load("zh_core_web_sm")
text = "東方明珠是一座位于中國上海市的標志性建筑!"
doc = nlp(text)
 
# 從doc中選擇了第0個和第1個詞元(token)組成的片段。
# 注意,spaCy中的詞元是文本的基本單元,可能是單詞、標點符號或其它詞匯單位。
# 這里東方、明珠是前兩個詞
span = doc[0:2]  
print(span.text)

 

東方明珠

 

 

4 參考