[英]ValueError: Target size (torch.Size([1, 1])) must be the same as input size (torch.Size([1, 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.