Missed TensorFlow World? Check out the recap. Learn more

Image classification

View on TensorFlow.org Run in Google Colab View source on GitHub Download notebook

このチュートリアルでは、画像から猫または犬を分類する方法を示します。 tf.keras.Sequential モデルを使用して画像分類器を構築し、 tf.keras.preprocessing.image.ImageDataGenerator を使用してデータをロードします。このチュートリアルでは、以下のコンセプトにしたがって、実践的な経験と感覚を養います。

  • tf.keras.preprocessing.image.ImageDataGenerator クラスを使用して データ入力パイプライン を構築し、モデルで使用するディスク上のデータを効率的に処理します。
  • 過学習(Overfitting) —過学習を識別および防止する方法。
  • データ拡張(Data Augmentation) および ドロップアウト(dropout) —データパイプラインおよび画像分類モデルに組み込むコンピュータービジョンタスクの過学習と戦うための重要なテクニック。

このチュートリアルは、基本的な機械学習のワークフローに従います。

  1. データの調査及び理解
  2. 入力パイプラインの構築
  3. モデルの構築
  4. モデルの学習
  5. モデルのテスト
  6. モデルの改善とプロセスの繰り返し

パッケージのインポート

まずは必要なパッケージをインポートすることから始めましょう。 osパッケージはファイルとディレクトリ構造を読み込み、 NumPy は python リストの numpy 配列への変換と必要な行列演算の実行、 matplotlib.pyplot はグラフの描画や学習データおよび検証データに含まれる画像の表示、に利用します。

from __future__ import absolute_import, division, print_function, unicode_literals

モデルの構築に必要な TensorFlow と Keras クラスをインポートします。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator

import os
import numpy as np
import matplotlib.pyplot as plt

データの読み込み

データセットのダウンロードから始めます。このチュートリアルでは、 Kaggle の Dogs vs Cats データセットをフィルタリングしたバージョンを使用します。データセットのアーカイブバージョンをダウンロードし、"/tmp/"ディレクトリに保存します。

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'

path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)

PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')
Downloading data from https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip
68608000/68606236 [==============================] - 0s 0us/step

データセットのディレクトリ構造は次のとおりです:

cats_and_dogs_filtered
|__ train
    |______ cats: [cat.0.jpg, cat.1.jpg, cat.2.jpg ....]
    |______ dogs: [dog.0.jpg, dog.1.jpg, dog.2.jpg ...]
|__ validation
    |______ cats: [cat.2000.jpg, cat.2001.jpg, cat.2002.jpg ....]
    |______ dogs: [dog.2000.jpg, dog.2001.jpg, dog.2002.jpg ...]

データの内容を抽出した後、学習および検証セットのための適切なファイルパスで変数を設定します。

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')
train_cats_dir = os.path.join(train_dir, 'cats')  # 学習用の猫画像のディレクトリ
train_dogs_dir = os.path.join(train_dir, 'dogs')  # 学習用の犬画像のディレクトリ
validation_cats_dir = os.path.join(validation_dir, 'cats')  # 検証用の猫画像のディレクトリ
validation_dogs_dir = os.path.join(validation_dir, 'dogs')  # 検証用の犬画像のディレクトリ

データの理解

学習および検証ディレクトリの中にある猫と犬の画像の数を見てみましょう:

num_cats_tr = len(os.listdir(train_cats_dir))
num_dogs_tr = len(os.listdir(train_dogs_dir))

num_cats_val = len(os.listdir(validation_cats_dir))
num_dogs_val = len(os.listdir(validation_dogs_dir))

total_train = num_cats_tr + num_dogs_tr
total_val = num_cats_val + num_dogs_val
print('total training cat images:', num_cats_tr)
print('total training dog images:', num_dogs_tr)

print('total validation cat images:', num_cats_val)
print('total validation dog images:', num_dogs_val)
print("--")
print("Total training images:", total_train)
print("Total validation images:", total_val)
total training cat images: 1000
total training dog images: 1000
total validation cat images: 500
total validation dog images: 500
--
Total training images: 2000
Total validation images: 1000

便宜上、データセットの前処理およびネットワークの学習中に使用する変数を設定します。

batch_size = 128
epochs = 15
IMG_HEIGHT = 150
IMG_WIDTH = 150

データの準備

モデルにデータを送る前に、画像を適切に前処理された浮動小数点テンソルにフォーマットします。

