日記

機械学習を中心に気ままに書きます。

Tensorflowの概略

機械学習を実装するためには高度な数学の力が必要であり、またサイズの大きな行列を扱うことが多くその際には計算量が大きくなるため並列処理をしなければなりません。しかし、Tensorflowにはこれらの課題に対しても問題はなく、機械学習を実装することができます。また、画像認識では様々なフィルターに対して画像を伝播させ、結果と教師データの誤差を逆に伝播させていき、少しずつフィルターの値を更新させることを繰り返すことによって学習していきますが、これらのフィルターもTensorflowにはすでに用意されています。したがって誰でも簡単に、まるでレゴブロックを組み立てていくようなやり方で機械学習を実装することができます。

 Tensorflowでは、オペレーション(Operation)やセッション(Session)という言葉がよく使われます。オペレーションとはグラフのノードのことであり、テンソルのことです。これは上記のフィルターの役目を果たすものです。またセッションとは、それらのオペレーションを実行させるものです。

 Tensorflowの主な関数

tensorflowをimportし、tfとしてそのライブラリを使うことが多く、公式でもtf.~と表記していることから以下tfは省かずに書いていきます。また、初心者向けに解説したものですので詳しくは公式を参照してください。また引数については全てを書く必要はないため簡略化しました。

 

  tf.matmul(a, b)

行列の積a*bを返すメソッド。

当然のことながら、a,bのサイズに気をつけないといけません。

 

  tf.truncated_normal(shape, 1.0)

乱数の生成をします。何のためのものかは下記のtf.Variable()をご覧ください。

 

  tf.Variable()

オペレーションと呼ばれるノードをセッション内に作ることによって機械学習のモデルを構築します。tf.Variableは学習させたいオペレーションを作るクラスです。初期値を全て0にすると、場合によっては出力がなくなってしまい勾配が計算できなくなってしまい最適化ができなくなってしまいます。そのためほとんどの場合乱数で初期化します。

 

 tf.placeholder(dtype, shape=None, name=None)

tf.Variableと同様にセッション内にオペレーションを生成します。機械学習には、教師あり学習、教師なし学習、強化学習などがあり、教師あり学習の場合、データと教師データの二つのデータが必要になります。そのデータを扱う際にこれを使います。注意しなければならないのは、それらのデータはこのオペレーションに代入する形で使われることになることです。詳しくは下記のSession.runをご覧ください。

 

 tf.Session()

これは上記のようなオペレーションを生成させた後、それらを実行するために用意されたクラスです。

以下のようにSession.runを使うことによって初めてオペレーションが計算されることになります。

 

 tf.Session.run(fetches,feed_dict={a:b})

 これは先ほど述べた通り、構築したオペレーションを実行するためのものです。

この場合、fetchesを計算します。その計算過程において、placeholderのaが現れた場合、そのaにbを供給する。

また、feed_dictは、ディクショナリ型であることに注意しましょう。fetchesを計算する際に、データと教師データの二つのplaceholderのオペレーションが現れることもあります。そのような時には、feed_dict={a:b,c:d}のように書くことができます。つまり、この場合には、placeholderクラスaが現れたら、bを代入し、cが現れたらdを代入してくれます。

 他の使い方としては、学習したフィルターWの値を参照したいときもあるでしょう。そんな時には、tf.Session.run(X)とすれば良いのですが、当然のことながら、Xを計算する過程において、placeholderが現れれば、feed_dict={a:b}といった形で引数に入れなければなりません。

 

 tf.square(A)

行列Aの各要素を二乗します。

二乗和誤差を計算する際に使ったりします。

 

 tf.reduce_sum()

行列Aの全ての要素の和を計算してくれます。

二乗和誤差を計算する際に使います。他にも様々な誤差関数を計算することがあるのでその時に使います。

 

 init_op=tf.global_variables_initializer()

tf.Variable()で生成したオペレーションは必ず初期化しなければなりません。

使い方としては以下の例を参考にして下さい。

 

 train_step=tf.train.AdamOptimizer().minimize(loss)

これは、引数lossを最小にするように各tf.Variableで生成されたオペレーションを少しだけ更新します。

この処理を繰り返すことによって誤差lossを小さくしていきます。つまり最適化が行われるわけです。

 

