В реальных задачах инженерам машинного обучения часто приходится решать несколько конкретных задач одновременно, а не одну. Для этого они используют модели типа BERT, которые предварительно обучаются на большом объеме данных, а затем настраиваются под каждую конкретную задачу. Итак, это однозадачные модели.

Однако размещение множества однозадачных моделей может потребовать большого количества ресурсов графического процессора и оперативной памяти, что приведет к высоким затратам.

Чтобы решить эту проблему, мы внедрили многозадачное обучение (MTL) в библиотеке DeepPavlov. Блокнот реализации вы можете найти здесь.

Что такое DeepPavlov?

Библиотека DeepPavlov — это диалоговая библиотека с открытым исходным кодом для обработки естественного языка (NLP) и разработки многопрофильного AI Assistant. Эта библиотека содержит множество важных современных моделей НЛП.

Вы можете просмотреть нашу предыдущую статью, чтобы увидеть введение в DeepPavlov.

В этой статье описывается многозадачное обучение (MTL) в библиотеке DeepPavlov. Эти модели поддерживаются в библиотеке с версии 1.1.1. Этот выпуск основан на PyTorch и использует пакеты Transformer и Datasets от HuggingFace для обучения различных моделей на основе преобразователей на сотнях наборов данных.

Что такое модель многозадачности?

Модель многозадачного машинного обучения — это модель, которая может одновременно обрабатывать и выполнять несколько отдельных задач. Он получает общие функции, используя базовую модель, а затем использует эти функции, используя уровни для конкретных задач.

Проиллюстрируем это на примере человека, пытающегося угадать настроение, личность и род занятий своего собеседника:

Представьте, что вы встречаете нового человека на общественном мероприятии и хотите узнать о нем больше, помимо первоначального знакомства. Будучи человеком, вы из прошлого опыта узнали, что определенные наблюдаемые сигналы могут дать подсказки о настроении, личности и роде деятельности человека. Эти сигналы могут быть аналогичны функциям, используемым в моделях машинного обучения.

1. Задача на распознавание настроения: Чтобы оценить настроение ребенка, вы можете посмотреть на выражение его лица, язык тела и тон голоса. Улыбка может указывать на позитивное настроение, а нахмуренные брови могут указывать на стресс или беспокойство.

2. Задача определения личности: Во время разговора вы можете обратить внимание на стиль общения, интересующие темы и социальное взаимодействие. Общительное и разговорчивое поведение может указывать на экстравертированную личность, тогда как тот, кто предпочитает слушать, может склоняться к интроверсии.

3. Задача по определению профессии: Вы можете наблюдать за их одеждой, аксессуарами и любыми обсуждениями, связанными с работой. Униформа или специфическая одежда, связанная с профессией, может намекать на род занятий.

Делая эти прогнозы, вы научитесь извлекать соответствующие функции для каждой подзадачи и использовать общую информацию для всех задач. Например, позитивное выражение лица может указывать как на позитивное настроение, так и на общительный характер.

Теперь давайте проведем параллель с многозадачной моделью:

Точно так же, как люди могут использовать знания из прошлых взаимодействий с разными людьми, чтобы улучшить свои способности делать предположения о новых компаньонах, многозадачные модели учатся использовать информацию из нескольких задач для улучшения своей общей производительности и возможностей обобщения.

Таким образом, многозадачное обучение не только дешевле в вычислительном отношении, чем однозадачное. Это также может обеспечить эффект передачи знаний: эффект, когда знания, полученные при выполнении одной задачи, повышают производительность при выполнении других задач.

Многозадачная архитектура в DeepPavlov

Наша модель MTL основана на классе AutoModel от HuggingFace, который позволяет использовать в качестве основы различные архитектуры на основе трансформаторов. Для справки см. Список поддерживаемых моделей HuggingFace.

Другими словами, эта модель использует линейный слой для каждой задачи поверх выходных данных. Это так просто.

Многозадачная модель в наших условиях почти не требует дополнительных параметров и вычислительных затрат, за исключением линейных слоев, поэтому ее простота выделяет ее.

Наша реализация MTL поддерживает:

  • классификация текста
  • текстовая регрессия (например, STS-B)
  • задача маркировки последовательности (например, NER, маркировка POS)
  • задание с множественным выбором (например, COPA)

Он может работать как с предложениями, так и с парами предложений.

