Python
機械学習
MachineLearning
DeepLearning
Keras

画像認識で「綾鷹を選ばせる」AIを作る

こんにちは、絶賛プログラミング勉強中のtomoです。
Aidemyで画像認識について勉強し始めて1ヶ月が経ったので、学習成果として投稿します。

はじめに

突然ですが、皆さん「緑茶の中でも選ばれてしまう緑茶は何か」と問われたら何と答えますか?

おそらく50%以上の人は「綾鷹」と答えるかと思います。
この記事では、そんな綾鷹を画像認識によって人々に選ばせるAIを作成します。

Aidemyで学習した内容

「ディープラーニングで画像認識モデルを作ってみよう!」ルートで8つのコースを学びました。
特に「CNNを用いた画像認識」コースにおいて学んだ技術を複数使用しています。
(後述する目次の「⑵モデルを構築/学習する」の仕組みを学べます。)

目次

・実装概要
・AIの作成
  ⑴Iphoneで撮った写真を学習/検証データにする
  ⑵モデルを構築/学習する
    ①シンプルにモデルを構築する
    ②データを拡張する
    ③dropoutを追加する
  ⑶綾鷹を選ばせるプログラムを作る
・おわりに
・(2018/06/23)追加検証

実装概要

以下の3点を実装します。

・緑茶のペットボトルの写真から、商品名を予測
・予測結果が綾鷹である場合、「選ばれたのは、綾鷹でした。」と表示
・綾鷹でない場合、「綾鷹を選んでください。(もしかして:あなたが選んでいるのは
 「(緑茶の名前)」ではありませんか?)」と表示

AIの作成

早速AIを作成していきます。
以下の流れで進めます。

⑴Iphoneで撮った写真を学習/検証データにする
⑵予測モデルを構築/学習する
⑶綾鷹を選ばせるプログラムを作る

⑴Iphoneで撮った写真を学習/検証データにする

今回予測の対象とする緑茶は以下の10種類とします。
ジャケ写のようになりました。

IMG_0664.jpg

各種緑茶について、42枚ずつ撮影します。
ちなみに、お茶を500枚近く撮影する姿を家族に見られ、「お前大丈夫か?」と心配されました。

撮影したデータについて、以下のコードでラベリング(画像データと商品名の紐付け)を実施し、学習/検証データを用意します。

なお、各商品ごとにディレクトリを作成し、画像データを格納しているのを前提としてます。

#ラベリングによる学習/検証データの準備

from PIL import Image
import os, glob
import numpy as np
import random, math

#画像が保存されているルートディレクトリのパス
root_dir = "パス"
# 商品名
categories = ["綾鷹","お〜いお茶 抹茶入り","なごみ","お〜いお茶 新茶",
              "綾鷹 茶葉のあまみ","お〜いお茶","伊右衛門","お〜いお茶 濃い茶",
              "生茶","お〜いお茶 新緑"]

# 画像データ用配列
X = []
# ラベルデータ用配列
Y = []

#画像データごとにadd_sample()を呼び出し、X,Yの配列を返す関数
def make_sample(files):
    global X, Y
    X = []
    Y = []
    for cat, fname in files:
        add_sample(cat, fname)
    return np.array(X), np.array(Y)

#渡された画像データを読み込んでXに格納し、また、
#画像データに対応するcategoriesのidxをY格納する関数
def add_sample(cat, fname):
    img = Image.open(fname)
    img = img.convert("RGB")
    img = img.resize((150, 150))
    data = np.asarray(img)
    X.append(data)
    Y.append(cat)

#全データ格納用配列
allfiles = []

#カテゴリ配列の各値と、それに対応するidxを認識し、全データをallfilesにまとめる
for idx, cat in enumerate(categories):
    image_dir = root_dir + "/" + cat
    files = glob.glob(image_dir + "/*.jpg")
    for f in files:
        allfiles.append((idx, f))

#シャッフル後、学習データと検証データに分ける
random.shuffle(allfiles)
th = math.floor(len(allfiles) * 0.8)
train = allfiles[0:th]
test  = allfiles[th:]
X_train, y_train = make_sample(train)
X_test, y_test = make_sample(test)
xy = (X_train, X_test, y_train, y_test)
#データを保存する(データの名前を「tea_data.npy」としている)
np.save("保存先パス/tea_data.npy", xy)

