简体   繁体   English

TensorFlow 2.0:如何使用tf.keras对图表进行分组? tf.name_scope / tf.variable_scope不再使用了?

[英]TensorFlow 2.0: how to group graph using tf.keras? tf.name_scope/tf.variable_scope not used anymore?

Back in TensorFlow < 2.0 we used to define layers, especially more complex setups like inception modules for example, by grouping them with tf.name_scope or tf.variable_scope . 回到TensorFlow <2.0,我们用于定义层,尤其是更复杂的设置,例如初始模块 ,通过将它们与tf.name_scopetf.variable_scope分组。

Utilizing these operators we were able to conveniently structure the compute graph, which results in TensorBoard's graph view being interpretable much easier. 利用这些运算符,我们能够方便地构建计算图,这使得TensorBoard的图表视图可以更容易解释。

Just one example for the structured groups: 结构化组只是一个例子: 在此输入图像描述

This comes in very handy for debugging complex architectures. 这对于调试复杂的体系结构非常方便。

Unfortunately, tf.keras seems to ignore tf.name_scope and tf.variable_scope is gone in TensorFlow >= 2.0. 不幸的是, tf.keras似乎忽略了tf.name_scope并且tf.variable_scope在TensorFlow> = 2.0中消失了。 Thus, a solution like this... 因此,像这样的解决方案......

with tf.variable_scope("foo"):
    with tf.variable_scope("bar"):
        v = tf.get_variable("v", [1])
        assert v.name == "foo/bar/v:0"

...is not available anymore. ...不再可用。 Is there any replacement? 有替代品吗?

How can we group layers and entire models in TensorFlow >= 2.0? 我们如何在TensorFlow> = 2.0中对图层和整个模型进行分组? If we do not group layers, tf.keras is creating a big mess for complex models by just placing everything serially in the graph view. 如果我们不对图层进行分组, tf.keras只需将所有内容串行放置在图表视图中, tf.keras就会为复杂模型创建一个大混乱。

Is there a replacement for tf.variable_scope ? 是否有替换tf.variable_scope I could not find any so far, but made heavy use of the method in TensorFlow < 2.0. 到目前为止我找不到任何东西,但在TensorFlow <2.0中大量使用了该方法。


EDIT : I have now implemented an example for TensorFlow 2.0 . 编辑 :我现在已经为TensorFlow 2.0实现了一个示例。 This is a simple GAN implemented using tf.keras : 这是使用tf.keras实现的简单GAN:

# Generator
G_inputs = tk.Input(shape=(100,), name=f"G_inputs")

x = tk.layers.Dense(7 * 7 * 16)(G_inputs)
x = tf.nn.leaky_relu(x)
x = tk.layers.Flatten()(x)
x = tk.layers.Reshape((7, 7, 16))(x)

x = tk.layers.Conv2DTranspose(32, (3, 3), padding="same")(x)
x = tk.layers.BatchNormalization()(x)
x = tf.nn.leaky_relu(x)
x = tf.image.resize(x, (14, 14))

x = tk.layers.Conv2DTranspose(32, (3, 3), padding="same")(x)
x = tk.layers.BatchNormalization()(x)
x = tf.nn.leaky_relu(x)
x = tf.image.resize(x, (28, 28))

x = tk.layers.Conv2DTranspose(32, (3, 3), padding="same")(x)
x = tk.layers.BatchNormalization()(x)
x = tf.nn.leaky_relu(x)

x = tk.layers.Conv2DTranspose(1, (3, 3), padding="same")(x)
x = tf.nn.sigmoid(x)

G_model = tk.Model(inputs=G_inputs,
                   outputs=x,
                   name="G")
G_model.summary()

# Discriminator
D_inputs = tk.Input(shape=(28, 28, 1), name=f"D_inputs")

