作者:qxl 邮箱: 1183129553@qq.com
系列文章链接
一、tensorflow安装方式及问题汇总
二、TensorFlow基础概念
三、神经网络结构设计
四、mnist数字识别问题
五、图像识别与卷积神经网络
六、U-NET网络
七、TensorFlow常用指令记录
前言
此部分主要参考"Tensorflow 实战google深度学习框架”来实现mnist数字数据集的识别完整的网络实现
参考书籍1
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
#mnist数据集的相关常数
INPUT_NODE = 784
OUTPUT_NODE = 10
#配置神经网络参数
LAYER1_NODE = 500
BATCH_SIZE = 100
LEARNING_RATE_BASE = 0.8#基础学习率
LEANRING_RATE_DECAY = 0.99#衰减率
REGULARIZATION_RATE = 0.0001#描述模型负责度的正则化项在损失函数中的系数
TRAINING_STEPS = 30000#训练的轮数
MOVING_AVERAGE_DECAY = 0.99#滑动平均衰减率
def inference(input_data, avg_class, weights1, biases1, weights2, biases2):
#当没有提供滑动平均类时直接使用当前的取值
if avg_class == None:
#计算隐藏层的前向传播结果
layer1 = tf.nn.relu(tf.matmul(input_data, weights1) + biases1)
#将第二层的结果输出
return tf.matmul(layer1, weights2)+biases2
else:
#首先使用avg_class计算滑动平均值
layer1 = tf.nn.relu(tf.matmul(input_data, avg_class.average(weights1)) + avg_class.average(biases1))
return tf.matmul(layer1, avg_class.average(weights2))+ avg_class.average(biases2)
#训练模型过程
def train(mnist):
x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
# 生成隐藏层的参数。
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
# 生成输出层的参数。
weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
# 计算不含滑动平均类的前向传播结果
y = inference(x, None, weights1, biases1, weights2, biases2)
# 定义训练轮数及相关的滑动平均类
global_step = tf.Variable(0, trainable=False)
variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
variables_averages_op = variable_averages.apply(tf.trainable_variables())
average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
# 计算交叉熵及其平均值
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, 1))
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 损失函数的计算
regularizer = tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
regularaztion = regularizer(weights1) + regularizer(weights2)
loss = cross_entropy_mean + regularaztion
# 设置指数衰减的学习率。
learning_rate = tf.train.exponential_decay(
LEARNING_RATE_BASE,
global_step,
mnist.train.num_examples / BATCH_SIZE,
LEANRING_RATE_DECAY,
staircase=True)
# 优化损失函数
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
# 反向传播更新参数和更新每一个参数的滑动平均值
with tf.control_dependencies([train_step, variables_averages_op]):
train_op = tf.no_op(name='train')
# 计算正确率
correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
with tf.Session() as sess:
#.run等价于sess.run
tf.initialize_all_variables().run()
validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
test_feed = {x: mnist.test.images, y_: mnist.test.labels}
#迭代进行模型训练
for i in range(TRAINING_STEPS):
#每1000轮输出一下在测试集上的输出结果
if i%1000 == 0:
validate_acc = sess.run(accuracy, feed_dict=validate_feed)
print("After %d training steps,validation accuracy is %g", (i, validate_acc))
xs, ys = mnist.train.next_batch(BATCH_SIZE)
sess.run(train_op, feed_dict={x:xs, y_:ys})
#训练结束后,在测试数据集上输出最终的正确率
test_acc = sess.run(accuracy, feed_dict=test_feed)
print("After %d training step, test accuracy using average model is %g", (TRAINING_STEPS, test_acc))
#定义主程序入口
def main(argv=None):
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
train(mnist)
if "__main__" == __name__:
tf.app.run()
持久化原理及数据格式
TensorFlow训练后的模型可以保存为checkpoint文件或者pb文件,checkpoint文件是结构与权重分离的四个文件,便于训练。pb文件则是graph_def的序列化文件,类似于caffemodel,便于发布和离线预测。官方提供的freeze_graph.py脚本可以将ckpt文件转换为pb文件。
保存为ckpt文件
V1 = tf.Variable(10, name="V1")#tf.constant(1.0, shape=[1])
V2 = tf.Variable(10, name="V2")
result = V1 + V2
init_op = tf.initialize_all_variables()
saver = tf.train.Saver()
# print(V1)
with tf.Session() as sess:
sess.run(init_op)
print(sess.run(result))
saver.save(sess, "./path/model/model1.ckpt")
通过上述代码可以将模型进行保存,生成三个文件,分别是
- model.ckpt.meta: meta文件是MetaGraphDef序列化的二进制文件,保存了网络结构相关的数据,包括graph_def和saver_def等;2
- .ckpt.index: index文件为数据文件提供索引,存储的核心内容是以tensor name为键以BundleEntry为值的表格entries,BundleEntry主要内容是权值的类型、形状、偏移、校验和等信息。Index文件由data block/index block/Footer等组成,构建时主要涉及BundleWriter、TableBuilder、BlockBuilder几个类,除了BundleEntry的序列化,还涉及了tensor name的编码及优化(比如丢弃重复的前缀)和data block的snappy压缩。
- checkpoint: 保存断点文件列表,可以用来迅速查找最近一次的断点文件。
- data: 保存所有的变量值,即网络权重值
读取模型1
#=========读取模型1====================================
V11 = tf.Variable(1, name="V111")#tf.constant(1.0, shape=[1])
V12 = tf.Variable(1, name="V222")
result = V11 + V12
#如果定义的变量和保存代码时一致,则不需要{"V1":V11, "V2":V12}映射部分。
saver = tf.train.Saver({"V1":V11, "V2":V12})#将写入的变量名映射到新的变量定义上,如果没有改变,此处可以忽略
with tf.Session() as sess:
saver.restore(sess, "./path/model/model1.ckpt")
print(sess.run(result))
读取模型2
#=========读取模型2====================================
saver = tf.train.import_meta_graph("./path/model/model1.ckpt.meta")#读取图结构
with tf.Session() as sess:
saver.restore(sess, "./path/model/model1.ckpt")
print(sess.run(tf.get_default_graph().get_tensor_by_name("V1:0")))
print(sess.run(tf.get_default_graph().get_tensor_by_name("add:0")))
#直接恢复模型,则应该按照tensorflow内部定义的名字来调用变量;那这里必须要知道模型是怎么定义内部节点的;通过print(variable)可以输出节点名称;比如: print(result), 输出:Tensor("add:0", shape=(), dtype=int32)
滑动平均变量取值
#========滑动平均变量存储======================================
v = tf.Variable(0, dtype=tf.float32, name="v")
for variable in tf.all_variables():
print(variable.name) #v:0
ema = tf.train.ExponentialMovingAverage(0.99)
maintain_averages_op = ema.apply(tf.all_variables())#创建影子变量
for variable in tf.all_variables():#此时生成了两个变量一个是v, 一个是v/ExponentialMovingAverage
print(variable)
#<tf.Variable 'v:0' shape=() dtype=float32_ref>
#<tf.Variable 'v/ExponentialMovingAverage:0' shape=() dtype=float32_ref>
saver = tf.train.Saver()
with tf.Session() as sess:
init_op = tf.initialize_all_variables()
sess.run(init_op)
sess.run(tf.assign(v, 10))
sess.run(maintain_averages_op)
saver.save(sess, "./path/model/model2.ckpt")
print(sess.run([v, ema.average(v)]))
#[10.0, 0.099999905]
变量读取
#==================变量读取================================
v =tf.Variable(0, dtype=tf.float32, name="v")
saver = tf.train.Saver({"v/ExponentialMovingAverage": v})
#为什么是"v/ExponentialMovingAverage" 而不是"v/ExponentialMovingAverage:0"
with tf.Session() as sess:
saver.restore(sess, "./path/model/model2.ckpt")
print(sess.run(v))
使用tf.train.Saver会保存运行TensorFlow程序所需要的全部信息,然而有时候并不需要某些信息。比如在测试或者离线预测时,只需要知道如何从神经网络的输入层经过前向传播计算得到输出层即可,而不需要类似于变量初始化、模型保存等辅助节点的信息。同时,将变量取值和计算图结构分开保存有时候也不方便,于是TensorFlow提供了convert_variables_to_constants函数,通过这个函数可以将计算图中的变量及其取值通过常量的形式保存,这样可以将整个程序放到一个文件中进行保存。
pb保存
#================pb保存====================================
from tensorflow.python.framework import graph_util
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name="v1")
v2 = tf.Variable(tf.constant(2.0, shape=[2]), name="v2")
result = v1 + v2
init_op = tf.initialize_all_variables()
with tf.Session() as sess:
sess.run(init_op)
graph_def = tf.compat.v1.get_default_graph().as_graph_def()
#将图中的变量及其取值转换为常量,同时将途中不必要的节点去掉,比如变量初始化操作无关紧要,那么就没有必要导出保存了
#需要注意导出保存的是'add', 而不是'add:0',add表示需要保存的节点的名称,而"add:0"表示计算节点的第一个输出。
output_graph_def = graph_util.convert_variables_to_constants(
sess, graph_def, ['add']
)
with tf.io.gfile.GFile("./path//model/combined_model.pb", "wb") as f:
f.write(output_graph_def.SerializeToString())