Хабрахабр

Погружение в свёрточные нейронные сети. Часть 5 / 1 — 9

Полный курс на русском языке можно найти по этой ссылке.
Оригинальный курс на английском доступен по этой ссылке.


Выход новых лекций запланирован каждые 2-3 дня.

  1. Интервью с Себастьяном Труном
  2. Введение
  3. Набор данных собак и кошек
  4. Изображения различного размера
  5. Цветные изображения. Часть 1
  6. Цветные изображения. Часть 2
  7. Операция свёртки на цветных изображениях
  8. Операция подвыборки по максимальному значению на цветных изображениях
  9. CoLab: кошки и собаки
  10. Softmax и sigmoid
  11. Проверка
  12. Расширение изображений
  13. Исключение
  14. CoLab: собаки и кошки. Повторение
  15. Другие техники для предотвращения переобучения
  16. Упражнения: классификация изображений цветов
  17. Решение: классификация изображений цветов
  18. Итоги

Эта тема нам очень интересна, особенно в практических частях текущего курса по работе с TensorFlow.
— Себастьян, сталкивались ли вы когда-то с переобучением (overfitting — over, fit)? — Итак, сегодня мы снова здесь, вместе с Себастьяном и говорить мы будем о переобучении. Нейронная сеть у которой малое количество весов не способна выучить достаточное количество примеров, подобная ситуация в машинном обучении называется искажением.
— Да.
— Нейронная сеть у которой очень много параметров может произвольным образом выбрать решение, которое вам не понравится, как раз из-за такого большого количества этих параметров. Если вы скажете, что не сталкивались, то я определённо скажу, что не могу вам верить!
— Итак, причиной переобучения является так называемый bias-variance trade-off (компромисс между значениями параметра смещения и их разбросом). Таким образом можно сформулировать простое правило: чем больше параметров присутствует в сети относительно размеров (количества) данных, тем больше вероятность получить случайное решение вместо нужного верного. Результат выбора решения нейронной сети зависит от вариантивности исходных данных. Сложная нейронная сеть может сообщить вам, что, например, все те у кого имена начинаются на Т — мужчины и, никогда не переобучиться. Например, вы задётесь вопросом "Кто в этой комнате мужчина, а кто — женщина?". Первое из них использует holdout-набор данных (небольшое количество из обучающей выборки для валидации точности работы модели). Существует два решения. Второе решение заключается в том, чтобы ввести в нейронную сеть ограничения. Вы можете взять данные, разделить их на две части — 90% на обучение, а 10% на тестирование и провести так называемую кросс-валидацию, где вы проверите точность работы модели на тех данных, которые нейронная сеть не видела — как только значение ошибки начнет расти после определенного цикла обучения,- пора останавливать обучение. Чем более ограничены веса, тем менее переобученной будет модель.
— Я правильно понимаю, что у нас могут быть наборы данных как для обучения, так и для тестирования и валидации, верно?
— Верно. Например, ограничить значения параметров смещений и весов, приводя их всё ближе и ближе к нулю. Если же вы показывали модели определённый набор данных много раз, то, безусловно, начнется процесс переобучения, что очень плохо для нас.
— Может быть вы вспомните наиболее интересные случаи, когда ваша модель переобучалась?
— Ах, да… был такой случай в далёкой молодости, когда я занимался разработкой нейронной сети для игры в шахматы. Если у вас есть набор данных для валидации, то у вас должен быть такой набор данных, к которому вы ещё никогда не притрагивались и не показывали его вашей нейронной сети. Что интересно было, так это то, что из шахматных данных на которых обучалась нейронная сеть, сеть быстро определила, что если эксперт делает ход королевой в центр шахматной доски, то существует 60% вероятность победы. Это было в 1993. Это было настолько глупым решением для любого игрока в шахматы, которое явно свидетельствовало о переобученности модели.
— Здорово! Что она начала делать — открывать "проход" пешкой и двигать королеву в центр шахматной доски. Как вы думаете, что является наиболее недооценённой стороной глубокого обучения?
— 90% вашей работы недооценены, потому что 90% вашей работы будет состоять в очистке данных.
— Здесь я с вами полностью согласна!
— Как показывает практика, любой набор данных содержит какой-то мусор. Итак, мы обсудили несколько техник относительно того, как можно улучшить наши модели. В реальности же 90% вашего времени будет занимать очистка данных.
— Отлично. Привести данные к нужному виду, сделать их консистентными очень сложно, это очень трудоёмкий процесс.
— Да, даже если ты работаешь с такими наборами данных как изображения или видео, где, казалось бы, вся информация уже там, внутри, всё равно есть необходимость производить предобработку изображений.
— Единственные люди для которых данные идеальны — это профессора, потому что у них есть возможность сделать вид в презентации в PowerPoint, что всё именно так и должно быть и всё идеально! Итак, давайте узнаем больше о переобучении и техниках, которые позволят нам усовершенствовать наши модели глубокого обучения.

