卷积神经网络CNN的理论部分直接看帖子:TensorFlow学习--卷积神经网络CNN
卷积神经网络CNN的结构一般包含这几个层:
主要的函数说明:
详见我之前的两个帖子:TensorFlow学习---tf.nn.conv2d实现卷积操作和
TensorFlow学习---tf.nn.max_pool实现池化操作
本例子通过搭建卷积神经网络来实现手写数字识别的功能,搭建的卷积神经网络结构如下图所示:
上代码,此代码运行环境为python2.7,tensorflow1.2.0。例子中输入的训练图片直接使用tensorflow提供的数据
#! /usr/bin/env python2.7
#coding=utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('/root/tftest/data/', one_hot=True)
#这里使用TensorFlow实现一个简单的卷积神经网络,使用的是MNIST数据集。网络结构为:数据输入层–卷积层1–池化层1–卷积层2–池化层2–全连接层1–全连接层2(输出层),这是一个简单但非常有代表性的卷积神经网络。
#28*28的灰度图,像素个数784
#是10分类问题
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
#初始化单个卷积核上的参数
#使用截断的正太分布,标准差为 0.1 的初始值来初始化权值
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
#初始化单个卷积核上的偏置值
#使用常量 0.1 来初始化偏置 B
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
#输入特征x,用卷积核W进行卷积运算,strides为卷积核移动步长、多少个channel、filter的个数即产生特征图的个数
#padding表示是否需要补齐边缘像素使输出图像大小不变
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
#对x进行最大池化操作,ksize进行池化的范围
#使用步长为2,下采样核为2 * 2 的方式来初始化池化层
# 2x2最大池化,即将一个2x2的像素块降为1x1的像素。最大池化会保留原始像素块中灰度值最高的那一个像素,即保留最显著的特征。
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
#先将mnist数据集图片还原为二维向量结构,28 * 28 = 784 1代表颜色通道数量,-1表示自动计算此维度
x_image = tf.reshape(x, [-1,28,28,1])
#第一个卷积层, 总共 32 个 5 * 5 的卷积核,由于使用 'SAME' 填充,因此卷积后的图片尺寸依然是 28 * 28
#把x_image的厚度由1增加到32,长宽由28*28缩小为14*14
W_conv1 = weight_variable([5, 5, 1, 32]) # 按照[5,5,输入通道=1,输出通道=32]生成一组随机变量
b_conv1 = bias_variable([32])
#进行卷积操作,并添加relu激活函数
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 输出size 28*28*32(因为conv2d()中x和y步长都为1,边距保持不变)
#第一个池化层,28 * 28的图片尺寸池化后,变为 14 * 14
h_pool1 = max_pool_2x2(h_conv1) # 输出size 14*14*32
#第二个卷积层, 总共 64 个 5 * 5 的卷积核,由于使用 'SAME' 填充,因此卷积后的图片尺寸依然是 14 * 14
#把h_pool1的厚度由32增加到64,长宽由14*14缩小为7*7
W_conv2 = weight_variable([5, 5, 32, 64]) #64个厚度32的 5 * 5 卷积核
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
#第二个池化层,14 * 14 的图片尺寸池化后,变为 7 * 7
h_pool2 = max_pool_2x2(h_conv2) # 输出size 7*7*64
#第一层全连接,把h_pool2由7*7*64,变成1024*1
#使用上面定义的方式初始化接下来的两个全连接层的参数 W 和 B
# fc1,将两次池化后的7*7共128个特征图转换为1D向量,隐含节点1024由自己定义
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
#将卷积的产出展开,将二维图片结构转化为一维图片结构
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
#神经网络计算,并添加relu激活函数.第一个全连接层,图片尺寸从 7 * 7 * 64维变换为1024维
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
#为了减轻过拟合,使用Dropout层
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#第二层全连接,输出层,使用softmax计算概率进行多分类,最后一层网络,从 1024 维变换为 10 维的one-hot向量
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
#计算交叉熵loss,并使用自适应动量梯度下降算法优化loss
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
#train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)
#计算准确率
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
#定义一个交互式的session,并初始化所有变量
sess = tf.InteractiveSession()
sess.run(tf.initialize_all_variables())
#print(tf.shape(W_conv2)[0])
for i in range(0,5):
for j in range(0,5):
print((W_conv2.eval()[i][j][31][63]))
saver=tf.train.Saver(max_to_keep=1)
for i in range(1000):
batch = mnist.train.next_batch(50)
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_: batch[1], keep_prob: 1.0})
print "step %d, training accuracy %g"%(i, train_accuracy)
#saver.save(sess, "/root/tftest/model_data/mnist_cnn.ckpt", global_step=i+1)
print "test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})
for i in range(0,5):
for j in range(0,5):
print((W_conv2.eval()[i][j][31][63]))
若要使用自己的图片数据进行训练,可以将上面程序修改如下地方,将读入图片的语句,改为如下
for i in range(1000):
#batch = mnist.train.next_batch(1)
batch= next_batch(50, i)
,其中next_batch()为自己读入图片的代码
root = '/root/tftest/data/'
f = open(root + 'mnist/train/train.txt')
#fpath = []
#num = []
fpath = [line.strip().split(" ")[0]for line in f]
f.close
f = open(root + 'mnist/train/train.txt')
num = [int(line.strip().split(" ")[1])for line in f]
f.close
f = open(root + 'mnist/test/test.txt')
#fpath = []
#num = []
fpath_test = [line.strip().split("")[0] for line in f]
f.close
f = open(root + 'mnist/test/test.txt')
num_test = [int(line.strip().split("")[1]) for line in f]
f.close
def next_batch(batch_size, step):
#global fpath
#global num
bath0= np.zeros( (batch_size, 784) )
bath1= np.zeros( (batch_size, 10) )
j=0
for iin range(batch_size * step, batch_size * step + batch_size):
#print(root + fpath[i])
im= cv2.imread(root + fpath[i], cv2.IMREAD_GRAYSCALE).astype(np.float32)
im= cv2.resize(im,(28,28),interpolation=cv2.INTER_CUBIC)
#img_gray = 255.0 - im
img_gray = (255.0 - im) / 255.0
x_img = np.reshape(img_gray , [-1 , 784])
bath0[j] = x_img
#print(num[i])
bath1[j][num[i]] = 1.0
j+= 1
returnbath0, bath1