PyTorch: clamp函数与梯度的关系
本文主要以下探究这一点:梯度反向传播过程中,测试强行修改后的预测结果是否还会传递loss?
clamp应用场景:在深度学习计算损失函数的过程中,会有这样一个问题,如果Label是1.0,而预测结果是0.0,则BCE损失中理论上计算出的结果是无穷大的(当然pytorch并不会报错,而是将损失上限设在了100.0),在实际开发过程中,我们一般会使用torch.clamp等函数将预测tensor不符合取值范围的值全部置为最小或最大值。
那对于这些被替换后的元素,他们会在反向传播过程中对网络产生梯度吗?第一想法是没有,因为这些替换后的新值的来源是一个赋值操作,而非通过网络计算出来的,但为了更好的理解,我们来做一个测试。
from torch.nn import functional as F
import torch.nn as nn
import torch
#初始化一个最简单的网络
fc = nn.Linear(in_features=1, out_features=1, bias=True)
fc.weight.data = torch.tensor([[0.01]])
fc.bias.data = torch.tensor([[0.01]])
input_t = torch.tensor([[1.0]], dtype=torch.float32)
pred = fc(input_t)
label = torch.tensor([[1.0]])
这里网络的预测结果为0.02,将其强行截断到0.1,损失的计算仍然是正常的,但是反向传播后,网络的训练参数是没有梯度的,即可以认为这个预测的样本对网络是没有更新作用的,是一个无意义的预测样本。
pred = torch.clamp(pred, min=0.1, max=0.9)
loss = F.binary_cross_entropy(pred, label)
print(loss) # tensor(2.3026, grad_fn=<BinaryCrossEntropyBackward>)
loss.backward()
print(fc.weight.grad, fc.bias.grad) #tensor([[0.]]) tensor([[0.]])
正常的预测结果产生梯度,符合预期。
loss = F.binary_cross_entropy(pred, label)
print(loss) #tensor(3.9120, grad_fn=<BinaryCrossEntropyBackward>)
loss.backward()
print(fc.weight.grad, fc.bias.grad) #tensor([[-50.]]) tensor([[-50.]])
更一般的,假设有n个预测结果,其中有k个值由于torch.clamp等操作被强行修改了值,还剩n - k个预测结果是真实来源于网络,则在梯度反向传播的时候只有这n - k个样本能够成功回传梯度给网络,即便计算的loss是这k个样本产生的。