You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

问题排查与修正

核心错误原因

  1. 维度不匹配:CrossEntropyLoss要求输入input形状为(N, C)(N是批量大小,C是类别数),目标target如果是类别索引则形状为(N),如果是one-hot则为(N, C)。代码中outputs[0]是(10,)(一维),onehot_labels也是(10,)(一维),Loss会误判输入维度,导致越界错误。
  2. CrossEntropyLoss无需手动转one-hot:PyTorch该Loss直接接受类别索引作为target,手动转换反而会引发维度和格式错误。
  3. 模型定义错误:
    • 类继承时super的第一个参数应为当前类名Net,而非EigenfaceDenseNet。
    • 输出层不能加ReLU,CrossEntropyLoss需要未激活的logits,ReLU会截断负数破坏损失计算逻辑。
  4. 训练循环缺失关键步骤:
    • 每次迭代前未清零梯度,会导致梯度累积。
    • 未执行参数更新,训练不会产生效果。
    • 循环变量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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:51:33