簡體   English   中英

BERT 分類器 ValueError:目標大小 (torch.Size([4, 1])) 必須與輸入大小 (torch.Size([4, 2])) 相同

[英]BERT Classifier ValueError: Target size (torch.Size([4, 1])) must be the same as input size (torch.Size([4, 2]))

我正在訓練分類器模型,但幾天后我無法克服問題! 我有 ValueError: Target size (torch.Size([4, 1])) must be the same as input size (torch.Size([4, 2])) 錯誤,但實際上對我來說似乎是正確的! 事實上,我使用 unsqueeze(1) 將它們的大小相同。 我還能嘗試什么? 謝謝!

class SequenceClassifier(nn.Module):

  def __init__(self, n_classes):
    super(SequenceClassifier, self).__init__()
    self.bert = BertModel.from_pretrained(PRE_TRAINED_MODEL_NAME,return_dict=False)
    self.drop = nn.Dropout(p=0.3)
    self.out = nn.Linear(self.bert.config.hidden_size, n_classes)
  
  def forward(self, input_ids, attention_mask):
    _, pooled_output = self.bert(
      input_ids=input_ids,
      attention_mask=attention_mask
    ) 
    output = self.drop(pooled_output)
    return self.out(output)

model = SequenceClassifier(len(class_names))
model = model.to(device)

EPOCHS = 10

optimizer = AdamW(model.parameters(), lr=2e-5, correct_bias=False)
total_steps = len(train_data_loader) * EPOCHS

scheduler = get_linear_schedule_with_warmup(
  optimizer,
  num_warmup_steps=0,
  num_training_steps=total_steps
)
weights=[0.5,1]
pos_weight=torch.FloatTensor(weights).to(device)
loss_fn=nn.BCEWithLogitsLoss(pos_weight=pos_weight)

def train_epoch(
  model, 
  data_loader, 
  loss_fn, 
  optimizer, 
  device, 
  scheduler, 
  n_examples
):
  model = model.train()

  losses = []
  correct_predictions = 0
  
  for d in data_loader:
    input_ids = d["input_ids"].to(device)
    attention_mask = d["attention_mask"].to(device)
    targets = d["targets"].to(device)

    outputs = model(
      input_ids=input_ids,
      attention_mask=attention_mask
    )

    _, preds = torch.max(outputs, dim=1)
    
    targets = targets.unsqueeze(1)
    loss = loss_fn(outputs, targets)
    

    correct_predictions += torch.sum(preds == targets)
    losses.append(loss.item())

    loss.backward()
    nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
    optimizer.step()
    scheduler.step()
    optimizer.zero_grad()

  return correct_predictions.double() / n_examples, np.mean(losses)
  

%%time

history = defaultdict(list)
best_accuracy = 0

for epoch in range(EPOCHS):

  print(f'Epoch {epoch + 1}/{EPOCHS}')
  print('-' * 10)

  train_acc, train_loss = train_epoch(
    model,
    train_data_loader,    
    loss_fn, 
    optimizer, 
    device, 
    scheduler, 
    len(df_train)
  )

  print(f'Train loss {train_loss} accuracy {train_acc}')

  val_acc, val_loss = eval_model(
    model,
    val_data_loader,
    loss_fn, 
    device, 
    len(df_val)
  )

  print(f'Val   loss {val_loss} accuracy {val_acc}')
  print()

  history['train_acc'].append(train_acc)
  history['train_loss'].append(train_loss)
  history['val_acc'].append(val_acc)
  history['val_loss'].append(val_loss)

  if val_acc > best_accuracy:
    torch.save(model.state_dict(), 'best_model_state.bin')
    best_accuracy = val_acc
ValueError: Target size (torch.Size([4, 1])) must be the same as input size (torch.Size([4, 2]))

編輯我有一個二進制分類器問題,實際上我有 2 個類編碼為 0(“壞”)和 1(“好”)。

萬一有人像我一樣偶然發現這個問題,我會寫出一個答案,因為這個目標大小/輸入大小錯誤並沒有很多谷歌點擊,並且之前的答案有一些事實不准確。

與之前的答案不同,真正的問題不在於損失函數,而在於模型的輸出。 nn.BCEWithLogitsLoss完全適用於多標簽和多類應用程序。 Chiara 更新了她的帖子,說事實上她有一個二元分類問題,但即使這樣也不應該是這個損失函數的問題。 那么為什么會出錯呢?

原始代碼有:

outputs = model(
  input_ids=input_ids,
  attention_mask=attention_mask
)
_, preds = torch.max(outputs, dim=1)

這意味着“運行模型,然后使用模型最高輸出的行索引創建preds ”。 顯然,如果有多個預測值,則只有一個“最高索引”。 多個輸出值通常意味着多個輸入類,所以我可以理解為什么 Shai 雖然這是多類。 但是為什么我們會從一個二元分類器中得到多個輸出呢?

事實證明,二元問題的 BERT(或 Huggingface)期望將n_classes設置為 2——將 classes 設置為 1 會使模型處於回歸模式。 這意味着在后台,二元問題被視為兩類問題,輸出大小為 [2, batch size ] 的預測——一列預測它為 1 的機會,一列預測它為 0 的機會. 損失函數會引發錯誤,因為它只提供了一行 one-hot 編碼標簽: targets = d["targets"].to(device)所以標簽的尺寸為 [ batch size ] 或在 unsqueeze 之后, [ 1、批量大小]。 無論哪種方式,尺寸都不匹配。

一些損失函數可以很好地處理這個問題,但其他損失函數需要完全相同的維度。 更令人沮喪的是,對於 1.10 版本, nn.BCEWithLogitsLoss需要匹配維度,但更高版本不需要。

因此,一種解決方案可能是更新您的 pytorch(例如 1.11 版即可)。

對我來說,這不是一個選擇,所以我最終選擇了不同的損失函數。 正如 Shai 所建議的, nn.CrossEntropyLoss確實可以解決問題,因為它接受任何具有相同長度的輸入。 換句話說,他們出於錯誤的原因有一個可行的解決方案。

您正在使用nn.BCEWithLogitsLoss損失函數。 此損失函數適用於二元分類任務,並期望預測和目標具有相同的形狀(和float數據類型)。
這與多類 CE 損失函數nn.CrossEntropyLoss形成對比,后者期望目標是指向預測類概率中正確位置的整數

仔細閱讀您正在使用的功能的文檔,並確保您正確使用它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM