web-dev-qa-db-ja.com

TensorFlow 2.0:tf.kerasを使用してグラフをグループ化する方法? tf.name_scope / tf.variable_scopeはもう使用されていませんか?

TensorFlow <2.0に戻ると、レイヤーを定義するために使用しました。特に開始モジュールなどのより複雑なセットアップを、tf.name_scopeまたはtf.variable_scopeでグループ化することで使用しました。

これらの演算子を利用して、計算グラフを便利に構造化することができました。これにより、TensorBoardのグラフビューをより簡単に解釈できるようになりました。

構造化グループの1つの例: enter image description here

これは、複雑なアーキテクチャのデバッグに非常に役立ちます。

残念ながら、tf.kerastf.name_scopeを無視しているようで、tf.variable_scopeはTensorFlow> = 2.0で削除されています。したがって、このようなソリューション...

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

...はもう利用できません。代替品はありますか?

TensorFlow> = 2.0でレイヤーとモデル全体をどのようにグループ化できますか?レイヤーをグループ化しない場合、tf.kerasはグラフビューにすべてをシリアルに配置するだけで複雑なモデルに大きな混乱をもたらします。

tf.variable_scopeの代替品はありますか?今のところ見つかりませんでしたが、TensorFlow <2.0でこのメソッドを多用しました。


[〜#〜] edit [〜#〜]TensorFlow 2.の例を実装しました。これは、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])

これらのモデルのTensorBoardのグラフthis のようになります。レイヤーは単なる完全な混乱であり、モデル "G"および "D"(右側)はいくつかの混乱をカバーしています。 「GAN」は完全に欠落しています。トレーニング操作「アダム」を適切に開くことができません。左から右にプロットされたレイヤーが多すぎて、場所全体に矢印が表示されています。この方法でGANの正当性をチェックすることは非常に困難です。


ただし、同じ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})

...結果の TensorBoardグラフ はかなりきれいに見えます。 "AdamD"と "AdamGAN"スコープが右上にあることを確認してください。オプティマイザが適切なスコープ/勾配に接続されていることを直接確認できます。

16
daniel451

コミュニティRFCによると TensorFlow 2.0の変数

  • 変数の命名を制御するには、ユーザーはtf.name_scope + tf.Variableを使用できます

確かに、 - 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"

また、上記のポイントは次のように述べています:

  • variable_scopeおよびget_variableのtf 1.0バージョンはtf.compat.v1に残されます。

だからあなたは tf.compat.v1.variable_scope および tf.compat.v1.get_variable 本当に必要な場合。

変数のスコープとtf.get_variableは便利な場合がありますが、特に、名前スコープと同様に動作しますが正確には動作しないため、マイナーな落とし穴やコーナーケースに悩まされており、実際にはそれに対応するメカニズムです。名前のスコープのみを使用する方が、一貫性があり簡単です。

2
jdehesa