1.ディスクから画像を読み取ります。 2.これらの画像のコンテンツをデコードし、RGB値にしたがって適切なグリッド形式に変換します。 3.それらを浮動小数点テンソルに変換します。 4.ニューラルネットワークは小さな入力値を扱う方が適しているため、テンソルを0〜255の値から0〜1の値にリスケーリングします。

幸い、これらすべてのタスクは、 tf.keras によって提供される ImageDataGenerator クラスで実行できます。この ImageDataGenerator はディスクから画像を読み取り、適切なテンソルに前処理を行います。さらに、これらの画像をテンソルのバッチに変換するジェネレータをセットアップします。これは、ネットワーク学習時に便利です。

train_image_generator = ImageDataGenerator(rescale=1./255) # 学習データのジェネレータ
validation_image_generator = ImageDataGenerator(rescale=1./255) # 検証データのジェネレータ

学習および検証画像のジェネレータを定義したのち、 flow_from_directory メソッドはディスクから画像をロードし、リスケーリングを適用し、画像を必要な大きさにリサイズします。

train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
                                                           directory=train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                           class_mode='binary')
Found 2000 images belonging to 2 classes.
val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
                                                              directory=validation_dir,
                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                              class_mode='binary')
Found 1000 images belonging to 2 classes.

学習用画像の可視化

学習用のジェネレータから画像バッチを抽出して可視化します。(この例では32個の画像を抽出し、そのうち5つを matplotlib で描画します。)

sample_training_images, _ = next(train_data_gen)

next 関数はデータセットからバッチを返します。 next 関数の返り値は (x_train、y_train) の形式で、 x_train は学習用の特徴量、 y_train はそのラベルです。ラベルを破棄して、学習用画像の可視化のみを行います。

# この関数は、1行5列のグリッド形式で画像をプロットし、画像は各列に配置されます。
def plotImages(images_arr):
    fig, axes = plt.subplots(1, 5, figsize=(20,20))
    axes = axes.flatten()
    for img, ax in zip( images_arr, axes):
        ax.imshow(img)
        ax.axis('off')
    plt.tight_layout()
    plt.show()
plotImages(sample_training_images[:5])

png

モデルの構築

モデルはmax pooling層を伴う3つの畳み込みブロックからなります。さらに relu 活性化関数によるアクティベーションを伴う512ユニットの全結合層があります。モデルは、シグモイド活性化関数による2値分類に基づいてクラスに属する確率を出力します。

model = Sequential([
    Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
    MaxPooling2D(),
    Conv2D(32, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(64, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Flatten(),
    Dense(512, activation='relu'),
    Dense(1, activation='sigmoid')
])

モデルのコンパイル

このチュートリアルでは、 ADAM オプティマイザーと binary cross entropy 損失関数を選択します。各学習エポックの学習と検証の精度を表示するために、metrics 引数を渡します。

model.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

モデルの概要

すべてのネットワークのレイヤーを見るには、モデルの summary メソッドを利用します:

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 150, 150, 16)      448       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 75, 75, 16)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 75, 75, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 37, 37, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 37, 37, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 18, 18, 64)        0         
_________________________________________________________________
flatten (Flatten)            (None, 20736)             0         
_________________________________________________________________
dense (Dense)                (None, 512)               10617344  
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 513       
=================================================================
Total params: 10,641,441
Trainable params: 10,641,441
Non-trainable params: 0
_________________________________________________________________

モデルの学習

ImageDataGenerator クラスの fit_generator メソッドを使用して、ネットワークを学習します。

