一、卷积神经网络的整体架构
1、卷积神经网络与全连接神经网络的唯一区别在于神经网络中相邻两层的连接方式,全连接中每两层之间的所有结点都是有边相连的,而卷积中只有部分节点相连。
2、全连接神经网络在处理复杂问题时参数过多,极易导致过拟合的问题。
3、卷积神经网络的架构图
一个卷积神经网络由5种机构组成:
(1)输入层。三维矩阵代表一张图片,长和宽表示图像像素的大小,深度表示图像的色彩通道。其中,黑白照片的深度为1,RGM模式下,深度为3。
(2)卷积层。卷积层中每一个节点的输入为上一层神经网络的一小块,通常为3乘3,或5乘5。通过卷积层处理过的节点矩阵会变更深,即将神经网络中的每一小块进行更加深入地分析从而得到抽象程度更高的特征。
(3)池化层。用于缩小矩阵的大小,但不会改变矩阵的深度。即可认为时将一张分辨率高的照片转化为分辨率低的照片。
(4)全连接层。卷积和池化层可看成时自动图像特征提取的过程,之后用全连接层完成分类任务。
(5)softmax层,得到当前样例的概率分布。
二、卷积层和池化层的网络结构
(一)卷积层
1、卷积层中的内核:过滤器
作用:学习图像不同结构的特征。具体地,将一个子节点矩阵转化为下一层网络上一个单位节点矩阵。单位节点矩阵指的是长和宽均为1,深度不限的节点矩阵。
卷积层过滤器结构示意图
(右侧矩阵的深度为5,每一层矩阵代表一幅图像,即对原始图像中不同特征的响应。对5层矩阵提取的特征进行组合,得到更为强大的特征。)
过滤器的尺寸指的是一个过滤器输入节点矩阵的大小,深度指的是输出单位节点矩阵的深度!!上图左侧小矩阵的尺寸为过滤器的尺寸,右侧单位矩阵的深度为过滤器的深度。(深度表示一共有几个卷积核,对于每一个卷积核来说,尺寸和深度同左侧小矩阵的一致。)
2、卷积层中的前向传播过程
前向传播过程第一步是通过左侧小矩阵中的节点计算出右侧单位矩阵中的节点。假设输入节点为2 * 2 * 3的节点矩阵,输出节点为1* 1* 5的单位节点矩阵。则单位矩阵中的第i个节点的取值g(i)为:
公式中, a为过滤器中节点(x,y,z)的取值,w表示输出单位节点矩阵中的 第i个节点&&&过滤器输入节点(x,y,z)的权重 ,b表示第i个输出节点的偏置项参数(输出层中的每一层仅对应一个偏置项参数)。
前向传播过程第二步是将一个过滤器从神经网络当前层的左上角移动到右下角,并且在移动过程中计算每一个对应的单位矩阵。
特别注意:每一个卷积层中使用的过滤器中的参数是一样的,即共享过滤器的参数可以使得图像上的内容不受位置的影响,通过卷积操作后的图像结构仍保留原来的位置关系。(感觉过滤器就像是一个魔方,输入层中的子节点矩阵是另一个魔方,两个魔方相互渗透计算点乘数值,得到输出层上的一个节点数值。同一层上的魔方参数相同,不同层上的魔方参数不同。一共有“深度”个魔方)
tensorflow实现:
conv=tf.nn.conv2d(input,filter_weight,strides=[1,1,1,1],padding='SAME') #卷积层前向传播算法的实现
bias=tf.nn.bias_add(conv,biases) #一个深度上只含有一个偏置项,为同一层不同位置上的系欸但加上同一个偏置项
actived_conv=tf.nn.relu(bias)
(二)池化层
1、池化层中的过滤器采用最大值运算或平均值运算;
2、不同于卷积层使用的过滤器是横跨整个深度的,池化层使用的过滤器只影响一个深度上的节点,因而还需要在深度这个维度上移动。
(3 * 3 * 1 节点矩阵未经过全0填充且步长为2的最大池化层前向传播过程示意图)
tensorflow实现:
pool=tf.nn.max_pool(actived_conv,ksize=[1,2,2,1],strides=[1,2,2,1],padding='VALID')
三、经典卷积网络模型
(一)LeNet-5模型
**1、卷积层
输入层:32 * 3 1;
过滤器: 尺寸5 * 5,深度8,不使用全0填充,步长为1;
下一层:尺寸 (32-5+1)/1=28, 28 * 28,深度8;
参数个数: 5 * 5 * 1 * 8+8;
连接数目: 28 * 28 * 8 * (5 * 5 +1)。(因为下一层中的每个节点均和25个当前层节点相连)
2、池化层
输入层:28 * 28 * 6;
过滤器:2 * 2,长宽步长均为2.
下一层: 14 * 14 * 6.
3、卷积层
输入层: 14 * 14 * 6.;
过滤器: 尺寸5 * 5,深度20 ,不使用全0填充,步长为1;
下一层:尺寸 (14-5+1)/1=10, 10 * 10,深度20;
参数个数: 5 * 5 * 1 20+20;
连接数目: 10 * 10 20 * (5 * 5 +1)。(因为下一层中的每个节点均和25个当前层节点相连)
4、池化层
输入层: 10 10 * 20;
过滤器:2 * 2,长宽步长均为2.
下一层: 5 * 5 * 20.
5、全连接层
输入层: 5 5 * 20;
过滤器:5 * 5,长宽步长均为2.
下一层:120 1 .
参数个数: 5 * 5 20 120+120 .
6、全连接层
。。。。。。。。。
。。。。。。。。。
Tensorflow的完整实现:
(只需修改上一节中的mnist_infernce.py即可,优化函数同深层神经网络。感受到了封装的幸福~)
# -* coding: utf-8 -*-
import tensorflow as tf
INPUT_NODE=784
OUTPUT_NODE=10
IMAGE_SIZE=28
NUM_CHANNELS=1
NUM_LABELS=10
#第一层卷积层的尺寸和深度
CONV1_DEEP=32
CONV1_SIZE=5
#第二层卷积层的尺寸和深度
CONV2_DEEP=64
CONV2_SIZE=5
#全连接层的节点数
FC_SIZE=512
#定义卷积神经网络的前向传播过程
def inference(input_tensor,train,regularizer):#参数train用于区分训练和测试过程
#声明第一层卷积层的变量并实现前向传播的过程
with tf.variable_scope('layer1-conv1'):
conv1_weights=tf.get_variable("weights",[CONV1_SIZE,CONV1_SIZE,NUM_CHANNELS,CONV1_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1))
conv1_biases=tf.get_variable("biases",[CONV1_DEEP],initializer=tf.constant_initializer(0.0))
conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.nn.bias_add(conv1, conv1_biases)
relu1 = tf.nn.relu(bias)
#实现第二层池化层的前向传播的过程
with tf.name_scope('layer2-pool1'):
pool1 = tf.nn.max_pool(relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
#声明第三层卷积层的变量并实现前向传播的过程
with tf.variable_scope('layer3-conv2'):
conv2_weights=tf.get_variable("weights",[CONV2_SIZE,CONV2_SIZE,CONV1_DEEP,CONV2_DEEP],initializer=tf.truncated_normal_initializer(stddev=0.1))
conv2_biases=tf.get_variable("biases",[CONV2_DEEP],initializer=tf.constant_initializer(0.0))
conv2 = tf.nn.conv2d(pool1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME')
bias = tf.nn.bias_add(conv2, conv2_biases)
relu2 = tf.nn.relu(bias)
# 实现第四层池化层的前向传播的过程
with tf.name_scope('layer4-pool2'):
pool2 = tf.nn.max_pool(relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')
#将第四层的输出转化为第五层全连接层的输出格式
pool_shape=pool2.get_shape().as_list() #pool_shape[0]为一个batch中数据的个数
print(pool_shape)
nodes=pool_shape[1]*pool_shape[2]*pool_shape[3]
reshaped=tf.reshape(pool2,[pool_shape[0],nodes])#将第四层的输出变成一个batch的向量
# 声明第五层全连接层的变量并实现前向传播的过程
with tf.variable_scope('layer5-fc1'):
fc1_weights=tf.get_variable("weights",[nodes,FC_SIZE],initializer=tf.truncated_normal_initializer(stddev=0.1))
if regularizer!=None:
tf.add_to_collection('losses',regularizer(fc1_weights))
fc1_biases=tf.get_variable("biases",[FC_SIZE],initializer=tf.constant_initializer(0.1))
fc1=tf.nn.relu(tf.matmul(reshaped,fc1_weights)+fc1_biases)
if train:fc1=tf.nn.dropout(fc1,0.5) #为避免过拟合问题,dropout在训练时随机将部分节点的输出改为0
# 声明第六层全连接层的变量并实现前向传播的过程
with tf.variable_scope('layer6-fc2'):
fc2_weights = tf.get_variable("weights", [FC_SIZE,NUM_LABELS],
initializer = tf.truncated_normal_initializer(stddev=0.1))
if regularizer != None:
tf.add_to_collection('losses', regularizer(fc2_weights))
fc2_biases = tf.get_variable("biases", [NUM_LABELS], initializer=tf.constant_initializer(0.1))
logit = tf.nn.relu(tf.matmul(fc1,fc2_weights) + fc2_biases)
return logit
注意:在mnist.train函数中,莫要忘记了对输入数据进行维度转换:
x = tf.placeholder(tf.float32,
[BATCH_SIZE, cnn_inference.IMAGE_SIZE,cnn_inference.IMAGE_SIZE,cnn_inference.NUM_CHANNELS],
name='x-input')
在sess.run里也要记得维度的转换哇:
reshaped_xs = np.reshape(xs,
(BATCH_SIZE, cnn_inference.IMAGE_SIZE, cnn_inference.IMAGE_SIZE, cnn_inference.NUM_CHANNELS))
四、迁移学习
所谓迁移学习就是将一个问题上训练好的模型通过简单的调整使其适用于一个新的问题。将新的图像通过训练好的卷积神经网络直到全连接层之前看成是对图像进行特征提取的过程…