⑵予測モデルを構築/学習する

モデルは畳み込みニューラルネットワーク(CNN)を使います。
シンプルなモデルから始め、精度をあげる工夫を追加していきます。

①シンプルにモデルを構築する

まずはシンプルにモデルを構築します。

#モデルの構築

from keras import layers, models

model = models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(150,150,3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(10,activation="sigmoid")) #分類先の種類分設定

#モデル構成の確認
model.summary()

「model.summary()」でモデル構成を確認できます。

続いて、モデルをコンパイルします。

#モデルのコンパイル

from keras import optimizers

model.compile(loss="binary_crossentropy",
              optimizer=optimizers.RMSprop(lr=1e-4),
              metrics=["acc"])

先ほど作成した学習/検証データを読み込み、データの正規化といった学習に向けた準備をします。

#データの準備

from keras.utils import np_utils
import numpy as np

categories = ["綾鷹","お〜いお茶 抹茶入り","なごみ","お〜いお茶 新茶",
              "綾鷹 茶葉のあまみ","お〜いお茶","伊右衛門","お〜いお茶 濃い茶",
              "生茶","お〜いお茶 新緑"]
nb_classes = len(categories)

X_train, X_test, y_train, y_test = np.load("保存した学習データ・テストデータのパス")

#データの正規化
X_train = X_train.astype("float") / 255
X_test  = X_test.astype("float")  / 255

#kerasで扱えるようにcategoriesをベクトルに変換
y_train = np_utils.to_categorical(y_train, nb_classes)
y_test  = np_utils.to_categorical(y_test, nb_classes)

準備したデータを使ってモデルを学習させます。

#モデルの学習

model = model.fit(X_train,
                  y_train,
                  epochs=10,
                  batch_size=6,
                  validation_data=(X_test,y_test))

epochごとに学習結果が表示されます。

Train on 336 samples, validate on 84 samples
Epoch 1/10
336/336 [==============================] - 20s 59ms/step - loss: 0.3543 - acc: 0.8887 - val_loss: 0.3321 - val_acc: 0.9000
Epoch 2/10
336/336 [==============================] - 20s 60ms/step - loss: 0.3214 - acc: 0.9000 - val_loss: 0.3152 - val_acc: 0.9000
Epoch 3/10
336/336 [==============================] - 19s 57ms/step - loss: 0.2802 - acc: 0.9012 - val_loss: 0.2602 - val_acc: 0.9083
Epoch 4/10
336/336 [==============================] - 20s 59ms/step - loss: 0.2119 - acc: 0.9179 - val_loss: 0.2072 - val_acc: 0.9262
Epoch 5/10
336/336 [==============================] - 20s 60ms/step - loss: 0.1548 - acc: 0.9423 - val_loss: 0.1791 - val_acc: 0.9345
Epoch 6/10
336/336 [==============================] - 21s 63ms/step - loss: 0.1217 - acc: 0.9536 - val_loss: 0.1331 - val_acc: 0.9488
Epoch 7/10
336/336 [==============================] - 20s 59ms/step - loss: 0.0950 - acc: 0.9664 - val_loss: 0.1264 - val_acc: 0.9536
Epoch 8/10
336/336 [==============================] - 21s 62ms/step - loss: 0.0747 - acc: 0.9759 - val_loss: 0.1395 - val_acc: 0.9429
Epoch 9/10
336/336 [==============================] - 20s 60ms/step - loss: 0.0646 - acc: 0.9792 - val_loss: 0.1314 - val_acc: 0.9476
Epoch 10/10
336/336 [==============================] - 19s 58ms/step - loss: 0.0513 - acc: 0.9830 - val_loss: 0.1073 - val_acc: 0.9631

学習が完了しましたら、学習結果をグラフで表示します。

#学習結果を表示

import matplotlib.pyplot as plt

acc = model.history['acc']
val_acc = model.history['val_acc']
loss = model.history['loss']
val_loss = model.history['val_loss']

epochs = range(len(acc))

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.savefig('精度を示すグラフのファイル名')

plt.figure()

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.savefig('損失値を示すグラフのファイル名')

figure1.png
figure2.png