И снова добро пожаловать на курс!
— На прошлом уроке мы разработали небольшую свёрточную нейронную сеть для классификации изображений элементов одежды в оттенках серого цвета из набора данных FASHION MNIST. — Привет! Однако в реальном мире нам предстоит работать с изображениями высокого разрешения и различных размеров. Мы убедились на практике в том, что наша небольшая нейронная сеть может классифицировать поступающие на вход изображения с достаточно высокой точностью. Поэтому мы начнём наш текущий урок с изучиния того, каким образом СНС работает с цветными изображениями.
— Позже, в этой же часты, вы построите свёрточную нейронную сеть, которая сможет классифицировать изображения кошек и собак. Одним из замечательных преимуществ СНС является тот факт, что они могут так же хорошо работать и с цветными изображениями. А в конце этого урока, в практической части, вы разработаете собственную свёрточную нейронную сеть для классификации изображений цветов. На пути к реализации свёрточной нейронной сети способной классифицировать изображения кошек и собак, мы так же научимся использовать различные техники для решения одной и самых частых проблем с нейронными сетями — переобучении. Давайте начинать!

До этого момента мы работали только с изображениями в оттенках серого цвета и размером 28х28 из набора данных FASHION MNIST.

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

Как мы уже упоминали в начале этого урока, на этом занятии мы будем разрабатывать свёрточную нейронную сеть, которая сможет классифицировать цветные изображения собак и кошек.

Каждое изображение в этом наборе данных обладает меткой 1 или 0, если на изображении, соответственно, собака или кошка. Для реализации задуманного мы воспользуемся изображениями кошек и собак из набора данных Microsoft Asirra.

Тренировка нашей свёрточной нейронной сети на этих 25 000 изображениях займёт очень много времени. Несмотря на то, что набор данных Microsoft Asirra содержит более 3 млн размеченных изображений кошек и собак, лишь 25 000 доступы публично. Именно поэтому мы будем использовать небольшое множество изображений для тренировки нашей свёрточной нейронной сети из доступных 25 000.

В тренировочном наборе данных 1 000 изображений содержит кошек, а другая 1 000 изображений — собак. Наше подмножество тренировочных изображений состоит из 2 000 шт и 1 000 шт изображений для валидации модели. О наборе данных для валидации мы поговорим немного позже в этой части урока.

Работая с данным набором данных мы столкнёмся с двумя основными трудностями — работа с изображениями разного размера и работа с цветными изображениями.

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

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

В качестве примера вы можете вспомнить из наших предыдущих частей использование input_shape параметра при создании Flatten-слоя:

Так как изображения в наборе данных Fashion MNIST были одного размера, то и результатирующий одномерный массив был одного размера и состоял из 784 элементов. Перед тем как передавать нейронной сети изображение элемента одежды мы его преобразовывали в 1D-массив фиксированного размера — 28х28 = 784 элемента (пикселя).

Однако, работая с изображениями различного размера (высота и ширина) и преобразовывая их в одномерные массивы мы получим массивы различных размеров.

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

Решая задачи классификации изображений мы всегда прибегаем к одному из вариантов унификации входных данных — приведение размеров изображений к единым значениям (resizing).

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

tf.keras.layers.Flatten(input_shape(150,150,1))

В результате мы получил одномерный массив состоящий из 150х150 = 22 500 значений (пикселей).

