10.3_通过梯度检验来自动测试

10.3 通过梯度检验来自动测试

我们在前面写了一个反向传播的测试方法。其中,导数的预期值是手动计算出来的。实际上有一种代替手动计算的自动测试方法,该方法叫作梯度检验(gradient checking)。梯度检验是将数值微分得到的结果与反向传播得到的结果进行比较。如果二者相差很大,则说明反向传播的实现有问题。

步骤4实现了数值微分。数值微分容易实现,并能得出大体正确的导数值。因此,可以通过与数值微分的结果进行比较来测试反向传播的实现是否正确。

梯度检验是一种高效的测试方式,我们只要准备输入值即可。下面添加基于梯度检验的测试方法。这里我们会使用在步骤4中实现的numerical_diff函数,这个函数的代码也会一并列出,兼作复习。

steps/step10.py

def numerical_diff(f, x, eps=1e-4):  
    x0 = Variable(x.data - eps)  
    x1 = Variable(x.data + eps)  
    y0 = f(x0)  
    y1 = f(x1)  
    return (y1.data - y0.data) / (2 * eps)  
class SquareTest(unittest.TestCase):  
    ...  
    def test_gradient_check(self):  
        x = Variable(np.random.randint(1))  # 生成随机的输入值  
        y = square(x)  
        y_backward()  
        num_grad = numerical_diff(square, x)  
        flg = np.allclose(x.grad, num_grad)  
        self.assertTrue(flg)

前面的代码会在进行梯度检验的 test_grad_check 方法中生成一个随机的输入值,然后,通过反向传播求出导数,再利用 numerical_diff 函数通过数值微分求导,最后检查这两种方法得到的数值是否基本一致。这里使用的是 NumPy 函数 np.allclose。

np.allclose(a, b) 用于判断 ndarray 实例 a 和 b 的值是否接近。多近才算近是由 np.allclose 函数的参数 rtol 和 atol 指定的,指定方式如 np.allclose(a, b, rtol=1e-05, atol=1e-08) 所示。这时,如果 a 和 b 的所有元素满足以下条件,则返回 True。

ab(atol+rtolb)| a - b | \leq (a t o l + r t o l * | b |) ^ {①}

另外,atol和rtol的值有时需要根据要进行梯度检验的计算对象(函数)加以微调。调整基准可以参照参考文献[5]等资料。添加了上面的梯度检验之后,再次进行测试。这次得到的结果如下。

...   
Ran 3 tests in 0.001s   
OK

在可以自动微分的深度学习框架中,我们可以像上面那样利用梯度检验建立一个半自动的测试机制。这样可以系统地构建更广泛的测试用例。