Классификация изображений

Смотреть на TensorFlow.org Запустить в Google Colab Смотреть источник на GitHub Скачать ноутбук

Этот обучающий пример показывает как распознать кошку или собаку по картинке. Для этого нужно построить классификатор изображений используя модель tf.keras.Sequential и загрузить данные, используя tf.keras.preprocessing.image.ImageDataGenerator. Ты получишь практический опыт и разовьешь интуицию для следующего:

  • Построение входного конвейера данных, с помощью tf.keras.preprocessing.image.ImageDataGenerator - класса для эффективной работы с данными на диске для использования с моделью.
  • Переобучение (Overfitting) - Как распознать и предотвратить.
  • Data augmentation and dropout — Ключевые техники борьбы с переобучением в задачах компьютерного зрения для включения в конвейер данных и модель классификатора изображений.

В этом обучающем примере соблюдается базовая последовательность машинного обучения:

  1. Оценить и понять данные
  2. Построить входной конвейер
  3. Построить модель
  4. Натренировать модель
  5. Протестировать модель
  6. Улучшить модель и повторить процесс

Импортирование пакетов

Начнем с импортирования необходимых пакетов. Пакет os используется для чтения файлов и структуры директорий, NumPy используется для преобразования питоновского списка в массив numpy и для выполнения необходимых операций с матрицами, matplotlib.pyplot используется для построения графиков для тренировочных и проверочных данных.

from __future__ import absolute_import, division, print_function, unicode_literals

Для построения нашей модели необходимо импортировать классы Tensorflow и Keras.

try:
  # %tensorflow_version only exists in Colab.
  %tensorflow_version 2.x
except Exception:
  pass
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

Загрузка данных

Начнем со скачивания датасета. В этом примере используется отфильтрованная версия Dogs vs Cats датасета из Kaggle. Скачайте архивную версию датасета и сохраните в директории "/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 [==============================] - 3s 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, так как нейронные сети предпочитают иметь дело с маленькими входными данными.

К счастью, все эти задачи могут быть выполнены с помощью класса ImageDataGenerator предоставленного tf.keras. Он может считывать изображения с диска и препроцессировать их в нужные тензоры. Он также настроит генераторы которые превратят эти изображения в batches тензоров (пакеты тензоров), что полезно при обучении сети.

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.

Визуализация тренировочных изображений

Визуализируйте тренировочные изображения, извлекая batch изображений из тренировочного генератора (в данном примере это 32 изображения), а затем постройте графики для пяти из них с помощью matplotlib.

sample_training_images, _ = next(train_data_gen)

Функция next возвращает batch из датасета. Полученное значение функции next в формате (x_train, y_train) где x_train - тренировочные признаки, а y_train - их метки. Уберите метки, чтобы визуализировать только тренировочные изображения.

# Эта функция построит график изображения в табличном виде с одной строкой и 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

Создание модели

Модель состоит из трех сверточных блоков со слоем макс-пулинга в каждом из них. Поверх них полносвязный слой с 512 элементами и функцией активации relu.

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 и функция потерь двоичная кросс-энтропия. Чтобы оценить тренировочную и проверочные точности для каждой тренировочной эпохи нужно передать аргумент 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
_________________________________________________________________

Обучение модели

