美文网首页
神经网络模型优化 - 梯度

神经网络模型优化 - 梯度

作者: Vector_Wan | 来源:发表于2019-08-10 17:24 被阅读0次

    之前的模型中我们实现了一个二层神经网络,准确度还行,但是训练的时间极长,我们在调参数之前先看看能不能对它的训练速度进行一个提升。

    在这个模型中我们使用了梯度下降法,并使用数值方法近似求损失函数关于各个参数的偏导数,一个很直接的想法就是将数值方法换成 基于计算图理论的前后传播方法。这个方法可以有效提高求导数的效率,目前很多成熟的模型求导都是用的这种方法。

    因为我们的模型比较简单,所以我们就不怎么改动之前的代码的结构直接修改 gradient 函数。(其实在使用计算图的时候更适合将每一个运算拆开,分别组成模块,使用的时候只需要像积木一样组装就好了,这是由计算图的性质决定的)

    先上源码:

        def gradient(self, x, t):
            W1, W2 = self.params['W1'], self.params['W2']
            b1, b2 = self.params['b1'], self.params['b2']
            grads = {}
    
            batch_num = x.shape[0]
    
            # forward
            a1 = np.dot(x, W1) + b1
            z1 = sigmoid(a1)
            a2 = np.dot(z1, W2) + b2
            y = softmax(a2)
    
            # backward
            dy = (y - t) / batch_num
            grads['W2'] = np.dot(z1.T, dy)
            grads['b2'] = np.sum(dy, axis=0)
    
            da1 = np.dot(dy, W2.T)
            dz1 = sigmoid_grad(a1) * da1
            grads['W1'] = np.dot(x.T, dz1)
            grads['b1'] = np.sum(dz1, axis=0)
    
            return grads
    

    使用计算图的话就需要一个向前传播,然后保存好相关计算数据,接着向后传播计算梯度。

    向前传播很好办,就是之前的预测函数,因为我们在向后传播的时候要用到向前传播的数据,所以不要直接用 predict 函数,还是要在这里面写一遍,

    向后传播的时候需要知道一些运算的向后传播导数计算方法,具体的推导过程在这里就不写了,直接给出结果:可以参考https://blog.csdn.net/oBrightLamp/article/details/84333111

    对于 softmax with loss 层反向传播的是 softmax 层的输出和监督标签的差分,也就是 (y_1-t_1,y_2-t_2,y_3-t_3)

    对于 Affine 层(批处理版本)可以参考下图:


    其实 Affine 的意思是仿射变换,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。
    具体来说,一个集合 X 的放射变换为:

    f(x) =Y= Wx + B, ~~x\in X

    直接看反向传播,首先最右端传过来一个 Y 例如大小为 (N,3)N 表示批的大小。这里面的表示方法其实是 numpy 的表示法,但是它的意义和矩阵理论中的一样,但是需要注意的是,numpy 里面的矩阵每一行作为一条信息。例如 np.array([ [1,2,3], [1,2,3] ]) 的大小为 (2,3)

    继续向前传播,遇到了一个加法运算,对于加法运算,直接把上层结果 \frac{\partial L}{\partial Y} 传递过去,但是还有一个问题,在正向传播的时候,偏置被加到了每一个数据上面,所以各个数据反向传播的值需要汇总一下作为偏置的梯度。

    再看另一个方向,另一个方向是一个点乘,这个没什么问题就是乘法的运算图。交换后相乘向下传播。

    最后是 sigmoid 层的反向传播,还记得当时在刚接触深度学习的时候老师跟我们讲 sigmoid 说为什么使用这个函数,有一个很重要的原因就是它好求导,当时没什么感觉,直到了解了它的反向传播才理解了这句话,
    直接,上层下来一个 \frac{\partial L}{\partial y} 输出 \frac{\partial L}{\partial y}y(1-y)
    为了代码的简洁,我单独写了一个函数,其实在这个地方不单独写也没事

    def sigmoid_grad(x):
        return (1.0 - sigmoid(x)) * sigmoid(x)
    

    调用的时候传入 a1 然后乘上 da1 就可以。
    再看代码就会感觉代码的含义还是很明确的。这样这个求梯度的函数就写完啦,

    从时间上来看明显是误差反向传播的方法更快,但是实现起来比较复杂容易出错,我们可以比较一下数值微分和误差反向传播计算的微分,看看是否一致,因为计算机计算精度的原因,一般来说是不一致的,但是不会相差太多,

    这种操作被称为梯度确认

    我们来测试一下我们写的代码

    # coding: utf-8
    import numpy as np
    from mnist import load_mnist
    from two_layer_net import TwoLayerNet
    
    # 读入数据
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    
    network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
    
    x_batch = x_train[:3]
    t_batch = t_train[:3]
    
    grad_numerical = network.numerical_gradient(x_batch, t_batch)
    grad_backprop = network.gradient(x_batch, t_batch)
    
    for key in grad_numerical.keys():
        diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )
        print(key + ":" + str(diff))
        
    

    从结果上来看确实差的不多:基本上都是在 10^{-7} 以下的数量级。

    相关文章

      网友评论

          本文标题:神经网络模型优化 - 梯度

          本文链接:https://www.haomeiwen.com/subject/tobljctx.html