О них мы поговорим в следующей части. Следующей проблемой с которой нам предстоит столкнуться будет проблема цвета — цветные изображения.

Давайте освежим в памяти то, что мы уже знаем. Для того чтобы разобраться и понять, каким образом свёрточные нейронные сети работают с цветными изображениями нам стоит углубиться в то, как именно СНС работают вообще.

image

Пример выше — изображение в оттенках серого цвета и то, каким образом его интерпретирует компьютер в виде двумерного массива значений пикселей.

Пример ниже — изображение, на этот раз цветное и то, каким образом его интерпретирует компьютер в виде трёхмерного массива значений пикселей.

Высота и ширина 3D-массива будет определена высотой и шириной изображения, а глубина (depth) определяет количеством цветовых каналов изображения.

Большинство цветных изображений могут быть представлены тремя цветовыми каналами — красным (red), зеленым (green) и синим (blue).

Объединение этих трёх каналов в результате даёт цветное изображение. Изображения, которые состоят из красного, зеленого и синего каналов называются RGB-изображениями. В каждом из RGB-изображении, каждый канал представлен отдельным двумерных массивом пикселей.

Таким образом цветное изображение состоящее из 3 цветовых каналов будет иметь следующее представление: Так как количество каналов у нас равно трём, то и в результате у нас будут три двумерных массива.

Итак, так как наше изображение теперь будет состоять из 3 цветов, а значит будет представлять собой трёхмерный массив значений пикселей, то и наш код необходимо будет изменить соответствующим образом.

Если посмотреть на код, который мы использовали в нашем прошлом уроке, когда решали задачу классификации элементов одежды на изображениях, то можно заметить, что мы указывали размерность входных данных:

model = Sequential()
model.add(Conv2D(32, 3, padding='same', activation='relu', input_shape=(28,28,1)))

Изображения в наборе данных Fashion MNIST были размером 28х28 пикселей. Первые два параметра кортежа (28,28,1) являются значениями высоты и ширины изображения. В наборе данных Fashion MNIST изображения были только в оттенках серого — 1 цветовой канал. Последней параметр в кортеже (28,28,1) обозначает количество каналов цветов.

Теперь, когда задача стала чуточку сложнее, а наши изображения кошек и собак стали различного размера (но преобразуются к единому — 150х150 пикселей) и содержат 3 цветовых канала, то кортеж значений должен быть тоже другим:

model = Sequential()
model.add(Conv2D(16, 3, padding='same', activation='relu', input_shape=(150,150,3)))

В следующей части мы посмотрим, каким образом происходит вычисление свёртки при наличии трёх цветовых каналов в изображении.

Но каким образом выполнять операцию свёртки на цветных изображениях? В прошлых уроках мы научились выполнять операцию свёртки на изображениях в оттенках серого. Давайте начнём с повторения, каким образом выполняется операция свёртки на изображениях в оттенках серого.

Всё начинается с фильтра (ядра) определённого размера.

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

Стоит так же вспомнить, что при операции свёртки, дабы не терять информацию на границах изображения, мы можем применить выравнивание и дополнить края изображения нулями:

Теперь давайте разберёмся, каким образом мы можем выполнить операцию свёртки над цветными изображениями.

Так же, как и при преобразовании изображения в оттенках серого, начнем мы с выбора размера фильтра (ядра) определённого размера.

К каждому "слою" цветового канала мы так же будем применять операцию свёртки фильтром выбранного размера. Единственная разница сейчас будет в том, что теперь и сам фильтр будет трёхмерным, а значение параметра глубины (depth) будет равно значению количества цветовых каналов в изображении — 3 (в нашем случае — RGB). Давайте посмотрим, как это будет на примере.

Стоит обратить внимание на тот факт, что наш фильтр состоит из 3х двумерных фильтров. Представьте, что у нас есть RGB-изображение и мы хотим применить операцию свёртки следующим 3D-фильтром. Для простоты давайте представим, что наше RGB-изображение размером 5х5 пикселей.

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

Как и при выполнении операции свёртки над изображениями в оттенках серого, так же и с цветными изображениями — осуществим выравнивание и дополним изображение по краям нулями, чтобы исключить потерю информации на границах.

