根据输出和目标之间的差异,使用不同的公式为自定义损失函数生成标量输出

问题描述 投票:0回答:1

目前正在尝试使用以下逻辑为线性回归实现自定义损失函数: *如果模型的输出值大于或等于目标,返回损失为(输出-目标)。 *如果模型的输出值小于目标值,返回损失为(目标 - 输出)^2

这是我目前的实现:

import torch.nn as nn
class E_Loss(nn.Module):
    def __init__(self, weight=None, size_average=True):
      super(E_Loss, self).__init__()
    def forward(self, inputs, targets, smooth=1):
      inputs = inputs.view(-1)
      targets = targets.view(-1)
      is_greater = torch.gt(inputs, outputs)
      print(is_greater)
      if is_greater: #torch.gt(inputs, targets):
        loss = (inputs - targets)
      else:
        loss = np.square(targets - outputs)
      return loss

使用我的模型进行训练时,我在 loss.backward() 步骤中遇到此错误: RuntimeError:只能为标量输出隐式创建 grad

假设它需要一个标量输出,我如何重写我的损失函数来产生这个? 重写我的代码而不使用数据加载器会更容易吗?

下面是整个模型部分

train_df, test_df = train_test_split(df, test_size=0.4)
train_dataset = FeatureDataset(train_df)
test_dataset = FeatureDataset(test_df)
train_dataloader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_dataset, batch_size=16, shuffle=False)
#setup dataloader

eloss = E_Loss()
criterion = eloss
model = linearRegression(16, 1)
learningRate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learningRate, weight_decay=0.05)

h_loss = []
epochs = 100

for epoch in range(epochs):
  running_loss = 0.0
  for i, (x, y) in enumerate(train_dataloader):
    optimizer.zero_grad()
    #clear gradients after each epoch so it isnt cumulative
    outputs = model(x)
    #get current output from model for comparison
    loss = criterion(outputs, y)
    loss.backward()
    optimizer.step()
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
<ipython-input-39-fb6074e17ffb> in <module>
---> 68     loss.backward()
     69     optimizer.step()
     70     running_loss += loss.item()

2 frames
/usr/local/lib/python3.8/dist-packages/torch/_tensor.py in backward(self, gradient, retain_graph, create_graph, inputs)
    486                 inputs=inputs,
    487             )
--> 488         torch.autograd.backward(
    489             self, gradient, retain_graph, create_graph, inputs=inputs
    490         )

/usr/local/lib/python3.8/dist-packages/torch/autograd/__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)
    188 
    189     grad_tensors_ = _tensor_or_tensors_to_tuple(grad_tensors, len(tensors))
--> 190     grad_tensors_ = _make_grads(tensors, grad_tensors_, is_grads_batched=False)
    191     if retain_graph is None:
    192         retain_graph = create_graph

/usr/local/lib/python3.8/dist-packages/torch/autograd/__init__.py in _make_grads(outputs, grads, is_grads_batched)
     83             if out.requires_grad:
     84                 if out.numel() != 1:
---> 85                     raise RuntimeError("grad can be implicitly created only for scalar outputs")
     86                 new_grads.append(torch.ones_like(out, memory_format=torch.preserve_format))
     87             else:

RuntimeError: grad can be implicitly created only for scalar outputs
python machine-learning pytorch linear-regression loss-function
1个回答
0
投票

您发布的代码没有任何意义,因为

  • is_greater = torch.gt(inputs, outputs)
    行使用了一个未定义的变量
    outputs
  • torch.gt
    是一个元素方面的大于操作,除非
    inputs
    outputs
    都是标量,否则在条件语句中使用它的结果是没有意义的。由于您的批量大小是 64,那么您应该得到一个例外
    RuntimeError: Boolean value of tensor with more than one value is ambiguous
    .
  • 您直接在火炬张量上使用 numpy 函数
    np.square
    。这是模棱两可的,可能会或可能不会工作取决于它是如何在引擎盖下实现的。将 PyTorch 函数与 PyTorch 张量结合使用。张量支持大多数 python 运算符,因此只需使用
    x**2
    x*x
    来进行元素平方。

您提出的错误表明,尽管存在所有明显的错误,您仍能够运行损失函数代码,但反向传播失败,因为损失函数不是标量值。您发布的代码不适合我,但似乎也没有进行任何形式的均值缩减。假设上述问题得到解决并且它确实运行了,那么我希望您会遇到这样的错误。发生此错误是因为

Tensor.backward
要求张量是标量值,即损失应该是单个数字。大多数情况下,这是通过平均整个批次的损失来实现的。

为了解决损失函数的实现,如果您将描述的函数视为

output - target
的函数,则更容易实现。因为
output >= target
等价于
output - target >= 0
那么在让
x = output - target
之后我们只想要一个函数等于
x
x
是非负的并且
x**2
否则。

这可以通过不同的方式实现,但一种简单的方法是认识到

Tensor.relu
可用于将函数分解为正项和负项
x = relu(x) + (-relu(-x))
。使用这个恒等式,很明显你想要的是
loss = relu(x) + relu(-x)**2
。解决均值降低问题以及损失函数的工作版本可能是:

class E_loss(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self, outputs, targets):
        x = outputs.flatten() - targets.flatten()
        return (x.relu() + ((-x).relu())**2).mean()
© www.soinside.com 2019 - 2024. All rights reserved.