我有一个简单的 nn
模型,结构如下:
class TestRNN(nn.Module): def __init__(self, batch_size, n_steps, n_inputs, n_neurons, n_outputs): super(TestRNN, self).__init__() ... self.basic_rnn = nn.RNN(self.n_inputs, self.n_neurons) self.FC = nn.Linear(self.n_neurons, self.n_outputs) def forward(self, X): ... lstm_out, self.hidden = self.basic_rnn(X, self.hidden) out = self.FC(self.hidden) return out.view(-1, self.n_outputs)
我使用 criterion = nn.CrossEntropyLoss()
来计算误差。操作顺序大致如下:
# 获取输入x, y = data# 前向传播 + 反向传播 + 优化outputs = model(x)loss = criterion(outputs, y)
我的训练数据 x
已经过归一化,数据看起来像这样:
tensor([[[7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [2.6164e-02, 2.6164e-02, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 1.3108e-05], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [9.5062e-01, 3.1036e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [0.0000e+00, 1.3717e-05, 3.2659e-07, ..., 0.0000e+00, 0.0000e+00, 3.2659e-07]], [[5.1934e-01, 5.4041e-01, 6.8083e-06, ..., 0.0000e+00, 0.0000e+00, 6.8083e-06], [5.2340e-01, 6.0007e-01, 2.7062e-06, ..., 0.0000e+00, 0.0000e+00, 2.7062e-06], [8.1923e-01, 5.7346e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00]], [[7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0714e-01, 7.0708e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 7.0407e-06], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00]], ..., [[7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.1852e-01, 2.3411e-02, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0775e-01, 7.0646e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 3.9888e-06], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00]], [[5.9611e-01, 5.8796e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0710e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.7538e-01, 2.4842e-01, 1.7787e-06, ..., 0.0000e+00, 0.0000e+00, 1.7787e-06], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00]], [[5.2433e-01, 5.2433e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [1.3155e-01, 1.3155e-01, 0.0000e+00, ..., 8.6691e-02, 9.7871e-01, 0.0000e+00], [7.4412e-01, 6.6311e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 0.0000e+00], [7.0711e-01, 7.0711e-01, 0.0000e+00, ..., 0.0000e+00, 0.0000e+00, 9.6093e-07]]])
而典型的 output
和传递给损失函数的 y
看起来像这样:
tensor([[-0.0513], [-0.0445], [-0.0514], [-0.0579], [-0.0539], [-0.0323], [-0.0521], [-0.0294], [-0.0372], [-0.0518], [-0.0516], [-0.0501], [-0.0312], [-0.0496], [-0.0436], [-0.0514], [-0.0518], [-0.0465], [-0.0530], [-0.0471], [-0.0344], [-0.0502], [-0.0536], [-0.0594], [-0.0356], [-0.0371], [-0.0513], [-0.0528], [-0.0621], [-0.0404], [-0.0403], [-0.0562], [-0.0510], [-0.0580], [-0.0516], [-0.0556], [-0.0063], [-0.0459], [-0.0494], [-0.0460], [-0.0631], [-0.0525], [-0.0454], [-0.0509], [-0.0522], [-0.0426], [-0.0527], [-0.0423], [-0.0572], [-0.0308], [-0.0452], [-0.0555], [-0.0479], [-0.0513], [-0.0514], [-0.0498], [-0.0514], [-0.0471], [-0.0505], [-0.0467], [-0.0485], [-0.0520], [-0.0517], [-0.0442]], device='cuda:0', grad_fn=<ViewBackward>)tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], device='cuda:0')
当应用损失函数时,我在运行 CUDA_LAUNCH_BLOCKING=1 时得到了以下错误:
/opt/conda/conda-bld/pytorch_1549628766161/work/aten/src/THCUNN/ClassNLLCriterion.cu:105: void cunn_ClassNLLCriterion_updateOutput_kernel(Dtype *, Dtype *, Dtype *, long *, Dtype *, int, int, int, int, long) [with Dtype = float, Acctype = float]: block: [0,0,0], thread: [7,0,0] Assertion `t >= 0 && t < n_classes` failed./opt/conda/conda-bld/pytorch_1549628766161/work/aten/src/THCUNN/ClassNLLCriterion.cu:105: void cunn_ClassNLLCriterion_updateOutput_kernel(Dtype *, Dtype *, Dtype *, long *, Dtype *, int, int, int, int, long) [with Dtype = float, Acctype = float]: block: [0,0,0], thread: [20,0,0] Assertion `t >= 0 && t < n_classes` failed.THCudaCheck FAIL file=/opt/conda/conda-bld/pytorch_1549628766161/work/aten/src/THCUNN/generic/ClassNLLCriterion.cu line=111 error=59 : device-side assert triggered
模型输出负值导致了上述错误,我该如何解决这个问题?
回答:
TL;DR
你有两个选择:
- 将
outputs
的第二维度大小改为 2 而不是 1。 - 使用
nn.BCEWithLogitsLoss
代替nn.CrossEntropyLoss
。
我认为问题不在于负数,而在于 outputs
的形状。
观察你的数组 y
,我发现你有两个不同的类别(可能更多,但我们假设是两个)。这意味着 outputs
的最后一个维度应该为 2。原因是 outputs
需要为两个不同的类别各提供一个“分数”(参见 文档)。这个分数可以是负数、零或正数。但你的 outputs
的形状是 [64,1]
,而不是所需的 [64,2]
。
nn.CrossEntropyLoss()
对象的一个步骤将是将这些分数转换为两个类别的概率分布。这是通过 softmax 操作完成的。然而,在进行二元分类(即只有两个类别的分类,如我们当前的情况)时,还有一个选择:只为一个类别提供分数,使用 sigmoid 函数将其转换为该类别的概率,然后对其执行“1-p”操作以获得另一个类别的概率。这个选项意味着 outputs
只需要为两个类别中的一个提供分数,就像你当前的情况一样。要选择这个选项,你需要将 nn.CrossEntropyLoss
更改为 nn.BCEWithLogitsLoss
。然后你可以像目前一样传递 outputs
和 y
(请注意,outputs
的形状需要与 y
的形状完全一致,因此在你的例子中,你需要传递 outputs[:,0]
而不是 outputs
。此外,你需要将 y
转换为浮点数:y.float()
。因此调用应为 criterion(outputs[:,0], y.float())
)