【问题标题】:extract noun-chunk from single token从单个标记中提取名词块
【发布时间】:2020-12-12 12:06:40
【问题描述】:

考虑这个简单的例子

import spacy
nlp = spacy.load("en_core_web_lg")

  doc = nlp('the angry programmer wrote a nice stackover question to another angry programmer')

for chunk in doc.noun_chunks:
    print(chunk)
the angry programmer
a nice stackover question
another angry programmer

我有兴趣编写一个函数,该函数为每个标记返回(可能)包含该标记的名词块。

类似:

for tok in doc:
    print(tok, func_get_noun_chunk(tok))


the, the angry programmer
angry, the angry programmer
programmer, the angry programmer
wrote,
...

我如何在 Spacy 中做到这一点?当然,问题是可能有多个名词块包含相同的标记。我需要归还正确的!

谢谢!

【问题讨论】:

  • 这能回答你的问题吗:stackoverflow.com/questions/55307452/…
  • 不是真的,因为我需要包含标记的确切名词块。同一个句子中可能有多个名词块,每个块都有相似的词。
  • 问题已编辑以更清晰

标签: python nlp spacy


【解决方案1】:

解决此问题的一种方法是构建所有名词块的列表,然后遍历标记并建立标记和名词块对的列表:

noun_chunks = [nc for nc in doc.noun_chunks]
token_chunks = list()
for tok in doc:
    for nc in noun_chunks:
        if tok.i >= nc.start and tok.i < nc.end:
            token_chunks.append((tok, nc))
            break
print(token_chunks)

输出

[(the, the angry programmer),
 (angry, the angry programmer),
 (programmer, the angry programmer),
 (a, a nice stackover question),
 (nice, a nice stackover question),
 (stackover, a nice stackover question),
 (question, a nice stackover question),
 (another, another angry programmer),
 (angry, another angry programmer),
 (programmer, another angry programmer)]

【讨论】:

    【解决方案2】:

    这是一个会员测试。

    首先,我们的导入和doc 创建:

    import spacy
    from spacy.tokens.doc import Doc
    from spacy.tokens.span import Span
    from typing import Union, Tuple
    
    nlp = spacy.load("en_core_web_sm")
    doc = nlp('the angry programmer wrote a nice stackover question to another angry programmer')
    

    方法一:

    def approach_1(doclike: Union[Doc, Span]) -> None:
        for token in doclike:
            print(token, next((noun_chunk for noun_chunk in doclike.noun_chunks if token in noun_chunk), None))
    

    但是,我们可以看到这不是一个理想的方法,因为我们为每个 token 循环了 noun_chunks

    %%timeit
    approach_1(doc)
    
    112 µs ± 1.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    相反,正如 Matthew Cox 建议的那样,我们应该保存 noun_chunks

    def approach_2(doclike: Union[Doc, Span]) -> None:
        noun_chunks: Tuple = (*(stack_overflow_doc.noun_chunks),)
        for token in doclike:
            for noun_chunk in noun_chunks:
                if token in noun_chunk:
                    print(token, noun_chunk)
                else:
                    print(token)
    

    这在时间上给我们带来了明显更好的性能:

    %%timeit
    approach_2(doc)
    
    29.6 µs ± 1.13 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    但是,如果您打算在同一个 doc 上多次执行此操作,我不建议使用任何一种方法。相反,为令牌提供自定义 noun_chunk 属性。这样做会产生设置成本,但后续查询应该会更便宜。

    Token.set_extension('noun_chunk', default=None)
    
    %%timeit
    for noun_chunk in doc.noun_chunks:
        for token in noun_chunk:
            token._.noun_chunk = noun_chunk
    
    25.2 µs ± 736 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    
    def approach_3(doclike: Union[Doc, Span]) -> None:
        for token in doclike:
            print(token, token._.noun_chunk)
    
    %%timeit
    approach_3(doc)
    
    21 µs ± 262 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    

    然后考虑adding a custom pipeline component 在初始doc 创建期间添加noun_chunk 属性。


    编辑: 对于这个例子,Matthew 的方法似乎更快。他的方法与管道组件相结合,可能正是您想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-26
      • 2019-11-05
      相关资源
      最近更新 更多