history = model.fit_generator(
    train_data_gen,
    steps_per_epoch=total_train // batch_size,
    epochs=epochs,
    validation_data=val_data_gen,
    validation_steps=total_val // batch_size
)
Epoch 1/15
15/15 [==============================] - 8s 550ms/step - loss: 0.9881 - accuracy: 0.4920 - val_loss: 0.6851 - val_accuracy: 0.6161
Epoch 2/15
15/15 [==============================] - 8s 543ms/step - loss: 0.6768 - accuracy: 0.5748 - val_loss: 0.6497 - val_accuracy: 0.6362
Epoch 3/15
15/15 [==============================] - 8s 517ms/step - loss: 0.6457 - accuracy: 0.6245 - val_loss: 0.6051 - val_accuracy: 0.6864
Epoch 4/15
15/15 [==============================] - 7s 491ms/step - loss: 0.6164 - accuracy: 0.6629 - val_loss: 0.6183 - val_accuracy: 0.6272
Epoch 5/15
15/15 [==============================] - 7s 482ms/step - loss: 0.5750 - accuracy: 0.6891 - val_loss: 0.6018 - val_accuracy: 0.6752
Epoch 6/15
15/15 [==============================] - 7s 465ms/step - loss: 0.5196 - accuracy: 0.7495 - val_loss: 0.5721 - val_accuracy: 0.7065
Epoch 7/15
15/15 [==============================] - 7s 450ms/step - loss: 0.4501 - accuracy: 0.7890 - val_loss: 0.5546 - val_accuracy: 0.7199
Epoch 8/15
15/15 [==============================] - 7s 455ms/step - loss: 0.3796 - accuracy: 0.8375 - val_loss: 0.5749 - val_accuracy: 0.7087
Epoch 9/15
15/15 [==============================] - 6s 430ms/step - loss: 0.4084 - accuracy: 0.8072 - val_loss: 0.5814 - val_accuracy: 0.6953
Epoch 10/15
15/15 [==============================] - 6s 414ms/step - loss: 0.3503 - accuracy: 0.8542 - val_loss: 0.6536 - val_accuracy: 0.7143
Epoch 11/15
15/15 [==============================] - 6s 395ms/step - loss: 0.2832 - accuracy: 0.8684 - val_loss: 0.6630 - val_accuracy: 0.6897
Epoch 12/15
15/15 [==============================] - 6s 405ms/step - loss: 0.2237 - accuracy: 0.9140 - val_loss: 0.6662 - val_accuracy: 0.7054
Epoch 13/15
15/15 [==============================] - 6s 413ms/step - loss: 0.1779 - accuracy: 0.9375 - val_loss: 0.7137 - val_accuracy: 0.7165
Epoch 14/15
15/15 [==============================] - 6s 398ms/step - loss: 0.1395 - accuracy: 0.9503 - val_loss: 0.8022 - val_accuracy: 0.7221
Epoch 15/15
15/15 [==============================] - 6s 402ms/step - loss: 0.1148 - accuracy: 0.9626 - val_loss: 0.8217 - val_accuracy: 0.7254

学習結果の可視化

ネットワークを学習した後、結果を可視化します。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

png

プロットからわかるように、学習セットの精度と検証セットの精度は大幅に外れており、モデルは検証セットで約70%の精度しか達成していません。

何がうまくいかなかったかを見て、モデル全体のパフォーマンスを向上してみましょう。

過学習

上記のプロットでは、学習セットの精度は時間とともに直線的に向上していますが、検証セットの精度は学習プロセスの中で約70%あたりで頭打ちになっています。そして、学習と検証の精度の違いが顕著です。これは 過学習 のサインです。

学習サンプルが少ない場合、モデルは学習サンプルに含まれるノイズや不要な詳細から学習してしまい、これによって新しいサンプルに対するモデルの性能に悪影響を与えることがあります。この現象は、過学習として知られています。過学習とは、モデルが新しいデータセットに対して汎化するのが難しい状態をいいます。

学習プロセスにおいて過学習に対抗する手段はいくつかあります。このチュートリアルでは、データ拡張(data Augmentation) を使用し、さらにモデルに ドロップアウト(dropout) を追加します。

データ拡張(Data augmentation)

過学習は一般に、学習サンプルが少ない場合に発生します。この問題を解決する方法の1つは、十分な数の学習サンプルが含まれるようにデータセットを拡張することです。データ拡張は、既存の学習サンプルに対してランダムな変換を行い、データセットとして利用できそうな画像を生成するアプローチをとります。このデータ拡張の目的は、学習中にモデルがまったくおなじ画像を2回利用しないようにすることです。これによってモデルをデータのより多くの特徴を利用し、より汎化することができます。

tf.keras においては、このデータ拡張を ImageDataGenerator クラスを使用して実装します。データセットに対するさまざまな変換を指定することによって、学習プロセス中にそれが適用されます。

データの拡張と可視化

最初に、ランダムな水平反転による拡張をデータセットに適用し、それぞれの画像が変換後にどのように見えるかを確認します。

水平反転の適用

