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

Что такое текстовая CAPTCHA?

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

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

Полностью сверточная нейронная сеть

Когда символы объединяются, их обычно очень трудно разделить с помощью эвристических алгоритмов. Следовательно, необходимо искать каждый символ в каждом месте текстового изображения. Полностью сверточная нейронная сеть справится с этой задачей. Полностью сверточная сеть - это сверточная сеть без полносвязного слоя. На вход такой сети подается изображение, а на выходе - изображение или несколько изображений (центральные карты).

Количество центральных карт равно длине алфавита символов, используемых в CAPTCHA. Центры персонажей отмечены на центральных картах. Учитывается масштабное преобразование, которое происходит в сети из-за уровней объединения. Пример карты символов для символа «D» показан ниже.

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

Сначала была протестирована полностью сверточная сеть на символе «R»:

Для теста использовалась небольшая сеть с 2 уровнями пула, обученными на CPU. Убедившись, что эта идея каким-то образом работает, была куплена бывшая в употреблении видеокарта Nvidia GTX 760, 2 ГБ. Это дало возможность обучать большие сети всем символам алфавита, а также (примерно в 10 раз) ускорило обучение. Для обучения сети использовалась библиотека Theano, которая сейчас не поддерживается.

Обучение с использованием генератора

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

Итоговая точность после обучения на сгенерированных CAPTCHA была примерно в 2 раза ниже, чем после обучения на реальных CAPTCHA. Вероятно, важны такие особенности, как пересечение символов, масштаб, их вес линий, параметры искажения и т. Д., И генератор не смог воспроизвести эти особенности. Сеть, обученная на сгенерированных CAPTCHA и протестированная на реальных CAPTCHA, имела точность около 10%. Точность - это процент правильно распознанных CAPTCHA. CAPTCHA считается распознанной, если все символы в ней распознаны правильно. Во всяком случае, этот эксперимент показал, что метод работает и требуется только для повышения точности распознавания.

Обучение на реальных данных

Для ручного аннотирования реальных наборов данных CAPTCHA на Matlab был написан скрипт с графическим интерфейсом:

Здесь кружки можно размещать или перемещать с помощью мыши. Круг отмечает центр персонажа. Ручное аннотирование заняло 5–15 часов. Однако есть сервисы, в которых за небольшую плату выполняют ручное аннотирование наборов данных. Но, как выяснилось, сервис Amazon Mechanical Turk не работает с российскими клиентами. Я разместил заказ на аннотирование набора данных на сайте фрилансера. К сожалению, качество аннотации было далеко не идеальным. Аннотацию исправил сам. Кроме того, поиск человека, который может это сделать, требует времени (1 неделя) и, к тому же, казался довольно дорогим: 30 долларов за 560 маркированных капч. Так что мне пришлось отказаться от этого метода, и, в конце концов, я стал использовать сайты для ручного распознавания капч, где самая низкая цена составляла 1 доллар за 2000 капч. Но ответ, который я получил, был всего лишь строкой. Таким образом, ручной расстановки центров не удалось избежать. Более того, исполнители в таких сервисах, по всей видимости, допускали ошибки или даже действовали недобросовестно, набирая в ответ произвольную строку. В результате мне пришлось проверить и исправить свои ошибки.

Более глубокая сеть

Очевидно, точности распознавания было недостаточно, поэтому встал вопрос о выборе архитектуры. Меня интересовало, «видит» ли один пиксель в выходном изображении весь символ во входном изображении:

Таким образом, я рассматриваю один пиксель в выходном изображении, и возникает вопрос: значения каких пикселей во входном изображении влияют на значения этого пикселя? Идея заключалась в следующем: если пиксель не видит всего персонажа, то используется не вся информация о персонаже и точность хуже. Чтобы определить размер области (назовем это так), я провел следующий эксперимент: установил для всех слоев свертки значение 0,01, а смещения - 0, и на вход сети поступило изображение, в котором значения всех пикселей равны 0, кроме центрального. В итоге сеть дает пятно на выходе:

Форма этого пятна близка к форме функции Гаусса. Возникает вопрос: почему пятно круглое, а ядра в сверточных слоях квадратные? (в сети используются ядра 3x3 и 5x5). Мое объяснение таково: это как центральная предельная теорема. Как мы видим здесь, в нем есть подход к распределению Гаусса. Согласно центральной предельной теореме для случайных величин, даже с разными распределениями, распределение их суммы равно свертке распределений. Таким образом, если мы сворачиваем любой сигнал сам с собой много раз, как следует из центральной предельной теоремы, результат приближается к функции Гаусса, и ширина функции Гаусса увеличивается как квадратный корень из числа сверток (слоев). Если в этой сети с постоянными весами мы видим, где значение пикселя больше нуля в выходном изображении, мы получим квадратную область (см. Рис. Ниже), размер этой области пропорционален сумме размеров сверток в сверточных слоях. сети.

Раньше я думал, что из-за ассоциативного свойства свертки две последовательные свертки 3x3 эквивалентны свертке 5x5, и поэтому, если вы сворачиваете 2 ядра 3x3, вы получите одно ядро ​​5x5. Однако затем я пришел к выводу, что это не эквивалентно: ядра 3x3 имеют 9 * 2 = 18 степеней свободы, а одно ядро ​​5x5 имеет 25 степеней свободы. В результате на выходе сети получается функция Гаусса, ширина которой меньше суммы размеров сверток в слоях. Итак, я нашел ответ на вопрос: на какие пиксели на выходе влияет один пиксель на входе. Хотя изначально вопрос был обратный. Но оба вопроса эквивалентны, что можно понять из следующего рисунка:

На рисунке мы можем представить, что это вид сбоку изображения или высота изображения равна 1. Каждый из пикселей A и B имеет свою собственную область влияния в выходном изображении (обозначена синим цветом): DC для A, CE для B. На значение пикселя C влияют значения пикселей A и B и значения всех пикселей между A и B. Расстояния равны: AB = DC = CE (с учетом масштабирования: сеть имеет объединение слоев, поэтому входные и выходные изображения имеют разное разрешение). В результате мы имеем следующий алгоритм определения размеров прицела:

  1. Установите постоянный вес в сверточных слоях и установите смещения на 0
  2. Подайте на вход изображение с одним ненулевым пикселем
  3. Получить размер пятна на выходе
  4. Умножьте размер на коэффициент масштабирования. Коэффициент масштабирования учитывает разное разрешение входного и выходного изображений (например, если у нас есть 2 объединяющих слоя в сети, выходное разрешение в 4 раза ниже, чем входное изображение, размер необходимо умножить на 4).

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

Затем с помощью градиентного спуска минимизируем функцию:

Здесь,

входной образ сети,

относится к выходным изображениям сети, c - константа, которая выбирается экспериментально (c = 0,1). При такой минимизации входные и выходные данные сети считаются переменными, а веса сети - константами. Начальное значение переменной

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

Результаты этого эксперимента следующие:

Для символа «2»:

Для символа «5»:

Для символа «L»:

Для символа «u»:

Изображения слева - это оригинальные изображения CAPTCHA, изображение справа - это оптимизированное изображение.

Квадрат на изображениях показывает область, где выходное значение больше 0, кружки на рисунке представляют собой изолинии для функции Гаусса осциллографа. Маленький кружок - это изолиния с уровнем 35% от максимального значения, большой кружок - с уровнем 3%. Примеры показывают, что сеть видит в пределах своих возможностей. Однако у персонажа «u» есть особенности, выходящие за рамки. Возможное объяснение состоит в том, что это частично ложное срабатывание на символе «n».

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

Синий шрифт над стрелками показывает количество изображений (характерных карт). c - сверточный слой, p - слой max-pooling, зеленый шрифт внизу показывает размеры ядер. Слои свертки используют ядра 3x3 и 5x5 без strade, слой объединения имеет патч 2x2. После каждого сверточного слоя находится слой ReLU (не показан). На входе - одно изображение, на выходе - 24 изображения (количество символов в алфавите). В сверточных слоях был специальный padding, делающий вывод слоя одинакового размера на входе. Padding добавляет нули, но это не влияет на работу сети, потому что значение пикселя фона CAPTCHA равно 0, потому что он всегда принимает негативное изображение (белые буквы на черном фоне). Паддинг лишь немного замедляет работу сети. Поскольку сеть имеет 2 уровня объединения, разрешение выходного изображения в 4 раза ниже, чем разрешение входного изображения, поэтому каждое объединение снижает разрешение вдвое, например, если размер входного сигнала CAPTCHA составляет 216x96, то выходной сигнал будет 24 изображения размером 54x24.

Улучшения

