多分もっともわかりやすいTensorFlow 入門 (Introduction)

  • 60
    いいね
  • 0
    コメント

TensorFlow を試してみた

まず、Get started を読んでみます。
Introduction | TensorFlow

いきなりTensorFlow のサンプルコードがでてきますが、ここではサンプルデータを使って、どれだけシンプルにモデルを構築できるかを見せてくれているわけです。ここはそもそも機械学習の知識が皆無だと、逆に意味がわからないと思います。

いろいろなTensorFlow の記事を見ると「機械学習知識ゼロでもいける」というのもみかけますが、TensorFlow はあくまでもツールにすぎないので、最低限の知識はあったほうが今後のためにも良いと思います。

次に、伝統的なMNIST(手書き文字認識)に取り組むということでデータのダウンロードを勧められます。MNIST 未経験なら「青いクスリ」を、ある程度機械学習に慣れた方は「赤いクスリ」をと勧められますので、ここは迷わず「赤いクスリ」を手にします。

だって、せっかく1ヵ月も機械学習の勉強をしたのだから。

で、ここで気づくのですが「MNIST をやってみよう」とかいいつつ、まだ TensorFlow のインストールすらしていませんでした。このGet started は順番があまりよろしくないです。インストールと最初のサンプルコードの解説で、Qiita 1記事としては十分な量になってしまいそうなので、MNIST はまた別の機会にやろうと思います。

TensorFlow install on Mac OS X

(参考)
Download and Setup | TensorFlow

GPU を使うなら... と書いてありますが、いまは機械学習の勉強に注力したいので、それ以外の環境構築でつまづくことを避け、GPU 環境は作りません。GPU環境構築まわりはすべて飛ばします。

いま手元にある Virtualenv の環境で用意するのが簡単そうでしたのでそうしました。

% virtualenv --system-site-packages ~/env/tensorflow

サイトのインストール手順では環境設定が格納されるディレクトリとして ~/tensorflow としていましたが、僕は環境を ~/env にまとめているので上記のようにしました。みなさんの環境にあわせて適宜変えてください。

% source ~/env/tensorflow/bin/activate

これでコンソールが下記のようになり、他の環境を汚さずにtensorflow 用の環境用意の準備が整いました。この中にtensorflow をインストールしていきます。

(tensorflow) %

サイトの手順に従い

(tensorflow) % export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/mac/cpu/tensorflow-0.12.1-py3-none-any.whl
(tensorflow) % pip3 install --upgrade $TF_BINARY_URL

これでインストール完了です。下記で動作確認して問題なければOKです。

(tensorflow) % python
...
>>> import tensorflow as tf
>>> hello = tf.constant('Hello, TensorFlow!')
>>> sess = tf.Session()
>>> print(sess.run(hello))
Hello, TensorFlow!
>>> a = tf.constant(10)
>>> b = tf.constant(32)
>>> print(sess.run(a + b))
42

線形回帰解説

さて、Get started でいきなり提示されるサンプルコードは簡単な線形回帰のコードでした。こちらを、線形回帰の説明もしつつ、詳解していきたいと思います。

短いのでコード全体を掲載させていただきました。

import tensorflow as tf
import numpy as np

# Create 100 phony x, y data points in NumPy, y = x * 0.1 + 0.3
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3

# Try to find values for W and b that compute y_data = W * x_data + b
# (We know that W should be 0.1 and b 0.3, but TensorFlow will
# figure that out for us.)
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b

# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

# Before starting, initialize the variables.  We will 'run' this first.
init = tf.global_variables_initializer()

# Launch the graph.
sess = tf.Session()
sess.run(init)

# Fit the line.
for step in range(201):
    sess.run(train)
    if step % 20 == 0:
        print(step, sess.run(W), sess.run(b))

# Learns best fit is W: [0.1], b: [0.3]

# Close the Session when we're done.
sess.close()