Используйте метод fit_generator класса ImageDataGenerator для обучения сети.

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
)
WARNING:tensorflow:From <ipython-input-1-01c6f78f4d4f>:6: Model.fit_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
Please use Model.fit, which supports generators.
Epoch 1/15
15/15 [==============================] - 7s 474ms/step - loss: 0.8048 - accuracy: 0.5053 - val_loss: 0.6918 - val_accuracy: 0.5112
Epoch 2/15
15/15 [==============================] - 7s 454ms/step - loss: 0.6929 - accuracy: 0.5064 - val_loss: 0.6905 - val_accuracy: 0.5078
Epoch 3/15
15/15 [==============================] - 7s 451ms/step - loss: 0.6881 - accuracy: 0.5192 - val_loss: 0.6827 - val_accuracy: 0.5513
Epoch 4/15
15/15 [==============================] - 7s 450ms/step - loss: 0.6729 - accuracy: 0.5817 - val_loss: 0.6583 - val_accuracy: 0.6250
Epoch 5/15
15/15 [==============================] - 7s 454ms/step - loss: 0.6370 - accuracy: 0.6490 - val_loss: 0.6138 - val_accuracy: 0.6752
Epoch 6/15
15/15 [==============================] - 7s 451ms/step - loss: 0.6060 - accuracy: 0.6693 - val_loss: 0.6155 - val_accuracy: 0.6629
Epoch 7/15
15/15 [==============================] - 7s 446ms/step - loss: 0.5585 - accuracy: 0.7292 - val_loss: 0.5798 - val_accuracy: 0.7076
Epoch 8/15
15/15 [==============================] - 7s 443ms/step - loss: 0.5063 - accuracy: 0.7505 - val_loss: 0.5848 - val_accuracy: 0.7199
Epoch 9/15
15/15 [==============================] - 7s 445ms/step - loss: 0.4728 - accuracy: 0.7745 - val_loss: 0.5966 - val_accuracy: 0.7076
Epoch 10/15
15/15 [==============================] - 7s 464ms/step - loss: 0.4603 - accuracy: 0.7693 - val_loss: 0.5810 - val_accuracy: 0.7254
Epoch 11/15
15/15 [==============================] - 7s 459ms/step - loss: 0.4172 - accuracy: 0.8104 - val_loss: 0.5857 - val_accuracy: 0.7121
Epoch 12/15
15/15 [==============================] - 7s 450ms/step - loss: 0.3774 - accuracy: 0.8355 - val_loss: 0.5701 - val_accuracy: 0.7154
Epoch 13/15
15/15 [==============================] - 7s 449ms/step - loss: 0.3230 - accuracy: 0.8707 - val_loss: 0.6207 - val_accuracy: 0.7165
Epoch 14/15
15/15 [==============================] - 7s 448ms/step - loss: 0.2944 - accuracy: 0.8761 - val_loss: 0.6163 - val_accuracy: 0.7210
Epoch 15/15
15/15 [==============================] - 7s 450ms/step - loss: 0.2419 - accuracy: 0.9065 - val_loss: 0.6705 - val_accuracy: 0.7321

Визуализация результатов обучения

Теперь визуализируйте результаты после обучения сети.

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)

Обычно переобучение случается, когда число тренировочных данных мало. Один из способов решить эту проблему это увеличить датасет так, чтобы число тренировочных данных стало достаточным. В процессе дополнения данных используется метод получения большего количества обучающих данных из существующих обучающих выборок путем увеличения выборок с использованием случайных преобразований, которые дают правдоподобные изображения. Цель состоит в том, чтобы модель никогда не увидела одну и ту же картинку дважды во время тренировки. Это помогает раскрывать модель для большего количества аспектов данных и лучше обобщать.

В tf.keras это можно реализовать, используя класс ImageDataGenerator. Передайте различные преобразования в датасет, и он позаботится о его применении в процессе обучения.

Дополнение и визуализация данных

Начнем с применения случайного горизонтального отражения к набору данных и посмотрим, как выглядят отдельные изображения после преобразования.

Применение горизонтального отображения

Передайте horizontal_flip в качестве аргумента классу ImageDataGenerator и установите для него значение 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.

Возьмите одно пробное изображение из тренировочных примеров и повторите его пять раз, чтобы дополнение было применено к одному и тому же изображению пять раз.

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%.

# zoom_range - диапазон увеличения - от 0 до 1 где 1 = 100%.
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.

Визуализируем, как одно изображение будет выглядеть пять раз при случайной передаче этих дополнений к датасету.

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

png

Создание генератора для проверочных данных

Как правило, дополнение данных применяется только к тренировочным данным. В данном случае измените только масштаб проверочных изображений и преобразуйте их в batches, используя 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

Другой способ уменьшить переобучение - это ввести dropout в сеть. Это форма регуляризации, которая заставляет веса в сети принимать только малые значения, что делает распределение значений веса более регулярным, и сеть может уменьшить переобучение на небольших тренировочных примерах. Dropout является одним из методов регуляризации, используемых в этом обучающем примере.

