祭囃子は遠く、

理系大学生のハッピーエヴリディを書いていきます。

TensorFlow:チュートリアル②

はいどーも♪
前回は基礎の基礎を書いたので、今回は軽い学習の章について書いて行きたいと思います。
例によって以下の焼増しです。
Getting Started  |  TensorFlow
今回はGetting Started With TensorFlowの後半と、MNIST For ML Beginnersに触れていきます。

1.最初に

全然機械学習やったことないので、僕が進めて行くのとパラレルに書いています。
信ぴょう性はこの記事もありません。
前回全コメ+ソース全文を載せてたので、今回も同様の形式でやります。
実行結果の掲載は気分次第で載せますが、載ってなかったらそういうことです。

今回の流れとしては(出来たら)

  1. すごく簡単な機械学習
  2. 手書き文字の画像認識について(概要)
  3. モデル
  4. 原理、クロスエントロピーについてなど
  5. 結果と次回

でいきたいと思います。

2.すごく簡単な機械学習(前回の続き)

前回の基本的な操作を用いて、簡単な機械学習をしていきます。(元記事の後半部分)

変数定義とかループのやり方さえ分かれば、ゴリ押しで大抵のプログラムはかけると信じています。
つまり、前回の記事がわかったら理論上なんでも書けるっていう事ですよ(クソザコプログラマー感)

学習と呼べるレベルかアヤシイですが、学習のhelloworldを見て行きましょう。
以下にいきなりコード貼ります。スラスラ読めたらそのあとの解説は読まなくていいです。

一応学習の流れだけ書いておくと

  1. 学習させたいデータを入力して、設定したモデルで計算
  2. 正解と比べてどの程度ずれているのかを計算
  3. パラメータ修正(正解に近付ける)

って感じです。まあ人間でもとりあえずトライしてみて、ミスってたらその都度やり方を変えますよね、それと同じです。

import tensorflow as tf #TensorFlowをtfとして読み込み

# モデルのパラメータ(これを変更していって入力が正解を吐き出すようにする)
W = tf.Variable([.3], dtype=tf.float32)#入力に対する重み
b = tf.Variable([-.3], dtype=tf.float32)#入力に依らない定数(バイアス)
# モデルとその入力と出力
x = tf.placeholder(tf.float32)#ここに教師データを入力
linear_model = W*x + b #モデルの式(これを計算して出力)
y = tf.placeholder(tf.float32)#ここに正解データを入れる

#正解からのずれを計算
loss = tf.reduce_sum(tf.square(linear_model - y)) # 正解とのズレの2乗和
#最適化手法(正解への近付き方にも色々有る)
optimizer = tf.train.GradientDescentOptimizer(0.01)#一番ポピュラーな最急降下法で正解に近づける
train = optimizer.minimize(loss)#上の方法で正解からのズレを小さくして行きましょう

#教え込みたいデータ
x_train = [1, 2, 3, 4]#入力データ(教師データ、今回は固定)
y_train = [0, -1, -2, -3]#正解データ(これも今回はハッキリと決まっている)
#学習
init = tf.global_variables_initializer()#変数の初期化をinitに
sess = tf.Session()#セッション打つのめんどいしsessにぶち込む
sess.run(init) #初期化(init)実行(sess.run)
for i in range(1000):#1000回学習
  sess.run(train, {x: x_train, y: y_train})#xに教師データ、yに正解データを入れて学習

#パラメータの値と正解からのズレの表示
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})#左辺にそれぞれW,b,lossを代入
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))#それを表示

最初は目がびっくりして追いつかない事があっても、理解出来ないってレベルではないと思いますが解説します。

まず、今回の学習の目標は線型モデルW*x + bx_train = [1, 2, 3, 4]を入れた時にy_train = [0, -1, -2, -3]が帰ってくるようにするというものです。普通に解こうと思ったら手で解けます。
正解が分かっているものを最初にやるのはとても大事なので、わざわざ機械に解かせてるわけです。

順を追って説明すると、まず始めにモデルで使う変数を定義しています。Wとかbが主役です。(繰り返しますが、これを変えていって正解を吐き出させるので)
その後、入力データxを定義し(実用上入力は動的に代入されるので初期値を入れたりする必要はない)正解データy
を定義しています。(こちらも同様)
ここから「正解との近さ」と「正解への近付き方」を定めるわけです。
まず、「正解の近さ」というのはどの様に定義されるでしょう。コードでは

loss = tf.reduce_sum(tf.square(linear_model - y)) # 正解とのズレの2乗和