6epoch目から精度が下がって(損失値が上がって)います。データ数が少なく、過学習を起こしていると思われます。

学習結果を保存しておきます。

#モデルの保存

json_string = model.model.to_json()
open('保存先パス/tea_predict.json', 'w').write(json_string)

#重みの保存

hdf5_file = "保存先パス/tea_predict.hdf5"
model.model.save_weights(hdf5_file)

続いて、学習させたモデルについて、未知のデータでテストします。(データ拡張は未実施)
一応、テストデータを準備するためのコードを貼っておきます。

from PIL import Image
import os, glob
import numpy as np
import random, math

# 画像が保存されているディレクトリのパス
root_dir = "パス"
# 画像が保存されているフォルダ名
categories = ["綾鷹","お〜いお茶 抹茶入り","なごみ","お〜いお茶 新茶",
              "綾鷹 茶葉のあまみ","お〜いお茶","伊右衛門","お〜いお茶 濃い茶",
              "生茶","お〜いお茶 新緑"]

X = [] # 画像データ
Y = [] # ラベルデータ

# フォルダごとに分けられたファイルを収集
#(categoriesのidxと、画像のファイルパスが紐づいたリストを生成)
allfiles = []
for idx, cat in enumerate(categories):
    image_dir = root_dir + "/" + cat
    files = glob.glob(image_dir + "/*.jpg")
    for f in files:
        allfiles.append((idx, f))

for cat, fname in allfiles:
    img = Image.open(fname)
    img = img.convert("RGB")
    img = img.resize((150, 150))
    data = np.asarray(img)
    X.append(data)
    Y.append(cat)

x = np.array(X)
y = np.array(Y)

np.save("保存先のパス/tea_data_test_X_150.npy", x)
np.save("保存先のパス/tea_data_test_Y_150.npy", y)

作成したテストデータを用いてモデルの予測精度を測ります。

# モデルの精度を測る

#評価用のデータの読み込み
eval_X = np.load("保存先のパス/tea_data_test_X_150.npy")
eval_Y = np.load("保存先のパス/tea_data_test_Y_150.npy")

#Yのデータをone-hotに変換
from keras.utils import np_utils

test_Y = np_utils.to_categorical(test_Y, 10)

score = model.model.evaluate(x=test_X,y=test_Y)

print('loss=', score[0])
print('accuracy=', score[1])

予測結果です。正解率は93.4%ですね。

100/100 [==============================] - 2s 20ms/step
loss= 1.02928255677
accuracy= 0.934000020027

その他、精度をあげるであろう手法をいくつか試していきます。

②データを拡張する

①では、画像数が少ないことから、過学習を起こしているように見えました。
そのため、KerasのImageDataGeneratorを使って画像を水増しします。

#画像の水増し

import os
import glob
import numpy as np
from keras.preprocessing.image import ImageDataGenerator,
                                      load_img, img_to_array, array_to_img

# 画像を拡張する関数
def draw_images(generator, x, dir_name, index):
    save_name = 'extened-' + str(index)
    g = generator.flow(x, batch_size=1, save_to_dir=output_dir,
                       save_prefix=save_name, save_format='jpeg')

    # 1つの入力画像から何枚拡張するかを指定(今回は50枚)
    for i in range(50):
        bach = g.next()

# 出力先ディレクトリの設定
output_dir = "出力先のディレクトリパス"

if not(os.path.exists(output_dir)):
    os.mkdir(output_dir)

# 拡張する画像の読み込み
images = glob.glob(os.path.join("画像の保存先ディレクトリのパス", "*.jpg"))

# ImageDataGeneratorを定義
datagen = ImageDataGenerator(rotation_range=30,
                            width_shift_range=20,
                            height_shift_range=0.,
                            zoom_range=0.1,
                            horizontal_flip=True,
                            vertical_flip=True)
:]
# 読み込んだ画像を順に拡張
for i in range(len(images)):
    img = load_img(images[i])
    img = img.resize((150, 150))
    x = img_to_array(img)
    x = np.expand_dims(x, axis=0)
    draw_images(datagen, x, output_dir, i)

うまく複製できているか確認します。

extened-0_0_1146.jpeg

ん?、ちょっと画像が粗いように思えます。①の時点で確認して気付けって話ですが。

