CAPTCHA существует как механизм, предотвращающий рассылку ботами спама API-интерфейсов, очистку веб-сайтов и общее злоупотребление привилегиями в Интернете, предназначенными для использования людьми. С самого начала CAPTCHA эволюционировала от простых черно-белых разделенных символов в простом шрифте без засечек с минимальным преобразованием (например, вращением) или без него, как показано на рисунке 1, до включения нескольких цветов, шума, различных шрифтов, различных шкалы символов и сложные сообщения / слова, как показано на рисунке 2. Несмотря на то, что изначально было трудно решить, системы в конечном итоге эволюционировали и стали включать в себя CAPTCHA изображений, аудио CAPTCHA и многомодальную CAPTCHA взаимодействия с пользователем, называемую reCAPTCHA, которую можно наблюдать в Рисунок 3.

Задача

Хотя это несколько очевидно, главная цель этого проекта - расшифровать текстовые CAPTCHA путем прогнозирования символов, скрытых в сообщении. Однако это само по себе является сложной задачей, поскольку существует 36⁴ перестановок CAPTCHA, если предполагается, что CAPTCHA содержит 4 символа, где каждый символ может быть одним из 36 символов (A – Z0–9). Таким образом, вместо оценки контролируемой модели с помощью 36-позиционной задачи классификации, цель упрощается до распознавания символов, что требует дополнительной предварительной обработки посредством сегментации CAPTCHA, но создает задачу классификации из 36 направлений вместо 36-позиционной. задача.

Набор данных