x = tk.layers.Conv2D(32, (3, 3), padding="same")(D_inputs)
x = tf.nn.leaky_relu(x)
x = tk.layers.MaxPooling2D((2, 2))(x)
x = tk.layers.Conv2D(32, (3, 3), padding="same")(x)
x = tf.nn.leaky_relu(x)
x = tk.layers.MaxPooling2D((2, 2))(x)
x = tk.layers.Conv2D(64, (3, 3), padding="same")(x)
x = tf.nn.leaky_relu(x)

x = tk.layers.Flatten()(x)

x = tk.layers.Dense(128)(x)
x = tf.nn.sigmoid(x)
x = tk.layers.Dense(64)(x)
x = tf.nn.sigmoid(x)
x = tk.layers.Dense(1)(x)
x = tf.nn.sigmoid(x)

D_model = tk.Model(inputs=D_inputs,
                   outputs=x,
                   name="D")

D_model.compile(optimizer=tk.optimizers.Adam(learning_rate=1e-5, beta_1=0.5, name="Adam_D"),
                loss="binary_crossentropy")
D_model.summary()

GAN = tk.Sequential()
GAN.add(G_model)
GAN.add(D_model)
GAN.compile(optimizer=tk.optimizers.Adam(learning_rate=1e-5, beta_1=0.5, name="Adam_GAN"),
            loss="binary_crossentropy")

tb = tk.callbacks.TensorBoard(log_dir="./tb_tf2.0", write_graph=True)

# dummy data
noise = np.random.rand(100, 100).astype(np.float32)
target = np.ones(shape=(100, 1), dtype=np.float32)

GAN.fit(x=noise,
        y=target,
        callbacks=[tb])

The graph in TensorBoard of these models looks like this . TensorBoard中这些模型的图形如下所示 The layers are just a complete mess and also the models "G" and "D" (on the right side) covers some mess. 层次只是一个完整的混乱,模型“G”和“D”(在右侧)涵盖了一些混乱。 "GAN" is completely missing. “GAN”完全缺失。 The training operation "Adam" cannot be opened properly: too many layers just plotted from left to right and arrows all over the place. 无法正确打开训练操作“Adam”:从左到右绘制了太多层,并在整个地方绘制了箭头。 Very hard to check the correctness of your GAN this way. 很难通过这种方式检查GAN的正确性。


Althought a TensorFlow 1.X implementation of the same GAN covers lots of "boilerplate code"... 虽然同样的GAN的TensorFlow 1.X实现涵盖了许多“样板代码”......

# Generator
Z = tf.placeholder(tf.float32, shape=[None, 100], name="Z")


def model_G(inputs, reuse=False):
    with tf.variable_scope("G", reuse=reuse):
        x = tf.layers.dense(inputs, 7 * 7 * 16)
        x = tf.nn.leaky_relu(x)
        x = tf.reshape(x, (-1, 7, 7, 16))

        x = tf.layers.conv2d_transpose(x, 32, (3, 3), padding="same")
        x = tf.layers.batch_normalization(x)
        x = tf.nn.leaky_relu(x)
        x = tf.image.resize_images(x, (14, 14))

        x = tf.layers.conv2d_transpose(x, 32, (3, 3), padding="same")
        x = tf.layers.batch_normalization(x)
        x = tf.nn.leaky_relu(x)
        x = tf.image.resize_images(x, (28, 28))

        x = tf.layers.conv2d_transpose(x, 32, (3, 3), padding="same")
        x = tf.layers.batch_normalization(x)
        x = tf.nn.leaky_relu(x)

        x = tf.layers.conv2d_transpose(x, 1, (3, 3), padding="same")
        G_logits = x
        G_out = tf.nn.sigmoid(x)

    return G_logits, G_out


# Discriminator
D_in = tf.placeholder(tf.float32, shape=[None, 28, 28, 1], name="D_in")


