指南

语言特征

智能处理原始文本非常困难:大多数单词都很罕见,而且看起来完全不同的单词往往表达几乎相同的意思。同样的单词以不同顺序排列可能表达完全不同的含义。甚至在许多语言中,将文本分割成有用的单词单元都很困难。虽然仅从原始字符出发可以解决部分问题,但通常最好利用语言学知识来添加有用信息。这正是spaCy的设计目标:输入原始文本,就能获得一个带有多种注释的Doc对象。

词性标注 需要模型

在分词之后,spaCy可以对给定的Doc进行解析标注。这时训练好的处理流程及其统计模型就会发挥作用,使spaCy能够预测在当前上下文中最可能适用的标签或标注。训练好的组件包含由系统通过学习足够多示例后生成的二进制数据,这些数据使其能够做出适用于整个语言的预测——例如,在英语中跟在"the"后面的单词很可能是名词。

语言注释可通过 Token属性获取。与许多NLP库类似,spaCy 将所有字符串编码为哈希值以减少内存使用并提高 效率。因此要获取属性的可读字符串表示形式,我们 需要在属性名称后添加下划线_

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本词元词性标签依存关系形状字母停用词
AppleapplePROPNNNPnsubjXxxxxTrueFalse
isbeAUXVBZauxxxTrueTrue
lookinglookVERBVBGROOTxxxxTrueFalse
atatADPINprepxxTrueTrue
buyingbuyVERBVBGpcompxxxxTrueFalse
U.K.u.k.PROPNNNPcompoundX.X.FalseFalse
startupstartupNOUNNNdobjxxxxTrueFalse
forforADPINprepxxxTrueTrue
$$SYM$quantmod$FalseFalse
11NUMCDcompounddFalseFalse
billionbillionNUMCDpobjxxxxTrueFalse

使用spaCy内置的displaCy可视化工具,以下是我们示例句子及其依存关系的展示效果:

Morphology

屈折形态学是指通过添加前缀或后缀来修改单词的词根形式,这些词缀指定其语法功能但不改变其词性。我们将词元(词根形式)与一个或多个形态特征屈折变化(修改/组合)以创建表层形式。以下是一些示例:

上下文表层形式词元词性形态特征
I was reading the paperreadingreadVERBVerbForm=Ger
I don’t watch the news, I read the paperreadreadVERBVerbForm=Fin, Mood=Ind, Tense=Pres
I read the paper yesterdayreadreadVERBVerbForm=Fin, Mood=Ind, Tense=Past

形态特征存储在MorphAnalysis下的Token.morph中,这允许您访问各个形态特征。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

统计形态学 v3.0需要模型

spaCy的统计组件Morphologizer会将形态学特征和粗粒度词性标注分配给Token.morphToken.pos

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

基于规则的词形变化

对于像英语这样形态系统相对简单的语言,spaCy可以通过基于规则的方法分配形态特征,该方法使用词符文本细粒度词性标签来生成粗粒度词性标签和形态特征。

  1. 词性标注器为每个标记分配一个细粒度词性标签。在API中,这些标签称为Token.tag。它们表示词性(例如动词)和一定程度的形态信息,例如该动词是过去时态(例如在宾州树库中过去时动词用VBD表示)。
  2. 对于未被先前流程设置粗粒度词性的单词,映射表会将细粒度标签映射为粗粒度词性标签和形态特征。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

词形还原 v3.0

spaCy 提供了两个用于词形还原的管道组件:

  1. Lemmatizer组件提供了一个可配置的组件,支持基于查找和规则的词形还原方法。每种语言可以扩展Lemmatizer作为其语言数据的一部分。
  2. EditTreeLemmatizer v3.3组件提供了一个可训练的词汇还原器。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

spaCy的词汇还原器数据包含在spacy-lookups-data包中。提供的训练好的流水线已经包含所有需要的表格,但如果您正在创建新的流水线,您可能需要安装spacy-lookups-data以便在初始化词汇还原器时提供数据。

Lookup lemmatizer

对于没有标注器或形态分析器的管道,只要提供了查找表(通常通过spacy-lookups-data),就可以向管道中添加查找式词形还原器。该查找式词形还原器会在查找表中直接查询词符表面形式,而不考虑词符的词性或上下文。

基于规则的词形还原器 需要模型

在训练包含词性标注组件(如带有POS映射的形态分析器或标注器)的流水线时,可以通过spacy-lookups-data中的规则表添加基于规则的词形还原器:

基于规则的确定性词形还原器会根据先前分配的粗粒度词性和形态学信息,将表面形式映射到一个词元,而无需参考该标记的上下文。基于规则的词形还原器还接受基于列表的例外文件。对于英语,这些例外文件是从WordNet获取的。

