実践型AIプログラミング特講 最終章その6 #45

実践型AIプログラミング特講 最終章その6 #45

今回は前回に引き続いて、機械学習の最適化についてと、カロリー算出アプリケーション作成手順を皆様にご紹介していきたいと思います。

前回の記事

続き

プログラムを実行すると、images ディレクトリーに「photos.npz」という NumPy 形式のファイルが出力されます。プログラムを確認してみましょう。(※1)の部分では、ディレクトリーとラベル番号を指定して、リストにデータを追記していきます。ここでは、ラベルに次のような意味を持たせています。

プログラムの(※2) の部分では、NumPy 形式でファイルヘデータを保存します。(※ 3)の部分では、実際にディレクトリーのなかのJPEG ファイルを読み込んで、リストに追加する加理を記述しています。ここでは、PILモジュールを利用して画像を読み込み、32ピクセルにリサイズしています。それでは、保存した画像が正しく NumPy形式になっているか、Jupyter Notebook で確認してみましょう。以下のプログラムを実行して、画像の一覧を確認してみてください。

import matplotlib.pyplot as plt

# 写真データを読み込み

photos= np.load (‘image/photos.npz’)

x=photos [‘x’]

y = photos [‘y’]

# 開始インデックス(*1)

idx = 0

# pyplot で出力

plt.figure (figsize=(10, 10))

for i in range(25):

plt.subplot (5, 5, i+1)

plt.title(y[i + idx])

plt.imshow(x[i + idx])

plt.show ()

実行すると、以下のように表示されます。上記プログラムの(※1)の部分を、100 や 200に変えると、サラダや寿司の画像を出力します。一覧を目視してみて、違う画像があればクリーニングの手順からやり直してください。

CNN で学習してみよう

それでは、これらの画像を学習させてみましょう。今回は、最初から結果が良いとわかっているCNN を利用してみましょう。今回は、以下のようなモデルを構築して利用することにします。先にモデルだけ定義してしまいましょう。以下のプログラムを、cnn_model.py という名前で保存しましょう。これをモジュールとして利用します。

import keras

from keras.models import Sequential

from keras.layers import Dense, Dropout, Flatten

from keras.layers import Conv2D, MaxPooling2D

from keras.optimizers import RMSprop

# CNN のモデルを定義する

def def_model(in_shape, nb_classes):

model=Sequential()

model.add (Conv2D(32,

activation=’relu’,

input_shape=in_shape))

model.add(Conv2D(32, (3, 3), activation=’relu’))

model.add (MaxPooling2D (pool_size=(2, 2)))

model.add (Dropout (0.25))

model.add(Conv2D(64, (3, 3), activation=’relu’))

model.add(Conv2D(64, (3, 3), activation=’relu’))

model.add(MaxPooling2D(pool_size=(2, 2)))

model.add (Dropout (0.25))

model.add(Flatten())

model.add (Dense (512, activation=’relu’))

model.add (Dropout (0.5))

model.add (Dense (nb_classes, activation=’softmax’)).

return model

# 構築済みのCNN のモデルを返す

def get_model(in_shape, nb_classes):

model = def_model (in_shape, nb_classes)

model.compile(

loss=’categorical_crossentropy’,

optimizer=RMSprop(),

metrics=[‘accuracy’])

return model

このモデルを利用して、学習を行うプログラムは以下のようになります。ただし、プログラムを実行する前に、JuDyter Notebook のメニューから IKernel > Restart] をクリックして、カーネルをリスタートする必要があります。

import cnn_model

import keras

import matplotlib.pyplot as plt

import numpy as np

from sklearn.model_selection import train_test_split

# 入力と出力を指定(* 1)

im_rows = 32 # 画像の縦ビクセルサイズ

im_cols=32 # 画像の横ピクセルサイズ

im_color= 3 # 画像の色空間

in_shape=(im_rows, im_cols, im_color)

nb_classes = 3

# 写真データを読み込み(*2)

photos = np.load (‘image/photos.npz’)

x = photos [‘x’]

y = photos [‘y’]

# 読み込んだデータをの三次元配列に変換(*3)

x=x.reshape (-1, im_rows, im_cols, im_color)

x = x.astype(‘float32’) / 255

# ラベルデータを One-Hot ベクトルに直す(*4)

