BERT DataLoader: разница между shuffle = True и Sampler?

Я обучил модель DistilBERT с помощью DistilBertForTokenClassification на данных ConLL для прогнозирования NER. Кажется, что обучение завершилось без проблем, но на этапе оценки у меня возникли 2 проблемы.

  1. У меня отрицательная сумма убытка

  2. Во время обучения я использовал shuffle = True для DataLoader. Но во время оценки, когда я выполняю shuffle = True для DataLoader, я получаю очень плохие метрические результаты (f_1, точность, отзыв и т. Д.). Но если я делаю shuffle = False или использую Sampler вместо перемешивания, я получаю довольно хорошие метрические результаты. Мне интересно, что-то не так с моим кодом.

Вот код оценки:


print('Prediction started on test data')
model.eval()

eval_loss = 0
predictions , true_labels = [], []

for batch in val_loader:
  b_input_ids = batch['input_ids'].to(device)
  b_input_mask = batch['attention_mask'].to(device)
  b_labels = batch['labels'].to(device)

  with torch.no_grad():
      outputs = model(b_input_ids, 
                      attention_mask=b_input_mask)

  logits = outputs[0]
  logits = logits.detach().cpu().numpy()
  label_ids = b_labels.detach().cpu().numpy()
  
  predictions.append(logits)
  true_labels.append(label_ids)

  eval_loss += outputs[0].mean().item()


print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))

из:

Prediction started on test data
Prediction completed
Validation loss: -0.2584906197858579

Я считаю, что здесь неправильно рассчитываю убыток. Можно ли получить отрицательные значения потерь с помощью BERT?

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

val_sampler = SequentialSampler(val_dataset)
val_loader = DataLoader(val_dataset, sampler=val_sampler, batch_size=128)

Но если я сделаю это, я получу очень плохие метрические результаты

val_loader = DataLoader(val_dataset, batch_size=128, shuffle=True)

Это нормально, что я получаю совершенно разные результаты с shuffle = True vs shuffle = False?

код для расчета метрики:

metric = load_metric("seqeval")
results = metric.compute(predictions=true_predictions, references=true_labels)
results

из:

{'LOCATION': {'f1': 0.9588207767898924,
  'number': 2134,
  'precision': 0.9574766355140187,
  'recall': 0.9601686972820993},
 'MISC': {'f1': 0.8658965344048217,
  'number': 995,
  'precision': 0.8654618473895582,
  'recall': 0.8663316582914573},
 'ORGANIZATION': {'f1': 0.9066332916145182,
  'number': 1971,
  'precision': 0.8947628458498024,
  'recall': 0.9188229325215627},
 'PERSON': {'f1': 0.9632426988922457,
  'number': 2015,
  'precision': 0.9775166070516096,
  'recall': 0.9493796526054591},
 'overall_accuracy': 0.988255561629313,
 'overall_f1': 0.9324058459808882,
 'overall_precision': 0.9322748349023465,
 'overall_recall': 0.932536893886156}

Вышеуказанные показатели печатаются, когда я использую Sampler или shuffle = False. Если я использую shuffle = True, я получаю:

{'LOCATION': {'f1': 0.03902284263959391,
  'number': 2134,
  'precision': 0.029496402877697843,
  'recall': 0.057638238050609185},
 'MISC': {'f1': 0.010318142734307824,
  'number': 995,
  'precision': 0.009015777610818933,
  'recall': 0.012060301507537688},
 'ORGANIZATION': {'f1': 0.027420984269014285,
  'number': 1971,
  'precision': 0.019160951996772892,
  'recall': 0.04819888381532217},
 'PERSON': {'f1': 0.02119907254057635,
  'number': 2015,
  'precision': 0.01590852597564007,
  'recall': 0.03176178660049628},
 'overall_accuracy': 0.5651741788003777,
 'overall_f1': 0.02722600361161272,
 'overall_precision': 0.020301063389034663,
 'overall_recall': 0.041321152494729445}

