大家好,又见面了,我是你们的朋友全栈君。
前言:先坦白的说,深度神经网络的学习在一开始对我造成的困扰还是很大的,我也是通过不断地看相关的视频资料、文献讲解尝试去理解记忆。毕竟这些内容大多都是不可查的,我们看到的都只是输入输出的东西,里面的内部运作以及工作原理,都需要沉心静思。
这篇CNN卷积神经网络的原理介绍,也是自己通过收集来的资料阅读、理解、操练后,有了一定的见解后才拙笔,里面的内容我会尽量详尽,不清楚明白的地方,望大家慧眼指出。
先给大家出个脑筋急转弯:在白纸上画出一个大熊猫,一共需要几种颜色的画笔?——大家应该都知道,只需要一种黑色的画笔,只需要将大熊猫黑色的地方涂上黑色,一个大熊猫的图像就可以展现出来。
我们画大熊猫的方式,其实和妈妈们的十字绣很接近——在给定的格子里,绣上不同的颜色,最后就可以展现出一幅特定的“图片”。而机器识图的方式正好和绣十字绣的方式相反,现在有了一幅图片,机器通过识别图片中每个格子(像素点)上的颜色,将每个格子里的颜色都用数字类型存储,得到一张很大的数字矩阵,图片信息也就存储在这张数字矩阵中。
上图中每一个格子代表一个像素点,像素点里的数字代表颜色码,颜色码范围是[0,255],(各式各样的颜色都是由红、绿、蓝三色组成,每个颜色都是0~255之间数字) –—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—- –—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—- 我们在得到的一张大数字矩阵的基础上开展卷积神经网络识别工作: 机器识图的过程:机器识别图像并不是一下子将一个复杂的图片完整识别出来,而是将一个完整的图片分割成许多个小部分,把每个小部分里具有的特征提取出来(也就是识别每个小部分),再将这些小部分具有的特征汇总到一起,就可以完成机器识别图像的过程了 –—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—- –—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—-
用CNN卷积神经网络识别图片,一般需要的步骤有:
卷积层的作用:就是提取图片每个小部分里具有的特征
假定我们有一个尺寸为 6 ∗ 6 6*6 6∗6 的图像,每一个像素点里都存储着图像的信息。我们再定义一个卷积核(相当于权重),用来从图像中提取一定的特征。卷积核与数字矩阵对应位相乘再相加,得到卷积层输出结果。
(429 = 18×1+54×0+51×1+55×0+121×1+75×0+35×1+24×0+204×1) 卷积核的取值在没有以往学习的经验下,可由函数随机生成,再逐步训练调整
当所有的像素点都至少被覆盖一次后,就可以产生一个卷积层的输出(下图的步长为1)
机器一开始并不知道要识别的部分具有哪些特征,是通过与不同的卷积核相作用得到的输出值,相互比较来判断哪一个卷积核最能表现该图片的特征——比如我们要识别图像中的某种特征(比如曲线),也就是说,这个卷积核要对这种曲线有很高的输出值,对其他形状(比如三角形)则输出较低。卷积层输出值越高,就说明匹配程度越高,越能表现该图片的特征。
卷积层具体工作过程: 比如我们设计的一个卷积核如下左,想要识别出来的曲线如下右:
现在我们用上面的卷积核,来识别这个简化版的图片——一只漫画老鼠
当机器识别到老鼠的屁股的时候,真实区域数字矩阵与卷积核相乘作用后,输出较大:6600
而用同一个卷积核,来识别老鼠的耳朵的时候,输出则很小:0
我们就可以认为:现有的这个卷积核保存着曲线的特征,匹配识别出来了老鼠的屁股是曲线的。我们则还需要其他特征的卷积核,来匹配识别出来老鼠的其他部分。卷积层的作用其实就是通过不断的改变卷积核,来确定能初步表征图片特征的有用的卷积核是哪些,再得到与相应的卷积核相乘后的输出矩阵
池化层的输入就是卷积层输出的原数据与相应的卷积核相乘后的输出矩阵 池化层的目的:
最常见的两种池化层的形式:
举例说明两种池化方式:(池化步长为2,选取过的区域,下一次就不再选取)
在 4 ∗ 4 4*4 4∗4的数字矩阵里,以步长 2 ∗ 2 2*2 2∗2选取区域,比如上左将区域[1,2,3,4]中最大的值4池化输出;上右将区域[1,2,3,4]中平均值5/2池化输出
卷积层和池化层的工作就是提取特征,并减少原始图像带来的参数。然而,为了生成最终的输出,我们需要应用全连接层来生成一个等于我们需要的类的数量的分类器。
全连接层的工作原理和之前的神经网络学习很类似,我们需要把池化层输出的张量重新切割成一些向量,乘上权重矩阵,加上偏置值,然后对其使用ReLU激活函数,用梯度下降法优化参数既可。
from tensorflow.examples.tutorials.mnist import input_data
#读取MNIST数据集
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
#预定义输入值X、输出真实值Y placeholder为占位符
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
keep_prob = tf.placeholder(tf.float32)
x_image = tf.reshape(x, [-1,28,28,1])
def weight_variable(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)
truncated_normal()函数:选取位于正态分布均值=0.1附近的随机值
def conv2d(x, W):
#stride = [1,水平移动步长,竖直移动步长,1]
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
# stride = [1,水平移动步长,竖直移动步长,1]
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
在上面卷积层的工作原理中,有展示strides=[1, 1, 1, 1]的动态图, 下面展示strides=[1, 2, 2, 1]时的情况:可以看到高亮的区域每次向右移动两格,向下移动两格
可以得到:当我们的卷积层步长值越大,得到的输出图像的规格就会越小。为了使得到的图像的规格和原图像保持一样的大,在输入图像四周填充足够多的 0 边界就可以解决这个问题,这时padding的参数就为“SAME”(利用边界保留了更多信息,并且也保留了图像的原大小)下图:
padding的另一个可选参数为“VALID”,和“SAME”不同的是:不用0来填充边界,这时得到的图像的规格就会小于原图像。新图像尺寸大小 = 原数据尺寸大小-卷积核尺寸大小+1(一般我们选用的padding都为“SAME”)
池化函数用简单传统的2×2大小的模板做max pooling,池化步长为2,选过的区域下次不再选取
x_image = tf.reshape(x, [-1,28,28,1])
# 卷积层1网络结构定义
# 卷积核1:patch=5×5;in size 1;out size 32;激活函数reLU非线性处理
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
# output size 28*28*32
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# output size 14*14*32
h_pool1 = max_pool_2x2(h_conv1)
#卷积层2网络结构定义
#卷积核2:patch=5×5;in size 32;out size 64;激活函数reLU非线性处理
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
# output size 14*14*64
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# output size 7 *7 *64
h_pool2 = max_pool_2x2(h_conv2)
# 全连接层1
W_fc1 = weight_variable([7*7*64,1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1,7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
# 全连接层2
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
#二次代价函数:预测值与真实值的误差
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=prediction))
#梯度下降法:数据太庞大,选用AdamOptimizer优化器
train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)
#结果存放在一个布尔型列表中
correct_prediction = tf.equal(tf.argmax(prediction,1), tf.argmax(y_,1))
#求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
for i in range(1000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_: batch[1], keep_prob: 1.0})
print("step",i, "training accuracy",train_accuracy)
train_step.run(feed_dict={
x: batch[0], y_: batch[1], keep_prob: 0.5})
''' #保存模型参数 saver.save(sess, './model.ckpt') print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) '''
当完成训练时,程序会保存学习到的参数,不用下次再训练 特别提醒:运行非常占内存,而且运行到最后保存参数时,有可能卡死电脑
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing
# -*- 2018/01/24;14:14
# -*- python3.5
from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
def weight_variable(shape):
# 产生随机变量
# truncated_normal:选取位于正态分布均值=0.1附近的随机值
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 conv2d(x, W):
#stride = [1,水平移动步长,竖直移动步长,1]
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
# stride = [1,水平移动步长,竖直移动步长,1]
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
#读取MNIST数据集
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
sess = tf.InteractiveSession()
#预定义输入值X、输出真实值Y placeholder为占位符
x = tf.placeholder(tf.float32, shape=[None, 784])
y_ = tf.placeholder(tf.float32, shape=[None, 10])
keep_prob = tf.placeholder(tf.float32)
x_image = tf.reshape(x, [-1,28,28,1])
#print(x_image.shape) #[n_samples,28,28,1]
#卷积层1网络结构定义
#卷积核1:patch=5×5;in size 1;out size 32;激活函数reLU非线性处理
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) #output size 28*28*32
h_pool1 = max_pool_2x2(h_conv1) #output size 14*14*32
#卷积层2网络结构定义
#卷积核2:patch=5×5;in size 32;out size 64;激活函数reLU非线性处理
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) #output size 14*14*64
h_pool2 = max_pool_2x2(h_conv2) #output size 7 *7 *64
# 全连接层1
W_fc1 = weight_variable([7*7*64,1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1,7*7*64]) #[n_samples,7,7,64]->>[n_samples,7*7*64]
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # 减少计算量dropout
# 全连接层2
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
prediction = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
#prediction = tf.nn.softmax(stf.matmul(h_fc1_drop, W_fc2) + b_fc2)
#二次代价函数:预测值与真实值的误差
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=prediction))
#梯度下降法:数据太庞大,选用AdamOptimizer优化器
train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)
#结果存放在一个布尔型列表中
correct_prediction = tf.equal(tf.argmax(prediction,1), tf.argmax(y_,1))
#求准确率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
saver = tf.train.Saver() # defaults to saving all variables
sess.run(tf.global_variables_initializer())
for i in range(1000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_: batch[1], keep_prob: 1.0})
print("step",i, "training accuracy",train_accuracy)
train_step.run(feed_dict={
x: batch[0], y_: batch[1], keep_prob: 0.5})
''' #保存模型参数 saver.save(sess, './model.ckpt') print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) '''
效果展示如下:
训练700次时候,成功率已经到达98%,越往后学习,准确率越高
特别提醒:由于我的电脑配置比较低,运行耗时较长,而且在保存参数时候还会出现卡死情况,大家请注意。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/162088.html原文链接:https://javaforall.cn