指南

基于规则匹配

查找短语和标记,并匹配实体

与在原始文本上使用正则表达式相比,spaCy基于规则的匹配引擎和组件不仅能找到您要搜索的单词和短语——还能让您访问文档中的词元及其关系。这意味着您可以轻松访问和分析周围的词元,将文本片段合并为单个词元,或向doc.ents中的命名实体添加条目。

对于复杂任务,通常最好训练一个统计实体识别模型。然而,统计模型需要训练数据,因此在许多情况下,基于规则的方法更为实用。这在项目初期尤其适用:您可以将基于规则的方法作为数据收集流程的一部分,帮助您"引导"统计模型的建立。

训练模型非常有用,当您有一些示例数据时,可以让系统基于这些示例进行泛化。这种方法在存在局部上下文线索时特别有效。例如,如果您想检测人名或公司名称,使用统计命名实体识别模型可能会为您的应用带来显著优势。

基于规则的系统是一个不错的选择,如果数据中你想查找的示例数量相对有限,或者存在非常清晰、结构化的模式,可以通过词符规则或正则表达式来表达。例如,国家名称、IP地址或URL这类内容,采用纯基于规则的方法可能就能很好地处理。

您还可以结合两种方法,通过规则改进统计模型,以处理非常特定的案例并提高准确性。详情请参阅基于规则的实体识别部分。

如果你已经有一个包含单或多标记短语的大型术语表或地名录,并希望在数据中精确查找这些短语的实例,PhraseMatcher会非常有用。从spaCy v2.1.0版本开始,你还可以基于LOWER属性进行匹配,实现快速且不区分大小写的匹配。

Matcher 不像 PhraseMatcher 那样极速,因为它需要比较各个token的属性。不过,它允许你使用词汇属性、模型预测的语言学特征、运算符、集合成员和丰富的比较方式,来编写非常抽象的token表示模式。例如,你可以查找一个名词,后跟词元为"love"或"like"的动词,再接一个可选的限定词和另一个长度至少10个字符的token。

基于Token的匹配

spaCy 配备了一个基于规则的匹配引擎 Matcher,它像正则表达式一样对词元进行操作。这些规则可以引用词元标注(例如词元的 texttag_,以及像 IS_PUNCT 这样的标志)。规则匹配器还允许您传入自定义回调函数来处理匹配项——例如合并实体并应用自定义标签。您还可以将模式与实体ID关联,以实现基本的实体链接或消歧。要匹配大型术语列表,您可以使用 PhraseMatcher,它接受 Doc 对象作为匹配模式。

添加模式

假设我们希望让spaCy能够识别三个标记的组合:

  1. 一个标记,其小写形式匹配“hello”,例如“Hello”或“HELLO”。
  2. 一个标记,其is_punct标志设置为True,即任何标点符号。
  3. 一个标记,其小写形式匹配“world”,例如“World”或“WORLD”。

首先,我们使用词汇表初始化Matcher。匹配器必须始终与其操作的文档共享相同的词汇表。现在我们可以调用matcher.add(),传入一个ID和模式列表。

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

匹配器返回一个(match_id, start, end)元组列表——在本例中为[('15578876784678163569', 0, 3)],对应原始文档中的doc[0:3]范围。match_id是字符串ID"HelloWorld"的哈希值。要获取字符串值,您可以在StringStore中查找该ID。

可选地,我们也可以选择添加多个模式,例如还可以匹配在"hello"和"world"之间没有标点符号的序列:

默认情况下,匹配器仅返回匹配项而不执行其他操作,例如合并实体或分配标签。这一切都取决于您,并且可以通过在add()方法中传入回调函数作为on_match参数,为每个模式单独定义。这非常有用,因为它允许您编写完全自定义且针对特定模式的逻辑。例如,您可能希望将某些模式合并为一个词符,同时为其他模式类型添加实体标签。您不必为每个处理流程创建不同的匹配器。

可用的词符属性

可用的词符模式键对应多个Token属性。基于规则匹配支持的属性包括:

