在神經網路中,會有好多層的 Linear + GELU 神經網路反覆計算著結果,而在這個過程中會發生梯度消失的狀況。為什麼會有梯度消失的狀況?因為在每一次學習時,都會乘上前一個梯度的局部斜率,很多層的比例都小於 1 的話,就會越乘越小,越難偵測到訊號。
會小於一個原因很多,例如,越靠近飽和斜率越小,一開始權重的初始值就太小或層數太多都有可能造成這個問題。那要怎麼維持一樣的深度但又不影響讓數值越來越小呢?
為了避免這個狀況在層的輸入處會拉一個快捷,到下一階段的輸入中,混合著此一階段的輸出。
第一部我們先透過 MSELoss 定義一個 Loss Function,接著透過 loss.backward()
來計算每一層的損失梯度。最後接著 named_parameters()
來疊代模型權重(讓他學習)。
def print_gradients(model, x):
output = model(x)
target = torch.tensor([[0.]])
loss = nn.MSELoss()
loss = loss(output, target)
loss.backward()
for name, param in model.named_parameters():
if 'weight' in name:
# Print the mean absolute gradient of the weights
print(f"{name} has gradient mean of {param.grad.abs().mean().item()}")
以一個五層的神經網路為例,每一層有一個 Linear +GELU Activation Function。我們開一個捷徑,如果 use_shortcut 為 true,就讓 output 加上原本的 input x。
class ExampleDeepNeuralNetwork(nn.Module):
def __init__(self, layer_sizes, use_shortcut):
super().__init__()
self.use_shortcut = use_shortcut
self.layers = nn.ModuleList([
nn.Sequential(nn.Linear(layer_sizes[0], layer_sizes[1]), GELU()),
nn.Sequential(nn.Linear(layer_sizes[1], layer_sizes[2]), GELU()),
nn.Sequential(nn.Linear(layer_sizes[2], layer_sizes[3]), GELU()),
nn.Sequential(nn.Linear(layer_sizes[3], layer_sizes[4]), GELU()),
nn.Sequential(nn.Linear(layer_sizes[4], layer_sizes[5]), GELU())
])
def forward(self, x):
for layer in self.layers:
layer_output = layer(x)
if self.use_shortcut and x.shape == layer_output.shape:
x = x + layer_output
else:
x = layer_output
return x
接著比較一下有開與沒有開的梯度變化,可以發現加上 Shorted Connection 減少梯度的變緩。