我最近重构了 spaCy 管道中的一些组件,结果,管道的运行速度比重构之前慢了 7 倍。我不确定 spaCy 管道中的哪个组件是速度减慢的罪魁祸首,并且无法弄清楚如何获得有关文本流经每个组件所需时间的透明度。如果我知道哪个组件导致速度变慢,那么识别有问题的代码就会容易得多。
我考虑过向每个 Doc 对象添加自定义扩展,并让每个管道组件添加处理所需的时间,但这似乎可能存在问题。
是否有 spaCy 推荐的方法可以做到这一点,或者是否有其他人以巧妙的方式解决了这个问题?
最佳答案
这是一个好问题,扩展属性的想法实际上非常聪明!唯一的缺点是您必须向所有现有组件添加调试代码(如果它们是第三方代码,则效果也会较差)。但如果您知道有问题的代码是您的代码库的一部分,那么这应该不是问题。
另一种选择是将每个管道组件包装在一个函数中,该函数记录时间戳和您需要的任何其他内容并返回pipe(doc)
。然后,您可以使用这些包装的组件覆盖 nlp.pipeline
:
def wrap_pipe(name, pipe):
def wrapped(doc):
print(f"Started '{name}'", datetime.datetime.now())
return pipe(doc)
return wrapped
def debug_wrap_pipeline(nlp):
nlp.pipeline = [(name, wrap_pipe(name, pipe)) for name, pipe in nlp.pipeline]
return nlp
debug_nlp = debug_wrap_pipeline(nlp)
但是,这里的缺点是您还需要包装每个组件的 .pipe
方法(如果可用),以便您可以在下面运行和调试 nlp.pipe
相同的条件。如果您正在进行基准测试,您通常希望在更大的范围内执行此操作并使用 nlp.pipe
处理文本流。
为了避免这种情况,一个稍微详细的选项可能是在管道中每个现有组件之前添加一个“调试组件”。基本上是这样的:
def make_debug_component(name):
def debug_component(doc):
print(f"Before '{name}'", datetime.datetime.now())
return doc
return debug_component
def debug_wrap_pipeline(nlp):
pipeline = list(nlp.pipeline) # we don't want to modify this while we're looping over it
for name, pipe in pipeline:
debug_component = make_debug_component(name)
nlp.add_pipe(debug_component, before=name, name=f"debug_{name}")
return nlp
免责声明:我只是将这些想法拼凑在一起,还没有对它们进行广泛的测试。但它们似乎确实有效。如果你最终探索这个,我会很好奇什么最有效。这也可能是 spaCy 可以开箱即用的一个功能,并且它可以与提议的 static analysis for pipeline components 完美搭配。 .
另外,为了完整起见,补充一下:在调试这样的文本处理管道时,始终使用您处理一次的单个语料库进行更大规模的基准测试(而不是循环遍历单个示例 1000次或类似的东西)。缓存效应(spaCy 内以及 CPU 内)、内存分配差异等都会产生影响,并使小规模测试的可靠性降低。当然,在这种情况下,您会遇到巨大的差异,即使处理单个文本也可能为您提供足够的线索以及进一步调试代码所需的一切。
关于python - 在 spaCy 管道中对自定义组件进行基准测试的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57963657/