こんにちは、エンジニアのさもです。
今回からいよいよTensorFlowで機械学習を実装していきます。
第1回目は、最小二乗法を使って、予測を行います。
目次
内容はこちらの書籍に沿っていきます

TensorFlowで学ぶディープラーニング入門 ~畳み込みニューラルネットワーク徹底解説~
- 作者: 中井悦司
- 出版社/メーカー: マイナビ出版
- 発売日: 2016/09/27
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
書籍と同じ内容を書いても意味がないので、もう少し詳しく書けそうだなというところを追加していきます。
あと、「この記事で解説があるから書籍は買わなくていいや」となってしまわないように、実装以外で解説されている部分は省く、または自分で勉強した内容で置き換えます。
実験対象
今回は、1月から12月までの平均気温の変化を4次関数
で近似します。
近似するデータは以下のものを用意しておきます
import numpy as np import matplotlib.pyplot as plt 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])
1月の平均気温が5.2度で、12月が6.6度になっています
こちらをグラフに表示するとこんな感じです
# jupyter notebookでグラフを表示するために必要 %matplotlib inline # train_tをプロット fig = plt.figure() # 複数グラフを表示する場合に位置を指定する # 1枚だけ表示する場合は(1,1,1)で固定 subplot = fig.add_subplot(1,1,1) # 表示するx軸の範囲 subplot.set_xlim(1,12) # scatterメソッドで散布図を準備 # 第一引数がx軸の配列で、第二引数がそのペアとなるy軸の配列 # rangeは第二引数の一つ手前までの範囲なので、第二引数は13にしなければならない subplot.scatter(range(1,13), train_t) subplot.plot()
スポンサーリンク
最小二乗法とは
最小二乗法とは、予測した値と、正解の値とがどれくらい違うかを、差の2乗(2乗誤差)で定義し、この値を最小になるようにパラメータを修正しようという方法です。
例えば、上記の今回のデータを見るとなんとなく、ひっくり返った二次関数で近似できそうです
この関数で近似したとして、実際に2乗誤差を調べてみましょう
# g(x)を使った予測データの準備 x = np.array(range(1,13)) y = -0.5*(x - 8) ** 2 + 25 y #=> array([ 0.5, 7. , 12.5, 17. , 20.5, 23. , 24.5, 25. , 24.5, 23. , 20.5, 17. ]) # 差の計算 err = np.sum((train_t - y)**2) err #=> 288.07
2乗誤差は288.07と計算されました。最小2乗法では、この値を最小にすることを目標とします。
ちなみに、g(x)も一緒にプロットするとこんな感じです
%matplotlib inline fig = plt.figure() subplot = fig.add_subplot(1,1,1) subplot.set_xlim(1,12) subplot.scatter(range(1,13), train_t) linex = np.linspace(1,12,100) liney = -0.5*(linex - 8) ** 2 + 25 subplot.plot(linex, liney)
6~8行目が編集した箇所です
後半が結構ずれていますね。
二次関数でもっとよりよく近似したい場合は、g(x)内の0.5や8, 25などのパラメータを変えていくことになると思います。
機械学習では、このパラメータの決定をプログラムでやってもらおうというわけです。
では、実際にそのプログラムを組んでいきたいと思います。
実装と解説
必要なライブラリのインポート
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt
変数の定義
3次式だとどうなんだろうと思ったときにすぐ変えれるように、何次式で近似するかdimという変数で管理しておきます。
dim = 4 x = tf.placeholder(tf.float32, [None, dim + 1]) w = tf.Variable(tf.zeros([dim+1,1])) y = tf.matmul(x,w) t = tf.placeholder(tf.float32, [None, 1])
xは入力値で、各月を0乗から4乗した値が配列形式で入力されます。
学習時に入力される値は、placeholderで定義しておきます。
wは4次式の係数で、5個の要素からなる配列です。
学習時にプログラムから更新される変数はVariableで定義しておきます。
y は入力と係数の内積を取った変数で、4次式そのものになります。
tは正解の変数です。
TensorFlowではこんな感じで、計算ルールを記述していき、学習時にはじめてplaceholderに値が入り、計算が実行されます。
メソッドを定義する感覚に似ている気がします。
誤差の定義
loss = tf.reduce_sum(tf.square(y - t))
2乗誤差を定義します。
yが4次式での計算結果、tが正解値なので、差を取って、tf.squareで2乗し、reduce_sumで足し合わせています。
学習方法の定義
train_step = tf.train.AdamOptimizer().minimize(loss)
2乗誤差をどのように係数にフィードバックするか定義しています。
上記のコードは、2乗誤差(loss)を最小化する(minimize)ように、tf.train.AdamOptimizer()という最適化アルゴリズムで学習(フィードバックして係数を更新)するという意味です。
セッションの初期化
sess = tf.Session() sess.run(tf.global_variables_initializer())
セッションが何というのは上手く説明できないですが、学習や予測の実行単位みたいなものです。
sess1,sess2みたいに二つ用意して、それぞれ学習させると、それぞれが持つ係数などは別の値になっています。RPGでいうセーブデータみたいな感じ?
tf.global_variables_initializerで、変数などを初期化しています。
入力データの準備
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, dim+1]) for row, month in enumerate(range(1, 13)): for col, n in enumerate(range(0, dim+1)): train_x[row][col] = month**n
実際に学習で用いるデータを準備します。
train_tが正解値で、変数の準備で定義しておいた変数tに学習実行時に代入されます。同様に、train_x は変数xに代入されます。
train_xの中身は次のようになっています
(注)ただし、train_xをそのまま表示しても見にくいので、次のコードで整数になおして表示しています
train_x = np.array(list(map(int, train_x.reshape(12 * (dim+1))))).reshape(12, (dim+1))
学習の実行
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))
sess.runで第一引数に渡した変数を1回だけ評価(実行)します。また、第2引数のfeed_dictに、変数の定義でplaceholderとして定義しておいた変数に値を入れます。
第一引数がtrain_stepのときは学習を1度実行し、lossを渡したときは、2乗誤差を計算して返すといった具合です。
学習は全部で10万回行います。そのうち1万回ごとに2乗誤差を計算して標準出力に表示しています。
Step: 10000, Loss: 31.014380 Step: 20000, Loss: 29.290693 Step: 30000, Loss: 28.022751 Step: 40000, Loss: 27.663746 Step: 50000, Loss: 25.792316 Step: 60000, Loss: 24.766474 Step: 70000, Loss: 23.838539 Step: 80000, Loss: 22.974419 Step: 90000, Loss: 22.176279 Step: 100000, Loss: 22.416885
こんな感じで実行過程が表示されます
学習結果を確認する
w_val = sess.run(w) w_val
で学習済みの係数を見ることができます。以下が表示内容です。
array([[ 3.76468992], [-1.58954322], [ 1.78510237], [-0.20117806], [ 0.00539352]], dtype=float32)
学習結果を使って4次式を表示する
def predict(x): result = 0.0 for n in range(0,dim+1): result += w_val[n][0] * x ** n return result
上記のメソッドで4次式をあらわしています。はじめの方に書いたに相当します。
次に、実験データの散布図に、この4次式を重ねて表示します。
%matplotlib inline fig = plt.figure() subplot=fig.add_subplot(1,1,1) subplot.set_xlim(1,12) subplot.scatter(range(1,13), train_t) linex = np.linspace(1,12,100) liney = predict(linex) subplot.plot(linex, liney)
7行目で4次式を使うように指定しています。
以下が実行結果です。
私が適当に当てはめた2次式よりずっといい近似になっていますね!
2乗誤差は22.416885と小さくなりました。
学習部分をもう少し多く(50万回ぐらい)やると2乗誤差は15程度まで小さくなりました。
その他の次数での近似
最後に、せっかく次数をdimという変数で定義したので、ここを変えて実験したいと思います。
- dim=3, loss: 32.606544
- dim=2, loss: 90.494446
- dim=5, loss: 12.882786
まとめ
処理を大きく分けると、変数と損失関数の準備、データの準備、学習の実行、学習結果を見る・使うって感じでしょうか。
はじめてTensorFlowを触ってみましたが、モデルと実装が近い形で書けるので、「これは楽しい」って思いました。
グラフを書くのも簡単ですね!
次回は分類を実装していきます。
読者登録をしていただけると、ブログを続ける励みになりますので、よろしくお願いします。