自然语言处理库之spaCy初探

一、自然语言处理简介

自然语言处理(Natural Language Processing,简称NLP)是一门研究人类语言与计算机之间交互的领域,旨在使计算机能够理解、解析、生成和处理人类语言。NLP结合了计算机科学、人工智能和语言学的知识,通过各种算法和技术来处理和分析文本数据。近年来,随着深度学习技术的发展,神经网络模型在自然语言处理(NLP)领域取得了重大的突破。其中,循环神经网络(RNN)、长短时记忆网络(LSTM)和Transformer等模型都发挥了关键作用。这些模型为NLP任务带来了更好的性能和效果,推动了NLP的发展和应用。

自然语言处理中的基本任务包括:分词、词性标注、依存句法分析、组块、词形还原和词干提取等,简单介绍如下:

分词(tokenization)是将文本分割成最小有意义单元(如单词、标点符号、符号等)的过程。例如,句子“We live in China”可以被划分为四个标记:We、live、in、China。分词通常是每个自然语言处理任务的第一步。分词是一个必要的步骤,因为计算机需要将自然语言数据分解成最基本的元素(或词条),这样它就可以在其他元素的上下文中分析每个元素。否则,它将不得不分析一段很长的文本或音频,就好像其是一个单一的元素,从而使这个问题难以处理。就像初学者把一个句子分解成小块来逐字学习和处理信息一样,计算机也需要这样做。即使是复杂的数值计算,计算机也能把问题分解成基本的元素,执行两组数字的加减乘除等任务。机器的主要优势在于它能以人类无法达到的速度和规模完成这项工作。分词将文本分解成最小有意义单元后,机器需要为每个单元分配元数据,提供更多关于如何在由其他单元组成的上下文环境下处理每个单元的信息。

词性(Part-Of-Speech,POS)标注是为词条(token)分配词语类型的过程,如名词、代词、动词、副词、形容词、连词、介词、感叹词等。在“We live in China”中,词语类型有代词、动词、介词和名词。这种词性标注为每个词条提供了更多的元数据,使机器更容易识别每个词条和其他词条之间的关系。在句子“I kick the ball”中,“I”和“ball”都是名词,“kick”是动词。利用这个元数据,我们可以推断“kick”以某种方式将“I”和“ball”联系起来,使我们能够在单词之间形成一种关系。这就是词性标注如此重要的原因。如果不知道一些单词是名词,另一些是动词等,那么机器将无法映射词条之间的关系。

依存句法分析(dependency parsing)涉及标注单个词条之间的关系,为句子分配句法结构。一旦这些关系被标注,整个句子就可以被构造为词条集合元素之间的一系列关系。一旦识别出文本的内在结构,计算机就更容易处理文本。想想看,如果你面前的句子中所有的单词都没有顺序,你又不知道语法规则,那么理解这个句子该有多么困难。同样,在机器执行依存句法分析之前,它对词条化的文本的结构几乎一无所知。一旦结构变得清晰,处理文本就会简单一些。

组块(chunking)包括将相关的词条组合成一个词条、创建相关的名词、相关的动词组等。例如,“New York City”可以被视为单个词条或块,而不是三个单独的词条。组块就是让这种操作成为可能的过程。一旦机器将原始文本分解成词条,识别出词性,并标注出每个词条与文本中其他词条之间的关系,就需要进行组块。组块将类似的词条组合在一起,使分析文本的整个过程更容易执行。例如,与其将“New”“York”和“City”作为三个单独的词条,不如推断它们是相关的,并将它们组合到一个组(或块)中。然后,我们可以把这个块和文本中的其他块联系起来。一旦我们对整个词条集都进行这样的处理,便会得到一个小得多的词条和块的工作集合。

词形还原(lemmatization)是将词语转化为其基本形式的过程。例如,词形还原将“horses”转换为“horse”,将“slept”转换为“sleep”,将“biggest”转换为“big”。词形还原可以让计算机简化它必须执行的文本处理工作。在执行词形还原后,文本处理将直接使用词的基本形式,而不是使用其变体。

词干提取(stemming)是一个与词形还原有关的过程,但更简单。词干提取将单词简化为词干。词干提取算法通常基于规则。例如,“biggest”这个词会被简化为“big”,但是“slept”这个词不会被简化。词干提取有时会产生无意义的子词,考虑到这个原因,我们更倾向于使用词形还原而不是词干提取。根据字典,词形还原将单词返回到其基本形式或规范形式,与词干提取相比,这是一个更昂贵的过程,因为它需要知道单词的词性才能顺利执行。