可训练的词形还原器 需要模型

EditTreeLemmatizer 可以从包含词元标注的训练语料库中学习形式到词元的转换规则。这消除了编写特定语言规则的需求,并且在许多情况下,能提供比基于查找和规则的词形还原器更高的准确率。

依存句法分析 需要模型

spaCy拥有一个快速准确的句法依存关系解析器,并提供丰富的API用于遍历语法树。该解析器还支持句子边界检测功能,并允许您遍历基本名词短语或"块"。您可以通过调用doc.has_annotation("DEP")来检查Doc对象是否已被解析,该方法会检查Token.dep属性是否已被设置并返回布尔值。如果结果为False,默认的句子迭代器将抛出异常。

名词短语块

名词块是“基础名词短语”——以名词为核心、结构扁平化的短语。你可以将名词块理解为一个名词加上描述该名词的词语,例如“奢华的绿草地”或“世界上最大的科技基金”。要获取文档中的名词块,只需遍历Doc.noun_chunks即可。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本root.textroot.dep_root.head.text
Autonomous carscarsnsubjshift
insurance liabilityliabilitydobjshift
manufacturersmanufacturerspobjtoward

spaCy使用术语headchild来描述依赖树中通过单条弧线连接的单词。术语dep用于表示弧线标签,它描述了将子节点连接到头节点的句法关系类型。与其他属性一样,.dep的值是一个哈希值。您可以通过.dep_获取其字符串值。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本依存关系头部文本头部词性子节点
AutonomousamodcarsNOUN
carsnsubjshiftVERBAutonomous
shiftROOTshiftVERBcars, liability, toward
insurancecompoundliabilityNOUN
liabilitydobjshiftVERBinsurance
towardprepshiftNOUNmanufacturers
manufacturerspobjtowardADP

由于句法关系构成一棵树,每个词都有唯一一个中心词。因此可以通过遍历句子中的词来迭代树中的依存弧。这通常是从下往上匹配目标依存弧的最佳方式:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

如果尝试从上方开始匹配,你将需要迭代两次。一次用于头部,然后再遍历子节点:

要遍历子节点,请使用token.children属性,该属性提供了一系列Token对象。

为了方便在标记周围的局部树中进行遍历,还提供了一些便捷属性。Token.leftsToken.rights属性分别提供了标记前后出现的语法子节点序列。这两个序列都遵循句子顺序。此外还有两个整数类型的属性Token.n_leftsToken.n_rights,用于表示左右子节点的数量。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

你可以通过语法头部使用Token.subtree属性获取整个短语。这会返回一个有序的标记序列。你可以使用Token.ancestors属性向上遍历树结构,并通过Token.is_ancestor检查支配关系

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本依存关系左节点数右节点数祖先节点
Creditnmod02holders, submit
andcc00holders, submit
mortgagecompound00account, Credit, holders, submit
accountconj10Credit, holders, submit
holdersnsubj10submit

最后,.left_edge.right_edge属性特别有用,因为它们提供了子树的第一个和最后一个词符。这是为语法短语创建Span对象的最简单方法。请注意,.right_edge给出的是子树内部的一个词符——因此如果将其用作范围的结束点,别忘了+1

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本词性依存关系中心词文本
Credit and mortgage account holdersNOUNnsubjsubmit
mustVERBauxsubmit
submitVERBROOTsubmit
theirADJpossrequests
requestsNOUNdobjsubmit

依存句法分析可以成为信息抽取的有用工具,特别是当与其他预测(如命名实体)结合使用时。以下示例提取货币和金额值(即标记为MONEY的实体),然后使用依存分析找到它们所指的名词短语——例如"Net income""$9.4 million"

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

可视化依赖关系

理解spaCy依存关系解析器的最佳方式是交互式体验。为简化这一过程,spaCy配备了可视化模块。您可以将DocDoc对象列表传递给displaCy,然后运行displacy.serve启动网络服务器,或使用displacy.render生成原始标记。若想编写针对特定句法结构的规则,只需将句子输入可视化工具,观察spaCy如何对其进行标注即可。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

禁用解析器

在spaCy提供的训练好的管道中,解析器默认作为标准处理管道的一部分被加载和启用。如果您不需要任何句法信息,应该禁用解析器。禁用解析器将使spaCy加载和运行得更快。如果您想加载解析器,但需要针对特定文档禁用它,您还可以通过nlp对象控制其使用。更多详情请参阅关于禁用管道组件的使用指南。

命名实体识别

spaCy拥有一个极其快速的统计实体识别系统,能够为连续的标记序列分配标签。默认的训练管道可以识别各种命名和数字实体,包括公司、地点、组织和产品。您可以为实体识别系统添加任意类别,并通过新样本更新模型。

命名实体识别基础