「img.resize((150, 150))」を「img.resize((250, 250))」に変更して再度水増しします。

extened-4_0_9551.jpeg

このレベルであれば十分目で判断できますね。

①と同じようにモデルを学習させます。
(各種パスの修正、また、モデルのinput sizeを250*250*3に修正する必要があります。)

以下、学習結果です。

Train on 3360 samples, validate on 840 samples
Epoch 1/10
3360/3360 [==============================] - 604s 180ms/step - loss: 0.3157 - acc: 0.9002 - val_loss: 0.2828 - val_acc: 0.9008
Epoch 2/10
3360/3360 [==============================] - 618s 184ms/step - loss: 0.2446 - acc: 0.9086 - val_loss: 0.2084 - val_acc: 0.9198
Epoch 3/10
3360/3360 [==============================] - 608s 181ms/step - loss: 0.1824 - acc: 0.9320 - val_loss: 0.1750 - val_acc: 0.9323
Epoch 4/10
3360/3360 [==============================] - 613s 183ms/step - loss: 0.1400 - acc: 0.9470 - val_loss: 0.1241 - val_acc: 0.9551
Epoch 5/10
3360/3360 [==============================] - 1054s 314ms/step - loss: 0.1068 - acc: 0.9610 - val_loss: 0.1053 - val_acc: 0.9625
Epoch 6/10
3360/3360 [==============================] - 572s 170ms/step - loss: 0.0795 - acc: 0.9717 - val_loss: 0.0733 - val_acc: 0.9735
Epoch 7/10
3360/3360 [==============================] - 564s 168ms/step - loss: 0.0639 - acc: 0.9775 - val_loss: 0.0681 - val_acc: 0.9723
Epoch 8/10
3360/3360 [==============================] - 3426s 1s/step - loss: 0.0480 - acc: 0.9837 - val_loss: 0.0707 - val_acc: 0.9737
Epoch 9/10
3360/3360 [==============================] - 635s 189ms/step - loss: 0.0380 - acc: 0.9871 - val_loss: 0.0577 - val_acc: 0.9770
Epoch 10/10
3360/3360 [==============================] - 653s 194ms/step - loss: 0.0277 - acc: 0.9907 - val_loss: 0.0376 - val_acc: 0.9867

figure1.png
figure2.png

精度上がってますね。
テストデータを250*250に変更した上で作成し、予測精度を測ります。

100/100 [==============================] - 5s 51ms/step
loss= 0.377869169712
accuracy= 0.975000026226

正解率97.5%、水増し前より4%上昇しました。

③dropoutを追加する

過学習を抑制するdropoutをモデルに追加します。

#モデルの構築

from keras import layers, models

model = models.Sequential()
model.add(layers.Conv2D(32,(3,3),activation="relu",input_shape=(250,250,3)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(3,3),activation="relu"))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512,activation="relu"))
model.add(layers.Dense(10,activation="sigmoid"))

②と同様に学習させます。
以下、学習結果。

Train on 3360 samples, validate on 840 samples
Epoch 1/10
3360/3360 [==============================] - 568s 169ms/step - loss: 0.3291 - acc: 0.8956 - val_loss: 0.2981 - val_acc: 0.9000
Epoch 2/10
3360/3360 [==============================] - 596s 177ms/step - loss: 0.2635 - acc: 0.9058 - val_loss: 0.2276 - val_acc: 0.9129
Epoch 3/10
3360/3360 [==============================] - 647s 192ms/step - loss: 0.2097 - acc: 0.9197 - val_loss: 0.1930 - val_acc: 0.9274
Epoch 4/10
3360/3360 [==============================] - 694s 206ms/step - loss: 0.1734 - acc: 0.9338 - val_loss: 0.1612 - val_acc: 0.9400
Epoch 5/10
3360/3360 [==============================] - 710s 211ms/step - loss: 0.1406 - acc: 0.9457 - val_loss: 0.1403 - val_acc: 0.9446
Epoch 6/10
3360/3360 [==============================] - 669s 199ms/step - loss: 0.1135 - acc: 0.9565 - val_loss: 0.1111 - val_acc: 0.9550
Epoch 7/10
3360/3360 [==============================] - 597s 178ms/step - loss: 0.0921 - acc: 0.9654 - val_loss: 0.0819 - val_acc: 0.9731
Epoch 8/10
3360/3360 [==============================] - 592s 176ms/step - loss: 0.0736 - acc: 0.9736 - val_loss: 0.0707 - val_acc: 0.9773
Epoch 9/10
3360/3360 [==============================] - 596s 177ms/step - loss: 0.0583 - acc: 0.9793 - val_loss: 0.0585 - val_acc: 0.9790
Epoch 10/10
3360/3360 [==============================] - 588s 175ms/step - loss: 0.0474 - acc: 0.9836 - val_loss: 0.0496 - val_acc: 0.9819