このデータ拡張を適用するためには、 ImageDataGenerator クラスの引数として horizontal_flip を渡し、 Trueを設定します。

image_gen = ImageDataGenerator(rescale=1./255, horizontal_flip=True)
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))
Found 2000 images belonging to 2 classes.

学習サンプルから1つのサンプル画像を取得する作業を5回繰り返して、おなじ画像に5回データ拡張が適用されるようにします。

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
# 上で学習用画像の可視化のために定義、使用されたおなじカスタムプロット関数を再利用する
plotImages(augmented_images)

png

画像のランダムな回転

回転のデータ拡張を利用して学習用サンプルをランダムに左右45度の範囲で回転させてみましょう。

image_gen = ImageDataGenerator(rescale=1./255, rotation_range=45)
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
Found 2000 images belonging to 2 classes.
plotImages(augmented_images)

png

ズームによるデータ拡張の適用

データセットにズームによるデータ拡張を適用して、画像をランダムに最大50%拡大します。

image_gen = ImageDataGenerator(rescale=1./255, zoom_range=0.5)
train_data_gen = image_gen.flow_from_directory(batch_size=batch_size,
                                               directory=train_dir,
                                               shuffle=True,
                                               target_size=(IMG_HEIGHT, IMG_WIDTH))

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
Found 2000 images belonging to 2 classes.
plotImages(augmented_images)

png

すべてのデータ拡張を同時に利用する

ここまでで紹介したすべてのデータ拡張機能を適用します。ここでは、学習用画像に対して、リスケール、45度の回転、幅シフト、高さシフト、水平反転、ズームを適用しました。

image_gen_train = ImageDataGenerator(
                    rescale=1./255,
                    rotation_range=45,
                    width_shift_range=.15,
                    height_shift_range=.15,
                    horizontal_flip=True,
                    zoom_range=0.5
                    )
train_data_gen = image_gen_train.flow_from_directory(batch_size=batch_size,
                                                     directory=train_dir,
                                                     shuffle=True,
                                                     target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                     class_mode='binary')
Found 2000 images belonging to 2 classes.

これらのデータ拡張がデータセットにランダムに適用されたときに、一つの画像に対して5回の個別の適用を行った際にそれぞれどのように見えるかを可視化します。

augmented_images = [train_data_gen[0][0][0] for i in range(5)]
plotImages(augmented_images)

png

検証データジェネレータの構築

一般に、データ拡張は学習サンプルのみに適用します。今回は、 ImageDataGenerator を使用して検証画像に対してリスケールのみを実施し、バッチに変換します。

image_gen_val = ImageDataGenerator(rescale=1./255)
val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                                 directory=validation_dir,
                                                 target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                 class_mode='binary')
Found 1000 images belonging to 2 classes.

ドロップアウト(dropout)

過学習を避けるもう一つの方法は、ネットワークに ドロップアウト を導入することです。これは、ネットワークにおいて重みを小さくする正則化の方式で、これによって重みの値の分布がより規則的になり、少ない学習データに対する過学習を減らすことができます。ドロップアウトはこのチュートリアルで利用される正則化手法の一つです。

ドロップアウトをレイヤーに適用すると、学習プロセス中に適用されたレイヤーのうちランダムに出力ユニットをドロップアウト(ゼロに設定)します。ドロップアウトは、入力値として0.1、0.2、0.4といった形式の小数をとります。これは、適用されたレイヤーからランダムに出力単位の10%、20%、または40%をドロップアウトすることを意味します。

特定のレイヤーに0.1ドロップアウトを適用すると、各学習エポックにおいて出力ユニットの10%がランダムに0にされます。

この新しいドロップアウト機能を使用したネットワークアーキテクチャを作成し、異なる畳み込みレイヤーや全接続レイヤーに適用してみましょう。

ドロップアウトを追加した新しいネットワークの構築

ここでは、ドロップアウトを最初と最後の max pool 層に適用します。ドロップアウトを適用すると、各学習エポック中にニューロンの20%がランダムにゼロに設定されます。これにより、学習データセットに対する過学習を避けることができます。