命名实体是指被赋予名称的"现实世界对象",例如人物、国家、产品或书名。spaCy能够通过模型预测来识别文档中的各类命名实体。由于模型是基于统计的,且高度依赖其训练样本,因此效果并非总是完美,根据具体使用场景可能需要进行后续调整。

命名实体可以通过Docents属性获取:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本起始位置结束位置标签描述
Apple05ORGCompanies, agencies, institutions.
U.K.2731GPEGeopolitical entity, i.e. countries, cities, states.
$1 billion4454MONEYMonetary values, including unit.

使用spaCy内置的displaCy可视化工具,以下是我们示例句子及其命名实体的展示效果:

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

访问实体标注和标签

访问实体标注的标准方式是通过doc.ents属性,它会生成一个Span对象序列。实体类型可以通过哈希值或字符串形式访问,使用属性ent.labelent.label_Span对象表现为一个词符序列,因此您可以遍历实体或通过索引访问。您还可以获取整个实体的文本形式,就像它是一个单独的词符。

你也可以通过token.ent_iobtoken.ent_type属性来访问词符的实体标注。token.ent_iob表示实体标签是开始、延续还是结束。如果词符上没有设置实体类型,它将返回空字符串。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

文本ent_iobent_iob_ent_type_描述
San3B"GPE"beginning of an entity
Francisco1I"GPE"inside an entity
considers2O""outside an entity
banning2O""outside an entity
sidewalk2O""outside an entity
delivery2O""outside an entity
robots2O""outside an entity

设置实体标注

为确保词符标注序列保持一致,您必须在文档级别设置实体标注。但您不能直接写入token.ent_iobtoken.ent_type属性,因此设置实体的最简单方法是使用doc.set_ents函数并将新实体创建为Span

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

请注意,Span是通过起始和结束的词元索引初始化的,而不是字符偏移量。要从字符偏移量创建span,请使用Doc.char_span

从数组设置实体标注

你也可以使用doc.from_array方法来分配实体标注。为此,你需要在导入的数组中同时包含ENT_TYPEENT_IOB属性。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

在Cython中设置实体标注

最后,如果你编译一个Cython函数,你总是可以写入底层结构。这很容易做到,并且允许你编写高效的原生代码。

显然,如果直接写入TokenC*结构体数组,您需要负责确保数据保持一致性状态。

内置实体类型

可视化命名实体

displaCy ENT visualizer可视化工具可让您交互式探索实体识别模型的行为。如果您正在训练模型,自行运行可视化会非常有用。为帮助您实现这一点,spaCy内置了可视化模块。您可以将DocDoc对象列表传递给displaCy,并通过displacy.serve运行网络服务器,或使用displacy.render生成原始标记。

更多详情和示例,请参阅 spaCy可视化使用指南

命名实体示例

When Sebastian Thrun PERSON started working on self-driving cars at Google ORG in 2007 DATE, few people outside of the company took him seriously.

实体链接

为了将命名实体锚定到“现实世界”中,spaCy提供了执行实体链接的功能,该功能将文本实体解析为知识库(KB)中的唯一标识符。您可以创建自己的KnowledgeBase并使用该自定义知识库训练一个新的EntityLinker

关于如何定义知识库和训练实体链接模型的示例,请参阅本教程,该教程使用了spaCy projects

访问实体标识符 需要模型

标注的知识库标识符可以通过哈希值或字符串形式访问,使用Span对象的ent.kb_ident.kb_id_属性,或者Token对象的ent_kb_ident_kb_id_属性。

Tokenization

分词是将文本分割成有意义片段的任务,这些片段称为标记。分词器的输入是unicode文本,输出是一个Doc对象。要构建一个Doc对象,你需要一个Vocab实例、一个word字符串序列,以及可选的spaces布尔值序列,这能让你保持标记与原始字符串的对齐。

在处理过程中,spaCy首先会对文本进行分词,也就是将其分割成单词、标点符号等。这是通过应用针对每种语言的特定规则来完成的。例如,句子末尾的标点符号应该被分开——而"U.K."则应保持为一个词元。每个Doc由独立的词元组成,我们可以遍历它们:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

012345678910
苹果正在考虑收购英国初创公司价格$1十亿

首先,原始文本会按照空白字符进行分割,类似于text.split(' ')。然后,分词器会从左到右处理文本。对于每个子字符串,它会执行两项检查:

  1. 子字符串是否匹配分词器异常规则? 例如,"don't"不包含空格,但应拆分为两个词元"do"和"n't",而"U.K."应始终保持为一个词元。

  2. 能否拆分前缀、后缀或中缀? 例如逗号、句号、连字符或引号等标点符号。

