Tensorflow 构建CNN详细介绍
Tensorflow 构建CNN
一.构建CNN准备
1.创建权重函数
def weight_variable(shape,name="w"): initial = tf.contrib.layers.xavier_initializer() return tf.get_variable(name,initializer = initial,shape=shape) |
注意:权重初始化值选择xavier,这个初始化器是用来保持每一层的梯度大小都差不多相同。
2.创建偏置函数
def biases_variable(shape): initial = tf.constant(0.0, shape=shape) return tf.Variable(initial) |
3.创建二维卷积函数
def conv2d(x, W,strides=[1, 1, 1, 1]): return tf.nn.conv2d(x, W, strides=strides,padding='SAME') |
如果padding=’SAME’,则输出的卷积后图像大小与输入的大小一样。如果padding=‘VALID’,则输出的卷积后图像大小为N=(imgSize-kSize)/Strides,这里imgSize为原来图像的宽或者高,kSize为卷积核大小,Strides为卷积步长。
4.创建最大池化层函数
def max_pool_2x2(x,ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1]): return tf.nn.max_pool(x,ksize ,strides ,padding='VALID') |
同样的,如果padding=’SAME’,则输出的卷积后图像大小与输入的大小一样。如果padding=‘VALID’,则输出的卷积后图像大小为N=(imgSize-kSize)/Strides,这里imgSize为原来图像的宽或者高,kSize为卷积核大小,Strides为卷积步长。
5.定义占位符
xs = tf.placeholder(tf.float32, [None, 465, 128, 1]) ys = tf.placeholder(tf.float32, [None, 10]) |
定义占位符只需要知道自己输入图的大小就可以按照相应的改写。例如输入100张20X20的图则:xs = tf.placeholder(tf.float32,[None,20,20,1])。其中None表示可以输入任意多的图。
二.构建CNN结构
上图为一个卷积层的示意图,可以知道,卷积层需要突触权值,偏置(可以选择不要偏置)激活函数,最后得到输出。
1.创建卷积层,并且用relu激活
kenerl_size = [3, 3, 1, 32] strides = [1, 1, 1, 1] W_conv1 = weight_variable(kenerl_size,name="w1") ##patch 3x3 ,in size 1,out size 32 b1 = biases_variable([kenerl_size[3]]) #b1=0 conv2_1 = tf.nn.relu(conv2d(xs, W_conv1)+b1) # N x 465 x 128 x 32 |
假设我们需要一个3x3的卷积核去卷积输入,并且输出32层,每次卷积的步长为1.那么我们的卷积核大小就为[3, 3, 1, 32]=>>[卷积宽度,卷积高度,卷积输入,卷积输出]
卷积步长[1, 1, 1, 1]=>>[固定1,卷积步长宽度,卷积步长高度,固定1]
因为权重是要随着训练改变的,因此定义权重变量,变量的大小为卷积核的大小。
偏置是加入对应每个输出上的,因此是卷积核大小为卷积输出大小。
最后卷积用激活函数relu去激活。
2.创建池化层
池化层按照我的理解是对卷积后的结果进行降维。降维后每个通道图大小为N=(imgSize-kSize)/Strides,这里imgSize为原来图像的宽或者高,kSize为池化核大小,Strides为池化步长。
pool_2 = max_pool_2x2(x=conv2_2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1]) # N x 116 x 32 x 64 |
3.对池化得到的结果压平用于全连接层
压平这个操作其实就是矩阵转换成一维矩阵,reshape即可,需要注意的是矩阵大小要计算准确,最后一维矩阵大小为N=high*wide*channel也就是输出通道数乘以图的宽度高度
Ft = tf.reshape(pool_2, [-1, 116 * 32 * 64]) # N x 3 x 2 x 64 =>> N x 384 |
4.创建全连接层
全连接是对压平后的数据再次变小,用矩阵乘法得到更新的维度再激活函数激活
# Dense layer # 1 Dense layer(units: 100, activation: ReLu ) Dropout(rate: 30 %) w_fc_1 = tf.Variable(tf.truncated_normal([116 * 32 * 64, 100], stddev=0.01)) # N x 384 =>> N x 100 b3 = biases_variable([100]) x = tf.nn.relu(tf.matmul(Ft, w_fc_1)+b3) |
5.预测
预测也是矩阵相乘,压缩输出
out_size = tf.Variable(tf.truncated_normal([100, 10], stddev=0.01)) # N x 100 =>> N x 10 prediction_values = tf.matmul(x, out_size) |
到此一个CNN构建完成,卷积池化全连接大小可以根据实际情况自行增加或者减少。最后可以看下图进行回顾。
三.训练模型
训练模型我们需要定义损失,优化损失方法,接下来就是训练。因为训练数据量很大我们需要对数据按照batch划分,一个一个小的batch进行训练。
1.定义损失
loss = tf.losses.softmax_cross_entropy(ys,prediction_values) |
如果损失函数选择了sofmax那么我们的ys也就是标签需要定义成one-hot。比如有10个类,那么第一类0的one-hot编码为[1,0,0,0,0,0,0,0,0,0]
2.定义训练
LEARNING_RATE_BASE = 0.001 LEARNING_RATE_DECAY = 0.1 LEARNING_RATE_STEP = 300 gloabl_steps = tf.Variable(0, trainable=False) learning_rate = tf.train.exponential_decay(LEARNING_RATE_BASE , gloabl_steps, LEARNING_RATE_STEP, LEARNING_RATE_DECAY, staircase=True) train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss,global_step=gloabl_steps) |
最小化损失minimize然后把损失函数放进去。这里学习率的定义需要注意:
曲线 初始时 上扬 [黄线]:
解决方法:初始学习率过大导致振荡,应减小学习率,并从头开始训练 。
曲线 初始时强势下降没多久归于水平 [绿线]:
解决方法:后期学习率过大导致无法拟合,应减小学习率,并重新训练后几轮 。
曲线 全程缓慢 [紫线]:
解决方法:初始学习率过小导致收敛慢,应增大学习率,并从头 开始训练 。
一般情况下,选择指数下降函数学习率学习。
四.一个完整例子
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: eric.lai 2018.11.19
"""
建立CNN方法
1.建立权重,偏移,卷积,池化函数
2.定义x,y输入输出占位符,定义防止过拟合keep_prob参数
3.定义一层卷积池化,二层卷积池化
4.定义全连接层两层
5.预测
6.损失函数
7.训练
"""
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data',one_hot=True)
def compute_accuracy(v_xs, v_ys):
global prediction
y_pre = sess.run(prediction, feed_dict={xs: v_xs, keep_prob: 1})
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
def weight_variable(shape):
initial = tf.truncated_normal(shape,stddev=0.1)
return tf.Variable(initial)
def biases_variable(shape):
initial = tf.constant(0.1,shape=shape)
return tf.Variable(initial)
def conv2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME') #strides=[1,1,1,1] 这里第一个和最后一个都固定为1,1 ,中间参数是间隔几个像素移动,padding中 VALID(抽取的图片比原图小),SAME(抽取的图片和原来一样)
def max_pool_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME') #ksize=[1,2,2,1]这里第一个和第四位一样,中间参数是间隔几个像素移动
## 定义x,y,keep_prob占位符
xs = tf.placeholder(tf.float32,[None,784])
ys = tf.placeholder(tf.float32,[None,10])
keep_prob = tf.placeholder(tf.float32)
x_image = tf.reshape(xs,[-1,28,28,1]) #[n_samples,28,28,1] -1代表输入暂时不定可以多张输入
### conv1 layer ###
W_conv1 = weight_variable([5,5,1,32]) ##patch 5x5 ,in size 1,out size 32
b_conv1 = biases_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1) #out size 28x28x32
h_pool1 = max_pool_2x2(h_conv1) # out size 14x14x32
###conv2 layer ###
W_conv2 = weight_variable([5,5,32,64]) # patch 5x5 ,in size 32 ,out size 64
b_conv2 = biases_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1,W_conv2)+b_conv2) # out size 14x14x32
h_pool2 = max_pool_2x2(h_conv2) # output size 7x7x64
### fc1 layer###
W_fc1 = weight_variable([7*7*64 ,1024])
b_fc1 = biases_variable([1024])
## [n_samples ,7 ,7 ,64] ->> [n_samples ,7x7x64]
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)
### fc2 layer###
W_fc2 = weight_variable([1024,10])
b_fc2 = biases_variable([10])
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
loss = tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(loss)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(1000)
print(type(batch_xs),batch_ys.shape)
sess.run(train_step,feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5})
if i % 50 == 0:
print(batch_xs.shape,batch_ys.shape)
print(compute_accuracy(mnist.test.images[:1000], mnist.test.labels[:1000]))
五.最后经验总结
对于CNN训练,如果无法收敛或者震荡,应当考虑以下情况:
1.数据是否正确,数据和标签对应与否
2.数据是否归一化
3.训练的batch是否太小
4.学习率,一般地选择指数衰减法
参考:以上图片均为网络图片,仅作示例,侵权联系删除
更多推荐
所有评论(0)