質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

Tnesorflow2のSubclassing記法で定義したグラフにダミーのデータを入れてTensorboardにグラフを表示する方法

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 242

hiretamago

score 0

前提・実現したいこと

■ 前提

言語:python 3.6
ライブラリ:Tensorflow 2.2.0

(参考)
開発環境:AWSのEMRのZeppelin

$ cat /etc/system-release
Amazon Linux AMI release 2018.03

■ 実現したいこと

VAEのグラフをdefine-by-runの記述が可能なTensorflow2で書き、ダミーのデータを流して仮のグラフを取得し、tensorboardで定義したグラフを確認したいと思っています。
しかし、描画が上手くできないため質問させていただきました。

Tensorflow2のAPIは大きく分けて

  • Sequential(積層型)モデル: コンパクトで簡単な書き方
  • Functional(関数型)API: 複雑なモデルも定義できる柔軟な書き方
  • Subclassing(サブクラス化)モデル: 難易度は少し上がるが、フルカスタマイズが可能

の3つですが、柔軟かつ自由にグラフを書きたいので、Subclassingの記法を採用しています。

発生している問題・エラーメッセージ

EncoderとDecoderのグラフ表示は上手く出来ているのですが、
reparameterizationTrick部分のグラフ表示が上手く出来ず、Tensorboardとterminalで次のエラーが出てしまいます。

Tensorboardでのエラー

Graph visualization failed.

Error: Malformed GraphDef. This can sometimes be caused by a bad 
network connection or difficulty reconciling multiple GraphDefs; for 
the latter case, please refer to https://github.com/tensorflow/tensorboard/issues/1929.

terminalでのエラー

ValueError: Cannot combine GraphDefs because nodes share a name but contents are different: Identity

該当のソースコード

reparameterizationTrickのグラフを作るコードだけを載せています。

import os
# tensorflowはCPUのみを使う
os.environ["CUDA_VISIBLE_DEVICES"]="-1" 
import tensorflow as tf
tf.random.set_seed(0)
import tensorboard
import pathlib as pl
import re
import datetime
import numpy as np
np.random.seed(0)

from PIL import Image
import matplotlib.pyplot as plt
class NeuralNetworkReparameterizationTrick(tf.keras.Model):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._dim_inputs_mean = None # batch_sizeを無視した入力の次元(タプル形式)
        self._dim_inputs_logvar = None # batch_sizeを無視した入力の次元(タプル形式)

    @property
    def dim_inputs_mean(self):
        return self._dim_inputs_mean

    @dim_inputs_mean.setter
    def dim_inputs_mean(self, dim_inputs_mean):
        """
        dim_inputs_mean -> (m, ) : m次元のデータ
        dim_inputs_mean -> (m, n, ) : m行n列のデータ 
        """
        if type(dim_inputs_mean)==tuple:
            self._dim_inputs_mean = dim_inputs_mean
        else:
            print("batch_sizeを無視したデータのサイズをタプル形式で指定してください。")
            exit()

    @property
    def dim_inputs_logvar(self):
        return self._dim_inputs_logvar

    @dim_inputs_logvar.setter
    def dim_inputs_logvar(self, dim_inputs_logvar):
        """
        dim_inputs_logvar -> (m, ) : m次元のデータ
        dim_inputs_logvar -> (m, n, ) : m行n列のデータ 
        """
        if type(dim_inputs_logvar)==tuple:
            self._dim_inputs_logvar = dim_inputs_logvar
        else:
            print("batch_sizeを無視したデータのサイズをタプル形式で指定してください。")
            exit()

    def call(self, mean, logvar):
        std = tf.exp(logvar*0.5, name="std")
        eps = tf.random.normal((std.shape[1],), name="eps")
        z = mean + eps * std
        return z
    def get_symbolic_model(self, dim_inputs_mean=(50,), dim_inputs_logvar=(50,), name_inputs_mean=None, name_inputs_logvar=None, name_model=None):
        """
        Imperative API で作成したモデルをベースに
        Symbolic API ( Functional API ) のモデルとして再作成した
        “仮のモデル”を生成する。
        """
        self.dim_inputs_mean = dim_inputs_mean
        self.dim_inputs_logvar = dim_inputs_logvar
        if name_inputs_mean is not None:
            inputs_mean = tf.keras.layers.Input(shape=self.dim_inputs_mean, name=name_inputs_mean)
        else:
            inputs_mean = tf.keras.layers.Input(shape=self.dim_inputs_mean)
        if name_inputs_logvar is not None:
            inputs_logvar = tf.keras.layers.Input(shape=self.dim_inputs_logvar, name=name_inputs_logvar)
        else:
            inputs_logvar = tf.keras.layers.Input(shape=self.dim_inputs_logvar)
        if name_model is not None:
            outputs = tf.keras.Model(inputs=[inputs_mean, inputs_logvar], outputs=self.call(mean=inputs_mean, logvar=inputs_logvar), name=name_model)
        else:
            outputs = tf.keras.Model(inputs=[inputs_mean, inputs_logvar], outputs=self.call(mean=inputs_mean, logvar=inputs_logvar))
        outputs.dim_inputs_mean = self.dim_inputs_mean
        outputs.dim_inputs_logvar = self.dim_inputs_logvar
        return outputs

    def output_tensorboard_structure(self, dim_inputs_mean=(50,), dim_inputs_logvar=(50,), name_inputs_mean=None, name_inputs_logvar=None, name_model=None, profiler_outdir="/tmp/sample_tensorboard/sample_logs"):
        """
        Imperative API で作成したモデルをベースに
        Symbolic API ( Functional API ) のモデルとして再作成した
        “仮のモデル”を生成する。
        """

        s_model = self.get_symbolic_model(dim_inputs_mean=model.reparameterizationTrick.dim_inputs_mean, dim_inputs_logvar=model.reparameterizationTrick.dim_inputs_logvar, name_inputs_mean=name_inputs_mean, name_inputs_logvar=name_inputs_logvar, name_model=name_model)
        @tf.function
        def traceme(x_mean, x_logvar):
            return s_model(inputs=[x_mean, x_logvar])

        writer = tf.summary.create_file_writer(logdir=profiler_outdir)
        tf.summary.trace_on(graph=True, profiler=True)
        # Forward pass
        dim1_plus_dim_inputs_mean = tuple([1]+list(self.dim_inputs_mean))
        dim1_plus_dim_inputs_logvar = tuple([1]+list(self.dim_inputs_logvar))
        sample_data_mean = tf.zeros(dim1_plus_dim_inputs_mean)
        sample_data_logvar = tf.ones(dim1_plus_dim_inputs_logvar)

        traceme(sample_data_mean, sample_data_logvar)
        with writer.as_default():
            tf.summary.trace_export(name="my_func_trace", step=0, profiler_outdir=profiler_outdir)
        tf.summary.trace_off()
        return None