ОБНОВЛЕНИЕ: я изменил код потери для оценки. Кажется, с этим кодом проблем нет. Вы можете увидеть новый код ниже:

print('Prediction started on test data')
model.eval()

eval_loss = 0
predictions , true_labels = [], []

for batch in val_loader:

  b_labels = batch['labels'].to(device)

  batch = {k:v.type(torch.long).to(device) for k,v in batch.items()}
  
  with torch.no_grad():
      outputs = model(**batch)

      loss, logits = outputs[0:2]
      logits = logits.detach().cpu().numpy()
      label_ids = b_labels.detach().cpu().numpy()
  
      predictions.append(logits)
      true_labels.append(label_ids)

      eval_loss += loss


print('Prediction completed')
eval_loss = eval_loss / len(val_loader)
print("Validation loss: {}".format(eval_loss))

Хотя у меня до сих пор нет ответа на вопрос о DataLoader. Также я просто понял, что когда я делаю print(model.eval()), я все еще получаю выпадение из модели в режиме оценки.


person Harun Küfrevi    schedule 21.12.2020    source источник
comment
Можете ли вы показать в коде, как вы рассчитываете f_1, accuracy, recall   -  person mujjiga    schedule 21.12.2020
comment
Просто добавил это сейчас   -  person Harun Küfrevi    schedule 21.12.2020
comment
Я добавил еще одно обновление для суммы убытка. Кажется, сейчас все работает нормально, но у меня до сих пор нет ответа на проблему с DataLoader.   -  person Harun Küfrevi    schedule 21.12.2020


Ответы (2)


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

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

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

person ValeKnappich    schedule 21.12.2020
comment
Извините, я случайно ввел неправильный код. Обычно я складывал все значения потерь из каждой партии вместе. Я обновил фрагмент кода для этой части, и итоговые потери все еще отрицательны (-0,15). Что касается метрик, я получаю одинаковое значение потерь от обоих объектов загрузчика данных. Я также добавил код метрики внизу. Я не уверен, следует ли мне добавлять ту часть, в которой я преобразовываю логиты в фактические значения, но? Я чувствую, что это просто ненужно добавлять в пост. Спасибо за ваш ответ! - person Harun Küfrevi; 21.12.2020
comment
Также нет особой причины, по которой я получаю среднее значение убытка. Это то, что я видел на всех других ноутбуках на этапе оценки. - person Harun Küfrevi; 21.12.2020
comment
Я добавил еще одно обновление для оценки убытков. Кажется, сейчас все работает нормально, но у меня до сих пор нет ответа на проблему с DataLoader. @ValeKnappich - person Harun Küfrevi; 21.12.2020

Насколько я понимаю, ответ довольно прост:

Я видел, как мой отец делал это так, и его отец тоже делал это, так что я тоже так делаю.

Я просмотрел множество записных книжек, чтобы увидеть, как люди загружают данные для проверки, и в каждой записной книжке, которую я видел, люди использовали Sequential Sampler для проверки. Во время проверки никто не использует перемешивание или случайную выборку. Точно не знаю почему, но дело обстоит именно так. Так что, если кто-то, посетивший этот пост, задавался вопросом о том же, ответ в основном будет тем, что я цитировал выше.

Кроме того, я отредактировал исходное сообщение о проблеме с потерей, которая у меня была. Я ошибся в расчетах. По-видимому, Берт возвращает потерю в индексе 0 вывода (output [0]), если вы также скармливаете модели исходные метки. В первом фрагменте кода, когда я получал выходные данные от модели, я не скармливал модели исходные метки, поэтому она не возвращала значение потерь с индексом 0, а возвращала только логиты.

В основном вам нужно:

outputs = model(input_ids, mask, label=label)
loss = outputs[0]
logits = outputs[1]
person Harun Küfrevi    schedule 27.12.2020