TensorFlow : Tutorials : TF Layers へのガイド : CNN を構築する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/08/2018
* Getting Started / Programmer’s Guide が再構成されましたが、Tutorials でも数編が新規追加されています。
* 本ページは、TensorFlow 本家サイトの Tutorials – A Guide to TF Layers: Building a Convolutional Neural Network
を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
TensorFlow layers (層) モジュール はニューラルネットワークを構築することを容易にする高位 API を提供します。それは dense (完全結合) 層と畳込み層の作成を容易にし、活性化関数を追加して、そして dropout 正則化を適用するメソッドを提供します。このチュートリアルでは、MNIST データセットの手書き数字を認識するために畳み込みニューラルネットワーク・モデルを layers を使用してどのように構築するかを学習します。
MNIST データセット は手書き数字 0-9 の 60,000 訓練サンプルと 10,000 テスト・サンプルから成り、28×28-ピクセルのモノクローム画像としてフォーマットされています。
Getting Started
私達の TensorFlow プログラムのためにスケルトンをセットアップしましょう。cnn_mnist.py と呼ばれるファイルを作成して、次のコードを追加します :
from __future__ import absolute_import from __future__ import division from __future__ import print_function # Imports import numpy as np import tensorflow as tf tf.logging.set_verbosity(tf.logging.INFO) # Our application logic will be added here if __name__ == "__main__": tf.app.run()
チュートリアルを通してワークするとき、畳み込みニューラルネットワークを構築し、訓練し、そして評価するためのコードを追加するでしょう。完全な、最終的なコードは ここで見つかります。
畳み込みニューラルネットワークへのイントロ
畳み込みニューラルネットワークは画像分類タスクのための現在の最先端モデル・アーキテクチャです。CNN は (モデルが分類のために使用できる) より高位の特徴を抽出して学習するために画像の生ピクセルデータにフィルタのシリーズを適用します。CNN は3つのコンポーネントを含みます :
- 畳み込み層、これは指定された数の畳み込みフィルタを画像に適用します。各部分領域に対して、層は出力特徴マップの単一の値を生成するために数学演算のセットを遂行します。畳み込み層はそれからモデルに非線形性を導入するために典型的には ReLU 活性化関数 を出力に適用します。
- プーリング層、これは畳み込み層により抽出された画像データを処理時間を減少させるために特徴マップの次元を削減して ダウンサンプリング します。一般的に使用されるプーリング・アルゴリズムはマックス・プーリングで、これは特徴マップの部分領域 (e.g., 2×2-ピクセル・タイル) を抽出し、それらの最大値を保持して、他の総ての値を捨てます。
- Dense (完全結合) 層、これは畳み込み層により抽出されてプーリング層によりダウンサンプリングされた特徴上で分類を遂行します。dense 層では、層の総てのノードは前の層の総てのノードに接続されています。
典型的には、CNN は特徴抽出を遂行する畳み込みモジュールのスタックから成ります。各モジュールはプーリング層が続く畳み込み層から成ります。最後の畳み込みモジュールには分類を遂行する一つまたはそれ以上の dense 層が続きます。CNN の最後の dense 層はモデルの各ターゲットクラス (モデルが予測できる可能な総てのクラス) のために単一のノードを含み、各ノードのために 0-1 の間の値を生成するための softmax 活性化関数を持ちます (これらの softmax 値の総計は 1 に等しいです)。与えられた画像に対する softmax 値を画像が各ターゲットクラスに収まる尤度の相対尺度として解釈できます。
Note: CNN アーキテクチャのより包括的なウォークスルーのためには、Visual Recognition コース・マテリアルのための Stanford University の畳み込みニューラルネットワーク を見てください。
CNN MNIST 分類器を構築する
次の CNN アーキテクチャを使用して MNIST データセットの画像を分類するためのモデルを構築してみましょう :
- Convolutional Layer #1: 32 5×5 フィルタを適用します (5×5-ピクセル部分領域を抽出), with ReLU 活性化関数
- Pooling Layer #1: 2×2 フィルタと 2 のストライド (これはプールされる領域がオーバーラップしないことを指定します) を持つマックス・プーリングを遂行します。
- Convolutional Layer #2: 64 5×5 フィルタを適用します、with ReLU 活性化関数
- Pooling Layer #2: 再度、2×2 フィルタと 2 のストライドを持つマックス・プーリングを遂行します。
- Dense Layer #1: 1,024 ニューロン、with dropout 正則化 0.4 のレート (0.4 の確率で任意の与えられた要素が訓練の間にドロップされます)。
- Dense Layer #2 (ロジット層): 10 ニューロン、一つが各数字ターゲットクラス (0-9)。
tf.layers モジュールは上の3つの層タイプの各々を作成するためのメソッドを含みます :
- conv2d(). 2-次元畳み込み層を構築します。引数としてフィルタ数、フィルタ・カーネルサイズ、パディング、そして活性化関数を取ります。
- max_pooling2d(). マックスプーリング・アルゴリズムを使用する 2-次元プーリング層を構築します。引数としてプーリング・フィルタサイズとストライドを取ります。
- dense(). dense 層を構築します。引数としてニューロン数と活性化関数を取ります。
これらのメソッドの各々は入力として tensor を受け取って出力として変換された tensor を返します。これは一つの層を他に接続することを容易にします : 一つの層作成メソッドから単に出力を取りそしてそれを他の一つの入力として供給します。
cnn_mnist.py をオープンして次の cnn_model_fn function 関数を追加します、これは TensorFlow の Estimator API (これについては後で詳述) により想定されるインターフェイスに準拠しています。cnn_mnist.py は MNIST 特徴データ、ラベル、そして モデル・モード (TRAIN, EVAL, PREDICT) を引数として取ります ; CNN を構成します ; そして予測、損失、そして訓練演算を返します :
def cnn_model_fn(features, labels, mode): """Model function for CNN.""" # Input Layer input_layer = tf.reshape(features["x"], [-1, 28, 28, 1]) # Convolutional Layer #1 conv1 = tf.layers.conv2d( inputs=input_layer, filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) # Pooling Layer #1 pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2) # Convolutional Layer #2 and Pooling Layer #2 conv2 = tf.layers.conv2d( inputs=pool1, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2) # Dense Layer pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64]) dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu) dropout = tf.layers.dropout( inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN) # Logits Layer logits = tf.layers.dense(inputs=dropout, units=10) predictions = { # Generate predictions (for PREDICT and EVAL mode) "classes": tf.argmax(input=logits, axis=1), # Add `softmax_tensor` to the graph. It is used for PREDICT and by the # `logging_hook`. "probabilities": tf.nn.softmax(logits, name="softmax_tensor") } if mode == tf.estimator.ModeKeys.PREDICT: return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions) # Calculate Loss (for both TRAIN and EVAL modes) loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits) # Configure the Training Op (for TRAIN mode) if mode == tf.estimator.ModeKeys.TRAIN: optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001) train_op = optimizer.minimize( loss=loss, global_step=tf.train.get_global_step()) return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op) # Add evaluation metrics (for EVAL mode) eval_metric_ops = { "accuracy": tf.metrics.accuracy( labels=labels, predictions=predictions["classes"])} return tf.estimator.EstimatorSpec( mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
以下のセクション (上の各コードブロックに対応する見出しとともに) では各層を作成するために使用される tf.layers コードに加えて、どのように損失を計算するか、訓練 op を構成するか、そして予測を生成するかについて深く潜ります。貴方が既に CNN と TensorFlow Estimator を経験していて、上のコードを直感的であると見い出すのであれば、これらのセクションは拾い読みして単に (最後のセクションの) “Training and Evaluating the CNN MNIST Classifier” へとスキップしても良いでしょう。
入力層
2-次元画像データのための畳み込みとプーリング層を作成する層モジュールのメソッドは入力 tensor がデフォルトでは [batch_size, image_height, image_width, channels] の shape を持つことを想定します。この挙動はdata_format パラメータを使用して変更できます ; 次のように定義されます :
- batch_size. 訓練の間に勾配降下を遂行するとき使用するサンプルのサブセットのサイズ。
- image_height. サンプル画像の高さ。
- image_width. サンプル画像の幅。
- channels. サンプル画像のカラーチャネル数。カラー画像については、チャネル数は 3 (赤、緑、青)。モノクローム画像については、丁度 1 チャネル (黒) があります。
- data_format. channels_last (デフォルト) か channels_first の一つの文字列。channels_last は shape (batch, …, channels) を持つ入力に対応する一方で channels_first は shape (batch, channels, …) を持つ入力に対応します。
ここで、MNIST データセットはモノクローム 28×28 ピクセル画像から成りますので、入力層のために望まれる shape は [batch_size, 28, 28, 1] です。
入力特徴マップ (features) をこの shape に変換するためには、次の reshape 演算を遂行できます :
input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])
バッチサイズとして -1 を示したことに注意してください、これはこの次元が (他の次元の定数の総てのサイズを保持しながら) features[“x”] の入力値の数に基づいて動的に計算されるべきであることを指定します。これは batch_size を調整可能なハイパーパラメータとして扱うことを可能にします。例えば、サンプルをモデルにバッチ 5 で供給する場合、features[“x”] は 3,920 値 (各画像の各ピクセルのために 1 値) を含み、そして input_layer は [5, 28, 28, 1] の shape を持つでしょう。同様に、バッチ 100 でサンプルを供給する場合、features[“x”] は 78,400 値を含み、そして input_layer は [100, 28, 28, 1] の shape を持つでしょう。
Convolutional Layer #1
最初の畳込み層で、入力層に 32 5×5 フィルタを適用することを望みます、ReLU 活性化関数とともに。この層を作成するために層モジュールの conv2d() メソッドを次のように使用できます。
conv1 = tf.layers.conv2d( inputs=input_layer, filters=32, kernel_size=[5, 5], padding="same", activation=tf.nn.relu)
入力引数は入力 tensor を指定します、これは shape [batch_size, image_height, image_width, channels] を持たなければなりません。ここで、最初の畳み込み層を入力層に接続しています、これは shape [batch_size, 28, 28, 1] を持ちます。
Note: conv2d() は引数 data_format=channels_first が渡されたときには代わりに [batch_size, channels, image_height, image_width] の shape を受け取るでしょう。
filters 引数は適用するフィルタの数を指定します (ここでは、32)、そして kernel_size はフィルタの次元を [height, width] として指定します (ここでは、[5, 5])。
TIP: フィルタの高さと幅が同じ値を持つ場合、kernel_size のために代わりに単一の整数を指定できます —e.g., kernel_size=5。
padding 引数は2つの列挙値 (case-insensitive) の一つを指定します : valid (デフォルト値) か same です。出力 tensor が入力 tensor と同じ高さと幅を持つことを指定するためには、ここで padding=same を設定します、これは TensorFlow に 28 の高さと幅を保存するために入力 tensor のエッジに 0 値を追加することを命令します。(padding なしでは、28×28 tensor に渡る 5×5 畳み込みは 24×24 tensor を生成します、何故ならば 28×28 グリッドから 5×5 タイルを抽出する 24×24 位置があるからです。)
activation 引数は畳み込みの出力に適用する活性化関数を指定します。ここでは、tf.nn.relu で ReLU 活性を指定します。
conv2d() により生成される出力 tensor は [batch_size, 28, 28, 32] の shape を持ちます : 入力と同じ高さと幅の次元ですが、今ではフィルタの各々からの 32 チャネルが出力を保持しています。
Pooling Layer #1
次に、作成したばかりの畳込み層に最初のプーリング層を接続します。2×2 フィルタと 2 のストライドを持つマックスプーリングを遂行する層を構築するために layers の max_pooling2d() メソッドを使用できます :
pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)
再び、inputs は [batch_size, image_height, image_width, channels] の shape を持つ入力 tensor を指定します。ここで、入力 tensor は conv1、最初の畳み込み層からの出力で、これは [batch_size, 28, 28, 32] の shape を持ちます。
Note: conv2d() と同様に、引数 data_format=channels_first が渡されたとき max_pooling2d() は代わりに [batch_size, channels, image_height, image_width] の shape を受け取ります。
pool_size 引数はマックスプーリング・フィルタのサイズを [height, width] (ここでは、[2, 2]) として指定します。もし両者の次元が同じ値を持つ場合には、代わりに単一の整数 (e.g., pool_size=2) を指定できます。
strides 引数はストライドのサイズを指定します。ここでは、2 のストライドを設定します、これは高さと幅の次元の両者でフィルタで抽出される部分領域が 2 ピクセル隔てられることを示します (2×2 フィルタについては、これは抽出された領域のいずれもがオーバーラップしないことを意味します)。高さと幅のために異なるストライド値を設定することを望む場合、代わりにタプルかリスト (e.g., stride=[3, 6]) を指定できます。
max_pooling2d() (pool1) により生成された出力 tensor は [batch_size, 14, 14, 32] の shape を持ちます : 2×2 フィルタは高さと幅をそれぞれ 50 % 削減します。
Convolutional Layer #2 と Pooling Layer #2
前のように conv2d() と max_pooling2d() を使用して2 番目の畳み込みとプーリング層を私達の CNN に接続できます。convolutional layer #2 については、ReLU 活性を伴う 64 5×5 フィルタを構成し、pooling layer #2 については、pooling layer #1 と同じ仕様を使用します (2 のストライドを持つ 2×2 マックスプーリング・フィルタ) :
conv2 = tf.layers.conv2d( inputs=pool1, filters=64, kernel_size=[5, 5], padding="same", activation=tf.nn.relu) pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)
convolutional layer #2 は入力として最初のプーリング層 (pool1) の出力 tensor を取り、出力として tensor conv2 を生成することに注意してください。conv2 は [batch_size, 14, 14, 64] の shape を持ち、pool1 と同じ高さと幅、そして 64 フィルタが適用されるために 64 チャネルです。
Pooling layer #2 は入力として conv2 を取り、出力として pool2 を生成します。pool2 は shape [batch_size, 7, 7, 64] を持ちます (conv2 から高さと幅の 50 % 削減)。
Dense Layer
次に、convolution/pooling 層で抽出された特徴上で分類を遂行するために私達の CNN に dense 層 (with 1,024 ニューロンと ReLU 活性) を追加することを望みます。層に接続する前に、けれども、tensor が2つの次元だけを持つように shape [batch_size, features] に特徴マップ (pool2) を平坦化します :
pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
上の reshape() 演算で、-1 は batch_size 次元は入力データのサンプルの数を基に動的に計算されることを表します。各サンプルは 7 (pool2 高さ) * 7 (pool2 幅) * 64 (pool2 チャネル) 特徴を持ちますので、特徴次元に 7 * 7 * 64 (総計で 3136) の値を持つことを望みます。出力 tensor, pool2_flat, は shape [batch_size, 3136] を持ちます。
今では、dense 層に接続するために次のように layers の dense() メソッドを使用できます :
dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
inputs 引数は入力 tensor を指定します : 平坦化された特徴マップ, pool2_flat です。units 引数は dense 層のニューロン数を指定します (1,024)。activation 引数は活性化関数を取ります ; 再び、ReLU 活性を追加するために tf.nn.relu を使用します。
モデルの結果を改善する手助けをするために、layers の dropout メソッドを使用して、dense 層に dropout 正則化もまた適用します :
dropout = tf.layers.dropout( inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)
再び、inputs は入力 tensor を指定し、これは dense 層 (dense) からの出力 tensor です。
rate 引数は dropout 率を指定します ; ここでは、0.4 を使用します、これは訓練の間に要素の 40 % がランダムにドロップアウトされることを意味します。
training 引数はモデルが現在訓練モードで実行されているか否かを指定する boolean を取ります ; dropout は training が True の場合に遂行されるだけです。ここでは、モデル関数 cnn_model_fn に渡される mode が TRAIN モードであるかをチェックします。
出力 tensor dropout は shape [batch_size, 1024] を持ちます。
Logits Layer
私達のニューラルネットワークの最終層は logits 層です、これは予測のために生の値を返します。10 ニューロン (各ターゲットクラス 0-9 のために一つ) で dense 層を作成します、linear 活性 (デフォルト) を伴います。
logits = tf.layers.dense(inputs=dropout, units=10)
CNN の最後の出力 tensor, logits, は shape [batch_size, 10] を持ちます。
予測を生成する
モデルの logits 層は [batch_size, 10]-次元 tensor の生の値として予測を返します。これらの生の値をモデル関数が返すことができる2つの異なるフォーマットに変換しましょう :
- 各サンプルのための 予測されるクラス : 0-9 の数字。
- 各サンプルのための各可能なターゲットクラスのための 確率 : サンプルが 0 である、1 である、2 である, etc. の確率です。
与えられたサンプルに対して、予測されるクラスは最も高い生の値を持つ logits tensor の対応する行の要素です。tf.argmax 関数を使用してこの要素のインデックスを見つけることができます :
tf.argmax(input=logits, axis=1)
input 引数はそこから最大値を抽出する tensor を指定します — ここでは logits です。axis 引数はそれに沿って最大値を見つける入力 tensor の軸を指定します。ここでは、1 のインデックスを持つ次元に沿った最大値を見つけることを望みます、これは予測に対応します (私達の logits tensor は shape [batch_size, 10] を持つことを思い出してください)。
tf.nn.softmax を使用して softmax 活性を適用することにより logits 層から確率を導出できます :
tf.nn.softmax(logits, name="softmax_tensor")
Note: この演算を softmax_tensor と明示的に命名するために name 引数を使用しますので、後でそれを参照できます (“Set Up a Logging Hook” で softmax 値のために logging をセットアップします)。
予測を辞書にコンパイルして、EstimatorSpec オブジェクトを返します :
predictions = { "classes": tf.argmax(input=logits, axis=1), "probabilities": tf.nn.softmax(logits, name="softmax_tensor") } if mode == tf.estimator.ModeKeys.PREDICT: return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
損失を計算する
訓練と評価の両者のために、モデルの予測がターゲット・クラスにどのくらい近いかを測定する 損失関数を定義する必要がありますMNIST のようなマルチクラス分類のためには、典型的には 交差エントロピー が損失メトリックとして使用されます。次のコードはモデルが TRAIN か EVAL モードのいずれかで実行されるとき交差エントロピーを計算します :
onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10) loss = tf.losses.softmax_cross_entropy( onehot_labels=onehot_labels, logits=logits)
上で何が起きているかをより密接に見てみましょう。
ラベル tensor はサンプル, e.g. [1, 9, …] のための予測のリストを含みます。交差エントロピーを計算するために、最初にラベルを対応する one-hot エンコーディング に変換する必要があります :
[[0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1], ...]
この変換を遂行するために tf.one_hot 関数を使用します。tf.one_hot() は2つの必要な引数を持ちます :
- indices. “on values” を持つ one-hot tensor 内の位置 — i.e., 上で示された tensor 内で 1 値の位置。
- depth. one-hot tensor の深さ — i.e., ターゲット・クラスの数。ここでは、depth は 10 です。
次のコードは私達のラベル、onehot_labels のための one-hot tensor を作成します :
onehot_labels = tf.one_hot(indices=tf.cast(labels, tf.int32), depth=10)
ラベルは 0-9 からの値のシリーズを含みますので、indices は 整数にキャストされた値を持つ単に私達の labels tensor です。depth は 10 です、何故ならば 10 の可能なターゲット・クラスを持つからです、各数字のために1つ。
次に、onehot_labels と logits 層からの予測の softmax の交差エントロピーを計算します。tf.losses.softmax_cross_entropy() は引数として onehot_labels と logits を取り、logits 上の softmax 活性を遂行し、交差エントロピーを計算し、そして損失をスカラー Tensor として返します :
loss = tf.losses.softmax_cross_entropy( onehot_labels=onehot_labels, logits=logits)
訓練 Op を構成する
前のセクションでは、CNN のための損失を logits 層とラベルの softmax 交差エントロピーとして定義しました。訓練の間のこの損失値を最適化するモデルを構成しましょう。0.001 の学習率と最適化アルゴリズムとして確率的勾配降下を使用します :
if mode == tf.estimator.ModeKeys.TRAIN: optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001) train_op = optimizer.minimize( loss=loss, global_step=tf.train.get_global_step()) return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)
評価メトリクスを追加する
モデルに accuracy メトリックを追加するために、次のように EVAL モードで eval_metric_ops 辞書を定義します :
eval_metric_ops = { "accuracy": tf.metrics.accuracy( labels=labels, predictions=predictions["classes"])} return tf.estimator.EstimatorSpec( mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
CNN MNIST 分類器を訓練して評価する
MNIST CNN モデル関数をコーディングしました ; 今ではそれを訓練して評価する準備ができています。
訓練とテストデータをロードする
最初に、訓練とテストデータをロードしましょう。cnn_mnist.py に次のコードで main() 関数を追加します :
def main(unused_argv): # Load training and eval data mnist = tf.contrib.learn.datasets.load_dataset("mnist") train_data = mnist.train.images # Returns np.array train_labels = np.asarray(mnist.train.labels, dtype=np.int32) eval_data = mnist.test.images # Returns np.array eval_labels = np.asarray(mnist.test.labels, dtype=np.int32)
訓練特徴データ (手書き数字の 55,000 画像のための生ピクセル値) と訓練ラベル (各画像のための 0-9 の対応する値) をそれぞれ train_data と train_labels に numpy 配列 としてストアします。同様に、評価特徴データ (10,000 画像) と評価ラベルをそれぞれ eval_data と eval_labels にストアします。
Estimator を作成する
次に、モデルのための Estimator (高位モデル訓練、評価、そして推論を遂行するための TensorFlow クラス) を作成します。main() に次のコードを追加します :
# Create the Estimator mnist_classifier = tf.estimator.Estimator( model_fn=cnn_model_fn, model_dir="/tmp/mnist_convnet_model")
model_fn は訓練、評価、そして予測のために使用するモデル関数を指定します ; それに前のセクションで作成した cnn_model_fn を渡します。model_dir 引数はモデルデータ (チェックポイント) がセーブされるディレクトリを指定します (個々では、temp ディレクトリ /tmp/mnist_convnet_model を指定します、しかし貴方の選択した他のディレクトリに自由に変更してください)。
Logging フックをセットアップします
CNN は訓練するのにしばらくかかるかもしれないので、訓練の間に進捗を追跡できるようにあるロギングをセットアップしましょう。CNN の softmax 層からの確率値をログを取る tf.train.LoggingTensorHook を作成するために TensorFlow の tf.train.SessionRunHook を使用できます。次を main() に追加してください :
# Set up logging for predictions tensors_to_log = {"probabilities": "softmax_tensor"} logging_hook = tf.train.LoggingTensorHook( tensors=tensors_to_log, every_n_iter=50)
ログを取りたい tensor の辞書を tensors_to_log にストアします。各キーは選択したラベルでこれはログ出力にプリントされます、そして対応するラベルは TensorFlow グラフの Tensor の名前です。ここでは probabilities は softmax_tensor で見つかります、名前は cnn_model_fn で probabilities を生成したとき softmax 演算子に前に与えています。
Note: name 引数を通して演算子に名前を明示的に割り当てない場合、TensorFlow はデフォルト名を割り当てます。演算子に適用される名前を発見する 2, 3 の簡単な方法は TensorFlow 上でグラフを可視化するか、TensorFlow Debugger (tfdbg) を有効にすることです。
次に、LoggingTensorHook を作成して、tensors_to_log を tensor 引数に渡します。every_n_iter=50 を設定します、これは probabilities が訓練の 50 ステップ毎後にログされるべきであることを指定しています。
モデルを訓練する
さてモデルを訓練する準備ができました、これは train_input_fn を作成して mnist_classifier 上で train() を呼び出すことにより行なうことができます。次のコードを main() に追加します :
# Train the model train_input_fn = tf.estimator.inputs.numpy_input_fn( x={"x": train_data}, y=train_labels, batch_size=100, num_epochs=None, shuffle=True) mnist_classifier.train( input_fn=train_input_fn, steps=20000, hooks=[logging_hook])
numpy_input_fn 呼び出し内で、訓練特徴データとラベルをそれぞれ (辞書として) x と y として渡します。100 の batch_size を設定します (これはモデルは各ステップで 100 サンプルのミニバッチ上で訓練されることを意味します)。num_epochs=None はモデルは指定されたステップ数に達するまで訓練されることを意味します。訓練データをシャッフルするために shuffle=True も設定します。train 呼び出しでは、steps=20000 を設定します (これはモデルが総計 20,000 ステップのために訓練されることを意味します)。logging_hook を hooks 引数に渡します、訓練の間にそれがトリガーされるように。
モデルを評価する
ひとたび訓練が完了したら、MNIST テストセット上でその精度を決定するためにモデルを評価することを望みます。evaluate メソッドを呼び出します、これは model_fn の eval_metric_ops 引数で指定されたメトリクスを評価します。main() に以下を追加します :
# Evaluate the model and print results eval_input_fn = tf.estimator.inputs.numpy_input_fn( x={"x": eval_data}, y=eval_labels, num_epochs=1, shuffle=False) eval_results = mnist_classifier.evaluate(input_fn=eval_input_fn) print(eval_results)
eval_input_fn を作成するために、num_epochs=1 を設定します、その結果モデルはデータの 1 エポックに渡るメトリクスを評価して結果を返します。データをシーケンシャルに iterate するために shuffle=False も設定します。
モデルを実行する
CNN モデル関数、Estimator、そして訓練/評価ロジックをコーディングしました ; 今は結果を見てみましょう。cnn_mnist.py を実行します。
Note: CNN の訓練は非常に計算的に集中的 (= intensive) です。cnn_mnist.py の見積もられる完了時間は貴方のプロセッサに依存して様々ですが、おそらく CPU 上で 1 時間以上でしょう。より迅速に訓練するために、train() に渡される steps の数を減らすことができますが、これは精度に影響することに注意してください。
モデルを訓練するにつれて、次のようなログ出力を見るでしょう :
INFO:tensorflow:loss = 2.36026, step = 1 INFO:tensorflow:probabilities = [[ 0.07722801 0.08618255 0.09256398, ...]] ... INFO:tensorflow:loss = 2.13119, step = 101 INFO:tensorflow:global_step/sec: 5.44132 ... INFO:tensorflow:Loss for final step: 0.553216. INFO:tensorflow:Restored model from /tmp/mnist_convnet_model INFO:tensorflow:Eval steps [0,inf) for training step 20000. INFO:tensorflow:Input iterator is exhausted. INFO:tensorflow:Saving evaluation summary for step 20000: accuracy = 0.9733, loss = 0.0902271 {'loss': 0.090227105, 'global_step': 20000, 'accuracy': 0.97329998}
ここで、テストデータセット上で 97.3 % の精度を獲得しました。
以上