TensorFlowが出てきてから一月も立ってしまいすでに出遅れ感がありますが、初心者用チュートリアルである「MNIST For ML Beginners」を試すためにまずは翻訳をしてみました。
ただし翻訳が間違っている可能性大であるため参考にされる場合はご注意を。
間違いがありましたら指摘いただけると嬉しいです。
MNIST For ML Beginners(機械学習初心者のためのMNIST)
このチュートリアルは機械学習とTensorFlowの両方に不慣れな読み手を対象としたものである。
もしあなたがすでにMNISTとは何か、softmax (多項ロジスティック)回帰とは何かを知っているならば、もっと先のチュートリアルをしたほうがいいだろう。
どのようにプログラムするか学んだ時、あなたが最初にするのは"Hello World."とprintするのが典型的である。
プログラミングがHello Worldを持つのと同じように、機械学習はMNISTを持つ。
MNISTはシンプルなコンピュータービジョンのデータセット(集まり)である。
データセットは以下のような手書き数字の画像から成り立っている。
(図)
またそれぞれの画像にはどの数字であるかのラベルが含まれている。例えば、上記画像のラベルであれば5, 0, 4, 1である。
このチュートリアルの中で、我々は画像を検査しそれらが何の数字であるかを予測するモデルを訓練する。
我々のゴールは実は最先端のパフォーマンスを達成する複雑なモデルを訓練することではなく(だけれども、我々はあなたに後にそのコードを提供する!)、むしろTensorFlowを試すことにある。
このように、我々はSoftmax回帰と呼ばれる大変シンプルなモデルを皮切りにする。
このチュートリアルの実際のコードは大変短く、たった3行で全ての興味深いことが起こる。
しかしながら、その背後にある着想を理解するのに大変重要である。
どのようにTensorFlowが働くかと機械学習のコンセプトの核心の両方である。
このため、我々はコードを通して大変注意して手助けします。
The MNIST Data(MNISTデータ)
MNISTデータはYann LeCun's websiteで提供されている。
あなたの利便性のため、我々は自動的にデータをダウンロードしインストールするとあるPythonコードを含めました。
あなたはこのコードをダウンロードし以下のようにインポートしても良いし、シンプルにコピーアンドペーストしてもよい。
(注:コピーアンドペーストとは、上記リンク先のinput_data.pyをダウンロードして別ファイルとしてインポートしなくても、同じファイル内にペーストして使ってもいいと言っていると思います。)
import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
このダウンロードされたデータは、6万点の訓練データ(mnist.train)と1万点のテストデータ(mnist.test)2つのパーツに分かれている。
この分割は大変重要である。
我々が実際に一般化の何を学んだかを確かめることができるように、我々が学ばない分割されたデータは機械学習の中では必要不可欠である!(注:このあたり翻訳怪しい。おそらく訓練に使う訓練データの他に、訓練には使わないテストデータは必要不可欠だ、ということを言いたいのかと・・・)
始めに言及したように、全てのMNISTデータポイントは手書きの数字の画像と関連するラベルの2つのパーツを持つ。
我々は画像を"xs"と呼び、ラベルを"ys"と呼ぶことにする。両方の訓練セットとテストセット(注:mnist.trainとmnist.testのことと思われる。)はxsとysを含む。
例えば、訓練データの画像はmnist.train.imagesであり、訓練データのラベルはmnist.train.labelsである。
それぞれの画像は28x28ピクセルである。
我々はこれを大きな数字の配列と解釈できる。
(図)
我々はこの配列を28x28=784の数のベクトルに伸ばすことができる。
我々が画像間で一貫していればどのように配列を伸ばすかは重要ではない。
この観点から、MNISTの画像はただの、大変豊かな構造(警告:計算上の集中的な視覚化)を持つ784次元ベクトル空間にある多数の点である。
データを伸ばすことは画像の2次元構造についての情報を捨てる。それは悪いことか?
それは、最高のコンピュータービジョンの方法はこの構造をその後のチュートリアルで活用する。(注:翻訳怪しい。)
しかし、我々がここで使うシンプルな方法はsoftmax回帰であり、それではない。
mnist.train.imagesは[60000, 784]の形を持つ(n次元配列)テンソルが結果である。
最初の次元(60000)は画像を表し、2番めの次元(784)はそれぞれの画像の中のピクセルを表す。
テンソルのそれぞれの項目は0と1の間のピクセル強度であり、特定の画像の中の特定のピクセルである。
(図)
MNISTの中の対応するラベルは0から9の数字である、画像に与えられる数字として記述される。
このチュートリアルの目的のため、我々は"1つのホットなベクトル"として我々のラベルを欲する。
1つのホットなベクトルは多次元の中の0や1次元の中の1のベクトルである。(注:後述のベクトルで1が一つだけあるということ。)
この場合、n番目の数字はn次元の1のベクトルとして表現される。
例えば、3は[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]である。
したがって、mnist.train.labelsはfloat型の[60000, 10]配列である。
(注:60000枚の28x28=784の学習画像があり、それはmnist.train.imagesで[60000, 784]で表され、それらの画像は0から9の数字であるため、mnist.train.labelsは[60000, 10]で表される。)
(図)
我々は今実際に我々のモデルを作る用意がある!
Softmax Regressions(ソフトマックス回帰)
我々はMNISTの全ての画像が数字であるということを知っており、0か9である。
我々は画像を探しだし、どの数字になるかのための確率を与えたい。
例えば、我々のモデルは9の画像を探し、それが9であるのは80%だと確信しているが、8となることを5%のチャンスを与えており(トップループのため)、確かでないため他になる確率を少し与えている。
これは、ソフトマックス回帰は自然でシンプルなモデルであるクラシックなケースである。
もしあなたが幾つかの異なるものの一つになるように確率を割り当てたいならば、ソフトマックスはそうするためのものである。
より遅く、我々がもっと洗練されたモデルを訓練するとき、最後のステップはソフトマックスの層となるだろう。
ソフトマックス回帰は2つのステップを持つ。
最初に、我々は確かなクラスとなる我々のインプットの証拠を大きくしていき、そして我々は証拠を確率に変える。
画像で与えられる証拠を合計することは特定のクラス(注:0-9の数字のこと)の中にあり、我々はピクセル強度の合計値を重み付ける。
重みはもしクラスの中にある画像に反対の証拠となる高い強度をもっているピクセルを持つならばネガティブとなり、そして支持の証拠となるならばポジティブとなる。
以下の図はそれぞれのクラスのために訓練した一つのモデルの重みを表す。
赤はネガティブな重みを表し、青はポジティブな重みを表す。
(図)
我々はまたバイアスと呼ばれるいくつかの追加の証拠を加える。
基本的には、我々はあるものは入力とは無関係であると言えるようになりたい。
クラス$i$のための証拠は入力 $x$ で与えられる結果である。
evidence_i = \sum_{j} W_{i,j} x_j + b_i
$W_i$は重みであり、$b_i$はクラス$i$のためのバイアスであり、$j$は入力画像$x$の中のピクセル合計値のインデックスである。
我々はそれからソフトマックス関数を使うことで我々の予測する確率$y$を証拠の合計から変換する。
y = softmax(evidence)
このソフトマックスは"活性化関数"または"リンク関数"として役に立ち、我々の線形関数の出力を我々が欲する形態へ形付ける。この場合、10ケースに渡る確率分布である。
あなたはこれを証拠の合計を各クラスとなる我々の入力の確率へ変換するものとして考えられる。以下のように定義される。
softmax(x) = normalize(exp(x))
もしこの数式を拡張するならば、あなたは以下を得る。
softmax(x)_i = \frac {exp(x_i)} {\sum_{j} exp(x_j)}
しかしこれはしばしば最初の方法のソフトマックスを考えるのに役立つ。入力をべき乗しそれから正規化することだ。
べき乗することは、ある仮説へと与えられる重みを増幅して増やす証拠のもう一つの単位を意味する。
そして一方で、証拠の単位を一つ減らすことは、仮説は早い重みを手にすることを意味する。
どの仮説もゼロまたはネガティブな重みを持たない。
ソフトマックスはそれからこれらの重みを正規化し、それらは合計して一つとなり、有効な確率分布へと形態を変える。
(ソフトマクス関数についてより直感的な知識を手に入れるために、双方向な視覚化を備えたMichael Nieslenの本のセクションを調べれば良い。
あなたはソフトマックス回帰を、多くの$x$があるにもかかわらず、以下のようなものとして思い浮かべることができる。
各出力のために、我々は$x$の重み付けされた合計を計算し、バイアスを加え、それからソフトマックスを適用する。
(図)
もし我々が数式として記述するならば、以下を得る。
\begin{bmatrix}
y_1 \\
y_2 \\
y_3 \\
\end{bmatrix} = softmax
\begin{pmatrix}
W_{1,1} x_1 + W_{1,2} x_2 + W_{1,3} x_3 + b_1 \\
W_{2,1} x_1 + W_{2,2} x_2 + W_{2,3} x_3 + b_2\\
W_{3,1} x_1 + W_{3,2} x_2 + W_{3,3} x_3 + b_3
\end{pmatrix}
我々はこの手順を"ベクトルで示す"ことができ、行列の掛け算とベクトルの加算に変えることができる。
これは計算効率のために役立つ。(また考えるのに役立つ方法である)
\begin{bmatrix}
y_1 \\
y_2 \\
y_3 \\
\end{bmatrix} = softmax
\begin{pmatrix}
\begin{bmatrix}
W_{1,1} & W_{1,2} & W_{1,3} \\
W_{2,1} & W_{2,2} & W_{2,3} \\
W_{3,1} & W_{3,2} & W_{3,3}
\end{bmatrix}
\cdot
\begin{bmatrix}
x_1 \\
x_2 \\
x_3
\end{bmatrix}
+
\begin{bmatrix}
b_1 \\
b_2\\
b_3
\end{bmatrix}
\end{pmatrix}
よりコンパクトにすると、我々はただ以下のように記述できる。
y = softmax(W_x + b)
Implementing the Regression(回帰の実装)
Pythonによって効果的な数値計算をするために、我々は典型的に、他の言語で実装された高い効果のあるコードを使い、Pythonの外側で行列の掛け算として高価な操作をするNumPyのようなライブラリを使う。
不幸なことに、全ての操作でPythonへ切り替えるのに多くのオーバーヘッド(負荷)がまだあるようだ。
このオーバーヘッドは、もしあなたがデータを転送するのに高いコストがあるだろうGPU上や分散された方法で計算を走らせたいならば特に悪い。
TensorFlowもまたPythonの外側で重い持ち上げをするが、しかしこのオーバーヘッドを避けるためにさらにステップをとる。
Pythonから独立して一つの高価な処理を走らせることの代わりに、TensorFlowは我々に、Pythonの外側で完全に走る相互作用の処理のグラフを描写させる。
(このような取り組み方法はいくつかの機械学習ライブラリの中で見られる。)
TensorFlowを使うために、我々はそれを導入する必要がある。
import tensorflow as tf
我々はこれらの相互作用する処理を操作する象徴的な変数によって描写する。
一つ作ってみよう。
x = tf.placeholder(tf.float32, [None, 784])
$x$は特別な値ではない。
これはplaceholderであり、計算を走らせるために我々がTensorFlowに尋ねるときに入力する値である。
我々は、それぞれ784次元に平行にされたMNISTの画像の各数字を入力できるようになりたい。
我々は[None, 784]の形とともに、平行にした点の数の2-Dテンソルとしてこれを表現する。
(このNoneはどんな長さにもなることのできる次元を意味する。)
我々はまた我々のモデルのために重みとバイアスを必要とする。
我々はこれらを追加の入力のように扱うことを想像できる、しかしTensorFlowはそれを扱うのにさらに良い方法をもつ。Variableである。
Variavleは変更可能なテンソルであり、相互作用の処理のTensorFlowのグラフの中に住む。
それは計算によって使われ変更されることができる。
機械学習アプリケーションのために、一般的にはモデルのパラメータはVariableである。
W = tf.Variable(tf.zeros([784, 10))
b = tf.Variable(tf.zeros([10]))
我々は、Variableの初期値としてtf.Variableを与えられる事によって、これらのVariableを作成する。
この場合、我々は全てゼロのテンソルとして$W$と$b$の両方を初期化する。
我々が$W$と$b$を訓練するまで、それらは大変問題である。
$W$は[784, 10]の形を持つことに注意してほしい、なぜなら我々は784次元の画像のベクトルとそれを掛け算し異なるクラスのための、10次元証拠のベクトルを生み出したいからだ。
$b$は[10]の形を持ち、我々は出力にそれを加算できる。
我々は今我々のモデルを実施できる。
それは一つの行のみで!
y = tf.nn.softmax(tf.matmul(x, W) + b)
最初に、我々は$x$と$W$をtf.matmul($x$, $W$)の表現によって掛け算する。
これは、多数の入力とともに2Dテンソルとなるxを処理するための小さな策略として、$W_x$を持った、我々の数式の中でそれらを掛け算するときにひっくり返される。
我々はそれから$b$を加算し、最後にtf.nn.softmaxを適用する。
それだけだ。
それは我々のモデルを定義するのに、一対の短いセットアップの行の後1行しかかからない。
それは、TensorFlowはソフトマックス回帰を特に簡単にできるよう設計されているからではない。
それは、物理シミュレーションのための機械学習のモデルから、多くの数値計算の種類を描写するためにただ大変柔軟な方法だからである。
そして一旦はっきりとすると、我々のモデルは異なるデバイスで実行されることができる。あなたのコンピューターのCPUやGPU、携帯電話でさえ!
Training(訓練)
我々のモデルを訓練するために、我々はモデルにとって良いとなることを意味するのは何なのかを定義する必要がある。
実際には機械学習の中で我々は一般的にモデルにとって悪いとなることを意味するのは何なのかを定義し、それは費用または損失と呼ばれ、そしてどのように悪いものを小さくしていくかを試す。
しかし2つは等価である。
一つの大変普通で大変よい費用関数は"交差エントロピー"である。
驚いたことに、交差エントロピーは情報論の情報圧縮符号について考えることから発生するが、結局多くの領域の重要なアイデアになる結果となり、機械学習のギャンブルから発生する。
それは以下のように定義される。
H_{y^{'}}(y) = -\sum_i y^{'}_i \log(y_i)
$y$は我々が予測した確率分布であり、$y_{'}$は真の確率分布(我々が入力する一つのホットなベクトル)である。
あるおおよその分別の中で、交差エントロピーは我々の予測が真ということのためになるのにどれだけ効率がわるいか評価している。
交差エントロピーのより詳細についてはこのチュートリアルの範囲外であるが、理解することには価値がある。
交差エントロピーを実装するために我々は最初にplaceholderに正しい回答の入力を加える必要がある。
y_ = tf.placeholder(tf.float32, [None, 10])
それから我々はクロスエントロピー、$-\sum_i y^{'}_i \log(y_i)$を実装できる。
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
最初に、tf.log
は各要素y
の対数を計算する。
次に、我々は各要素y_
と対応する要素tf.log(y)
を掛け合わせる。
最後に、tf.reduce_sum
は全ての要素のテンソルを足し算する。
(注釈として、これは1つの予測のなかの真の交差エントロピーなだけではなく、しかし我々が探した全ての100の画像の交差エントロピーの合計である。100のデータがどれだけ良いかは、どれだけ我々のモデルが一つのデータよりもいいかがより良く描写される。)
我々は我々のモデルにしてもらいたいことを知る今、TensorFlowがそれをすることは大変簡単である。
なぜならばTensorFlowはあなたの計算の全体のグラフを知るからであり、どのようにあなたの変数が最小化して欲しいとした費用に影響するかを効率的に特定するために、自動的に誤差逆伝搬法のアルゴリズムを使うことができる。
それから変数を変更し費用を削減するためにあなたが選択した最適化アルゴリズムを適用できる。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
この場合、我々はTensorFlowにcross_entropy
を0.01の学習係数で勾配降下法を使うことで最小化してほしいという。
勾配降下はシンプルな手順であり、TensorFlowは単純に各変数を経費を削減する方向の中の小さなビットへ変更する。
しかしTensorFlowはまた多くの他の最適化アルゴリズムを提供する。
1行調整するのと同じくらいシンプルなものを使うことで。
TensorFlowが実際にここですること、舞台の後ろで、は新しい処理を誤差伝搬法と勾配降下を実装したあなたのグラフに加える事である。
それから実行されるときに勾配降下訓練のステップを踏み、わずかに費用を削減するよう変数を調整する一つの処理を戻って与えられる。
今、我々は訓練するためのモデルのセットアップを行った。
実行する前の最後のことは、我々は我々が作成した変数の初期化の処理を加える必要があることだ。
init = tf.initialize_all_variables()
我々は今Session
の中でモデルを実行でき、そして変数を初期化する処理を走られられる。
sess = tf.Session()
sess.run(init)
訓練してみよう。我々は1000回の訓練ステップを実行する!
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train_step, feed_dict={x: batch_xs, y_:batch_ys})
ループの各ステップで、我々は訓練セットから100のランダムなデータ点の"バッチ"を得る。
我々はplaceholder
らを交換するためにバッチデータを取り込む train_step
を実行する。
ランダムなデータの小さなバッチを使うことは確率訓練と呼ばれ、この場合は確率勾配降下である。
理想を言えば、我々がすることになるべきことの良い分別を与えてくれるため、我々は訓練の全てのステップで全てのデータを使いたいが、費用が高い。
そのかわり、我々は毎回異なる部分集合を使う。
これをすることは安くて同じ利益を得る。
Evaluation Our Model(モデルの評価)
どの程度我々のモデルが良いのか?
さて最初に、我々が予測した正しいラベルを知っておこう。
tf.argmax
は非常に役立つ関数であり、ある軸に沿ってテンソルの中で最も高い入力のインデックスを与えてくれる。
例えば、tf.argmax(y,1)
は各入力で最も可能性が高い我々のモデルのラベルであり、一方tf.argmax(y_,1)
は正しいラベルである。
我々は我々の予測が真にあっているかを確認するためにtf.equal
を使うことができる。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
これは我々にbooleanのリストを与える。
関数が正しいかを決定するために、我々は浮動小数点にキャストし、それから平均値を取る。
例えば、[True, False, True, True]は[1,0,1,1]になり、(注:平均値は)0.75となる。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
最後に、我々はテストデータで精度を要求する。
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels}))
これは約91%になるべきである。
良いだろうか?
それほどでもない。
事実、少々悪い。
これは大変シンプルなモデルを使ったからである。
ある小さな変更で、我々は97%を得ることができる。
一番良いモデルが99.7%を超える精度を得ることができる!
(より多くの情報は、この結果のリストを見てほしい。)
問題であるのは我々はこのモデルから学習したことである。
まだ、もしこれらの結果についてあなたがよく思わないならば、すっとよく、TensorFlowを使うことでどのようにより洗練されたモデルを構築するかを学ぶ次のチュートリアルを確認してほしい!
以上がMNIST For ML Beginnersの翻訳です。
終わりに
ところどころ翻訳が怪しくて微妙なところもありますが、だいたいしたいことはこれで判明しました。
数式もちらほら出てきてますが、やっていることは単なる行列の掛け算と足し算でそんな難しいことはしていないです。
難しそうなソフトマックス回帰とか勾配降下とかはTensorFlowが良きにはからってくれます。
まあ、ここを理解してこそのものだとは思います。
簡単にまとめると下記になるかと思います。
- 訓練画像とその正解ラベルを読み込む
- ソフトマックス回帰を行う
- 交差エントロピーを計算する
- 交差エントロピーを最小化するよう誤差逆伝搬法を使う
- これを1000回行う
- テスト画像で評価し確認する
次は実際にコードを組んで文字認識ができるか実行してみたいと思います。