figure1.png
figure2.png

テストデータで予測精度を測ります。

100/100 [==============================] - 5s 52ms/step
loss= 0.516686134338
accuracy= 0.967000012398

ありゃ、精度が下がりました。
②は過学習に至る前であり、単に学習効率が下がったということでしょうか。

損失率を0.25に下げて再度学習してみます。

Train on 3360 samples, validate on 840 samples
Epoch 1/10
3360/3360 [==============================] - 583s 173ms/step - loss: 0.3241 - acc: 0.8952 - val_loss: 0.2866 - val_acc: 0.9037
Epoch 2/10
3360/3360 [==============================] - 571s 170ms/step - loss: 0.2450 - acc: 0.9087 - val_loss: 0.2085 - val_acc: 0.9226
Epoch 3/10
3360/3360 [==============================] - 563s 167ms/step - loss: 0.1822 - acc: 0.9326 - val_loss: 0.1853 - val_acc: 0.9299
Epoch 4/10
3360/3360 [==============================] - 603s 180ms/step - loss: 0.1407 - acc: 0.9469 - val_loss: 0.1332 - val_acc: 0.9525
Epoch 5/10
3360/3360 [==============================] - 567s 169ms/step - loss: 0.1136 - acc: 0.9576 - val_loss: 0.1001 - val_acc: 0.9650
Epoch 6/10
3360/3360 [==============================] - 574s 171ms/step - loss: 0.0895 - acc: 0.9676 - val_loss: 0.0888 - val_acc: 0.9648
Epoch 7/10
3360/3360 [==============================] - 584s 174ms/step - loss: 0.0693 - acc: 0.9750 - val_loss: 0.0750 - val_acc: 0.9742
Epoch 8/10
3360/3360 [==============================] - 570s 170ms/step - loss: 0.0547 - acc: 0.9811 - val_loss: 0.0532 - val_acc: 0.9799
Epoch 9/10
3360/3360 [==============================] - 579s 172ms/step - loss: 0.0446 - acc: 0.9846 - val_loss: 0.0545 - val_acc: 0.9788
Epoch 10/10
3360/3360 [==============================] - 563s 168ms/step - loss: 0.0334 - acc: 0.9888 - val_loss: 0.0433 - val_acc: 0.9842

figure1.png
figure2.png

テストデータの予測結果。やはり②の方が精度が高いです。

100/100 [==============================] - 5s 53ms/step
loss= 0.432936085963
accuracy= 0.973000030518

ただ、グラフを見る限り、dropoutの損失率を0.5にした時が最もうまく学習できているようです。

今回はepoch数を10で固定していますが、epoch数を増やした時にdropoutが過学習を抑制し、さらに高い精度が出るのでは、、と思います。(後述の「(2018/06/23)追加検証」で検証しています。)

⑶綾鷹を選ばせるプログラムを作る

②のモデルを採択し、綾鷹を選ばせるプログラムを作ります。

#綾鷹を選ばせるプログラム

from keras import models
from keras.models import model_from_json
from keras.preprocessing import image
import numpy as np

#保存したモデルの読み込み
model = model_from_json(open('保存先のフォルダ/tea_predict.json').read())
#保存した重みの読み込み
model.load_weights('保存先のフォルダ/tea_predict.hdf5')

categories = ["綾鷹","お〜いお茶 抹茶入り","なごみ","お〜いお茶 新茶","綾鷹 茶葉のあまみ",
              "お〜いお茶","伊右衛門","お〜いお茶 濃い茶","生茶","お〜いお茶 新緑"]

#画像を読み込む
img_path = str(input())
img = image.load_img(img_path,target_size=(250, 250, 3))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)