y = keras.utils.to_categorical (y.astype (‘int32’), nb_classes)

# 学習用とテスト用に分ける

x_train, x_test, y_train, y_test = train_test_split(

x, y, train_size30.8)

# CNN モデルを取得(*6)

model = cnn_model.get_model (in_shape, nb_classes)

# 学習を実行(*7)

hint=model.fit(x_train, y_train,

batch_size=32,

epochs=20,

verbose=1,

validation_data=(x_test, y_test))

# モデルを評価(*8)

score = model.evaluate (x_test, y_test, verbose=1)

print (‘ 正解 =’,score[1], ‘loss=’, score [0])

# 学習の様子をグラフへ描画(*9)

# 正解率の推移をプロット

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-light.hdf5’)

プログラムを実行すると、以下のような結果が出力されます。プログラムの実行結果は、0.766..(約77%)でした。もちろん、画像のクリーニングの結果により、大きく値が変わってきますので、結果がずいぶん悪かったという方は、クリーニングからやり直してみましょう。学習精度の大きな差は、たいていデータの質にかかっています。プログラムを確認してみましょう。と言っても、すでに CNNのプログラムは何度も紹介しているので、詳しくは5章を参考にしてみてください。簡単に解説すると、プログラムの(※1)の部分で、入力と出力を指定します。(※2)で写真デーータを読み込み、(※3)でデータを三次元の配列に変換して、0-1 の範囲になるように正規化します。(※4)では、ラベル (0-2) の値をOne-Hot ベクトルの形式に変換します。(※5)の部分では、学習用とテスト用に画像を分割し、(※6)でCNN モデルを取得したら(※)の部分で学習を実行します。(※8)の部分で、モデルを評価したら、(※9)で学習の様子をグラフへ描画します。

学習データを水増ししてみよう

それでも、判定精度が0.77 では、それほど良い値とは言えません。もう少し精度の向上を目指してみましょう。よくあるテクニックとして、データの水増しがあります。写真を回転させたり、反転させたりするのです。人間の目には同じ写真に見えますが、コンピューターには回転した写真はまったく異なる写真です。そこで、画像を回転させることで、データを水増しできるのです。画像の回転には、OpenCV を利用してみましょう。以下は、プログラムの動作をわかりやすくするために、寿司画像を利用して 180度回転させてみましょう。

import matplotlib.pyplot as plt

import cv2 # OpenCV を利用する

# 写真データを読み込み

Photos­= np.load (‘image/photos.npz’)

x = photos [‘x’]

img = x[12] # わかりやすい写真を選択

plt.figure(figsize=(10, 10))

for i in range (36):

plt.subplot (6, 6, i+1)

# 回転を実行

center =(16, 16)# 回転の中心点

angle=i*5# 角度を変えて出力

scale = 1.0 # 拡大率

mtx =cv2.getRotationMatrix2D(center, angle, scale)

img2=cv2.warpAffine (img, mtx, (32, 32))

# 回転した画像を表示

plt.imshow (img2)

plt.show ()

実行すると、以下のようになります。このようにして、画像を大量に生成して、学習データとして与えるのです。

水増ししつつデータを学習しよう

それでは、水増しを行った後、データを学習させるようにプログラムしてみましょう。以下は、水増ししつつ CNN で学習を行うプログラム「cnn2.py」からの抜粋です。プログラムの大半は、前回の「cnn.py」と同じなので、追加したところだけ抜粋してみます。

import cv2

(省略)

# 学習用とテスト用に分ける

x_train, x_test, y_train, y_test = train_test_split(

x, y, train_size=0.8)

# 学習用データを水増しする(*1)

x_new =[]

y_new =[]

for i, xi in enumerate (x_train):

yi = y_train [i]

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

# 回転させる(* 2)

center =(16, 16) # DEDEDA

mtx =cv2.getRotationMatrix2D(center, ang, 1.0)

xi2 = cv2.warpAffine (xi, mtx, (32, 32))

x_new.append (xi2)

y_new.append(yi)

# さらに左右反転させる(*3)

хi3 =cv2.flip(xi2, 1)

х_new.append (хі3)

y_new.append (yi)

cv2.flip(xi2, 1)

# 水増しした画像を学習用に置き換える

print (‘水増し前 =’, len(y_train))