分词、词性标注、依存句法分析、组块、词形还原和词干提取是处理下游NLP(自然语言处理)应用中的自然语言任务的基础。换句话说,这些任务是达到目的的手段。

二、自然语言处理开源库spacy介绍

spacy(https://spacy.io)是一个用于自然语言处理的开源库,于2015年首次发布,它具有“炫目”的高性能,并且是使用Python和Cython构建的。在spacy之前,自然语言工具包(NLTK;https://www.nltk.org)是研究人员中首选的NLP库,但NLTK慢慢过时(最初发布于2001年)且不易规模化。spacy是第一个面向商业受众的现代自然语言处理库,它在设计上充分考虑了产品规模化的需求。现在作为企业自然语言处理应用程序的首选库之一,它支持超过73+种语言,并为25种语言提供了训练代码。基于自然处理领域的最新研究,spaCy提供了一系列高效且易用的工具,用于文本预处理、文本解析、命名实体识别、词性标注、句法分析和文本分类等任务。

三、spaCy的安装

官网(https://spacy.io)提供了详细的安装指引,但通过python安装模型库的时候因各种原因会报错。

但通过python安装模型库的时候因各种原因会报错。可以直接先在github (https://github.com/explosion/spacy-models/releases)上下载模型,然后通过pip安装模型.

 

模型后缀的解释:

后缀为 sm:en_core_web_sm-3.7.1 代表 small 模型;

后缀为 md: 代表 middle 模型;

后缀为 lg: 代表 large 模型;

后缀为 trf: 代表涵盖 transformer 模型

三、spaCy实践

3.1 查找单词、短语、名称和概念

import spacy
nlp = spacy.blank("en")
doc = nlp("Hello world!")
token = doc[1]
print(token.text)

当使用nlp对象处理文本时,spaCy会创建一个Doc对象——“文档”的缩写。文档允许以结构化的方式访问有关文本的信息,并且不会丢失任何信息。

Token对象表示文档中的标记,例如单词或标点符号。要在特定位置获取令牌,可以索引到文档中。令牌对象还提供各种属性,可以访问有关令牌的更多信息。例如,.text属性返回逐字标记文本。

Span对象是由一个或多个标记组成的文档切片。它只是Doc的一个视图,本身不包含任何数据。要创建跨度,可以使用Python的切片表示法。例如,1:3将创建一个切片,从位置1的标记开始,直到–但不包括位置3处的令牌。

 

import spacy
nlp = spacy.blank("en")
doc = nlp("Hello world!")
span = doc[1:3]
print(span.text)

词汇属性

import spacy
nlp = spacy.blank("en")
doc = nlp("It cousts $5.")
print("Index: ",[token.i for token in doc])
print("Text: ",[token.text for token in doc])
print("是否是字母:",[token.is_alpha for token in doc])
print("是否是标点:",[token.is_punct for token in doc])
print("是否是数字:",[token.like_num for token in doc])

3.2 分词

在处理过程中,spaCy首先对文本进行标记,即将其分段为单词、标点符号等Token。这是通过应用每种语言特有的规则来实现的。Token表示自然语言文本的最小单位。每个Token都代表着文本中的一个原子元素,通常是单词或标点符号。

import spacy
nlp = spacy.load("zh_core_web_sm")for token in doc:
    print(token.text)
我
喜欢
自然
语言
处理
技术
!
View Code

 

我们可以添加自定义词典来解决该问题,但是要注意的是自定义词典添加只针对某些语言模型。

import spacy
nlp = spacy.load("zh_core_web_sm")
nlp.tokenizer.pkuseg_update_user_dict(["自然语言处理"])
doc = nlp("我喜欢自然语言处理技术!")
for token in doc:
    print(token.text)
我
喜欢
自然语言处理
技术
!
View Code

3.3 词性标注与依存关系

spaCy在分词后,会对句子中每个词进行词性标注以及确定句子中单词之间的语法关系,要注意这些关系具体范围取决于所使用的模型。示例代码如下所示:

import spacy
nlp = spacy.load("zh_core_web_sm")
doc = nlp("我吃了个肉夹馍")
# token.text: 单词的原始形式。
# token.lemma_: 单词的基本形式(或词干)。例如,“running”的词干是“run”。
# token.pos_: 单词的粗粒度的词性标注,如名词、动词、形容词等。
# token.tag_: 单词的细粒度的词性标注,提供更多的语法信息。
# token.dep_: 单词在句子中的依存关系角色,例如主语、宾语等。
# token.head: 属性返回句法头词符。你可以认为这是词在句子中所依附的母词符
for token in doc:
    print(token.text, token.pos_,  token.dep_, token.head.text)

依存关系的定义:

代词"我"是一个依附在动词(这里是"吃")上的名词主语。名词"肉夹馍"是一个依附在动词"吃"上面的目的语。这个肉夹馍被主语"我"吃掉了。

 

 

3.4 命名实体识别

命名实体识别(Named Entity Recognition, 简称NER)是自然语言处理中的一项基础任务,应用范围非常广泛。 NER是指识别文本中具有特定意义或者指代性强的实体,通常包括人名、地名、机构名、日期时间、专有名词等。spaCy使用如下:

import spacy
nlp = spacy.load("zh_core_web_sm")
# 添加自定义词汇
nlp.tokenizer.pkuseg_update_user_dict(["广州塔","小蛮腰"])
# 自定义词汇可能不会进入实体识别。
doc = nlp("广州塔,昵称小蛮腰,其位于中国广东省广州市海珠区(艺洲岛)赤岗塔附近,距离珠江南岸125米,与珠江新城、花城广场、海心沙岛隔江相望。广州塔塔身主体高454米,天线桅杆高146米,总高度600米")
for ent in doc.ents:
    # 实体文本,开始位置,结束位置,实体标签
    print(ent.text, ent.start_char, ent.end_char, ent.label_)
中国 13 15 GPE
广州市 18 21 GPE
珠江 37 39 LOC
125米 41 45 QUANTITY
珠江新城 47 51 ORG
广州塔 66 69 GPE
454米 74 78 QUANTITY
146米 84 88 QUANTITY
600米 92 96 QUANTITY
View Code

3.5 词向量与相似性

词向量是自然语言处理中一种重要的表示方式,它将单词映射为实数向量。这种表示方式能够捕捉单词之间的语义关系,并将语义信息转化为计算机能够处理的数值形式。

传统的自然语言处理方法往往将文本表示为离散的符号,例如独热编码或者词袋模型。然而,这种方法忽略了单词之间的语义相似性,而且维度过高,造成稀疏性问题。相比之下,词向量通过将每个单词映射到连续的向量空间中,可以更好地捕捉单词之间的语义关系,并且降低了特征空间的维度,使得文本处理更加高效。通过计算两个词向量之间的距离或夹角可以衡量词向量的相似性。

提取句子中每一个词的词向量代码如下:

import spacy
# 加载中文模型"zh_core_web_sm"
nlp = spacy.load("zh_core_web_sm")
# 对给定文本进行分词和词性标注
tokens = nlp("广州塔,昵称小蛮腰,其位于中国广东省广州市海珠区。")
# 遍历分词后的每个词语
for token in tokens:
    # 输出词语的文本内容、是否有对应的向量表示、向量范数和是否为未登录词(Out-of-vocabulary,即不在词向量词典中的词)
    print(token.text, token.has_vector, token.vector_norm, token.is_oov)
广州塔 True 11.11559 True
, True 10.881732 True
昵称 True 11.425206 True
小 True 13.203326 True
蛮 True 11.324032 True
腰 True 10.01184 True
, True 10.610201 True
其 True 11.367814 True
位于 True 11.852476 True
中国 True 15.4708395 True
广东省 True 12.754725 True
广州市 True 12.668334 True
海珠区 True 11.086041 True
。 True 13.315495 True
View Code

spaCy提供了similarity函数以计算两个文本向量的相似度。示例代码如下:

import spacy
nlp = spacy.load("zh_core_web_sm")
doc1 = nlp("东方明珠是一座位于中国上海市的标志性建筑")
doc2 = nlp("广州塔是位于中国广东省广州市的标志性建筑")
# 计算两个文本的相似度
print(doc1, "<->", doc2, doc1.similarity(doc2))
东方明珠是一座位于中国上海市的标志性建筑 <-> 广州塔是位于中国广东省广州市的标志性建筑 0.7901337486881256
View Code

Tip: spacy.explain方法,快速获得大部分常见的标注和标签定义

 

四、spaCy结构体系

当在一个文本上调用nlp模型时,spaCy首先对文本进行分词处理,生成一个Doc对象。接着,Doc对象将在几个不同的步骤中进行处理。训练好的处理流程通常包括词性标注器、依存句法解析器和实体识别器等处理组件。这些组件相互独立,每个处理流程组件都会返回处理后的Doc对象,然后将其传递给下一个组件。

最终生成的Doc对象是一个包含了所有单词和标点符号的序列,每个单词被表示为Token对象。每个Token对象包含了单词本身的内容、词性标注、词形还原后的形式等信息。以下图片解释了使用spaCy进行文本处理的过程。

 

 

如上图所示,模型管道中所涉及到的模块主要取决于该模型的结构和训练方式。其中分词tokenizer是一个特殊的组件且独立于其他组件之外,这是因为其他组件模块在调用前都会先调用tokenizer以对字符串进行分词。所有支持主要的模块如下,这些模块的使用已在前一章进行介绍。

 

一个spacy的模型所支持的文本处理组件查看方式如下:

import spacy
# 加载中文模型"zh_core_web_sm"
nlp = spacy.load("zh_core_web_sm")
# 查看所支持的组件
nlp.pipe_names
['tok2vec', 'tagger', 'parser', 'attribute_ruler', 'ner']

五、spaCy中的数据结构

Language类、Vocab和Doc对象。

Language类用于处理文本并将其转换为Doc对象。它通常存储为一个名为nlp的变量。Doc对象拥有令牌序列及其所有注释。通过在Vocab中集中字符串、词向量和词法属性。这些主要类和对象的介绍如下所示:

常用模块的介绍如下:

Doc:

Doc是spaCy中一个重要的对象,它代表了一个文本文档,并包含了文本中的所有信息,如单词、标点、词性、依赖关系等。可以通过spaCy的Language对象对文本进行处理,得到一个Doc对象。

DocBin:

DocBin 是用于高效序列化和反序列化Doc对象的数据结构,以在不同的过程中保存和加载Doc对象。使用代码如下:

# 导入所需的库
import spacy
from spacy.tokens import DocBin
# 加载英文预训练模型
nlp = spacy.load("en_core_web_sm")
# 定义待处理文本
texts = ["This is sentence 1.", "And this is sentence 2."]
# 将每个文本转化为Doc对象,用nlp处理并保存到docs列表中
docs = [nlp(text) for text in texts]
# 创建一个新的DocBin对象,用于保存文档数据,并启用存储用户数据的功能
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()
# 从字节数据中恢复加载DocBin对象
loaded_docbin = DocBin().from_bytes(bytes_data)

# 使用nlp.vocab获取词汇表,并通过DocBin获取所有加载的文档
loaded_docs = list(loaded_docbin.get_docs(nlp.vocab))

# 输出加载的文档
loaded_docs
View Code
[This is sentence 1., And this is sentence 2.]

Example:

Example用于训练spaCy模型,它包含了一个输入文本(Doc)和其对应的标注数据。关于spaCy模型的训练。

Language:

Language是spaCy的核心对象之一,它负责处理文本的预处理、词性标注、句法分析等任务。可以通过spacy.load()来加载一个具体的语言模型,获取对应的Language对象。

Lexeme:

Lexeme(语素)是词汇表中和语境无关的元素,在词汇表中查询一个字符串或者一个哈希ID就会获得一个Lexeme,Lexeme可以暴露出一些属性,就像词符一样。它们代表着一个词的和语境无关的信息,比如文本本身,或者是这个词是否包含了英文字母。Lexeme中没有词性标注、依存关系或者实体标签这些和语境关联的信息。

import spacy
nlp = spacy.load("zh_core_web_sm")
doc = nlp("我爱喝咖啡。")
lexeme = nlp.vocab["咖啡"]
# 打印词汇的属性
print(lexeme.text, lexeme.orth, lexeme.is_alpha)

包含了一个词的和语境无关的信息

  • 词组的文本:lexeme.textlexeme.orth(哈希值)
  • 词汇的属性如lexeme.is_alpha
  • 并不包含和语境相关的词性标注、依存关系和实体标签

事实上对于Lexeme,只要可能,spaCy就会尝试将数据存储在一个词汇表Vocab中,该词汇表将由多个模型共享。为了节省内存,spaCy还将所有字符串编码为哈希值,如果一个词出现多过一次,我们就不需要每次都多存储一次。相反,spaCy使用哈希方程生成一个ID,和对应的字符串一起在字符串库中仅存储一次。 nlp.vocab.strings这个字符串库就在nlp.vocab.strings里。这是一个双向的查询表。你以查找一个字符串获得其哈希值, 也可以查找一个哈希值获得其字符串值。 spaCy内部的信息交流都是通过哈希ID进行的。然而哈希ID不能逆求解,如果一个词不在词汇表里,那我们也没法办拿到它的字符串。

 如下所示,不同模型下“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
View Code

 

六、官方介绍与练习地址

https://course.spacy.io/zh

 官方练习小题目:

 

参考

https://spacy.io/

https://github.com/explosion/spaCy

https://blog.csdn.net/LuohenYJ/article/details/131965198

《自然语言处理实战:预训练模型应用及其产品化》

 

posted @ 2024-02-05 08:59  Eleven_Liu  阅读(856)  评论(0编辑  收藏  举报