コードにもコメントが書いてあるのですが、もう少し詳しいほうがよいと思うのでそれもあわせて説明していきます。

このコードでやろうとしていること

  1. $ y = \frac{ 1 }{ 10 }x + \frac{ 3 }{ 10 } $ で表される直線上に乗る点を100個ほど用意する。
  2. それをトレーニングデータとして線形回帰を行う
  3. $ y = W x + b $ という形でモデルを定義して、TensorFlow を使って線形回帰を行い、パラメータ $W$ と $b$ を得る。
  4. $W$ と $b$ がそれぞれ、 $\frac{ 1 }{ 10 }(=0.1)$ と $\frac{ 3 }{ 10 }(=0.3)$ に近いことを確認する。

コード解説

必要なライブラリをimport

import tensorflow as tf
import numpy as np

トレーニングデータの準備

# Create 100 phony x, y data points in NumPy, y = x * 0.1 + 0.3
x_data = np.random.rand(100).astype(np.float32)
y_data = x_data * 0.1 + 0.3

本来、線形回帰を行うのであれば、実データからトレーニングデータを用意するのですが、今回はチュートリアルなので、予め分布が予測できるトレーニングデータを100個ほど生成して用意しています。まず、x の点を numpy のrandom 関数を使って100個用意しそれを x_data とし、それに対応する、グラフ上の点を y_data も100個用意しています。つまり、学習用のデータはバラつきもなく、すべて $ y = \frac{ 1 }{ 10 }x + \frac{ 3 }{ 10 } $ 上の点です。

これを $ y = W x + b $ という形でモデル化すれば、$ W = 0.1 $ と $ b = 0.3 $ は自明なのですが、それを、「これらの点をトレーニングデータとしてTensorFlow で線形回帰する]
というのがこのチュートリアルの目的です。

線形回帰のモデル定義

# Try to find values for W and b that compute y_data = W * x_data + b
# (We know that W should be 0.1 and b 0.3, but TensorFlow will
# figure that out for us.)
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
b = tf.Variable(tf.zeros([1]))
y = W * x_data + b

W = ... というところは W の初期値を与えています。
b = ... というところは b の初期値を与えています。

ニューラルネットワークなどでは、対称性を破るためにW には少し乱数を与える必要があるのですが、線形回帰ではW の初期値もb と同様に tf.zeros([1]) でも構いません(結果も同じです)。今回はサンプルコードをそのまま使っていますので、乱数で初期化しています。

y = ... というところは、x_data に対応するこのモデルが出力する y の点の集合です。つまり仮説関数のことです。

$$ y = h_\theta(x) $$
であり、
$$ h_\theta(x) = W x + b $$
であるということです。

コスト関数の定義

# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

さて、さきほどのモデルの予測が、実データともっとも誤差が少ないように $W$ と $b$ を選びたいので、まずその誤差を定義します。ある点の誤差は、予測データ y と実データ y_data の差の二乗で定義し、全ての点における誤差の平均を loss として定義しています。

一度ここで線形回帰のコスト関数を再確認しておきます。

$$ J(\theta) = \frac{ 1 }{ m }\sum_{ i = 1 }^{ m }\frac{ 1 }{ 2 }(h_\theta(x^{ (i) }) - y^{ (i) })^2 $$

この $J(\theta)$ を TensorFlow を使ったpython で表現したものが loss です。($\frac{ 1 }{ 2 }$は最小化する上では関係ないので省略しています)

次にoptimizer を定義しています。これはどんなアルゴリズムを使ってコストを最小化していくかということを決めるところです。いまは、TensorFlow に予め用意されているアルゴリズムのうち、最急降下法を使うということで、 GradientDescentOptimizer を指定しています。引数の 0.5 は学習率です。本来はいろいろな値を試すべきところなのですが、今回は 0.5 あたりでだいたいうまくいくので0.5 を指定しています。

