
Я Александр Иванов, техлид команды компьютерного зрения. У любого банка очень много задач, связанных с документами. Особенно важна задача по оцифровке. Ее можно решить, скормив скан мощной мультимодальной LLM, но это работает не всегда. Поэтому мы разрабатываем специализированные решения по оцифровке разных текстов, о чем я и расскажу.
Банковские OCR-задачи
Задача по оптическому распознаванию символов (OCR) заключается в автоматическом извлечении печатного или рукописного текста из изображений. Сюда входит распознавание отдельных символов, их объединение в слова и предложения, а также коррекция ошибок. Результат — текстовая версия информации, представленной на изображении. Что это за задачи:
-
Пользовательские продукты. Например, умная камера: наводите смартфон на текст, и программа распознает платежные реквизиты, или номера телефонов, или реквизиты банковской карты.
-
Внутренний документооборот. Нужно распознавание и классификация входящей корреспонденции, документов со встреч с клиентами: писем, договоров, паспортов, оферт, СНИЛС и так далее (у нас нет отделений и представители выезжают к клиентам для всех операций).
-
Модерация контента в различных источниках, например анализ содержимого мемов.
-
Помощь операторам с вводом данных.
-
Прикладная помощь бухгалтерам, юристам и прочим сотрудникам, работающим с документами.
-
Поиск документов в разных базах данных.
Что касается документооборота, готовых решений для OCR русского текста не то чтобы много, а при высокой плотности текста они работают крайне медленно. Кроме того, некоторые документы имеют строго фиксированную структуру, знание которой позволяет сильно повысить качество распознавания. А в некоторых случаях ошибки распознавания фатальны, поэтому нужно уметь калибровать вероятности. Все это существующие решения не умеют.
Умная камера должна очень быстро работать с видеопотоком, при этом распознавать и печатный, и рукописный текст, желательно одной моделью. А для многообразия разовых задач нам нужна была максимальная гибкость настройки. Open-source для русского языка хорошо решал только задачу по чтению крупного, геометрически не искаженного текста.
Наш типичный конвейер OCR

-
Сканирование исходника.
-
Определение текста (Text Detection). CRAFT-модель на архитектуре DbNet++ предсказывает геометрию блоков текста или информацию об этих блоках.
-
Затем изображение обрабатывают Cropper и LineOCR — аккуратно вырезают все блоки текста и читают их как отдельные строки. Задача по чтению строки текста — это практически сегментация временного ряда. Мы читаем слева направо и считаем, что у нас есть порядок букв, который нужно восстановить.
-
Последний этап — восстановление структуры документа: таблиц, расположения блоков на странице.
Обучение моделей
Мы учим почти целиком на синтетических данных, потому что разметить большое количество реальных документов, указав для каждой буквы или слова координаты, — задача дорогая и трудоемкая.
Детектор текста мы обучаем:
-
на «чистой» синтетике на основе syntext;
-
синтетике на основе различных сложноструктурированных документов из интернета;
-
небольшом количестве реальных данных для тонкой настройки под конкретные задачи, имеющие большое бизнес-значение.
LineOCR учим почти целиком с помощью синтетики: печатных данных, рукописных (полусинтетики на основе прописей и открытых данных), многочисленных открытых, но маленьких наборов кириллических рукописных текстов — всего около 100 тысяч картинок.
Суммарный объем обучающих данных для детектора текста составил:
-
~300 тысяч страниц отрендеренных PDF из интернета;
-
~300 тысяч синтетических данных для документо- и книгоподобного домена;
-
~400 тысяч синтетических данных с текстом in-the-wild: вывесок, надписей, псевдофотографий.
Для Line OCR:
-
~5 млн строк синтетических текстов с различными шрифтами;
-
~400 тысяч печатных строк с внутренних доменов;
-
~300 тысяч рукописных строк с внутренних доменов;
-
~200 тысяч строк из открытых датасетов — как печатных, так и рукописных.
Обнаружение текста
На изображении ниже — результат instance-сегментации моделью на основе CRAFT, которая предсказывает буквы, промежутки между ними и информацию о том, как символы соединены между собой в строки, как строки соединены в абзацы, абзацы соединены в текст.