Теперь мы готовы к выполнению операции свёртки!

Единственная разница между выполняемыми операциями над изображениями в оттенках серого и цветными — операцию свёртки теперь необходимо выполнить 3 раза по каждому цветовому каналу. Механизм выполнения свёртки для цветных изображений будет аналогичным процессу, который мы выполняли с изображениями в оттенках серого.

Полученное новое значение фиксируем в той же позиции в новом изображении, в какой позиции находился текущий преобразуемый пиксель. Затем, после того как мы выполнили операцию свёртки над каждым цветовым каналом — складываем три полученных значения и прибавляем к ним 1 (стандартное значение используемое при выполнении операций подобного рода).

Подобную операцию преобразования (операцию свёртки) мы выполняем для каждого пикселя в нашем исходном изображении и по каждому цветовому каналу.

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

Как можно заметить, применение операции свёртки единственным 3D-фильтром даёт в результате единственное выходное значение.

Если мы будем использовать более одного 3D-фильтра, то результатом будет несколько выходных значений — каждое значение на результат работы одного фильтра. Однако, когда работа ведётся со свёрточными нейронными сетями, обычной практикой считается использование более одного 3D-фильтра.

В нашем примере выше, так как мы используем 3 фильтра, то результатирующее 3D-представление будет иметь глубину 3 — каждый слой будет соответствовать выходному значению преобразования одного фильтра над изображением со всеми его цветовыми каналами.

Если, например, вместо 3 фильтров мы решили бы использовать 16, то выходное 3D-представление содержало бы 16 глубинных слоёв.

В коде мы можем контролировать количество создаваемых фильтров передавая соответствующее значение для параметра filters:

tf.keras.layers.Conv2D(filters, kernel_size, ...)

Например, для создания 3 фильтров размером 3х3, как было в нашем примере выше, мы можем записать код следующим образом: Так же мы можем указывать размер фильтра через параметр kernel_size.

tf.keras.layers.Conv2D(3, (3,3), ...)

Запомните, что во время тренировки свёрточной нейронной сети, значения в 3D-фильтрах будут обновляться, чтобы минимизировать значение функции потерь.

Теперь, когда мы знаем как выполнять операцию свёртки на цветных изображениях, пора разобраться с тем, как к полученному результату применять операцию подвыборки по максимальному значению (тот самый max-pooling).

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

Для простоты давайте представим, что наше выходное представление выглядит таким образом:

Операция подвыборки по максимальному значению начинается с "установки" ядра размером 2х2 в левом верхнем углу каждого выходного представления (представления, которое было получено после применения операции свёртки). Как и ранее, мы будем использовать ядро размером 2х2 и шагом 2 для выполнения операции подвыборки по максимальному значению.

Например, в нашем первом выходном представлении в ядро размером 2х2 попали следующие значения — 1, 9, 5, 4. Теперь мы можем начать выполнять операцию подвыборки по максимальному значению. Подобная операция повторяется для каждого входного представления. Так как максимальное значение в этом ядре — 9, то именно она и отправляется в новое выходное представление.

В итоге мы должны получить следующий результат:

После выполнения операции подвыборки по максимальному значению в результате получаются 3 двумерных массива, каждый из которых размером в 2 раза меньше исходного входного представления.

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

Теперь давайте посмотрим, как же это будет работать в коде! Итак, это вся теория, которая нам понадобится для дальнейшей работы.

Оригинальный CoLab на английском языке доступен по этой ссылке.
CoLab на русском языке доступен по этой ссылке.

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

Идеи, которые будут затронуты в этой части:

Мы получим практический опыт разработки классификатора и разовьём интуитивное понимание следующих концепций:

  1. Построение модели потока данных (data input pipelines) с использованием tf.keras.preprocessing.image.ImageDataGenerator-класса (Каким образом эффективно работать с данными на диске взаимодействуя с моделью?)
  2. Переобучение — что это такое и как его определить?

Перед тем как мы начнем...

Подобное действие позволит избежать проблем с нехваткой памяти, если параллельно вы работали или работаете с несколькими редакторами. Перед тем как запускать код в редакторе, рекомедуем сбросить все настройки в Runtime -> Reset all в верхнем меню.