以上のことを念頭に以下では、2*2行列のデータに対して、2*2のフィルターWを掛け、バイアスbを足した結果が、教師データ2*2行列に近づくような学習モデルを考えます。

 

# -*- coding: utf-8 -*- と書くと#を使ってコメントを書くことができるようになります。

 

↓以下ソースコード コピペしてすぐ実行できます。

import tensorflow as tf
# -*- coding: utf-8 -*-

def variable(shape):
  initial=tf.truncated_normal(shape,0.1)
  return tf.Variable(initial)
#生成するオペレーションの型を受け取りその型のオペレーションで各要素を乱数で初期化したものを返す。

class model:
  def __init__(self):
   self.prepare_model()
   self.prepare_session()

  def prepare_model(self):
    #ここで、オペレーションを作りモデルを構築していく。
    data_X=tf.placeholder(tf.float32,[2,2])
    self.data_X=data_X
    #データなのでplaceholderでオペレーションを生成する。

    W=variable([2,2])
    #Wは、いわゆるフィルターであり、この値を更新していくことになるのでtf.Variableで生成する。
    self.W=W

    b=variable([2])
    #bはいわゆるバイアスであり、Wと同様に、この値を更新していくことになるのでtf.Variableで生成する。
    self.b=b

    XW_b=(tf.matmul(data_X,W))+b
    self.XW_b=XW_b
    #reduce_sum(A,1)は同じ行の要素の和。
    #X*W+bを計算。

    t=tf.placeholder(tf.float32,[2,2])
    self.t=t

    loss=tf.square(t-(XW_b))
    #XW_bの各要素を二乗。

    loss_val=tf.reduce_sum(loss)
    self.loss_val=loss_val
    #すべての要素の和を計算。

    train_step=tf.train.AdamOptimizer().minimize(loss)
    self.train_step=train_step

    #以上でオペレーションの定義はできた。しかし、これではまだ計算してくれない。続いてprepare_session()でオペレーションの準備が完了する。

  def prepare_session(self):
    init_op=tf.global_variables_initializer()
    #tf.Variableを使う際には必ず初期化しなければならない。

    sess=tf.Session()
    #これによってオペレーションの準備ができる。

    sess.run(init_op)
    #Variableの初期化が完了する。

    self.sess=sess

data_x=[[2.0,4.0],[3.0,-1.0]]
data_t=[[1.0,7.0],[2.0,3.0]]

#data_x,data_tの数値は好きなように変更できます。

 


a=model()

loss_val=10
i=0
while(loss_val>0.0001):
i+=1
a.sess.run(a.train_step,feed_dict={a.data_X:data_x,a.t:data_t})
loss_val=a.sess.run(a.loss_val,feed_dict={a.data_X:data_x,a.t:data_t})

if i%500==0:
print(i,":",loss_val)

W=a.sess.run(a.W,feed_dict={a.data_X:data_x,a.t:data_t})
b=a.sess.run(a.b,feed_dict={a.data_X:data_x,a.t:data_t})
xwb=a.sess.run(a.XW_b,feed_dict={a.data_X:data_x,a.t:data_t})

 

↑以上ソースコード

 

これを実行すると、以下のような結果が現れます。

500 : 23.4659
1000 : 6.77596
1500 : 2.14259
2000 : 0.65792
2500 : 0.15359
3000 : 0.0230899
3500 : 0.00194364
[[ 1.03540099  0.69215465]   ←W
 [ 0.00989378  0.93842751]]
[-1.10390711  1.86197555]    ←b
[[ 1.00646996  6.99999523]   ←X*W+b
 [ 1.99240208  3.00001192]]
[[1.0, 7.0], [2.0, 3.0]]               ←data_t

よく見てみると、x*W+bの結果が、data_tとかなりよく似ていることがわかります。
今回は、データと教師データを自分の手で作るというかなり強引なやり方で機械学習をさせてみましたが、画像認識の畳み込みニューラルネットワーク(CNN)の概略は今回の例とほとんど変わりません。CNNはprepare_modelの部分が少し長くなりますが、しかしそこに書かなければならないコードは、Tensorflowで用意されています。そのため今回の例のように数学の知識がそれほどなくとも簡単に実装できるといったのはこのためです。