神經網絡的Backward Pass實現是比較容易出錯的,一個不錯的做法是實現一個神經網絡之后,利用Numeric的方法計算一個粗略的梯度,和你實際上算出來的梯度進行比對,如果差的不多,就說明你實現對了。
前兩天做CS231n作業的時候遇到了一個特別奇怪的事,我實現了一個N層卷積層,之后跟M層FC-Relu層的簡單神經網絡,指定N=1,M=2之后,死活過不了Gradient Check,并且永遠是FC層的第二層的bias出錯,其他參數都完全正確:
num_inputs = 2input_dim = (3, 16, 16)reg = 0.0num_classes = 10X = np.random.randn(num_inputs, *input_dim)y = np.random.randint(num_classes, size=num_inputs)model = ConvNet(num_filters=[3], filter_size=[3], input_dim=input_dim, hidden_dim=[7, 7], dtype=np.float64)loss, grads = model.loss(X, y)for param_name in sorted(grads): f = lambda _: model.loss(X, y)[0] param_grad_num = eval_numerical_gradient(f, model.params[param_name], verbose=False, h=1e-6) #h就是delta,計算公式是d = (f(x+h)-f(x-h))/2h e = rel_error(param_grad_num, grads[param_name]) 查了好幾天,百思不得其解,我以為是不是又出現了誰的cache被悄悄了修改的情況,但是打斷點進去以后發現一切輸入都非常正常。經過很長時間的debug無果以后,我開始懷疑是不是標答有問題。這個時候我突然注意到,在輸入FC1,進行完XW+b之后,輸出給Relu1的X是這樣的: [[ 7.29069965e-08 -2.22044158e-07 -1.74749819e-08 -2.84726995e-08] [ 1.43855178e-07 -3.39216429e-07 1.75782951e-08 -2.42950271e-08]]我們可以看到,這個X都是1e-8級別的,非常小,但是Gradient Check的時候,delta的值卻是1e-6,其量級已經與X相當,所以當然得不出正確的結果。
究其原因,這個ConvNet在初始化的時候采用的是從一個均值為0,標準差為weight_scale的正態分布中采樣參數的。當時的weight_scale設的是1e-3,因此X每過一層,大約都會減少1000倍。隨著層數的深入,值也就會越來越小。當到了第3層以后,與一個1e-6的值相加,自然就會出錯。
我嘗試調小了delta的值到1e-8,但是結果變成所有的梯度都不對了。我猜是因為這么小的delta不太能準確計算出梯度了。
既然減小delta不行,那么就將weight_scale調大好了,調大到1之后就順利Pass了Gradient Check。
新聞熱點
疑難解答