如果匹配成功,规则将被应用,分词器继续循环处理,从新分割的子字符串开始。这样,spaCy可以分割复杂、嵌套的标记,比如缩写组合和多个标点符号的组合。

Example of the tokenization process

虽然标点符号规则通常相当通用,但分词器异常强烈依赖于每种语言的具体特性。这就是为什么每种可用语言都有其自己的子类,如EnglishGerman,它们会加载硬编码数据和异常规则列表。

spaCy引入了一种新颖的分词算法,在性能、定义简便性和与原字符串对齐的便捷性之间实现了更好的平衡。

在消耗前缀或后缀后,我们会再次检查特殊案例。我们希望特殊案例能处理像英语中"don't"这样的情况,并且希望同样的规则适用于"(don't)!"。我们通过先拆分开括号,然后是感叹号,接着是闭括号,最后匹配特殊案例来实现这一点。以下是一个用Python实现的算法,优化了可读性而非性能:

该算法可以总结如下:

  1. 遍历以空格分隔的子字符串。
  2. 检查是否针对此子字符串有明确定义的特殊情况。如果有,则使用它。
  3. 查找标记匹配项。如果找到匹配项,则停止处理并保留此标记。
  4. 检查我们是否为此子字符串明确定义了特殊情况。如果有,则使用它。
  5. 否则,尝试消耗一个前缀。如果成功消耗了前缀,则返回步骤#3,以确保令牌匹配和特殊情况始终优先处理。
  6. 如果我们没有消耗前缀,尝试消耗后缀然后回到步骤3。
  7. 如果无法匹配前缀或后缀,则查找URL匹配项。
  8. 如果没有匹配的URL,则查找特殊情况。
  9. 查找“中缀”——例如连字符等,并将子字符串在所有中缀处分割为词元。
  10. 当我们无法再处理字符串的更多部分时,将其视为单个标记。
  11. 对文本进行最终检查,查找包含空格或因词缀增量处理而遗漏的特殊情况。

全局语言特定的分词器数据通过spacy/lang中的语言数据提供。分词器例外定义了特殊案例,比如英语中的"don't"需要被拆分为两个标记:{ORTH: "do"}{ORTH: "n't", NORM: "not"}。前缀、后缀和中缀主要定义标点规则——例如,何时拆分句点(在句子末尾),以及何时保留包含句点的标记完整(如"U.S."这样的缩写)。

针对特定语言但可以在该语言范围内通用的分词规则,理想情况下应该存放在spacy/lang的语言数据中——我们始终欢迎提交pull request!任何特定领域或文本类型的规则——比如金融交易缩写词或巴伐利亚青年俚语——应该作为特殊规则添加到你的分词器实例中。如果你需要处理大量自定义规则,可能需要创建一个完全自定义的子类。


添加特殊分词规则

大多数领域至少存在一些需要自定义分词规则的特殊情况。这可能是非常特定的表达方式,或仅在该特定领域使用的缩写。以下是如何向现有的Tokenizer实例添加特殊案例规则:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

特殊情况不需要匹配整个以空格分隔的子字符串。 分词器会逐步拆分标点符号,并继续查找 剩余的子字符串。特殊情况的规则也优先于 标点符号拆分。

调试分词器

上述伪代码的一个可用实现是nlp.tokenizer.explain(text),可用于调试。它会返回一个元组列表,显示每个标记匹配了哪个分词器规则或模式。生成的标记与nlp.tokenizer()相同,除了空白标记:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

自定义spaCy的Tokenizer类

假设你想为一种新语言或特定领域创建一个分词器。可能需要定义以下六个方面:

  1. 一个包含特殊案例的字典。这用于处理诸如缩写词、计量单位、表情符号、特定缩写等情况。
  2. 一个函数 prefix_search,用于处理前置标点符号,例如开引号、开括号等。
  3. 一个函数 suffix_search,用于处理后续标点符号,例如 逗号、句号、右引号等。
  4. 一个函数 infix_finditer,用于处理非空格分隔符,例如连字符等。
  5. 一个可选的布尔函数token_match,用于匹配永远不应被拆分的字符串,覆盖中缀规则。适用于数字等场景。
  6. 一个可选的布尔函数url_match,其功能类似于token_match,区别在于在应用匹配前会移除前缀和后缀。

通常您不需要创建Tokenizer子类。标准用法是使用re.compile()构建正则表达式对象,并传递其.search().finditer()方法:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

如果需要改为继承分词器类,需要专门实现的相关方法是 find_prefixfind_suffixfind_infix

修改现有规则集

在许多情况下,您不一定需要完全自定义规则。有时您只是想在前缀、后缀或中缀中添加另一个字符。默认的前缀、后缀和中缀规则可以通过nlp对象的DefaultsTokenizer属性(如Tokenizer.suffix_search)获取,这些属性是可写的,因此您可以使用修改后的默认规则通过编译的正则表达式对象覆盖它们。spaCy附带了一些实用函数来帮助您编译正则表达式——例如compile_suffix_regex

