TensorFlow学习笔记之快速求解四元一次方程的完整代码以及保存模型和读取模型的讲解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/TeFuirnever/article/details/100517618

〇、写在前面

在写 PaddlePaddle炼丹初体验以及paddlepaddle与TensorFlow求解四元一次方程的代码比较 这个博客时,发现自己已经好久没认真写过TensorFlow的程序了,训练和测试的保存模型和读取模型代码出现的各种bug竟然是一脸懵逼,所以简单记录一下遇到的问题。

一、使用说明

本博客是借助于一个例子——快速求解四元一次方程,进行TensorFlow框架下的函数训练和测试的讲解,主要是保存模型和读取模型!!!可以作为一个用户手册来学习,因为TensorFlow的代码有固定的套路,所以只要掌握了关键点,就可以很容易搭建,最重要的是,先看完整个文章 😃

二、训练代码

我们主要是想编写一个程序进行快速求解四元一次方程,基于TensorFlow的代码如下:

# 训练代码
import tensorflow as tf
import numpy as np
import time

start = time.clock()

# 生成数据
np.random.seed(0)
outputs = np.random.randint(5, size=(10, 4))
res = []
for i in range(10):
    # 假设方程式为 y=4a+6b+7c+2d
    y = 4 * outputs[i][0] + 6 * outputs[i][1] + \
        7 * outputs[i][2] + 2 * outputs[i][3]
    res.append([y])

# 定义数据
train_data = np.array(outputs).astype('float32')
y_true = np.array(res).astype('float32')

