Тонкая настройка DistilBertForSequenceClassification: не обучение, почему потери не меняются? Вес не обновляется? - PullRequest
0 голосов
/ 02 августа 2020

Я относительно новичок в преобразователях PyTorch и Huggingface и экспериментировал с DistillBertForSequenceClassification на этом Kaggle-Dataset .

from transformers import DistilBertForSequenceClassification
import torch.optim as optim
import torch.nn as nn
from transformers import get_linear_schedule_with_warmup

n_epochs = 5 # or whatever
batch_size = 32 # or whatever

bert_distil = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')
#bert_distil.classifier = nn.Sequential(nn.Linear(in_features=768, out_features=1), nn.Sigmoid())
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(bert_distil.parameters(), lr=0.1)

X_train = []
Y_train = []

for row in train_df.iterrows():
    seq = tokenizer.encode(preprocess_text(row[1]['text']),  add_special_tokens=True, pad_to_max_length=True)
    X_train.append(torch.tensor(seq).unsqueeze(0))
    Y_train.append(torch.tensor([row[1]['target']]).unsqueeze(0))
X_train = torch.cat(X_train)
Y_train = torch.cat(Y_train)

running_loss = 0.0
bert_distil.cuda()
bert_distil.train(True)
for epoch in range(n_epochs):
    permutation = torch.randperm(len(X_train))
    j = 0
    for i in range(0,len(X_train), batch_size):
        optimizer.zero_grad()
        indices = permutation[i:i+batch_size]
        batch_x, batch_y = X_train[indices], Y_train[indices]
        batch_x.cuda()
        batch_y.cuda()
        outputs = bert_distil.forward(batch_x.cuda())
        loss = criterion(outputs[0],batch_y.squeeze().cuda())
        loss.requires_grad = True
   
        loss.backward()
        optimizer.step()
   
        running_loss += loss.item()  
        j+=1
        if j == 20:   
            #print(outputs[0])
            print('[%d, %5d] running loss: %.3f loss: %.3f ' %
              (epoch + 1, i*1, running_loss / 20, loss.item()))
            running_loss = 0.0
            j = 0

[1, 608] текущие потери: 0,689 потери: 0,687 [1, 1248] текущие потери: 0,693 потери: 0,694 [1, 1888] текущие потери: 0,693 потери: 0,683 [1, 2528] текущие потери: 0,689 потери: 0,701 [1, 3168] текущие потери: 0,690 потери: 0,684 [1, 3808] текущий убыток: 0,689 убыток: 0,688 [1, 4448] текущий убыток: 0,689 убыток: 0,692 et c ...

Независимо от того, что я пробовал, убыток никогда не был уменьшаться или даже увеличиваться, и при этом прогноз не улучшился. Мне кажется, я что-то забыл, так что веса фактически не обновляются. У кого-то есть идея? O

что я пробовал

  • Различные функции потерь
    • BCE
    • CrossEntropy
    • даже MSE -loss
  • One-Hot Encoding против одиночного вывода нейрона
  • Разные скорости обучения и оптимизаторы
  • Я даже изменил все цели на одну одна метка, но даже тогда сеть не сходилась.

Ответы [ 2 ]

1 голос
/ 27 августа 2020

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

Кроме того, в вашем коде есть некоторые проблемы, исправляющие все из них, и поведение такое, как ожидалось: потеря медленно уменьшается после каждого эпохи, и он также может быть заменен на небольшую мини-партию. Пожалуйста, посмотрите на код, изменения включают: использование model(x) вместо model.forward(x), cuda() вызывается только один раз, меньшая скорость обучения и т. Д. c.

Настройка и точная настройка моделей ML затруднены работа.

n_epochs = 5
batch_size = 1

bert_distil = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(bert_distil.parameters(), lr=1e-3)

X_train = []
Y_train = []
for row in train_df.iterrows():
    seq = tokenizer.encode(row[1]['text'],  add_special_tokens=True, pad_to_max_length=True)[:100]
    X_train.append(torch.tensor(seq).unsqueeze(0))
    Y_train.append(torch.tensor([row[1]['target']]))
X_train = torch.cat(X_train)
Y_train = torch.cat(Y_train)

running_loss = 0.0
bert_distil.cuda()
bert_distil.train(True)
for epoch in range(n_epochs):
    permutation = torch.randperm(len(X_train))
    for i in range(0,len(X_train), batch_size):
        optimizer.zero_grad()
        indices = permutation[i:i+batch_size]
        batch_x, batch_y = X_train[indices].cuda(), Y_train[indices].cuda()
        outputs = bert_distil(batch_x)
        loss = criterion(outputs[0], batch_y)
        loss.backward()
        optimizer.step()
   
        running_loss += loss.item()  

    print('[%d] epoch loss: %.3f' %
      (epoch + 1, running_loss / len(X_train) * batch_size))
    running_loss = 0.0

Вывод:

[1] epoch loss: 0.695
[2] epoch loss: 0.690
[3] epoch loss: 0.687
[4] epoch loss: 0.685
[5] epoch loss: 0.684
1 голос
/ 26 августа 2020

Я бы выделил две возможные причины ваших «стабильных» результатов:

  1. Я согласен с тем, что скорость обучения определенно слишком высокая , что мешает модели из каких-либо значительных обновлений.
  2. Но важно знать, что точная настройка, основанная на современных документах, имеет очень незначительное влияние на основные способности Трансформеров в области НЛП. Например, в paper сказано, что точная настройка применяет только очень небольшие изменения веса. Ссылаясь на это: «Тонкая настройка практически не влияет на точность NEL, COREF и REL, что указывает на то, что эти задачи уже достаточно покрыты предварительной подготовкой». Некоторые статьи предполагают, что тонкая настройка задач классификации - это, по сути, трата времени. Таким образом, учитывая, что DistilBert на самом деле является студенческой моделью BERT, возможно, вы не получите лучших результатов. Попробуйте предварительное обучение с вашими данными. Как правило, предварительная подготовка имеет более значительное влияние.
...