Для набора задач GLUE многозадачные модели в среднем работают лучше, чем однозадачные (подробности см. в нашей документации). Подробнее о таких моделях вы также можете прочитать в нашей статье.

Ниже вы увидите, как можно вызывать многозадачные модели DeepPavlov.

Вывод многозадачной конфигурации

Вот пример кода для вывода этих моделей. Мы покажем, как эта модель работает для всех поддерживаемых типов задач. Этот пример вы также можете найти в этом блокноте.

Прежде всего нам необходимо установить DeepPavlov соответствующей версии.

# Install the right version
!pip install deeppavlov==1.1.1
from deeppavlov import build_model, configs
model = build_model('multitask_example', download=True, install=True)
# If you use your config from scratch, it should look like
# model = build_model('path/to/your/config.json')
tasks =['cola', 'rte', 'stsb', 'copa', 'conll']
# the same order as config
x=dict()
for task in tasks:  # Buillding input
  if task=='rte': # Sentence pair classification/regression
  # Example can be a tuple
      x[task]=[('pair 1 phrase 1', 'pair 1 phrase 2'),
               ('pair 2 phrase 1', 'pair 2 phrase 2')]
  elif task=='cola': # Single sentence classification/regression
  # Example can be a string
      x[task]=['phrase1']
  elif task=='conll': # NER
  # For NER, examples are strings
      x[task]=['first second'] # NER
  elif task=='stsb': # Single sentence regression.
  #Examples for any task can be empty, like in that case
      x[task]=[]
  elif task=='copa':
      x[task]=[('context in pair 1', ['choice 1 in pair 1', 'choice 2 in pair 1']),
               ('context in pair 2', ['choice 1 in pair 2', 'choice 2 in pair 2'])]
  # Illustrating multiple choice task
  else:
      x[task]=['test phrase']
list_of_x = [x[task] for task in tasks]
list_of_y = [[] for _ in tasks]
args = list_of_x + list_of_y
outputs=model(*args)

Ниже мы подробно объясним, как можно обучить модель многозадачности для собственных задач. Для этого вам сначала нужно сделать конфигурацию.

Мы покажем вам создание конфигурации, которая объединяет задачу классификации пар предложений (RTE) с задачей маркировки последовательностей (CONLL) и задачей с множественным выбором (COPA). Мы сохранили только эти задачи из вышеупомянутого примера, чтобы упростить конфигурацию. Вы можете скачать этот оптимизированный конфиг здесь.

Делаем многозадачный конфиг

Считыватель наборов данных

Прежде чем работать с данными, нам необходимо их прочитать. Именно поэтому в библиотеке DeepPavlov необходим компонент dataset_reader.

Для реализации компонента dataset_reader мы используем класс multitask_reader. Этот класс должен иметь параметр tasks, который представляет собой словарь {имя задачи: параметры задачи}. Порядок задач в этом словаре должен быть точно таким же, как и на последующих этапах настройки.

Любой параметр для любой задачи, если он не существует в этом словаре, извлекается из другого параметра с именем task_defaults. task_defaults содержит словарь по умолчанию для любой задачи (словарь также может быть пустым).

Поля dataset_reader, path, train, validation и test должны существовать для все задачи — либо как поля по умолчанию, либо как поля, явно заданные в словаре.

Например:

{
  "dataset_reader": {
    "class_name": "multitask_reader",
    "task_defaults": {
      "class_name": "huggingface_dataset_reader",
      "path": "glue",
      "train": "train",
      "valid": "validation",
      "test": "test"
    },
    "tasks": {
      "rte": {"name": "rte"},
      "copa": {
        "path": "super_glue",
        "name": "copa"
      },
      "conll": {
        "class_name": "conll2003_reader",
        "use_task_defaults": false,
        "data_path": "{DOWNLOADS_PATH}/conll2003/",
        "dataset_name": "conll2003",
        "provide_pos": false
      }
    }
  },

Здесь также можно использовать свои данные, как и в любом конфиге DeepPavlov.

Итератор набора данных

Для реализации компонента dataset_iterator мы используем класс multitask_iterator. В этот класс мы также передаем задачи-словари, которые содержат имя класса итератора и параметры (если они установлены) для всех задач аналогично multitask_reader.

Мы также задаем количество шагов накопления градиента, эпохи обучения и размер пакета в одном классе (эти параметры также должны быть в трейнере).

Также мы переходим в режим выборки multitask_iterator, который определяет для каждой задачи вероятность того, что выборки будут взяты из ее набора выборок. Мы поддерживаем равномерную выборку (одинаковую вероятность выборки для всех задач), простую выборку (вероятность выборки пропорциональна количеству выборок) и отожженную выборку (как в этой статье).

Обратите внимание, что dataset_reader и dataset_iterator не требуются в настройке только вывода.

"dataset_iterator": {
    "class_name": "multitask_iterator",
    "num_train_epochs": "{NUM_TRAIN_EPOCHS}",
    "gradient_accumulation_steps": "{GRADIENT_ACC_STEPS}",
    "seed": 42,
    "task_defaults": {
      "class_name": "huggingface_dataset_iterator",
      "label": "label",
      "use_label_name": false,
      "seed": 42
    },
    "tasks": {
      "rte": {
        "features": ["sentence1", "sentence2"]
      },
      "copa": {
        "features": ["contexts", "choices"]
      },
      "conll": {
        "class_name": "basic_classification_iterator",
        "seed": 42,
        "use_task_defaults": false
      }
    }
  },

Цепной

Компонент chainer может использовать элементы для каждой задачи независимо от элементов другой задачи.

Однако, чтобы упростить многозадачную предварительную обработку, мы ввели дополнительный класс multitask_pipeline_preprocessor. Для этого класса вам следует установить vocab_file для токенизатора и либо препроцессор по умолчанию имя класса, либо список имен препроцессоров(а не те, которые используются в конфигурациях, но те, которые определены в библиотеке). Пользователь также может указать, следует ли делать примеры строчными буквами и печатать ли первый пример. Распечатка первого примера может помочь при отладке, поскольку помогает исключить возможные проблемы с данными.

"chainer": {
    "in": ["x_rte", "x_copa", "x_conll"],
    "in_y": ["y_rte", "y_copa", "y_conll"],
    "pipe": [
      {
        "class_name": "multitask_pipeline_preprocessor",
        "possible_keys_to_extract": [0, 1],
        "preprocessors": [
          "TorchTransformersPreprocessor",
          "TorchTransformersMultiplechoicePreprocessor",
          "TorchTransformersNerPreprocessor"
        ],
        "do_lower_case": true,
        "n_task": 3,
        "vocab_file": "{BACKBONE}",
        "max_seq_length": 200,
        "max_subword_length": 15,
        "token_masking_prob": 0.0,
        "return_features": true,
        "in": ["x_rte", "x_copa", "x_conll"],
        "out": [
          "bert_features_rte",
          "bert_features_copa",
          "bert_features_conll"
        ]
      },
      {
        "id": "vocab_conll",
        "class_name": "simple_vocab",
        "unk_token": ["O"],
        "pad_with_zeros": true,
        "save_path": "{MODELS_PATH}/tag.dict",
        "load_path": "{MODELS_PATH}/tag.dict",
        "fit_on": ["y_conll"],
        "in": ["y_conll"],
        "out": ["y_ids_conll"]
      },

Многозадачный_трансформер

Для определения модели многозадачности мы используем класс multitask_transformer. В этом классе определяется базовая модель многозадачного обучения; мы советуем использовать тот же самый, который использовался для токенизации в предыдущих компонентах.

В этом классе вы должны указать в качестве параметра tasks словарь, который имеет точно такой же порядок задач, как в считывателе, итераторе и компонентах in_x и in_y в цепочке. >

Для каждой задачи необходимо установить параметры options и task_type.

Вы указываете «in» (bert_features, тот же порядок, что и задачи) и «in_y» (y для каждой задачи, также тот же порядок) и получаете вероятности, если return_probas имеет значение True или помечает индексы в противном случае. Это не относится к задачам регрессии и NER (sts-b и conll в конфигурации).

{
        "id": "multitask_transformer",
        "class_name": "multitask_transformer",
        "optimizer_parameters": {"lr": 2e-5},
        "gradient_accumulation_steps": "{GRADIENT_ACC_STEPS}",
        "learning_rate_drop_patience": 2,
        "learning_rate_drop_div": 2.0,
        "return_probas": true,
        "backbone_model": "{BACKBONE}",
        "save_path": "{MODEL_PATH}",
        "load_path": "{MODEL_PATH}",
        "tasks": {
          "rte": {
            "type": "classification",
            "options": 2
          },
          "copa": {
            "type": "multiple_choice",
            "options": 2
          },
          "conll": {
            "type": "sequence_labeling",
            "options": "#vocab_conll.len"
          }
        },
        "in": [
          "bert_features_rte",
          "bert_features_copa",
          "bert_features_conll"
        ],
        "in_y": ["y_rte", "y_copa", "y_ids_conll"],
        "out": [
          "y_rte_pred_probas",
          "y_copa_pred_probas",
          "y_conll_pred_ids"
        ]
      }

Показатели многозадачности

После multitask_transformer все остальные компоненты такие же, как и в однозадачной настройке:

{
        "in": ["y_rte_pred_probas"],
        "out": ["y_rte_pred_ids"],
        "class_name": "proba2labels",
        "max_proba": true
      },
      {
        "in": ["y_copa_pred_probas"],
        "out": ["y_copa_pred_ids"],
        "class_name": "proba2labels",
        "max_proba": true
      },
      {
        "in": ["y_conll_pred_ids"],
        "out": ["y_conll_pred_labels"],
        "ref": "vocab_conll"
      }
    ],
    "out": ["y_rte_pred_ids", "y_copa_pred_ids", "y_conll_pred_labels"]
  },

Однако метрики multitask_accuracy, multitask_f1_macro и multitask_f1_weighted являются новыми. Каждая из этих метрик вычисляется путем расчета соответствующих метрик (точность, f1-макро и f1-взвешенная) для всех задач отдельно, а затем усреднение метрик по задачам соответственно. Как и в любой конфигурации библиотеки DeepPavlov, ранняя остановка выполняется для первой метрики в списке метрик. В этой конфигурации это многозадачная точность, которая рассчитывается как среднее значение всех точностей по задачам, невзвешенное.

"train": {
    "epochs": "{NUM_TRAIN_EPOCHS}",
    "batch_size": 32,
    "metrics": [
      {
        "name": "multitask_accuracy",
        "inputs": ["y_rte", "y_copa", "y_rte_pred_ids", "y_copa_pred_ids"]
      },
      {
        "name": "ner_f1",
        "inputs": ["y_conll", "y_conll_pred_labels"]
      },
      {
        "name": "ner_token_f1",
        "inputs": ["y_conll", "y_conll_pred_labels"]
      },
      {
        "name": "accuracy",
        "alias": "accuracy_rte",
        "inputs": ["y_rte", "y_rte_pred_ids"]
      },
      {
        "name": "accuracy",
        "alias": "accuracy_copa",
        "inputs": ["y_copa", "y_copa_pred_ids"]
      }
    ],
    "validation_patience": 3,
    "log_every_n_epochs": 1,
    "show_examples": false,
    "evaluation_targets": ["valid"],
    "class_name": "torch_trainer",
    "pytest_max_batches": 2
  },
  "metadata": {
    "variables": {
      "ROOT_PATH": "~/.deeppavlov",
      "MODELS_PATH": "{ROOT_PATH}/models/multitask_example",
      "DOWNLOADS_PATH": "{ROOT_PATH}/downloads",
      "BACKBONE": "distilbert-base-uncased",
      "MODEL_PATH": "{MODELS_PATH}/{BACKBONE}_3task",
      "NUM_TRAIN_EPOCHS": 5,
      "GRADIENT_ACC_STEPS": 1
    }
  }
}

Обучение модели

Для обучения описанной выше модели многозадачности (mtl_3task.json) необходимо выполнить одну команду:

python -m deeppavlov train mtl_3task.json

Заключение

Многозадачная модель, реализованная в библиотеке DeepPavlov, позволяет существенно сократить использование вычислительных ресурсов без ущерба для качества модели. Мы надеемся, что это было полезно и что вы захотите использовать библиотеку DeepPavlov для своих собственных сценариев многозадачного обучения.

Подробнее о нас вы можете прочитать в нашем официальном блоге, а о многозадачном обучении в DeepPavlov — в нашем руководстве. Посетите нас на странице GitHub. И не забывайте, что у DeepPavlov есть специальный форум, где приветствуются любые вопросы, касающиеся фреймворка и моделей. Мы ценим ваши отзывы, дайте нам знать, что вам нравится и что вам не нравится в библиотеке DeepPavlov.