model_new = Sequential([
    Conv2D(16, 3, padding='same', activation='relu', 
           input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
    MaxPooling2D(),
    Dropout(0.2),
    Conv2D(32, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Conv2D(64, 3, padding='same', activation='relu'),
    MaxPooling2D(),
    Dropout(0.2),
    Flatten(),
    Dense(512, activation='relu'),
    Dense(1, activation='sigmoid')
])

モデルのコンパイル

ネットワークにドロップアウトを導入した後、モデルをコンパイルし、レイヤーの概要を表示します。

model_new.compile(optimizer='adam',
              loss='binary_crossentropy',
              metrics=['accuracy'])

model_new.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_3 (Conv2D)            (None, 150, 150, 16)      448       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 75, 75, 16)        0         
_________________________________________________________________
dropout (Dropout)            (None, 75, 75, 16)        0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 75, 75, 32)        4640      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 37, 37, 32)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 37, 37, 64)        18496     
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 18, 18, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 18, 18, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 20736)             0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               10617344  
_________________________________________________________________
dense_3 (Dense)              (None, 1)                 513       
=================================================================
Total params: 10,641,441
Trainable params: 10,641,441
Non-trainable params: 0
_________________________________________________________________

モデルの学習

学習サンプルにデータ拡張を導入し、ネットワークにドロップアウトを追加した後、この新しいネットワークを学習します:

history = model_new.fit_generator(
    train_data_gen,
    steps_per_epoch=total_train // batch_size,
    epochs=epochs,
    validation_data=val_data_gen,
    validation_steps=total_val // batch_size
)
Epoch 1/15
15/15 [==============================] - 15s 1s/step - loss: 2.4046 - accuracy: 0.5091 - val_loss: 0.6950 - val_accuracy: 0.5000
Epoch 2/15
15/15 [==============================] - 15s 985ms/step - loss: 0.6930 - accuracy: 0.5073 - val_loss: 0.6914 - val_accuracy: 0.5000
Epoch 3/15
15/15 [==============================] - 14s 904ms/step - loss: 0.6933 - accuracy: 0.5027 - val_loss: 0.6903 - val_accuracy: 0.5011
Epoch 4/15
15/15 [==============================] - 13s 855ms/step - loss: 0.6888 - accuracy: 0.5524 - val_loss: 0.6801 - val_accuracy: 0.5971
Epoch 5/15
15/15 [==============================] - 13s 853ms/step - loss: 0.6818 - accuracy: 0.5668 - val_loss: 0.6723 - val_accuracy: 0.6172
Epoch 6/15
15/15 [==============================] - 13s 888ms/step - loss: 0.6660 - accuracy: 0.6000 - val_loss: 0.6432 - val_accuracy: 0.6228
Epoch 7/15
15/15 [==============================] - 13s 855ms/step - loss: 0.6732 - accuracy: 0.5668 - val_loss: 0.6463 - val_accuracy: 0.6406
Epoch 8/15
15/15 [==============================] - 13s 840ms/step - loss: 0.6563 - accuracy: 0.6332 - val_loss: 0.6443 - val_accuracy: 0.6473
Epoch 9/15
15/15 [==============================] - 13s 879ms/step - loss: 0.6550 - accuracy: 0.6323 - val_loss: 0.6353 - val_accuracy: 0.6049
Epoch 10/15
15/15 [==============================] - 13s 853ms/step - loss: 0.6468 - accuracy: 0.6330 - val_loss: 0.6246 - val_accuracy: 0.6752
Epoch 11/15
15/15 [==============================] - 13s 855ms/step - loss: 0.6295 - accuracy: 0.6554 - val_loss: 0.6055 - val_accuracy: 0.6618
Epoch 12/15
15/15 [==============================] - 13s 834ms/step - loss: 0.5977 - accuracy: 0.6859 - val_loss: 0.5966 - val_accuracy: 0.6696
Epoch 13/15
15/15 [==============================] - 14s 902ms/step - loss: 0.6286 - accuracy: 0.6375 - val_loss: 0.6298 - val_accuracy: 0.6429
Epoch 14/15
15/15 [==============================] - 13s 863ms/step - loss: 0.6231 - accuracy: 0.6480 - val_loss: 0.5883 - val_accuracy: 0.6752
Epoch 15/15
15/15 [==============================] - 13s 854ms/step - loss: 0.6053 - accuracy: 0.6752 - val_loss: 0.5820 - val_accuracy: 0.6763

モデルの可視化

学習後に新しいモデルを可視化すると、過学習が前回よりも大幅に少ないことがわかります。より多くのエポックでモデルを学習すると、精度はさらに向上するはずです。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs_range = range(epochs)

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

png