有人可以帮助我理解为什么权重没有更新吗?
unet = Unet() optimizer = torch.optim.Adam(unet.parameters(), lr=0.001) loss_fn = torch.nn.MSELoss() input = Variable(torch.randn(32, 1, 64, 64, 64 ), requires_grad=True) target = Variable(torch.randn(32, 1, 64, 64, 64), requires_grad=False) optimizer.zero_grad() y_pred = unet(input) y = target[: , : , 20:44, 20:44, 20:44] loss = loss_fn(y_pred, y) print(unet.conv1.weight.data[0][0]) # 打印Unet第一层的权重 loss.backward() optimizer.step() print(unet.conv1.weight.data[0][0]) # 权重没有变化
模型定义如下:
class Unet(nn.Module):def __init__(self): super(Unet, self).__init__() # 下行路径1 self.conv1 = nn.Conv3d(1, 2, kernel_size=3, stride=1) self.conv2 = nn.Conv3d(2, 2, kernel_size=3, stride=1) # 下行路径2 self.conv3 = nn.Conv3d(2, 4, kernel_size=3, stride=1) self.conv4 = nn.Conv3d(4, 4, kernel_size=3, stride=1) # 底部 self.convbottom1 = nn.Conv3d(4, 8, kernel_size=3, stride=1) self.convbottom2 = nn.Conv3d(8, 8, kernel_size=3, stride=1) # 上行路径1 self.upConv0 = nn.Conv3d(8, 4, kernel_size=3, stride=1) self.upConv1 = nn.Conv3d(4, 4, kernel_size=3, stride=1) self.upConv2 = nn.Conv3d(4, 2, kernel_size=3, stride=1) # 上行路径2 self.upConv3 = nn.Conv3d(2, 2, kernel_size=3, stride=1) self.upConv4 = nn.Conv3d(2, 1, kernel_size=1, stride=1) self.mp = nn.MaxPool3d(kernel_size=3, stride=2, padding=1) # 其他不相关的属性...
前向函数如下:
def forward(self, input): # 使用U-net理论更新滤波器。 # 示例方法... input = F.relu(self.conv1(input)) input = F.relu(self.conv2(input)) input = self.mp(input) input = F.relu(self.conv3(input)) input = F.relu(self.conv4(input)) input = self.mp(input) input = F.relu(self.convbottom1(input)) input = F.relu(self.convbottom2(input)) input = F.interpolate(input, scale_factor=2, mode='trilinear') input = F.relu(self.upConv0(input)) input = F.relu(self.upConv1(input)) input = F.interpolate(input, scale_factor=2, mode='trilinear') input = F.relu(self.upConv2(input)) input = F.relu(self.upConv3(input)) input = F.relu(self.upConv4(input)) return input
我已经按照我能找到的所有示例和文档的方法进行操作,但我无法理解为什么它不起作用?
我发现y_pred.grad
在反向传播调用后为None,这是不应该的。如果没有梯度,当然优化器无法在任何方向上改变权重,但为什么没有梯度呢?
回答:
我发现这个问题是“ReLU死亡问题”导致的。由于数据是豪斯菲尔德单位,而Pytorch的权重初始化的均匀分布意味着许多神经元会从ReLU的零区域开始,使它们瘫痪并依赖其他神经元产生能够将它们拉出零区域的梯度。这种情况在训练过程中不太可能发生,因为所有神经元都会被推入ReLU的零区域。
这个问题有几个解决方案。你可以使用Leaky_relu或其他没有零区域的激活函数。
你也可以使用批量归一化来标准化输入数据,并将权重初始化为仅正值。
第二种解决方案可能是最优的,因为两者都能解决问题,但Leaky_relu会延长训练时间,而批量归一化则会起到相反的作用,提高准确性。另一方面,Leaky_relu是一个简单的修复方案,而另一种解决方案需要额外的工作。
对于豪斯菲尔德数据,还可以将常数1000添加到输入中,以消除数据中的负单位。这仍然需要不同于Pytorch标准初始化的权重初始化。