PythonMania

普段はロボットとAIを組み合わせて色々作ってます。Python関係以外も色々投稿していくと思います。

【Python】画像認識 - KerasでVGG16のfine-tuningを試してみる 【DeepLearning】

f:id:mizuhiki0111:20190430180019p:plain

これまでfastaiを使った転移学習は行ってきましたが、学習した結果を使って

アプリを作成したりする場合にはKeras(Tensorflow)の方が便利そうだったので

今回はkerasを使った転移学習を行っていきます。



学習に使用するデータセットは例によってKaggleのいつものやつを使います。
(書いていませんでしたが、データセットをダウンロードするためにはKaggleへの登録が必要です)


www.kaggle.com



Kerasにも事前学習済モデルが用意されており、以下のモデルが使えるみたいです。



Xception
VGG16
VGG19
ResNet50
InceptionV3
InceptionResNetV2
MobileNet
DenseNet121
DenseNet169
DenseNet201


keras.io




今回はVGG16を使用して作成していきます。

ちなみにモデルを呼び出すときは

from keras.applications.vgg16 import VGG16

こんな感じで書けは使用することができます。


①画像・ラベルの読み込み・前加工・Keras形式のデータセット作成

#必要なライブラリのインポート
from sklearn.model_selection import train_test_split
import numpy as np
from PIL import Image
import os
from glob import glob
from sklearn.preprocessing import LabelEncoder
import cv2
from keras.utils import np_utils
from matplotlib import pyplot as plt
import pandas as pd

#画像のサイズ指定・カテゴリ数等指定
ScaleTo = 70
seed = 7
num_clases = 12



#トレーニングデータの読み込み
#画像データをフォルダ毎に分けて入れておけば、ラベルデータも生成してくれる

data_dir = "~/train"
path = "~/train/*/*.png"
files = glob(path)

trainImg = []
trainLabel = []
j = 1
num = len(files)

for img in files:
    print(str(j) + "/" + str(num) , end="\r")
    trainImg.append(cv2.resize(cv2.imread(img) ,(ScaleTo,ScaleTo)))
    j += 1


#ラベルデータの読み込み
for dir in os.listdir(data_dir):
    if dir == ".DS_Store":
        continue

    dir1 = data_dir + "/" + dir
    label = dir

    for file in os.listdir(dir1):
        if file != "Thumbs.db":
            trainLabel.append(label)

# kerasに渡すために画像をnumpy配列・ラベルをpd.Daraframeに変換。
image_list = np.asarray(trainImg)
label_list = pd.DataFrame(trainLabel)




画像の前加工
clearTrainImg = []
examples = [];getEx = True

for img in image_list:
    #ぼかしを入れてノイズを除去
    blurImg = cv2.GaussianBlur(img ,(5,5),0)
    #RGBからHSVに変換
    hsvImg = cv2.cvtColor(blurImg , cv2.COLOR_BGR2HSV)
    #マスクを作成
    lower_green = (25,40,50)
    upper_green = (75,255,255)
    mask = cv2.inRange(hsvImg , lower_green ,upper_green)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(11,11))
    mask = cv2.morphologyEx(mask , cv2.MORPH_CLOSE,kernel)

    #ブールマスクの作成
    bMask = mask > 0

    #マスクの適用
    #空のイメージの作成
    clear = np.zeros_like(img , np.uint8)
    #オリジナル画像にブールマスクを適用
    clear[bMask] = img[bMask]

    clearTrainImg.append(clear)

#8枚ほど表示してみる
for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(clearTrainImg[i])


#加工後の画像もnp配列に変換
clearTrainImg = np.asarray(clearTrainImg)
clearTrainImg.shape

#画像の正則化
clearTrainImg = clearTrainImg / 255




# ラベルをワンホットエンコーディング
le = LabelEncoder()
le = le.fit(label_list)
label_list = le.transform(label_list)
label_list
label_list = np_utils.to_categorical(label_list)
label_list

#トレーニングデータとバリデーションデータの分割
X_train, X_test, y_train, y_test = train_test_split(clearTrainImg, label_list, test_size=0.33, random_state=0)


#kerasのImageDataGeneratorを使いオーグメンテーションを指定
from keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
        rotation_range=180,  # randomly rotate images in the range
        zoom_range = 0.1, # Randomly zoom image
        width_shift_range=0.1,  # randomly shift images horizontally
        height_shift_range=0.1,  # randomly shift images vertically
        horizontal_flip=True,  # randomly flip images horizontally
        vertical_flip=True  # randomly flip images vertically
    )