Хотя мы и обучаем Text Detector видеть все символы в документе и читать их, но еще можем тонко настраивать модель, чтобы она могла видеть только определенные типы текстов, например реквизиты. Это помогает в будущем распознавать не весь документ, а только небольшую его часть, что значительно ускорит работу.
В основном для обучения Text Detector использовали PDF из интернета — это хороший источник данных: там встречается сложная верстка, их можно обогащать, часто есть текстовый слой, который сразу является готовой разметкой. Если бы не несколько но:
-
Не все хотят, чтобы их PDF использовали в любых целях. И некоторые файлы защищены от извлечения текстового слоя: буквы превращаются в набор кривых или все рендерится как одна картинка.
-
Сам PDF — это скан, текстовый слой поверх которого нанесен другой системой OCR. В этом случае мы не учимся читать честный текст, а делаем fine-tune под чужой OCR, обычно не очень хорошего качества. Ситуация крайне неприятная и внезапно достаточно распространенная в рунете.
-
Таблицы так зашиты в структуру PDF, что их тяжело извлечь скриптами. Причина — отсутствие ограниченного формата представления таблиц.
Распознавание текста

LineOCR основана на модификации модели TrOCR для ее использования с CTC loss. Сверточный энкодер принимает на вход картинку и превращает ее в некоторую последовательность токенов. Дальше эта последовательность попадает в transformer-encoder-блок с ограниченным window attention. На выходе из трансформерного блока стоит CTC-декодер.
Мы выбрали CTC-декодер потому, что он давал качество на уровне трансформерных моделей, но в то же время показывал значительно более высокую производительность работы. Кроме того, мы доработали CTC-декодер и соответствующую функцию потерь, и теперь он не допускает дублирования букв. Благодаря этому декодирование происходит очень быстро и можно гарантировать уверенность модели в каждой букве или токене (для нескольких букв). Это позволяет очень хорошо контролировать качество предсказания каждой части слова или фразы. Например, в паспортах клиентов нельзя ошибаться ни в одной букве.
LineOCR обучали на синтетических печатных текстах, рукописных полусинтетических и открытых данных, а еще на небольшом количестве реальных размеченных рукописных текстов из документов внутри банка.
Любопытно, что LineOCR обычно инференсится на сконкатенированных строках: собираем все строки в пакет и просим модель обработать его. На входе может быть сразу несколько миллионов пикселей. Если правильно перепаковать картинки в пакете, склеив различными алгоритмами несколько строк в одну, можно сократить размер входных данных примерно на четверть, а иногда даже в два раза.

На практике переупорядочивание и реорганизация вырезанных строк внутри батча позволяют сильно ускорить инференс, хорошо сочетаются с CTC-декодером на выходе. Из интересного: подобную схему гораздо сложнее заставить качественно работать с трансформерным декодером в силу его авторегрессионного принципа работы.
Теперь нужно преобразовать выход TextDetector для подачи на вход LineOCR. Это довольно важная часть кода, которая должна работать быстро и решать нетривиальную задачу: находить маски слов, аккуратно их вырезать и собирать в батч.

Код, реализующий функционал поиска масок слов, их вырезки и сбора в батч, у нас реализован на операциях scatter и gather. Они очень эффективно работают на видеокартах.
Cropper вырезает обнаруженный с помощью TD текст, собирает из него упакованный в батч вход для LineOCR, выравнивает смятые строки и делает так, чтобы вход LineOCR был линейным. Работает исключительно на GPU.
Восстановление структуры документа
Прочитав все слова, нужно понять, в каком порядке читать текст. Если он линейный, то есть один документ и все упорядочено сверху вниз, сложностей никаких. А если текст нелинейный?

Когда структура документа более сложная, нужно упорядочить строки. У нас это реализовано с помощью классического алгоритма — топологической сортировки. Заключается он в том, что для двух строк иногда можно явно сказать, какая из них совершенно точно должна читаться после другой. Если ввести такой частичный порядок и написать несколько геометрических правил, можно получить порядок слов, который более-менее похож на то, как человек глазами проходит по тексту. Становится очевидно, как собираются абзацы. Иногда заголовки в верстке встают не на свои места, но, так как у них больше шрифт, их можно несложными эвристиками доставать и переносить вверх. А самое главное, что автоматически восстанавливаются колонки.
Алгоритм довольно быстрый. С его помощью можно из собранных в интернете PDF извлечь разметку для обучения TD. Большинство же проблем с качеством его работы без проблем решаются последующими за OCR NLP-моделями.
Оптимизация инференса
После всех инженерных трюков у нас получилось решение, эффективно работающее как на серверах, так и на мобильных устройствах. Вот пример профилирования инференса картинки плотного текста А4:

Эти результаты получены на RTX 3090. Первая часть — декодирование картинки. Она может быть в формате JPEG, PNG, BMP, GIF, PDF. Ее нужно превратить в тензор пикселей. Задача нетривиальная и может занимать довольно много времени. Вполне может быть картинка, которая декодируется 100 миллисекунд, — фотография на много мегапикселей. Картинки среднего размера отрабатываются за 20 миллисекунд, и оптимизировать эту часть практически невозможно. Есть декодеры на GPU, есть — на CPU. На GPU может быть раза в 1,5 быстрее, принципиального выигрыша нет.
TextDetector — это сегментационная модель, работающая с разрешением 1600 × 1600, на базе EfficientNet с головой DbNet++. Легкая модель примерно на 8—10 млн параметров, обрабатывает в среднем за 35 миллисекунд.
Дальше идет наш магический GPU-код. Он отрабатывает примерно за 10—13 миллисекунд, восстанавливая геометрию всех блоков и собирая вход для LineOCR.
LineOCR отрабатывает примерно за 0,4 миллисекунды на строку, но она работает пакетно, поэтому документы размером А4 обрабатываются в среднем за 16 миллисекунд на RTX 3090. Для устройств вроде iPhone 15 Pro или Google Pixel 7 длительность можно умножать примерно втрое.
Аугментации
Хочется обучать модели быстро, поэтому пришлось написать свой набор аугментации (геометрических искажений бумаги), который, может быть, даже когда-нибудь опубликуем. Аугментации должны работать на GPU, потому что мы учим хотя и легкую модель, но на картинках большого разрешения — 1600 × 1600 и больше. Процессору такое лучше не передавать.

Адаптация под задачи
При возникновении явных проблем в каком-либо домене мы можем добавлять в обучающий набор похожие синтетические данные, потому что информации о различных доменах в train-выборке TD и LineOCR нужно довольно много.
У нас нет повторения токенов в процессе СТС-декодирования, поэтому декодер, хорошо калибрующийся на вероятности, приятен в управлении, хотя работает незначительно хуже, чем трансформенный декодер. Здесь можно ограничивать алфавиты, контролировать грамматику, запускать маленькие n-граммные языковые модели.
И можно заставлять TD маскировать текст, находя только нужный, тем самым резко сокращая все последующие стадии обработки.
Взаимодействие с другими моделями
Разумеется, наша система будет взаимодействовать с другими моделями. В основном выход LineOCR предназначен либо для человека, либо для передачи в NLP-модели. А при значительном дообучении LineOCR разные модели ведут себя по-разному.
Например, если это очень простые классические NLP-модели, работающие на токенах, им почти все равно на порядок слов, макет документа, выпадение отдельных предлогов и союзов. Если это NLP на старом DL (FastText, ELMO), их embedding часто ломаются от изменения макета документа. С более-менее современными NLP-моделями типа Bert, T5 или простенькими LLM все зависит от удачи и степени повреждения текста в процессе распознавания. Изменение порядка слов или грамматические опечатки их обычно не слишком беспокоят. А с большими LLM мы сейчас набираем статистику работы, но в среднем они не очень чувствительны к выходу LineOCR, и проблема достаточно неплохо решается за счет промпт-инжиниринга.
Советы тем, кто пойдет по нашему пути
-
Не гонитесь за использованием SOTA и больших моделей. Связка из нескольких простых моделей прекрасно работает, не давая значительных просадок в качестве.
-
Все решают данные. Архитектура почти не важна, когда параметров меньше 200 млн.
-
Нужны генераторы качественной синтетики. На них нужно выделять очень много времени.
-
Нужны очень разнообразные аугментации данных.
-
Нужны данные из разных доменов. Чем шире, тем лучше.
Автор: inkln