x_train =np.array (x_new)

y_train = np.array (y_new)

print (‘ 水増し後=’, len(y_train))

(省路)

# CNN モデルを取得

model = cnn_model.get_model (in_shape, nb_classes)

プログラムを実行してみましょう。もともと300個のうち学習用に取り分けた8割の写真は、240枚だけでしたが、回転と反転を行って水増しして、5760枚にすることができました。ただしその分、習にかかる時間は何倍もかかります。その代わり、判定精度が改善され、0.849(約 85%)になりました。それでは、プログラムを確認してみましょう。プログラムの(※1)以降の部分で、学習用に取り分けたデータを-30度から 30度まで5度ずつ回転させて水増しデータとして追加します。(※2)では、OpenCVを使って回転させます。(※3)では左右反転させます。

オリジナル写真で試してみよう

それでは、プログラムを完成させましょう。 画像を指定して実行すると、 写真を判定して、 カロリーを表示するようにしてみましょう。なお、ここでは以下のカロリーを表示することにしましょう。

ここでは、以下のテスト写真を使って判定に挑戦してみましょう。以下のプログラムを用いて判定を行います。

import cnn_model

import keras

import matplotlib.pyplot as plt

import numpy as np

from PIL import Image

import matplotlib.pyplot as plt

target_image= “test-sushi.jpg”

im_rows= 32 # 画像の縦ピクセルサイズ

im_cols= 32 # 画像の横ピクセルサイズ

im_color=3

in_shape=(im_rows, im_cols, im_color)

nb_classes= 3

LABELS=[“寿司”,”サラダ”, “麻婆豆腐”]

CALORIES=[588, 118, 648]

# 保存した CNN モデルを読み込む

model =cnn_model.get_model(in_shape, nb_classes)

model.load_weights(‘./image/photos-model.hdf5’)

def check_photo(path):

# 画像を読み込む

img = Image.open (path)

img=img.convert(“RGB”) # 色空間をRGBへ変換

img=img.resize ((im_cols, im_rows)) # サイズ変更

plt.imshow (img)

plt.show()

# データに変換

  1. asarray (img)

x=x.reshape (-1, im_rows, im_cols, im_color)

x = x / 255

# 予測

pre=model.predict([x]) [0]

idx=pre.argmax()

per= int (pre [idx] * 100)

return (idx, per)

def check_photo_str(path):

idx, per = check_photo (path)

# 答えを表示

print(” この写真は、”, LABELS [idx],”で、カロリーは”, CALORIES [idx],”kcal”)

print(“可能性は、”,per, “%”)

if __name__==’__main__’:

check_photo_str(‘test-sushi.jpg’)

check_photo_str(‘test-salad.jpg’)

さて、うまく判定できるでしょうか。以下のプログラムを実行してみましょう。

改良のヒント

さて、本節のプログラムでは、CNN を利用して、寿司とサラダと麻婆豆腐の3つの写真を判定するというものでした。もっとたくさんのメニューを学習させることで、もっ少し汎用的に使えるプログラムに仕上げることができるでしょう。ただしその場合、たくさんの料理の写真を集める必要がおいます。実際に本稿の通りに写真をダウンロードしても、なかなか思った通りの結果が出なかったとい読者の方もいるかもしれません。その大きな理由は学習に利用した写真データであることでしょう。機械学習で一番難しい作業は、データセットの作成なのです。もし、今回のようにゼロから写真を集めるのではなく、すでに業務データがあるときも同様で、それをどのように学習させてのように業務を支援するのかという点が問題となります。

本記事のまとめ

・Flickr API を使うことで大量の写真をダウンロードできます

・画像判定を行う場合、正しい画像データセットを作るのが重要です

・画像判定の精度が上がらないと思ったら、データセットを改善しましょう

・画像の水増しテクニックを使うと、少ない画像でも比較的精度を改善できます

以上になります、いかがでしたでしょうか。限られたソースで最大限効果を発揮するために画像を水増しする考えはちょっと意外だったのではないでしょうか。こうして常に先人たちが思考し続けた結果、我々は簡単に機械学習が可能になります。このことを忘れず、常に前向きに機械学習しかりプログラミングに取り組んでいきましょう。

 

 

プログラミングカテゴリの最新記事