先日初めてTensorflowに触ってみましたが、今回はちょっとニューラルネットのオブジェクトに触ってみました。
最初に色々組もうとハイレベルな挑戦をしてみましたが、全く上手く動いてくれなかったので、基本を大切に・・・の精神で、超超初歩的なことを書いてみます。
触ってみたのは、
tensorflow.layers.dense
という部分。
どうやら、NewralNetworkの”層”に相当するようです。
探してみると、以下のような例文がありました。
hidden1 = tf.layers.dense(x_ph, 32, activation=tf.nn.relu)
これを組んでいくと、結構複雑なことができそうです。
ただ、今回はシンプルに・・・以下のようなモデルを考えてみます。
入力は2個、出力は1個です。
もはやニューラルネットワークとはいえないような気もしますが・・・
これが理解できなきゃダメだろうと思って、まずはここからスタートしてみます。
まずは、ニューラルネットワークの層を作ってみます。
newral_out = tf.layers.dense(x_ph, 1)
これで、入力のはx_phで定義、出力の数が1個となるようです。
オプションで色々出来るそうですが、今回は単なる線形結合っぽいような感じでいってみます。
x_phは、データ入力用の箱のplaceholderで、今回は以下で定義しておきます。
x_ph = tf.placeholder(tf.float32, [None, 2])
サイズの[None 2]、Noneは入れる数のサンプル数で任意の数突っ込めるという意味で、2は入力する変数の数なので、ここでは2個(X1,X2)としておきます。
今回は、本当にシンプルに、以下の数式のアウトプットをy1相当としました。
y_1 = w_1 x_1 + w_2 x_2 + w_0
任意の(x1,x2)とy1を適当に突っ込んでみて、上手くw_1,w_2,w_0が推定できるか??
という本当にシンプルな問題です。
実はここまでいくと、最初のサンプルのコードがそのまま流用できるため、恐らく凄い簡単に実装できて、以下のような感じでいけそうです。
# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(newral_out - y_ph))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
あとは、これを学習させていけばOKかな?
# initialize tensorflow session
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for k in range(101):
# shuffle train_x and train_y
n = np.random.permutation(len(train_x))
train_x = train_x[n]
train_y = train_y[n].reshape([len(train_y), 1])
# execute train process
sess.run(train,feed_dict = {
x_ph: train_x, # x is input data
y_ph: train_y # y is true data
})
これで一旦動きそうなのですが、折角なので、NewralNetworkのパラメタがどういう挙動をしているか知りたい。
直接読む方法もあるようなのですが、色々難しそう。
問題としてもシンプルなので、折角なので自前の解析機能を考えてみました。
考え方としては、最小二乗法でw0,w1,w2を求める方法。
ニューラルネットワークを仮想の線形近似方程式と見なして、そのパラメタをチェックしてみます。
まずは、入力するデータを
{{x_1}_k},{{x_2}_k}
と設定し、Newralを通して推測された情報を
{y^{(new)}}_k = Newral({{x_1}_k},{{x_2}_k})
とします。この{y^{(new)}}_kは何らかの決定的な数値が入ります。
ニューラルネットの推定にはBiasがあるかもしれないので、それを加えた形の以下の方程式を考えます。
{y^{(new)}}_k = w_1{x_1}_k + w_2{x_2}_k + w0
このとき、w_1,w_2,w_0が未知数、他は数値が決定しているという状態。
ここで、各kにおいて、連立方程式として考えると以下のようになります。
\left(
\begin{matrix}
{y^{(new)}}_1 \\
... \\
{y^{(new)}}_K \\
\end{matrix}
\right)
=
\left(
\begin{matrix}
{x_1}_1 & {x_2}_1 & 1 \\
... & ... \\
{x_1}_K & {x_2}_K & 1 \\
\end{matrix}
\right)
\left(
\begin{matrix}
w_1 \\
w_2 \\
w_0 \\
\end{matrix}
\right)
ここまでくれば、単純な最小二乗法の問題に帰着します。簡単のため、
A
=
\left(
\begin{matrix}
{x_1}_1 & {x_2}_1 & 1 \\
... & ... \\
{x_1}_K & {x_2}_K & 1 \\
\end{matrix}
\right)
と、おくと、
\left(
\begin{matrix}
w_1 \\
w_2 \\
w_0 \\
\end{matrix}
\right)
=
\left(
A^T A
\right)^{-1}
A^T
\left(
\begin{matrix}
{y^{(new)}}_1 \\
... \\
{y^{(new)}}_K \\
\end{matrix}
\right)
で、パラメタを求めることができそうです。
実際に真値を作るときには、w_1,w_2,w_0を何らかの数値に設定しておけば、その正解にどうやって近づいているか???を観察できれば、ニューラルネットワークの中身が少しだけ分かった気になれそうです(笑)
というわけで、そんなコードの全体を貼り付けておきます。
import numpy as np
#import matplotlib.pyplot as plt
import tensorflow as tf
# deta making???
N = 50
x = np.random.rand(N,2)
# true param???
w = np.array([0.5,0.5]).reshape(2,1)
# sum > 1.0 > 1 : else > 0
#y = np.floor(np.sum(x,axis=1))
y = np.matmul(x,w)
train_x = x
train_y = y
# make placeholder
x_ph = tf.placeholder(tf.float32, [None, 2])
y_ph = tf.placeholder(tf.float32, [None, 1])
# create newral parameter(depth=1,input:2 > output:1)
newral_out = tf.layers.dense(x_ph, 1)
# Minimize the mean squared errors.
loss = tf.reduce_mean(tf.square(newral_out - y_ph))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)
# initialize tensorflow session
sess = tf.Session()
sess.run(tf.global_variables_initializer())
for k in range(101):
if np.mod(k,10) == 0:
# get Newral predict data
y_newral = sess.run( newral_out
,feed_dict = {
x_ph: x, # xに入力データを入れている
y_ph: y.reshape(len(y),1) # yに正解データを入れている
})
# check for newral_parameter(w0,w1,w2)???
#
x_ext = np.hstack([x,np.ones(N).reshape(N,1)])
A = np.linalg.inv( np.matmul(np.transpose(x_ext),x_ext) )
A = np.matmul(A,np.transpose(x_ext))
w_ext = np.matmul(A,y_newral)
# errcheck??? ([newral predict] vs [true value])
err = y_newral - y
err = np.matmul(np.transpose(err),err)
# check y_newral
# check LS solution(approaching to NewralNet Parameter)
# check predict NewralParam
print('[%d] err:%.5f w1:%.2f w2:%.2f bias:%.2f' % (k,err,w_ext[0],w_ext[1],w_ext[2]))
# shuffle train_x and train_y
n = np.random.permutation(len(train_x))
train_x = train_x[n]
train_y = train_y[n].reshape([len(train_y), 1])
# execute train process
sess.run(train,feed_dict = {
x_ph: train_x, # x is input data
y_ph: train_y # y is true data
})
何も書いていませんでしたが、誤差は二乗和で求めることにしちゃいました。
この結果を見てみると・・・???
[0] err:1.06784 w1:0.36 w2:0.36 bias:0.00
[10] err:0.02231 w1:0.45 w2:0.45 bias:0.06
[20] err:0.00795 w1:0.47 w2:0.47 bias:0.03
[30] err:0.00283 w1:0.48 w2:0.48 bias:0.02
[40] err:0.00101 w1:0.49 w2:0.49 bias:0.01
[50] err:0.00036 w1:0.49 w2:0.49 bias:0.01
[60] err:0.00013 w1:0.50 w2:0.50 bias:0.00
[70] err:0.00005 w1:0.50 w2:0.50 bias:0.00
[80] err:0.00002 w1:0.50 w2:0.50 bias:0.00
[90] err:0.00001 w1:0.50 w2:0.50 bias:0.00
[100] err:0.00000 w1:0.50 w2:0.50 bias:0.00
真値のパラメタは、w1=0.5,w2=0.5,bias(w0)=0なので、30回ぐらい回るといい感じに収束してきている様子が見られました。
ニューラルネットワークというと、なんだか複雑そうな印象でしたが、ここまでシンプルにすることで、単なる線形結合と等価回路っぽくなるんですね。
玄人の方からすると当たり前だろうという結果かもしれませんが、私にとっては一つ大きな収穫となりました。
こんな感じで、次はもうちょっと複雑なことにも挑戦してみようと思います!