学習率(=学習係数)についてはこちら
機械学習を1ヵ月で実践レベルにする #4 (線形回帰編)

予め用意されているOptimizer はドキュメントをご覧いただければわかります。
Optimizers | TensorFlow

そして最後に、このoptimizer でloss を最小化(minimize)するように指示しています。

実行前の初期化

# Before starting, initialize the variables.  We will 'run' this first.
init = tf.global_variables_initializer()

実はTensorFlow はここまでのコードでは処理は一切行いません。各種設定をして準備をしているだけです。このあと、 run を実行することで実際に学習が走るわけですが、その前に各種値を初期化するというのがここでやっていることです。

ちなみに少し古い入門記事ですと、 ここで initialize_all_variables というものを使っているケースもあるのでご注意ください。これは Deprecated になっているので、新たに使う理由はありません。

(参考)TensorFlowのinitialize_all_variablesがDeprecatedになった件

実行フェイズ

# Launch the graph.
sess = tf.Session()
sess.run(init)

# Fit the line.
for step in range(201):
    sess.run(train)
    if step % 20 == 0:
        print(step, sess.run(W), sess.run(b))

# Learns best fit is W: [0.1], b: [0.3]

# Close the Session when we're done.
sess.close()

最後が実行フェイズです。
TensorFlow はSession のなかで上記の設定にあわせて学習を行います。この例では、iteration 回数を200回 (range(201))として、予め train に設定したコスト関数の最小化処理を、繰り返し実行しています。20 step ずつ、コンソールへの出力を行い、状況を確認できるようにしています。

結果

0 [ 0.17769754] [ 0.34861392]
20 [ 0.1106104] [ 0.29447961]
40 [ 0.10272982] [ 0.29857972]
60 [ 0.10070232] [ 0.29963461]
80 [ 0.10018069] [ 0.29990602]
100 [ 0.1000465] [ 0.29997581]
120 [ 0.10001197] [ 0.29999378]
140 [ 0.10000309] [ 0.2999984]
160 [ 0.1000008] [ 0.29999959]
180 [ 0.10000021] [ 0.29999989]
200 [ 0.1000001] [ 0.29999995]

あまり面白いものではありませんが、イテレーションを繰り返すことで、だんだんと $ W = 0.1 $ と $ b = 0.3 $ に収束していっている様子がわかります。

ここまでわかれば、一般的な線形回帰の問題を適用するのもそう難しくないと思います。すぐに使えることでしょう。ロジスティック回帰のような分類の問題に対応するのも、コスト関数にもうひとひねり加えるだけで問題なく対応できることと思います。

(参考)機械学習を1ヵ月で実践レベルにする #7 (分類問題: ロジスティック回帰 #1)

できればニューラルネットワークで実践してみたいですね。ニューラルネットワークはおそらく今回はやらなかった、「伝統的なMNIST(手書き文字認識)」で使うことになりますので、なるべく早いうちに見ておきたいと思います。

追記:書きました!
TensorFlow 入門 〜 MNIST for beginner のプチ応用 〜
TensorFlow のチュートリアルである「伝統的なMNIST(手書き文字認識)」を、自分の持っている知識で少し拡張して試しました。チュートリアルでは、コスト関数にソフトマックス関数を、最小化に確率的勾配法を使っているのですが、それらをsigmoid 関数を使ったものや、最急降下法を使ったものに差し替えたりして理解を深めています。

参考

その他参考にしたTensorFlow 入門記事

今後

  • 伝統的なMNIST(手書き文字認識)でニューラルネットワークを試す
  • GPU 版で速度がどれだけ改善するのかを見てみたい
  • AWS でやってみたい
  • 実践的な課題に適応してみたい
    • ニュースの記事のテーマ自動分類
    • 記事についたコメントのSpam 判定 or 有用度予測
    • 囲碁(9路盤)のAI

今後はこういうことをやっていきたいなと思っています。