Переход от решателя SGD к решателю ADAM дал заметный импульс обучению, и конечное качество улучшилось. Решатель ADAM импортирован из модуля Lasagne и использовал его в анано-коде, параметр скорости обучения был 0,0005, добавлена ​​регуляризация L2 через вектор градиента. Было замечено, что от тренировки к тренировке результат был разным. Это можно объяснить тем, что алгоритм градиентного спуска застревает в недостаточно оптимальном локальном минимуме. Я частично решил эту проблему следующим образом: я начал тренировку несколько раз и выбрал одни из лучших результатов, затем я продолжил тренироваться еще несколько эпох, после этого я выбрал один лучший результат, и этот лучший результат тренировался в течение долгого времени. время. Таким образом, удавалось избежать застревания в недостаточно оптимальных локальных минимумах, а оптимальное конечное значение функции потерь было довольно небольшим. На рисунке показан график изменения значения функции потерь:

По оси абсцисс отложено количество эпох, по оси у - значение функции потерь. Разные цвета показывают разные тренировки. Порядок обучения примерно такой:

1) стартовые 20 тренировок на 10 эпох

2) выбираем 10 лучших значений (с меньшими потерями) и тренируем их еще на 100 эпох

3) выбор одного лучшего результата и продолжение его тренировки еще на 1500 эпох.

На это уходит около 12 часов. Конечно, в целях экономии памяти тренировки проводятся последовательно, например, в пункте 2) последовательно выполнялись 10 тренировок, одна за другой. Для этого я модифицировал решатель ADAM для лазаньи. После модификации он смог сохранять и загружать переменные состояния в решатель.

Набор данных был разделен на 3 части, чтобы отслеживать переоснащение сети:

Часть 1: набор данных для обучения - использовался для обучения сети

Часть 2: набор тестовых данных, в котором сеть проверяется во время обучения

Часть 3: второй набор тестовых данных, он предназначен для проверки качества обучения после обучения

Наборы данных 2 и 3 в моем случае малы. В каждом было по 160 отловов. Набор данных 2 использовался для поиска оптимального порога, то есть порога, который установлен в выходном изображении. Если значение пикселя превышает пороговое значение, то в данном месте находится символ. Обычно оптимальное значение порога составляет 0,3–0,5. Если точность набора тестовых данных значительно ниже точности набора обучающих данных, это означает, что произошло переобучение и набор данных необходимо увеличить. Если точности примерно такие же, но невысокие, то необходимо усложнить архитектуру нейронной сети и увеличить набор обучающих данных. Есть два способа усложнить архитектуру сети: увеличить глубину или увеличить ширину сети.

Предварительная обработка изображений также повысила точность распознавания. Пример предварительной обработки приведен ниже:

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

Эти искажения необходимо компенсировать.

Неудачные идеи

Всегда интересно читать о неудачах людей, я сейчас их опишу.

Возникла проблема небольшого набора данных: для хорошего распознавания требовался большой набор данных, который нужно было пометить вручную (около 1000 захватов). Я делал много попыток обучить сеть с небольшим набором данных. Я попытался обучить сеть по результатам распознавания другой сети. При этом я выбрал только те изображения CAPTCHA и те места, в которых сеть была уверена. Достоверность определялась значением пикселя на выходном изображении. Таким образом, удалось увеличить набор данных. Однако идея не сработала, после нескольких итераций обучения качество распознавания ухудшилось: сеть не распознавала некоторые символы, путала их. Итак, ошибки распознавания накапливались.

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

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

Пример работы автоэнкодера:

Первое изображение - это входное изображение, второе изображение - выходное изображение.

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

Также был пример CAPTCHA, в котором использовался цвет (см. Ниже):

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

Результат

Что еще можно улучшить

Может быть сделано автоматическое аннотирование центров символов. Сервисы для ручного распознавания капч выдают только распознанные строки, поэтому автоматическое аннотирование центров поможет полностью автоматизировать аннотацию обучающего набора данных. Идея состоит в том, чтобы выбрать только те CAPTCHA, где каждый символ встречается один раз, чтобы обучить отдельную сверточную сеть для каждого символа. Такая сеть будет отвечать только на один вопрос: существует ли символ в CAPTCHA? Затем можно было проверить, какие функции использует сеть, с помощью метода минимизации значений пикселей входных изображений (описанного выше). Таким образом, собранные функции позволят локализовать персонажа, а затем можно будет обучить полностью сверточную сеть на найденных центрах символов.

Выводы

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

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

© Максим А. Веденев