自己写一个卷积神经网络
姓名 郭宇
学号 16130130299
转载自
【深度学习系列 自己手写一个卷积神经网络】
http://m.toutiaocdn.net/group/6491421372814721549/?iid=18102364373&app=news_article&tt_from=android_share&utm_medium=toutiao_android&utm_campaign=client_share
【嵌牛导读】如何自己写一个卷积神经网络以及掌握一些技巧
【嵌牛鼻子】人工智能 机器学习
【嵌牛提问】怎样进一步理解卷积神经网络
【嵌牛正文】卷积神经网络的前向传播
首先我们来看一个最简单的卷积神经网络:
1.输入层---->卷积层
以上一节的例子为例,输入是一个4*4 的image,经过两个2*2的卷积核进行卷积运算后,变成两个3*3的feature_map
以卷积核filter1为例(stride = 1 ):
计算第一个卷积层神经元o11的输入:
神经元o11的输出:(此处使用Relu激活函数)
其他神经元计算方式相同
2.卷积层---->池化层
计算池化层m11 的输入(取窗口为 2 * 2),池化层没有激活函数
3.池化层---->全连接层
池化层的输出到flatten层把所有元素“拍平”,然后到全连接层。
4.全连接层---->输出层
全连接层到输出层就是正常的神经元与神经元之间的邻接相连,通过softmax函数计算后输出到output,得到不同类别的概率值,输出概率值最大的即为该图片的类别。
卷积神经网络的反向传播
传统的神经网络是全连接形式的,如果进行反向传播,只需要由下一层对前一层不断的求偏导,即求链式偏导就可以求出每一层的误差敏感项,然后求出权重和偏置项的梯度,即可更新权重。而卷积神经网络有两个特殊的层:卷积层和池化层。池化层输出时不需要经过激活函数,是一个滑动窗口的最大值,一个常数,那么它的偏导是1。池化层相当于对上层图片做了一个压缩,这个反向求误差敏感项时与传统的反向传播方式不同。从卷积后的feature_map反向传播到前一层时,由于前向传播时是通过卷积核做卷积运算得到的feature_map,所以反向传播与传统的也不一样,需要更新卷积核的参数。下面我们介绍一下池化层和卷积层是如何做反向传播的。
在介绍之前,首先回顾一下传统的反向传播方法:
卷积层的反向传播
由前向传播可得:
首先计算输入层的误差项δ11:
观察一下上面几个式子的规律,归纳一下,可以得到如下表达式:
此时我们的误差敏感矩阵就求完了,得到误差敏感矩阵后,即可求权重的梯度。
推论出权重的梯度:
误差项的梯度:
可以看出,偏置项的偏导等于这一层所有误差敏感项之和。得到了权重和偏置项的梯度后,就可以根据梯度下降法更新权重和梯度了。
池化层的反向传播
池化层的反向传播就比较好求了,看着下面的图,左边是上一层的输出,也就是卷积层的输出feature_map,右边是池化层的输入,还是先根据前向传播,把式子都写出来,方便计算:
这样就求出了池化层的误差敏感项矩阵。同理可以求出每个神经元的梯度并更新权重。
手写一个卷积神经网络
1.定义一个卷积层
首先我们通过ConvLayer来实现一个卷积层,定义卷积层的超参数
其中calculate_output_size用来计算通过卷积运算后输出的feature_map大小
2.构造一个激活函数
此处用的是RELU激活函数,因此我们在activators.py里定义,forward是前向计算,backforward是计算公式的导数:
其他常见的激活函数我们也可以放到activators里,如sigmoid函数,我们可以做如下定义:
如果我们需要自动以其他的激活函数,都可以在activator.py定义一个类即可。
3.定义一个类,保存卷积层的参数和梯度
4.卷积层的前向传播
1).获取卷积区域
2).进行卷积运算
3).增加zero_padding
4).进行前向传播
其中element_wise_op函数是将每个组的元素对应相乘
5.卷积层的反向传播
1).将误差传递到上一层
2).保存传递到上一层的sensitivity map的数组
3).计算代码梯度
4).按照梯度下降法更新参数
6.MaxPooling层的训练
1).定义MaxPooling类
2).前向传播计算
3).反向传播计算
完整代码请见:cnn.py(https://github.com/huxiaoman7/PaddlePaddle_code/blob/master/1.mnist/cnn.py)
最后,我们用之前的4 * 4的image数据检验一下通过一次卷积神经网络进行前向传播和反向传播后的输出结果:
运行一下:
运行结果:
总结
本文主要讲解了卷积神经网络中反向传播的一些技巧,包括卷积层和池化层的反向传播与传统的反向传播的区别,并实现了一个完整的CNN,后续大家可以自己修改一些代码,譬如当水平滑动长度与垂直滑动长度不同时需要怎么调整等等。