属性描述
ORTHThe exact verbatim text of a token. str
TEXTThe exact verbatim text of a token. str
NORMThe normalized form of the token text. str
LOWERThe lowercase form of the token text. str
LENGTHThe length of the token text. int
IS_ALPHA, IS_ASCII, IS_DIGITToken text consists of alphabetic characters, ASCII characters, digits. bool
IS_LOWER, IS_UPPER, IS_TITLEToken text is in lowercase, uppercase, titlecase. bool
IS_PUNCT, IS_SPACE, IS_STOPToken is punctuation, whitespace, stop word. bool
IS_SENT_STARTToken is start of sentence. bool
LIKE_NUM, LIKE_URL, LIKE_EMAILToken text resembles a number, URL, email. bool
SPACYToken has a trailing space. bool
POS, TAG, MORPH, DEP, LEMMA, SHAPEThe token’s simple and extended part-of-speech tag, morphological analysis, dependency label, lemma, shape. Note that the values of these attributes are case-sensitive. For a list of available part-of-speech tags and dependency labels, see the Annotation Specifications. str
ENT_TYPEThe token’s entity label. str
_Properties in custom extension attributes. Dict[str, Any]
OPOperator or quantifier to determine how often to match a token pattern. str

不,不应该这样。spaCy会在内部标准化名称, {"LOWER": "text"}{"lower": "text"} 都会产生相同的结果。 使用大写版本主要是一种约定,目的是明确这些属性是"特殊的", 并不完全映射到像 Token.lowerToken.lower_ 这样的标记属性。

spaCy无法提供对所有属性的访问,因为Matcher循环处理的是Cython数据而非Python对象。在匹配器内部,我们处理的是TokenC结构体——而非Token的实例。这意味着所有涉及计算属性的特性都无法被访问。

大写的属性名称如LOWERIS_PUNCT引用自spacy.attrs枚举表。它们被传入一个本质上是大规模case/switch语句的函数,以确定返回哪个结构体字段。相同的属性标识符也用于Doc.to_array,以及代码中其他几个需要描述此类字段的地方。


扩展模式语法与属性

除了映射到单一值之外,词符模式还可以映射到属性字典。例如,可以指定词元值应属于某个值列表,或设置最小字符长度。以下是可用的丰富比较属性:

属性描述
INAttribute value is member of a list. Any
NOT_INAttribute value is not member of a list. Any
IS_SUBSETAttribute value (for MORPH or custom list attributes) is a subset of a list. Any
IS_SUPERSETAttribute value (for MORPH or custom list attributes) is a superset of a list. Any
INTERSECTSAttribute value (for MORPH or custom list attributes) has a non-empty intersection with a list. Any
==, >=, <=, >, <Attribute value is equal, greater or equal, smaller or equal, greater or smaller. Union[int, float]

正则表达式

在某些情况下,仅匹配词符和词符属性是不够的——例如,您可能希望匹配单词的不同拼写形式,而不必为每种拼写都添加新规则。

REGEX运算符允许为任何属性字符串值定义规则,包括自定义属性。它始终需要应用于像TEXTLOWERTAG这样的属性:

在全文本上匹配正则表达式

如果你的表达式适用于多个词符(token),一个简单的解决方案是使用re.finditer匹配doc.text,并通过Doc.char_span方法根据匹配的字符索引创建Span。如果匹配的字符没有映射到一个或多个有效词符,Doc.char_span会返回None

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

在某些情况下,您可能希望将匹配范围扩展到最近的词符边界,这样即使只有子字符串"US"被匹配,您也可以为"USA"创建一个Span。您可以通过计算文档中词符的字符偏移量(可通过Token.idx获取)来实现这一点。这将生成一个有效的词符起始和结束边界列表,从而转化为一个相当基础的算法问题:给定一个数字,在指定的数字列表中找到下一个较低值(起始词符)或较高值(结束词符),这将成为最接近的有效词符边界。

实现这一目标有多种方法,最直接的方式是创建一个以Doc中字符为键的字典,将其映射到所属的词元。这种方法编写简单且不易出错,并能提供恒定的查询时间:每个Doc只需创建一次字典。