同样,你可以从默认后缀中移除一个字符:

Tokenizer.suffix_search属性应该是一个接收unicode字符串并返回正则表达式匹配对象None的函数。通常我们使用已编译正则表达式对象的.search属性,但您也可以使用其他行为相同的函数。

前缀、中缀和后缀规则集不仅包含单个字符,还包括考虑周围上下文的详细正则表达式。例如,有一个正则表达式会将字母间的连字符视为中缀。如果您不希望分词器在字母间的连字符处分割,可以修改lang/punctuation.py中现有的中缀定义:

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

要查看默认正则表达式的概述,请参阅 lang/punctuation.py 和 特定语言的定义,例如德语的 lang/de/punctuation.py

将自定义分词器集成到处理流程中

分词器是处理管道的第一个组件,也是唯一一个无法通过写入nlp.pipeline来替换的组件。这是因为它与其他所有组件的签名不同:它接收文本并返回一个Doc,而其他所有组件都预期接收已分词的Doc

The processing pipeline

要覆盖现有的分词器,你需要将nlp.tokenizer替换为一个自定义函数,该函数接收文本并返回一个Doc

参数类型描述
textstrThe raw text to tokenize.

示例1:基础空格分词器

这是一个最基本的空白符分词器示例。它使用共享词汇表,因此可以构建Doc对象。当对文本调用时,它会返回一个由单空格字符分割文本组成的Doc对象。然后我们可以用自定义分词器的实例覆盖nlp.tokenizer属性。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

示例2:第三方分词器(BERT词片段)

你可以使用相同的方法接入任何其他第三方分词器。你的自定义可调用对象只需返回一个包含分词器生成标记的Doc对象。本示例中,封装器使用了由tokenizers库提供的BERT word piece分词器。现在spaCy返回的Doc对象中的标记与该分词器生成的精确词片段完全匹配。

自定义BERT词片分词器

使用自定义分词进行训练 v3.0

spaCy的训练配置描述了用于构建和训练流程的设置、超参数、管道和分词器。[nlp.tokenizer]块引用了一个注册函数,该函数接收nlp对象并返回一个分词器。这里,我们在@tokenizers注册表中注册了一个名为whitespace_tokenizer的函数。为确保spaCy在训练期间知道如何构建您的分词器,您可以通过在运行spacy train时设置--code functions.py来传入您的Python文件。

functions.py

注册的函数也可以接收来自配置的参数。这使您能够快速更改并跟踪不同的设置。在这里,名为bert_word_piece_tokenizer的注册函数接收两个参数:词汇表文件的路径以及是否将文本转为小写。Python类型提示strbool确保接收的值具有正确的类型。

functions.py

为了避免在配置文件中硬编码本地路径,你也可以在运行spacy train时,通过使用override参数--nlp.tokenizer.vocab_file在命令行界面设置词汇表路径。有关使用注册函数的更多详情,请参阅training with custom code文档。

使用预分词的文本

spaCy 默认通常假设您的数据是原始文本。然而, 有时您的数据是部分标注的,例如包含预先存在的分词、 词性标注等。最常见的情况是您拥有 预定义的分词。如果您有一个字符串列表,可以直接创建 Doc对象。可选地,您还可以指定一个布尔值列表, 用于指示每个单词后是否跟随空格。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

如果提供,空格列表必须与单词列表长度相同。空格列表会影响doc.textspan.texttoken.idxspan.start_charspan.end_char属性。如果不提供spaces序列,spaCy会默认所有单词后面都跟着一个空格。一旦获得Doc对象,就可以通过写入其属性来设置词性标注、句法依存关系、命名实体和其他属性。

对齐分词

spaCy的分词是非破坏性的,并使用针对树库注释兼容性优化的语言特定规则。其他工具和资源有时会以不同方式分词——例如,"I'm"["I", "'", "m"]而不是["I", "'m"]

在这种情况下,您通常需要对分词进行对齐,以便将来自不同来源的注释合并在一起,或者将预训练的BERT模型预测的向量应用于spaCy的分词。spaCy的Alignment对象支持双向分词索引的一对一映射,并能处理多个分词对齐到单个分词的情况。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

以下是上述示例中生成的对齐信息的一些见解:

  • 前四个标记的一对一映射完全相同,这意味着它们相互对应。这很合理,因为它们在输入中也相同:"i""listened""to""obama"
  • x2y.data[6]的值为5,这意味着other_tokens[6] ("podcasts")与spacy_tokens[5](同样为"podcasts")对齐。
  • x2y.data[4]x2y.data[5] 的值都是 4,这意味着 other_tokens 的第4和第5个标记("'""s")都对应到 spacy_tokens 的第4个标记("'s")。

