带有值向量的回归模型的pytorch损失函数

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

我正在使用 PyTorch 训练 CNN 架构来解决回归问题,其中我的输出是 25 个值的张量。输入/目标张量可以是全零或 sigma 值为 2 的高斯分布。4 个样本批次的示例如下:

[[0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,  0., 0.],
 [0., 0., 0.,  0., 0., 0., 0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 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.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534 ],
  [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.]]

我的问题是如何为模型设计损失函数以有效学习具有 25 个值的回归输出。

我尝试了两种类型的损失,

torch.nn.MSELoss()
torch.nn.MSELoss()-torch.nn.CosineSimilarity()
。他们有点工作。然而,有时网络会很难收敛,尤其是当有很多样本全部为“0”时,这会导致网络输出一个包含所有 25 个小值的向量。

我的问题是,还有其他我们可以尝试的损失吗?

machine-learning pytorch conv-neural-network regression loss
1个回答
1
投票

您的价值观在规模上似乎没有很大差异,因此 MSELoss 似乎可以正常工作。由于目标中有许多零,您的模型可能会崩溃。

你可以随时尝试

torch.nn.L1Loss()
(但我不认为它会比
torch.nn.MSELoss()
好很多)

我建议您尝试预测高斯平均值/mu,然后如果您确实需要的话,尝试为每个样本重新创建高斯。

因此,如果您选择尝试此方法,您有两种选择。

备选方案1

一个好的替代方案是将您的目标编码为看起来像分类目标。您的 25 个元素向量变成单个值,其中原始目标 == 1(可能的类为 0、1、2、...、24)。然后,我们可以将包含“仅零”的样本分配为最后一个类别“25”。所以你的目标:

[[0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,  0., 0.],
 [0., 0., 0.,  0., 0., 0., 0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 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.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534 ],
  [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.]]

成为

[4,
10,
20,
25]

如果你这样做,那么你可以尝试常见的

torch.nn.CrossEntropyLoss()

我不知道你的数据加载器是什么样的,但给定原始格式的单个样本,你可以使用以下方法将其转换为我建议的格式:

def encode(tensor):
    if tensor.sum() == 0:
        return len(tensor)
    return torch.argmax(tensor)

然后回到高斯:

def decode(value):
    n_values = 25
    zero = torch.zeros(n_values)
    if value == n_values:
        return zero
    # Create gaussian around value
    std = 2
    n = torch.arange(n_values) - value
    sig = 2*std**2
    gauss = torch.exp(-n**2 / sig2)
    # Only return 9 values from the gaussian
    start_ix = max(value-6, 0)
    end_ix = min(value+7,n_values)
    zero[start_ix:end_ix] = gauss[start_ix:end_ix]
    return zero

(注意我没有尝试过批次,只是样品)

替代2

第二个选项是将回归目标(仍然只有 argmax 位置(mu))更改为 0-1 范围内更好的回归值,并有一个单独的神经元输出“掩码值”(也是 0-1)。那么您的批次:

[[0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,  0., 0.],
 [0., 0., 0.,  0., 0., 0., 0.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534, 0.043937, 0.011109, 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.13534, 0.32465, 0.60653, 0.8825, 1.0000, 0.88250,0.60653, 0.32465, 0.13534 ],
  [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.]]

成为

# [Mask, mu]
[
[1, 0.1666], # True, 4/24
[1, 0.4166], # True, 10/24
[1, 0.8333], # True, 20/24
[0, 0]       # False, undefined
]

如果您使用此设置,那么您应该能够使用经过修改的 MSELoss:

def custom_loss(input, target):
    # Assume target and input is of shape [Batch, 2]
    mask = target[...,1]
    mask_loss = torch.nn.functional.mse_loss(input[...,0], target[...,0])
    mu_loss = torch.nn.functional.mse_loss(mask*input[...,1], mask*target[...,1])
    return (mask_loss + mu_loss) / 2

如果目标的掩码为 1,则此损失只会查看第二个值 (mu)。否则,它只会尝试优化正确的掩码。

要编码为这种格式,您可以使用:

def encode(tensor):
    n_values = 25
    if tensor.sum() == 0:
        return torch.tensor([0,0])
    return torch.argmax(tensor) / (n_values-1)

并解码:

def decode(tensor):
    n_values = 25

    # Parse values
    mask, value = tensor
    mask = torch.round(mask)
    value = torch.round((n_values-1)*value)

    zero = torch.zeros(n_values)
    if mask == 0:
        return zero
    # Create gaussian around value
    std = 2
    n = torch.arange(n_values) - value
    sig = 2*std**2
    gauss = torch.exp(-n**2 / sig2)
    # Only return 9 values from the gaussian
    start_ix = max(value-6, 0)
    end_ix = min(value+7,n_values)
    zero[start_ix:end_ix] = gauss[start_ix:end_ix]
    return zero
© www.soinside.com 2019 - 2024. All rights reserved.