然后你可以查找给定位置的字符,并获取该字符所属对应token的索引。你的span范围将是doc[token_start:token_end]。如果某个字符不在字典中,意味着它是分词时被忽略的(空白)字符。不过这种情况应该不会发生,因为那表示你的正则表达式产生了带有首尾空格匹配结果。

模糊匹配 v3.5

模糊匹配允许您匹配具有不同拼写、拼写错误等的标记,而无需指定所有可能的变体。

FUZZY属性允许对任何属性字符串值进行模糊匹配,包括自定义属性。与REGEX类似,它始终需要应用于像TEXTLOWER这样的属性。默认情况下,FUZZY允许至少2个Levenshtein编辑距离,最多可达模式字符串长度的30%。使用更具体的属性FUZZY1..FUZZY9,您可以直接指定允许的最大编辑距离。

使用正则表达式和模糊匹配处理列表 v3.5

从spaCy v3.5开始,REGEXFUZZY都可以与属性INNOT_IN结合使用:


运算符与量词

匹配器还允许您使用量词,通过'OP'键指定。 量词让您可以定义要匹配的标记序列,例如一个或多个 标点符号,或指定可选标记。请注意,没有嵌套或 作用域量词——相反,您可以通过on_match 回调来构建这些行为。

OP描述
!Negate the pattern, by requiring it to match exactly 0 times.
?Make the pattern optional, by allowing it to match 0 or 1 times.
+Require the pattern to match 1 or more times.
*Allow the pattern to match zero or more times.
{n}Require the pattern to match exactly n times.
{n,m}Require the pattern to match at least n but not more than m times.
{n,}Require the pattern to match at least n times.
{,m}Require the pattern to match at most m times.

使用通配符标记模式

虽然词符属性提供了许多选项来编写高度特定的模式, 你也可以使用空字典 {} 作为代表任意词符的通配符。如果你知道要匹配内容的上下文, 但对具体词符及其字符知之甚少时,这非常有用。例如, 假设你正尝试从数据中提取人们的用户名。你只知道 它们被列为"用户名: {username}"。名称本身可能包含 任意字符,但不能有空格——因此你知道它将被视为一个 词符。

验证和调试模式

Matcher可以通过选项validate=True根据JSON模式验证模式。这在开发过程中调试模式非常有用,特别是用于捕获不受支持的属性。

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

添加on_match规则

为了举一个更实际的例子,假设你正在处理一个大型博客文章语料库,想要匹配所有提及"Google I/O"的内容(spaCy会将其分词为['Google', 'I', '/', 'O'])。为了确保准确性,你只匹配大写版本,避免匹配到像"Google i/o"这样的短语。

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

顺便说一下,内置的EntityRuler也实现了非常相似的逻辑。它还负责处理重叠匹配的问题,否则您需要自行处理这种情况。

我们现在可以在文档上调用匹配器。这些模式将按照它们在文本中出现的顺序进行匹配。然后,匹配器会遍历所有匹配项,查找与匹配ID对应的回调函数并执行它。

当回调函数被调用时,它会接收四个参数:匹配器本身、文档、当前匹配的位置以及所有匹配项的完整列表。这允许您编写能够考虑整个匹配短语集的回调函数,从而以您偏好的任何方式解决重叠和其他冲突问题。

参数描述
matcherThe matcher instance. Matcher
docThe document the matcher was used on. Doc
iIndex of the current match (matches[i]). int
matchesA list of (match_id, start, end) tuples, describing the matches. A match tuple describes a span doc[start:end]. List[Tuple[int, int int]]

从匹配项创建span

从返回的匹配结果创建Span对象是一个非常常见的用例。spaCy通过提供对每个匹配项的startend标记的访问使这一过程变得简单,您可以使用这些标记来构建带有可选标签的新span。从spaCy v3.0开始,您还可以在Doc上调用匹配器时设置as_spans=True,这将返回一个使用match_id作为span标签的Span对象列表。

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

