
本文深入探讨了spacy `matcher`在处理重叠或包含关系模式时可能遇到的优先级问题。当存在多个匹配规则,其中一个规则是另一个更长规则的子集时,`matcher`的默认行为可能导致较短模式被优先匹配,从而遗漏更完整的信息。通过引入`matcher.add()`方法的`greedy="longest"`参数,我们可以强制匹配器优先识别最长的匹配项,从而确保提取数据的准确性和完整性。
SpaCy的Matcher是一个强大的工具,用于在文本中查找符合特定词法、句法或形态特征序列的模式。它通过定义一系列的Token属性(如POS、DEP、TEXT等)来构建模式规则。当在文档上运行Matcher时,它会识别所有符合已添加规则的文本片段。
然而,当一个文本片段可以被多个已定义的模式匹配时,或者当一个模式是另一个更长模式的子集时,Matcher的默认行为可能会导致非预期的结果。例如,如果模式A是[NOUN, ADP, NOUN],模式B是[NOUN, ADP, NOUN, ADJ],并且文本中存在NOUN ADP NOUN ADJ这样的序列,Matcher可能会优先匹配模式A,因为它可能先被发现或其匹配逻辑优先触发。
考虑以下场景,我们希望从文本中提取复合名词短语作为“COMPONENTE”:
import spacy
from spacy.matcher import Matcher
from spacy.tokens import Span
txt = "Os edifícios multifamiliares devem ser providos de proteção contra descargas atmosféricas, atendendo ao estabelecido na ABNT NBR 5419 e demais Normas Brasileiras aplicáveis, nos casos previstos na legislação vigente."
nlp = spacy.load("pt_core_news_md")
doc = nlp(txt)
patterns_config = [
{"label": "COMPONENTE", "pattern": [
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"},{"POS": "ADJ"}], # 模式1:名词+介词+名词+形容词
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "ADJ"}],
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"}], # 模式2:名词+介词+名词
[{"POS": "NOUN", "DEP":"nsubj"},{"POS": "ADJ"},{"POS": "ADJ"}],
[{"POS": "NOUN", "DEP":"nsubj"}],
[{"POS": "NOUN"},{"POS": "ADJ"}]
]}
]
def buscar_padroes_sequencialmente(doc, patterns_config):
resultados = []
tokens_processados = set()
for pat_dict in patterns_config:
label = pat_dict["label"]
matcher = Matcher(doc.vocab) # 每个label创建一个Matcher实例
# 将所有属于当前label的模式添加到matcher中
# 注意:这里是问题的关键点,默认行为可能导致短模式优先匹配
for i, padrao_atual in enumerate(pat_dict["pattern"]):
matcher.add(f"{label}_{i}", [padrao_atual]) # 为每个子模式赋予唯一ID
for padrao_id, inicio, fim in matcher(doc):
rótulo = matcher.vocab.strings[padrao_id].split('_')[0] # 提取原始label
# 检查是否有任何token已被处理
if any(token.i in tokens_processados for token in doc[inicio:fim]):
continue
# 添加当前匹配的token到已处理集合
tokens_processados.update(token.i for token in doc[inicio:fim])
# 将匹配到的token转换为Span对象
span = Span(doc, inicio, fim, label=rótulo)
resultados.append((rótulo, span))
return resultados
resultados = buscar_padroes_sequencialmente(doc, patterns_config)
print("Frase:", txt)
for i, (rotulo, span) in enumerate(resultados, start=1):
pos_tokens = [token.pos_ for token in span]
print(f"OSemantic {i}:", span.text, f'({rotulo})')
print("POStoken:", pos_tokens)
print()运行上述代码,我们期望从“proteção contra descargas atmosféricas”中提取出NOUN ADP NOUN ADJ(即“proteção contra descargas atmosféricas”)。然而,实际输出可能显示为:
OSemantic 4: proteção contra descargas (COMPONENTE) POStoken: ['NOUN', 'ADP', 'NOUN']
这表明 Matcher 优先匹配了较短的模式 [{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"}],而忽略了更长的、包含形容词的模式 [{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"},{"POS": "ADJ"}]。即使调整模式的顺序,问题也可能依然存在,除非完全移除较短的模式。
SpaCy Matcher的add()方法提供了一个greedy参数,用于控制当多个模式可以匹配同一段文本时,应该如何选择匹配项。greedy参数可以接受两个值:"FIRST"或"LONGEST"。
通过将greedy参数设置为"LONGEST",我们可以确保即使存在较短的子模式,Matcher也会尝试找到并返回最长的有效匹配。
为了解决短模式抢占长模式的问题,我们只需在Matcher.add()方法中添加greedy="LONGEST"参数。
import spacy
from spacy.matcher import Matcher
from spacy.tokens import Span
txt = "Os edifícios multifamiliares devem ser providos de proteção contra descargas atmosféricas, atendendo ao estabelecido na ABNT NBR 5419 e demais Normas Brasileiras aplicáveis, nos casos previstos na legislação vigente."
nlp = spacy.load("pt_core_news_md")
doc = nlp(txt)
patterns_config = [
{"label": "COMPONENTE", "pattern": [
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"},{"POS": "ADJ"}], # 模式1:名词+介词+名词+形容词
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "ADJ"}],
[{"POS": "NOUN"},{"POS": "ADP"},{"POS": "NOUN"}], # 模式2:名词+介词+名词
[{"POS": "NOUN", "DEP":"nsubj"},{"POS": "ADJ"},{"POS": "ADJ"}],
[{"POS": "NOUN", "DEP":"nsubj"}],
[{"POS": "NOUN"},{"POS": "ADJ"}]
]}
]
def buscar_padroes_sequencialmente_corrigido(doc, patterns_config):
resultados = []
tokens_processados = set()
for pat_dict in patterns_config:
label = pat_dict["label"]
matcher = Matcher(doc.vocab)
# 关键修正:在添加模式时指定 greedy="LONGEST"
# 为每个子模式赋予唯一ID,以便在匹配后识别其原始label
for i, padrao_atual in enumerate(pat_dict["pattern"]):
matcher.add(f"{label}_{i}", [padrao_atual], greedy="LONGEST") # <--- 修正点
for padrao_id, inicio, fim in matcher(doc):
rótulo = matcher.vocab.strings[padrao_id].split('_')[0]
# 检查是否有任何token已被处理
if any(token.i in tokens_processados for token in doc[inicio:fim]):
continue
# 添加当前匹配的token到已处理集合
tokens_processados.update(token.i for token in doc[inicio:fim])
# 将匹配到的token转换为Span对象
span = Span(doc, inicio, fim, label=rótulo)
resultados.append((rótulo, span))
return resultados
resultados_corrigidos = buscar_padroes_sequencialmente_corrigido(doc, patterns_config)
print("Frase:", txt)
for i, (rotulo, span) in enumerate(resultados_corrigidos, start=1):
pos_tokens = [token.pos_ for token in span]
print(f"OSemantic {i}:", span.text, f'({rotulo})')
print("POStoken:", pos_tokens)
print()应用此修正后,输出将变为:
OSemantic 4: proteção contra descargas atmosféricas (COMPONENTE) POStoken: ['NOUN', 'ADP', 'NOUN', 'ADJ']
这正是我们期望的结果,Matcher成功地识别并提取了最长的匹配项。
SpaCy Matcher的greedy参数是实现精确模式匹配的关键工具,尤其是在处理具有重叠或包含关系的复杂模式集合时。通过合理设置greedy="LONGEST",开发者可以确保Matcher优先识别并提取最长的、最完整的文本片段,从而提高信息抽取的准确性和鲁棒性。理解并灵活运用这一参数,对于构建高效、准确的自然语言处理系统至关重要。
以上就是SpaCy Matcher的greedy参数:优化复杂模式匹配顺序与准确性的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号