TensorFlow : コード解説 : 専門家のための深層 MNIST
* TensorFlow : Tutorials : 専門家のための深層 MNIST に、数式は排除/コード重視の方針で詳細な解説を加筆したものです。
* 基本モデルの説明は TensorFlow : ML 初心者向けの MNIST (コード解説) と重複していますので、噛み砕いた説明はそちらを参照してください。
モデルを作成する前に MNIST データセットをロードし、そして TensorFlow セッションを開始します。
MNIST データのロード
MNIST データセットを自動的にダウンロードしてインポートするスクリプトを含めています。これは ‘MNIST_data’ ディレクトリを作成してそこにデータファイルをストアします。
from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
ここで mnist は軽量のクラスで、訓練、検証そしてテスト用のデータセットを NumPy 配列としてストアしています。ミニバッチ・データを通して反復するための関数も提供されていて、これは後で使います。
TensorFlow InteractiveSession
TensorFlow は計算の実行は高度に効率的な C++ バックエンドに頼っています。このバックエンドへの接続はセッションと呼ばれています。TensorFlow プログラムの共通する利用法は最初にグラフを作成してそしてセッションの中に launch することです。
ここではその代わりに便利な InteractiveSession クラスを使います、これはコードの構成において TensorFlow をより柔軟にします。これはグラフを実行する演算とともに 計算グラフ を構築する不連続な操作を可能にします。IPython のような対話的なコンテキストで作業する時に特に便利です。
import tensorflow as tf sess = tf.InteractiveSession()
【注意点】InteractiveSession を使わないのであれば、セッションを開始して グラフを launch する前に全体の計算グラフを構築する必要があります。
計算グラフ
Python で効率的な数値計算を行なうために典型的には NumPy のようなライブラリを用います。これらは他の言語で実装された効率的なコードを用い、行列乗算のような高コストな演算を Python の外で実行します。しかしながらどの演算についても Python へのスイッチバックから多くのオーバヘッドが発生します。このオーバヘッドは、データ転送に高いコストがつく GPU 上や分散環境での計算を実行したい場合に、特に悪いです。
TensorFlow は Python の外へ重い荷物の持ち出しをしますが、このオーバーヘッドを避けるためにもう一歩先に進めています。Python から独立的に単独の高コストな演算を実行する代わりに TensorFlow は、Python の外部で完全に動作する、相互作用を持つ演算のグラフの記述を可能にしています。(このアプローチは Theano や Torch で使われているものと類似のものです。)
従って Python コードの役割は、この外部計算グラフを構築し、そして計算グラフのどのパートが実行されるべきかを命じることにあります。より詳細は 基本的な使い方 の計算グラフのセクションを見てください。
Softmax 回帰モデルの構築
このセクションでは単一の線形層で softmax 回帰モデルを構築します。次のセクションでは、多層畳み込みネットワークによりこのモデルを拡張します。
プレースホルダー
入力画像と目的出力クラスのためのノードを作成することにより計算グラフの組み立てを開始します。
x = tf.placeholder(tf.float32, shape=[None, 784]) y_ = tf.placeholder(tf.float32, shape=[None, 10])
ここで x と y_ は特定の値ではなく個別のプレースホルダーです — TensorFlow に計算の実行を依頼する時に入力される値です。
入力画像 x は浮動小数点 2d テンソルからなり、形状 [None, 784] を割り当てます。784 は平坦化した MNIST 画像の次元数で、None はバッチサイズに相当する最初の次元が任意のサイズを取りうることを示しています。
目的出力クラス y_ もまた 2d テンソルからなり、各行は one-ホット 10-次元ベクタで該当の MNIST 画像がどの数字クラスに属するかを示しています。
プレースホルダーへの形状引数はオプションですが、矛盾するテンソル形状に由来するバグを TensorFlow が自動的に捉えることを可能にします。
変数
モデルのために重み W とバイアス b を変数として定義します。変数は TensorFlow の計算グラフで生存する値です。計算により使用できて修正も可能です。機械学習アプリケーションにおいて一般にモデル・パラメータは変数となります。
W = tf.Variable(tf.zeros([784,10])) b = tf.Variable(tf.zeros([10]))
tf.Variable への呼び出しで各パラメータに初期値を渡します。ここでは W と b を全て 0 のテンソルとして初期化しています。W は 784 x 10 行列(何故なら 784 入力特徴と 10 出力を持つからです)で b は 10-次元ベクタ(何故なら 10 クラスあるから)です。
変数がセッションで使用可能になるためには、これらはそのセッションで初期化されなければなりません。このステップは指定されている初期値(この場合はテンソルは全て 0)を取り、各変数にそれらを割り当てます。この手続きは全ての変数について一度で行なわれます。
sess.run(tf.initialize_all_variables())
予測クラスとコスト関数
これで回帰モデルが実装できます。わずか1行だけです! ベクタ化された入力画像 x を重み行列 W で乗算して、バイアス b を加算して、そして各クラスに割り当てられた softmax 確率を計算します。
y = tf.nn.softmax(tf.matmul(x,W) + b)
訓練で最小化されるコスト関数は簡単に指定できます。我々のコスト関数は目的とモデルの予測の間の交差エントロピーになります。
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
tf.reduce_sum は全てのクラスのみならず、ミニバッチの全ての画像に渡って合計することに注意してください。全体のミニバッチに対して交差エントロピーを計算します。
モデルを訓練する
モデルと訓練するコスト関数を定義した今、後は TensorFlow を使って訓練するだけです。TensorFlow は計算グラフ全体を知っているので、各変数に関連してコストの勾配を見つけるために自動微分が使用できます。TensorFlow は様々な 組み込みの最適化アルゴリズム を持ちます。この例では、交差エントロピーを降るために 0.01 のステップ長で最急勾配降下を用います。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
TensorFlow がこの一行で実際に行なったことは計算グラフに新しい演算を追加したことです。これらの演算は勾配を計算し、パラメータ更新ステップを計算し、更新ステップをパラメータに適用する演算を含んでいます。
戻り値の演算 train_step は、実行時、勾配降下更新をパラメータに適用します。モデルの訓練は従って train_step を繰り返し実行することにより達成されます。
for i in range(1000): batch = mnist.train.next_batch(50) train_step.run(feed_dict={x: batch[0], y_: batch[1]})
各訓練反復で 50 訓練サンプルをロードします。そして プレースホルダー・テンソル x と y_ を訓練サンプルで置き換えるために feed_dict を用いて train_step 演算を実行します。
【補足】 feed_dict を用いて計算グラフの任意のテンソルを置き換えられることに注意してください — プレースホルダーのみに制限されているわけではありません。
モデルを評価する
モデルはどの程度上手くやれたでしょうか?
最初にどこで正しいラベルを予測できているかを明確にしましょう。tf.argmax は有用な関数である軸に沿ってテンソルのもっとも高い要素のインデックスを与えてくれます。例えば、tf.argmax(y,1) は各入力に対してモデルが尤もそれらしいと考えるラベルで、tf.argmax(y_,1) は真のラベルを表します。予測が真の値にマッチしているか確認するためには tf.equal が使用できます。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
これはブール値のリストを与えてくれます。どの程度の割合いが正しいか決定するために、浮動小数点数値にキャストしてから平均を取ります。例えば、[True, False, True, True] は [1,0,1,1] となりこれは 0.75 となります。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
最後にテストデータ上で精度を評価します。これは約 91% 正しくなるはずです。
print(accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
多層畳み込みネットワークの構築
【注意】 ここからが、TensorFlow : ML 初心者向けの MNIST (コード解説) を引き継いだ内容になっています。
MNIST 上の 91 % の精度の取得は悪いです、当惑するほどに悪いです。このセクションではこれを解決します。非常に簡単なモデルから適度に洗練されたモデル – 小さい畳み込みニューラルネットワークへジャンプします。これは 99.2% 程度の精度へ導いてくれます — 最高水準ではありませんが立派なものです。
重みの初期化
この新しいモデルを作成するためには、多くの重みとバイアスを作成していく必要があります。一般に対称性の破れのためそして 0 勾配を防ぐために、微量のノイズとともに重みを初期化すべきです。ReLU ニューロンを使用していますので “死んだニューロン” を回避するためにわずかに正の初期バイアスでそれらを初期化することは良い実践です。モデル構築の間に繰り返しこれを行なう代わりに、便利な関数を作成しておきましょう。
def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial)
畳み込みとプーリング
TensorFlow は畳み込みとプーリング処理についても多くの柔軟性を与えてくれます。
境界をどのように処理しますか? ストライドの間隔はどのくらい?
この例では常に普通のやり方を選択していきます。畳み込みには 1 のストライドを用い、0 でパディングされますので、出力は入力と同じサイズになります。
プーリングは 2×2 ブロックの分かりやすい古典的な最大プーリングです。
コードをきれいに保つため、これらの演算も関数に抽象化しておきましょう。
def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
第1畳み込み層
これで第1層の実装ができます。畳み込み層と、それに続く最大プーリング層から成り立ちます。畳み込みは各 5×5 パッチのために 32 の特徴を計算します。その重みテンソルは [5, 5, 1, 32] の形状を持ちます。最初の2次元はパッチ・サイズ、次が入力チャネルの数、そして最後が出力チャネルの数です。
各出力チャネルのための成分を持つバイアス・ベクタをまた持ちます。
W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32])
この層を適用するために、第2と3の次元は画像の幅と高さに相当し、そして最後の次元はカラーチャネル数に相当するように、入力 x を 4d テンソルに最初に形状変更しておきましょう。
x_image = tf.reshape(x, [-1,28,28,1])
そして x_image を重みテンソルで畳み込み、バイアスを加算し、ReLU 関数を適用し、
最後に最大プールを適用します。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1)
第2畳み込み層
深層ネットワークを構築するためにこのタイプの層を幾つか積み重ねます。
第2層は各 5×5 パッチに対して 64 の特徴を持ちます。
W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2)
全結合層
画像サイズが 7×7 まで減じられた今、画像全体の処理を可能にするために 1024 ニューロンの全結合層を追加します。
プーリング層からのテンソルをベクタのバッチに形状変更し、重み行列を乗算し、バイアスを加算し、ReLU を適用します。
W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
ドロップアウト
過学習を減じるために、読み出し層 (readout layer) の前にドロップアウトを適用します。
確率のためにプレースホルダーを作成します、これはニューロンの出力をドロップアウトの間保持するものです。これにより訓練中はドロップアウトを有効にし、テストの間には無効にすることを可能にしてくれます。
TensorFlow の tf.nn.dropout OP はニューロン出力のマスクに加えてスケーリングを自動的に処理し、ドロップアウトは追加のスケーリングなしに動作します。
keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
読み出し層 (Readout Layer)
最後に(前述の基本モデルの softmax 回帰の一つの層のように)softmax 層を追加します。
W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
モデルを訓練し評価する
このモデルはどの程度上手くやるでしょう?
このモデルを訓練し評価するために、前述の単純な単層 SoftMax ネットワークのものとほぼ同一のコードを用います。違いは次のようなものです :
* 最急勾配降下オプティマイザーをより洗練された ADAM オプティマイザーで置き換えます。
* ドロップアウト率を制御するために feed_dict の追加パラメータ keep_prob を含めます。
* そして訓練プロセスにおいて 100番目の反復毎にロギングを追加します。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) sess.run(tf.initialize_all_variables()) for i in range(20000): batch = mnist.train.next_batch(50) if i%100 == 0: train_accuracy = accuracy.eval(feed_dict={ x:batch[0], y_: batch[1], keep_prob: 1.0}) print("step %d, training accuracy %g"%(i, train_accuracy)) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) print("test accuracy %g"%accuracy.eval(feed_dict={ x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
このコードを実行した後の最終的なテストセットの精度は約 99.2 % になるでしょう。
(訳注 : 実測で 99.26 % が確認できました。)
私たちは TensorFlow を使って、どのように素早く簡単にかなり洗練された深層学習モデルを構築し、訓練し、そして評価するかを学びました。
MNIST 畳み込みモデルの実装まとめ
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True) def weight_variable(shape): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial) def bias_variable(shape): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial) def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') def max_pool_2x2(x): return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') x = tf.placeholder(tf.float32, [None, 784]) y_ = tf.placeholder(tf.float32, [None, 10]) x_image = tf.reshape(x, [-1, 28, 28, 1]) W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) W_fc1 = weight_variable([7 * 7 * 64, 1024]) b_fc1 = bias_variable([1024]) h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) keep_prob = tf.placeholder(tf.float32) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) sess = tf.InteractiveSession() sess.run(tf.initialize_all_variables()) for i in range(20000): batch = mnist.train.next_batch(50) if i%500 == 0: train_accuracy = accuracy.eval(feed_dict={x:batch[0], y_: batch[1], keep_prob: 1.0}) print("step %d, training accuracy %g"%(i, train_accuracy)) train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5}) print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0}))
MNIST 畳み込みモデルの実行例
以下はクラスキャット深層学習サービスを利用して実測した結果です :
$ time python deep_mnist_for_experts.py step 0, training accuracy 0.1 step 500, training accuracy 0.94 step 1000, training accuracy 0.98 step 1500, training accuracy 0.98 step 2000, training accuracy 1 step 2500, training accuracy 1 step 3000, training accuracy 0.9 step 3500, training accuracy 0.96 step 4000, training accuracy 0.98 step 4500, training accuracy 0.98 test accuracy 0.9863 real 1m46.310s user 1m30.557s sys 0m37.777s
$ time python deep_mnist_for_experts.py I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcublas.so.7.5 locally I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcudnn.so.4.0.7 locally I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcufft.so.7.5 locally I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcuda.so.1 locally I tensorflow/stream_executor/dso_loader.cc:105] successfully opened CUDA library libcurand.so.7.5 locally Extracting /tmp/data/train-images-idx3-ubyte.gz Extracting /tmp/data/train-labels-idx1-ubyte.gz Extracting /tmp/data/t10k-images-idx3-ubyte.gz Extracting /tmp/data/t10k-labels-idx1-ubyte.gz I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:900] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero I tensorflow/core/common_runtime/gpu/gpu_init.cc:102] Found device 0 with properties: name: GRID K520 major: 3 minor: 0 memoryClockRate (GHz) 0.797 pciBusID 0000:00:03.0 Total memory: 4.00GiB Free memory: 3.95GiB I tensorflow/core/common_runtime/gpu/gpu_init.cc:126] DMA: 0 I tensorflow/core/common_runtime/gpu/gpu_init.cc:136] 0: Y I tensorflow/core/common_runtime/gpu/gpu_device.cc:717] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GRID K520, pci bus id: 0000:00:03.0) I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 1.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 2.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 4.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 8.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 16.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 32.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 64.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 128.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 256.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 512.0KiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 1.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 2.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 4.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 8.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 16.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 32.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 64.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 128.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 256.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 512.00MiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 1.00GiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 2.00GiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:51] Creating bin of max chunk size 4.00GiB I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:73] Allocating 3.66GiB bytes. I tensorflow/core/common_runtime/gpu/gpu_bfc_allocator.cc:83] GPU 0 memory begins at 0x7023a0000 extends to 0x7ec688000 step 0, training accuracy 0.1 step 500, training accuracy 0.9 step 1000, training accuracy 0.94 step 1500, training accuracy 0.96 step 2000, training accuracy 1 step 2500, training accuracy 0.96 step 3000, training accuracy 0.98 step 3500, training accuracy 0.96 step 4000, training accuracy 1 step 4500, training accuracy 1 step 5000, training accuracy 0.96 step 5500, training accuracy 0.98 step 6000, training accuracy 0.98 step 6500, training accuracy 1 step 7000, training accuracy 1 step 7500, training accuracy 0.96 step 8000, training accuracy 0.96 step 8500, training accuracy 1 step 9000, training accuracy 0.98 step 9500, training accuracy 1 step 10000, training accuracy 1 step 10500, training accuracy 1 step 11000, training accuracy 1 step 11500, training accuracy 1 step 12000, training accuracy 1 step 12500, training accuracy 1 step 13000, training accuracy 1 step 13500, training accuracy 1 step 14000, training accuracy 1 step 14500, training accuracy 1 step 15000, training accuracy 1 step 15500, training accuracy 1 step 16000, training accuracy 1 step 16500, training accuracy 1 step 17000, training accuracy 1 step 17500, training accuracy 1 step 18000, training accuracy 1 step 18500, training accuracy 1 step 19000, training accuracy 1 step 19500, training accuracy 0.96 test accuracy 0.9926 real 6m57.061s user 6m5.816s sys 2m21.036s
以上