【问题标题】:How can i process multi loss in pytorch?如何在 pytorch 中处理多重损失?
【发布时间】:2019-05-28 09:49:55
【问题描述】:

比如这个,我想用一些辅助损失来提升我的模型性能。
pytorch中可以实现哪个类型代码?

#one
loss1.backward()
loss2.backward()
loss3.backward()
optimizer.step()
#two
loss1.backward()
optimizer.step() 
loss2.backward()
optimizer.step() 
loss3.backward()
optimizer.step()   
#three
loss = loss1+loss2+loss3
loss.backward()
optimizer.step()

感谢您的回答!

【问题讨论】:

    标签: python pytorch


    【解决方案1】:

    第一次和第三次尝试完全相同且正确,而第二次尝试完全错误。

    原因是,在 Pytorch 中,低层梯度不会被后续的backward() 调用“覆盖”,而是累加或求和。这使得第一种方法和第三种方法相同,但如果您有低内存 GPU/RAM,第一种方法可能更可取,因为立即调用 backward() + step() 的批量大小为 1024 与具有大小为 128 和 8 个 backward() 的 8 个批次相同电话,最后有一个step() 电话。

    为了说明这个想法,这里是一个简单的例子。我们想让我们的张量x同时最接近[40,50,60]

    x = torch.tensor([1.0],requires_grad=True)
    loss1 = criterion(40,x)
    loss2 = criterion(50,x)
    loss3 = criterion(60,x)
    

    现在是第一种方法:(我们使用tensor.grad 来获取张量x 的当前梯度)

    loss1.backward()
    loss2.backward()
    loss3.backward()
    
    print(x.grad)
    

    此输出:tensor([-294.])(编辑:将retain_graph=True 放在前两个backward 调用更复杂的计算图)

    第三种方法:

    loss = loss1+loss2+loss3
    loss.backward()
    print(x.grad)
    

    再次输出:tensor([-294.])

    第二种方法不同,因为我们在调用step() 方法后不调用opt.zero_grad。这意味着在所有 3 个step 调用中使用第一个backward 调用的梯度。例如,如果 3 个损失为相同的权重提供梯度 5,1,4,而不是 10 (=5+1+4),那么现在您的权重将具有 5*3+1*2+4*1=21 作为梯度。

    进一步阅读:Link 1,Link 2

    【讨论】:

    • 我用了第三种方法,成功了。感谢您的耐心和细心的回复。
    • 我在尝试方法一的时候需要把retain_graph=True放在第一个loss中,否则会报错。你知道为什么吗?
    • 是的,bcz python 在backward() 调用后丢弃了中间计算图,因此以后的调用没有任何要反向传播的图。我的简单示例没有任何中间计算,所以这里不是问题。立即进行编辑
    • @ShihabShahriarKhan 实现是否保证反向传播仅适用于相应的组件?例如,“loss1”应该只影响 conv 11x11 层。或者这是因为与每个组件关联的 gradient_fn 而明确知道?
    • 另外,在这种情况下 loss2 呢。它是反向传播到 conv 11x11 还是只影响 conv 5x5?
    【解决方案2】:

    -- 对第一种方法的评论已删除,请参阅其他答案--

    您的第二种方法需要使用retain_graph=True 进行反向传播,这会产生大量计算成本。此外,这是错误的,因为您将使用第一个优化器步骤更新网络权重,然后您的下一个 backward() 调用将在更新之前计算梯度,这意味着 second step() 调用会将噪声插入您的更新。另一方面,如果您执行另一个 forward() 调用以通过更新的权重进行反向传播,您最终将获得异步优化,因为第一层将使用第一个 step() 更新一次,然后再为每个后续step() 调用(本身没有错,但效率低下,可能不是你想要的)。

    长话短说,要走的路是最后的方法。将每个损失减少为一个标量,对损失求和并反向传播产生的损失。边注;确保您的减少方案有意义(例如,如果您使用 reduction='sum' 并且损失对应于多标签分类,请记住每个目标的类数是不同的,因此每个损失贡献的相对权重也会不同)

    【讨论】:

    • 我相信这可能有一些错误:link1,Link2。如果我自己犯了任何错误,请告诉我......
    • 好点,感谢您注意到错误信息,并为错误的信息感到抱歉——我确信出于某种原因是这种情况。
    • 感谢您的耐心和细心的回答。您的回答给了我一些启发,但我正在处理反对检测问题,所以我不知道它会对分类产生什么影响。再次感谢!
    【解决方案3】:

    第三次尝试是最好的。

    Two different loss functions

    如果你有两个不同的损失函数,分别完成它们的转发,最后你可以做(loss1 + loss2).backward()。它更高效一些,跳过了很多计算。

    额外提示:总结损失

    在你想要做的代码中:

    loss_sum += loss.item()
    

    以确保您不会跟踪所有损失的历史记录。

    item() 将破坏图形,从而使其从循环的一次迭代中释放到下一次迭代中。你也可以使用detach()

    【讨论】:

      【解决方案4】:

      第一次和第三次尝试是正确的,但不一样。

      如果使用第一次尝试,它将多次计算Conv 11x11的梯度流, 但只能使用第 3 次尝试。

      Conv 5x5, Conv 3x3 ...梯度计算相同。

      【讨论】:

      • 如果不一样,怎么可能是正确的?您能否提供支持您论点的链接或示例?谢谢
      • @ShihabShahriarKhan 他们创建了三个不同的 AD 计算图,因此更加复杂。我可以为您提供来自主要 PyTorch 开发人员的 feedback。检查效率一词。
      【解决方案5】:

      我来到这里时一直在寻找的答案如下:

      y = torch.tensor([loss1, loss2, loss3])
      y.backward(gradient=torch.tensor([1.0,1.0,1.0]))
      

      请参阅https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#gradients 进行确认。

      存在类似的问题,但这个问题使用了不同的措辞,是我在遇到问题时首先发现的问题。类似的问题可以在Pytorch. Can autograd be used when the final tensor has more than a single value in it?

      【讨论】:

        猜你喜欢
        • 2018-08-30
        • 2019-08-17
        • 2022-01-18
        • 1970-01-01
        • 2020-08-10
        • 2019-06-01
        • 1970-01-01
        • 2021-02-20
        • 1970-01-01
        相关资源
        最近更新 更多