Создание пользовательского анализатора зависимостей с нуля в Spacy 3

#python-3.x #spacy #spacy-3

Вопрос:

Я пытаюсь реализовать свой собственный анализатор зависимостей с нуля в Spacy 3. Я создаю пустую модель, создаю пустой анализатор зависимостей, обучаю его и сохраняю его конфигурацию. Но когда я снова пытаюсь загрузить конфигурацию пользовательского синтаксического анализатора, я могу сделать это успешно только в том случае, если модель пуста. Если я использую непустую модель, то я продолжаю получать эту ошибку-ошибка значения: не удалось передать входной массив из формы (106,64) в форму (27,64).

 import spacy
import random
from spacy.tokens import Doc
from spacy.training import Example
from spacy.pipeline import DependencyParser
from typing import List, Tuple

PARSER_CONFIG = 'parser.cfg'
TRAINING_DATA = [
    ('find a high paying job with no experience', {
        'heads': [0, 4, 4, 4, 0, 7, 7, 4],
        'deps': ['ROOT', '-', 'QUALITY', 'QUALITY', 'ACTIVITY', '-', 'QUALITY', 'ATTRIBUTE']
    }),
    ('find good workout classes near home', {
        'heads': [0, 3, 3, 0, 5, 3],
        'deps': ['ROOT', 'QUALITY', 'QUALITY', 'ACTIVITY', 'QUALITY', 'ATTRIBUTE']
    })
]


def create_training_examples(training_data: List[Tuple]) -> List[Example]:
    """ Create list of training examples """
    examples = []
    nlp = spacy.load('en_core_web_md')
    for text, annotations in training_data:
        print(f"{text} - {annotations}")
        examples.append(Example.from_dict(nlp(text), annotations))
    return examples


def save_parser_config(parser: DependencyParser):
    print(f"Save parser config to '{PARSER_CONFIG}' ... ", end='')
    parser.to_disk(PARSER_CONFIG)
    print("DONE")


def load_parser_config(parser: DependencyParser):
    print(f"Load parser config from '{PARSER_CONFIG}' ... ", end='')
    parser.from_disk(PARSER_CONFIG)
    print("DONE")


def main():
    nlp = spacy.blank('en')
    # Create new parser
    parser = nlp.add_pipe('parser', first=True)
    for text, annotations in TRAINING_DATA:
        for label in annotations['deps']:
            if label not in parser.labels:
                parser.add_label(label)
    print(f"Added labels: {parser.labels}")

    examples = create_training_examples(TRAINING_DATA)

    # Training
    # NOTE: The 'lambda: examples' part is mandatory in Spacy 3 - https://spacy.io/usage/v3#migrating-training-python
    optimizer = nlp.initialize(lambda: examples)
    print(f"Training ... ", end='')
    for i in range(25):
        print(f"{i} ", end='')
        random.shuffle(examples)
        nlp.update(examples, sgd=optimizer)
    print(f"... DONE")

    save_parser_config(parser)

    # I can load parser config to blank model ...
    nlp = spacy.blank('en')
    parser = nlp.add_pipe('parser')

    # ... but I cannot load parser config to already existing model
    # Return -> ValueError: could not broadcast input array from shape (106,64) into shape (27,64)
    # nlp = spacy.load('en_core_web_md')
    # parser = nlp.get_pipe('parser')

    load_parser_config(parser)

    print(f"Current pipeline is {nlp.meta['pipeline']}")

    doc = nlp(u'find a high paid job with no degree')
    print(f"Arcs: {[(w.text, w.dep_, w.head.text) for w in doc if w.dep_ != '-']}")


if __name__ == '__main__':
    main()
 

Сам пользовательский синтаксический анализатор работает так, как ожидалось. Вы можете проверить это, закомментировав весь код от save_parser_config(parser) до load_parser_config(parser) (включительно) и запустив код снова. Вы увидите, что новые метки назначаются по мере необходимости. Вот почему я думаю, что корень проблемы заключается в невозможности загрузить конфигурацию анализатора пустой модели в непустую модель. Но как это обойти?

Комментарии:

1. То же самое на форумах spaCy github.com/explosion/spaCy/discussions/9239

Ответ №1:

Я связался с разработчиками, и вот что они ответили — https://github.com/explosion/spaCy/discussions/9239