Введение
Одним из наиболее распространенных применений TensorFlow и Keras является распознавание и классификация изображений. Если вы хотите узнать, как использовать Keras для классификации или распознавания изображений, эта статья научит вас, как это делать.
Определения
Если вы не понимаете основных концепций распознавания изображений, вам будет сложно полностью понять основную часть этой статьи. Поэтому, прежде чем мы продолжим, давайте определимся с терминологией.
TensorFlow / Keras
TensorFlow - это библиотека с открытым исходным кодом, созданная для Python командой Google Brain. TensorFlow компилирует множество различных алгоритмов и моделей, позволяя пользователю реализовать глубокие нейронные сети для использования в таких задачах, как распознавание и классификация изображений, а также обработка естественного языка. TensorFlow - это мощный фреймворк, который функционирует путем реализации ряда узлов обработки, каждый из которых представляет математическую операцию, а весь ряд узлов называется «графом».
Говоря о Keras, это высокоуровневый API (интерфейс прикладного программирования), который может использовать функции TensorFlow (а также другие библиотеки ML, такие, как Theano). Keras был разработан с удобством и модульностью в качестве руководящих принципов. С практической точки зрения Keras позволяет реализовать множество мощных, но зачастую сложных функций TensorFlow максимально просто, к тому же он настроен для работы с Python без каких-либо серьезных изменений или настроек.
Распознавание изображений (классификация)
Распознавание изображения относится к задаче ввода изображения в нейронную сеть и присвоения какой-либо метки для этого изображения. Метка, которую выводит сеть, будет соответствовать заранее определенному классу. Может быть присвоено как сразу несколько классов, так и только один. Если существует всего только один класс, обычно применяется термин «распознавание», тогда как задача распознавания нескольких классов часто называется «классификацией».
Подмножество классификаций изображений - является уже определением объектов, когда определенные экземпляры объектов идентифицируются как принадлежащие к определенному классу, например, животные, автомобили или люди.
Ярким примером такой классификации является решение самой распространённой капчи — ReCaptcha v2 от Google, где из набора картинок необходимо выбрать только те, которые принадлежат к указанному в описании классу.
Функция извлечения
Чтобы выполнить распознавание или классификацию изображений, нейронная сеть должна выполнить извлечение признаков. Признаки - это элементы данных, которые представляют максимальный интерес и которые будут передаваться по нейросети. В конкретном случае распознавания изображений такими признаками являются группы пикселей, такие как линии и точки, которые сеть будет анализировать на наличие паттерна.
Распознавание признаков (или извлечение признаков) - это процесс извлечения соответствующих признаков из входного изображения, чтобы их можно было проанализировать. Многие изображения содержат аннотации или метаданные, которые помогают нейросети находить соответствующие признаки.
Как нейронные сети учатся распознавать изображения
Понимание о том, как нейронная сеть распознает изображения, поможет вам при реализации модели нейронной сети, поэтому давайте кратко рассмотрим процесс распознавания изображений в следующих разделах.
Извлечение признаков при помощи фильтров
Первый слой нейронной сети принимает все пиксели в изображении. После того, как все данные введены в сеть, к изображению применяются различные фильтры, которые формируют понимание различных частей изображения. Это извлечение признаков, которое создает «карты признаков».
Этот процесс извлечения признаков из изображения выполняется с помощью «сверточного слоя», и свертка просто формирует представление части изображения. Именно из этой концепции свертки мы получаем термин “Сверточная нейронная сеть” (Convolutional Neural Network, CNN) — тип нейронной сети, наиболее часто используемый в классификации и распознавании изображений.
Если вы хотите визуализировать, как именно работает создание карт признаков, представьте себе процесс поднесения фонарика к изображению в темной комнате. Когда вы скользите лучом по картинке, вы узнаёте об особенностях изображения. Фильтр - это то, что сеть использует для формирования представления об изображении, и в этой метафоре свет от фонарика является фильтром.
Ширина луча вашего фонарика определяет размер фрагмента изображения, который вы просматриваете за один раз, и нейронные сети имеют аналогичный параметр — размер фильтра. Размер фильтра влияет на то, сколько пикселей проверяется за один раз. Общий размер фильтра, используемого в CNN, равен 3, и он охватывает как высоту, так и ширину, поэтому фильтр проверяет область пикселей 3 x 3.
В то время как размер фильтра покрывает высоту и ширину фильтра, глубина фильтра также должна быть указана.
Но как 2D изображение может иметь глубину?
Дело в том, что цифровые изображения отображаются в виде высоты, ширины и некоторого значения RGB, которое определяет цвет пикселя, поэтому отслеживаемая «глубина» - это количество цветовых каналов, которые имеет изображение. Изображения в градациях серого (не цветные) имеют только 1 цветной канал, в то время как цветные изображения имеют глубину в 3 канала.
Все это означает, что для фильтра размером в 3, примененного к полноцветному изображению, итоговые размеры этого фильтра будут 3 x 3 x 3. Для каждого пикселя, охватываемого этим фильтром, сеть умножает значения фильтра на значения самих пикселей, чтобы получить числовое представление этого пикселя. Затем этот процесс выполняется для всего изображения, чтобы получить полное представление. Фильтр перемещается по остальной части изображения в соответствии с параметром, называемым «шаг», который определяет, на сколько пикселей должен быть перемещен фильтр после того, как он вычислит значение в своей текущей позиции. Обычный размер шага для CNN - 2.
Конечным результатом всех этих расчетов является карта признаков. Этот процесс обычно выполняется с несколькими фильтрами, которые помогают сохранить сложность изображения.
Функции активации
После того, как карта признаков изображения была создана, значения, представляющие изображение, передаются через функцию активации или слой активации. Функция активации принимает эти значения, которые благодаря сверточному слою находятся в линейной форме (то есть просто список чисел) и увеличивает их нелинейность, поскольку сами изображения являются нелинейными.
Типичной функцией активации, используемой для достижения этой цели, является выпрямленная линейная единица (ReLU), хотя есть и некоторые другие функции активации, которые также иногда используются (вы можете прочитать о них здесь .
Объединение слоёв
После активации данные отправляются через объединяющий слой. Объединение «упрощает» изображение: берёт информацию, которая представляет изображение, и сжимает её. Процесс объединения в пул делает сеть более гибкой и способной лучше распознавать объекты и изображения на основе соответствующих функций.
Когда мы смотрим на изображение, нас, как правило, волнует не вся информация (например, что на заднем плане изображения), а только признаки, которые нас интересуют — люди, животные и т. д.
Аналогично, объединяющий слой в CNN избавится от ненужных частей изображения, оставив только те части, которые он считает релевантными, в зависимости от заданного размера объединяющего слоя.
Поскольку сеть должна принимать решения относительно наиболее важных частей изображения, расчёт идёт на то, что она изучит только те части изображения, которые действительно представляют суть рассматриваемого объекта. Это помогает предотвратить “переобучение” — когда сеть слишком хорошо изучает все аспекты учебного примера и уже не может обобщать новые данные, поскольку учитывает нерелевантные отличия.
Существуют различные способы объединения значений, но чаще всего используется максимальное объединение. Максимальное объединение подразумевает взятие максимального значения среди пикселей в пределах одного фильтра (в пределах одного фрагмента изображения). Это отсеивает 3/4 информации, при условии использования фильтра размером 2 x 2.
Максимальные значения пикселей используются для того, чтобы учесть возможные искажения изображения, а количество параметров (размер изображения) уменьшены, чтобы контролировать переобучение. Существуют и другие принципы объединения, такие как среднее или суммарное объединение, но они используются не так часто, поскольку максимальное объединение даёт большую точность.
Сжатие
Последние слои нашей CNN — плотно связанные слои — требуют, чтобы данные были представлены в форме вектора для дальнейшей обработки. По этой причине данные необходимо «свести воедино». Для этого значения сжимаются в длинный вектор или столбец последовательно упорядоченных чисел.
Полностью связанный слой
Конечные слои CNN представляют собой плотно связанные слои или искусственную нейронную сеть (Artificial neural networks (ANN)). Основной функцией ANN является анализ входных признаков и объединение их в различные атрибуты, которые помогут в классификации. Эти слои образуют наборы нейронов, которые представляют различные части рассматриваемого объекта, а набор нейронов может представлять собой, например, висячие уши собаки или красноту яблока. Когда достаточное количество этих нейронов активируется в ответ на входное изображение, то оно будет классифицировано как объект.
Ошибка или разница между рассчитанными значениями и ожидаемым значением в обучающем наборе рассчитывается с помощью ANN. Затем сеть подвергается методу обратного распространения ошибки, где рассчитывается влияние данного нейрона на нейрон в следующем слое и затем его влияние (вес) корректируется. Это сделано для оптимизации производительности модели. Этот процесс повторяется снова и снова: так сеть обучается на данных и изучает связи между входными признаками и выходными классами.
Нейроны в средних полностью связанных слоях будут выводить двоичные значения, относящиеся к возможным классам. Если у вас есть четыре разных класса (скажем, собака, машина, дом и человек), нейрон будет иметь значение «1» для класса, который, как он считает, представляет изображение, и значение «0» для других классов.
Конечный полностью связанный слой, получив выходные данные предыдущего слоя, присваивает вероятность каждому из классов в пределах единицы (в совокупности). Если категории «собака» присвоено значение 0,75 — это означает 75% вероятность того, что изображение является собакой.
Рабочий процесс машинного обучения
Прежде чем мы перейдем к примеру обучения классификатора изображений, давайте уделим немного времени пониманию рабочего процесса или “конвейера” машинного обучения. Процесс обучения модели нейронной сети является довольно стандартным и может быть поделен на четыре различных этапа.
Классификатор изображений теперь обучен и изображения могут быть переданы в CNN, которая теперь выведет предположение о содержании этого изображения.
Подготовка данных
Во-первых, вам нужно будет собрать свои данные и оформить их так, чтобы нейросеть смола обучаться на них. Это включает в себя сбор изображений и их маркировку. Даже если вы скачали набор данных, подготовленный кем-то другим, то скорее всего вам всё-равно потребуется предварительная обработка или подготовка, прежде чем вы сможете использовать его для обучения. Подготовка данных - это искусство само по себе, связанное с решением таких проблем, как пропущенные значения, поврежденные данные, данные в неправильном формате, неправильные метки и т. д.
В этой статье мы будем использовать предварительно обработанный набор данных.
Создание модели
Создание модели нейронной сети включает выбор различных параметров и гиперпараметров. Вы должны принять решение о количестве слоев, используемых в вашей модели, о том, каким будет размер входных и выходных слоев, какие функции активации вы будете использовать, будете ли вы использовать исключение (Dropout) и т. д.
Понимание того, какие параметры и гиперпараметры стоит использовать, придет со временем (изучать придётся много), но существуют некоторые базовые методы, которые вы можете использовать на старте, и мы рассмотрим некоторые из них в нашем примере.
Обучение модели
После того, как ваша модель создана, вам просто остаётся создать экземпляр модели и подогнать его к своим данным для обучения. Наибольшее внимание при обучении модели уделяется количеству требуемого для обучения времени. Вы можете указать продолжительность обучения сети, задав количество эпох обучения. Чем дольше вы тренируете модель, тем выше ее эффективность, но если использовать слишком много эпох обучения - вы рискуете переобучить модель.
Выбор количества эпох для обучения - это то, что вы научитесь определять со временем, и, как правило, следует всегда сохранять веса нейросети между тренировочными сессиями, чтобы вам не нужно было начинать сначала после достижения определенного прогресса в обучении.
Оценка модели
Существует несколько шагов для оценки модели. Первым шагом является сравнение производительности модели с набором проверочных данных: тех данных, на которых модель не была обучена. Таким образом, вы проверите работу модели с этим новым набором данных и проанализируете её эффективность с помощью различных показателей.
Существуют различные метрики для определения производительности модели нейронной сети, но наиболее распространенной является «точность», то есть количество правильно классифицированных изображений, делённое на общее количество изображений в вашем наборе данных.
После того, как вы увидите точность модели в проверочном наборе данных, вы, вероятно, снова вернетесь и до-обучите сеть, используя слегка подправленные параметры, поскольку вряд ли будете удовлетворены эффективностью своей сети при первой тренировке. Вы будете продолжать настраивать параметры своей сети, повторно обучать ее и измерять эффективность, пока не будете удовлетворены точностью сети.
Наконец, вы проверите эффективность сети на тестовом наборе. Это еще один набор данных, который ваша модель никогда не видела раньше.
Возможно, вам интересно: зачем нужен ещё один тестовый набор данных? Ведь вы уже получили представление о точности вашей модели, разве не это было целью “проверочного набора”?
Всё дело в том, что все изменения параметров, которые вы производили, донастраивая сеть при работе с “проверочным набором данных” в сочетании с многократным повторным тестированием этого набора — могли привести к тому, что ваша сеть изучила некоторые особенности набора, но при этом она не будет так же хорошо обобщать данные вне выборки. Именно поэтому следует предоставить сети абсолютно новые тестовые данные.
Цель тестового набора - проверить наличие проблем, таких как переобучение, чтобы быть более уверенными в том, что ваша модель действительно пригодна для работы в реальном мире.
Распознавание изображений с CNN
Мы уже многое рассмотрели и если вся эта информация была, возможно, немного неясной, то объединение вышеописанных концепций в выборочном классификаторе, обученном на наборе данных, должно окончательно все прояснить Итак, давайте рассмотрим полный пример распознавания изображений с использованием Keras — от загрузки данных до оценки эффективности модели.
Для начала нам понадобится набор данных для обучения. В этом примере мы будем использовать известный набор данных CIFAR-10. CIFAR-10 - это большой набор данных, содержащий более 60000 изображений, представляющих 10 различных классов объектов, таких как кошки, самолеты и автомобили.
Изображения являются полноцветными RGB, но они достаточно малы, всего 32 x 32. Отличительной особенностью набора данных CIFAR-10 является то, что он поставляется в комплекте с Keras, поэтому загрузить набор данных очень просто, а сами изображения нуждаются лишь в минимальной предварительной обработке.
Первое, что мы должны сделать, это импортировать необходимые библиотеки. Вы ещё увидите, как именно этот импорт происходит по ходу дела, а пока же просто имейте в виду, что мы будем использовать Numpy и различные модули, связанные с Keras:
import numpy from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten, BatchNormalization, Activation from keras.layers.convolutional import Conv2D, MaxPooling2D from keras.constraints import maxnorm from keras.utils import np_utils
Мы собираемся использовать случайный SEED (симметричный блочный криптоалгоритм на основе Сети Фейстеля), чтобы результаты, полученные в этой статье, могли быть воспроизведены вами, поэтому нам нужен numpy:
# Set random seed for purposes of reproducibility seed = 21
Подготовка данных
Теперь нам нужно осуществить еще один импорт: сам набор данных.
from keras.datasets import cifar10
Теперь давайте загрузим набор данных. Мы можем сделать это просто указав, в какие переменные мы хотим загрузить данные, а затем использовать функцию load_data():
# loading in the data (X_train, y_train), (X_test, y_test) = cifar10.load_data()
В большинстве случаев вам потребуется выполнить некоторую предварительную обработку ваших данных, чтобы подготовить их к использованию, но поскольку мы используем уже готовый и упакованный набор данных, то такая обработка сведена к минимуму. Одним из действий, которые мы хотим сделать, будет нормализация входных данных.
Если значения входных данных находятся в слишком широком диапазоне, это может отрицательно повлиять на работу сети. В нашем случае входными значениями являются пиксели в изображении, которые имеют значение от 0 до 255.
Таким образом, чтобы нормализовать данные, мы можем просто разделить значения изображения на 255. Для этого нам сначала нужно перевести данные в формат с плавающей запятой, поскольку в настоящее время они являются целыми числами. Мы можем сделать это, используя Numpy команду astype(), а затем объявить желаемый тип данных:
# normalize the inputs from 0-255 to between 0 and 1 by dividing by 255 X_train = X_train.astype('float32') X_test = X_test.astype('float32') X_train = X_train / 255.0 X_test = X_test / 255.0
Следующая вещь, которую нам нужно сделать, чтобы подготовить данные для сети, - перевести их в унитарный код. Не будем вдаваться в подробности унитарного кодирования, но знайте, что изображения не могут использоваться нейросетью в том виде, в каком они есть — их нужно сначала кодировать, а лучше всего при проведении двоичной классификации использовать именно унитарное кодирование.
Мы успешно применяем здесь двоичную классификацию, потому что изображение либо принадлежит одному определённому классу, либо нет: оно не может быть где-то посередине. Для унитарного кодирования используется команда Numpy to_categorical(). Вот почему мы импортировали функцию np_utils из Keras, так как она содержит to_categorical().
Нам также нужно задать количество классов в наборе данных, чтобы мы поняли, до скольких нейронов сжимать конечный слой:
# one hot encode outputs y_train = np_utils.to_categorical(y_train) y_test = np_utils.to_categorical(y_test) class_num = y_test.shape[1]
Проектирование модели
Мы достигли стадии проектирования модели CNN. Первое, что нужно сделать, это определить формат, который мы хотели бы использовать для модели. У Keras есть несколько различных форматов (планов) для построения моделей, но наиболее часто используется Sequential — поэтому мы импортировали его из Keras.
Создание модели
model = Sequential()
Первый слой нашей модели - это сверточный слой. Он будет принимать входные данные и пропускать их через сверточные фильтры.
При реализации этого в Keras, мы должны указать количество каналов (фильтров), которое нам нужно (а это 32), размер фильтра (3 x 3 в нашем случае), форму входа (при создании первого слоя), функцию активации и отступы.
Как уже упоминалось, relu является наиболее распространенной функцией активации, а отступы мы определим через padding = 'same', то есть, мы не меняем размер изображения:
model.add(Conv2D(32, (3, 3), input_shape=X_train.shape[1:], padding='same')) model.add(Activation('relu'))
Примечание: Вы также можете объединить в одну строку нужные команды, например так:
model.add(Conv2D(32, (3, 3), input_shape=(3, 32, 32), activation='relu', padding='same'))
Теперь мы создадим исключающий слой для предотвращения переобучения, который случайным образом устраняет соединения между слоями (0,2 означает, что он отбрасывает 20% существующих соединений):
model.add(Dropout(0.2))
Также мы можем выполнить пакетную нормализацию. Пакетная нормализация нормализует входные данные, поступающие в следующий слой, гарантируя, что сеть всегда создает функции активации с тем же распределением, которое нам нужно:
model.add(BatchNormalization())
Теперь следует еще один сверточный слой, но размер фильтра увеличивается, так что сеть уже может изучать более сложные представления:
model.add(Conv2D(64, (3, 3), padding='same')) model.add(Activation('relu'))
А вот и объединяющий слой, который, как обсуждалось ранее, помогает сделать классификатор изображений более корректным, чтобы он мог изучать релевантные шаблоны. Также опишем исключение (Dropout) и пакетную нормализацию:
model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.2)) model.add(BatchNormalization())
Это основа рабочего процесса в первой части реализации CNN: свертка, активация, исключение, объединение. Теперь вы понимаете, зачем мы импортировали Dropout, BatchNormalization, Activation, Conv2d и MaxPooling2d.
Вы можете варьировать количество сверточных слоев по своему вкусу, но каждый из них увеличивает вычислительные затраты. Обратите внимание, что при добавлении сверточных слоев вы обычно увеличиваете и количество фильтров, чтобы модель могла выучить более сложные представления. Если числа, выбранные для этих слоев, кажутся несколько произвольными, то просто знайте, что рекомендуется увеличивать фильтры постепенно, устанавливая значение 2 в степени (2^n), что может дать небольшое преимущество при обучении модели на GPU.
Важно не иметь слишком много объединяющих уровней, так как каждый из них отбрасывает часть данных. Слишком частое объединение приведет к тому, что плотно связанные слои почти ничего не узнают, когда данные достигнут их.
Необходимое количество объединяющих слоев зависит от выполняемой задачи — это то, что вы определите со временем. Поскольку изображения в нашем наборе уже достаточно малы, мы не будем объединять их более двух раз.
Теперь вы можете повторить эти слои, чтобы дать вашей сети больше представлений для работы:
model.add(Conv2D(64, (3, 3), padding='same')) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.2)) model.add(BatchNormalization()) model.add(Conv2D(128, (3, 3), padding='same')) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(BatchNormalization())
После того, как мы закончили со сверточными слоями, нам нужно сжать данные, поэтому мы импортировали функцию Flatten выше. Мы также добавим слой исключения снова:
model.add(Flatten()) model.add(Dropout(0.2))
Теперь мы используем импортированную функцию Dense и создаем первый плотно связанный слой. Нам нужно указать количество нейронов в плотном слое. Обратите внимание, что число нейронов в последующих слоях уменьшается, в конечном итоге приближаясь к тому же числу нейронов, что и классы в наборе данных (в данном случае 10). Ограничение ядра может упорядочить данные в процессе обучения, что также помогает предотвратить переобучение. Вот почему мы импортировали maxnorm ранее.
model.add(Dense(256, kernel_constraint=maxnorm(3))) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(BatchNormalization()) model.add(Dense(128, kernel_constraint=maxnorm(3))) model.add(Activation('relu')) model.add(Dropout(0.2)) model.add(BatchNormalization())
В этом последнем слое мы уравниваем количество классов с числом нейронов. Каждый нейрон представляет класс, поэтому на выходе этого слоя будет вектор из 10 нейронов, каждый из которых хранит некоторую вероятность того, что рассматриваемое изображение принадлежит его классу.
Наконец, функция активации softmax выбирает нейрон с наибольшей вероятностью в качестве своего выходного значения, предполагая, что изображение принадлежит именно этому классу:
model.add(Dense(class_num)) model.add(Activation('softmax'))
Теперь, когда мы разработали модель, которую хотим использовать, остаётся лишь скомпилировать ее. Давайте укажем количество эпох для обучения, а также оптимизатор, который мы хотим использовать.
Оптимизатор - это то, что настроит веса в вашей сети так, чтобы приблизиться к точке с наименьшими потерями. Алгоритм Адама является одним из наиболее часто используемых оптимизаторов, потому что он дает высокую производительность в большинстве задач:
epochs = 25 optimizer = 'adam'
Теперь скомпилируем модель с выбранными параметрами. Давайте также укажем метрику для оценки.
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
Мы также можем распечатать сводку по модели, чтобы получить представление о модели в целом.
print(model.summary())
Распечатка сводки даст нам некоторую информацию:
Results: Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 32, 32, 32) 896 _________________________________________________________________ activation_1 (Activation) (None, 32, 32, 32) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 32, 32, 32) 0 _________________________________________________________________ batch_normalization_1 (Batch (None, 32, 32, 32) 128 _________________________________________________________________ conv2d_2 (Conv2D) (None, 32, 32, 64) 18496 _________________________________________________________________ activation_2 (Activation) (None, 32, 32, 64) 0 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 16, 16, 64) 0 _________________________________________________________________ dropout_2 (Dropout) (None, 16, 16, 64) 0 _________________________________________________________________ batch_normalization_2 (Batch (None, 16, 16, 64) 256 _________________________________________________________________ conv2d_3 (Conv2D) (None, 16, 16, 64) 36928 _________________________________________________________________ activation_3 (Activation) (None, 16, 16, 64) 0 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 8, 8, 64) 0 _________________________________________________________________ dropout_3 (Dropout) (None, 8, 8, 64) 0 _________________________________________________________________ batch_normalization_3 (Batch (None, 8, 8, 64) 256 _________________________________________________________________ conv2d_4 (Conv2D) (None, 8, 8, 128) 73856 _________________________________________________________________ activation_4 (Activation) (None, 8, 8, 128) 0 _________________________________________________________________ dropout_4 (Dropout) (None, 8, 8, 128) 0 _________________________________________________________________ batch_normalization_4 (Batch (None, 8, 8, 128) 512 _________________________________________________________________ flatten_1 (Flatten) (None, 8192) 0 _________________________________________________________________ dropout_5 (Dropout) (None, 8192) 0 _________________________________________________________________ dense_1 (Dense) (None, 256) 2097408 _________________________________________________________________ activation_5 (Activation) (None, 256) 0 _________________________________________________________________ dropout_6 (Dropout) (None, 256) 0 _________________________________________________________________ batch_normalization_5 (Batch (None, 256) 1024 _________________________________________________________________ dense_2 (Dense) (None, 128) 32896 _________________________________________________________________ activation_6 (Activation) (None, 128) 0 _________________________________________________________________ dropout_7 (Dropout) (None, 128) 0 _________________________________________________________________ batch_normalization_6 (Batch (None, 128) 512 _________________________________________________________________ dense_3 (Dense) (None, 10) 1290 _________________________________________________________________ activation_7 (Activation) (None, 10) 0 ================================================================= Total params: 2,264,458 Trainable params: 2,263,114 Non-trainable params: 1,344
Теперь мы приступаем к обучению модели. Для этого нам нужно вызвать функцию fit () для модели и передать выбранные параметры.
Вот где используется SEED, выбранный в целях воспроизводимости.
numpy.random.seed(seed) model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=epochs, batch_size=64)
Возьмём тренировочный набор в 50000 образцов и проверочный в 10000 образцов.
Запуск этого куска кода даст:
Epoch 1/25 64/50000 [..............................] - ETA: 16:57 - loss: 3.1479 - acc: 0.0938 128/50000 [..............................] - ETA: 10:12 - loss: 3.0212 - acc: 0.0938 192/50000 [..............................] - ETA: 7:57 - loss: 2.9781 - acc: 0.1250 256/50000 [..............................] - ETA: 6:48 - loss: 2.8830 - acc: 0.1484 320/50000 [..............................] - ETA: 6:07 - loss: 2.8878 - acc: 0.1469 384/50000 [..............................] - ETA: 5:40 - loss: 2.8732 - acc: 0.1458 448/50000 [..............................] - ETA: 5:20 - loss: 2.8842 - acc: 0.1406 ... ... ... 49664/50000 [============================>.] - ETA: 1s - loss: 1.5160 - acc: 0.4611 49728/50000 [============================>.] - ETA: 1s - loss: 1.5157 - acc: 0.4612 49792/50000 [============================>.] - ETA: 1s - loss: 1.5153 - acc: 0.4614 49856/50000 [============================>.] - ETA: 0s - loss: 1.5147 - acc: 0.4615 49920/50000 [============================>.] - ETA: 0s - loss: 1.5144 - acc: 0.4617 49984/50000 [============================>.] - ETA: 0s - loss: 1.5141 - acc: 0.4617 50000/50000 [==============================] - 262s 5ms/step - loss: 1.5140 - acc: 0.4618 - val_loss: 1.0715 - val_acc: 0.6195 End of Epoch 1
Обратите внимание, что в большинстве случаев вам нужно иметь проверочный набор, отличный от набора для тестирования, поэтому вы должны указать процент данных обучения, которые будут использоваться в качестве набора для проверки. В этом случае мы просто передадим тестовые данные, чтобы убедиться, что тестовые данные отложены и не использовались для обучения. В этом примере мы будем иметь только тестовые данные, чтобы все было проще.
Теперь мы можем оценить модель и посмотреть, как она работает. Просто вызовите model.evaluate():
# Model evaluation scores = model.evaluate(X_test, y_test, verbose=0) print("Accuracy: %.2f%%" % (scores[1]*100))
И вот мы получаем результат:
Accuracy: 83.01%
Ну, вот и всё! Теперь у нас есть обученная распознаванию изображений CNN. Неплохо для первого запуска, но вы, вероятно, захотите поэкспериментировать со структурой модели и параметрами, чтобы попытаться добиться лучшей эффективности.
Практическое применение нейросетей для распознавания капч
Теоретические и экспериментальные работы по CNN закономерно подводят к вариантам использования нейросетей для решения практических повседневных задач. Наиболее актуальной в сфере распознавания и классификации изображений является задача по решению капчи, в частности — самой популярной на сегодняшний день Google ReCaptcha v2.
Несмотря на схожесть с нашим примером, на практике реализовать рабочую нейросеть для решения капчи представляется крайне затратным и неэффективным решением из-за постоянно меняющегося набора данных (картинок в капче). Столь частое и непредсказуемое обновление входящих данных влечёт за собой целую вереницу проблем:
- необходимость регулярно собирать и обрабатывать новые данные
- необходимость постоянного контроля процесса со стороны человека и внесение правок в модель по ходу работы (включая эксперименты с параметрами)
- необходимость мощного оборудования для обучения модели 24/7
- и т. д.
Универсальное решение проблемы обхода различных капч онлайн
Для решения капч в беспрерывном режиме, с высокой скоростью и сравнительно низкой стоимостью — большим спросом пользуются онлайн сервисы по распознаванию капч, которые привлекают для этого реальных пользователей. На отечественном рынке лидером является сервис RuCaptcha.com, который выгодно отличается от конкурентов:
высокой точностью (до 99%) и скоростью решений (12 секунд для обычных текстовых капч и 24 секунды для ReCaptcha)
приемлемыми фиксированными ценами (цена не возрастает при увеличении нагрузки на сервера сервиса): 35 рублей за 1000 решений обычных капч и 160 рублей за 1000 решений ReCaptcha
возвратом средств за редкие неуспешные распознавания
технической возможностью решать огромные объёмы капч (более 10,000 в минуту)
простым и функциональным API
готовыми библиотеками и образцами кода для различных языков программирования
* привлекательной партнёрской программой, позволяющей разработчикам и рефоводам получать до 15% от расходов привлечённых клиентов и 10% от доходов привлеченных в сервис работников.
Любые возникающие вопросы по работе сервиса — оперативно решаются службой поддержки через систему тикетов.
Вывод
Что ж, теперь, когда вы реализовали свою первую сеть по распознаванию изображений в Keras, было бы неплохо поиграть с моделью и посмотреть, как изменение ее параметров влияет на эффективность.
Это даст вам некоторое интуитивное понимание оптимальных значений для различных параметров модели. Вы должны также изучить различные варианты параметров и гиперпараметров, работая над своей моделью. После того, как вы основательно освоитесь, вы можете попробовать реализовать собственный классификатор изображений уже на другом наборе данных.
Что же касается рутинных практических рабочих задач, как, например, распознавание капчи — то создание и обучение нейросети едва ли является гибким и эффективным решением. Значительно быстрее, дешевле и удобнее использовать для таких целей онлайн сервис RuCaptcha.com .
Собственно, а что исходников то нет?
Большая кнопка GITHUB РЕПОЗИТОРИЙ ни о чём не говорит?
Окей, нашел (возможно глаза привыкают к тому, что на сайтах мелькают объявления и большие яркие кнопки автоматически игнорируются). Но не нашел данные на которых обучалась нейросеть. Без них повторить "это" я все равно не смогу ведь...
хех... непривычно, что практически всё является полезным контентом? Самому непривычно... надеюсь, что протяну год в таком режиме, чтобы в конце сделать выводы - "может ли бесплатный ресурс жить без рекламных сетей?"
Что касается исходных данных, то извини, тут не помогу, есть причины, почему они не приложены. Ок?
Ок :)
Спасибо за Ваш труд, очень крутой у вас ресурс, особенно нравятся статьи по Django!
Не тому написали, ну да ладно )) Спасибо за отзыв :D
будем считать что авторы статьи по django примут на свой счет :D
Огромное спасибо за метериал, по-больше бы подобных статей (с подробным описанием работы и примерами применения) на тему современных технологий.
Вопрос поразмышлять. На текущий момент реализовано немало технологий в основе которых лежит комп. зрение. Одна из таких - это государственные сканнеры проходящих людей, например в аэропортах. Что лежит в основе такой технологии? как такой объем данных обрабатывается в реальном времени и как входные данные подгоняются под один тип?
почему то вместо 50000 обрабатывает по 782 картинки кажду. эпоху
А в чём проблема-то? Если не жалко тратить пару-тройку сотен рублей на хостинг/VDS в месяц, то любой сайт прекрасно существует без всякой рекламы сколь угодно долго.
Статья очень понравилась! Как раз искал пример, где работа с изображениями с помощью нейросети расписана по шагам для начинающих.
Время на развитие, трудозатраты на разработку, ответы на специализированные вопросы и т.д. вы видимо в расчёт не берёте.
Естественно, поскольку собственное время — ресурс бесплатный, и предполагается, что проект приносит удовольствие (или какие-то другие нематериальные бонусы типа чувства собственной значимости и компетентности).
Собственное время не бесплатно, поскольку бесценно и не возобновимо. И не все это понимают, а Вы понимаете?
А поддержание подобного ресурса со временем становится накладным, сколько бы удовольствия он не приносил, это не личный бложик обо всём и ни о чём. Я много раз порывался писать статьи обо всякой ерунде, но останавливал себя, поскольку это не то место, чтобы устраивать здесь свой дневник. Это специализированный ресурс, а подготовка специализированной статьи порой занимает много времени.
И при всём своём желании его развивать, у меня нет желания делать это в ущерб личной жизни. Поэтому со временем такой ресурс либо монетизируется и все силы бросаются на него, либо застывает без новых статей и нового материала, как сейчас.
Так что делайте выводы сами. Но далее обсуждать это не буду, это оффтоп и пустое столкновение мнений.
Понимаю. А ещё понимаю, что не потратить время нельзя. День всё равно пройдёт, и вопрос только в том, на что он будет потрачен: на то, что приносит в жизни удовольствие или нет.
Насчёт застывает — ничего страшного в этом нет (кроме негативного влияния на SEO). Я, например, у себя на сайте в некоторые разделы пишу 2—3 раза в год, когда есть идеи, вдохновение и желание поделиться информацией, и считаю это совершенно нормальным.
Впрочем, похоже, мы и правда не поймём друг друга. Слишком разная система жизненных ценностей. Так что оставлю вас в покое.
А что собственно выводит программа, как вывести то что она смогла распознать?