Давайте начнём с импорта нужных пакетов:

  • os — чтение файлов и структуры директорий;
  • numpy — для некоторых матричных операций вне TensorFlow;
  • matplotlib.pyplot — построение графиков и отображение изображений из тестового и валидационного набора данных.

from __future__ import absolute_import, division, print_function, unicode_literals import os
import matplotlib.pyplot as plt
import numpy as np

Импортируем TensorFlow:

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator

import logging
logger = tf.get_logger()
logger.setLevel(logging.ERROR)

Набор данных, который мы используем представляет собой отфильтрованную версию набора данных Собаки vs Кошки с сервиса Kaggle (в конце концов именно этот набор данных предоставляется Microsoft Research). Разработку нашего классификатора мы начинаем с загрузки набора данных.

В этом CoLab однако, мы воспользуемся классом tf.keras.preprocessing.image. В прошлом CoLab мы с вами использовали набор данных из самого TensorFlow Dataset модуля, который оказывается крайне удобным для работы и тестирования. Поэтому предварительно нам необходимо загрузить набор данных Собаки VS Кошки и разархивировать его. ImageDataGenerator для чтения данных с диска.

_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
zip_dir = tf.keras.utils.get_file('cats_and_dogs_filterted.zip', origin=_URL, extract=True)

Набор данных, который мы загрузили, имеет следующую структуру:

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

Чтобы получить полный список директорий можно воспользоваться следующей командой:

zip_dir_base = os.path.dirname(zip_dir)
!find $zip_dir_base -type d -print

В результате получим нечто подобное:

/root/.keras/datasets
/root/.keras/datasets/cats_and_dogs_filtered
/root/.keras/datasets/cats_and_dogs_filtered/train
/root/.keras/datasets/cats_and_dogs_filtered/train/dogs
/root/.keras/datasets/cats_and_dogs_filtered/train/cats
/root/.keras/datasets/cats_and_dogs_filtered/validation
/root/.keras/datasets/cats_and_dogs_filtered/validation/dogs
/root/.keras/datasets/cats_and_dogs_filtered/validation/cats

Теперь присвоим переменным корректные пути к директориям с наборами данных для тренировки и валидации:

base_dir = os.path.join(os.path.dirname(zip_dir), 'cats_and_dogs_filtered')
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, '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('Кошек в тестовом наборе данных: ', num_cats_tr)
print('Собак в тестовом наборе данных: ', num_dogs_tr) print('Кошек в валидационном наборе данных: ', num_cats_val)
print('Собак в валидационном наборе данных: ', num_dogs_val)
print('--')
print('Всего изображений в тренировочном наборе данных: ', total_train)
print('Всего изображений в валидационном наборе данных: ', total_val)

Вывод последнего блока будет следующим:

Кошек в тестовом наборе данных: 1000
Собак в тестовом наборе данных: 1000
Кошек в валидационном наборе данных: 500
Собак в валидационном наборе данных: 500
--
Всего изображений в тренировочном наборе данных: 2000
Всего изображений в валидационном наборе данных: 1000

Для удобства мы вынесем установку переменных, которые нам понадобятся для дальнейшей обработки данных и тренировки модели, в отдельное объявление:

BATCH_SIZE = 100 # количество тренировочных изображений для обработки перед обновлением параметров модели
IMG_SHAPE = 150 # размерность 150x150 к которой будет преведено входное изображение

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

  1. Прочитать изображения с диска
  2. Декодировать содержимое изображений и преобразовать в нужный формат с учетом RGB-профиля
  3. Преобразовать к тензорам со значениями с плавающей запятой
  4. Произвести нормализацию значений тензора из интервала от 0 до 255 к интервалу от 0 до 1, так как нейронные сети лучше работают с маленькими входными значениями.

ImageDataGenerator-класса. К счастью все эти операции могут быть выполнены с использованием tf.keras.preprocessing.image.

Всё это мы можем сделать с использованием нескольких строк кода:

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_SHAPE,IMG_SHAPE), 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, shuffle=False, target_size=(IMG_SHAPE,IMG_SHAPE), class_mode='binary')

