TensorFlow GAN项目程序回顾2020.12.03

1.前言

GAN项目被我搁置一旁很久了,最近想回顾一下写过的程序,看看能不能发现一些错误或是从中得到一些新的灵感和启发。

2.程序回顾

废话不多说,直接上代码,我只挑一些比较重要的进行分析。
TensorFlow-gpu版本:1.12.0

(1)sys.path

Python在看一个模块是否存在时首先会看看自身内核中的模块,如果不存在,则就要到sys.path中去找,print(sys.path)输出的是所有路径,如果你是自定义的模块,就要通过sys.path.append加入模块所在的目录即可,否则import的时候会报错。

(2)tf.set_random_seed(seed)

如果在一个tf.Graph中加入tf.set_random_seed(seed)会让使用tf的变量(tf.constanttf.Variable()的初始化每次运行的时候都是相同的值),举个例子吧:

import tensorflow as tf
graph1 = tf.Graph()  # 创建一个新的计算图
with graph1.as_default():
    tf.set_random_seed(10)  # 这个随机种子将决定整个graph1下的所有变量的取值
    a = tf.random_normal([5])

    with tf.Session() as sess1:
        print(sess1.run(a))
        print(sess1.run(a))

    with tf.Session() as sess2:
        print(sess2.run(a))
        print(sess2.run(a))

结论:在同一个graph1下的不同会话的输出值是相同的。

注意:如果存在with, tf.set_random_seed(10)(计算图级)必须放放在with中,否则将没有作用,因为不是全局设置的语句!或者在变量中去定义seed:a = tf.random_normal([5], seed=1)也就是设置操作级的随机数。

下面,我再举例说明.as_default()的用法:

import tensorflow as tf
a = tf.constant(1.0)  # 常数
print(a.graph)
graph1 = tf.Graph()  # 创建一个新的计算图
print(a.graph is tf.get_default_graph())  # 输出True,说明创建a的时候就已经创建了一个graph了
print(a.graph is graph1)  # 输出False

with graph1.as_default() as graph:  # 一共有两个图了,指定graph1为默认的图
    print(a.graph is graph1)  # 输出False,因为图不一样
    b = tf.Variable(tf.constant(2.0))  # 变量
    print(b.graph is graph1)  # 输出True,因为图一样
    init = tf.global_variables_initializer()  # 因为有Variable
    with tf.Session().as_default() as sess:  # .as_default()保证了当前会话在with结束后不关闭
       sess.run(init)  # 必须单独写
       print(sess.run([b]))  # run是有顺序的
       # sess.close()  # 加上这句,即使有.as_default(),后面也会报错的
    print(b.eval(session=sess))  # 必须要指定sess

结论:
① Variable的init必须是单独的
② run可以多个变量一起,必须用[]或者()
③ eval一次只能对一个变量进行输出
④ 在一个session下要run两个变量,如果两个变量是在不同的graph中,则会报错
例如:print(sess.run([b,a]))
⑤ 当一个变量被创建的时候,就会生成一个默认的图,一个线程中可以画很多图,但是默认的图只有一个

(3)tf.variable_scope(name)tf.name_scope(name)

tf.variable_scope(name)域下的变量可以使用tf.get_variable()来定义,也可以用tf.Variable()来定义;而tf.name_scope(name)仅能用tf.Variable()来定义,具体操作见下:

>>> with tf.variable_scope('vars'):
...     var1 = tf.get_variable(name='var1', shape=[1, 2])
... 
>>> var1.name
'vars/var1:0'
# 下面如果要再在vars域下面声明一个名叫var1的变量是会报错误的,如果是要做变量共享,则可以继续这样做:
>>> with tf.variable_scope('vars', reuse=True):
...     var2 = tf.get_variable(name='var1', shape=[1, 2])
... 
>>> var2.name
'vars/var1:0'
# 从上面可以看出,变量的生成只与name有关,需要注意的是当变量name相同时,你的shape也要一致;name_scope是不能使用reuse的,但是允许在同一个域下使用相同name的变量,因为会自动为这些变量增加序号哦:
>>> with tf.name_scope('vars'):
...     var1 = tf.Variable(1, name='var1')
... 
>>> var1.name
'vars_5/var1:0'
>>> with tf.name_scope('vars'):
...     var2 = tf.Variable(1, name='var1')
... 
>>> var2.name
'vars_6/var1:0'

(4)tf.contrib.layers.batch_norm()tf.nn.dropout()

批归一化在算法层面有两点需要注意:
① 训练阶段只有两个可训练的参数,β和γ,用于缩放,然后就是两个不可训练的参数,滑动平均值和方差
② 测试阶段,一般使用保存好的滑动平均值和方差即可

在实际使用批归一化的时候,却没那么简单,对于不同的深度学习框架,配置方式和难易程度差别很大,这里就只介绍TensorFlow下的实现方式:

第一步:搭建批归一化层,其中最重要的就是is_training参数,True为训练过程,False为测试过程,当然,你可以将这个层封装为一个可以直接调用的函数,也就是放在def __call__(self, x):

tf.contrib.layers.batch_norm(x, decay=self.momentum, updates_collections=None, is_training=self.trainable, epsilon=self.epsilon, scale=True, scope=self.name)

第二步,在优化器层上封装如下语句,这个是tf.contrib.layers.batch_norm硬性规定的,目的就是前面提到的批归一化参数变量

update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(update_ops):
  d_optim = tf.train.AdamOptimizer().minimize(loss, var_list=var)

对于dropout,这个与批归一化类似,在训练阶段需要设置keep_prob为0.5或者其他的<1.0的数,在测试阶段一般是不使用dropout的,因为这个是随机的过程,你不能也无法取模拟训练的过程,所以设置为1.0

需要注意的一点:在TensorFlow中只能自己通过sess.run()去设置keep_prob和is_training

================= To be continued =================

上一篇:从Java底层分析Stack(栈)的用法——源码分析系列


下一篇:Linux中date命令的各种实用方法--转载