Когда вы применяете dropout к слою, он случайным образом выбрасывает (обнуляет) часть выходов из слоя в процессе обучения. Dropout принимает дробное число в качестве входного значения в форме, например, 0,1, 0,2, 0,4 и т.д. Это означает случайное выбрасывание 10%, 20% или 40% выходных единиц случайным образом из примененного слоя.

Применение 0,1 dropout к определенному слою, случайным образом убивает 10% выходных единиц во время каждой эпохи тренировки.

Создайте сетевую архитектуру с помощью этой новой функции dropout и примените ее к различным конволюциям и полносвязным слоям.

Создание новой сети с Dropouts

Здесь вы применяете dropout к первому и последнему слоям макс-пулинга. Применение dropout случайным образом обнулит 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')
])

Компиляция модели

После внесения dropouts в сеть, скомпилируйте модель и просмотрите сводку слоев.

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
_________________________________________________________________

Тренировка модели

После успешного добавления дополнений данных в обучающие примеры и добавления dropouts в сеть, натренируем эту новую сеть:

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 [==============================] - 14s 918ms/step - loss: 1.3608 - accuracy: 0.4947 - val_loss: 0.6932 - val_accuracy: 0.4967
Epoch 2/15
15/15 [==============================] - 13s 892ms/step - loss: 0.6931 - accuracy: 0.5064 - val_loss: 0.6931 - val_accuracy: 0.5056
Epoch 3/15
15/15 [==============================] - 13s 891ms/step - loss: 0.6929 - accuracy: 0.5230 - val_loss: 0.6925 - val_accuracy: 0.5402
Epoch 4/15
15/15 [==============================] - 13s 886ms/step - loss: 0.6929 - accuracy: 0.5032 - val_loss: 0.6930 - val_accuracy: 0.5078
Epoch 5/15
15/15 [==============================] - 13s 895ms/step - loss: 0.6930 - accuracy: 0.5021 - val_loss: 0.6915 - val_accuracy: 0.5904
Epoch 6/15
15/15 [==============================] - 13s 888ms/step - loss: 0.6909 - accuracy: 0.5246 - val_loss: 0.6838 - val_accuracy: 0.5926
Epoch 7/15
15/15 [==============================] - 13s 888ms/step - loss: 0.6927 - accuracy: 0.5299 - val_loss: 0.6932 - val_accuracy: 0.5011
Epoch 8/15
15/15 [==============================] - 13s 894ms/step - loss: 0.6928 - accuracy: 0.5198 - val_loss: 0.6907 - val_accuracy: 0.5480
Epoch 9/15
15/15 [==============================] - 13s 889ms/step - loss: 0.6910 - accuracy: 0.5139 - val_loss: 0.6801 - val_accuracy: 0.5815
Epoch 10/15
15/15 [==============================] - 13s 886ms/step - loss: 0.6852 - accuracy: 0.5673 - val_loss: 0.6616 - val_accuracy: 0.5904
Epoch 11/15
15/15 [==============================] - 13s 880ms/step - loss: 0.6806 - accuracy: 0.5684 - val_loss: 0.6690 - val_accuracy: 0.6027
Epoch 12/15
15/15 [==============================] - 13s 888ms/step - loss: 0.6670 - accuracy: 0.5897 - val_loss: 0.6387 - val_accuracy: 0.6317
Epoch 13/15
15/15 [==============================] - 14s 907ms/step - loss: 0.6538 - accuracy: 0.5935 - val_loss: 0.6374 - val_accuracy: 0.6083
Epoch 14/15
15/15 [==============================] - 13s 890ms/step - loss: 0.6500 - accuracy: 0.6202 - val_loss: 0.6275 - val_accuracy: 0.6440
Epoch 15/15
15/15 [==============================] - 13s 885ms/step - loss: 0.6388 - accuracy: 0.6154 - val_loss: 0.6217 - val_accuracy: 0.6484

Визуализация модели

Визуализируя новую модель после тренировки, вы можете видеть, что там значительно меньше переобучения, чем раньше. Точность должна повыситься после обучения модели за большее количество эпох.

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