原帖末层 Linear(input_size,1),却把浮点标签交给 CrossEntropyLoss:
RuntimeError: Expected object of scalar type Long but got scalar type Float for argument #2 'target'
类型修完后,还要确认类别数
原题的单输出层只给交叉熵一个类别通道。如果标签含 1,简单 .long() 会把错误推进到“类别越界”。因此这题与普通的 Long/Float 标签错误不同,需要把最后一层、损失和准确率计算一起确认。
这里同时存在两个需要判断的问题:目标类型与输出定义。把标签转成 long,未必足以修复只有一个 logit 的二分类模型。
在损失调用前,y_hat 是模型输出,y 是当前标签:
print("josh050608")
print("prediction:", y_hat.shape, y_hat.dtype)
print("target:", y.shape, y.dtype, y.unique())
若保留一个输出神经元,采用 BCEWithLogitsLoss。下面假定 y 是每样本一个 0/1 值,输出为 [N,1]:
import torch.nn.functional as F
target = y.reshape(-1, 1).to(y_hat)
assert target.shape == y_hat.shape
loss = F.binary_cross_entropy_with_logits(y_hat, target)
pred = (y_hat.squeeze(1) >= 0).long()
accuracy = (pred == y.reshape(-1).long()).float().mean()
模型末尾不要再加 sigmoid,预测阶段才把 logit 转为概率。原题还计算 L2 正则项,可以在得到这个标量 loss 后按原设计相加。
若坚持用交叉熵,末层应为 Linear(input_size,2),输出 [N,2] 原始 logits,标签为 [N] long,取值 0 或 1;预测使用 argmax(dim=1)。只有一个类别通道时,类别 1 不在交叉熵可接受的范围内。
修复后先做一批前向与反向,检查标签含义和类别映射,再测准确率。不要把所有模型输入改成 long,这个类型要求针对的是类别索引目标。
两种完整设计的对照小例子
下面使用相同的合成二分类标签,分别验证单 logit 与双 logit 的接口,不声称两套随机模型的数值结果相同:
import torch
from torch import nn
x = torch.randn(4, 3)
labels = torch.tensor([0, 1, 1, 0], dtype=torch.long)
one = nn.Linear(3, 1)
two = nn.Linear(3, 2)
logit = one(x)
binary_target = labels.to(logit).unsqueeze(1)
loss_one = nn.BCEWithLogitsLoss()(logit, binary_target)
logits = two(x)
loss_two = nn.CrossEntropyLoss()(logits, labels)
loss_one.backward()
loss_two.backward()
assert one.weight.grad is not None and two.weight.grad is not None
prediction_one = (logit.squeeze(1) >= 0).long()
prediction_two = logits.argmax(dim=1)
assert prediction_one.shape == prediction_two.shape == labels.shape
print("losses:", loss_one.item(), loss_two.item())
单输出方案中,浮点标签是 BCE 接口所需;双输出方案中,long 标签表示两个互斥类别的编号。这不是一条“所有标签永远转 long”的规则。
原题还手工把 L2 项加到损失。保留时确认正则项是与参数相连的标量,再与正确的基础损失相加;不要把 .item() 后的 Python 数值当作仍可对参数求导的正则项。如果同时给优化器设置 weight_decay,还应核对是否有意重复惩罚。
修改完成后分别检查 loss 的标量性、参数 grad、预测 shape 和 0/1 类别映射。准确率若把 [N,1] 与 [N] 直接比较,可能广播成 [N,N],应像示例那样先对齐为一维再逐样本比较。