回归损失函数不正确

Posted

技术标签:

【中文标题】回归损失函数不正确【英文标题】:Regression loss functions incorrect 【发布时间】:2018-01-11 09:59:19 【问题描述】:

我正在尝试一个基本的平均示例,但如果我增加训练时间,验证和损失不匹配并且网络无法收敛。我正在训练一个具有 2 个隐藏层的网络,每个 500 个单位宽,来自 [0,9] 范围内的三个整数,学习率为 1e-1,Adam,批量大小为 1,并且 dropout 进行 3000 次迭代并验证每个100 次迭代。如果标签和假设之间的绝对差小于一个阈值,这里我将阈值设置为 1,我认为这是正确的。有人可以让我知道这是选择损失函数的问题,Pytorch 的问题还是我正在做的事情。下面是一些图:

val_diff = 1
acc_diff = torch.FloatTensor([val_diff]).expand(self.batch_size)

在验证期间循环 100 次:

num_correct += torch.sum(torch.abs(val_h - val_y) < acc_diff)

在每个验证阶段后追加:

validate.append(num_correct / total_val)

以下是(假设和标签)的一些示例:

[...(-0.7043088674545288, 6.0), (-0.15691305696964264, 2.6666667461395264),
 (0.2827358841896057, 3.3333332538604736)]

我尝试了 API 中通常用于回归的六个损失函数:

                                                 torch.nn.L1Loss(size_average=False)

                                                                  torch.nn.L1Loss()

                                                  torch.nn.MSELoss(size_average=False)

                                                                            torch.nn.MSELoss()

                                           torch.nn.SmoothL1Loss(size_average=False)

                                                           torch.nn.SmoothL1Loss()

谢谢。

网络代码:

class Feedforward(nn.Module):
    def __init__(self, topology):
        super(Feedforward, self).__init__()
        self.input_dim     = topology['features']
        self.num_hidden    = topology['hidden_layers']
        self.hidden_dim    = topology['hidden_dim']
        self.output_dim    = topology['output_dim']
        self.input_layer   = nn.Linear(self.input_dim, self.hidden_dim)
        self.hidden_layer  = nn.Linear(self.hidden_dim, self.hidden_dim)
        self.output_layer  = nn.Linear(self.hidden_dim, self.output_dim)
        self.dropout_layer = nn.Dropout(p=0.2)


    def forward(self, x):
        batch_size = x.size()[0]
        feat_size  = x.size()[1]
        input_size = batch_size * feat_size

        self.input_layer = nn.Linear(input_size, self.hidden_dim).cuda()
        hidden = self.input_layer(x.view(1, input_size)).clamp(min=0)

        for _ in range(self.num_hidden):
            hidden = self.dropout_layer(F.relu(self.hidden_layer(hidden)))

        output_size = batch_size * self.output_dim
        self.output_layer = nn.Linear(self.hidden_dim, output_size).cuda()
        return self.output_layer(hidden).view(output_size)

培训代码:

def train(self):
    if self.cuda:
        self.network.cuda()

    dh        = DataHandler(self.data)
    # loss_fn = nn.L1Loss(size_average=False)
    # loss_fn = nn.L1Loss()
    # loss_fn = nn.SmoothL1Loss(size_average=False)
    # loss_fn = nn.SmoothL1Loss()
    # loss_fn = nn.MSELoss(size_average=False)
    loss_fn   = torch.nn.MSELoss()
    losses    = []
    validate  = []
    hypos     = []
    labels    = []
    val_size  = 100
    val_diff  = 1
    total_val = float(val_size * self.batch_size)

    for i in range(self.iterations):
        x, y = dh.get_batch(self.batch_size)
        x = self.tensor_to_Variable(x)
        y = self.tensor_to_Variable(y)

        self.optimizer.zero_grad()
        loss = loss_fn(self.network(x), y)
        loss.backward()
        self.optimizer.step()

【问题讨论】:

您是否在更新权重后将梯度归零?这是一个常见的错误。此外,您的学习率似乎很高。 @mexmex 我不应该在每次迭代中清除局部梯度吗?这不就是 optimizer.zero_grad 的目的吗? 是的,只是检查您是否确实这样做了!抱歉,如果我的语言不明确。 你能给我们看看神经网络代码吗? @Cedias 我刚刚用代码更新了我的帖子 【参考方案1】:

您似乎误解了 pytorch 中层的工作原理,这里有一些提示:

在转发时,当您执行 nn.Linear(...) 时,您正在定义 新层,而不是使用您在网络中预定义的那些 __init__。因此,它无法学习任何东西,因为权重会不断重新初始化。

您不需要在net.forward(...) 中调用.cuda(),因为您已经通过调用self.network.cuda() 将gpu 上的网络复制到train

理想情况下,net.forward(...) 输入应该直接具有第一层的形状,因此您不必修改它。在这里你应该有x.size() &lt;=&gt; Linear -- &gt; (Batch_size, Features)

你的前锋应该看起来接近这个:

def forward(self, x):
    x = F.relu(self.input_layer(x))
    x = F.dropout(F.relu(self.hidden_layer(x)),training=self.training)
    x = self.output_layer(x)
    return x

【讨论】:

所以我每次迭代都初始化新层以适应动态输入。 pytorch 会自动为您处理动态输入和动态批处理吗? @Soubriquet 动态输入和动态批处理是什么意思?不过,在每个输入处使用新的权重是行不通的。 是的,你如何动态批处理 批次大小可以在批次之间无缝变化,因为层不期望固定批次输入。 所以 pytorch 会自动处理这个问题?

以上是关于回归损失函数不正确的主要内容,如果未能解决你的问题,请参考以下文章

自定义 keras 损失函数二元交叉熵给出不正确的结果

损失函数选择

我的自定义损失函数是不是正确? (火炬)

统计学习笔记之逻辑回归

svm损失函数

损失函数总结