mari_an_shum
@mari_an_shum
Компьютерный лингвист

В чем ошибка в процессе обучения и как ее исправить?

Добрый день, уважаемые обитатели форума!

Обучаю модель roberta base на испанском датасете conll2002 на задачу NER. В процессе тренировки модели получаю:
IndexError: boolean index did not match indexed array along dimension 0; dimension is 2 but corresponding boolean dimension is 3


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

Загрузка датасета и некоторые (не очень существенные, но нужные) манипуляции с ним:
from datasets import load_dataset
from datasets.dataset_dict import DatasetDict

data = load_dataset("conll2002", "es")
exp_data = DatasetDict()
exp_data["train"] = data["validation"]
exp_data["validation"] = data["test"]


Теперь датасет exp_data преобразуется в формат "слово - метка NER":
ner_tags = ['O', 'B-PER', 'I-PER', 'B-ORG', 'I-ORG', 'B-LOC', 'I-LOC', 'B-MISC', 'I-MISC']  # Все тэги NER. Заранее ориентированы на многословные названия сущностей, для этого им даются префиксы B и I

train_data = [{"words": elem["tokens"], "labels": [ner_tags[index] for index in elem["ner_tags"]]} for elem in exp_data["train"]]
dev_data = [{"words": elem["tokens"], "labels": [ner_tags[index] for index in elem["ner_tags"]]} for elem in exp_data["validation"]]


Потом создается токенизатор:
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("roberta-base", use_fast=True, add_prefix_space=True)


Так как roberta разбивает слова на субтокены, нужно для каждого слова распространить его NER-метку на все его субтокены. Для этого есть функция:
def make_subtoken_mask(mask, mode="first", has_cls=True, has_eos=True):
    if has_cls:
        mask = mask[1:]
    if has_eos:
        mask = mask[:-1]
    is_word = list((first != second) for first, second in zip(mask[1:], mask[:-1]))
    is_word = ([True] + is_word) if mode == "first" else (is_word+[True])
    if has_cls:
        is_word = [False] + is_word
    if has_eos:
        is_word.append(False)
    return is_word


Код специального класса UDDataset, данные в котором хранятся в разметке conllu:
from collections import Counter
import numpy as np
from torch.utils.data.dataset import Dataset

class UDDataset(Dataset):

    def __init__(self, data, tokenizer, min_count=3, tags=None, subtoken_mode="first"):
        self.data = data
        self.tokenizer = tokenizer
        if tags is None:
            tag_counts = Counter([tag for elem in data for tag in elem["labels"]])
            self.tags_ = ["<PAD>", "<UNK>"] + [x for x, count in tag_counts.items() if count >= min_count]
        else:
            self.tags_ = tags
        self.tag_indexes_ = {tag: i for i, tag in enumerate(self.tags_)}
        self.unk_index = 1
        self.ignore_index = -100
        self.subtoken_mode = subtoken_mode

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        item = self.data[index]
        tokenization = self.tokenizer(item["words"], is_split_into_words=True)
        last_subtoken_mask = make_subtoken_mask(tokenization.word_ids(), mode=self.subtoken_mode)
        answer = {"input_ids": tokenization["input_ids"], "mask": last_subtoken_mask}
        if "labels" in item:
            labels = [self.tag_indexes_.get(tag, self.unk_index) for tag in item["labels"]]
            zero_labels = np.array([self.ignore_index] * len(tokenization["input_ids"]), dtype=int)
            zero_labels[last_subtoken_mask] = labels
            answer["labels"] = zero_labels
        return answer


Из подготовленных данных создаются специальные датасеты:
train_dataset = UDDataset(train_data, tokenizer)
dev_dataset = UDDataset(dev_data, tokenizer, tags=train_dataset.tags_)


Собирается модель:
model = AutoModelForTokenClassification.from_pretrained("roberta-base", num_labels=len(train_dataset.tags_))
optimizer = AdamW(model.parameters(), lr=1e-5, weight_decay=0.01)
training_args = TrainingArguments(
    output_dir="trainer_logs", evaluation_strategy="steps", save_strategy='steps',
    logging_strategy="steps", save_total_limit=2,
    num_train_epochs=1, eval_steps=200, disable_tqdm=False,
    metric_for_best_model='F1',
    warmup_ratio=0.1
)
trainer = Trainer(
    model=model,
    optimizers=(optimizer,None),
    args=training_args,
    data_collator=DataCollatorForTokenClassification(tokenizer=tokenizer),
    train_dataset=train_dataset,
    eval_dataset=dev_dataset


Наконец, производится обучение модели, где и возникает ошибка спустя примерно 2/3 эпохи:
trainer.train()

Помогите, пожалуйста, разобраться: какую ячейку мне следует отредактировать? Подозреваю, что проблема в масках и make_subtoken_mask, но даже в этом сильно сомневаюсь.

Заранее благодарю за внимание и за комментарии!
  • Вопрос задан
  • 108 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы