Tensorflow变量管理及模型持久化,实现实现线性回归
变量管理
随着神经网络的结构更加复杂,参数更多时,需要一个更好的方式来传递和管理变量。在TF中提供了通过变量的名字来创建或者获取一个变量的机制,通过这个机制不同函数可以直接通过变量的名字来直接使用变量。这机制主要是通过tf.get_variable和tf.variable_scope实现的。
tf.get_variable
当tf.get_variable用来创建变量时,其功能与tf.Variable是等价的。比如:
v = tf.get_variable('v', shape=[1], initializer=tf.constant_initializer(1.0)) v = tf.Variable(tf.constant(1.0, shape=[1], name='v'))
其中
tf.constant(value,dtype=None,shape=None,name='Const')。创建一个常量tensor,按照给出value来赋值,可以用shape来指定其形状。value可以时一个数,也可以是一个list。如果是一个数,那么这个常量中所有值按该数来赋值。如果是list,那么len(value)一定要小于等于shape展开后的长度。赋值时,先将value中的值逐个存入。不够的部分,则全部存入value的最后一个值。
tf.Variable和tf.get_variable最大的区别在于指定变量名称的参数。tf.Variable的变量名称是一个可选的参数,通过name='v'给出。但是tf.get_variable的变量名称是一个必填的参数,tf.get_variable会根据这个名字去创建参数,因此不能创建一个已经存在的名字,否则就会报错。
tf.variable_scope
如果需要通过tf.get_variable来获取一个已经创建的变量,则需要通过tf.variable_scope来生成一个上下文管理器来控制tf.get_variable函数获取已创建的变量:
#在名字为foo的命名空间中创建名字为v的变量 with tf.variable_scope('foo'): v = tf.get_variable('v',[1],initializer=tf.constant_initializer(1.0)) #由于已经存在v,所以以下代码将会报错 with tf.variable_scope('foo'): v = tf.get_variable('v',[1]) #Variable foo/v already exists, disallowed. Did you mean to set reuse=True in VarScope? #在生成上下文管理器时,将reuse设置为True,即可获取变量 with tf.variable_scope('foo', reuse=True): v = tf.get_variable('v',[1])
tf.variable_scope函数会创建一个TF的命名空间,在命名空间中创建的变量名称都会带上这个命名空间作为前缀,所以这个函数也提供了一个管理变量命名空间的方式:
v1= tf.get_variable('v', [1]) print(v1.name) #v:0 #0表示是这个变量运算的第一个结果 with tf.variable_scope('foo'): v2 = tf.get_variable('v', [1]) print(v2.name) #foo/v:0 with tf.variable_scope('foo'): with tf.variable_scope('bar'): v3 = tf.get_variable('v', [1]) print(v3.name) #foo/bar/v:0
tf.variable_scope的参数reuse=True时,tf.get_variable将直接且只能获取已经存在的变量,若变量不存在,就会报错。
with tf.variable_scope('foo'): v = tf.get_variable('v', [1]) print(v.name) #foo/v:0 with tf.variable_scope('foo',reuse=True): v1 = tf.get_variable('v', [1]) print(v = v1)#因为之前已经创建了foo/v 所以v=v1 #True v1 = tf.get_variable('v1', [1]) # 这句会报错,因为并没有叫v1的变量 with tf.variable_scope('',reuse=True): v2 = tf.get_variable('foo/v', [1]) #另外也可以直接在一个空的空间直接获取其他空间的变量 print(v2 = v) #因为之前已经创建了foo/v 所以v=v2
TensorFlow的7种不同的初始化函数
使用变量管理对TF模型进行改进,这种方法可以提高复杂程序的可读性:
def inference(input_tensor,avg_class,regularizer): if avg_class==None: with tf.variable_scope('layer1'): weights = tf.get_variable('weights', [INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)) biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0)) layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases) with tf.variable_scope('layer2'): weights = tf.get_variable('weights', [LAYER1_NODE, OUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)) biases = tf.get_variable("biases", [OUT_NODE], initializer=tf.constant_initializer(0.0)) layer2 = tf.matmul(layer1, weights) + biases return layer2 else: with tf.variable_scope('layer1',reuse=True): weights = tf.get_variable('weights', [INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)) biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0)) layer1 = tf.nn.relu(tf.matmul(input_tensor,avg_class.average(weights))+avg_class.average(biases)) with tf.variable_scope('layer2',reuse=True): weights = tf.get_variable('weights', [LAYER1_NODE, OUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1)) biases = tf.get_variable("biases", [OUT_NODE], initializer=tf.constant_initializer(0.0)) layer2 = tf.matmul(layer1,avg_class.average(weights))+avg_class.average(biases) return layer2 x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input') y = inference(x) #若需要使用训练好的神经网络进行推导时 new_x = ... new_y = inference(new_x, True)
模型持久化
TF提供了一个简单的API来保存和还原一个神经网络模型。这个API就是tf.train.Saver类。
保存模型
下面即为保存TensorFlow计算图的方法(tf.train.Saver.save())
import tensorflow as tf v1 = tf.Variable(tf.constant(1.0, shape=[1], name='v1')) v2 = tf.Variable(tf.constant(1.0, shape=[1], name='v2')) result = v1 + v2 #声明tf.train.Saver类用于保存模型 saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.initialize_all_variables()) #将模型保存到model/model.ckpt文件 saver.save(sess, 'model/model.ckpt')
输出
这样就实现了持久化一个简单的TF模型的功能,通过saver.save函数将TF模型保存到model.ckpt中。虽然只指定了一个文件路劲,但是在该路径下会出现三个文件:
model.ckpt.meta:保存了TF计算图的结构
model.ckpt :保存了TF程序种每个变量的取值
checkpoint:保存了一个目录下所有的模型文件列表
读取模型
在保存了TF模型后,下面是使用saver.restore()加载该模型:
import tensorflow as tf v1 = tf.Variable(tf.constant(1.0, shape=[1], name='v1')) v2 = tf.Variable(tf.constant(1.0, shape=[1], name='v2')) result = v1 + v2 #声明tf.train.Saver saver = tf.train.Saver() with tf.Session() as sess: #加载已经保存的模型,并通过已经保存的模型中的变量来计算 saver.restore(sess, ''model/model.ckpt'') print(sess.run(result))
注意加载模型的代码没有初始化变量,而是通过已经保存的模型加载进来。如果不想重新定义模型的结构,也可以直接将模型的结构加载出来:
import tensorflow as tf #加载模型结构 saver = tf.train.import_meta_graph('model/model.ckpt.meta') with tf.Session() as sess: saver.restore(sess, 'model/model.ckpt') #通过张量的名称来获取张量 print(sess.run(tf.get_default_graph().get_tensor_by_name('add:0'))) #[2.]
为了保存和加载部分变量,在声明tf.train.Saver类时可以提供一个列表来指定保存或者加载的变量,如tf.train.Saver([v1]),这时就只有变量v1会被加载进来。除了可以指定被加载的变量,tf.train.Saver类也支持在保存或加载时给变量重命名:
import tensorflow as tf v1 = tf.Variable(tf.constant(2.0, shape=[1], name='other-v1')) v2 = tf.Variable(tf.constant(3.0, shape=[1], name='other-v2')) #这里如果直接使用tf.train.Saver()来加载模型会报错 #使用一个字典来重命名变量就可以加载原来的模型了 #这个字典指定了原名称为v1的变量现在加到v1变量中 saver = tf.train.Saver({'v1': v1, 'v2': v2})
实现线性回归
线性回归原理
根据数据建立回归模型,w1x1+w2x2+...+b=y,通过真实值与预测值之间建立误差,使用梯度下降优化得到损失最小对应的权重和偏置。最终确定模型的权重和偏置参数。最后可以用这些参数进行预测。
1.构建模型
y = w1x1+w2x2+...+b
2.构造损失函数
均方误差(MSE)
3.优化损失
梯度下降
准备真实的数据
100个样本
x特征值 形状(100,1)(指的是一百行一列-形状)
y_true目标值 (100,1)
y_true = 0.8x + 0.7
假定x和y之间的关系满足:y = kx +b (k ≈ 0.8,b ≈ 0.7)
流程分析:
(100,1)*(1,1) = (100,1) 【其中*指的是矩阵乘法 -matmul】
y_predict = x * weights(1,1) + bias(1,1)
y_predict = tf.matmul(x,weights) + bias
API
运算:
矩阵运算:tf.matmul(x,w)
平方 :tf.square(error)
均值 :tf.reduce_mean(error)
梯度下降优化:
tf.train.GradientDescentOptimizer(learning_rate)
[tf.compat.v1.train.GradientDescentOptimizer]
梯度下降优化
learning_rate:学习率,一般为0-1之间比较小的值
method:minimize(lose)
return :梯度下降op
1.构建模型
y_predict = tf.matmul(x,weights) + bias
2.构建损失函数
error = tf.reduce_mean(tf.square(y_predict-y_true))
3.优化损失
optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate=0.01).minimize(error)
增加其他功能
1.变量tensorboard显示
2.增加命名空间
3.模型保存与加载
4.命令行参数设置
1.增加变量显示
目的:在tensorboard当中观察模型的参数,损失值等变量值的变化
1)收集变量
1.tf.summary.scalar(name=",tensor) 收集对于损失函数和准确率等单值变量,name为变量的名字,tensor为值;
2.tf.summary.histogram(name='',tensor)收集高维度的变量参数-直方图(二维)
3.tf.summary.image(name='',tensor)收集输入的图片张量能显示图片-图片(三维)
2)合并变量写入事件文件
1.merged = tf.summary.merge_all()
2.运行合并:summary = sess.run(merged) 每次迭代都需运行
3.添加:FileWriter.add_summary(summary,i) i表示第几次的值
# 1——创建事件文件 file_writer = tf.compat.v1.summary.FileWriter("./tmp/linear", graph=sess.graph) # 2——收集变量 tf.compat.v1.summary.scalar("error", error) tf.compat.v1.summary.histogram("weights", weights) tf.compat.v1.summary.histogram("bias", bias) # 3——合并变量 merged = tf.compat.v1.summary.merge_all() #4——运行合并变量操作 summary = sess.run(merged) #5——将每次迭代后的变量写入事件文件 file_writer.add_summary(summary, i)
2.增加命名空间
使得代码结构更加清晰,tensorboard图结构清楚
3.模型的保存与加载
tf.train.Saver(var_list=None,max_to_keep=5)
保存和加载模型 (格式:checkpoint文件--ckpt)
var_list:指定将要保存和还原的变量。它可以作为一个dict或一个列表传递
max_to_keep:指示要保留的最近检查点文件的最大数量。创建新文件时,会删除较旧的文件,如果无或0,则保留所有检查点文件。默认为5(即保留最新的5个检查点文件)
#保存模型 if i % 10 == 0: saver.save(sess, "./tmp/model/my_linear.ckpt") #加载模型 if os.path.exists("./tmp/model/checkpoint"): saver.restore(sess, "./tmp/model/my_linear.ckpt") print("训练前模型参数为:权重%f, 偏置%f, 损失为%f" % (weights.eval(), bias.eval(), error.eval())
4.学习率的设置,步数设置与梯度爆炸
学习率愈大,训练到较好结果的步数越小;学习率越小,训练到较好结果的步数越大。
但是学习过大会出现梯度爆炸现象。
梯度爆炸:
在极端情况下,权重的值变得非常大,以至于溢出,导致NaN值
如何解决梯度爆炸问题?
1.重新设计网络
2.调整学习率
3.使用梯度截断(在训练过程中检查和限制梯度的大小)
4.使用激活函数
5.变量的trainable设置观察
trainable的参数作用,指定是否训练
代码:
def linear_regression(): """ 自实现一个线性回归 """ with tf.compat.v1.variable_scope("prepare_data"): # 1) 准备数据 X = tf.compat.v1.random_normal(shape=[100, 1], name="feature") y_true = tf.matmul(X, [[0.8]]) + 0.7 with tf.compat.v1.variable_scope("create_model"): # 2)构造模型 #定义模型参数 用变量 weights = tf.Variable(initial_value=tf.compat.v1.random_normal(shape=[1, 1]), name="Weights") bias = tf.Variable(initial_value=tf.compat.v1.random_normal(shape=[1, 1]), name="Bias") y_predict = tf.matmul(X, weights) + bias with tf.compat.v1.variable_scope("loss_function"): # 3)构造损失函数(MSE-均方误差) error = tf.reduce_mean(tf.square(y_predict - y_true)) with tf.compat.v1.variable_scope("optimizer"): # 4)优化损失 optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate=0.01).minimize(error) # 2——收集变量 tf.compat.v1.summary.scalar("error", error) tf.compat.v1.summary.histogram("weights", weights) tf.compat.v1.summary.histogram("bias", bias) # 3——合并变量 merged = tf.compat.v1.summary.merge_all() #创建Saver对象 saver = tf.compat.v1.train.Saver() # 显式地初始化变量 init = tf.compat.v1.global_variables_initializer() # 开启会话 with tf.compat.v1.Session() as sess: # 初始化变量 sess.run(init) # 1——创建事件文件 file_writer = tf.compat.v1.summary.FileWriter("./tmp/linear", graph=sess.graph) # 查看初始化模型参数之后的值 print("训练前模型参数为:权重%f, 偏置%f, 损失为%f" % (weights.eval(), bias.eval(), error.eval())) # 开始训练 for i in range(100): sess.run(optimizer) print("训练前模型参数为:权重%f, 偏置%f, 损失为%f" % (weights.eval(), bias.eval(), error.eval())) #4——运行合并变量操作 summary = sess.run(merged) #5——将每次迭代后的变量写入事件文件 file_writer.add_summary(summary, i) #保存模型 if i % 10 == 0: saver.save(sess, "./tmp/model/my_linear.ckpt") #加载模型 if os.path.exists("./tmp/model/checkpoint"): saver.restore(sess, "./tmp/model/my_linear.ckpt") print("训练前模型参数为:权重%f, 偏置%f, 损失为%f" % (weights.eval(), bias.eval(), error.eval())) return None