為什麼要進行初始化
首先假設有一個兩層全連線網路,第一層的第一個節點值為 \(H_{11}= \sum_{i=0}^n X_i*W_{1i}\),
這個時候,方差為 \(D(H_{11}) = \sum_{i=0}^n D(X_i) * D(W_{1i})\), 這個時候,輸入\(X_i\)一般會做歸一化,那麼其方差為1,而權重W如果不進行歸一化的話,H的方差就會變得很大,然後多層累計,下一次的輸入會越來越大,使得網路不好收斂,如果權重W進行了初始化,使得其方差保持在1/n附近,那麼方差H則會收斂在1附近,從而使得網路變得更好優化。 很多初始化都是使用的這個原理,控制每一層的輸出,使其保持在一定的範圍內。
一些常見初始化方法
Xavier
Xavier初始化也是類似的原理, 假設輸入X 以及做了歸一化,其方差為1 ,那麼Xavier所希望的就是上述公式D(H) 保持在1左右,那麼就可以得到公式
其中n1 和 n2 為網路層的輸入輸出節點數量,一般情況下,輸入輸出是不一樣的,為了均衡考慮,可以做一個平均操作,於是變得到 \(D(W) = \frac{2}{n_1+n_2}\)
這個時候,我們假設 W服從均勻分佈 \(U[-a, a]\), 那麼在這個條件下,
推出\(a = \frac{\sqrt{6}}{\sqrt{n_1+n_2+1}}\),從而得到:
這樣就可以得到Xavier初始化,在pytorch中使用Xavier初始化方式如下,值得注意的是,Xavier對於sigmoid和tanh比較好,對於其他的可能效果就不是那麼好了
nn.init.xavier_uniform_(m.weight.data)
Kaiming
Kaiming 初始化比較適合ReLU啟用函式,其原理也跟上述差不多,也是希望將權重的方差保持在一定的範圍內,使得正反向傳播的值得到有效的控制,在kaiming初始化中,主要將權重的方差設定為 \(D(w) = \frac{2}{ni}\),由於考慮到ReLU啟用函式,將方差調整為\(D(w)= \frac{2}{(1+a^2)*n_i}\), 這裡的a是ReLU的斜率。
在pytorch中使用Kaiming初始化
nn.init.kaiming_normal_(m.weight.data)
LSTM初始化
LSTM中,公式和引數值的設定如下所示
在LSTM中,由於很多門控的權重尺寸是一樣的,所以可以使用如下方法進行初始化
def _init_lstm(self, weight):
for w in weight.chunk(4, 0):
init.xavier_uniform(w)
self._init_lstm(self.lstm.weight_ih_l0)
self._init_lstm(self.lstm.weight_hh_l0)
self.lstm.bias_ih_l0.data.zero_()
self.lstm.bias_hh_l0.data.zero_()
Embedding進行初始化
self.embedding = nn.Embedding(embedding_tokens, embedding_features, padding_idx=0)
init.xavier_uniform(self.embedding.weight)
其他通用初始化方法
遍歷初始化
for name, param in net.named_parameters():
if 'weight' in name:
init.normal_(param, mean=0, std=0.01)
print(name, param.data)
for name, param in net.named_parameters():
if 'bias' in name:
init.constant_(param, val=0)
print(name, param.data)
## 通過instance 初始化
for m in self.children():
if isinstance(m, nn.Linear):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, -100)
# 也可以判斷是否為conv2d,使用相應的初始化方式
elif isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight.item(), 1)
nn.init.constant_(m.bias.item(), 0)
直接使用pytorch內建初始化
from torch.nn import init
init.normal_(net[0].weight, mean=0, std=0.01)
init.constant_(net[0].bias, val=0)
自帶初始化方法中,會自動消除梯度反向傳播,但是手動情況下必須自己設定
def no_grad_uniform(tensor, a, b):
with torch.no_grad():
return tensor.uniform_(a, b)
使用apply進行初始化
批量初始化方法,注意net裡面的apply函式,可以作用網路的所有module
def weights_init(m): # 1
classname = m.__class__.__name__ # 2
if classname.find('Conv') != -1: # 3
nn.init.kaiming_normal_(m.weight.data) # 4
elif classname.find('BatchNorm') != -1: # 5
nn.init.normal_(m.weight.data, 1.0, 0.02) # 6
nn.init.constant_(m.bias.data, 0) # 7
net.apply(weights_init)