異なる背景に対する学習精度の変化

狙い

CNNを利用したネットワークでは認識したい物体の背景が学習に影響を与えると考えられている。
今回の記事ではその現象を実験的に示した。

データ

丸と四角をクラスとする2値分類問題とする。

グループ1(背景差なし)

f:id:kpsdr:20180613212822p:plain:w100f:id:kpsdr:20180613213900p:plain:w100
丸、四角クラス画像の例 背景は均一に赤

グループ2(背景差あり)

f:id:kpsdr:20180613212822p:plainf:id:kpsdr:20180613214438p:plainf:id:kpsdr:20180613213900p:plainf:id:kpsdr:20180613214434p:plain
丸、四角クラス画像の例 背景は赤と青

各グループにおいて、クラス違い、背景違いで等分されるようにTrain,Testに分割した。

モデル

# モデルの定義
model = Sequential()

model.add(Conv2D(32,3,input_shape=(256,256,3),kernel_initializer='glorot_uniform'))
model.add(Activation('sigmoid'))
model.add(Conv2D(32,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(128,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Conv2D(64,3,kernel_initializer='glorot_uniform'))
model.add(Activation('relu'))
model.add(MaxPool2D(pool_size=(2,2)))

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('relu'))
model.add(Dropout(1.0))

model.add(Dense(2, activation='softmax'))

rms = RMSprop(lr=0.0005, rho=0.9, epsilon=None, decay=0.0)
run_options = tf.RunOptions(report_tensor_allocations_upon_oom = True)
model.compile(optimizer=rms, loss='binary_crossentropy', metrics=["accuracy"],options=run_options)

結果

グループごとのvalidation error

val_error 1回目 2回目 3回目
G1 (背景差なし) 81.25% 81.25% 87.50%
G2 (背景差あり) 62.50% 56.25% 68,75%

まとめ

実験結果からもわかるように背景差がないグループの方が高い汎化性能を示している。
背景差がないということは画素値の変化が各画像間で少ないことを示し,モデルがフィッティグする際の変数が少ないとも言い換えられる。
機械学習チュートリアル的データセットであるMNISTは1チャンネルのモノクロ画像であるが、背景差なしのデータセットと考えることもできるだろう。

背景差に対応する手段としては、背景変化がある画像を大量に用意することやコントラスト正規化等の手法が想定されるであろう。

簡単な分類問題を解いてみる

目標

機械学習の効果を確認するために、簡単な分類問題を解いてみよう。

データ

以下のようなデータをpythonで出力する。

f:id:kpsdr:20171104203620j:plain

     図1 散布図

エリアごとに色分けされたデータが散らばっていることがわかる。

各点の色は座標(x1,x2)によって定まるようである。

グラフのデータを機械学習で利用しやすい形に持ち込む。

f:id:kpsdr:20171104205333j:plain

       図2 行列データ

図2において、1行目はx1 =0.031295,x2=0.284174の座標に赤点を存在することをします。

モデル

f:id:kpsdr:20171105184435j:plain
図3モデル

プログラム

今のプログラムの目的は、学習の経過を示すグラフの出力と新データに対して正しくラベル(色)が予測されているかを確認することである。

import keras
from keras.layers import Input, Dense#,Dropout
from keras.models import Model
import numpy as np
import matplotlib.pyplot as plt
from keras.optimizers import RMSprop


#データを作成

# generate data
x1_r = np.random.rand(100)*0.5
x2_r = np.random.rand(100)*0.5

#label 1(red)
l_r = np.zeros_like(x1_r)
l_r = l_r + 0

x1_b = np.random.rand(100)*0.5 + 0.5
x2_b = np.random.rand(100)*0.5

#label 2(blue)
l_b = np.zeros_like(x1_b)
l_b = l_b + 1


x1_g = np.random.rand(100)*0.5
x2_g = np.random.rand(100)*0.5 + 0.5

#label 3(green)
l_g = np.zeros_like(x1_g)
l_g = l_g + 2


x1_y = np.random.rand(100)*0.5 + 0.5
x2_y = np.random.rand(100)*0.5 + 0.5

#label 4(yellow)
l_y = np.zeros_like(x1_y)
l_y = l_y + 3

#array merge
A_r = np.c_[x1_r,x2_r,l_r]
A_b = np.c_[x1_b,x2_b,l_b]
A_g = np.c_[x1_g,x2_g,l_g]
A_y = np.c_[x1_y,x2_y,l_y]


A = np.r_[A_r,A_b,A_g,A_y]

#shuffle
np.random.shuffle(A)


#split トレイン用データとテスト用データを分離
data,label = np.hsplit(A,[2])
#data_train :(300,2) date_test :(100,2) 
data_train,data_test = np.vsplit(data,[300])
#label_train :(300,1) label_test :(100,1)  
label_train,label_test = np.vsplit(label,[300])

label_train = keras.utils.np_utils.to_categorical(label_train.astype('int32'),4)
label_test = keras.utils.np_utils.to_categorical(label_test.astype('int32'),4)


#ニューラルネットワークのモデルを定義
#入力層 2入力
inputs = Input(shape=(2,))
#隠れ層 3出力で活性化関数ははrelu
nw = Dense(3, activation='relu')(inputs)
#出力層 4出力 ソフトマックス関数を挿入(0~1の値に収める)
predictions = Dense(4, activation='softmax')(nw)

#モデルをまとめる
model = Model(inputs=inputs, outputs=predictions)
#モデルをコンパイル  最適化手法:RMSprop 目的関数:categorical_crossentropy
model.compile(optimizer=RMSprop(),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
#学習の実行 epochsは学習回数 historyには学習の経過が記録される。
history = model.fit(data_train, label_train, batch_size=10, epochs=150, verbose=1,validation_data=(data_test, label_test))

#学習の経過をグラフ化
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()


#新データに対する予測
xa = np.array([[0.8,0.7]])
print("新データ(0.8,0.7)に対する予想")
print(model.predict(xa))

出力

f:id:kpsdr:20171106212010j:plain
図4 認識精度の推移


epoch(学習回数)が増えるごとに認識精度が向上していることが見て取れる。

新データ(0.8,0.7)に対する予想
[[ 0.00191311  0.06421792  0.00333812  0.93053085]]

学習済みのニューラルネットワークは新データ(0.8,0.7)に対して4番目の要素(黄色に対応)である確率が
0.93053085でもっとも高いと予想している。
図1を見てみるとデータ(0.8,0.7)には黄色のラベルが付されそうである。
人間の判断とニューラルネットワークの判断が一致している。
このニューラルネットワークは正確な分類器として機能しているようである。

まとめ

非常に簡単なデータにたいして、ニューラルネットワークをトレーニングさせ、高精度の分類器を実現できた。
今回はepoch数を150に設定したが、50に設定しトレーニングすると、認識精度は150のときのそれに及ばなかった。つまり、epoch数を増やすことで認識精度が向上する様子を観察できた。
さらなる認識精度の向上には、さまさまな手法が存在するが、またの機会に記事にしたい。

KerasとTensorFlowで機械学習始めます。(1)

 

Kerasとtensorflowなのか

機械学習のライブラリといえば、Chainer,TensorFlow,Theanoなどがあがると思います。

機械学習を始めたての私にとっては当然どれがいいのかわからなくて、とりあえず国産のライブラリであるChainerから始めました。国産であるので、日本語のリファレンス等も豊富で、さらに記法もわかりやすい! といいところづくめだったんですが,GPU対応がどうしてもできませんでした。WindowsマシンにGPU対応環境をつくることが私の能力ではできなかった・・・。

そこで次にTensorFlowに手を出しました。Googleを出してるライブラリで世界的なシェアが大きく、学ぶには大きな意味がありそうでした。さらにTensorFlowでよかったのがWindows マシンでGPU環境を作ることが簡単にできたことです。オンラインのリファレンス読んで、もろもろのソフトのversionに気を付ければ結構簡単にGPUを利用できるようになりました!

しかし!私はこのTensorFlowを断念しました! なんか書き方が難しい! マジ無理!

計算グラフを常に考えた記法は私にとって少々難解でした。早くプログラム動かしたいのに、なんか覚えるメソッド多すぎ! 私はTensorFlowとお別れしました。

 

ただ、TensorFlowでGPU環境を作れたので、このアドバンテージは捨てたくない。

そこで、救世主Kerasが現れました。

KerasはTensorFlowなどのライブラリをバックエンド(計算等は元のライブラリに任せる)として機械学習のプログラムを簡単に書ける優れものでした。

TensorFlowで作ったGPU環境を利用しつつ簡単な記述でニューラルネットワークを記述できるんです!私自身まだ始めたてなのですが、これはいけるぞ! という感じがありますね。

 

このような経緯で私はKerasとTensorFlowでコーディングをしていくことを決意しました。

 

これから

このブログではとにかく、いろいろな問題に対して機械学習を用いて、解決策を見つけていこうと思います。とにかく、実装をこころがけて邁進します。

完全に私の忘備録になりますので、細かな説明などなくだらだら書いてしまうとおもいますが、もし興味を持ってコメントだったり、アドバイスをしてくれたら大変うれしいです。