使用自定义管道组件

Let’s say your data also contains some annoying pre-processing artifacts, like leftover HTML line breaks (e.g.
or
). To make your text easier to analyze, you want to merge those into one token and flag them, to make sure you can ignore them later. Ideally, this should all be done automatically as you process the text. You can achieve this by adding a custom pipeline component that’s called on each Doc object, merges the leftover HTML spans and sets an attribute bad_html on the token.

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

除了将模式硬编码到组件中,您还可以让它接收一个包含模式的JSON文件路径。这样您就可以根据应用程序的不同,复用该组件并应用不同的模式。当使用nlp.add_pipe将组件添加到管道时,您可以通过config参数传入配置:

示例:使用语言注释

假设你正在分析用户评论,想了解人们对Facebook的评价。你可以先找出跟在"Facebook is"或"Facebook was"后面的形容词。这显然是一个非常基础的解决方案,但速度很快,是了解数据内容的好方法。你的模式可能如下所示:

这意味着匹配一个小写形式为"facebook"的词符(如Facebook、facebook或FACEBOOK),后跟一个词元为"be"的词符(例如is、was或's),再后跟一个可选副词,最后跟一个形容词。在这里使用语言学标注特别有用,因为你可以让spaCy匹配"Facebook's annoying",但匹配"Facebook's annoying ads"。可选副词确保你不会漏掉带有强调词的形容词,比如"pretty awful"或"very nice"。

要快速查看结果概览,您可以收集所有包含匹配项的句子,并使用displaCy可视化工具进行渲染。在回调函数中,您可以访问每个匹配项的startend,以及父级Doc对象。这使您能够确定包含匹配项的句子doc[start:end].sent,并计算该匹配项在句子中的起始和结束位置。通过在"手动"模式下使用displaCy,您可以传入包含待渲染文本和实体的字典列表。

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

示例:电话号码

电话号码可能有多种不同的格式,匹配它们通常很棘手。在分词过程中,spaCy会保持数字序列的完整性,仅根据空格和标点符号进行分割。这意味着您的匹配模式必须注意特定长度的数字序列,这些序列被特定的标点符号包围——具体取决于国家惯例

这里的IS_DIGIT标志作用不大,因为它无法告诉我们长度信息。不过您可以使用SHAPE标志,其中每个d代表一个数字(最多4位数字/字符):

这将匹配格式为(123) 4567 8901(123) 4567-8901的电话号码。若要同时匹配(123) 456 789这类格式,可以添加第二个模式,用'ddd'替代'dddd'。通过硬编码某些值,您可以仅匹配特定国家/地区的号码。例如,以下是一个匹配最常见德国国际号码格式的模式:

根据您的应用程序需要匹配的格式,创建这样一套全面的规则通常比训练模型更好。它能产生更可预测的结果,更容易修改和扩展,并且不需要任何训练数据——只需要一组测试用例。

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

示例:社交媒体上的话题标签和表情符号

社交媒体帖子,尤其是推文,处理起来可能很困难。它们非常简短,通常包含各种表情符号和话题标签。如果只看纯文本,你会丢失大量有价值的语义信息。

假设你提取了大量关于某个特定主题的社交媒体帖子样本,例如提及某个品牌或产品的帖子。作为数据探索的第一步,你需要筛选出包含特定表情符号的帖子,并根据表达的情绪是正面还是负面(例如😀或😞)来分配一个总体情感评分。你还希望查找、合并并标注像#MondayMotivation这样的标签,以便后续可以忽略或分析它们。

默认情况下,spaCy的分词器会将表情符号拆分为单独的标记。这意味着您可以为一个或多个表情符号标记创建匹配模式。有效的标签通常由#加上一串不含空格的ASCII字符组成,这使得它们也很容易匹配。

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

由于on_match回调函数会接收每个匹配项的ID,您可以使用相同的函数来处理正向和负向模式的情感赋值。为简化操作,我们将加减0.1分——这样,分数也能反映表情符号的组合情况,甚至是同时包含正向负向表情的情况。

借助像emoji这样的库,我们还可以获取每个表情符号的简短描述——例如,😍的官方名称是"笑脸带爱心眼"。将其分配给表情符号span上的自定义属性后,就可以通过span._.emoji_desc访问它。

要标注这些标签,我们可以使用在相应词符上设置的自定义属性

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

高效短语匹配

如果需要匹配大量术语列表,您也可以使用 PhraseMatcher并创建Doc对象 来代替词符模式,这样整体效率会更高。Doc 模式可以包含单个或多个词符。

添加短语模式

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

由于spaCy用于处理模式和待匹配的文本,您无需担心具体的分词问题——例如,您只需传入nlp("Washington, D.C."),而无需编写复杂的词符模式来覆盖该术语的精确分词方式。

匹配其他词符属性

默认情况下,PhraseMatcher会匹配字面上的词符文本,例如Token.text。通过在初始化时设置attr参数,您可以更改匹配器在比较短语模式与匹配的Doc时应使用哪个词符属性。例如,使用LOWER属性可以让您基于Token.lower进行匹配,并创建不区分大小写的匹配模式:

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

另一个可能的用例是根据数字标记的形状来匹配类似IP地址的内容。这意味着您无需担心这些字符串将如何被分词,只需基于少量示例就能找到标记及其组合。这里,我们匹配的形状是ddd.d.d.dddd.ddd.d.d

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

理论上,同样的方法也适用于像POS这样的属性。例如,一个基于词性标签匹配的模式nlp("I like cats")会返回与"I love dogs"匹配的结果。你也可以通过布尔标志如IS_PUNCT来匹配具有相同标点和非标点标记序列的短语。但这很容易让人困惑,而且相比编写一两个标记模式并没有太大优势。

Dependency Matcher v3.0需要模型

DependencyMatcher 允许您使用Semgrex操作符在依存句法分析中匹配模式。它需要一个包含解析器(如DependencyParser)的模型。与Matcher模式中定义相邻标记列表不同,DependencyMatcher模式匹配依存句法分析中的标记并指定它们之间的关系。

添加到依存匹配器的模式由一个字典列表组成,每个字典描述一个要匹配的词符及其与模式中现有词符的关系。除了第一个仅使用RIGHT_IDRIGHT_ATTRS定义锚定词符的字典外,每个模式应包含以下键:

名称描述
LEFT_IDThe name of the left-hand node in the relation, which has been defined in an earlier node. str
REL_OPAn operator that describes how the two nodes are related. str
RIGHT_IDA unique name for the right-hand node in the relation. str
RIGHT_ATTRSThe token attributes to match for the right-hand node in the same format as patterns provided to the regular token-based Matcher. Dict[str, Any]

添加到模式中的每个额外标记都通过关系LEFT_ID与现有标记REL_OP相关联。新标记被命名为RIGHT_ID,并由属性RIGHT_ATTRS描述。

Dependency matcher运算符

以下操作符由DependencyMatcher支持,其中大部分直接来自Semgrex

符号描述
A < BA is the immediate dependent of B.
A > BA is the immediate head of B.
A << BA is the dependent in a chain to B following dep → head paths.
A >> BA is the head in a chain to B following head → dep paths.
A . BA immediately precedes B, i.e. A.i == B.i - 1, and both are within the same dependency tree.
A .* BA precedes B, i.e. A.i < B.i, and both are within the same dependency tree (Semgrex counterpart: ..).
A ; BA immediately follows B, i.e. A.i == B.i + 1, and both are within the same dependency tree (Semgrex counterpart: -).
A ;* BA follows B, i.e. A.i > B.i, and both are within the same dependency tree (Semgrex counterpart: --).
A $+ BB is a right immediate sibling of A, i.e. A and B have the same parent and A.i == B.i - 1.
A $- BB is a left immediate sibling of A, i.e. A and B have the same parent and A.i == B.i + 1.
A $++ BB is a right sibling of A, i.e. A and B have the same parent and A.i < B.i.
A $-- BB is a left sibling of A, i.e. A and B have the same parent and A.i > B.i.
A >+ B v3.5.1B is a right immediate child of A, i.e. A is a parent of B and A.i == B.i - 1 (not in Semgrex).
A >- B v3.5.1B is a left immediate child of A, i.e. A is a parent of B and A.i == B.i + 1 (not in Semgrex).
A >++ BB is a right child of A, i.e. A is a parent of B and A.i < B.i.
A >-- BB is a left child of A, i.e. A is a parent of B and A.i > B.i.
A <+ B v3.5.1B is a right immediate parent of A, i.e. A is a child of B and A.i == B.i - 1 (not in Semgrex).
A <- B v3.5.1B is a left immediate parent of A, i.e. A is a child of B and A.i == B.i + 1 (not in Semgrex).
A <++ BB is a right parent of A, i.e. A is a child of B and A.i < B.i.
A <-- BB is a left parent of A, i.e. A is a child of B and A.i > B.i.

设计依赖匹配器模式

假设我们想找到描述谁创立了哪种公司的句子:

  • 史密斯于2005年创立了一家医疗保健公司。
  • 威廉姆斯最初于1987年创立了一家保险公司。
  • 李是一位经验丰富的CEO,已经创立了两家AI初创公司。

“Smith founded a healthcare company”的依存关系解析展示了我们想要匹配的关系类型和标记:

我们感兴趣的关系是:

  • 创始人(founder)是文本为founded的token的主语(nsubj)
  • 该公司是宾语 (dobj) 属于 founded
  • 公司类型可能是一个形容词(amod, 上例未显示)或一个复合词(compound)

第一步是为模式选择一个锚点标记。由于它是依存解析的根节点,founded在这里是个不错的选择。当所有依存关系操作符都从头部指向子节点时,构建模式通常会更容易。在这个例子中,我们只使用>,它以head > child的形式将头部连接到直接依赖项。

最简单的依赖匹配器模式将识别并命名树中的单个标记:

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

现在我们有了一个命名锚点标记(anchor_founded),我们可以将创始人添加为founded的直接依存项(>),并使用依存标签nsubj

步骤 1

直接宾语(dobj)以相同方式添加:

步骤 2

当添加主语和宾语标记时,它们需要在RIGHT_ID键下具有名称,这些名称可以是任何唯一的字符串,例如founded_subject。然后这些名称可以用作LEFT_ID将新标记链接到模式中。对于模式的最后部分,我们将指定标记founded_object应该有一个依赖关系为amodcompound的修饰符:

步骤 3

你可以将创建依赖匹配器模式的过程想象为:在左侧定义一个锚点标记,然后通过使用关系运算符在右侧逐个链接标记来构建模式。要创建一个有效的模式,每个新标记都需要链接到其左侧的现有标记。就像本例中的founded,一个标记可能会链接到其右侧的多个标记:

Dependency matcher pattern

完整的模式如下例所示:

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

基于规则的实体识别

EntityRuler 是一个组件,允许您基于模式字典添加命名实体,这使得将基于规则和统计的命名实体识别相结合变得更加容易,从而构建更强大的处理流程。

实体模式

实体模式是包含两个键的字典:"label",指定如果模式匹配时要分配给实体的标签,以及"pattern",即匹配模式。实体规则器接受两种类型的模式:

  1. 用于精确字符串匹配的短语模式(字符串)。

  2. 词符模式 用一个字典描述一个词符(列表)。

使用实体标尺

EntityRuler 是一个通常通过 nlp.add_pipe 添加的管道组件。当 nlp 对象处理文本时,它会在 doc 中查找匹配项,并将它们作为实体添加到 doc.ents 中,使用指定的模式标签作为实体标签。如果有任何匹配项重叠,则匹配最多标记的模式优先。如果它们长度也相同,则选择在 Doc 中先出现的匹配项。

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

实体规则器设计用于与spaCy现有管道组件集成,并增强命名实体识别器。如果将其添加"ner"组件之前,实体识别器将尊重现有实体范围并围绕其调整预测。这在某些情况下可以显著提高准确性。如果将其添加"ner"组件之后,实体规则器仅会在不与模型预测的现有实体重叠时向doc.ents添加范围。要覆盖重叠实体,您可以在初始化时设置overwrite_ents=True

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

验证和调试EntityRuler模式

实体规则器可以通过配置设置"validate"来根据JSON模式验证模式。详情请参阅验证和调试模式

为模式添加ID

EntityRuler 还可以为每个模式接受一个 id 属性。使用 id 属性可以让多个模式关联到同一个实体。

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

如果在EntityRuler的模式中包含id属性,则匹配实体的ent_id_属性将被设置为模式中给定的id。因此在上面的示例中,很容易识别出"San Francisco"和"San Fran"是同一个实体。

使用模式文件

to_diskfrom_disk 允许您将模式保存到JSONL(换行分隔的JSON)文件以及从中加载模式,每行包含一个模式对象。

patterns.jsonl

当你保存一个添加了EntityRuler到其管道的nlp对象时,其模式会自动导出到管道目录:

保存的管道现在在其config.cfg中包含"entity_ruler",并且管道目录中包含带有模式的patterns.jsonl文件。当您重新加载管道时,所有管道组件将被恢复和反序列化——包括实体标尺。这使您可以打包包含二进制权重规则的强大管道包!

使用大量短语模式

当使用大量短语模式(约超过10000个)时,了解实体规则器的add_patterns函数工作原理很有帮助。对于每个短语模式,EntityRuler会调用nlp对象来构建一个doc对象。这种情况发生在您尝试将EntityRuler添加到现有管道末尾时(例如包含POS标注器的管道),并希望基于模式的POS特征提取匹配项。在这种情况下,您需要为实体规则器传递配置值"phrase_matcher_attr": "POS"

在大规模短语模式列表中对每个模式运行完整的语言处理流程,其耗时呈线性增长,因此在处理大量短语模式时可能需要很长时间。从spaCy v2.2.4版本开始,add_patterns函数已重构为对所有短语模式使用nlp.pipe方法,这使得处理5,000至100,000个短语模式时的速度提升了约10-20倍。尽管有这种加速(但特别是如果您使用的是旧版本),add_patterns函数仍可能耗时较长。一个简单的优化方法是:在添加短语模式时暂时禁用其他语言处理组件。

基于规则的跨度匹配 v3.3.1

SpanRuler是实体规则器的通用版本,它允许您基于模式字典将跨度添加到doc.spansdoc.ents中,这使得结合基于规则和统计的管道组件变得容易。

Span模式

pattern格式与实体规则器相同:

  1. 用于精确字符串匹配的短语模式(字符串)。

  2. 词符模式 用一个字典描述一个词符(列表)。

使用span ruler

SpanRuler是一个通常通过nlp.add_pipe添加的管道组件。当nlp对象处理文本时,它会在doc中查找匹配项,并将它们作为span添加到doc.spans["ruler"]中,使用指定的模式标签作为实体标签。与doc.ents不同,doc.spans允许重叠匹配,因此不需要过滤,但在保存之前可以对span应用可选的过滤和排序。

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

span ruler 旨在与 spaCy 现有的流水线组件集成,并增强 SpanCategorizerEntityRecognizeroverwrite 设置决定了是否保留 doc.spansdoc.ents 中的现有标注。由于 doc.ents 不允许重叠实体,默认情况下会使用 util.filter_spans 进行过滤。有关如何自定义匹配跨度的排序和过滤的更多信息,请参阅 SpanRuler API 文档

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

使用模式文件

您可以将模式保存为JSONL文件(换行符分隔的JSON),以便通过 SpanRuler.initializeSpanRuler.add_patterns加载。

patterns.jsonl

结合模型与规则

您可以通过多种方式结合统计和基于规则的组件。基于规则的组件可用于提高统计模型的准确性,通过为特定标记预设标签、实体或句子边界。统计模型通常会遵循这些预设的标注,这有时会提高其他决策的准确性。您还可以在统计模型之后使用基于规则的组件来纠正常见错误。最后,基于规则的组件可以引用统计模型设置的属性,以实现更抽象的逻辑。

示例:扩展命名实体

在使用训练好的命名实体识别模型从文本中提取信息时,您可能会发现预测的范围仅包含您要查找实体的部分内容。有时,这种情况发生在统计模型错误预测实体时。其他情况下,当原始训练语料库中定义的实体类型与您的应用需求不匹配时,也会出现这种情况。

例如,spaCy的英文管道训练所用的语料库将PERSON实体定义为仅包含人名,而不包含"Mr."或"Dr."等头衔。这种定义是合理的,因为它能更轻松地将实体类型关联回知识库。但如果您的应用需要包含头衔的完整姓名该怎么办?

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

虽然你可以尝试通过更新更多包含头衔的文本片段示例来教导模型新的PERSON实体定义,但这可能不是最高效的方法。现有模型是在超过200万单词上训练的,因此要完全改变一个实体类型的定义,你可能需要大量训练样本。不过,如果你已经预测出PERSON实体,可以采用基于规则的方法来检查它们是否带有头衔——如果有的话,就将实体范围扩展一个词元。毕竟,本例中所有头衔的共同点是:如果出现头衔,它们必定位于人物实体前一个词元的位置。

上述函数接收一个Doc对象,修改其doc.ents属性后返回该对象。 通过使用@Language.component装饰器,我们可以 将其注册为管道组件,使其能在处理文本时 自动运行。我们可以使用 nlp.add_pipe将其添加到当前管道中。

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

另一种方法是使用 扩展属性._.person_title并将其添加到Span对象(包括doc.ents中的实体跨度)。这里的优势在于实体文本保持完整,仍可用于在知识库中查找名称。以下函数接收一个Span对象,检查前一个标记是否为PERSON实体,如果找到标题则返回。通过Span.doc属性我们可以轻松访问该跨度的父文档。

我们现在可以使用Span.set_extension方法来添加自定义扩展属性"person_title",并将get_person_title作为getter函数。

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

示例:使用实体、词性标注和依存句法分析

假设您想解析专业传记并提取人名和公司名称,以及这是他们当前工作的公司还是之前CURRENT_ORG和PREVIOUS_ORG——但这种区别非常微妙,实体识别器可能难以学习。"Acme Corp Inc."本身并没有"当前"或"之前"的属性。

然而,句子语法中包含一些非常重要的线索:我们可以检查诸如"work"这样的触发词,判断它们是过去时态还是现在时态,是否关联了公司名称,以及该人是否是主语。所有这些信息都可以通过词性标注和依存句法分析获得。

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

Visualization of dependency parse

在这个例子中,"worked"是句子的核心词,并且是一个过去式动词。 它的主语是"Alex Smith",即工作的人。"at Acme Corp Inc."是一个 附加在动词"worked"上的介词短语。要提取这种 关系,我们可以从识别预测的PERSON实体开始,找到 它们的头部词并检查是否连接到像"work"这样的触发词。 接着,我们可以检查附加在头部词上的介词短语以及 它们是否包含ORG实体。最后,要确定公司 关联是否是当前的,我们可以检查头部词的词性标注。

要在处理文本时自动应用此逻辑,我们可以将其作为自定义管道组件添加到nlp对象中。上述逻辑还要求将实体合并为单个词符。spaCy自带一个方便的merge_entities内置组件来处理这个问题。除了直接打印结果外,您还可以将其写入实体Span自定义属性中,例如._.orgs._.prev_orgs._.current_orgs

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

如果你改变上面的句子结构,例如改成“was working”,你会注意到我们当前的逻辑失效了,无法正确检测该公司作为过去的一个组织。这是因为根节点是分词形式,而时态信息附着在辅助动词“was”上:

Visualization of dependency parse

为了解决这个问题,我们可以调整规则,使其也能检查上述结构:

在你的最终基于规则的系统中,你可能需要多个不同的代码路径来处理数据中出现的各种结构类型。