社畜エンジニア発掘戦線

駆けだしAIエンジニア

Tensorflow:線形回帰

おつかれさまです。

Tensorflowを使い始めたので、これまで書いてきたディープラーニングのプログラムを書き直してみたいと思います。今回は線形回帰のこの問題をトライ。照らし合わせながら新しいコードを確認してもらえると幸い!
shachicode.hatenablog.com

CONTENTS

データセット

Tensorflow関係なく、データセットは相変わらず自作します。値も以前と同じで「y=2*x+5」に沿う直線を形成します。

import numpy as np
import matplotlib
matplotlib.use('tkagg')
from matplotlib import pyplot as plt

num_of_sam = 30
x = np.random.uniform(0, 4, num_of_sam)

t = 2*x+5

noise = np.random.normal(0, 0.5, num_of_sam) 
t = 2*x+5+noise

matplotlibのimport記述がなんか変ですね。Anacondaでtensorflowをインストールした際にopenCVという画像関連のライブラリもインストールしてしまったんですが、こいつがどうもmatplotlibとケンカしているみたいで、今までのimport文だとエラーが出てしまいます。そこで上記のような呪文を唱え、エラーを回避しています。tensorflowの本筋ではないのでふーんぐらいで流してもらえればと思います。ではさっとプロットしてみます。

plt.figure()
plt.plot(x,t)
plt.show()


f:id:sutokun:20190714115212p:plain:w500
オッケーですね。

計算グラフの作成

さて、ここからTensorflowを使い始めます、まずはインポート。

import tensorflow as tf

Tensorflowはあらかじめ処理を記述し、セッションと呼ばれる範囲の中で実際に演算が行われる、ようです。まだ使い始めたばっかりなんで詳しいところは分かりません。このあらかじめ記述される処理が「計算グラフ」と呼ばれるそうです。計算グラフはちゃんとした定義と書き方(表現の仕方)があるんですが、そこまで突っ込んで考えていると長くなるので、「処理を記述するところ」ぐらいの認識でとどめておきます。


f:id:sutokun:20190714120659j:plain:w400


まずは必要な変数を設定していきます。

Tensorflowでは外から代入する変数(今回はxとtが対応)を使うためにはplaceholderという変数を用います。そのまま使えないのがややこしいところです。

holder_x = tf.placeholder(tf.float32)
holder_t = tf.placeholder(tf.float32)

holder_x にx、holder_tにtが代入されます。この時点でこれらのplaceholderはまだ空の箱の状態で、セッション内で具体的に変数が代入されます。「tf.float32」は少数が入りまっせ、ということです。

次に、処理の中で自発的に設定される変数(ここではwとbが対応)にはVariableを用います。

w = tf.Variable(tf.random_normal([1],mean=0.0, stddev=1.0))
b = tf.Variable(tf.random_normal([1],mean=0.0, stddev=1.0))

random_normalを使って初期値はランダム(mean=0.0, stddev=1.0)に設定します。今回、変数は行列にする必要はないので[1]です。ちなみに、この変数は逆伝搬で更新の対象になります。

これで変数の設定が完了です。次に純伝搬の処理を記述します。

y = holder_x * w + b

この線形回帰なので活性化関数は不要です。すでに初期変数の設定は完了しているので、ここはそのまま変数の演算で記述できます。

次に損失関数の設定です。回帰問題は損失関数に二乗和誤差を使ってましたね、懐かしい。

二乗和誤差関数:
\displaystyle E = \sum_{i} (y_{i} -t_{i} )^2

この式をTensorflowで書いてみます。

loss = tf.reduce_sum(tf.square(y - holder_t))

reduce_sumはシグマ、tf.squareは2乗を記述しています。


f:id:sutokun:20190714120742j:plain:w500
ここまであっさりと純伝搬の計算が欠けました。

逆伝搬計算

Tensorflowの逆伝搬はヤバい、何がヤバいってなにも考えなくていい、言い過ぎた、ほとんど考えなくていい。とりあえずコードを書いてみます。

optimizer = tf.train.GradientDescentOptimizer(0.001)
train = optimizer.minimize(loss)

学習を実行するには逆伝搬するための「微分式」と「更新手法」が必要でした。更新手法はもとのプログラムと同じ「勾配降下法」を用います。損失関数(今回はloss)が減少していくようにパラメータ(wとb)は更新されていきます。

この2行ではoptimizerでパラメータの更新手法(ここではGradientDescentOptimizer:勾配降下法で学習率0.001)を設定してあげて、損失関数(loss)を減少(minimize)させましょう、という記述です。

ハイおわり、

...おわり!??何なの微分しなくていいの??「w = w - u*dw」とかいらないの??

最初はかなり動揺しました、tensorflowはこれで全ての逆伝搬処理を実行してくれます。めっちゃ頑張って微分してたあの日々は何だったのか、というかこれだけの記述で自動で逆伝搬を実行してくれるTensorflow とはいったいどんな構造になっとんねん、Google天才やんけ、とにかく感動しました。

とりあえずこれで逆伝搬が完成です。

セッションの実行

実際にTensorflowの計算するためにはセッションを実行する必要があります。下記の1行でセッションの準備が整います。このwithの中で具体的にセッションを実行させます。

with tf.Session() as sess:

セッションの実行をsessという変数(のようなもの)で置き、各計算結果が欲しい場合「sess.run」としてその結果を得ます。

とりあえず進めていきましょう。まずはお約束ごとで、Variable変数(wとb)を使用する場合はその値を初期化する必要があります。

    sess.run(tf.global_variables_initializer())

呪文みたいなもんです、パラメータは更新の対象なのでその辺の兼ね合いなんでしょうかね。

次はイテレーションを設定します、回数はとりあえず3000回とします。ここはforの繰り返し処理を用います。

    for step in range(3000):
        sess.run(train, feed_dict={holder_x: x, holder_t: t})

学習が進められてパラメータが更新される部分は、上記の逆伝搬「train = optimizer.minimize(loss)」のところなので、この「train」をセッション内で回します。このタイミングで、空っぽだったplaceholderに変数(xとt)を辞書形式で代入します。

学習が終われば、更新完了後の変数を取り出します。

    weight = sess.run(w)
    bias = sess.run(b)

この処理はセッション内で記述しなければ具体的な値として取り出せないので注意です。

これで学習を行うコードの記述が完了しました、こんな以前と比べてあっさりと終わってしまったのでホンマに大丈夫なんかと不安になります。

グラフ化

学習後のパラメータも取り出せたので、これをグラフにプロットしてみたいと思います。

x_ = [0, 4]

plt.figure()
plt.plot(x,t,'.')
plt.plot(x,x*weight+bias)
plt.show()

めんどくs...、スピード重視のためグラフの体裁は整えてません笑 アニメ化も特にしてません笑

とりあえずうまくいったっぽいかな、自作プログラムとの比較をしたいところですが、早く先に進みたい(CNNの時間が短縮できるか確認したい)ので、基礎問題の結果表示はこの辺りにしてどんどん次に進めていきたいと思います。

ちなみに、純伝搬のlossまでは以前作った自前の計算プログラムと比較しましたが、値はうまく一致しました。そりゃ同じ計算式使ってんだから当たり前なんだけど、あのTensorflowさんと同じ結果が得られたってのはちょっと感動です。

まとめ

今回はTensorflowを使った初めての試みとして、以前に作成した線形回帰のプログラムをTensorflowでトレースしてみました。深く突っ込んで調査できていませんが、大筋うまくいってそうです。本当にあの2行で逆伝搬が実行されたのはびっくりしました。

次回は非線形回帰問題にトライします。