合并与拆分

Doc.retokenize上下文管理器允许您合并和拆分词元。对词元化的修改会被暂存,并在上下文管理器退出时一次性执行。要将多个词元合并为单个词元,需将一个Span传递给retokenizer.merge。通过可选的attrs字典可以设置合并后词元的属性——例如词元原形、词性标注或实体类型。默认情况下,合并后的词元将继承被合并span根节点的相同属性。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

如果attrs中的某个属性是上下文相关的词符属性,它将被应用于底层的Token。例如LEMMAPOSDEP仅适用于上下文中的单词,因此它们是词符属性。如果某个属性是上下文无关的词法属性,它将被应用于底层的Lexeme,即词汇表中的条目。例如,LOWERIS_STOP适用于所有拼写相同的单词,而不考虑上下文。

分词处理

retokenizer.split 方法允许将一个标记(token)拆分为两个或多个标记。这在仅靠分词规则无法满足需求时非常有用。例如,您可能希望将"its"拆分为"it"和"is"两个标记 - 但不拆分表示所有格的"its"。您可以编写基于规则的逻辑来仅找到需要拆分的正确"its",但到那时Doc对象已经完成了分词。

这个分词过程需要更多设置,因为你需要指定单个token的文本、可选的每个token属性,以及这些token应如何附加到现有的语法树。这可以通过提供一个heads列表来实现——要么指定新分出的token要附加到的token,要么当新分出的token需要附加到另一个子token时,使用(token, subtoken)元组。在本例中,"New"应附加到"York"(第二个分出的子token),而"York"应附加到"in"。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

将头部指定为token(token, subtoken)元组列表,允许将分割后的子标记附加到其他子标记上,而无需在分割后跟踪标记索引。

词元头部描述
"New"(doc[3], 1)Attach this token to the second subtoken (index 1) that doc[3] will be split into, i.e. “York”.
"York"doc[2]Attach this token to doc[1] in the original Doc, i.e. “in”.

如果您不关心头部信息(例如,仅运行分词器而不运行解析器),可以将每个子词标记附加到自身:

覆盖自定义扩展属性

如果您注册了自定义的 扩展属性, 在标记化过程中可以通过在attrs"_"键中提供一个属性名到新值的映射字典来覆盖它们。对于合并操作,您需要为最终合并的标记提供一个属性字典。对于拆分操作,您需要提供一个包含自定义属性的字典列表,每个拆分子标记对应一个字典。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

句子分割

通过Doc.sents属性可以访问Doc对象的句子。要查看Doc的句子,您可以遍历Doc.sents,这个生成器会生成Span对象。您可以通过调用Doc.has_annotation并传入属性名"SENT_START"来检查Doc是否具有句子边界。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

spaCy 提供了四种句子分割的替代方案:

  1. 依存关系解析器: 基于统计的 DependencyParser通过完整的依存关系分析提供最精确的句子边界划分。
  2. 统计句子分割器: 统计型 SentenceRecognizer是一个比解析器更简单快速的替代方案,它仅设置句子边界。
  3. 基于规则的管道组件: 基于规则的 Sentencizer使用可自定义的句子结尾标点符号列表来设置句子边界。
  4. Custom function: 您添加到处理管道的自定义函数可以通过写入Token.is_sent_start来设置句子边界。

默认:使用依存句法分析 需要模型

与其他库不同,spaCy使用依存句法分析来确定句子边界。这通常是最准确的方法,但它需要一个训练好的管道来提供准确的预测。如果您的文本更接近通用新闻或网络文本,使用spaCy提供的预训练管道应该可以直接获得良好效果。对于不符合相同规则的社交媒体或对话文本,您的应用程序可能会受益于自定义训练或基于规则的组件。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

spaCy的依存解析器会尊重已设置的边界,因此你可以在解析之前使用自定义组件预处理你的Doc。根据你的文本内容,这也可能提高解析准确性,因为解析器被限制为预测与句子边界一致的解析结果。

统计句子分割器 v3.0需要模型

SentenceRecognizer 是一个简单的统计组件,仅提供句子边界识别功能。相比解析器,它不仅速度更快、体积更小,主要优势还在于训练更简单——因为它只需要标注句子边界,而非完整的依存关系解析。spaCy的预训练管道同时包含解析器和训练好的句子分割器(默认禁用)。若您仅需句子边界识别而不需要解析器,可通过spacy.loadexcludedisable参数加载不含解析器的管道,然后使用nlp.enable_pipe显式启用句子识别器。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

基于规则的流水线组件

