TensorFlowを活用するうえで、TensorBoardは非常に役立つツールだ。スカラー値やデータフローグラフをログファイルとして出力し、可視化する方法を説明する。
TensorBoard
本連載の最後に、TensorBoardを紹介しておく。TensorBoardは、今後読者がTensorFlowを活用していく際に欠かせないツールである。
TensorBoardとは
TensorFlowには、TensorBoardというダッシュボードツールが付属している。TensorBoardはデータフローグラフの可視化や学習の履歴(損失関数の変化など)の可視化、あるいは途中過程で生成される画像や音声の表示を行うことができる。これにより、モデルの最適化やパラメーターチューニングに対する示唆が得られる。
スカラーの出力
典型的な利用方法としては、「損失関数の出力値が学習ステップを通して、どのように変化していくか」を折れ線グラフで表示するものである。前回のRNNの損失関数の出力過程を出力してみよう。
TensorBoardで可視化に利用されるデータは、ログとして出力された結果である。そのログは、tf.summaryモジュールにより提供される。
ここでは損失関数の出力すなわちスカラー値を可視化したいので、スカラー値のログを出力するためのtf.summary.scalarメソッドを用いる。tf.summaryモジュールにはログを出力するためのtf.summary.FileWriterクラスが定義されており、これを用いてログを出力できる。ログの出力はadd_summaryメソッドを用いればよい。https://cdn.cxpublic.com/articleCenterInBody_20190703.html#media=html&asId=0000000164657642&sid=1127153793923352357&width=100%25&height=190&resizeToContentSize=true&useSecureUrls=true&ctx=https%3A%2F%2Fwww.atmarkit.co.jp%2Fait%2Farticles%2F1804%2F27%2Fnews151.html&usi=kjr25fwetoo8nrtz&rnd=1846174694&prnd=kki2ffzac76xqzbu&tzo=-540&parentElementId=cxWidget_0.994657626279039
次にログ出力に必要なコードの骨格のみを示す。適宜、前回のリスト14のコードに、今回のリスト1の太字部分のコードを差し込めばよい。
# 学習回数
NUM_TRAIN = 10_000
# 学習中の出力頻度
OUTPUT_BY = 500
# 標準化
train_mean = train_dataset.mean()
train_std = train_dataset.std()
standardized_train_dataset = train_dataset.standardize()
# 損失関数の出力をトレース対象とする
loss_summary = tf.summary.scalar(‘MAE’, loss)
# logsディレクトリに出力するライターを作成して利用
with tf.summary.FileWriter(‘logs’) as writer:
# 学習の実行
sess.run(tf.global_variables_initializer())
for i in range(NUM_TRAIN):
batch = standardized_train_dataset.next_batch(SERIES_LENGTH, BATCH_SIZE)
summary, mae, _ = sess.run([loss_summary, loss, optimizer], feed_dict={ x: batch[0], y: batch[1]})
if i % OUTPUT_BY == 0:
print(‘step {:d}, error {:.2f}’.format(i, mae))
# ログの出力
writer.add_summary(summary, global_step=i)
# ログの出力
writer.add_summary(summary, global_step=NUM_TRAIN)リスト1 損失関数の出力をログファイルとして書き出す
ログのマージ
通常は、損失関数以外にもさまざまな途中過程をログとして出力したいというケースが多い。つまりトレース対象とする変数が増えるわけだが、そのような場合、複数のスカラー値を1つの変数にマージできる。具体的には、tf.summary.merge_allメソッドを用いると、これまでに定義したスカラー値などのトレース対象をまとめた変数が定義できる。
これを評価すれば、すべての変数についてのログがとれる。リスト2がそのコード例だ。
すべての変数ではなく明示的に変数をまとめたい場合は、tf.summary.mergeメソッドを用いる。
# 学習回数
NUM_TRAIN = 10_000
# 学習中の出力頻度
OUTPUT_BY = 500
# 標準化
train_mean = train_dataset.mean()
train_std = train_dataset.std()
standardized_train_dataset = train_dataset.standardize()
# ログ対象とするスカラー値を複数定義する
# 損失関数の出力をトレース対象とする
tf.summary.scalar(‘MAE’, loss)
# ……中略(他にもたくさんのログを対象とする)……
# ログ対象をまとめる
merged = tf.summary.merge_all()
# logsディレクトリに出力するライターを作成して利用
with tf.summary.FileWriter(‘logs’) as writer:
# 学習の実行
sess.run(tf.global_variables_initializer())
for i in range(NUM_TRAIN):
batch = standardized_train_dataset.next_batch(SERIES_LENGTH, BATCH_SIZE)
summary, mae, _ = sess.run([merged, loss, optimizer], feed_dict={ x: batch[0], y: batch[1]})
if i % OUTPUT_BY == 0:
print(‘step {:d}, error {:.2f}’.format(i, mae))
# ログの出力
writer.add_summary(summary, global_step=i)
# ログの出力
writer.add_summary(summary, global_step=NUM_TRAIN)リスト2 複数のトレース対象のログをマージする
データフローグラフの出力
tf.summary.FileWriterクラスのインスタンス作成時にgraph引数を指定するか、インスタンス作成後にadd_graphメソッドを用いると、データフローグラフを可視化できる。なお、本稿では省略しているが、各テンソルに名前を付けておけば、その名前が可視化の際に利用される。
# ……省略……
# logsディレクトリに出力するライターを作成して利用
# データフローグラフを出力する
with tf.summary.FileWriter(‘logs’, sess.graph) as writer:
# ……省略……リスト3 データフローグラフを出力するための引数指定
そのまま出力すると、データフローグラフのノードがそのままバラバラに表示されてしまい、見づらくなってしまう(どのように表示されるかは最後にまとめる)。tf.name_scopeメソッドで名前スコープを定義しておくことで、表示単位をまとめることができる。次のリスト4はその例で、前回のリスト12の前半「最終出力(予測)」を「prediction」という名前スコープでまとめたものだ。
# 全結合
with tf.name_scope(‘prediction’):
# 重み
w = tf.Variable(tf.zeros([20, FEATURE_COUNT]))
# バイアス
b = tf.Variable([0.1] * FEATURE_COUNT)
# 最終出力(予測)
prediction = tf.matmul(last_state, w) + bリスト4 名前スコープで表示をまとめる
本稿の例では、同様の手順で、前回示したリスト10/11/12に対して、
- 「input」名前スコープ: リスト10の後半にある「# 入力」
- 「rnn」名前スコープ: リスト11の「# RNNセルの作成」
- 「optimization」名前スコープ: リスト12の後半にある「# 損失関数と最適化」
を定義すればよい。
TensorBoardの起動
以上の手順でログが取れたら、TensorBoardを起動する。TensorBoardはtensorboardコマンド(リスト5)で起動できる。当然ながら、カレントディレクトリを適切な場所(Jupyter Notebookを使っている場合は.ipynbファイルのあるディレクトリ)に変更してから実行すること。
$ tensorboard –logdir=logs/リスト5 TensorBoardを起動するコマンド
「ImportError: cannot import name ‘encodings’」というエラーが発生して起動できないときは、pip install html5lib==1.0b8というコマンドを実行するとよい(※詳細は「Now broken with html5lib · Issue #318 · xhtml2pdf/xhtml2pdf」を参照されたい)。
デフォルトでは6006番ポートでWebサーバーが起動するので*1、http://localhost:6006/にアクセスするとTensorBoardが表示される(図1)。
*1 ポート番号は–portオプションで指定することもできる。図1 TensorBoardの画面([SCALARS]タブ)
画面上部のタブ、具体的には[SCALARS]タブでログ出力したスカラー値(図1)や、[GRAPHS]タブでデータフローグラフ(図2)などの表示を切り替えることができる。前述の通り、名前スコープがない場合(図2)とある場合(図3)で異なる。図2 [GRAPHS]タブ(名前スコープなし)
図2では全要素が表示されるため全体の内容をつかみづらい。スコープを定義しておけば、図3のようにすっきりと表示される。
![[prediction]をダブルクリックすると、そのスコープが拡大表示される](https://image.itmedia.co.jp/ait/articles/1804/27/di-04-09-2.gif)
[prediction]をダブルクリックすると、そのスコープが拡大表示される▼図3 [GRAPHS]タブ(名前スコープあり)
以上で本連載は完結である。続編としてChainerで同様の内容を解説するので、ぜひそちらも併せて参照してほしい。
【TL;DR】TensorBoard
- TensorBoard: TensorFlowに付属するダッシュボードツール。データフローグラフや学習履歴の可視化、途中過程で生成される画像や音声の表示などが行える
- tf.summaryモジュール: 出力結果のログを提供する。このログが、TensorBoardでのデータの可視化に利用される
- tf.summary.scalarメソッド: スカラー値のログを取得する
- tf.summary.FileWriterクラス: ログを出力する。インスタンス作成時にgraph引数を指定すると、データフローグラフを可視化できる
- tf.summary.merge_allメソッド: 複数のスカラー値を1つの変数にマージできる
- tf.name_scopeメソッド: 名前スコープを定義しておくことで、表示単位をまとめられる
- TensorBoardの起動: tensorboard –logdir=logs/コマンドを実行
TensorFlow 1.8 – TensorBoard: Visualizing Learning
TensorBoard:学習の視覚化
TensorFlowを使用する計算は、大規模なディープニューラルネットワークのトレーニングのように、複雑で混乱を招く可能性があります。 TensorFlowプログラムの理解、デバッグ、および最適化を容易にするために、TensorBoardと呼ばれる一連の視覚化ツールが含まれています。 TensorBoardを使用すると、TensorFlowグラフを視覚化し、グラフの実行に関する定量的な指標をプロットし、それを通過する画像などの追加データを表示できます。 TensorBoardが完全に構成されていると、次のようになります。

この30分のチュートリアルは、TensorBoardの簡単な使い方を始めるためのものです。 これはTensorFlowの基本的な理解を前提としています。
他にも利用可能なリソースがあります。 TensorBoard GitHubに は、 TensorBoard 内の個々のダッシュボードの使い方に関する、ヒントとテクニック、デバッグ情報など、さらに多くの情報があります。
セットアップ
TensorFlowをインストールしてください 。 pipを介してTensorFlowをインストールすると、自動的にTensorBoardもインストールされるはずです。
データをシリアル化する
TensorBoardは、TensorFlowイベントファイルを読み取ることによって機能します。このファイルには、TensorFlowの実行時に生成できる要約データが含まれています。 これがTensorBoard内の要約データの一般的なライフサイクルです。
まず、サマリーデータを収集したいTensorFlowグラフを作成し、どのノードに サマリー操作で 注釈を付けるかを決定します。
たとえば、MNISTの数字を認識するための畳み込みニューラルネットワークをトレーニングしているとします。 学習率が時間とともにどのように変化するか、そして目的関数がどのように変化しているかを記録します。 学習率と損失をそれぞれ出力するノードに tf.summary.scalar
opsを添付してこれらを収集します。 それから、それぞれの scalar_summary
'learning rate'
や 'loss function'
ような意味のある tag
ます。
特定のレイヤーから発生するアクティベーションの分布、またはグラデーションや重みの分布を視覚化したいと思うかもしれません。 このデータを収集するには、 tf.summary.histogram
opsをグラデーション出力と重みを保持する変数にそれぞれ接続します。
利用可能なすべてのサマリオペレーションの詳細については、サマリオペレーションに関するドキュメントをチェックしてください。
TensorFlowでの操作は、それらを実行するまで何もしません。あるいは、それらの出力に依存する操作です。 そして作成したサマリーノードはグラフの周辺にあります。現在実行しているオペレーションはどれもそれらに依存しません。 したがって、要約を生成するには、これらの要約ノードをすべて実行する必要があります。 それらを手動で管理するのは面倒ですので、 tf.summary.merge_all
を使用してそれらを単一の操作にまとめ、すべての要約データを生成してください。
その後、マージされたsummary opを実行するだけで、指定されたステップですべての要約データを含む直列化された Summary
protobufオブジェクトが生成されます。 最後に、この要約データをディスクに書き込むには、要約protobufを tf.summary.FileWriter
ます。
FileWriterはそのコンストラクタに FileWriter
取ります – このlogdirは非常に重要です、それはすべてのイベントが書き出されるディレクトリです。 また、 FileWriter
はオプションでそのコンストラクターに Graph
FileWriter
ことができます。 Graph
オブジェクトを受け取ると、TensorBoardはテンソル形状情報とともにグラフを視覚化します。 これにより、グラフに何が流れるかをよりよく理解できます 。Tensor形状情報を 参照して ください 。
グラフを修正して FileWriter
た FileWriter
、ネットワークの運用を開始する準備が整いました。 必要に応じて、ステップごとにマージされたサマリーopを実行し、大量のトレーニングデータを記録することができます。 ただし、これは必要以上のデータになる可能性があります。 代わりに、 n
ステップごとにマージされた要約操作を実行することを検討してください。
以下のコード例は、 簡単なMNISTチュートリアルを 修正したものです。ここにいくつかの要約操作を追加し、それらを10ステップごとに実行します。 これを実行してから tensorboard --logdir=/tmp/tensorflow/mnist
、トレーニング中に重みや精度がどのように変化したかなどの統計を視覚化できます。 以下のコードは抜粋です。 完全な情報源は here です。
def variable_summaries(var): """Attach a lot of summaries to a Tensor (for TensorBoard visualization).""" with tf.name_scope('summaries'): mean = tf.reduce_mean(var) tf.summary.scalar('mean', mean) with tf.name_scope('stddev'): stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean))) tf.summary.scalar('stddev', stddev) tf.summary.scalar('max', tf.reduce_max(var)) tf.summary.scalar('min', tf.reduce_min(var)) tf.summary.histogram('histogram', var) def nn_layer(input_tensor, input_dim, output_dim, layer_name, act=tf.nn.relu): """Reusable code for making a simple neural net layer. It does a matrix multiply, bias add, and then uses relu to nonlinearize. It also sets up name scoping so that the resultant graph is easy to read, and adds a number of summary ops. """ # Adding a name scope ensures logical grouping of the layers in the graph. with tf.name_scope(layer_name): # This Variable will hold the state of the weights for the layer with tf.name_scope('weights'): weights = weight_variable([input_dim, output_dim]) variable_summaries(weights) with tf.name_scope('biases'): biases = bias_variable([output_dim]) variable_summaries(biases) with tf.name_scope('Wx_plus_b'): preactivate = tf.matmul(input_tensor, weights) + biases tf.summary.histogram('pre_activations', preactivate) activations = act(preactivate, name='activation') tf.summary.histogram('activations', activations) return activations hidden1 = nn_layer(x, 784, 500, 'layer1') with tf.name_scope('dropout'): keep_prob = tf.placeholder(tf.float32) tf.summary.scalar('dropout_keep_probability', keep_prob) dropped = tf.nn.dropout(hidden1, keep_prob) # Do not apply softmax activation yet, see below. y = nn_layer(dropped, 500, 10, 'layer2', act=tf.identity) with tf.name_scope('cross_entropy'): # The raw formulation of cross-entropy, # # tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(tf.softmax(y)), # reduction_indices=[1])) # # can be numerically unstable. # # So here we use tf.losses.sparse_softmax_cross_entropy on the # raw logit outputs of the nn_layer above. with tf.name_scope('total'): cross_entropy = tf.losses.sparse_softmax_cross_entropy(labels=y_, logits=y) tf.summary.scalar('cross_entropy', cross_entropy) with tf.name_scope('train'): train_step = tf.train.AdamOptimizer(FLAGS.learning_rate).minimize( cross_entropy) with tf.name_scope('accuracy'): with tf.name_scope('correct_prediction'): correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) with tf.name_scope('accuracy'): accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) tf.summary.scalar('accuracy', accuracy) # Merge all the summaries and write them out to /tmp/mnist_logs (by default) merged = tf.summary.merge_all() train_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/train', sess.graph) test_writer = tf.summary.FileWriter(FLAGS.summaries_dir + '/test') tf.global_variables_initializer().run()
FileWriters
を初期化した後、モデルをトレーニングしてテストするときに FileWriters
サマリーを追加する必要があります。
# Train the model, and also write summaries. # Every 10th step, measure test-set accuracy, and write test summaries # All other steps, run train_step on training data, & add training summaries def feed_dict(train): """Make a TensorFlow feed_dict: maps data onto Tensor placeholders.""" if train or FLAGS.fake_data: xs, ys = mnist.train.next_batch(100, fake_data=FLAGS.fake_data) k = FLAGS.dropout else: xs, ys = mnist.test.images, mnist.test.labels k = 1.0 return {x: xs, y_: ys, keep_prob: k} for i in range(FLAGS.max_steps): if i % 10 == 0: # Record summaries and test-set accuracy summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False)) test_writer.add_summary(summary, i) print('Accuracy at step %s: %s' % (i, acc)) else: # Record train set summaries, and train summary, _ = sess.run([merged, train_step], feed_dict=feed_dict(True)) train_writer.add_summary(summary, i)
これで、TensorBoardを使用してこのデータを視覚化する準備がすべて整いました。
TensorBoardを起動する
TensorBoardを実行するには、次のコマンドを使用します(あるいは python -m tensorboard.main
)
tensorboard --logdir=path/to/log-directory
logdir
は、 FileWriter
がデータを FileWriter
化したディレクトリを指します。 この logdir
ディレクトリが、別々の実行からのシリアル化されたデータを含むサブディレクトリを含む場合、 logdir
はそれらすべての実行からのデータを視覚化します。 TensorBoardが起動したら、Webブラウザを localhost:6006
に移動してTensorBoardを表示します。
TensorBoardを見ると、右上隅にナビゲーションタブがあります。 各タブは、視覚化できる一連のシリアル化データを表します。
グラフタブ を使用して グラフ を視覚化する方法の詳細については、 TensorBoard:Graph Visualizationを 参照してください。
TensorBoardの一般的な使用法については、 TensorBoard GitHubを 参照してください。