class NeuralNetwork(tf.keras.Model):

    def __init__(self, *args, **kwargs):
        """
        グラフの要素を定義する。
        ただし、入力は定義しない。
        """
        super().__init__(*args, **kwargs)
        self.reparameterizationTrick = NeuralNetworkReparameterizationTrick(name="ReparameterizationTrick")

    def call(self, inputs):
        """
        __init__で定義された要素を繋ぎ合わせて、
        グラフのフォワードパスを定義する。
        """
        mean, logvar = inputs
        x = self.reparameterizationtrick(mean, logvar)
        outputs =x
        return outputs

# モデルの生成
model = NeuralNetwork()
#######################################################################################
# 一部のグラフの描画(reparameterizationTrick)
#######################################################################################
# 仮のモデルの次元を指定
model.reparameterizationTrick.dim_inputs_mean = (20,)
model.reparameterizationTrick.dim_inputs_logvar = (20,)
# 仮のモデルを取得
name_inputs_mean="inputs_mean"
name_inputs_logvar="inputs_logvar"
name_model="symbolic_model_reparameterizationTrick"
s_model = model.reparameterizationTrick.get_symbolic_model(dim_inputs_mean=model.reparameterizationTrick.dim_inputs_mean, dim_inputs_logvar=model.reparameterizationTrick.dim_inputs_logvar, name_inputs_mean=name_inputs_mean, name_inputs_logvar=name_inputs_logvar, name_model=name_model)
# グラフのsummaryを表示
s_model.summary()
# グラフの構造のPNGを保存
to_file_reparameterizationTrick = '/tmp/'+name_model+'.png'
tf.keras.utils.plot_model(model=s_model, show_shapes=True, show_layer_names=True, to_file=to_file_reparameterizationTrick, rankdir='TB', dpi=300)
# グラフの構造をTensorboardで閲覧出来るようにする
stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S.%f")
logdir = 'test_tensorboard_logs/func/'+name_model+'_%s' % stamp
profiler_outdir = "/tmp/" + logdir
path = pl.Path(profiler_outdir).parent.as_posix()
model.reparameterizationTrick.output_tensorboard_structure(dim_inputs_mean=model.reparameterizationTrick.dim_inputs_mean, dim_inputs_logvar=model.reparameterizationTrick.dim_inputs_logvar, name_inputs_mean=name_inputs_mean, name_inputs_logvar=name_inputs_logvar, name_model=name_model, profiler_outdir=profiler_outdir)

print("次のコマンドを実行してからアクセスすること。")
print("tensorboard --logdir {}".format(path))
#######################################################################################

試したこと

EncoderとreparameterizationTrickのグラフのモデルのsummaryを比較をしたところ、
入力層を除く各層の入力と出力のデータ形式に違いがあることは確認しました。
Encoder:タプル
reparameterizationTrick:タプル内包リスト
データ形式の違いがバグ解消のヒントなのでは?と考えています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Pythonに関する質問
  • Tnesorflow2のSubclassing記法で定義したグラフにダミーのデータを入れてTensorboardにグラフを表示する方法