Sentencizer组件是一个管道组件,它根据标点符号如.!?来分割句子。如果您只需要句子边界而不需要依赖解析,可以将其插入到您的管道中。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

自定义基于规则的策略

如果你想实现与默认基于规则的句子分割方法不同的自定义策略,也可以创建一个自定义管道组件,该组件接收Doc对象并在每个单独的词元上设置Token.is_sent_start属性。如果设为False,该词元会被明确标记为句子开头。如果设为None(默认值),则被视为缺失值,仍可由解析器覆盖。

这是一个实现预处理规则的组件示例,用于在"..."标记处进行分割。该组件被添加在解析器之前,然后用于进一步分割文本。这是可行的,因为is_sent_start仅对某些标记设置为True——所有其他标记仍将未设置的句子边界指定为None。如果您想为特定数据实现额外规则,同时仍能利用基于依赖关系的句子分割,这种方法会很有用。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

映射与异常处理 v3.0

AttributeRuler 负责管理所有词符级别属性的基于规则的映射和例外。随着从spaCy v2到v3版本中管道组件数量的增长,在每个组件中单独处理规则和例外已变得不切实际,因此AttributeRuler提供了一个单一组件,为所有词符属性映射和例外提供统一的模式格式。

AttributeRuler 使用 Matcher 模式来识别 词元并为其分配提供的属性。如果需要, Matcher 模式可以包含目标词元周围的上下文。 例如,属性规则器可以:

  • 为任何token属性提供异常
  • 细粒度标签映射到粗粒度标签,适用于没有统计形态分析器的语言(替换v2.x版本中语言数据里的tag_map
  • 将标记的表面形式 + 细粒度标签映射到形态特征 (替代v2.x版本中语言数据里的morph_rules)
  • 指定空格标记的标签(替换标记器中硬编码的行为)

以下示例展示了如何为短语"The Who"指定标签和词性标注NNP/PROPN,覆盖统计标注器和词性标注映射提供的标签。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

词向量与语义相似性

相似度是通过比较词向量或"词嵌入"来确定的,即词语的多维语义表示。词向量可以使用类似word2vec的算法生成,通常呈现如下形式:

banana.vector

内置词向量的Pipeline包使它们可以通过Token.vector属性获取。 Doc.vectorSpan.vector默认会返回其词向量的平均值。您还可以检查一个词是否分配了向量,并获取L2范数(可用于向量归一化)。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

单词"dog"、"cat"和"banana"在英语中都非常常见,因此它们是spacy词汇表的一部分,并且拥有对应的词向量。而单词"afskfsd"则非常罕见且属于词汇表外(OOV)词汇——所以它的向量表示由300维的0组成,这意味着它实际上不存在。如果您的应用程序需要更大的词汇量和更多向量,您应该考虑使用更大的spacy模型包或加载完整的词向量包,例如en_core_web_lg,它包含68.5万个独特向量

spaCy能够比较两个对象,并预测它们的相似程度。预测相似度对于构建推荐系统或标记重复内容非常有用。例如,您可以向用户推荐与他们当前查看内容相似的资讯,或者当支持工单与已有工单高度相似时将其标记为重复。

每个DocSpanTokenLexeme都带有一个.similarity 方法,可用于与其他对象进行比较并确定 相似度。当然相似度总是主观的——两个词、片段 或文档是否相似实际上取决于您如何看待它。spaCy的 相似度实现通常采用一种相当通用的相似度定义。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

相似度结果的预期

计算相似度分数在许多情况下都很有帮助,但对其能提供的信息保持现实的预期也很重要。词语之间可以通过多种方式相互关联,因此单一的"相似度"分数始终是不同信号的混合体,而且基于不同数据训练的向量可能产生差异很大的结果,这些结果可能并不符合您的需求。以下是需要牢记的一些重要考量因素:

  • 相似性没有客观的定义。"我喜欢汉堡"和"我喜欢意大利面"是否相似取决于您的应用场景。两者都谈论食物偏好,这使得它们非常相似——但如果您在分析提及的食物时,这些句子就相当不同了,因为它们谈论的是完全不同的食物。
  • DocSpan对象的相似度默认采用平均词向量计算。这意味着"fast food"的向量是"fast"和"food"向量的平均值,这种计算方式不一定能准确反映短语"fast food"的含义。
  • 向量平均意味着多个标记的向量对单词的顺序不敏感。两个用不同措辞表达相同含义的文档将返回较低的相似度分数,而两个恰好包含相同单词但表达不同含义的文档则会获得较高分数。

添加词向量

自定义词向量可以使用多个开源库进行训练,例如 GensimFastText, 或Tomas Mikolov的原始 Word2vec实现。大多数 词向量库会输出易于阅读的文本格式,其中每行 包含单词及其向量。在日常使用中,我们需要 将向量转换为二进制格式,这样加载更快且占用更少 磁盘空间。最简单的方法是使用 init vectors命令行工具。这将输出一个 空的spaCy管道到目录/tmp/la_vectors_wiki_lg中,为您提供 一些优质的拉丁语向量。然后您可以将目录路径传递给 spacy.load或在 [initialize]配置中使用它,当您 训练模型时。

为了帮助您在覆盖范围和内存使用之间取得良好平衡,spaCy的Vectors类允许您将多个键映射到表格的同一行。如果您使用spacy init vectors命令创建词汇表,设置--prune标志时将自动处理向量修剪。您也可以通过以下步骤手动完成:

  1. 从一个词向量包开始,它涵盖了大量词汇。例如,en_core_web_lg包提供了685k个英语词汇的300维GloVe向量。
  2. 如果您的词汇表为Lexeme.prob属性设置了值, 词素将按概率降序排列以确定要修剪的向量。否则,词素将按照它们在Vocab中的顺序排序。
  3. 调用Vocab.prune_vectors并传入你想保留的向量数量。

Vocab.prune_vectors 将当前向量表缩减至指定数量的唯一条目,并返回一个包含被移除单词的字典,该字典映射到(string, score)元组,其中string是被移除单词映射到的条目,而score是两个单词之间的相似度分数。

已移除的词汇

在上面的示例中,“Shore”的词向量被移除并重新映射到“coast”的词向量,这两个词被认为有约73%的相似度。“Leaving”被重新映射到“leaving”的词向量,这两个词完全相同。如果您使用init vectors命令,可以通过设置--prune选项,在将词向量添加到spaCy管道时轻松减小向量的大小:

这将创建一个空白的spaCy管道,其中包含前10,000个单词的向量。向量中的所有其他单词将被映射到保留向量中最接近的向量。

单独添加向量

vector属性是一个只读的numpy或cupy数组(取决于您是否配置了spaCy使用GPU内存),数据类型为float32。该数组为只读状态,以便spaCy尽可能避免不必要的复制操作。您可以通过VocabVectors表来修改向量。如果您有任意格式的向量,使用Vocab.set_vector方法通常是最简单的解决方案,因为您可以用自己的逻辑读取向量,并通过简单的循环进行设置。这种方法可能比一次性处理整个向量表的方法要慢,但在将nlp对象保存到磁盘之前进行一次性转换时,这是一个很好的选择。

添加向量

语言数据

每种语言都有所不同——通常充满了例外情况和特殊规则,尤其是在最常用的词汇中。其中一些例外是跨语言共通的,而另一些则完全特定于某种语言——通常特定到需要硬编码处理。lang模块包含了所有语言特定的数据,这些数据以简单的Python文件形式组织,便于更新和扩展。

目录根目录中的共享语言数据包含可跨语言通用的规则——例如基本标点符号、表情符号、表情图和单字母缩写的规则。子模块中的特定语言数据包含仅与特定语言相关的规则。它还负责整合所有组件并创建Language子类——例如EnglishGerman。这些值在Language.Defaults中定义。

名称描述
Stop words
stop_words.py
List of most common words of a language that are often useful to filter out, for example “and” or “I”. Matching tokens will return True for is_stop.
Tokenizer exceptions
tokenizer_exceptions.py
Special-case rules for the tokenizer, for example, contractions like “can’t” and abbreviations with punctuation, like “U.K.”.
Punctuation rules
punctuation.py
Regular expressions for splitting tokens, e.g. on punctuation or special characters like emoji. Includes rules for prefixes, suffixes and infixes.
Character classes
char_classes.py
Character classes to be used in regular expressions, for example, Latin characters, quotes, hyphens or icons.
Lexical attributes
lex_attrs.py
Custom functions for setting lexical attributes on tokens, e.g. like_num, which includes language-specific words like “ten” or “hundred”.
Syntax iterators
syntax_iterators.py
Functions that compute views of a Doc object based on its syntax. At the moment, only used for noun chunks.
Lemmatizer
lemmatizer.py spacy-lookups-data
Custom lemmatizer implementation and lemmatization tables.

创建自定义语言子类

如果你想自定义语言数据的多个组件或添加对自定义语言或特定领域"方言"的支持,也可以实现自己的语言子类。该子类应定义两个属性:lang(唯一语言代码)和定义语言数据的Defaults。有关可重写可用属性的概述,请参阅Language.Defaults文档。

可编辑代码spaCy v3.7 · Python 3 · 通过 Binder

@spacy.registry.languages装饰器允许您注册自定义语言类并为其分配字符串名称。这意味着您可以调用spacy.blank并使用您的自定义语言名称,甚至可以用它训练管道并在训练配置中引用它。

注册自定义语言