# 占位符
x = tf.placeholder(tf.float32, shape=[None, 4], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')

# w 是要求的各个参数的权重,是目标输出,对应 t_w
w = tf.Variable(np.ones(4, dtype=np.float32).reshape((4, 1)), tf.float32)

# 实际输出数据
y_ = tf.matmul(x, w)

# 定义损失函数,均方误差
loss = tf.reduce_mean(tf.square(y - y_))

# 定义一个梯度下降算法来进行训练的优化器
optimizer = tf.train.GradientDescentOptimizer(0.05)

# 最小化代价函数
train = optimizer.minimize(loss)

# 初始化变量
init = tf.global_variables_initializer()

# 开始训练
with tf.Session() as sess:
    sess.run(init)
    for step in range(500):
        _, curr_loss = sess.run([train,loss], feed_dict={x: train_data, y: y_true})
        if step % 50 == 0:
            print("运行%d 次,loss=%s" % (step,curr_loss))
            #用saver 保存模型
            saver = tf.train.Saver()
            saver.save(sess, "model_data/model")
            elapsed = (time.clock() - start)
			print("Training Time used:",elapsed)

运行结果如下:
在这里插入图片描述
在这里插入图片描述


1、代码讲解:计算运行时间

import time

start = time.clock()
......
......
elapsed = (time.clock() - start)
print("Training Time used:",elapsed)

中间穿插的这个程序是一个计算运行时间的程序,在省略号的位置放置想要测试的程序即可。首先引入time库,然后记录初始时间以及最后的时间,最后通过做差的方法求解程序的运行时间。


2、代码讲解:占位符

# 占位符
x = tf.placeholder(tf.float32, shape=[None, 4], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')

通过占位符的方式定义两个变量,作为网络的输入x,和网络的输出y。简单地介绍一下函数的用法,详细地可以看一下这个博客——tf.placeholder()函数解析(最清晰的解释)

tf.placeholder(dtype, shape=None, name=None)

参数:

  • dtype:数据类型。常用的是tf.float32,tf.float64等数值类型
  • shape:数据形状。默认是None,就是一维值,也可以是多维(比如[2,3], [None, 3]表示列是3,行不定)
  • name:名称

这里重点说一下函数的shape参数,为什么要设置成 [None,**] 的形式,我们的训练输入是10组方程的数据,每一组4个数,一个 label,所以输入数据的 shape[10, 4],而后面测试的时候,输入数据的shape是 [1,4],所以如果设置成固定的大小的话,会出现不同的shape相乘出现错误的情况。但是 None 可以随着程序而相应地改变。


3、代码讲解:网络结构搭建

# w 是要求的各个参数的权重,是目标输出,对应 t_w
w = tf.Variable(np.ones(4, dtype=np.float32).reshape((4, 1)), tf.float32)

# 实际输出数据
y_ = tf.matmul(x, w)

这里面的w是网络的权重,也是我们初始化之后的参数,这会影响网络的最优化;而因为y=x*w,所以根据线性代数的要求,x的列数应该等于w的行数,不然会报错误。网络的输出是 y_,通过TensorFlow的矩阵乘法 tf.matmul,进行简单的网络结构搭建。


4、代码讲解:损失函数和优化器

# 定义损失函数,均方误差
loss = tf.reduce_mean(tf.square(y - y_))

# 定义一个梯度下降算法来进行训练的优化器
optimizer = tf.train.GradientDescentOptimizer(0.05)

# 最小化代价函数
train = optimizer.minimize(loss)

定义一下损失函数,使用均方误差函数,之后准备写一个损失函数的总结博客,之后再说;然后定义一下梯度下降算法优化器,这里面使用的 GradientDescentOptimizer,因为在之前的那个paddlepaddle中使用的 SGD DescentOptimizer,而在TensorFlow中没有找到这个函数,而是梯度下降法,也就是 GradientDescentOptimizer,详见这个博客——;最后最小化代价函数,这都是固定的套路了。


5、代码讲解:开始训练

# 初始化变量
init = tf.global_variables_initializer()

# 开始训练
with tf.Session() as sess:
    sess.run(init)
    for step in range(500):
        _, curr_loss = sess.run([train,loss], feed_dict={x: train_data, y: y_true})
        if step % 50 == 0:
            print("运行%d 次,loss=%s" % (step,curr_loss))

一定要进行变量的初始化 init = tf.global_variables_initializer(),不然程序会出现问题,然后在 sess.run(init),这样就完成前期准备工作了。

本次程序一共进行500step,然后每50step打印一次损失。

注意,_, curr_loss = sess.run([train,loss], feed_dict={x: train_data, y: y_true}) 这句代码中的 train 是必须的,正是我们在第四步中的优化器,也就是说如果这个位置没有 train 的话,整个反向传播过程就相当于没有了!!!同时在 feed_dict 中喂入训练数据 train_data 和标记 y_true,这样就完成了学习和迭代优化的过程。


6、代码讲解:保存模型

#用saver 保存模型
saver = tf.train.Saver()
saver.save(sess, "model_data/model")

tensorflow 提供了 tf.train.Saver 类来保存模型,值得注意的是,在tensorflow中,变量是存在于Session环境中,也就是说,只有在Session环境下才会存有变量值,因此,保存模型时需要传入session。

如果我们不对 tf.train.Saver 指定任何参数,默认会保存所有变量。如果你不想保存所有变量,而只保存一部分变量,可以通过指定 variables/collections。在创建 tf.train.Saver 实例时,通过将需要保存的变量构造list或者dictionary,传入到Saver中。如果你希望每2小时保存一次模型,并且只保存最近的5个模型文件:

tf.train.Saver(max_to_keep=5, keep_checkpoint_every_n_hours=2)

注意:tensorflow默认只会保存最近的5个模型文件,如果你希望保存更多,可以通过 max_to_keep 来指定。

三、测试代码

# 测试代码
import tensorflow as tf
import numpy as np

def main():
    sess = tf.InteractiveSession()
    # 加载模型
    saver = tf.train.import_meta_graph('model_data/model.meta')
    saver.restore(sess, 'model_data/model')
    graph = tf.get_default_graph()
	
	# 得到当前图中所有变量的名称
	# tensor_name_list = [tensor.name for tensor in graph.as_graph_def().node]
    # print(tensor_name_list)
    
	x = graph.get_tensor_by_name('x:0')
    pre_result = graph.get_tensor_by_name("MatMul:0")
    
    # 生成测试数据
    test = np.array([[9,5,2,10]]).astype('float32')

	# 进行预测
    pre_reload_out = sess.run(pre_result, feed_dict={x: test})
    print("9a+5b+2c+10d={}".format(pre_reload_out))

    # 关闭会话
    sess.close()

if __name__ == '__main__':
    main()

在这里插入图片描述
在这里插入图片描述


1、代码讲解:读取模型

sess = tf.InteractiveSession()
# 加载模型
saver = tf.train.import_meta_graph('model_data/model.meta')
saver.restore(sess, 'model_data/model')
graph = tf.get_default_graph()

首先是通过 saver 进行读取模型,这里就要先说一下模型的文件了,我们在checkpoint_dir目录下保存的文件结构如下:

|--checkpoint_dir
|    |--checkpoint
|    |--MyModel.meta
|    |--MyModel.data-00000-of-00001
|    |--MyModel.index

在这里插入图片描述

  1. meta文件

model.meta文件保存的是图结构,meta文件是pb(protocol buffer)格式文件,包含变量、op、集合等。

  1. ckpt文件

ckpt文件是二进制文件,保存了所有的weights、biases、gradients等变量。在tensorflow 0.11之前,保存在 .ckpt 文件中。0.11后,通过两个文件保存,如:

model.data-00000-of-00001
model.index
  1. checkpoint文件

checkpoint_dir目录下还有checkpoint文件,该文件是个文本文件,里面记录了保存的最新的checkpoint文件以及其它checkpoint文件列表。在inference时,可以通过修改这个文件,指定使用哪个model


2、代码讲解:变量名获取

#得到当前图中所有变量的名称
tensor_name_list = [tensor.name for tensor in graph.as_graph_def().node]
print(tensor_name_list)

运行结果如下:

['x', 'y', 'Variable/initial_value', 'Variable', 'Variable/Assign', 'Variable/read', 'MatMul', 'sub', 
'Square', 'Const', 'Mean', 'gradients/Shape'......]

可以看到这些节点变量名,选择我们需要的,一个是输入,也就’x’;一个是输出,根据 y_ = tf.matmul(x, w),也就是’MatMul’。


3、代码讲解:加载参数

x = graph.get_tensor_by_name('x:0')
pre_result = graph.get_tensor_by_name("MatMul:0")

我们希望使用已经训练好的模型。这时候,需要获取训练好的模型中的一些中间结果值,可以通过 graph.get_tensor_by_name() 来获取,注意括号里面的是tensor的name,也就是节点变量名。


4、代码讲解:预测结果

# 进行预测
pre_reload_out = sess.run(pre_result, feed_dict={x: test})

通过对训练好的模型,feed_dict 一个测试数据,就可以得到预测结果了!!!

总结

到这里讲解就完事了,你懂了嘛?总体来说,TensorFlow的代码还是比较容易理解的,加油!

文章创建于: 2019-09-03 23:21:11
展开阅读全文

没有更多推荐了,返回首页