神经网络的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]) PRint '%s max relative error: %e' % (param_name, rel_error(param_grad_num, grads[param_name]))W_conv0 max relative error: 2.488318e-02W_fc0 max relative error: 3.070288e-02W_fc1 max relative error: 1.011455e-02W_soft max relative error: 5.722041e-03b_conv0 max relative error: 3.798044e-03b_fc0 max relative error: 6.489261e-05b_fc1 max relative error: 1.000000e+00b_soft max relative error: 9.867731e-10查了好几天,百思不得其解,我以为是不是又出现了谁的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。
新闻热点
疑难解答