PyTorch CrossEntropyLoss维度越界错误排查求助
问题:CNN训练时出现维度越界错误
代码背景
导入库:
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim
拥有尺寸为50 x 37 = 1850的向量化图像,x_train存储向量化图像,y_train存储真实标签,单样本形状:
data.shape torch.Size([1850])
错误代码
模型定义
class Net(nn.Module): def __init__(self, num_classes): super(EigenfaceDenseNet, self).__init__() # 类名不匹配错误 self.model = nn.Sequential( nn.Linear(50*37,200), nn.ReLU(), nn.Linear(200,200), nn.ReLU(), nn.Linear(200, num_classes), nn.ReLU(), # 输出层不需要ReLU,CrossEntropyLoss需要logits ) def forward(self, x): x = x.view(-1, 50*37) # 展平为一维 return self.model(x)
初始化组件
net = Net(10); # 10 == 数据集中的类别数量。 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001)
训练循环
n_epochs = 3 for epoch in range(n_epochs): running_loss = 0.0 for i, data in enumerate(zip(X_train, y_train)): # (索引 (图像, 标签)) inputs, labels = torch.tensor(data[0]), torch.tensor(data[1]) outputs = net(inputs) print(inputs.shape) onehot_labels = torch.tensor([(float(1) if i == labels else 0) for i in range(n_classes)]) # 变量名i冲突,且CrossEntropyLoss不需要手动转one-hot print(outputs[0]) print(onehot_labels) loss_v = criterion(outputs[0], onehot_labels) # 维度不匹配,且target格式错误 loss_v.backward() running_loss += loss_v.item() if i % 2000 == 1999: # 每2000个小批量打印一次 print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}') running_loss = 0.0 print("训练完成")
错误信息
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Input In [76], in <cell line: 3>() 12 print(outputs[0]) 13 print(onehot_labels) ---> 15 loss_v = criterion(outputs[0], onehot_labels) 17 loss_v.backward() 19 running_loss += loss_v.item() File ~\.conda\envs\3710\lib\site-packages\torch\nn\modules\module.py:1102, in Module._call_impl(self, *input, **kwargs) 1098 # If we don't have any hooks, we want to skip the rest of the logic in 1099 # this function, and just call forward. 1100 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks 1101 or _global_forward_hooks or _global_forward_pre_hooks): -> 1102 return forward_call(*input, **kwargs) 1103 # Do not call functions when jit is used 1104 full_backward_hooks, non_full_backward_hooks = [], [] File ~\.conda\envs\3710\lib\site-packages\torch\nn\modules\loss.py:1150, in CrossEntropyLoss.forward(self, input, target) 1149 def forward(self, input: Tensor, target: Tensor) -> Tensor: -> 1150 return F.cross_entropy(input, target, weight=self.weight, 1151 ignore_index=self.ignore_index, reduction=self.reduction, 1152 label_smoothing=self.label_smoothing) File ~\.conda\envs\3710\lib\site-packages\torch\nn\functional.py:2846, in cross_entropy(input, target, weight, size_average, ignore_index, reduce, reduction, label_smoothing) 2844 if size_average is not None or reduce is not None: 2845 reduction = _Reduction.legacy_get_string(size_average, reduce) -> 2846 return torch._C._nn.cross_entropy_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index, label_smoothing) IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
问题排查与修正
核心错误原因
- 维度不匹配:
CrossEntropyLoss要求输入input形状为(N, C)(N是批量大小,C是类别数),目标target如果是类别索引则形状为(N),如果是one-hot则为(N, C)。代码中outputs[0]是(10,)(一维),onehot_labels也是(10,)(一维),Loss会误判输入维度,导致越界错误。 - CrossEntropyLoss无需手动转one-hot:PyTorch该Loss直接接受类别索引作为target,手动转换反而会引发维度和格式错误。
- 模型定义错误:
- 类继承时
super的第一个参数应为当前类名Net,而非EigenfaceDenseNet。 - 输出层不能加
ReLU,CrossEntropyLoss需要未激活的logits,ReLU会截断负数破坏损失计算逻辑。
- 类继承时
- 训练循环缺失关键步骤:
- 每次迭代前未清零梯度,会导致梯度累积。
- 未执行参数更新,训练不会产生效果。
- 循环变量
i与生成one-hot的变量i重名,导致标签生成逻辑错误。 - 逐个样本训练效率极低,建议采用小批量训练。
修正后的代码
模型修正
class Net(nn.Module): def __init__(self, num_classes): super(Net, self).__init__() # 修正类名匹配问题 self.model = nn.Sequential( nn.Linear(50*37,200), nn.ReLU(), nn.Linear(200,200), nn.ReLU(), nn.Linear(200, num_classes), # 移除输出层ReLU ) def forward(self, x): x = x.view(-1, 50*37) return self.model(x)
训练循环修正(单样本版本)
n_epochs = 3 n_classes = 10 for epoch in range(n_epochs): running_loss = 0.0 net.train() # 切换到训练模式 for idx, (img, label) in enumerate(zip(X_train, y_train)): # 转为float tensor匹配模型输入类型,标签用long类型存储类别索引 inputs = torch.tensor(img, dtype=torch.float32) labels = torch.tensor(label, dtype=torch.long) optimizer.zero_grad() # 清零梯度 outputs = net(inputs) # outputs形状为(1, 10) # 直接传入类别索引,无需转one-hot,调整标签维度匹配批量大小 loss_v = criterion(outputs, labels.unsqueeze(0)) loss_v.backward() optimizer.step() # 更新模型参数 running_loss += loss_v.item() # 每100个样本打印一次损失(可根据样本数量调整) if (idx + 1) % 100 == 0: print(f'[{epoch + 1}, {idx + 1:5d}] loss: {running_loss / 100:.3f}') running_loss = 0.0 print("训练完成")
进阶优化:小批量训练(推荐)
from torch.utils.data import TensorDataset, DataLoader # 将训练数据转为tensor格式 X_train_tensor = torch.tensor(X_train, dtype=torch.float32) y_train_tensor = torch.tensor(y_train, dtype=torch.long) # 创建数据集和DataLoader实现小批量加载 train_dataset = TensorDataset(X_train_tensor, y_train_tensor) train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True) n_epochs = 3 for epoch in range(n_epochs): running_loss = 0.0 net.train() for idx, (inputs, labels) in enumerate(train_loader): optimizer.zero_grad() outputs = net(inputs) # outputs形状为(32, 10) loss_v = criterion(outputs, labels) # labels形状为(32,) loss_v.backward() optimizer.step() # 累积总损失(乘以批量大小) running_loss += loss_v.item() * inputs.size(0) # 每个epoch结束后打印平均损失 epoch_loss = running_loss / len(train_dataset) print(f'Epoch {epoch + 1}/{n_epochs}, Loss: {epoch_loss:.4f}') print("训练完成")
内容的提问来源于stack exchange,提问作者flying_loaf_3
相关产品推荐
相关产品推荐