これで定義されています。正解データの値と入力データをモデルにぶち込んで得られた値の差の2乗です。
これは非常にシンプルかつ直感的で良い定義だと思います。単純に自分の家と駅の離れ具合(出力と正解の差に相当)を聞かれたら、普通は距離で表しますよね(時間というのもアリですが実質同じ事です)
いわばこの「正解との距離」が0になればそれは正解な訳です。これで一つ目の「正解との近さ」は定義し終えました。
二つ目の「正解への近付き方」ですが、これは無数にあります。現実でも飛行機とか徒歩とか色々モノや場所への近付き方ありますよね(実際はこれらはあまり良い例えじゃないですが・・・)その中から一つ選ぶわけです。
今回は最急降下法(GradientDescentoMethod)を用いているので、その説明をします。
名前にGradientと入っているくらいなので、勾配を使って関数の最小値(勾配0)を探す、というアルゴリズムです。
先にアルゴリズムの式を以下に載せます。
f:id:ta_ichi:20180111200633p:plain:w200
一般的には
f:id:ta_ichi:20180111200936p:plain:w250
こんな感じでしょうか。(ここ直すのめんどいからやりませんがあんまり一般的じゃないですね)

これだけパッと見てあーーーってなるかもしれませんが一応解説します。
例として一番簡単なのは二次関数です。

わりとと数式を使う解説

f:id:ta_ichi:20180111203037p:plain:w100
わざわざこの様に書いたのにも理由があります。高校物理を思い出すと、これは加速度aで時間tだけ進んだ時の距離の式でよく見ました。右辺が今まででいう正解との距離に相当します(つまりこれを0にする作業)これを上の式に当てはめてみましょう。
f:id:ta_ichi:20180111204030p:plain:w200
こう書くとこのアルゴリズムの正体がわかりやすくなってきます。
ある時刻t^kから、適当な係数×その時刻での速さで、原点(t=0)に向かって近づいていくという式」になっています。
また、t=0を代入するとそこで更新が止まるのも確認できます。(そんなに大変じゃないので、自分でa*α=0.01、t=0.5とかで計算してみて下さい)
この時、問題はある適当な係数を速度にかけるというところで、これは大きすぎるとt=0を通り越してしまうのがわかると思います。(例えば極端な話t=1から始めてa*αが100000だったらダメですよね)
なのでそこそこ小さな値に設定しなければならないわけです。でも小さすぎると計算回数が増えてしまう・・・。こういうのは誰か熱心な人が大体この値にしとけばオッケーって決めてて、コードでは0.01になっているのはそういう事情でしょう。
正直思ったよりわかりやすい解説にならなかったのを感じている

図でなんとなく解説

f:id:ta_ichi:20180111234044j:plain:w300
関数の上に球を置くと極小値に向かうのが想像できると思います。これが最急降下法といっても良いでしょう。
点線の球の間隔がα(速度vでどの程度の時間進む事にするか)に対応します。

というわけで

アルゴリズムがわかったので、今回のヤツを見ていきましょう。
f:id:ta_ichi:20180111194956p:plain:w200
今回は変数がW,bとあってこの両方を変えながら正解との距離を縮めていくわけです。この時は

f:id:ta_ichi:20180111235606p:plain:w200
f:id:ta_ichi:20180111235629p:plain:w200
それぞれについて偏微分して二つについてどんどん傾きが小さくなる方(速度小の方)に行けば、いつか「正解までの距離」が0になる地点が見つかると思います。

注意

f:id:ta_ichi:20180112000558j:plain:w300
この場合最急降下法は使えるでしょうか?関数の上に載せられた級は明らかに手前の極小値(not最小値)にハマってしまいそうですね。
これが最急降下法の弱点で、局所解(極小値)にハマりやすいです。図のレベルならαの値をチューニングしたり、スタートの位置を変えれば最小値が見つかりますが、機械学習ではパラメータが数百とか数千とか越えることがあるみたいです。数千次元の空間で球をいろんなところから転がしていたらキリがありません。なので、他の方法を使ったりします。(他の方法でも同様の問題は抱えていますが・・・。)でも結構高級なライブラリなので、こんな単純なことだけをやる最急降下法じゃないのかもしれません。これはこれで強力な方法なので、当面はこれを使っていきます。

やっと続き

話がそれたというか本筋にやっと戻りますが、こうして正解との距離を縮めて学習していくわけです。
上を実行すると

W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11

と出てくると思います。W=-1とb=1を突っ込むとxを入力したときに正解のyが帰ってくるのがすぐに確かめられるので、上手に学習できていることが確認できました。めでたしめでたし

予告

簡単な学習なので、簡単に書くつもりが長くなってしまいました。
残りはチュートリアル③にまわしていきたいと思います。
今週中には書きたい・・・。
あと最近やっとDeep MNIST for Expertsのコンテンツへの理解がやっと追いついてきたので、もうちょっと高級な記事もかけるかもしれない。(初心者の域を脱さない)