水増しした画像データを使って再度CNNでデータを学習

f:id:hanamichi_sukusuku:20210301140333p:plain

f:id:hanamichi_sukusuku:20210301140357p:plain

実行結果

f:id:hanamichi_sukusuku:20210301140529p:plain

f:id:hanamichi_sukusuku:20210301140545p:plain

hanamichi-sukusuku.hatenablog.com

実行結果としては上記で水増しせずに学習を行った場合の正解率は0.816...ほどだったので少し精度をあげることができた。

 

プログラムを見ていく。

 

必要な変数の定義

import cnn_model
import keras
import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split
import cv2

# 入力と出力を指定
im_rows = 32 # 画像の縦ピクセルサイズ
im_cols = 32 # 画像の横ピクセルサイズ
im_color = 3 # 画像の色空間
in_shape = (im_rows, im_cols, im_color)
nb_classes = 3

 

im_rows、im_colsでは学習に使用する縦、横のピクセルサイズを指定。

im_colorではRGB色空間を指定。白黒なら1。

in_shapeではCNNのモデルの入力層に渡す入力データ数を定義。

nd_classesは出力数。今回はsushi,salad,tohuの3種類なので3。

 

画像データ読み込み

photos = np.load('image/photos.npz')
x = photos['x']
y = photos['y']

imageディレクトリのphotos.npzはFlickr APIで取得した画像データをnumpy形式に変換したデータが格納されている。

x = photos['x'], y = photos['y']ではnumpy形式でデータを保存するときに配列名をそれぞれx,yと指定したため、['配列名']として取り出すことができる。

 

各データをCNNで学習できるように変換

# 読み込んだデータをの三次元配列に変換
x = x.reshape(-1, im_rows, im_cols, im_color)
x = x.astype('float32') / 255
# ラベルデータをone-hotベクトルに直す
y = keras.utils.to_categorical(y.astype('int32'), nb_classes)

# 学習用とテスト用に分ける
x_train, x_test, y_train, y_test = train_test_split(
x, y, train_size=0.8)

 

画像データ(x)は一つの画像を3つの要素を32列保有した行列を32行で表すことができるのでreshapeで三次元の配列に変換し、astype('float32') でndarrayの配列が保有しているデータ型を変換している。float32で小数点第八位までの値を扱うように指定。小数点第九位の値を四捨五入して第八位の値を決定する。255で割ることで0.0~1.0でデータを表現するようにしている。

 

ラベルデータ(y)ではkeras.utils.to_categorical()でone-hotベクトルに変換する。

train_test_split()で学習用、テスト用にデータを分割。

 

学習データの水増し

f:id:hanamichi_sukusuku:20210301143151p:plain

画像データを回転させ、学習用のデータを水増しする。

for i, xi in enumerate(x_train):

学習データを一つずつ取り出す。

for ang in range(-30, 30, 5):

range()に3つの引数を渡した時は(start, stop, step)の様に指定することでstepの値だけ飛ばした等差数列が生成される。

今回で言えば

(-30, 30, 5)なので[-30,-25, -20, -15. -10, -5, 0, 5, 10, 15, 20, 25]という数列が生成される。この時、start<= i < stopなので気を付ける。

 

cv2.getRotation Matrix2D(回転の中心点, 角度, 拡大比率)でアフィン変換行列を生成。

cv2.warpAffine(元画像データ, アフィン変換行列, ピクセルサイズ)でアフィン変換。

アフィン変換を行ったデータをx_newに追加しデータを作り直していく。

ラベルデータも忘れずに。

 

cv2.flip()はopencvで画像を上下左右に反転させる関数。第一引数に元となるndarrayのデータ、第二引数に反転の方向を指定。

flipcode = 0上下反転、flipcode > 0左右反転、flipcode < 0上下左右反転。0か1か-1を目的に応じて指定すれば良い。今回は1なので左右反転。

反転させた画像もx_newに追加。

 

水増しした画像をnumpy形式に変換して学習用に置き換える

print('水増し前=', len(y_train))
x_train = np.array(x_new)
y_train = np.array(y_new)
print('水増し後=', len(y_train))

 

モデルの読み込みと学習、評価

f:id:hanamichi_sukusuku:20210301150911p:plain

別ファイルに作成したCNNモデルをcnn_modelファイルのget_model()関数を呼び出し、返り値としてCNNモデルを取得。

model.fit()で学習。

modelevaluate()で評価。

 

学習の様子をグラフで表示、モデルの重みデータを保存

# 正解率の推移をプロット
plt.plot(hist.history['accuracy'])
plt.plot(hist.history['val_accuracy'])
plt.title('Accuracy')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

# ロスの推移をプロット
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.title('Loss')
plt.legend(['train', 'test'], loc='upper left')
plt.show()

model.save_weights('./image/photos-model.hdf5')