Вывод:

Found 1000 images belonging to 2 classes.

Визуализируем изображения из тренировочного набора

Мы можем визуализировать изображения из тренировочного набора данных воспользовавшись matplotlib:

sample_training_images, _ = next(train_data_gen)

Один блок представляет собой кортеж из (множество изображений, множество меток). Функция next возвращает блок изображений из набора данных. В данный момент мы отбросим метки, так как они нам не нужны — нас интересуют сами изображения.

# данная функция отрисует изобраэения в сетке размером 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) plt.tight_layout() plt.show()

plotImages(sample_training_images[:5]) # отрисовываем изображения 0-4

Пример вывод (2 изображения вместо всех 5):

Описываем модель

Далее у нас идёт полносвязный слой с 512 нейронами и функцией активации relu. Модель состоит из 4 блоков свёртки после каждого из которых следует блок со слоем подвыборки. Модель выдаст распределение вероятностей по двум классам — собаки и кошки — используя softmax.

model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(IMG_SHAPE, IMG_SHAPE, 3)), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D(2, 2), tf.keras.layers.Flatten(), tf.keras.layers.Dense(512, activation='relu'), tf.keras.layers.Dense(2, activation='softmax')
])

Компилирование модели

В качестве функции потерь воспользуемся sparse_categorical_crossentropy. Как и ранее мы воспользуемся оптимизатором adam. Так же мы хотим на каждой обучающей итерации следить за точностью модели, поэтому передаём значение accuracy в параметр metrics:

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

Представление модели

Давайте взглянем на структуру нашей модели по уровням используя метод summary:

model.summary()

Вывод:

Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param # =================================================================
conv2d (Conv2D) (None, 148, 148, 32) 896 _________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 74, 74, 32) 0 _________________________________________________________________
conv2d_1 (Conv2D) (None, 72, 72, 64) 18496 _________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 36, 36, 64) 0 _________________________________________________________________
conv2d_2 (Conv2D) (None, 34, 34, 128) 73856 _________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 17, 17, 128) 0 _________________________________________________________________
conv2d_3 (Conv2D) (None, 15, 15, 128) 147584 _________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 7, 7, 128) 0 _________________________________________________________________
flatten (Flatten) (None, 6272) 0 _________________________________________________________________
dense (Dense) (None, 512) 3211776 _________________________________________________________________
dense_1 (Dense) (None, 2) 1026 =================================================================
Total params: 3,453,634
Trainable params: 3,453,634
Non-trainable params: 0

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

Настала пора тренировки модели!

Так как обучающие блоки будут поступать из генератора (ImageDataGenerator) мы воспользуемся методом fit_generator вместо ранее используемого метода fit:

EPOCHS = 100
history = model.fit_generator( train_data_gen, steps_per_epoch=int(np.ceil(total_train / float(BATCH_SIZE))), epochs=EPOCHS, validation_data=val_data_gen, validation_steps=int(np.ceil(total_val / float(BATCH_SIZE)))
)

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

Теперь мы визуализируем результаты тренировки нашей модели:

acc = history.history['acc']
val_acc = history.history['val_acc'] 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='Точность на обучении')
plt.plot(epochs_range, val_acc, label='Точность на валидации')
plt.legend(loc='lower right')
plt.title('Точность на обучающих и валидационных данных') plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Потери на обучении')
plt.plot(epochs_range, val_loss, label='Потери на валидации')
plt.legend(loc='upper right')
plt.title('Потери на обучающих и валидационных данных')
plt.savefig('./foo.png')
plt.show()

Вывод:

Как можно заметить на графиках, точность на тренировочном и валидационном наборах данных различаются на достаточно большое значение и наша модель достигла всего лишь 70% точности на валидационном наборе данных (зависит от количества обучающих итераций).

Как только кривые обучения и валидации начинают расходиться это значит, что модель начинает запоминать тренировочные данные и плохо отрабатывать на валидационном наборе данных. Это является очевидным свидетельством переобучения.

Продолжение следует… в новой публикации.

… и стандартные call-to-action — подписывайся, ставь плюс и делай share 🙂

YouTube
Telegram
ВКонтакте

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть