TensorFlow 2.0 : 上級 Tutorials : 画像 :- 画像分類 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 11/04/2019
* 本ページは、TensorFlow org サイトの TF 2.0 – Advanced Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
画像 :- 画像分類
このチュートリアルは画像から猫か犬かをどのように分類するかを示します。それは tf.keras.Sequential モデルを使用して画像分類器を構築して tf.keras.preprocessing.image.ImageDataGenerator を使用してデータをロードします。貴方は次の概念について何某かの実際の経験を得て直感を育てるでしょう :
- モデルで使用するためにディスク上のデータで効率的に作業するために tf.keras.preprocessing.image.ImageDataGenerator クラスを使用してデータ入力パイプラインを構築します。
- Overfitting — それをどのように識別して回避するか。
- データ増強とドロップアウト — データパイプラインと画像分類器モデルに組み合わせてコンピュータビジョン・タスクで overfitting に対抗するための主要なテクニックです。
このチュートリアルは基本的な機械学習ワークフローに従います :
- データを調べて理解する
- 入力パイプラインを構築する
- モデルを構築する
- モデルを訓練する
- モデルをテストする
- モデルを改良してプロセスを繰り返す
パッケージをインポートする
必要なパッケージをインポートすることから始めましょう。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 データセットのフィルターされたバージョンを使用します。
_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 [==============================] - 1s 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') # directory with our training cat pictures train_dogs_dir = os.path.join(train_dir, 'dogs') # directory with our training dog pictures validation_cats_dir = os.path.join(validation_dir, 'cats') # directory with our validation cat pictures validation_dogs_dir = os.path.join(validation_dir, 'dogs') # directory with our validation dog pictures
データを理解する
訓練と検証ディレクトリに幾つの猫と犬の画像があるか見てみましょう :
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
データ準備
ネットワークに供給する前に、画像を前処理された浮動小数点 tensor に適切にフォーマットします :
- ディスクから画像を読む。
- これらの画像のコンテンツをデコードしてそれを RGB コンテンツ毎に妥当なグリッド形式に変換します。
- それらを浮動小数点 tensor に変換します。
- tensor を 0 と 255 の間の値から 0 と 1 の間の値にリスケールします、ニューラルネットワークは小さい入値力を処理すること好むためです。
幸い、これら総てのタスクは tf.keras により提供される ImageDataGenerator クラスで処理できます。それはディスクから画像を読みそれらを妥当な tensor に前処理することができます。それはまたこれらの画像を tensor のバッチに変換する generator もセットアップします — ネットワークを訓練するときに役立ちます。
train_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our training data validation_image_generator = ImageDataGenerator(rescale=1./255) # Generator for our validation data
訓練と検証画像のために generator を定義した後、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.
訓練画像を可視化する
訓練 generator から画像のバッチを抽出して訓練画像を可視化します — これはこのサンプルで 32 画像です — それからそれらの 5 つを matplotlib でプロットします。
sample_training_images, _ = next(train_data_gen)
next 関数はデータセットからのバッチを返します。next 関数の戻り値は (x_train, y_train) の形式でここで x_train は訓練特徴で y_train はそのラベルです。訓練画像を可視化するだけのためにラベルは捨てます。
# This function will plot images in the form of a grid with 1 row and 5 columns where images are placed in each column. 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])
モデルを作成する
モデルは各々が max pool 層を伴う 3 つの畳み込みブロックから成ります。その上には 512 ユニットを持つ完全結合層があり、これは relu 活性化関数により活性化されます。モデルは sigmoid 活性化関数による二値分類に基づいてクラス確率を出力します。
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 optimizer と二値交差エントロピー損失関数を選択します。各訓練エポックのために訓練と検証精度を見るために、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 [==============================] - 9s 577ms/step - loss: 1.1625 - accuracy: 0.4834 - val_loss: 0.6920 - val_accuracy: 0.5056 Epoch 2/15 15/15 [==============================] - 8s 535ms/step - loss: 0.6906 - accuracy: 0.5491 - val_loss: 0.6831 - val_accuracy: 0.5882 Epoch 3/15 15/15 [==============================] - 8s 529ms/step - loss: 0.6605 - accuracy: 0.5999 - val_loss: 0.6566 - val_accuracy: 0.5915 Epoch 4/15 15/15 [==============================] - 8s 556ms/step - loss: 0.5781 - accuracy: 0.7078 - val_loss: 0.6292 - val_accuracy: 0.6473 Epoch 5/15 15/15 [==============================] - 7s 492ms/step - loss: 0.5303 - accuracy: 0.7390 - val_loss: 0.6236 - val_accuracy: 0.6775 Epoch 6/15 15/15 [==============================] - 7s 483ms/step - loss: 0.4817 - accuracy: 0.7655 - val_loss: 0.6050 - val_accuracy: 0.6942 Epoch 7/15 15/15 [==============================] - 7s 470ms/step - loss: 0.3981 - accuracy: 0.8255 - val_loss: 0.6463 - val_accuracy: 0.7054 Epoch 8/15 15/15 [==============================] - 7s 451ms/step - loss: 0.4122 - accuracy: 0.7975 - val_loss: 0.6424 - val_accuracy: 0.6763 Epoch 9/15 15/15 [==============================] - 6s 413ms/step - loss: 0.3481 - accuracy: 0.8432 - val_loss: 0.7087 - val_accuracy: 0.6920 Epoch 10/15 15/15 [==============================] - 6s 410ms/step - loss: 0.2979 - accuracy: 0.8686 - val_loss: 0.7242 - val_accuracy: 0.6886 Epoch 11/15 15/15 [==============================] - 6s 410ms/step - loss: 0.2737 - accuracy: 0.8859 - val_loss: 0.7637 - val_accuracy: 0.7087 Epoch 12/15 15/15 [==============================] - 6s 405ms/step - loss: 0.2363 - accuracy: 0.9001 - val_loss: 0.7757 - val_accuracy: 0.7009 Epoch 13/15 15/15 [==============================] - 6s 400ms/step - loss: 0.1879 - accuracy: 0.9338 - val_loss: 0.7952 - val_accuracy: 0.7176 Epoch 14/15 15/15 [==============================] - 6s 385ms/step - loss: 0.1438 - accuracy: 0.9523 - val_loss: 0.8334 - val_accuracy: 0.7176 Epoch 15/15 15/15 [==============================] - 6s 411ms/step - loss: 0.1224 - accuracy: 0.9625 - val_loss: 0.9019 - val_accuracy: 0.7154
訓練結果を可視化する
ネットワーク訓練後、今は結果を可視化します。
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()
プロットから見れるように、訓練精度と検証精度は大きな差で離れていてそしてモデルは検証セットでおよそ 70 % 精度だけを達成しています。
何が間違っているのか見てみましょう、そしてモデルの全体的なパフォーマンスを上げてみましょう。
Overfitting
上のプロットでは、訓練精度は時間に渡り線形に増加していきますが、検証精度は訓練プロセスでおよそ 70% で行き詰まります。また、訓練と検証精度間の精度の違いは顕著です — overfitting の兆候です。
小さい数の訓練サンプルがあるとき、モデルは時々ノイズや訓練サンプルの望まれていない詳細から学習します — それが新しいサンプル上でモデルのパフォーマンスにマイナスに影響する程度に。この減少は overfitting として知られています。それはモデルが新しいデータセット上で一般化することが上手くいかないことを意味します。
訓練プロセスで overfitting に対抗する複数の方法があります。このチュートリアルではデータ増強を使用してモデルに dropout を追加します。
データ増強
overfitting は一般に小さい数の訓練サンプルがあるときに発生します。この問題を修正する一つの方法はデータセットを増強することです、その結果それは十分な数の訓練サンプルを持ちます。データ増強は (リアルに見える画像を生成する) ランダム変換を使用してサンプルを増強することにより、既存の訓練サンプルからより多くの訓練データを生成するアプローチを取ります。目標はモデルが訓練の間に正確に同じ画像を決して二度は見ないことです。これはモデルにデータのより多くの様相をさらしてより良く一般化するために役立ちます。
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.
訓練サンプルから一つのサンプル画像を取りそれを 5 回繰り返すと、増強は同じ画像に 5 回適用されます。
augmented_images = [train_data_gen[0][0][0] for i in range(5)]
# Re-use the same custom plotting function defined and used # above to visualize the training images plotImages(augmented_images)
ランダムに画像を回転する
回転と呼ばれる異なる増強を見てみましょう、そして訓練サンプルに 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)
ズーム増強を適用する
画像を 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)
Put it all together
前の増強総てを適用します。ここでは、訓練画像にリスケール、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)
検証データ generator を作成する
一般に、データ増強は訓練サンプルに適用するだけです。このケースでは、検証画像をリスケールだけしてそれらを 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
overfitting を減じるもう一つのテクニックはネットワークに dropout を導入することです。それはネットワークの重みに小さい値だけを取るように強制する正則化の形式で、これは重み値の分布をより正則にしてネットワークは小さい訓練サンプル上で overfitting を減じることができます。dropout はこのチュートリアルで使用される正則化テクニックの一つです。
層に dropout を適用するとき、それは訓練プロセスの間に適用された層から出力ユニットの数をランダムにドロップアウトします ( ゼロに設定します)。dropout はその入力値として分数数字を取ります、0.1, 0.2, 0.4, etc. のような形式です。これは適用された層から出力ユニットの 10%, 20% or 40% をランダムにドロップアウトすることを意味します。
ある特定の層に 0.1 dropout を適用するとき、それは各訓練エポックで出力ユニットの 10% をランダムに止めます (= kill)。
この新しい dropout 特徴でネットワーク・アーキテクチャを作成してそれを異なる畳み込みと完全結合層に適用します。
Dropout で新しいネットワークを作成する
ここでは、最初と最後の max pool 層に dropout を適用します。dropout の適用は各訓練エポックの間ランダムにニューロンの 20% をゼロに設定します。これは訓練データセット上で overfitting を回避するのに役立ちます。
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') ])
モデルをコンパイルする
ネットワークに dropout を導入後、モデルをコンパイルして層の要約を見ます。
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 _________________________________________________________________
モデルを訓練する
成功的に訓練サンプルにデータ増強を導入してネットワークに dropout を追加した後、この新しいネットワークを訓練します :
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 995ms/step - loss: 1.6760 - accuracy: 0.5101 - val_loss: 0.6929 - val_accuracy: 0.5033 Epoch 2/15 15/15 [==============================] - 14s 953ms/step - loss: 0.6932 - accuracy: 0.4925 - val_loss: 0.6929 - val_accuracy: 0.5301 Epoch 3/15 15/15 [==============================] - 14s 908ms/step - loss: 0.6930 - accuracy: 0.4909 - val_loss: 0.6917 - val_accuracy: 0.5089 Epoch 4/15 15/15 [==============================] - 13s 864ms/step - loss: 0.6912 - accuracy: 0.5118 - val_loss: 0.6905 - val_accuracy: 0.5569 Epoch 5/15 15/15 [==============================] - 13s 874ms/step - loss: 0.6933 - accuracy: 0.5026 - val_loss: 0.6918 - val_accuracy: 0.5993 Epoch 6/15 15/15 [==============================] - 12s 830ms/step - loss: 0.6926 - accuracy: 0.5466 - val_loss: 0.6850 - val_accuracy: 0.5078 Epoch 7/15 15/15 [==============================] - 13s 849ms/step - loss: 0.6891 - accuracy: 0.5235 - val_loss: 0.6819 - val_accuracy: 0.6239 Epoch 8/15 15/15 [==============================] - 13s 877ms/step - loss: 0.6903 - accuracy: 0.5256 - val_loss: 0.6899 - val_accuracy: 0.5301 Epoch 9/15 15/15 [==============================] - 13s 877ms/step - loss: 0.6898 - accuracy: 0.5286 - val_loss: 0.6808 - val_accuracy: 0.6049 Epoch 10/15 15/15 [==============================] - 12s 831ms/step - loss: 0.6847 - accuracy: 0.5510 - val_loss: 0.6688 - val_accuracy: 0.6116 Epoch 11/15 15/15 [==============================] - 13s 872ms/step - loss: 0.6734 - accuracy: 0.5807 - val_loss: 0.6703 - val_accuracy: 0.5960 Epoch 12/15 15/15 [==============================] - 13s 848ms/step - loss: 0.6668 - accuracy: 0.5769 - val_loss: 0.6368 - val_accuracy: 0.6183 Epoch 13/15 15/15 [==============================] - 12s 826ms/step - loss: 0.6688 - accuracy: 0.5833 - val_loss: 0.6335 - val_accuracy: 0.6150 Epoch 14/15 15/15 [==============================] - 13s 882ms/step - loss: 0.6452 - accuracy: 0.6083 - val_loss: 0.6236 - val_accuracy: 0.6362 Epoch 15/15 15/15 [==============================] - 13s 853ms/step - loss: 0.6466 - accuracy: 0.5978 - val_loss: 0.6198 - val_accuracy: 0.6451
モデルを可視化する
訓練後のモデルを可視化すると、前よりも少ない overfitting があることがはっきりと見て取れます。精度はより多くの訓練の間モデルを訓練した後上がるはずです。
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()
以上