プロジェクト 輪読 15K0102 猪狩 泉里
Chapter 1-3 TensorFlowクイックツアー このチャプターでは、月々の平均気温を予測する 問題をTensorFlowで解いていきます。まずは データの関係を行列で表現して、それを TensorFlowのコードに直していきます。
多次元配列によるモデルの表現 TensorFlowでは、計算に使用するデータは多次元配列として表現 します。 𝑦 𝑛 = 𝑛 0 , 𝑛 1 , 𝑛 2 , 𝑛 3 , 𝑛 4 𝑤 0 𝑤 1 𝑤 2 𝑤 3 𝑤 4
・12か月分のデータを1つの計算式にまとめると、 𝑦=𝑋𝑤 (1.1) 𝑦= 𝑦 1 𝑦 2 ⋮ 𝑦 11 𝑦 12 , 𝑋= 1 0 1 1 2 0 ⋮ 12 0 2 1 12 1 1 2 1 3 2 2 12 2 2 3 12 3 1 4 2 4 12 4 , 𝑤= 𝑤 0 𝑤 1 𝑤 2 𝑤 3 𝑤 4 1.) Xはトレーニングセットとして与えられたデータから構成されます。このような変数を「Placeholder」と呼びます。 2.) wはこれから最適化を実施するパラメータです。このような変数を「Variable」と呼びます。 パラメータの最適化を実施するには二乗誤差を求める必要があります。これは先程定義した予測値yと、トレーニングセットtから計算できます。次にtを定義していきます。
𝑡= 𝑡 1 𝑡 2 ⋮ 𝑡 12 n月の実際の平均気温を縦に並べたベクトルtを定義します。 ここで、新しい演算を2つ定義します。どちらもTensorFlowを利用する際には関数として用意されていますが、まずはデータの関係を学んでいくために定義していきます。 ベクトル 𝑣= 𝑣 1 𝑣 2 ⋮ 𝑣 𝑁 に対して、 𝑠𝑞𝑢𝑎𝑟𝑒 𝑣 = 𝑣 1 2 𝑣 2 2 ⋮ 𝑣 𝑁 2
𝐸= 1 2 𝑟𝑒𝑑𝑢𝑐𝑒_𝑠𝑢𝑚 𝑠𝑞𝑢𝑎𝑟𝑒 𝑦 −𝑡 (1.2) 𝑟𝑒𝑑𝑢𝑐𝑒_𝑠𝑢𝑚 𝑣 = 𝑖=1 𝑁 𝑣 𝑖 squareはベクトルの各成分を二乗し、reduce_sumはベクトルの各成分の和を計算します。 これらの関数を用いて、二乗誤差は 𝐸= 1 2 𝑟𝑒𝑑𝑢𝑐𝑒_𝑠𝑢𝑚 𝑠𝑞𝑢𝑎𝑟𝑒 𝑦 −𝑡 (1.2) と表せます。これで平均気温を予測する関数と、そこに含まれるパラメータを最適化する基準となる誤差関数を行列形式で表すことができました。 ここからTensorFlowのコードに置き換えていきます。
TensorFlowのコードによる表現 今回は簡単化のため、定義式などをJupyterのノートブックに直接 書き下していきます。
・まず始めに 必要なモジュールをインポートしていきます。 import tensorflow as tf import numpy as np import matplotlib.pyplot as plt 数値計算ライブラリのNumPyと、グラフ描画ライブラリのmatplotlibをインポートします。
x = tf.placeholder(tf.float32, [None, 5]) Xは「Placeholder」と呼ばれるものと紹介しました。TensorFlowで定義されているplaceholderクラスを使います。 x = tf.placeholder(tf.float32, [None, 5]) 1.) float32 … 行列の要素となる数値のデータ型を指定しています。(float32は32ビット浮動小数点) 2.) [None, 5] … 行列のサイズを指定してします。(1.1)では12×5の行列でしたが、12という部分はユーザが使用するデータ数によって変わります。そういう場合、Noneと指定することによってプログラムが任意のデータ数を受け取ることができるようになります。
w = tf.Variable(tf.zeros([5,1])) wは最適化の対象となる「Variable」に対応しています。なので、クラスVariableを使います。 1.) tf.zeros([5,1]) … 引数の部分で変数に初期値を与えています。この場合、すべての要素が0の5×1行列を生成しています。0以外の定数や、乱数を用いることも可能です。
・(1.1)の式を表してみる y = tf.matmul(x, w) tf.mutmulは、行列の掛け算を行う関数です。(1.1)をそのまま表現できていることがわかります。 この時点ではまだxに具体的な値が入っていません。実際の計算処理はこのあとやっていきます。
t = tf.placeholder(tf.float32, [None, 1]) ・誤差関数(1.2)を表現する。 (1.2)の右辺には、先ほど表現したyに加えて、実際に観測された気温tが使われています。これはXと同様に、「クラスplaceholder」を用います。 t = tf.placeholder(tf.float32, [None, 1])
loss = tf.reduce_sum(tf.square(y - t)) ・誤差関数を表現する(続き) ここまでで誤差関数に必要なものは定義してきたので、表現してみましょう。 loss = tf.reduce_sum(tf.square(y - t)) 1.) tf.reduce_sumとtf.squareはそれぞれ先ほど勝手に定義した関数です。 2.) (1.2)では右辺の先頭に 1 2 がついていましたが、誤差関数が最小になるようにパラメータを設定するという意味では、先頭の 1 2 はあっても無くても変わらないので省略されています。
train_step = tf.train.AdamOptimizer().minimize(loss) ・二乗誤差を最小にするパラメータ 次に、二乗誤差を最小にするパラメータを決定する処理を書いていきます。TesorFlowでは最適化を行うとき、勾配降下法によるパラメータ最適化のアルゴリズムを利用しています。まずは、最適化に使用するアルゴリズム(トレーニングアルゴリズム)を選択します。 train_step = tf.train.AdamOptimizer().minimize(loss) 1.) tf.train.AdamOptimizer()はTensorFlowが提供するトレーニングアルゴリズムの1つです。与えられたトレーニングセットのデータから誤差関数を計算して、その勾配ベクトルの反対方向にパラメータを修正します。 2.) 比較的性能が良いです。学習率εが自動的に調節されるため、ディープラーニングでよく利用される。 3.) 一番後ろのminimize(loss)は、lossを誤差関数として、これを最小化するようになっています。 学習率がよく分かるサイト : http://qiita.com/isaac-otao/items/6d44fdc0cfc8fed53657
・ここから ここまでで機械学習を実行する準備が整いました。 ここまでで機械学習を実行する準備が整いました。 次から実際に、トレーニングアルゴリズムを実行して、誤差関数を最小にするパラメータの値を決定します。
sess.run(tf.initialize_all_variables()) ・セッションによるトレーニングの実行 TensorFlowではトレーニングアルゴリズムの実行環境となる「セッション」を用意して、その中でパラメータ(Variable)に相当する変数の値を計算します。 まずは新しいセッションを用意して、Variableを初期化します。 sess = tf.Session() sess.run(tf.initialize_all_variables()) 1.) セッションを変数sessに格納しています。 2.) tf.initialize_all_variable()でセッション内のVariableの値が初期化されます。 3.)一般には複数のセッションを定義して、それぞれのセッションごとに計算することが可能です。次のスライドで紹介します。
・セッションによるトレーニングの実行
・トレーニングアルゴリズムの実行 トレーニングアルゴリズムの実行もセッション内で行います。この際、Placeholderにトレーニングセットのデータを代入する必要があるので以下のデータを用意します。 1.) tとXをそれぞれ初期化しています。reshape()で12×1行列に直しています。Xはまず0で12×5行列に初期化したあと、値を代入しています。 train_t = np.array([5.2, 5.7, 8.6, 14.9, 18.2, 20.4, 25.5, 26.4, 22.8, 17.5, 11.1, 6.6]) train_t = train_t.reshape([12,1]) train_x = np.zeros([12,5]) for row, month in enumerate(range(1,13)): for col, n in enumerate(range(0,5)): train_x[row][col] = month**n
・勾配降下法によるパラメータの最適化 1.)4行目:トレーニングアルゴリズムを実行することでVariable(ここではw)の値を修正しています。feed_dictでは、Placeholderに辞書形式で具体的な値をセットしています。 2.)6行目:セッション内で計算値lossを評価しており、その時点の値を取り出す効果があります。この場合、その時点でのVariableの値を用いて計算した結果を返しています。 実行結果を確認してみます。 i = 0 for _ in range(100000): i += 1 sess.run(train_step, feed_dict = {x:train_x, t:train_t}) if i % = 10000 == 0: loss_val = sess.run(loss, feed_dict = {x:train_x, t:train_t}) print(‘Step: %d, Loss: %f’ % (i, loss_val))
・実行結果 実行結果を見ると、パラメータ(w)の修正を繰り返すことで誤差関数が減少していることがわかります。 50000回目から60000回目増加していますが、このような変動を繰り返しながらも全体として、誤差関数が減少していきます Step: 10000, Loss: 31.014391 Step: 20000, Loss: 29.295158 Step: 30000, Loss: 28.033054 Step: 40000, Loss: 26.855808 Step: 50000, Loss: 25.771938 Step: 60000, Loss: 26.711918 Step: 70000, Loss: 24.436256 Step: 80000, Loss: 22.975143 Step: 90000, Loss: 22.194229 Step: 100000, Loss: 21.434664
・どこまで減少すれば十分か? どこまでやればいいかを見極めるのは簡単ではないので、さらに100,000回繰り返してみると最後に値の増加が現れます。そこでトレーニングを打ち切り、パラメータの値を確認します。 1行目で、セッション内でVariable wを評価することで保持されている値を取り出しています。このときPlaceholderの値は影響しないので、feed_dictの指定はいりません。 wの値はNumPyのarrayオブジェクトとして取り出され、print文で表示すると行列形式に整形されて表示されます。 w_val = sess.run(w) print w_val
・予測気温の計算 ここまでの結果を用いて、予測気温を次式で計算する関数を用意します。 ここまでの結果を用いて、予測気温を次式で計算する関数を用意します。 𝑦 𝑥 = 𝑤 0 + 𝑤 1 𝑥+ 𝑤 2 𝑥 2 + 𝑤 3 𝑥 3 + 𝑤 4 𝑥 4 = 𝑚=0 4 𝑤 𝑚 𝑥 𝑚 def predict(x): result = 0.0 for n in range(0,5): result += w_val[n][0] * x**n return result
・この関数をグラフに表示する 1行目: 図形領域を示すオブジェクトを取得する 2行目: その中にグラフを描く領域を取得(次で詳しく) 1行目: 図形領域を示すオブジェクトを取得する 2行目: その中にグラフを描く領域を取得(次で詳しく) 3行目: x軸の表示範囲の設定(この場合、単位は月) 4行目: トレーニングセットのデータを散布図にプロット fig = plt.figure() subplot = fig.add_subplot(1,1,1) subplot.set_xlim(1,12) subplot.scatter(range(1,13), train_t)
・fig.add_subplot(y,x,n)の解説
・関数をグラフに表示する(つづき) 1行目: np.linespace()は、1~12の範囲を等間隔に分けた100個の値のリストを返します。 2行目: NumPyのarrayオブジェクトには、本来引数にスカラーを取る関数にarrayオブジェクトを渡した場合、それぞれの要素を引数に渡した結果返ってきた値を格納したarrayオブジェクトを返してくれる、という性質があります(このような関数をユニバーサル関数と呼ぶ)。この場合、linexの各要素を引数に渡した結果が配列として返ってきます。 3行目: 一般的にpyplotモジュールでグラフを描画する場合は、「x軸方向のデータリスト」と「y軸方向のデータリスト」を引数として渡します。今回もそのようにしてグラフを描画しています。 linex = np.linspace(1,12,100) liney = predict(linex) subplot.plot(linex, liney)
・表示されたグラフ
・まとめ どこまで繰り返せば最小になるか自分でやってみた結果、 500000万回を越えたあたりで最小値が出始めた。1000000回やったらほぼ最小値でした。1000000回もやらなくても、その半分ですでに最小値が出ているのだから、見極めはだいぶ難しそうです。