Evgenii Legotckoi
Evgenii Legotckoi13 мая 2020 г. 5:42

Распознавание изображений на Python с помощью TensorFlow и Keras

Введение

Одним из наиболее распространенных применений 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 .

Сервис распознавания капчи Rucaptcha
Сервис распознавания капчи Rucaptcha
Качественный и быстрый сервис по приемлемым ценам

Вам это нравится? Поделитесь в социальных сетях!

Vladimir Sergeevich
  • 14 мая 2020 г. 4:37

Собственно, а что исходников то нет?

Evgenii Legotckoi
  • 14 мая 2020 г. 4:51
  • (ред.)

Большая кнопка GITHUB РЕПОЗИТОРИЙ ни о чём не говорит?

Vladimir Sergeevich
  • 14 мая 2020 г. 6:58

Окей, нашел (возможно глаза привыкают к тому, что на сайтах мелькают объявления и большие яркие кнопки автоматически игнорируются). Но не нашел данные на которых обучалась нейросеть. Без них повторить "это" я все равно не смогу ведь...

Evgenii Legotckoi
  • 14 мая 2020 г. 7:17

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

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

Vladimir Sergeevich
  • 14 мая 2020 г. 7:21

Ок :)

BG
  • 26 мая 2020 г. 21:24

Спасибо за Ваш труд, очень крутой у вас ресурс, особенно нравятся статьи по Django!

Evgenii Legotckoi
  • 27 мая 2020 г. 1:19

Не тому написали, ну да ладно )) Спасибо за отзыв :D

BG
  • 28 мая 2020 г. 4:38

будем считать что авторы статьи по django примут на свой счет :D

progammist
  • 5 июня 2020 г. 11:52
  • (ред.)

Огромное спасибо за метериал, по-больше бы подобных статей (с подробным описанием работы и примерами применения) на тему современных технологий.
Вопрос поразмышлять. На текущий момент реализовано немало технологий в основе которых лежит комп. зрение. Одна из таких - это государственные сканнеры проходящих людей, например в аэропортах. Что лежит в основе такой технологии? как такой объем данных обрабатывается в реальном времени и как входные данные подгоняются под один тип?

R
  • 15 апреля 2021 г. 11:55

R
  • 15 апреля 2021 г. 11:56

почему то вместо 50000 обрабатывает по 782 картинки кажду. эпоху

4X_Pro
  • 25 августа 2021 г. 9:39
  • (ред.)

"может ли бесплатный ресурс жить без рекламных сетей?"

А в чём проблема-то? Если не жалко тратить пару-тройку сотен рублей на хостинг/VDS в месяц, то любой сайт прекрасно существует без всякой рекламы сколь угодно долго.

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

Evgenii Legotckoi
  • 25 августа 2021 г. 10:20

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

4X_Pro
  • 25 августа 2021 г. 12:02

Естественно, поскольку собственное время — ресурс бесплатный, и предполагается, что проект приносит удовольствие (или какие-то другие нематериальные бонусы типа чувства собственной значимости и компетентности).

Evgenii Legotckoi
  • 25 августа 2021 г. 15:26

Собственное время не бесплатно, поскольку бесценно и не возобновимо. И не все это понимают, а Вы понимаете?
А поддержание подобного ресурса со временем становится накладным, сколько бы удовольствия он не приносил, это не личный бложик обо всём и ни о чём. Я много раз порывался писать статьи обо всякой ерунде, но останавливал себя, поскольку это не то место, чтобы устраивать здесь свой дневник. Это специализированный ресурс, а подготовка специализированной статьи порой занимает много времени.
И при всём своём желании его развивать, у меня нет желания делать это в ущерб личной жизни. Поэтому со временем такой ресурс либо монетизируется и все силы бросаются на него, либо застывает без новых статей и нового материала, как сейчас.
Так что делайте выводы сами. Но далее обсуждать это не буду, это оффтоп и пустое столкновение мнений.

4X_Pro
  • 26 августа 2021 г. 7:32

Понимаю. А ещё понимаю, что не потратить время нельзя. День всё равно пройдёт, и вопрос только в том, на что он будет потрачен: на то, что приносит в жизни удовольствие или нет.
Насчёт застывает — ничего страшного в этом нет (кроме негативного влияния на SEO). Я, например, у себя на сайте в некоторые разделы пишу 2—3 раза в год, когда есть идеи, вдохновение и желание поделиться информацией, и считаю это совершенно нормальным.
Впрочем, похоже, мы и правда не поймём друг друга. Слишком разная система жизненных ценностей. Так что оставлю вас в покое.

МА
  • 26 октября 2022 г. 13:16

А что собственно выводит программа, как вывести то что она смогла распознать?

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
e
  • ehot
  • 31 марта 2024 г. 11:29

C++ - Тест 003. Условия и циклы

  • Результат:78баллов,
  • Очки рейтинга2
B

C++ - Тест 002. Константы

  • Результат:16баллов,
  • Очки рейтинга-10
B

C++ - Тест 001. Первая программа и типы данных

  • Результат:46баллов,
  • Очки рейтинга-6
Последние комментарии
k
kmssr8 февраля 2024 г. 15:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко4 февраля 2024 г. 22:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 7:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 5:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 декабря 2023 г. 18:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
a
a_vlasov14 апреля 2024 г. 3:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев13 апреля 2024 г. 23:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 1:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
P
Pisych27 февраля 2023 г. 1:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 января 2024 г. 8:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

Следите за нами в социальных сетях