如何在 Spacy v3 中使用 BILUO 模式更新 NER 模型?

How to update NER model using BILUO schema in Spacy v3?

我想在 sm NER spacy 模型中添加一些 PERSON 词汇。为此,我使用 BILUO 模式标记了从 ProPublica API 中获取的国会官员的姓名。我看过很多关于使用带实体格式的字典的帖子。但是到目前为止,在 v3 中找不到任何使用 BILUO 模式的方法。我将数据作为元组列表。第一个值是字符串,第二个值是标记的实体。

data = [('Nanette Barragán', list(['B-PERSON', 'L-PERSON'])),
 ('Jack Bergman', list(['B-PERSON', 'L-PERSON'])),
 ('Andy Biggs', list(['B-PERSON', 'L-PERSON'])),
 ('Lisa Blunt Rochester', list(['B-PERSON', 'I-PERSON', 'L-PERSON'])),
 ('Anthony Brown', list(['B-PERSON', 'L-PERSON'])),
 ('Ted Budd', list(['B-PERSON', 'L-PERSON'])),
 ('Troy Balderson', list(['B-PERSON', 'L-PERSON'])),
 ('James Baird', list(['B-PERSON', 'L-PERSON'])),
 ('Tim Burchett', list(['B-PERSON', 'L-PERSON']))]

我想将这些名称添加到已加载的 NER 模型中。我一直在遵循以下步骤:。这指的是 v2,但是 v3 不再使用 GoldParse,如下面 Spacy v3 文档中的文本所示:

  The Language.update, Language.evaluate and 
  TrainablePipe.update methods now all take batches of Example 
  objects instead of Doc and GoldParse objects, or raw text and a 
  dictionary of annotations.

此处显示了示例文档:https://spacy.io/api/example

下面是我更新的脚本

import spacy
from spacy import displacy
import en_core_web_sm
import random 
from spacy.util import minibatch, compounding
from spacy.tokens import Doc
from spacy.training import Example

nlp = en_core_web_sm.load()


if 'ner' not in nlp.pipe_names:
    nlp.add_pipe('ner', last=True)
else:
    ner = nlp.get_pipe('ner')

ner.add_label('PERSON')

other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
with nlp.disable_pipes(*other_pipes):  # only train NER
    optimizer = nlp.create_optimizer()
    tags = dict()
    for itn in range(10):
        print("Starting iteration " + str(itn))
        random.shuffle(data)
        losses = {}
        batches = minibatch(data, size=compounding(4.0, 16.0, 1.001))
        # type 2 with mini batch
        for batch in batches:
            texts, _ = zip(*batch)
            golds = [Example(Doc(nlp.vocab, words = t), references = a) for t,a in batch]
            nlp.update(
                texts,  # batch of texts
                golds,  # batch of annotations
                drop=0.4,  # dropout - make it harder to memorise data
                losses=losses,
                sgd=optimizer)

但是,我收到错误消息:

   Starting iteration 0
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/var/folders/36/j_203fcj42q9bvnlt1sl3j640000gp/T/ipykernel_162/1201951981.py in <module>
     14         for batch in batches:
     15             texts, _ = zip(*batch)
---> 16             golds = [Example(Doc(nlp.vocab, words = t),references = a) for t,a in batch]
     17             nlp.update(
     18                 texts,  # batch of texts

/var/folders/36/j_203fcj42q9bvnlt1sl3j640000gp/T/ipykernel_162/1201951981.py in <listcomp>(.0)
     14         for batch in batches:
     15             texts, _ = zip(*batch)
---> 16             golds = [Example(Doc(nlp.vocab, words = t),references = a) for t,a in batch]
     17             nlp.update(
     18                 texts,  # batch of texts

~/.pyenv/versions/3.8.3/lib/python3.8/site-packages/spacy/training/example.pyx in spacy.training.example.Example.__init__()

TypeError: __init__() takes exactly 2 positional arguments (1 given)

我也试过将下面的 Doc var 替换为原始代码,但仍然出现同样的错误。

 golds = [Example(nlp.make_doc(t),references = a) for t,a in batch]

我不知道如何解决这个错误。非常感谢任何帮助!

发生该错误是因为您将关键字参数 references 传递给了 Example class 构造函数,但构造函数采用两个位置参数和一个仅关键字参数 alignment。也就是说,references 不是 Example 构造函数的关键字参数。

这应该可以修复该错误。

golds = [Example(Doc(nlp.vocab, words = t), a) for t, a in batch]

我不确定您发布的数据是否代表您的实际数据。如果是这样,那么在其上训练 NER 可能没有太大价值,因为它只是一个没有上下文的实体列表。在这种情况下,您最好使用 rule-based approach like a PhraseMatcher.

我不是在这里解决你的具体错误,而是你似乎有一些误解,所以我想弄清楚事情是如何运作的。

首先,NER 模型不仅仅使用单词列表。您不能只向其中添加单词列表。如果要添加这些特定名称,您需要 包含它们的实际句子 以用作训练数据。但是您还需要一整套训练数据,所以仅仅添加这些名称并不是一个好主意。

如果你想使用 NER 模型 加上 你想始终识别这些名字,那么我会将这些名字放在 EntityRuler 中并放在管道中的 NER 组件。