Python人工智能 | 九.卷积神经网络CNN原理详解及TensorFlow编写CNN
文章目录:
一.卷积神经网络原理
1.什么是CNN
2.CNN原理
二.TensorFlow实现CNN
三.总结
https://github.com/eastmountyxz/
AI-for-TensorFlowhttps://github.com/eastmountyxz/
AI-for-Keras
学Python近八年,认识了很多大佬和朋友,感恩。作者的本意是帮助更多初学者入门,因此在github开源了所有代码,也在公众号同步更新。深知自己很菜,得拼命努力前行,编程也没有什么捷径,干就对了。希望未来能更透彻学习和撰写文章,也能在读博几年里学会真正的独立科研。同时非常感谢参考文献中的大佬们的文章和分享。
- https://blog.csdn.net/eastmount
一.卷积神经网络原理
2.CNN原理
首先,这是有一张彩色图片,它包括RGB三原色分量,图像的长和宽为256*256,三个层面分别对应红(R)、绿(G)、蓝(B)三个图层,也可以看作像素点的厚度。
其次,CNN将图片的长度和宽度进行压缩,变成12812816的方块,压缩的方法是把图片的长度和宽度压小,从而增高厚度。
再次,继续压缩至646464,直至3232256,此时它变成了一个很厚的长条方块,我们这里称之为分类器Classifier。该分类器能够将我们的分类结果进行预测,MNIST手写体数据集预测结果是10个数字,比如[0,0,0,1,0,0,0,0,0,0]表示预测的结果是数字3,Classifier在这里就相当于这10个序列。
最后,CNN通过不断压缩图片的长度和宽度,增加厚度,最终会变成了一个很厚的分类器,从而进行分类预测。
VALID PADDING: 抽出来这层比原先那层图片宽和长裁剪了一点,抽取的内容全部是图片内的。
SAME PADDING: 抽离出的那层与之前的图片一样的长和宽,抽取的内容部分再图片外,图片外的值用0来填充。
IMAGE 图片
CONVOLUTION 图层
MAX POOLING 更好地保存原图片的信息
CONVOLUTION 图层
MAX POOLING 更好地保存原图片的信息
FULLY CONNECTED 神经网络隐藏层
FULLY CONNECTED 神经网络隐藏层
CLASSIFIER 分类器
二.TensorFlow实现CNN
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 下载数据集 数字1到10
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
#-------------------------------定义计算准确度函数------------------------------
# 参数:预测xs和预测ys
def compute_accuracy(v_xs, v_ys):
# 定义全局变量
global prediction
# v_xs数据填充到prediction变量中 生成预测值0到1之间的概率
y_pre = sess.run(prediction, feed_dict={xs:v_xs,keep_prob: 1})
# 比较预测最大值(y_pre)和真实最大值(v_ys)的差别 如果等于就是预测正确,否则错误
correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(v_ys,1))
# 计算正确的数量
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 输出结果为百分比 百分比越高越准确
result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys, keep_prob:1})
return result
#---------------------------------定义权重和误差变量------------------------------
# 输入shape返回变量定义的参数
def weight_variable(shape):
# 产生截断正态分布随机数
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
# 误差初始值定义为0.1
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#---------------------------------定义卷积神经网络层------------------------------
# 定义二维CNN x表示输入值或图片的值 W表示权重
def conv2d(x, W):
# 输入x表示整张图片的信息 权重W strides表示步长跨度 [1,x_movement,y_movement,1]
# strides:一个长度为4的列表 第一个和最后一个元素为1 第二个为元素是水平x方向的跨度 第三个元素为垂直y方向跨度
# padding包括两种形式 VALID和SAME
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
#------------------------------------定义POOLING---------------------------------
def max_pool_2x2(x):
# Must have strides[0] = striders[3] = 1
# x_movement和y_movement隔两个步长移动一次 从而压缩整幅图片的长和宽
return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')
# 设置传入的值xs和ys
xs = tf.placeholder(tf.float32, [None, 784]) #每张图片28*28=784个点
ys = tf.placeholder(tf.float32, [None, 10]) #每个样本有10个输出
# keeping probability
keep_prob = tf.placeholder(tf.float32)
# 形状修改
# xs包括了所有的图片样本 -1表示图片个数维度暂时不管(后续补充)
# 28*28表示像素点 1表示信道(该案例图片黑白为1,彩色为3)
x_image = tf.reshape(xs, [-1,28,28,1])
print(x_image.shape) #[n_samples,28,28,1]
接下来我们就开始讲解如何添加神经层。
第九步,增加神经层 conv1 layer。
小方块的长度和宽度是5,in size为1是图片的厚度,输出的高度是32。
h_conv1输出的大小为282832,因为padding采用“SAME”的形式,W_conv1输出值为32,故厚度也为32,长度和宽度相同为28。而由于POOLING处理设置的strides步长为2,故其输出大小也有变化,其结果为141432。核心代码如下:
#-------------------------------增加神经层 conv1 layer------------------------------
# 定义权重
W_conv1 = weight_variable([5,5,1,32]) #patch 5*5, input size 1, output size 32
# 定义bias
b_conv1 = bias_variable([32]) #32个长度
# 搭建CNN的第一层
# 嵌套一个relu非线性化激励处理 计算 = x_image输入*权重 + 误差
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 28*28*32
# POOLING处理
h_pool1 = max_pool_2x2(h_conv1) # output size 14*14*32
第十步,通过同样的方法定义conv2 layer。
W_conv2定义的patch为5*5,传入大小为32,传出大小为64,不断将其变厚,类似于下图所示。图片最早的厚度为1(MNIST数据集是黑白图片,如果是彩色则为3),接着第一层厚度变成32,第三层厚度增长为64。
此时h_conv2的输出结果为141464,第二层POOLING处理会继续缩小一半,h_pool2输出结果为7764,高度不变。
#-------------------------------增加神经层 conv2 layer------------------------------
# 定义权重
W_conv2 = weight_variable([5,5,32,64]) #patch 5*5, input size 32, output size 64
# 定义bias
b_conv2 = bias_variable([64]) #64个长度
# 搭建CNN的第二层
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 14*14*64
# POOLING处理
h_pool2 = max_pool_2x2(h_conv2) # output size 7*7*64
接下来我们开始定义func1 layer和func2layer。
第十一步,定义func1 layer,即第一个全连接神经层。
定义权重,输入值为conv2 layer的输出值7764,输出值为1024,让其变得更高更厚。
#-------------------------------增加神经层 func1 layer------------------------------
# 定义权重 输入值为conv2 layer的输出值7*7*64 输出为1024
W_fc1 = weight_variable([7*7*64, 1024])
# 定义bias
b_fc1 = bias_variable([1024]) #1024个长度
# 将h_pool2输出值7*7*64转换为一维数据 [n_samples,7,7,64]->>[n_samples,7*7*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) #-1表示样本数
# 乘法
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 解决过拟合
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
第十二步,进行最后一层的Layer处理。
#-------------------------------增加神经层 func2 layer------------------------------
# 定义权重 输入值为1024 输出为10对应10个数字
W_fc2 = weight_variable([1024, 10])
# 定义bias
b_fc2 = bias_variable([10])
# 预测 使用softmax计算概率
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
这里简单总结下神经网络:
conv1 layer:经过卷积和POOLING处理,最终输出141432
conv2 layer:经过卷积和POOLING处理,最终输出7764
func1 layer:平常使用的神经网络,输入7764,最终输出1024
func2 layer:平常使用的神经网络,输入1024,最终输出10,代表10个数字,即为prediction
第十三步,定义误差loss和训练。
这里使用的优化器是AdamOptimizer()函数,其学习效率比GradientDescentOptimizer()更高,学习效率设置为0.0001。
# 预测值与真实值误差 平均值->求和->ys*log(prediction)
cross_entropyloss = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
reduction_indices=[1])) #loss
# 训练学习 学习效率设置为0.0001
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropyloss) #减小误差
第十四步,初始化操作。
# 定义Session
sess = tf.Session()
# 初始化
init = tf.initialize_all_variables()
sess.run(init)
第十五步,神经网络分类学习。
for i in range(1000):
# 提取一部分的xs和ys
batch_xs, batch_ys = mnist.train.next_batch(100) #从下载好的数据集提取100个样本
# 训练
sess.run(train_step, feed_dict={xs:batch_xs, ys:batch_ys})
# 每隔50步输出一次结果
if i % 50 == 0:
# 计算准确度
print(compute_accuracy(
mnist.test.images, mnist.test.labels))
最终完整代码如下:
# -*- coding: utf-8 -*-
"""
Created on Fri Dec 20 14:27:01 2019
@author: xiuzhang Eastmount CSDN
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 下载数据集 数字1到10
mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
#-------------------------------定义计算准确度函数------------------------------
# 参数:预测xs和预测ys
def compute_accuracy(v_xs, v_ys):
# 定义全局变量
global prediction
# v_xs数据填充到prediction变量中 生成预测值0到1之间的概率
y_pre = sess.run(prediction, feed_dict={xs:v_xs,keep_prob: 1})
# 比较预测最大值(y_pre)和真实最大值(v_ys)的差别 如果等于就是预测正确,否则错误
correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(v_ys,1))
# 计算正确的数量
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
# 输出结果为百分比 百分比越高越准确
result = sess.run(accuracy, feed_dict={xs:v_xs, ys:v_ys, keep_prob:1})
return result
#---------------------------------定义权重和误差变量------------------------------
# 输入shape返回变量定义的参数
def weight_variable(shape):
# 产生截断正态分布随机数
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
# 误差初始值定义为0.1
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#---------------------------------定义卷积神经网络层------------------------------
# 定义二维CNN x表示输入值或图片的值 W表示权重
def conv2d(x, W):
# 输入x表示整张图片的信息 权重W strides表示步长跨度 [1,x_movement,y_movement,1]
# strides:一个长度为4的列表 第一个和最后一个元素为1 第二个为元素是水平x方向的跨度 第三个元素为垂直y方向跨度
# padding包括两种形式 VALID和SAME
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
#------------------------------------定义POOLING---------------------------------
def max_pool_2x2(x):
# Must have strides[0] = striders[3] = 1
# x_movement和y_movement隔两个步长移动一次 从而压缩整幅图片的长和宽
return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')
#-----------------------------定义placeholder输入至神经网络-------------------------
# 设置传入的值xs和ys
xs = tf.placeholder(tf.float32, [None, 784]) #每张图片28*28=784个点
ys = tf.placeholder(tf.float32, [None, 10]) #每个样本有10个输出
# keeping probability
keep_prob = tf.placeholder(tf.float32)
# 形状修改
# xs包括了所有的图片样本 -1表示图片个数维度暂时不管(后续补充)
# 28*28表示像素点 1表示信道(该案例图片黑白为1,彩色为3)
x_image = tf.reshape(xs, [-1,28,28,1])
print(x_image.shape) #[n_samples,28,28,1]
#-------------------------------增加神经层 conv1 layer------------------------------
# 定义权重
W_conv1 = weight_variable([5,5,1,32]) #patch 5*5, input size 1, output size 32
# 定义bias
b_conv1 = bias_variable([32]) #32个长度
# 搭建CNN的第一层
# 嵌套一个relu非线性化激励处理 计算 = x_image输入*权重 + 误差
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 28*28*32
# POOLING处理
h_pool1 = max_pool_2x2(h_conv1) # output size 14*14*32
#-------------------------------增加神经层 conv2 layer------------------------------
# 定义权重
W_conv2 = weight_variable([5,5,32,64]) #patch 5*5, input size 32, output size 64
# 定义bias
b_conv2 = bias_variable([64]) #64个长度
# 搭建CNN的第二层
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 14*14*64
# POOLING处理
h_pool2 = max_pool_2x2(h_conv2) # output size 7*7*64
#-------------------------------增加神经层 func1 layer------------------------------
# 定义权重 输入值为conv2 layer的输出值7*7*64 输出为1024
W_fc1 = weight_variable([7*7*64, 1024])
# 定义bias
b_fc1 = bias_variable([1024]) #1024个长度
# 将h_pool2输出值7*7*64转换为一维数据 [n_samples,7,7,64]->>[n_samples,7*7*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) #-1表示样本数
# 乘法
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
# 解决过拟合
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#-------------------------------增加神经层 func2 layer------------------------------
# 定义权重 输入值为1024 输出为10对应10个数字
W_fc2 = weight_variable([1024, 10])
# 定义bias
b_fc2 = bias_variable([10])
# 预测 使用softmax计算概率
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
#------------------------------定义loss和训练-------------------------------
# 预测值与真实值误差 平均值->求和->ys*log(prediction)
cross_entropyloss = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
reduction_indices=[1])) #loss
# 训练学习 学习效率设置为0.0001
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropyloss) #减小误差
#-----------------------------------初始化-----------------------------------
# 定义Session
sess = tf.Session()
# 初始化
init = tf.initialize_all_variables()
sess.run(init)
#---------------------------------神经网络学习---------------------------------
for i in range(1000):
# 提取一部分的xs和ys
batch_xs, batch_ys = mnist.train.next_batch(100) #从下载好的数据集提取100个样本
# 训练
sess.run(train_step, feed_dict={xs:batch_xs, ys:batch_ys, keep_prob:0.5})
# 每隔50步输出一次结果
if i % 50 == 0:
# 计算准确度
print(compute_accuracy(
mnist.test.images, mnist.test.labels))
接着运行代码,CPU基本上满负荷运转。
最终输出结果如下图所示,可以看到,最早预测的准确度结果非常低为6.78%,最后提升到了96.92%,其结果高于之前的一般神经网络的结果87.79%(第六篇博客),由此可见TensorFlow CNN的分类学习效果还不错。
0.0678
0.8096
0.8839
0.9119
0.9266
0.9361
0.9441
0.9496
0.9505
0.9561
0.9566
0.9616
0.9625
0.9644
0.9648
0.9668
0.9689
0.9698
0.9709
0.9692
三.总结
写到这里,这篇文章就结束了。本文详细介绍了卷积神经网络CNN的基本原理,并通过TensorFlow实现CNN卷积神经网络,结合MNIST手写体识别数据集进行分类学习。
最后,希望这篇基础性文章对您有所帮助,如果文章中存在错误或不足之处,还请海涵~作为人工智能的菜鸟,我希望自己能不断进步并深入,后续将它应用于图像识别、网络安全、对抗样本等领域,指导大家撰写简单的学术论文,一起加油!
读博不易,但深夜总喜欢挤时间写上一篇文章,算是对自己这么多年分享的鼓励,也希望自己能坚持,感谢家人的支持,小珞珞太可爱了。如果您也是从事Python数据分析、图像处理、人工智能、网络安全的朋友,我们可以深入探讨,尤其是做研究的同学,共同进步~
前文分享(可以点击喔):
天行健,君子以自强不息。
地势坤,君子以厚德载物。
真诚地感谢您关注“娜璋之家”公众号,也希望我的文章能陪伴你成长,希望在技术路上不断前行。文章如果对你有帮助、有感悟,就是对我最好的回报,且看且珍惜!再次感谢您的关注,也请帮忙宣传下“娜璋之家”,初来乍到,还请多指教。
(By:Eastmount 2021-11-09 夜于东西湖)
参考文献:
[1] 神经网络和机器学习基础入门分享 - 作者的文章
[2] 斯坦福机器学习视频NG教授:https://class.coursera.org/ml/class/index
[3] 书籍《游戏开发中的人工智能》、《游戏编程中的人工智能技术》
[4] 网易云莫烦老师视频(强推 我付费支持老师一波):https://study.163.com/course/courseLearn.htm?courseId=1003209007
[5] 神经网络激励函数 - deeplearning
[6] tensorflow架构 - NoMorningstar
[7] Tensorflow实现CNN用于MNIST识别 - siucaan
[8] MNIST手写体识别任务 - chen645096127
[9] https://github.com/siucaan/CNN_MNIST
[10] https://github.com/eastmountyxz/AI-for-TensorFlow
[11] Google官方卷积神经网络介绍视频