def model_D(inputs, reuse=False):
    with tf.variable_scope("D", reuse=reuse):
        with tf.variable_scope("conv"):
            x = tf.layers.conv2d(inputs, 32, (3, 3), padding="same")
            x = tf.nn.leaky_relu(x)
            x = tf.layers.max_pooling2d(x, (2, 2), (2, 2))
            x = tf.layers.conv2d(x, 32, (3, 3), padding="same")
            x = tf.nn.leaky_relu(x)
            x = tf.layers.max_pooling2d(x, (2, 2), (2, 2))
            x = tf.layers.conv2d(x, 64, (3, 3), padding="same")
            x = tf.nn.leaky_relu(x)

        with tf.variable_scope("dense"):
            x = tf.reshape(x, (-1, 7 * 7 * 64))

            x = tf.layers.dense(x, 128)
            x = tf.nn.sigmoid(x)
            x = tf.layers.dense(x, 64)
            x = tf.nn.sigmoid(x)
            x = tf.layers.dense(x, 1)
            D_logits = x
            D_out = tf.nn.sigmoid(x)

    return D_logits, D_out

# models
G_logits, G_out = model_G(Z)
D_logits, D_out = model_D(D_in)
GAN_logits, GAN_out = model_D(G_out, reuse=True)

# losses
target = tf.placeholder(tf.float32, shape=[None, 1], name="target")
d_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=D_logits, labels=target))
gan_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=GAN_logits, labels=target))

# train ops
train_d = tf.train.AdamOptimizer(learning_rate=1e-5, name="AdamD") \
    .minimize(d_loss, var_list=tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope="D"))
train_gan = tf.train.AdamOptimizer(learning_rate=1e-5, name="AdamGAN") \
    .minimize(gan_loss, var_list=tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope="G"))

# dummy data
dat_noise = np.random.rand(100, 100).astype(np.float32)
dat_target = np.ones(shape=(100, 1), dtype=np.float32)

sess = tf.Session()
tf_init = tf.global_variables_initializer()
sess.run(tf_init)

# merged = tf.summary.merge_all()
writer = tf.summary.FileWriter("./tb_tf1.0", sess.graph)

ret = sess.run([gan_loss, train_gan], feed_dict={Z: dat_noise, target: dat_target})

...the resulting TensorBoard graph looks considerably cleaner. ...生成的TensorBoard图表看起来更清晰。 Notice how clean "AdamD" and "AdamGAN" scopes are on the top right. 请注意清洁的“AdamD”和“AdamGAN”范围是如何在右上方。 You can directly check that your optimizers are attached to the right scopes / gradients. 您可以直接检查优化器是否附加到正确的范围/梯度。

According to the community RFC Variables in TensorFlow 2.0 : 根据TensorFlow 2.0中的社区RFC 变量

  • to control variable naming users can use tf.name_scope + tf.Variable 控制变量命名的用户可以使用tf.name_scope + tf.Variable

Indeed, tf.name_scope still exists in TensorFlow 2.0, so you can just do: 实际上, tf.name_scope仍然存在于TensorFlow 2.0中,因此您可以这样做:

with tf.name_scope("foo"):
    with tf.name_scope("bar"):
        v = tf.Variable([0], dtype=tf.float32, name="v")
        assert v.name == "foo/bar/v:0"

Also, as the point above that states: 此外,如上所述:

  • the tf 1.0 version of variable_scope and get_variable will be left in tf.compat.v1 var_scope和get_variable的tf 1.0版本将保留在tf.compat.v1中

So you can just fall back to tf.compat.v1.variable_scope and tf.compat.v1.get_variable if you really need to. 所以如果你真的需要,你可以回到tf.compat.v1.variable_scopetf.compat.v1.get_variable

Variable scopes and tf.get_variable can be convenient but are riddled with minor pitfalls and corner cases, specially since they behave similarly but not exactly like name scopes, and it is actually a parallel mechanism to it. 变量范围和tf.get_variable可以很方便,但是有很小的陷阱和角落情况,特别是因为它们的行为相似但不完全像名称范围,它实际上是一个并行的机制。 I think having just name scopes will be more consistent and straightforward. 我认为只有名称范围将更加一致和直接。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM