使用 DocBin 训练与自定义数据读取和批处理时的 Spacy 对齐差异

use*_*154 5 python nlp named-entity-recognition spacy spacy-3

我刚刚开始训练 Spacy 命名实体识别模型,并遵循此处描述的基本示例,您可以通过实例Doc化对象并使用DocBin.

我的自定义preprocess.py文件如下所示:


if __name__ == '__main__':
    nlp = spacy.blank("en")
    counter = 0

    db = DocBin()

    with open(sys.argv[1], 'r') as fp:
        line = fp.readline()
        while line:

            record = MyRecord.build(json.loads(line))

            doc = record.to_spacy_doc(nlp=nlp)
            # internally, something like:
            # # char-level indices
            # ent = doc.char_span(0, 5, label='SOMETHING') 
            # doc.set_ents([ent])

            db.add(doc)

            counter += 1
            # hacky way to save 1000 docs in each DocBin
            if counter == 1000:
                db.to_disk("./train.spacy")
                db = DocBin()

            if counter == 2000:
                db.to_disk("./dev.spacy")
                break
            line = fp.readline()
Run Code Online (Sandbox Code Playgroud)

然后使用如下命令运行训练脚本:

python -m spacy train config.cfg --output ./output --paths.train train.spacy --paths.dev dev.spacy
Run Code Online (Sandbox Code Playgroud)

这似乎运作良好。然而,我随后了解到,您可以通过编写和注册一个生成器来编写自定义数据加载器函数,该生成器在此处Example描述的过程中生成 的实例。这让我很感兴趣,因为理论上你可以在训练循环期间读取大于 RAM 的文件。

我自己在 中编写了这样一个生成器,它从外部数据源(恰好是来自磁盘的自定义 JSONL)functions.py生成实例,其中我有一些已知的实体标签(带有字符级索引):Example

@spacy.registry.readers("corpus_variants.v1")
def stream_data(source: str) -> Callable[[Language], Iterator[Example]]:
    def generate_stream(nlp: Language):
        counter = 0
        with open(source, 'r') as fp:
            line = fp.readline()
            while line:

                record = MyRecord.build(json.loads(line))

                #doc = nlp(record.text)
                doc = nlp.make_doc(record.doc_with_annotations.text)

                entities = [
                    (start, end, label)     # char-level offets (not token-level)
                    for start, end, label, _
                    in record.get_entity_tuples()
                ]

                gold_dict = dict(
                    entities=entities
                )

                example = Example.from_dict(doc, gold_dict)
                yield example

                counter += 1
                # arbitrarily stop at 20 for debugging purposes, but ideally stream the very large file
                if counter > 20:
                    break
                line = fp.readline()
    return generate_stream
Run Code Online (Sandbox Code Playgroud)

我还进行了修改config.cfg以包含以下内容:

[corpora.dev]
@readers = "corpus_variants.v1"
source = "dev.jsonl"

[corpora.train]
@readers = "corpus_variants.v1"
source = "train.jsonl"
Run Code Online (Sandbox Code Playgroud)

当我运行训练命令时:

[corpora.dev]
@readers = "corpus_variants.v1"
source = "dev.jsonl"

[corpora.train]
@readers = "corpus_variants.v1"
source = "train.jsonl"
Run Code Online (Sandbox Code Playgroud)

我收到很多UserWarning: [W030] Some entities could not be aligned in the text警告。我读过一些关于这些警告背后的理论的文章,​​但我很好奇为什么当我保存时不会发生这种行为DocBin?对齐实际上是不同的还是仅在显式创建实例时才会出现警告Example

我对让自定义数据加载器处理这些数据感兴趣,但也对替代方法感兴趣,这些方法本质上允许我从(大于 RAM)文件中流式传输任意行作为训练示例。

最后,与回答这个问题相关的还有理解从头开始训练新的 (NER) 模型与更新现有模型之间的差异。如果我正确理解 Spacy 管道,更新现有模型可能会有一些对齐优势,因为在组装训练示例和推理过程中可以使用相同的分词器。