TensorFlow : TensorLayer : チュートリアル (2) 画像分類 / Autoencoder (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/02/2018
* 本ページは、TensorLayer の以下のドキュメントの一部を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
チュートリアル (2)
MNIST example を実行する
チュートリアルの最初のパートでは、TensorLayer のソース配布に含まれる MNIST サンプルを単に実行します。MNIST データセットは様々な画像処理システムを訓練するために一般に使用される 60000 手書き数字を含みます。
フォルダに入り tutorial_mnist.py サンプル・スクリプトを実行します :
python tutorial_mnist.py
総てが正しくセットアップされている場合、次のような出力を得るでしょう :
tensorlayer: GPU MEM Fraction 0.300000 Downloading train-images-idx3-ubyte.gz Downloading train-labels-idx1-ubyte.gz Downloading t10k-images-idx3-ubyte.gz Downloading t10k-labels-idx1-ubyte.gz X_train.shape (50000, 784) y_train.shape (50000,) X_val.shape (10000, 784) y_val.shape (10000,) X_test.shape (10000, 784) y_test.shape (10000,) X float32 y int64 [TL] InputLayer input_layer (?, 784) [TL] DropoutLayer drop1: keep: 0.800000 [TL] DenseLayer relu1: 800, relu [TL] DropoutLayer drop2: keep: 0.500000 [TL] DenseLayer relu2: 800, relu [TL] DropoutLayer drop3: keep: 0.500000 [TL] DenseLayer output_layer: 10, identity param 0: (784, 800) (mean: -0.000053, median: -0.000043 std: 0.035558) param 1: (800,) (mean: 0.000000, median: 0.000000 std: 0.000000) param 2: (800, 800) (mean: 0.000008, median: 0.000041 std: 0.035371) param 3: (800,) (mean: 0.000000, median: 0.000000 std: 0.000000) param 4: (800, 10) (mean: 0.000469, median: 0.000432 std: 0.049895) param 5: (10,) (mean: 0.000000, median: 0.000000 std: 0.000000) num of params: 1276810 layer 0: Tensor("dropout/mul_1:0", shape=(?, 784), dtype=float32) layer 1: Tensor("Relu:0", shape=(?, 800), dtype=float32) layer 2: Tensor("dropout_1/mul_1:0", shape=(?, 800), dtype=float32) layer 3: Tensor("Relu_1:0", shape=(?, 800), dtype=float32) layer 4: Tensor("dropout_2/mul_1:0", shape=(?, 800), dtype=float32) layer 5: Tensor("add_2:0", shape=(?, 10), dtype=float32) learning_rate: 0.000100 batch_size: 128 Epoch 1 of 500 took 0.342539s train loss: 0.330111 val loss: 0.298098 val acc: 0.910700 Epoch 10 of 500 took 0.356471s train loss: 0.085225 val loss: 0.097082 val acc: 0.971700 Epoch 20 of 500 took 0.352137s train loss: 0.040741 val loss: 0.070149 val acc: 0.978600 Epoch 30 of 500 took 0.350814s train loss: 0.022995 val loss: 0.060471 val acc: 0.982800 Epoch 40 of 500 took 0.350996s train loss: 0.013713 val loss: 0.055777 val acc: 0.983700 ...
このサンプルスクリプトは、多層パーセプトロン、ドロップアウト、Dropconnect, Stacked Denoising Autoencoder そして畳込みニューラルネットワークを含む、異なるモデルを試すことを可能にします。if __name__ == ‘__main__’: から異なるモデルを選択します。
main_test_layers(model='relu') main_test_denoise_AE(model='relu') main_test_stacked_denoise_AE(model='relu') main_test_cnn_layer()
MNIST サンプルを理解する
さてそれを起こすために何が必要かを調査しましょう!追随するために、ソースコードを開いてください。
Preface
貴方が気づく最初のことは TensorLayer の他にも、numpy と tensorflow もインポートすることです :
import tensorflow as tf import tensorlayer as tl from tensorlayer.layers import set_keep import numpy as np import time
既知のように、TensorLayer は TensorFlow の上に構築されています、それは何某かのタスクを手伝う補足であり、置き換えではありません。TensorLayer を幾つかの vanilla TensorFlow コードと常に混在させるでしょう。set_keep は Denoising Autoencoder を使用するとき確率を保持する placeholder にアクセスするために使用されます。
データをロードする
コードの最初のピースは関数 load_mnist_dataset() を定義します。その目的は MNIST データセットを (もしそれがまだダウンロードされていないのであれば) ダウンドードしてそれを通常の numpy 配列の形式で返すことです。関連するような TensorLayer は全くありませんので、このチュートリアルのためには、それを次のように認識できます :
X_train, y_train, X_val, y_val, X_test, y_test = \ tl.files.load_mnist_dataset(shape=(-1,784))
X_train.shape は (50000, 784) です、これは次のように解釈されます: 50,000 画像と各画像は 784 ピクセルを持ちます。y_train.shape は単純に (50000,) です、これは X_train の同じ長さのベクトルで各画像のために整数クラスラベルを与えます – つまり、(数字を描いた人間の注釈者に従う) 画像で示される 0 と 9 の間の数字です 。
畳み込みニューラルネットワーク・サンプルのためには、MNIST は次のように 4D バージョンとしてロードされます :
X_train, y_train, X_val, y_val, X_test, y_test = \ tl.files.load_mnist_dataset(shape=(-1, 28, 28, 1))
X_train.shape は (50000, 28, 28, 1) で、これはそれぞれ 1 チャネル、28 行と 28 列を持つ 50,000 画像を表します。チャネル 1 はそれがグレースケール画像であるためで、総てのピクセルは 1 つの値だけを持ちます。
モデルを構築する
これは TensorLayer が介入するところです。それは層を作成してスタックあるいはマージすることにより任意に構造化されたニューラルネットワークを定義することを可能にします。
上で言及したように、tutorial_mnist.py は 4 つのタイプのモデルをサポートし、そして同じインターフェイスの容易に交換可能な関数を通してそれを実装しています。最初に総てのステップを詳細に説明する固定されたアーキテクチャの多層パーセプトロン (MLP) を作成する関数を定義します。それから Denoising Autoencoder (DAE) を実装し、その後で総ての Denoising Autoencoder をスタックしてそれらを再調整します。加えて、tutorial_mnist_simple.py で MNIST データセットのための単純なサンプル、tutorial_cifar10_tfrecord.py で CIFAR-10 データセットのための CNN サンプル。
多層パーセプトロン (MLP)
最初のスクリプト main_test_layers() はそれぞれ 800 ユニットの 2 つの隠れ層を作成し、10 ユニットの softmax 出力層が続きます。それは入力データに 20% ドロップアウトそして隠れ層に 50% ドロップアウトを適用します。
データをネットワークに供給するために、TensorFlow placeholder が次のように定義される必要があります。ここで None はコンパイルの後ネットワークが任意のバッチサイズの入力データを受け取ることを意味します。x は X_train データを保持するために使用されて y_ は y_train データを保持するために使用されます。前もってバッチサイズを知りこの柔軟性を必要としない場合には、ここでバッチサイズを与えるべきです – 特に畳み込み層のためには、これは TensorFlow にある最適化を適用することを可能にします。
x = tf.placeholder(tf.float32, shape=[None, 784], name='x') y_ = tf.placeholder(tf.int64, shape=[None, ], name='y_')
TensorFlow の各ニューラルネットワークの基礎はネットワークに続いて供給される入力データを表わす InputLayer インスタンスです。InputLayer はどのような特定のデータにもまだ結び付けられていないことに注意してください。
network = tl.layers.InputLayer(x, name='input')
最初の隠れ層を追加する前に、入力データに 20% ドロップアウトを適用します。これは DropoutLayer インスタンスを通して実現されます :
network = tl.layers.DropoutLayer(network, keep=0.8, name='drop1')
最初のコンストラクタ引数は incoming 層であることに注意してください、2 番目の引数は活性値のための保持確率です。さて続いて 800 ユニットの最初の完全結合隠れ層で進めます。DenseLayer をスタックするとき注意してください。
network = tl.layers.DenseLayer(network, n_units=800, act = tf.nn.relu, name='relu1')
再度、最初のコンストラクタ引数は network の上にネットワークをスタックしていることを意味しています。n_units はこの完全結合層に対するユニット数を与えます。act は活性化関数を取り、それらの幾つかは tensorflow.nn と tensorlayer.activation で定義されます。ここでは rectifier を選択しましたので、ReLU を得ます。50 % のドロップアウト、もう一つの 800-ユニット dense 層と再度 50 % ドロップアウトを追加します :
network = tl.layers.DropoutLayer(network, keep=0.5, name='drop2') network = tl.layers.DenseLayer(network, n_units=800, act = tf.nn.relu, name='relu2') network = tl.layers.DropoutLayer(network, keep=0.5, name='drop3')
最後に、完全結合出力層を追加します、この n_units はクラス数に等しいです。softmax は計算をスピードアップするために内部的には tf.nn.sparse_softmax_cross_entropy_with_logits() で実装されていますので、 最後の層では identity を使用することに注意してください、より詳細は tl.cost.cross_entropy() で。
network = tl.layers.DenseLayer(network, n_units=10, act = tf.identity, name='output')
上で言及したように、各層はその incoming 層(s) にリンクされていますので、TensorLayer のネットワークにアクセスするためには出力層が必要なだけです :
y = network.outputs y_op = tf.argmax(tf.nn.softmax(y), 1) cost = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(y, y_))
ここで、network.output は network からの (one hot フォーマットの) 10 identity 出力で、y_op は整数出力ですクラスインデックスを表します。一方で cost はターゲットと予測ラベルの間の交差エントロピーです。
Denoising Autoencoder (DAE)
オートエンコーダは教師なし学習モデルで、表現特徴を抽出することができ、それはデータの学習生成モデルと Greedy layer-wise な事前訓練のためにより広く使用されるようになりました。vanilla オートエンコーダについては、深層学習チュートリアル を見てください。
スクリプト main_test_denoise_AE() は 50% の corrosion 率を持つ Denoising Autoencoder を実装しています。オートエンコーダは次のように定義されます、ここではオートエンコーダは DenseLayer で表わされます :
network = tl.layers.InputLayer(x, name='input_layer') network = tl.layers.DropoutLayer(network, keep=0.5, name='denoising1') network = tl.layers.DenseLayer(network, n_units=200, act=tf.nn.sigmoid, name='sigmoid1') recon_layer1 = tl.layers.ReconLayer(network, x_recon=x, n_units=784, act=tf.nn.sigmoid, name='recon_layer1')
DenseLayer を訓練するためには、単純に ReconLayer.pretrain() を実行します、denoising Autoencoder を使用する場合、corrosion 層 (DropoutLayer) の名前が次のように指定される必要があります。特徴画像をセーブするために、save を True に設定します。異なるアーキテクチャとアプリケーションにより多くの種類の pre-train メトリクスがあります。sigmoid 活性のためには、オートエンコーダは KL ダイバージェンスを使用して実装できます、一方で rectifier のためには、活性出力の L1 正則化が出力をスパースにすることができます。そのため ReconLayer のデフォルトの挙動は KLD だけを提供して sigmoid 活性化関数のために交差エントロピーそして rectifying 活性化関数のために活性出力の L1 と平均二乗誤差 (mean-squared-error) です。貴方自身の pre-train メトリクスを達成するために ReconLayer を修正することを勧めます。
recon_layer1.pretrain(sess, x=x, X_train=X_train, X_val=X_val, denoise_name='denoising1', n_epoch=200, batch_size=128, print_freq=10, save=True, save_name='w1pre_')
加えて、スクリプト main_test_stacked_denoise_AE() は一つのネットワークにどのように複数のオートエンコーダをスタックして再調整するかを示します。
畳み込みニューラルネットワーク (CNN)
最後に、main_test_cnn_layer() は 2 つの CNN 層と max pooling ステージ、完全結合隠れ層そして完全結合出力層を作成します。より多くの CNN サンプルは tutorial_cifar10_tfrecord.py のような他のサンプルで見つかります。
network = tl.layers.Conv2d(network, 32, (5, 5), (1, 1), act=tf.nn.relu, padding='SAME', name='cnn1') network = tl.layers.MaxPool2d(network, (2, 2), (2, 2), padding='SAME', name='pool1') network = tl.layers.Conv2d(network, 64, (5, 5), (1, 1), act=tf.nn.relu, padding='SAME', name='cnn2') network = tl.layers.MaxPool2d(network, (2, 2), (2, 2), padding='SAME', name='pool2') network = tl.layers.FlattenLayer(network, name='flatten') network = tl.layers.DropoutLayer(network, keep=0.5, name='drop1') network = tl.layers.DenseLayer(network, 256, act=tf.nn.relu, name='relu1') network = tl.layers.DropoutLayer(network, keep=0.5, name='drop2') network = tl.layers.DenseLayer(network, 10, act=tf.identity, name='output')
モデルを訓練する
tutorial_mnist.py スクリプトの残りの部分はセットアップして交差エントロピーだけを使用して MNIST データセットに渡り訓練ループを実行することを処理します。
Dataset iteration
iteration 関数は、与えられた項目数のミニバッチでそれぞれ入力データとターゲットの 2 つの numpy 配列 に渡り同期的に反復するためのものです。より多くの iteration 関数は tensorlayer.iterate で見つかります。
tl.iterate.minibatches(inputs, targets, batchsize, shuffle=False)
損失と更新式
続けて、訓練で最小化されるべき損失式を作成します :
y = network.outputs y_op = tf.argmax(tf.nn.softmax(y), 1) cost = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(y, y_))
より多くの損失または正則化はここで適用できます。例えば、重み行列上の max-norm を適用するために、次の行を追加できます。
cost = cost + tl.cost.maxnorm_regularizer(1.0)(network.all_params[0]) + tl.cost.maxnorm_regularizer(1.0)(network.all_params[2])
解いている問題に依拠して、異なる損失関数が必要となるでしょう、より多くは tensorlayer.cost を見てください。変数を得るために、network.all_params を使用する以外に、特定の変数を文字列名で得るために tl.layers.get_variables_with_name を使用することもできます。
ここでモデルと損失関数を持ち、ネットワークを訓練するための更新式/演算を作成します。TensorLayer は多くの optimizer を提供しません、代わりに TensorFlow の optimizer を使用しました :
train_params = network.all_params train_op = tf.train.AdamOptimizer(learning_rate, beta1=0.9, beta2=0.999, epsilon=1e-08, use_locking=False).minimize(cost, var_list=train_params)
ネットワークを訓練するために、feed_dict へデータと 保持確率を供給しました。
feed_dict = {x: X_train_a, y_: y_train_a} feed_dict.update( network.all_drop ) sess.run(train_op, feed_dict=feed_dict)
その一方で、検証とテストについては、少し異なる方法を使用します。総ての Dropout, Dropconnect, Corrosion 層は無効にされる必要があります。総ての network.all_drop を 1 に設定するために tl.utils.dict_to_one を使用します。
dp_dict = tl.utils.dict_to_one( network.all_drop ) feed_dict = {x: X_test_a, y_: y_test_a} feed_dict.update(dp_dict) err, ac = sess.run([cost, acc], feed_dict=feed_dict)
評価のためには、分類精度のための式を作成します :
correct_prediction = tf.equal(tf.argmax(y, 1), y_) acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
What Next?
私達はまた tutorial_cifar10_tfrecord.py でより多くの進んだ画像分類サンプルを持ちます。コードとノートを読み、より多くの訓練データをどのように生成するかそして local response normalization とは何かを理解してください。その後で、残差ネットワークを実装することを試してください (Hint: you may want to use the Layer.outputs)。
以上