之前简单的测试版本在MNIST上只有91%正确率,实在太糟糕。这里用一个稍微复杂的模型:卷积神经网络来改善效果。这会达到大概99.2%的准确率。
权重初始化
为了创建这个模型,需要创建大量的权重和偏置项。这个模型中的权重在初始化时应该加入少量的噪声来打破对称性以及避免0梯度。由于使用的是ReLU神经元,因此比较好的做法是用一个较小的正数来初始化偏置项,以避免神经元节点输出恒为0的问题(dead neurons)。为了不在建立模型的时候反复做初始化操作,定义两个函数用于初始化。
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)
卷积和池化
TensorFlow在卷积和池化上有很强的灵活性。我们怎么处理边界?步长应该设多大?在这个实例里,我们会一直使用vanilla版本。卷积使用1步长(stride size),0边距(padding size)的模板,保证输出和输入是同一个大小。池化用简单传统的2x2大小的模板做max pooling。为了代码更简洁,把这部分抽象成一个函数。
def conv2d(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')
第一层卷积
由一个卷积接一个max pooling完成。卷积在每个5x5的patch中算出32个特征。卷积的权重张量形状是[5, 5, 1, 32] ,前两个维度是patch的大小,接着是输入的通道数目,最后是输出的通道数目。 而对于每一个输出通道都有一个对应的偏置量。
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
为了用这一层,把x 变成一个4d向量,其第2、第3维对应图片的宽、高,最后一维代表图片的颜色通道数(因为是灰度图所以这里的通道数为1,如果是rgb彩色图,则为3)。
x_image = tf.reshape(x, [-1,28,28,1])
把x_image 和权值向量进行卷积,加上偏置项,然后应用ReLU激活函数,最后进行max pooling。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
第二层卷积
为了构建一个更深的网络,我们会把几个类似的层堆叠起来。第二层中,每个5x5的patch会得到64个特征。
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)
h_pool2 = max_pool_2x2(h_conv2)
密集连接层
现在,图片尺寸减小到7x7,加入一个有1024个神经元的全连接层,用于处理整个图片。把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU。
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)
Dropout
为了减少过拟合,在输出层之前加入dropout。用一个placeholder 来代表一个神经元的输出在dropout中保持不变的概率。这样可以在训练过程中启用dropout,在测试过程中关闭dropout。 TensorFlow的tf.nn.dropout 操作除了可以屏蔽神经元的输出外,还会自动处理神经元输出值的scale。所以用dropout的时候可以不用考虑scale。
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
输出层
最后,添加一个softmax层,就像前面的单层softmax regression一样。
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)
训练和评估模型
为了进行训练和评估,使用与之前简单的单层SoftMax神经网络模型几乎相同的一套代码,只是会用更加复杂的ADAM优化器来做梯度最速下降,在feed_dict 中加入额外的参数keep_prob 来控制dropout比例。然后每100次迭代输出一次日志。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).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"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
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 %d, training accuracy %g"%(i, train_accuracy)
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print "test accuracy %g"%accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})
以上代码,在最终测试集上的准确率大概是99.2%。
代码解析:
#-*-coding:utf-8-*-
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#1.加载数据
mnist = input_data.read_data_sets("MNIST_data",one_hot=True)
#2.开启回话
sess = tf.InteractiveSession()
#3.真实值占位符
x=tf.placeholder(tf.float32,[None,784])
y_=tf.placeholder(tf.float32,[None,10])
# W=tf.Variable(tf.zeros([784,10]))
def weight_varible(shape):
initial = tf.truncated_normal(shape,stddev=0.1)
return tf.Variable(initial)
# b = tf.Variable(tf.zeros([10]))
def bias_variable(shape):
initial = tf.constant(0.1,shape=shape)
return tf.Variable(initial)
#conv
def conv_2d(x,W):
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')
#pooling
def max_pooling_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME')
#Conv_1
x_image = tf.reshape(x,[-1,28,28,1])
W_conv1 = weight_varible([5,5,1,32])
b_conv1 = bias_variable([32])
activate_conv1 = tf.nn.relu(conv_2d(x_image,W_conv1)+b_conv1)
pool_conv1 = max_pooling_2x2(activate_conv1)
#Conv_2
W_conv2 = weight_varible([5,5,32,64])
b_conv2 = bias_variable([64])
activate_conv2 = tf.nn.relu(conv_2d(pool_conv1,W_conv2)+b_conv2)
pool_conv2 = max_pooling_2x2(activate_conv2)
#fc
W_fc1 = weight_varible([7*7*64,1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(pool_conv2,[-1,7*7*64])
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)
#output
W_fc2 = weight_varible([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2)
#train model
cross_entropy =-tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
corret_pre = tf.equal(tf.arg_max(y_conv,1),tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(corret_pre,tf.float32))
sess.run(tf.global_variables_initializer())
for i in range(20000):
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:%d,training accuracy:%g"%(i,train_accuracy)
sess.run(train_step,feed_dict={x:batch[0],y_:batch[1],keep_prob:0.5})
print "test_accuracy: %g"%(accuracy.eval({x:mnist.test.images,y_:mnist.test.labels,keep_prob:1.0}))
知识点:
1、tf.Intersation()
让你在运行图的时候,插入一些计算图,这些计算图是由某些操作(operations)构成的。这对于工作在交互式环境中的人们来说非常便利, 可以使用InteractiveSession 代替Session
类, 使用 Tensor.eval()
和 Operation.run()
方法代替Session.run()
. 这样可以避免使用一个变量来持有会话。
tf.Session():需要在启动session之前构建整个计算图,然后启动该计算图, 启动图的第一步是创建一个 Session
对象, 如果无任何创建参数, 会话构造器将启动默认图.
mean在我们使用tf.InteractiveSession()来构建会话的时候,我们可以先构建一个session然后再定义操作(operation),如果我们使用tf.Session()来构建会话我们需要在会话构建之前定义好全部的操作(operation)然后再构建会话。
2、tf.reduce_mean()
(input_tensor,reduction_indices=None,keep_dims=False,name=None)
按某一维度(reduction_indices)计算一个张量的各元素的平均值参数说明:
Input_tensor:被降维的张量其数据类型必须被预先指定。
reduction_indices:降维的维度如果为None(default),则所有维度都要降维。
keep_dims:如果keep_dims为true,则降维的尺寸将保留为1
name:降维操作的名字。
返回一个降维后的张量。
3、tf.reduce_sum
(input_tensor, axis=None,keep_dims=False,name=None,reduction_indices=None)
参数说明:
input_tensor:表示输入
axis:表示在哪个维度进行sum操作。
keep_dims:表示是否保留原始数据的维度,False相当于执行完后原始数据就会少一个维度。
reduction_indices:为了跟旧版本的兼容,现在已经不使用了。
EG:
# 'x' is [[1, 1, 1]
# [1, 1, 1]]
#求和
tf.reduce_sum(x) ==> 6
#按列求和
tf.reduce_sum(x, 0) ==> [2, 2, 2]
#按行求和
tf.reduce_sum(x, 1) ==> [3, 3]
#按照行的维度求和
tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
#行列求和
tf.reduce_sum(x, [0, 1]) ==> 6
4、tf.cast(x, dtype, name=None)
将x的数据格式转化成dtype.例如,原来x的数据格式是bool, 那么将其转化成float以后,就能够将其转化成0和1的序列。
eg:
a = tf.Variable([1,0,0,1,1])
b = tf.cast(a,dtype=tf.bool)
sess = tf.Session()
sess.run(tf.initialize_all_variables())
print(sess.run(b))
#[ True False False True True]
5、accuracy.eval()
Session.run
和 Tensor.eval
如果你有一个Tensor
t,在使用t.eval()时,等价于:
tf.get_default_session().run(t)
.
这其中最主要的区别就在于你可以使用sess.run()
在同一步获取多个tensor中的值
t = tf.constant(42.0)
u = tf.constant(37.0)
tu = tf.mul(t, u)
ut = tf.mul(u, t)
with sess.as_default():
tu.eval() # runs one step
ut.eval() # runs one step
sess.run([tu, ut]) # evaluates both tensors in a single step
6. tf.app
案例1:
_*_coding:utf-8_*_
import tensorflow as tf
# tensorflow定义了 tf.app.flags用来接收命令行带来的参数
flag = tf.app.flags
#param1: 变量名称
#param2:变量默认值
#param3:变量描述
flag.DEFINE_string(name='name',default='julia',help='username')
flag.DEFINE_integer(name='age',default=20,help="'user's age")
flag.DEFINE_boolean(name='sex',default=True,help='True:boy, False:girl')
Flags = flag.FLAGS
def main(temp):
print(Flags.name)
print(Flags.age)
print(Flags.sex)
if __name__ == "__main__":
#这个函数,就是首先处理flag解析,
#再之后执行main()函数
tf.app.run()
直接在pycharm中运行py文件输出结果(不传递参数的默认结果)
julia
20
True
在terminal:
$python test.py --name JULIA --age 22 --sex True
案例2:
#_*_coding:utf-8_*_
import tensorflow as tf
#导入命令行解析模块
import argparse
import sys
FLAGS=None
def main(_):
print(sys.argv[0])
if __name__=="__main__":
#创建对象
parse=argparse.ArgumentParser()
#增加命令行
parse.add_argument('--dataDir',type=str,default='\\tmp\\tensorflow\\mnist\\inputData',
help='Directory for string input data')
FLAGS, unparsed=parse.parse_known_args()
tf.app.run(main=main, argv=[sys.argv[0]] + unparsed) #解析命令行参数,调用main函数 main(sys.argv)
在你进行学习之前,需要这几个mnist文件:
- train-images-idx3-ubyte: training set images
- train-labels-idx1-ubyte: training set labels
- t10k-images-idx3-ubyte: test set images
- t10k-labels-idx1-ubyte: test set labels
步骤:
1、将要识别的图片转为灰度图,并且转化为28*28矩阵(单通道,每个像素范围0-255,0为黑色,255为白色,这一点与MNIST中的正好相反)
2、将28*28的矩阵转换成1维矩阵(也就是把第2,3,4,5....行矩阵纷纷接入到第一行的后面)
3、用一个1*10的向量代表标签,也就是这个数字到底是几,举个例子e数字1对应的矩阵就是[0,1,0,0,0,0,0,0,0,0]
4、softmax回归预测图片是哪个数字的概率
5、用交叉熵和梯度下降法训练参数