如何基于spaCy等NLP工具提取句子完整linguistic phrases
完整语言短语提取实现方案
你要提取的这类「作为统一句法成分、不可拆分移位」的短语,本质是句法成分块,和关键短语抽取的目标完全不同,基于spaCy的依存解析能力完全可以实现,不需要额外引入其他工具。
核心原理
你给出的示例切分逻辑,本质是沿句子依存树的最高层结构边界切分:
- 先定位句子的根节点,通常是句子的核心谓语动词
- 根节点的每一类直接从属成分,连带它自身的所有子孙修饰节点,就构成一个完整的、不可拆分的短语块
拿你提供的例句做拆解:
测试句:The excited students thanked the visiting teacher from an overseas university after the inspiring lecture
句子依存树根节点是谓语thanked,它的直接从属成分有三个:
- 名词主语
students:连带它的前置修饰词,构成短语 The excited students- 直接宾语
teacher:连带它的前置定语、后置介词修饰结构,和根节点谓语合并后,构成短语 thanked the visiting teacher from an overseas university(即你给出的预期输出里的谓语短语块)- 介词状语引导词
after:连带它的宾语和修饰成分,构成短语 after the inspiring lecture
如果要得到你提到的替代粒度输出,只需要把宾语内部嵌套的介词修饰from an overseas university从宾语块里拆出来单独成块即可。
基于spaCy的落地实现
实现步骤
- 加载带依存解析能力的spaCy模型,对输入句子完成句法解析
- 定位句子的根节点(核心谓语),遍历它的直接从属子节点,过滤标点等无实际语义的节点
- 对每个有效子节点,递归收集它覆盖的所有token,按原文顺序拼接得到基础短语块
- 根据需要的短语粒度调整合并规则:要粗粒度就把嵌套修饰全部并入父节点块,要细粒度就把介词短语、从句类嵌套修饰单独拆分
最小可运行代码
import spacy # 加载英文依存解析模型,中文场景替换为zh_core_web系列模型即可 nlp = spacy.load("en_core_web_sm") def extract_syntactic_phrases(sent_text, coarse_grained=True): doc = nlp(sent_text) # 定位句子核心根节点(谓语) root = next(token for token in doc if token.dep_ == "ROOT") phrases = [] subject_chunk = None object_chunk = None adverbial_chunks = [] for child in root.children: # 收集主语块 if child.dep_ in ("nsubj", "nsubjpass", "csubj"): chunk_tokens = sorted(child.subtree, key=lambda x: x.i) subject_chunk = " ".join(t.text for t in chunk_tokens) # 收集宾语块 elif child.dep_ in ("dobj", "iobj", "pobj"): chunk_tokens = sorted(child.subtree, key=lambda x: x.i) object_chunk = " ".join(t.text for t in chunk_tokens) # 收集状语块(介词短语、副词状语、状语从句) elif child.dep_.startswith("prep") or child.dep_ in ("advmod", "advcl"): chunk_tokens = sorted(child.subtree, key=lambda x: x.i) adverbial_chunks.append(" ".join(t.text for t in chunk_tokens)) # 按粒度组装结果 if subject_chunk: phrases.append(subject_chunk) if coarse_grained: # 粗粒度:谓语和宾语块合并,嵌套修饰全部保留,匹配你的预期输出 if object_chunk: phrases.append(f"{root.text} {object_chunk}") else: # 细粒度:谓语单独成块,嵌套的介词修饰从宾语中拆分,匹配你的替代输出 phrases.append(root.text) if object_chunk: phrases.append(object_chunk) # 此处可补充逻辑:遍历宾语子树,将嵌套的prep类从属单独抽块加入结果 phrases.extend(adverbial_chunks) return phrases # 测试例句 test_sent = "The excited students thanked the visiting teacher from an overseas university after the inspiring lecture" print(extract_syntactic_phrases(test_sent, coarse_grained=True)) # 输出:['The excited students', 'thanked the visiting teacher from an overseas university', 'after the inspiring lecture']
调优说明
- 粒度调整完全可以通过修改纳入同一块的依存标签规则实现:比如要拆分更细,就不要把
prep类从属的子树并入父名词块,单独收集成块即可 - PyTextRank这类工具的设计目标是抽取有语义价值的名词性关键短语并做权重排序,本身不做完整句法成分的切分,自然无法满足这个场景的需求
- 处理复杂从句(比如定语从句、宾语从句)时,只需要在遍历子节点时把
relcl、ccomp类依存对应的子树按相同逻辑处理即可,核心规则不变
内容的提问来源于stack exchange,提问作者user2016538
相关产品推荐
相关产品推荐

