使用带有一个隐藏层的MLP训练简单的异或神经网络,发现当设置不同的随机种子时,模型有时候收敛,有时候并不收敛。
不收敛的代码例子:
import torch
import torch.nn as nn
torch.manual_seed(24)
class XOR(nn.Module):
def __init__(self):
super(XOR, self).__init__()
self.fc1 = nn.Linear(2, 2)
self.fc2 = nn.Linear(2, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.sigmoid(self.fc2(x))
return x
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.kaiming_normal_(m.weight)
m.bias.data.fill_(0.01)
model = XOR()
model.apply(init_weights)
EPOCH = 10000
criterion = nn.BCELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
inputs = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=torch.float32)
labels = torch.tensor([[0], [1], [1], [0]], dtype=torch.float32)
for epoch in range(EPOCH):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
if epoch % 1000 == 0:
print(f'Epoch [{epoch + 1}/{EPOCH}], Loss: {loss.item()}')
此时运行代码会发现loss居高不下,获取反向传播的梯度情况,发现此时fc1层梯度消失。经过分析,发现这是因为ReLU(Rectified Linear Unit)激活函数引发梯度消失问题,被称为"死亡ReLU"(Dead ReLU)问题。
当ReLU的输入为负时,它会输出零;当输入为正时,输出为原值。因此,如果神经网络中的某些神经元的输出始终为零(即ReLU的输入始终小于零),那么这些神经元的梯度将永远为零,导致它们在反向传播中无法更新,从而“死亡”。