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

Результирующая программа выглядит следующим образом:

import System . Environment
import Captcha . Leprosorium . Recognizer

process [ infile ] = do
code <- recognizeLeprosoriumCaptcha infile
putStrLn $ «RESULT: » ++ code

process _ = do
prog <- getProgName
putStrLn $ «Usage: » ++ prog ++ » +RTS -N -RTS captcha.png»

main = do
args <- getArgs
process args

… где функция recognizeLeprosoriumCaptcha определена так:

module Captcha . Leprosorium . Recognizer where

import Captcha . Leprosorium . Recognizer . Preprocess
import Captcha . Leprosorium . Recognizer . GeneticAlgorithm
import Captcha . Leprosorium . Recognizer . Compress ( cropAndCompress )
import Captcha . Leprosorium . Recognizer . NeuralNetwork
import Codec . Picture
import Codec . Picture . RGBA8

recognizeLeprosoriumCaptcha :: FilePath -> IO String
recognizeLeprosoriumCaptcha infile = do
Right dynimg <- readImage infile
wbimg <- imageToWhiteAndBlack $ fromDynamicImage dynimg
img <- removeNoise wbimg
( letters , _ ) <- splitImageOnLetters img
let letters’ = map cropAndCompress letters
return $ map recognizeLetter letters’

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

Так насколько это работает? Прогон на 3112 капчах показал, что из них полностью правильно распознается 106, то есть, примерно 3.4%. Или 1 капча из 29.36, против 1 из 11 миллионов, если просто угадывать. Много это или мало? Оказывается, что в случае с капчами 3.4% — это на самом деле очень много. Можно сказать, что угадывание семизначного числа было сведено к угадыванию числа от 1 до 30. Совсем неплохо, учитывая, что задачу я решал, что называется, «в лоб», и потратил на поиск решения всего лишь пару вечеров. На практике правильное распознавание капчи даже в 0.1% случаев уже означает, что капча сломана.

Почему так?

Допустим, мы написали робота, рассылающего спам по форумам. Пускай он работает в 100 потоков, каждый из которых размещает по одному сообщению на форуме в секунду. При правильном распознавании капчи в 3% случаев робот будет размещать по 3 сообщения каждую секунду. Что же касается оставшихся 97 неудачных попыток, робот просто вернется к этим форумам чуть позже.

Другой пример. Мы собрали базу из 10 миллионов пользователей некоторого крупного почтового сервиса и решили попробовать зайти под каждым из этих пользователей, используя какой-нибудь распространенный пароль, скажем, «qwerty». Если не ошибаюсь, это называется обратный брутфорс. Не во всех сервисах предусмотрена адекватная защита от него. Так вот, правильно угадывая капчу в 3% случаев мы успешно совершим 300 000 попыток входа, что не так уж и мало. К тому же, мы не ограничены в количестве повторных попыток.

Ну хорошо, а что делать, если внутри нас с вами сидит маленький перфекционист и говорит, что 3% — это слишком мало? Какой точности распознавания вообще можно добиться при помощи нейронных сетей? Рассмотрим стандартный dataset от MNIST с изображениями рукописных цифр. Нейронные сети без особого труда распознают этот набор с точностью 95% и более (напоминаю, мы получили всего лишь 55%). А ансамбль из 25 сетей 784-800-10 при дополнительной обработке входных данных достигает точности 99.61%.

Чтобы добиться более высокой точности, неплохой идеей, по всей видимости, будет более точно определять границы символов при нарезании картинки на буквы. Используя центр масс и процентили или еще как-то. Можно попробовать использовать сети свертки ( хабр ). Также у Хайкина описано несколько способов усилений нейронных сетей — усреднение по ансамблю и усиление за счет фильтрации. В частности, рекурсивное применение последнего приема позволяет сделать из сети, работающей чуть лучше, чем простое угадывание, сеть со сколь угодно малой ошибкой распознавания. Также можно поэкспериментировать с обучением отдельных сетей для каждой буквы и/или разбиением изображения на 4-9 частей и обучением отдельной сети для каждой этой части, с дальнейшим объединением выходов полученных сетей. В общем, можно распознавать капчи с куда большей точностью, чем 3%. Все зависит только от количества времени, которым вы располагаете, и желания перебирать варианты.

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

Всего-навсего задавшись вопросом «интересно, а как можно написать программу, распознающую капчу» я:

Могу ошибаться, но, кажется, я интересно и с пользой провел время. Воистину, взлом капчи оказался замечательным code kata.

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

На этом у меня все. Как всегда, если у вас есть вопросы или дополнения, не стесняйтесь пользоваться комментариями.

EnglishRussianUkrainian