datagen.fit(X_train)






②モデルの構築

#必要なライブラリの読み込み
from keras.applications.vgg16 import VGG16
from keras.models import Model
import tensorflow as tf
import keras
from tensorflow import keras
from keras.layers import Conv2D, MaxPooling2D,Input
from keras.layers import Dense, Dropout, Flatten, Activation,GlobalAveragePooling2D,Input
from keras.models import Sequential
from keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint
from keras.optimizers import Adam
from keras import optimizers



# VGG16のロード。FC層は不要なので include_top=False
input_tensor = Input(shape=(ScaleTo, ScaleTo, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)



# 最下層の出力層等オリジナルモデル部分をSequentialモデルで作成
top_model = Sequential()
top_model.add(Flatten(input_shape=vgg16.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(nb_classes, activation='softmax'))


# VGG16とオリジナルモデル部分を結合してモデルを作成
vgg_model = Model(input=vgg16.input, output=top_model(vgg16.output))

#一旦モデル全体を表示してみる
vgg_model.summary()

# input層を含めた15層目までは重みを固定
#最初の方の層は、汎用的な特徴検出に使われるため
#個々の部分の学習を省略することで効率よく学習を進めることができる
for layer in vgg_model.layers[:15]:
    layer.trainable = False


#モデルをコンパイル、多クラス分類を指定
vgg_model.compile(loss='categorical_crossentropy',
          optimizer=optimizers.SGD(lr=1e-3, momentum=0.9),
          metrics=['accuracy'])
vgg_model.summary()


③学習の実行

#学習の実行
hist = vgg_model.fit_generator(datagen.flow(X_train, y_train, batch_size=75),
                        epochs=35, validation_data=(X_test, y_test),
                        steps_per_epoch=X_train.shape[0])



④パラメータの保存と精度の評価

#パラメータの保存
vgg_model.save_weights('mnist_mlp_weights.h5')


print(vgg_model.evaluate(X_train, y_train)) #トレーニングの精度
print(vgg_model.evaluate(X_test, y_test))  #テスト精度


④テストデータの成型と推論、提出用データの作成


path = "~/test/*.png"
files = glob(path)

testImg = []
testId = []
j = 1
num = len(files)


for img in files:
    print("Obtain images: " + str(j) + "/" + str(num), end='\r')
    testId.append(img.split('/')[-1])  # Images id's
    testImg.append(cv2.resize(cv2.imread(img), (ScaleTo, ScaleTo)))
    j += 1

testImg = np.asarray(testImg)  # Train images set

for i in range(8):
    plt.subplot(2, 4, i + 1)
    plt.imshow(testImg[i])






#テストデータの成型
clearTestImg = []
examples = []; getEx = True
for img in testImg:
    # Use gaussian blur
    blurImg = cv2.GaussianBlur(img, (5, 5), 0)

   
    hsvImg = cv2.cvtColor(blurImg, cv2.COLOR_BGR2HSV)

    
    lower_green = (25, 40, 50)
    upper_green = (75, 255, 255)
    mask = cv2.inRange(hsvImg, lower_green, upper_green)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    
    bMask = mask > 0

    
    clear = np.zeros_like(img, np.uint8)  # Create empty image
    clear[bMask] = img[bMask]  # Apply boolean mask to the origin image

    clearTestImg.append(clear)  # Append image without backgroung

    
    if getEx:
        plt.subplot(2, 3, 1); plt.imshow(img)  # Show the original image
        plt.subplot(2, 3, 2); plt.imshow(blurImg)  # Blur image
        plt.subplot(2, 3, 3); plt.imshow(hsvImg)  # HSV image
        plt.subplot(2, 3, 4); plt.imshow(mask)  # Mask
        plt.subplot(2, 3, 5); plt.imshow(bMask)  # Boolean mask
        plt.subplot(2, 3, 6); plt.imshow(clear)  # Image without background
        getEx = False

clearTestImg = np.asarray(clearTestImg)

clearTestImg = clearTestImg / 255





pred = vgg_model.predict(clearTestImg)


#提出ファイルの作成
predNum = np.argmax(pred, axis=1)
predStr = le.classes_[predNum]

res = {'file': testId, 'species': predStr}
res = pd.DataFrame(res)
res.to_csv("res.csv", index=False)