На момент написания этого блога в 2020 году текстовые CAPTCHA относительно устарели. Несмотря на это, они существуют, и есть смысл использовать их в качестве игрушечной задачи для побочного проекта компьютерного зрения для иллюстрации как традиционных методов зрения (например, эрозия, шумоподавление и т. Д.), Так и методов глубокого обучения (например, CNN). В этом проекте будут использоваться CAPTCHA, подобные показанной на рисунке 2. Эти CAPTCHA генерируются из 4 символов с разрешением 140x76 за счет использования библиотеки капчи Python (https://github.com/lepture/captcha/) и содержат заглавные английские буквы (AZ) и цифры 0–9 для всего 36 разных персонажей.

Предварительная обработка

Вызовы

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

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

Во-первых, изображение RGB считывается как изображение в градациях серого, чтобы упростить любые процедуры шумоподавления; все значения пикселей находятся в диапазоне 0–255. Затем изображение преобразуется в двоичную форму с порогом 230. Таким образом, все значения интенсивности пикселей ≤ 230 преобразуются в значение интенсивности 0 (черный), а значения ›230 преобразуются в 255 (белый). Это пороговое значение является чисто эмпирическим и работает практически во всех наблюдаемых случаях после ручной проверки изображений.

Удаление шума

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

Изображение сначала инвертируется, поэтому буквы белые, а фон черный. Затем выполняется эрозия с ядром (2, 2) для 1 итерации, чтобы ослабить как круговой, так и линейный шум. Как правило, с большими изображениями и более толстыми кругами / линиями шума это ядро ​​должно быть больше, но оно работает для набора данных в этой задаче.

Затем используются медианные фильтры, чтобы ослабить любые дополнительные линии. Первый медианный фильтр - (5, 1), представляющий вертикальный фильтр длиной 5, который удаляет практически весь линейный шум, присутствующий в изображении. По совпадению, это также помогает ослабить круговой шум. Затем горизонтальный фильтр размеров (1, 3) удаляет большую часть кругового шума. На этом этапе мы не только стерли шум, но и стерли некоторые фактические данные персонажа, необходимые для сохранения звука и целостности наших персонажей. Итак, расширение выполняется для 1 итерации с размером фильтра (2, 2), чтобы буквы стали больше. Здесь важно отметить, что расширение оживляет буквы больше, чем любое остающееся изображение шума. Однако из-за расширения этот небольшой оставшийся шум может стать немного более заметным на всем изображении, поэтому финальный средний фильтр размера (3, 3) пропускается по изображению, чтобы удалить последний оставшийся слабый шум.

При работе над проектом эмпирическая оценка имеет первостепенное значение для обеспечения правильного выполнения всех процедур предварительной обработки и обучения. Таким образом, после некоторой качественной оценки шумоподавленных изображений иногда появлялся некоторый дополнительный шум. Изначально я не собирался заниматься этим, так как это требовало дополнительной работы, чтобы получить не намного больше дополнительных преимуществ, но в конечном итоге это вызвало проблемы при сегментации. Таким образом, используются некоторые заключительные процедуры для удаления окончательного шума. Во-первых, преобразование hough circle определяет центры окружностей и их радиусы, которые действуют как маски для удаления любого дополнительного шума окружности. Круги с радиусами от 0 до 2 обнаруживаются с минимальным расстоянием между каждым кругом, равным 1. Затем, после удаления кругов, изображение стирается на 1 итерацию с ядром (3, 3), чтобы удалить любые края из шума круга. Вертикальный медианный фильтр (5, 1) устраняет дополнительный горизонтальный шум. Предпоследняя процедура - это расширение на 2 итерации с фильтром (3, 3) для восстановления изображения, что в конечном итоге приводит к появлению пухлых символов и вносит немного больше шума в изображение. Последний шаг - стереть изображение за 1 итерацию с ядром размера (3, 3). Качественно это устраняет практически весь шум, как круговой, так и линейный.

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

Сегментация персонажей

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

Сначала связанные компоненты изображения обнаруживаются с помощью функции connectedComponents (…) OpenCV. Затем алгоритм водораздела пытается дополнительно сегментировать перекрывающиеся символы.

В идеале, когда все символы не пересекаются и в изображении не остается шума, это должно возвращать 4 компонента, что указывает на 4 символа. Однако иногда возникают ошибки, указывающие на случаи, когда символы перекрываются (что делает сегментацию почти невозможной) или шум распознается как символ. Первый сценарий сложен, но возможен, в то время как вторую проблему решить вполне реально. Например, в изображении размером 140x76, содержащем 4 символа, символы могут быть настолько большими и такими маленькими, чтобы CAPTCHA стала полностью нечитаемой. Таким образом, было эмпирически определено, что маска символа, содержащая менее 100 пикселей, является «шумом», поэтому она удаляется из любой будущей предварительной обработки изображения. Во-вторых, когда изображение содержит ≥ 2200 пикселей, символ считается «совместным символом», что означает, что маска содержит два пересечения символов друг с другом. Чтобы разрешить эту встречу, используется наивный подход. Маска делится прямо посередине, левая подмаска представляет собой один символ, а правая подмаска - другой символ. Этот итерационный подход удаления масок «шума» и разделения масок «объединенного характера» выполняется для 10 итераций. К этому моменту, если не сгенерировано ровно 4 маски, CAPTCHA выбрасывается из набора данных, что считается плохим примером, поскольку все 4 маски символов не могут быть идентифицированы. Благодаря статистическому анализу, в сгенерированных масках присутствует постоянная ошибка в 5%, что указывает на то, что 5% из N сгенерированных CAPTCHA выбрасываются из конечного набора данных, используемого для обучения и оценки. К этому моменту предварительная обработка 1M CAPTCHA на виртуальной машине Google Colab занимает примерно 3 часа.

Последний шаг предварительной обработки и сегментации после извлечения символов - их квадратизация. Это требование, поскольку сверточные нейронные сети (CNN) требуют ввода фиксированного размера. Таким образом, наличие символов с размерами 45x67 и 76x65 приведет к тому, что модель будет выдавать ошибки несоответствия размера. Чтобы исправить это, фоновые пиксели добавляются к уже сегментированному изображению символа, чтобы заставить символы иметь размер 76x76, а также центрировать изображение. Весь этот процесс показан на рисунке 5, где показаны 4 основных этапа предварительной обработки (исходное изображение, бинаризация, удаление шума и сегментация).

Теперь, после удаления шума и сегментации CAPTCHA, мы перешли от N изображений CAPTCHA к 4N символам в пространстве A-Z0–9 (36 классов). Пришло время обучить модель распознавать этих персонажей.

Модель

Модель, которая начиналась как предварительно обученная AlexNet, превратилась в настраиваемую сверточную нейронную сеть (CNN). Сеть можно описать следующим образом:

2D Conv: in = 1, out = 20, kernel = (5, 5), stride = 1, padding = 4
ReLU()
2D Max Pool: kernel = (2, 2), stride = 2, padding = 0, dilation = 1
2D Conv: in = 20, out = 50, kernel = (5, 5), stride = 1, padding = 4
ReLU()
2D Max Pool: kernel = (2, 2), stride = 2, padding = 0, dilation = 1
Flatten()
FC: in = 24200, out = 500, bias = true
ReLU()
FC: in = 500, out = 36, bias = true

По сравнению с недавней литературой о видении, эта архитектура глубокого обучения относительно неглубокая, но она выполняет свою работу. Реализацию этой модели в PyTorch можно найти в коде файла model.py. В этой модели нет ничего особенного, но полезно сохранить ее относительно простой, поскольку были протестированы более продвинутые / недавние архитектуры глубокого обучения (VGG, ResNet), и они фактически работали хуже, чем эта модель, что указывает на возможное переоснащение набору данных. Кроме того, с точки зрения практикующего специалиста легче исправить / изменить архитектуру по мере необходимости на протяжении всего процесса.

ПРИМЕЧАНИЕ. Для сверточных слоев in = # входных каналов, out = # выходных каналов. Для полносвязных слоев (FC) in = # входных нейронов, out = # выходных нейронов. Наконец, для последнего уровня FC out = 36, потому что существует 36 классов.

Обучение

Обучение этой модели - относительно простой процесс, во многом похожий на проект, связанный с классификацией MNIST. Оптимизатор Адама используется с кросс-энтропийной потерей (сокращение «суммы», а не «среднего»). Для оптимизатора эмпирически было установлено, что скорость обучения 0,0001, снижение веса 0,98 и размер пакета 32 работают лучше всего. Когда я тренировался со 100 эпохами, модель обычно сходится менее чем за 20 эпох.

Модель, использованная на этапе обучения, описана выше. Важно отметить, что все изображения, передаваемые в сеть, имеют размер 76x76x1 (третий канал указывает оттенки серого).

Мы экспериментировали со снижением скорости обучения, но в конечном итоге исключили его из финального обучения из-за отсутствия воздействия. Однако размер партии сыграл роль. Было обнаружено, что размер пакета 512 снижает точность незначительно.

Следовательно, в конечном итоге для обучения модели использовалась скорость обучения 0,00005 с размером пакета 512. Размер пакета, хотя и не наблюдался, чтобы оказать существенное влияние в диапазоне 32 ≤ batch_size ≤ 512. , был выбран больший размер партии, чтобы ускорить процесс обучения.

Некоторые из наиболее важных вариантов дизайна для обучения модели фактически связаны с набором данных. И размер набора данных, и размер раздела оказали приличное влияние на производительность модели. Первые испытания были выполнены на наборе данных из 1000 CAPTCHA (~ 4000 символов). В конечном итоге это число было увеличено до 10K, 100K и 1M CAPTCHA, генерируя примерно 40K, 400K и 4M символов соответственно. На практике количество символов в наборе данных оказывается ниже расчетного из-за вышеупомянутой 5% -ной ошибки сегментации CAPTCHA из раздела Предварительная обработка. Точность этих размеров наборов данных показана в разделе Результаты. Между тем, первоначальное разделение поездов / проверки / испытаний было выполнено с частотой 60% / 20% / 20% соответственно. Однако одной из отличительных особенностей глубокого обучения является проверка, и размеры тестов не должны поддерживать определенное соотношение к размеру обучающей выборки, чтобы считаться «респектабельными» или «беспристрастными». Таким образом, как только набор данных был увеличен до 1 млн CAPTCHA, разбиения преобразовались в 80% / 10% / 10%, что по-прежнему сохраняло большие сбалансированные разделы для проверочных и тестовых разделений, позволяя модели просматривать больше обучающих изображений, таким образом повышение надежности в будущем.

Наконец, после того, как были выбраны все варианты дизайна, модель была обучена. С указанными выше гиперпараметрами обучение заняло около 10 часов на графическом процессоре NVIDIA K80 (приобретенном в Google Colab).

Результаты

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

Самая важная метрика оценки для этой задачи - точность, которая носит количественный характер. При разработке надежной системы безопасности с помощью CAPTCHA такие показатели, как оценка F1, точность и отзыв, практически бесполезны. Архитекторы заботятся только о том, чтобы обеспечить низкий уровень успеха для пользователей, не являющихся людьми. В этом эксперименте успех для предположений нашего набора данных - это когда система может идентифицировать все 4 символа в CAPTCHA; меньшее количество указывает на сбой. Таким образом, точность вычислений относительно проста. Интуитивно понятно, что высокая скорость декодирования CAPTCHA подразумевает высокую точность распознавания символов. Таким образом, точность используется как для задач / наборов данных, так и результаты отображаются ниже.

Количественный

Прежде чем обсуждать количественные результаты, важно обсудить ожидаемые результаты. В задаче классификации MNIST часто встречаются модели, достигающие ›99% точности классификации, но разница между этой задачей и этой задачей состоит в том, что MNIST - это 10-позиционная классификация (а не 36-позиционная), и цифры не требуют предварительной обработки, поскольку они заранее тщательно передаются исследователю. Таким образом, перед обучением модели ожидаемая точность должна была быть в пределах 80–100%. Кроме того, если модель достигает точности X% в задаче классификации символов, мы можем оценить точность задачи CAPTCHA, поскольку это простая перестановка символов. Например, если модель достигает 80% точности классификации символов, при этом вероятность успеха каждого класса одинакова, мы ожидаем, что точность CAPTCHA будет (0,8) ⁴ = 0,4096 = 40,96%. Все ожидаемые точности CAPTCHA наблюдаются в правом столбце таблицы 1.

Таким образом, положительный случай - это классификация символов 90%, что указывает на более точный, чем случайный переворот, получение правильной всей CAPTCHA. Однако в идеальном случае точность символов составляет 95% или 99%, что приводит к уверенной модели декодирования CAPTCHA. Но хватит теоретической дискуссии о ожидании точности. Перейдем к реальным эмпирическим результатам.

Сначала мы наблюдаем за точностью распознавания символов после обучения нашей модели:

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

Как видно из таблицы 2, размер набора данных играет важную роль в точности классификации символов, достигая почти 91% точности, что является положительным знаком. Однако при использовании этой модели с точностью 91% на полномасштабных CAPTCHA (прошедших ту же стадию предварительной обработки для сегментации символов, что и в обучающем наборе) возвращается точность 44,41%, что примерно на 20% ниже ожидаемой точности от Таблица 1. Это интересный результат не только из-за самой низкой точности, но и из-за того, что точность значительно ниже ожидаемой.

На первый взгляд, первоначальные предположения таковы, что набор данных несбалансирован. Однако после некоторого анализа оказалось, что это не так. Кроме того, с набором данных из 1 млн CAPTCHA и около 4 млн символов предполагается, что символы генерируются равномерно, так что это вторая проверка, что дисбаланс классов не является проблемой. Итак, после того, как эта идея была опровергнута, был проведен анализ точности отдельных букв. Как показано ниже, почти все буквы имеют точность 90% или выше, за двумя исключениями, O и 0, обе имеют низкую точность, колеблющуюся в диапазоне 50–70%. Однако причина такого сценария проста, поскольку O и 0 очень похожи по форме. Точности можно увидеть в таблице 4.

После этого анализа я посмотрел на персонажей, которых модель классифицировала неправильно. Таблица 5 содержит долю неправильно классифицированных персонажей, принадлежащих к определенному классу. Эта таблица немного информативна, показывая, что одни символы с большей вероятностью, чем другие будут классифицированы неправильно. В то время как «O» - это символ, неправильно классифицированный с наибольшей частотой, неудивительно, но «0», являющийся седьмым по частоте ошибок, немного удивляет, учитывая его низкую точность классификации.

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

Качественный

Хотя количественные результаты объективны и просты, качественные результаты также всегда приветствуются. Не стоит тратить время на показ модели, предсказывающей метку для данного персонажа. Вместо этого для цифр были созданы графики t-SNE, как показано на рисунках 6 и 7. На рисунке 6 изображения символов 76x76 сплющены для получения 5775 «функций» / пикселей изображения без передачи в классификатор. На рисунке 7 вместо этого изображения проходят через полностью обученную модель классификации персонажей, а 500 функций извлекаются из последнего полностью подключенного слоя. Хотя выполнения t-SNE (итеративного, нелинейного метода уменьшения размерности) достаточно для создания некоторых красивых графиков, t-SNE требует больших вычислительных ресурсов, с временем выполнения O (NlogN) или O (N²) в зависимости от метода. . Таким образом, характеристики изображения сокращаются с помощью PCA (замкнутой формы, метод уменьшения линейной размерности) до 100 компонентов, затем эти компоненты передаются в алгоритм t-SNE для создания представленных ниже визуализаций. Это значительно сокращает время, необходимое для обучения алгоритма t-SNE.

ПРИМЕЧАНИЕ. На этих графиках написано «Цифры», но вместо этого они должны быть «Символами». Графики действительно для всех 36 классов, а не для подмножества.

Одно ключевое замечание, которое стоит обсудить, - это разделимость классов. Сюжеты до и после поразительны. С обученной моделью внутриклассовая изменчивость значительно уменьшается, в то время как межклассовая разделимость умеренно увеличивается. Стоит сравнить этот график из модели с точностью классификации 91% с графиками MNIST t-SNE (из модели с точностью 99,2% только для 10 классов), которые можно найти здесь: https://github.com/kingsman142 / Мнист-классификация . Ясно, что разделимость не идеальна, но для визуализации 36 классов я доволен результатами. Я предполагаю, что в трехмерном графике существует большая разделимость между классами, но это выходит за рамки этого проекта.

ПРИМЕЧАНИЕ: я планировал создать легенды для обоих графиков, отображающих цвет каждого класса, но оказалось, что в matplotlib слишком много работы, чтобы того стоить, и это сделало бы графики некрасивыми, так что я избежал хлопот.

Обсуждение

Предположения

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

  • Линейный шум всегда одинаковой ширины во всех CAPTCHA.
  • Линейный шум всегда меньше ширины штриха символов.
  • Изображения всегда 140x76
  • Круговой шум всегда одинакового радиуса
  • В CAPTCHA используются всего 4 символа.
  • Все изображения набора данных CAPTCHA содержат примерно одинаковый стиль
  • Стиль набора данных CAPTCHA - не самый сложный стиль, который можно найти во всех библиотеках Python или исследовательских работах, генерирующих CAPTCHA.
  • Линейный шум в целом всегда горизонтальный и никогда не вертикальный.
  • Персонажи не выдолблены
  • CAPTCHA - это не 3D
  • Никакие строчные буквы не используются (преобразовали бы проблему в задачу 62 класса)
  • Максимум 2 символа соединяются одновременно
  • Контраст между фоном и буквами достаточно, чтобы пороговое значение было эффективным при предварительной обработке.

Вызовы

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

  • Символы не имеют фиксированной ширины по размеру
  • Символы не имеют фиксированной высоты по размеру
  • Символы не в фиксированном (x, y) месте
  • Персонажи расположены не под фиксированным углом
  • Изображения представлены в формате RGB и значительно различаются по цвету.
  • Изображения имеют круговой шум
  • Изображения имеют криволинейный шум
  • Круговой шум не похож на соль и перец, поэтому удаление с помощью медианного фильтра было бы чрезвычайно простым.
  • Линейный шум не является идеально линейным, поэтому необходимо использовать несколько комбинированных градиентных методов и фильтров, чтобы максимально его устранить.
  • Персонажи имеют разный масштаб (одни больше / меньше других)
  • CAPTCHA содержат всего 4 символа

Возможные исследования

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

  • Помогает ли установка порога на этапе предварительной обработки? Влияет ли значение порога на предварительно обработанные изображения?
  • Помогает ли шумоподавление? Хотя интуитивно понятный ответ - да, что, если вместо этого мы просто наивно разделим символы? Существенно ли снизится точность классификации символов, если набор данных будет достаточно большим?
  • Вредит ли результативность разделение соединенных персонажей пополам по сравнению с альтернативными, более сложными методами?
  • Имеет ли значение белый отступ символа (при разделении сегментированных символов на квадраты)?
  • Что произойдет, если мы полностью сбалансируем набор данных цифр? Он и так довольно сбалансирован, но стоит изучить, не ухудшает ли балансировка точности на 1-2%.

Результаты

В этом отчете была затронута более серьезная задача - разгадывать зашумленные текстовые CAPTCHA. Хотя главной целью были CAPTCHA, была выполнена необходимая подзадача классификации символов, достигнув точности ~ 91%. Однако, несмотря на эту впечатляющую точность, в действительности точность задачи CAPTCHA составляет 44,41%, что примерно на 20% ниже ожидаемого, учитывая точность классификации символов. Несмотря на это, посредством качественной оценки модель показывает улучшение внутриклассовой изменчивости и межклассовой разделимости по сравнению с самими необработанными данными, что является важным и жизненно важным наблюдением.

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

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

Технологии

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

  • Python3
  • PyTorch
  • Scikit-Learn
  • Scipy
  • OpenCV
  • NumPy
  • Матплотлиб

Полный код можно найти здесь: https://github.com/kingsman142/captcha-solver.

Предварительно обученную модель можно найти здесь: https://drive.google.com/open?id=16Vwha7uxy7coe9y-Nkh6skYPW3Kz8xZA. Чтобы использовать его, создайте каталог models / в корневом каталоге проекта и поместите туда модель, а затем следуйте инструкциям в репозитории выше, чтобы оценить модель.