ryk*_*ryk 10 python spacy spacy-transformers spacy-3
我正在使用 spacy 版本 3 进行一些性能测试,以便在生产中调整实例的大小。我正在观察以下情况
观察:
| 型号名称 | 没有 NER 的时间 | 与 NER 共度时光 | 评论 |
|---|---|---|---|
| en_core_web_lg | 4.89秒 | 21.9秒 | NER在原来的基础上增加了350% |
| en_core_web_trf | 43.64秒 | 52.83秒 | NER 只比原来的时间增加了 20% |
为什么在使用 Transformer 模型的情况下,使用 NER和不使用 NER 的情况没有显着差异?在 en_core_web_trf 的情况下,NER 是否只是 POS 标记之后的增量任务?
测试环境: GPU实例
测试代码:
import spacy
assert(spacy.__version__ == '3.0.3')
spacy.require_gpu()
texts = load_sample_texts() # loads 10,000 texts from a file
assert(len(texts) == 10000)
def get_execution_time(nlp, texts, N):
return timeit.timeit(stmt="[nlp(text) for text in texts]",
globals={'nlp': nlp, 'texts': texts}, number=N) / N
# load models
nlp_lg_pos = spacy.load('en_core_web_lg', disable=['ner', 'parser'])
nlp_lg_all = spacy.load('en_core_web_lg')
nlp_trf_pos = spacy.load('en_core_web_trf', disable=['ner', 'parser'])
nlp_trf_all = spacy.load('en_core_web_trf')
# get execution time
print(f'nlp_lg_pos = {get_execution_time(nlp_lg_pos, texts, N=1)}')
print(f'nlp_lg_all = {get_execution_time(nlp_lg_all, texts, N=1)}')
print(f'nlp_trf_pos = {get_execution_time(nlp_trf_pos, texts, N=1)}')
print(f'nlp_trf_all = {get_execution_time(nlp_trf_all, texts, N=1)}')
Run Code Online (Sandbox Code Playgroud)
虽然不是专家,但我认为这可能是由于管道的设计造成的。
\n模型的文档sm/md/lg指出:
\n\n\n
该模型的文档trf指出:
\n\n在变压器 (
\ntrf) 模型中,tagger、parser和ner(如果存在)都侦听该transformer组件。
\n\n在组件之间重用 tok2vec 层可以使管道运行得更快,并产生更小的模型。但是,它可能会降低管道的模块化程度,并使交换组件或重新训练管道的某些部分变得更加困难。\n
\n
| 共享 | 独立的 |
|---|---|
| \xe2\x9c\x85更小:模型只需要包含嵌入的单个副本 | \xe2\x9d\x8c较大:模型需要包含每个组件的嵌入 |
| \xe2\x9c\x85更快:为整个管道嵌入文档一次 | \xe2\x9d\x8c较慢:重新运行每个组件的嵌入 |
| \xe2\x9d\x8c可组合性较差:所有组件都需要在管道中使用相同的嵌入组件 | \xe2\x9c\x85模块化:组件可以自由移动和交换 |
注 1:我正在使用20 个新闻组文本数据集,并且没有 GPU,因此时间可能会有所不同,但结果指向相同的总体方向。
\n注2:我正在使用:
\nspacy3.5.4en_core_web_lg3.5.0en_core_web_trf3.5.0scikit-learn1.3.0Python3.11.4."""Replicating code as much as possible."""\n\nimport timeit\n\nfrom sklearn.datasets import fetch_20newsgroups\nimport spacy # 3.5.4\n\n\n# spacy.require_gpu() # I don\'t have a GPU available\nbunch = fetch_20newsgroups(random_state=0)\ntexts = bunch.data\n\ndef get_execution_time(nlp, texts, N):\n return timeit.timeit(\n stmt="[nlp(text) for text in texts]",\n globals={\'nlp\': nlp, \'texts\': texts},\n number=N\n ) / N\n\n# load models\nnlp_lg_pos = spacy.load(\'en_core_web_lg\', disable=[\'ner\', \'parser\'])\nnlp_lg_all = spacy.load(\'en_core_web_lg\')\nnlp_trf_pos = spacy.load(\'en_core_web_trf\', disable=[\'ner\', \'parser\'])\nnlp_trf_all = spacy.load(\'en_core_web_trf\')\n\n# get execution time\nprint(f\'nlp_lg_pos = {get_execution_time(nlp_lg_pos, texts, N=1)}\')\nprint(f\'nlp_lg_all = {get_execution_time(nlp_lg_all, texts, N=1)}\')\nprint(f\'nlp_trf_pos = {get_execution_time(nlp_trf_pos, texts, N=1)}\')\nprint(f\'nlp_trf_all = {get_execution_time(nlp_trf_all, texts, N=1)}\')\nRun Code Online (Sandbox Code Playgroud)\n| 型号名称 | 没有 NER 或解析器的时间 | 使用 NER 和解析器的时间 | 评论 |
|---|---|---|---|
en_core_web_lg | 8.48秒 | 13.98秒 | NER 和 Parser 比原始时间增加 65% |
en_core_web_trf | 387.67秒 | 382.84秒 | NER 和 Parser 减少了 1% 的时间(可以忽略不计) |
修改现有组件并使其在不重新训练的情况下监听共享tok2vec或transformer层是很困难的。因此,我将用变压器层自己的副本替换 和 组件侦听器en_core_web_trf ner。parser如果文档正确,这应该会导致“Time with (Independent) NER and (Independent) Parser”结果比之前的结果慢得多en_core_web_trf。
nlp_trf_all_independent = spacy.load(\'en_core_web_trf\')\n\n# make `ner` and `parser` components independent\nnlp_trf_all_independent.replace_listeners("transformer", "ner", ["model.tok2vec"])\nnlp_trf_all_independent.replace_listeners("transformer", "parser", ["model.tok2vec"])\n\nprint(f\'nlp_trf_all_independent = {get_execution_time(nlp_trf_all_independent, texts, N=1)}\')\nRun Code Online (Sandbox Code Playgroud)\n| 型号名称 | 没有 NER 或解析器的时间 | 使用(独立)NER 和(独立)解析器的时间 | 评论 |
|---|---|---|---|
en_core_web_trf | 387.67秒 | 1125.31 秒 | (独立)NER和(独立)解析器在原始时间上加上190% |
正如您所看到的,使组件独立,即不共享/监听tok2vec/transformer层,会导致更慢(但更模块化)的管道。en_core_web_lg我相信这就是当您添加组件时模型明显变慢的原因,ner因为默认情况下它是独立的。