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

 

posted @ 2023-05-19 15:15  sqsq  阅读(15)  评论(0编辑  收藏  举报