手写数字识别神经网络的实现(2)

描述

在练习二中,手写数字识别使用数值微分的方式实现了神经网络,现在用误差反向传播法来实现。两者的区别仅仅是使用不同方法求梯度。

1、2层神经网络的类

将2层神经网络实现为一个TwoLayerNet的类(和上次的代码仅仅是求梯度的方式不同,不同的地方加*表示):

class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std *np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std *np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)
*       #生成层        
*       self.layers = OrderedDict()
*       self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
*       self.layers['Relu1'] = Relu()
*       self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
*       self.lastLayer = SoftmaxWithLoss()
*   def predict(self, x):
        for layer in self.layers.values():
           x = layer.forward(x)
        return x
    # x:输入数据, t:监督数据
    def loss(self, x, t):
        y = self.predict(x)
*       return self.lastLayer.forward(y, t)
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
*       if t.dim != 1 : t=np.argmax(t,axis=1)
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
    # x:输入数据, t:监督数据
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        return grads
*    def gradient(self, x, t):
        # forward
        self.loss(x, t)
        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)      
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        # 设定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
        return grads

只介绍和数值微分求导法不同的部分:

初始化: layer是保存神经网络的层的有序字典型变量 。OrderedDict是有序字典, 有序是说它可以记住向字典中添加元素的顺序 ,正向传播只需要按照添加元素的顺序调用各层的forward方法就可以,反向传播顺序相反地顺序调用backward方法。通过layers['Affine1'],layers['Relu1'],layers['Affine2']的形式保存各个层。还定义了最后一层lastlayer,上一篇中介绍了Affine,Relu,SoftmaxWithLoss函数的封装,在这一层中直接定义。

定义predict函数,用于识别:predict()函数的参数是输入的图像数据,按照层的顺序(layers的for语句)依次调用每一层的forward()函数,先是x经过Affine1层的forward函数,返回的out值作为Relu层的forward函数的输入参数,返回的out作为Affine2层的forward函数的输入参数,返回的out作为最后一层SoftmaxWithLoss层的forward函数的输入参数,返回的out是最后的输出,也就是识别的结果。

定义损失函数:输入参数是x输入图像和t标签,经过predict函数得到识别数据y,返回值是输入为y和t的SoftmaxWithLoss()的前向函数。

定义识别精度:经过predict()之后的输出y是识别结果,如果与标签一致说明结果准确,识别精度就是准确的次数与输入图像数量之比,即准确的概率。批处理时假设一次处理N张,y的形状是(N,10),有两个方向,a[1]是按行方向索引,argmax是找到最大值所在的位置。按行找到到最大值的索引值,就对应每一张图片识别到最大可能性的结果对应的分类。将该分类与标签进行比较,求识别正确的概率。

定义了数值微分求导的过程,这个是数值微分的方法,如果用误差反向传播法需要将这段注释掉;或者后续要比较两个方法求导的结果。

定义了误差反向传播法求导的方法:先求dout=1通过lastlayer层的backward函数的输出,返回dx。由于反向传播经过的层和正向相反,正向的有序字典变量需要reverse反向,这样通过for语句调用每一层的backward函数经过的层顺序就是affine2,relu,affine1,此时的输出就是最后的求导结果。

将神经网络用层的方式实现,将每一层模块化,是非常方便的,即使层很多,也只需要接好每一层的接口。通过各个层内部的forward和backward函数就可以识别或者计算梯度。

2、数值微分的计算耗时,但是计算简单不易出错。误差反向传播法的实现相对复杂容易出错。所以比较两者的结果可以确认误差反向传播法是否正确。这个比较的操作成为梯度确认。梯度确认的代码:

(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))

通过numerical_gradient函数得到的数值微分求导结果保存在grad_numerical中,通过gradient函数得到的误差反向传播结求导结果保存在grad_backprop中。通过求对应位置的数值的绝对值的平均值,判断误差反向传播求导结果是否正确。

W1:3.705434371413955e-10

b1:2.37776902256894e-09

W2:5.412727643613116e-09

b2:1.396563151026542e-07

输出的误差很小,所以是正确的。

3、使用误差反向传播法的学习

和数值微分的方法一样,因为不同之处仅仅是求梯度。

因此在练习二中的代码,把数值微分求梯度的代码注释掉,用误差反向传播法求梯度的代码就可以了。

总结:通过买水果的例子介绍了误差反向传播法的原理,介绍了神经网络中的误差反向传播法,原理一样只是不用标量用矩阵。将relu,softmaxwithloss,affine,softmax层封装成模块,内部实现了forward和backward方法。只需要搭建神经网络的每一层,通过内部的forward和backward函数,将数据正向或反向传播就可以求导。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分