大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理解,而且能够帮助新手快速入门。
本文主要介绍了CNN(卷积神经网络),希望能对同学们有所帮助。
1. 卷积神经网络(CNN)结构
cnn网络结构一般为卷积层----池化层----卷积层----池化层----卷积层----池化层----…----全连接层----全连接层----…
2. 卷积层
卷积层的输入是是一个四维数组,如[batch, in_height, in_width, in_channels],如[128, 28, 28, 3],这里的3指channel(通道个数),28×28分别对应height和width,128指一批训练样本共有128个,完整物理含义可以理解为128个样本中,对于每个样本的输入是3个28×28的矩阵。卷积层的计算是输入和卷积核(filter)对应元素相乘再相加。卷积核也是一个四维数组,[filter_height, filter_width, in_channels, out_channels],heigt与width类似于input,filter的height和width决定了将input划分的形状;in_channels要和input的in_channels一样,out_channel即输出结果的channel数,也即filter的个数,就像全连接中x与w相乘后维度由n变成m。卷积层具体计算过程如下图所示:对于filter1,第一步:A与1相乘的结果+B与5相乘的结果;第二步:按照stride(步长)如2,下图中1对应的矩阵就要向右移动2步,上面移动完后向下移动两步,然后向右。依次计算A×2+B×6、A×3+B×7、A×4+B×8。最终得到一个2×2的矩阵,然后再加上一个偏置项。filter2和filter1一样做同样的操作。
最终结果如下图:
卷积层输出的结果也为四维数组,和input一样[batch, in_height, in_width, in_channels]。
tf中,卷积层是用tf.nn.conv2d(
input,
filter=None,
strides=None,
padding=None,
use_cudnn_on_gpu=True,
data_format=‘NHWC’,
dilations=[1, 1, 1, 1],
name=None,
filters=None
)
input和filter上述已经讲过,strides为int或list of ints,只有一个数时如2,表示height和width每次都移动两步;当为list of ints式,如[1, 2, 3, 1]表示heiht每次移动两步,width每次移动3步,开头的1对应input的batch,一般都为1,最后的1对应input的channel,一般也为1。
padding的意思是,按照stride向右移动或者向下移动后,如果最右边或最下边不够了怎么办,两种处理方式:SAME和VALID。
其他参数可以忽略。
3. 池化层
3.1 池化层正向传播
池化层和卷积层的移动原理相似,首先定义一个小区域(小矩阵),使用最大池化、平均池化、随机池化中的一种得到该小矩阵的结果(最大池化即返回小矩阵的最大值,平均池化返回小矩阵的平均值,随机池化就是随机选择一个数)。然后按照stride移动得到一个新的小矩阵,依次类推得到最终结果。如上述卷积层后按照每2×2,步长为1进行最大池化,得到结果为3 & 6。tf中最大池化为tf.nn.max_pool,平均池化为tf.nn.avg_pool。下面以max_pool为例讲解:
tf.nn.max_pool(
value,
ksize,
strides,
padding,
data_format=‘NHWC’,
name=None,
input=None
)
value即卷积层后的结果值,ksize类似stride,int或list of ints,[1, 2, 3, 1],开头和结果两个1对应batch_size 和channel,一般都为1,中间的2,3表示每2×3的小矩阵进行一次最大池化。stride、padding和卷积层一样
3.2 池化层反向传播
4. 全连接层
全连接层同dnn,注意池化层的结果仍然是一个四维的,因此要扁平化处理成二维的[batch, dimension。
5. 使用tensorflow实现cnn实例
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data', one_hot = True)
# batch_size批次大小
batch_size = 100
# iteration
n_batch = mnist.train.num_examples // batch_size
# 初始化权值
def weight_variabel(shape):
initial = tf.truncated_normal(shape, stddev = 0.1)
return tf.Variable(initial)
# 初始化偏置
def bias_variable(shape):
initial = tf.constant(0.1, shape = shape)
return tf.Variable(initial)
# 卷积层
def con2d(x, W):
return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')
# 池化层
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')
# 定义placeholder
x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
# 改变x的格式转化为4D向量[batch, in_height, in_width, in_channels]
x_image = tf.reshape(x, [-1, 28, 28, 1])
# 初始化第一个卷积层的权值和偏置
w_conv1 = weight_variabel([5, 5, 1, 32])# 5*5的采样窗口,32个卷积核从一个平面抽取数据
b_conv1 = bias_variable([32])# 每个卷积核有一个偏置
# 将x_image和权值向量进行卷积,再加上偏置,然后应用于relu激活函数
h_conv1 = tf.nn.relu(con2d(x_image, w_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
# 初始化第二个卷积层的权值和偏置
w_conv2 = weight_variabel([5, 5, 32, 64])
b_conv2 = bias_variable([64])
# 将h_pool和权值向量进行卷积,再加上偏置项,应用于relu函数
h_conv2 = tf.nn.relu(con2d(h_pool1, w_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
# 28*28第一次卷积后为28*28,第一次池化后为14*14
# 14*14第一次卷积后为14*14,第一次池化后为7*7
# 上述操作后得到64张7*7的平面
# 初始化第一个全连接层的权值
w_fc1 = weight_variabel([7*7*64, 1024])
b_fc1 = bias_variable([1024])
# 把池化层2的输出扁平化为1维
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
# 或者可以使用tf.layers.Flatten(h_pool2):保留第0维数据,其他维度展平成到一个轴上
# 求第一个全连接层的输出
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, w_fc1) + b_fc1)
# keep_prob 用来表示神经元的输出概率
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 初始化第二个全连接层
w_fc2 = weight_variabel([1024, 10])
b_fc2 = bias_variable([10])
# 计算输出
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc2) + b_fc2)
# 交叉熵损失函数
cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = prediction))
# 优化器
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# 结果存放到布尔
correct_prediction = tf.equal(tf.argmax(prediction, 1), tf.argmax(y, 1))
# 准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(10):
for batch in range(n_batch):
batch_xs, batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step, feed_dict = {x:batch_xs, y:batch_ys, keep_prob:0.7})
acc = sess.run(accuracy, feed_dict = {x:mnist.test.images, y:mnist.test.labels,keep_prob:1.0})
print('Iter' + str(epoch) + ', Testing Accuracy = ' + str(acc))
Iter0, Testing Accuracy = 0.7716
Iter1, Testing Accuracy = 0.8763
Iter2, Testing Accuracy = 0.9767
Iter3, Testing Accuracy = 0.9799
Iter4, Testing Accuracy = 0.9826
Iter5, Testing Accuracy = 0.9859
Iter6, Testing Accuracy = 0.9872
Iter7, Testing Accuracy = 0.9849
Iter8, Testing Accuracy = 0.988
Iter9, Testing Accuracy = 0.9879