#予測
features = model.predict(x)

#予測結果によって処理を分ける
if features[0,0] == 1:
    print ("選ばれたのは、綾鷹でした。")

elif features[0,4] == 1:
    print ("選ばれたのは、綾鷹(茶葉のあまみ)でした。")

else:
    for i in range(0,10):
          if features[0,i] == 1:
              cat = categories[i]
    message = "綾鷹を選んでください。(もしかして:あなたが選んでいるのは「" + cat + "」ではありませんか?)"
    print(message)

さて、テストしてみましょう。

IMG_0045.jpg

出力結果:
選ばれたのは、綾鷹(茶葉のあまみ)でした。

IMG_0035.jpg

出力結果:
綾鷹を選んでください。(もしかして:あなたが選んでいるのは「なごみ」ではありませんか?)

IMG_0078.jpg

出力結果:
選ばれたのは、綾鷹でした。

IMG_0097.jpg

出力結果:
綾鷹を選んでください。(もしかして:あなたが選んでいるのは「お〜いお茶 新緑」ではありませんか?)

問題なく予測できました。

おわりに

前述したepoch数の変更だけでなく、活性化関数、損失関数といったモデルの変更、画像データの白色化といった前処理など、まだまだ精度をあげる余地があります。
今回は一旦の投稿となりましたが、そういった機械学習の面白さに気づくとこができました。

今後は画像認識だけでなく、自然言語処理などにも挑戦し、また、kaggleにもチャレンジしようかと思います。

お読みいただき、ありがとうございました。
※記事にミスや改善点等ありましたら、ご指摘いただけると幸いです。

(2018/06/23)追加検証

epoch数を10から15に増やして精度を検証しました。
対象は②のモデル(画像水増しのみ)と、③のモデル(dropoutを追加)のうち損失率を0.5としたモデルです。

まずは②のモデルを再度学習させます。
学習結果は以下の通り。

Train on 3360 samples, validate on 840 samples
Epoch 1/15
3360/3360 [==============================] - 571s 170ms/step - loss: 0.3126 - acc: 0.8946 - val_loss: 0.2541 - val_acc: 0.9065
Epoch 2/15
3360/3360 [==============================] - 580s 173ms/step - loss: 0.2143 - acc: 0.9204 - val_loss: 0.1797 - val_acc: 0.9306
Epoch 3/15
3360/3360 [==============================] - 561s 167ms/step - loss: 0.1543 - acc: 0.9429 - val_loss: 0.1335 - val_acc: 0.9506
Epoch 4/15
3360/3360 [==============================] - 595s 177ms/step - loss: 0.1162 - acc: 0.9567 - val_loss: 0.1060 - val_acc: 0.9575
Epoch 5/15
3360/3360 [==============================] - 583s 174ms/step - loss: 0.0855 - acc: 0.9686 - val_loss: 0.0832 - val_acc: 0.9683
Epoch 6/15
3360/3360 [==============================] - 559s 166ms/step - loss: 0.0658 - acc: 0.9765 - val_loss: 0.0654 - val_acc: 0.9767
Epoch 7/15
3360/3360 [==============================] - 572s 170ms/step - loss: 0.0481 - acc: 0.9833 - val_loss: 0.0574 - val_acc: 0.9788
Epoch 8/15
3360/3360 [==============================] - 568s 169ms/step - loss: 0.0396 - acc: 0.9862 - val_loss: 0.0402 - val_acc: 0.9862
Epoch 9/15
3360/3360 [==============================] - 563s 168ms/step - loss: 0.0296 - acc: 0.9907 - val_loss: 0.0674 - val_acc: 0.9763
Epoch 10/15
3360/3360 [==============================] - 582s 173ms/step - loss: 0.0242 - acc: 0.9925 - val_loss: 0.0316 - val_acc: 0.9885
Epoch 11/15
3360/3360 [==============================] - 580s 173ms/step - loss: 0.0180 - acc: 0.9941 - val_loss: 0.0447 - val_acc: 0.9837
Epoch 12/15
3360/3360 [==============================] - 586s 174ms/step - loss: 0.0140 - acc: 0.9965 - val_loss: 0.0272 - val_acc: 0.9910
Epoch 13/15
3360/3360 [==============================] - 569s 169ms/step - loss: 0.0127 - acc: 0.9962 - val_loss: 0.0286 - val_acc: 0.9896
Epoch 14/15
3360/3360 [==============================] - 567s 169ms/step - loss: 0.0117 - acc: 0.9973 - val_loss: 0.0198 - val_acc: 0.9927
Epoch 15/15
3360/3360 [==============================] - 555s 165ms/step - loss: 0.0067 - acc: 0.9983 - val_loss: 0.0350 - val_acc: 0.9874

figure1.png
figure2.png

8epoch目からうまく学習が進んでいませんね。

100/100 [==============================] - 5s 51ms/step
loss= 0.384550094604
accuracy= 0.97600001812

正解率は97.6%。0.1%精度が上がりました。

続いて、③の損失率を0.5としたモデルを再度学習させます。

Train on 3360 samples, validate on 840 samples
Epoch 1/15
3360/3360 [==============================] - 577s 172ms/step - loss: 0.3261 - acc: 0.8910 - val_loss: 0.2827 - val_acc: 0.9006
Epoch 2/15
3360/3360 [==============================] - 568s 169ms/step - loss: 0.2429 - acc: 0.9095 - val_loss: 0.2026 - val_acc: 0.9261
Epoch 3/15
3360/3360 [==============================] - 578s 172ms/step - loss: 0.1865 - acc: 0.9318 - val_loss: 0.1787 - val_acc: 0.9342
Epoch 4/15
3360/3360 [==============================] - 564s 168ms/step - loss: 0.1458 - acc: 0.9458 - val_loss: 0.1369 - val_acc: 0.9474
Epoch 5/15
3360/3360 [==============================] - 572s 170ms/step - loss: 0.1184 - acc: 0.9563 - val_loss: 0.1229 - val_acc: 0.9521
Epoch 6/15
3360/3360 [==============================] - 566s 168ms/step - loss: 0.0937 - acc: 0.9662 - val_loss: 0.1051 - val_acc: 0.9642
Epoch 7/15
3360/3360 [==============================] - 559s 166ms/step - loss: 0.0750 - acc: 0.9735 - val_loss: 0.0887 - val_acc: 0.9664
Epoch 8/15
3360/3360 [==============================] - 561s 167ms/step - loss: 0.0629 - acc: 0.9784 - val_loss: 0.0599 - val_acc: 0.9776
Epoch 9/15
3360/3360 [==============================] - 560s 167ms/step - loss: 0.0500 - acc: 0.9827 - val_loss: 0.0586 - val_acc: 0.9771
Epoch 10/15
3360/3360 [==============================] - 573s 171ms/step - loss: 0.0408 - acc: 0.9857 - val_loss: 0.0434 - val_acc: 0.9844
Epoch 11/15
3360/3360 [==============================] - 638s 190ms/step - loss: 0.0362 - acc: 0.9873 - val_loss: 0.0350 - val_acc: 0.9877
Epoch 12/15
3360/3360 [==============================] - 568s 169ms/step - loss: 0.0272 - acc: 0.9910 - val_loss: 0.0392 - val_acc: 0.9865
Epoch 13/15
3360/3360 [==============================] - 561s 167ms/step - loss: 0.0234 - acc: 0.9926 - val_loss: 0.0483 - val_acc: 0.9825
Epoch 14/15
3360/3360 [==============================] - 579s 172ms/step - loss: 0.0220 - acc: 0.9932 - val_loss: 0.0332 - val_acc: 0.9869
Epoch 15/15
3360/3360 [==============================] - 562s 167ms/step - loss: 0.0161 - acc: 0.9945 - val_loss: 0.0262 - val_acc: 0.9901

figure1_epoch15.png
figure2_epoch15.png

これでも10epoch目から過学習気味のようです。

100/100 [==============================] - 6s 58ms/step
loss= 0.36878341198
accuracy= 0.97700001955

正解率は97.7%。今までで一番高い精度です。

「dropoutを追加して学習効率が下がっただけで、epoch数を増やせば高い精度が出るのではないか」と推測しましたが、その通りになったのではないかと思います。

あとは過学習を抑えるために損失率を少しずつあげていけば、より高い正解率になると思います。