Содержание
Когда говорят «OCR», обычно представляют печатный текст: договор, счёт, скан паспорта. Рукописные цифры в ячейке таблицы — другая задача: другой шум, другой алфавит, другая постановка. Одна картинка может означать класс 7, а может — строку 783. От выбора формулировки зависит, нужен ли вам простой классификатор, CRNN с CTC или полноразмерный TrOCR.
Ниже — практический разбор для инженеров, которые строят распознавание на реальных фотографиях бланков, а не на датасете MNIST. Мы пройдём путь от геометрии документа до сравнения архитектур, разберём, как получить разметку из базы данных, и покажем, почему самая большая модель редко оказывается лучшим первым шагом. Материал дополняет контур OCR в загрузке документов для RAG — там фокус на корпоративных PDF, здесь — на узкой, но типичной задаче полевого ввода.
Ключевые выводы
OCR — не одна задача. Классификация одной цифры и распознавание последовательности требуют разных моделей, метрик и данных.
Структура документа важнее нейросети. Если шаблон бланка известен, сначала выравниваете геометрию и режете ячейки — а не скармливаете целую страницу универсальному OCR.
Лучший датасет — production-подобный. Высокая точность на MNIST не переносится на фотографии с перспективой, тенями и линиями таблицы.
Разметка из базы может быть ценнее ручной. Когда фото связано с записью в системе, ground truth уже есть — остаётся построить конвейер cell image → label.
CNN — сильный baseline. Для одной цифры в известной ячейке маленькая сверточная сеть часто быстрее, дешевле и понятнее, чем Transformer.
CRNN + CTC — естественный шаг для последовательностей. Несколько цифр в ячейке без посимвольной разметки — классическая постановка Connectionist Temporal Classification.
TrOCR оправдан не всегда. Универсальная модель имеет смысл при сложном тексте и расширении алфавита; для десяти цифр в фиксированной ячейке специализированное решение часто выигрывает.
Production — это пайплайн. Детекция документа, коррекция перспективы, правила валидации и human-in-the-loop не менее важны, чем выбор архитектуры.
Две разные задачи под одним словом «OCR»
Прежде чем сравнивать CNN и TrOCR, нужно зафиксировать, что именно вы распознаёте.
Классификация изображения
Одна обрезанная ячейка содержит одну цифру: картинка [ 7 ] → класс 7. Это классификация на 10 классов (0–9). Модель выдаёт распределение вероятностей по фиксированному набору меток. Длина ответа всегда равна одному символу.
Такая постановка возможна, когда шаблон бланка гарантирует: в конкретном поле всегда одна цифра, или вы заранее режете область так, что в кадре остаётся один символ.
Распознавание последовательности
Другая ячейка содержит несколько цифр: [ 7 8 3 ] → строка "783". Модель должна определить символы, их порядок и количество. Количество классов на выходе больше не фиксировано одним числом — нужен механизм декодирования последовательности.
Это принципиально другая задача. Классификатор, обученный на одиночных цифрах, не «додумает» порядок трёх символов без отдельного этапа сегментации. А сегментация рукописных цифр на реальном бланке — отдельная головная боль.
Почему MNIST — плохой прокси для реальных бланков
MNIST — 60 000 обучающих изображений рукописных цифр, 10 классов, одна цифра на картинку, нормализованный размер 28×28, контролируемые условия съёмки. Для обучения основ computer vision датасет отличный. Для обещаний заказчику — опасный.
Реальные фотографии заполненных бланков добавляют факторы, которых в MNIST нет:
| Фактор | Что ломает |
|---|---|
| Перспектива и поворот | Цифра искажена, пропорции не совпадают с обучающей выборкой |
| Неравномерное освещение | Тени, блики, локальный контраст |
| Сетка таблицы | Линии пересекают штрихи цифр |
| Фон бумаги | Текстура, пятна, сгибы |
| Разная толщина пера | Тонкие и жирные штрихи в одном датасете |
| Разные почерки и размеры | Один и тот же класс 3 выглядит по-разному |
| Качество камеры | Шум, размытие, сжатие JPEG |
Это domain gap: модель, натренированная на MNIST, может показать 99% accuracy на тесте датасета и провалиться на фотографиях с телефона оператора.
Главный принцип прост: лучший датасет для вашей задачи — данные, максимально похожие на production. Если в проде — фото бланков с полей, тренировочная выборка должна состоять из таких же фото, а не из нормализованных глифов 28×28.
Первый этап OCR — не нейросеть
Типичная ошибка — сразу искать «лучшую OCR-модель» и скармливать ей целую фотографию. На структурированных бланках выигрывает другой порядок:
фотография → обнаружение бланка → коррекция перспективы →
привязка к шаблону → выделение ячеек → предобработка → OCR
Почему известный шаблон — огромное преимущество
Если форма бланка фиксирована:
- координаты полей известны заранее или вычисляются после выравнивания;
- строки и столбцы таблицы предсказуемы;
- не нужно распознавать всю страницу целиком.
Вместо фотография → большая OCR-модель → весь текст работает схема фотография → geometry → cell → маленькая OCR-модель. Каждая ячейка — отдельный вход с маленьким алфавитом и ограниченной длиной. Задача упрощается на порядок.
Это тот же инженерный приём, что и в промышленной загрузке документов: сначала структура и канон, потом извлечение смысла. Для бланков «канон» — выровненное изображение с вырезанными ячейками.
OCR только там, где он нужен
Универсальный OCR по всей странице тратит вычисления на заголовки, подписи, печатный текст и пустые поля. На фиксированном шаблоне разумнее:
- Найти углы бланка (контуры, маркеры, QR).
- Применить гомографию — выровнять перспективу.
- Наложить маску шаблона.
- Вырезать только ячейки с рукописными показаниями.
- Запустить узкую модель на каждой ячейке.
Самая ценная часть — ground truth
Без честной разметки любой эксперимент с архитектурами — сравнение шума.
Откуда взять правильные ответы
Во многих прикладных системах фотография бланка уже связана с записью в базе. Оператор сфотографировал показания счётчика, а правильное значение лежит в таблице readings. Или диспетчер ввёл данные вручную после звонка — и фото служит подтверждением.
В таком случае разметка не требует армии разметчиков. Нужен конвейер:
фотография → идентификация бланка/сессии → запрос в БД →
правильное значение поля → пара (cell image, label)
Примеры:
cell_00001.png → "78"cell_00002.png → "73"cell_00003.png → "71"
Почему это лучше ручной разметки
- Масштаб: тысячи примеров собираются автоматически по мере работы системы.
- Меньше ошибок: человек не переписывает цифры вручную в отдельный файл.
- Актуальность: датасет обновляется вместе с потоком документов.
- Связь с бизнес-контекстом: метки соответствуют тому, что система считает истиной.
Оговорка: ground truth из БД честен только если запись в базе действительно верна. Если операторы систематически ошибаются при вводе, модель выучит те же ошибки.
Data leakage — тихий убийца метрик
При разбиении train/validation/test нельзя случайно смешивать:
- разные кропы одного и того же бланка;
- фотографии одного человека с почти идентичным почерком;
- серии снимков, сделанных в одинаковых условиях освещения.
Случайное перемешивание без группировки даст оптимистичные 98% на тесте и разочарование в проде. Группируйте по документу, автору, сессии съёмки или дате — в зависимости от того, что реально «новое» в production.
Подход к оценке качества ИИ-систем в целом — в материале «Оценка корпоративного ИИ»; для OCR критичны именно групповые сплиты и метрики на уровне последовательности.
Первая модель — CNN как baseline
Convolutional Neural Network — сверточная нейронная сеть. Для задачи «одна цифра в ячейке» это естественная отправная точка.
Как CNN видит цифру
Иерархия признаков типична:
- Нижние слои — края, линии, углы.
- Средние — фрагменты штрихов, дуги, пересечения.
- Верхние — целый глиф, похожий на класс.
Схема: изображение → свёртки → пулинг → признаки → полносвязный слой → 10 классов.
Почему CNN — хороший baseline
| Критерий | CNN |
|---|---|
| Размер модели | Десятки–сотни КБ – несколько МБ |
| Время обучения | Минуты–часы на GPU, часы на CPU |
| Inference | Миллисекунды на ячейку |
| Отладка | Confusion matrix, визуализация ошибок |
| Деплой | Легко упаковать в ONNX, TFLite, edge |
На первом эксперименте измерьте не только accuracy, но и confusion matrix, время inference, размер артефакта. Эти цифры станут аргументом против «давайте сразу TrOCR» на совещании.
Минимальный эксперимент
# Псевдокод: классификатор 10 классов
model = Sequential([
Conv2D(32, 3, activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, activation='relu'),
MaxPooling2D(),
Flatten(),
Dense(128, activation='relu'),
Dense(10, activation='softmax'),
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
Для production добавьте нормализацию размера ячейки, инверсию при тёмном фоне и логирование уверенности (softmax probability).
Несколько цифр в одной ячейке
Ячейка [ 78 ] ломает постановку «один класс на картинку». Наивный путь:
78 → сегментация → 7 | 8 → два прогона CNN → "78"
Почему сегментация — отдельная проблема
- Цифры разного размера в одной ячейке.
- Расстояние между символами непостоянно.
- Символы соприкасаются или перекрываются.
- Линии таблицы проходят через штрихи.
- Почерк не соблюдает моноширинную сетку.
Каждый из этих факторов добавляет этап, который сам по себе требует разметки и метрик. Проще спросить: можно ли распознать всю последовательность сразу? Здесь появляется CTC.
CTC: последовательность без посимвольной разметки
Connectionist Temporal Classification учит соответствие между изображением и строкой без разметки границ каждого символа. Достаточно пары целая ячейка → "783".
Интуиция
Модель на каждом временном шаге выдаёт распределение по символам плюс специальный токен blank (пустой шаг). Декодер CTC схлопывает повторы и убирает blank:
сырой выход: 7 → 7 → blank → 8 → 8 → 8 → blank → 3
после CTC: 7 → 8 → 3
Вам не нужно вручную рисовать bounding box вокруг каждой цифры.
Ограничения CTC
- Последовательность обычно моделируется слева направо (для цифр в ячейке это чаще всего ок).
- Двумерная структура текста (многострочные блоки) CTC переносит хуже, чем seq2seq с attention.
- Длина входной последовательности признаков должна быть сопоставима с длиной строки — для коротких чисел в ячейке ограничение редко мешает.
CRNN — классический рабочий конь OCR
Convolutional Recurrent Neural Network объединяет свёртки, рекуррентные слои и CTC:
image → CNN → feature map → sequence → BiLSTM → CTC → "783"
Зачем RNN между CNN и CTC
Текст имеет порядок: 7 затем 8 затем 3. Feature map после CNN можно разрезать на полосы слева направо; каждая полоса — шаг времени. BiLSTM смотрит в обе стороны и уточняет, какой символ вероятен с учётом соседей.
Плюсы и минусы
| Плюсы | Минусы |
|---|---|
| Компактная модель | RNN медленнее параллельных Transformer на длинных последовательностях |
| Хорошо изучен, много рецептов | Сложнее тюнить, чем простой CNN |
| Работает на CPU для коротких строк | Хуже масштабируется на свободный текст |
| Прямая тренировка end-to-end с CTC | Менее гибок при смене алфавита, чем generative decoder |
Для ячеек с двумя–пятью цифрами CRNN + CTC остаётся сильным выбором в 2026 году — особенно если важны размер модели и inference на CPU.
TrOCR — когда в игру входит Transformer
TrOCR (Transformer-based OCR) — end-to-end модель: Vision Transformer кодирует изображение, Transformer decoder генерирует текст посимвольно.
image → ViT encoder → visual tokens → text decoder → "783"
CRNN против TrOCR
| CRNN + CTC | TrOCR | |
|---|---|---|
| Энкодер | CNN | Vision Transformer |
| Декодер | CTC (выравнивание) | Autoregressive Transformer |
| Предобучение | Обычно с нуля на своих данных | Доступны веса на печатном/рукописном тексте |
| Размер | Малый–средний | Small / Base / Large — от десятков МБ до сотен |
| Сильная сторона | Короткие цифровые строки | Сложный текст, смешанный алфавит |
Fine-tuning pretrained
Типичный рецепт: взять microsoft/trocr-base-handwritten или аналог, дообучить на своих ячейках с цифрами. Pretrained знания о штрихах и кривых переносятся; последние слои адаптируются к вашему домену — линиям таблицы, шуму камеры, локальному почерку.
Важно: больше параметров ≠ лучше на вашей выборке. Large-модель может переобучиться на тысяче ячеек, тогда как Small даст ту же CER при inference в десять раз быстрее.
Почему TrOCR может быть избыточен
Главный тезис: если задача формулируется как image → "7" в известной ячейке с алфавитом из десяти цифр, полноразмерный OCR-Transformer — стрельба из пушки по воробьям.
TrOCR становится интереснее, когда:
- в ячейке переменная длина (
7835,12,0); - алфавит расширяется (буквы, десятичная точка, знак минус);
- качество съёмки сильно варьирует и pretrained даёт заметный перенос;
- планируется единая модель на цифры и короткий текст в соседних полях.
Если же:
- шаблон фиксирован;
- алфавит — только
0–9; - длина ограничена (например, не больше пяти символов);
- область текста вырезана геометрией,
то специализированная CRNN или даже каскад CNN часто выигрывает по latency, стоимости и простоте сопровождения.
Эксперимент: CNN vs CRNN vs TrOCR на одном датасете
Сравнение архитектур имеет смысл только на одинаковых данных и с честным сплитом.
Разбиение выборки
Типичное соотношение: 70% train, 15% validation, 15% test — но внутри групп:
- по
document_id(все ячейки одного бланка — в одном сплите); - по
author_id/ оператору; - по серии бланков или дате съёмки.
Случайное перемешивание ячеек без группировки завышает метрики.
Метрики
Для одной цифры (CNN):
- Accuracy;
- Precision / Recall по классам (особенно для редких
0и1); - Confusion matrix.
Для последовательности (CRNN, TrOCR):
- CER (Character Error Rate) — доля неверных символов;
- Exact Match Accuracy — доля полностью верных строк;
- Sequence Accuracy — то же, с явным именем в отчётах.
Инженерные метрики (часто решают спор на проде):
| Метрика | Зачем |
|---|---|
| Размер модели | Деплой на edge, мобильное приложение |
| Время inference на CPU/GPU | Пропускная способность на смену |
| VRAM / RAM | Батч на сервере |
| Throughput | Ячеек в секунду на линии |
| Время обучения | Как быстро переобучить после смены бланка |
Зафиксируйте железо и batch size в отчёте — иначе сравнение бессмысленно.
Анализ ошибок важнее одной цифры accuracy
accuracy = 98.7% звучит хорошо, пока не откроете confusion matrix. Типичные пары путаницы на рукописных цифрах:
1 ↔ 73 ↔ 85 ↔ 60 ↔ 64 ↔ 9
Что смотреть кроме матрицы
- Почерк: какие операторы или контрагенты дают худший CER;
- Инструмент письма: гелевая ручка vs карандаш;
- Освещение: блик на ламинированном бланке;
- Размер цифр: мелкий почерк в узкой ячейке;
- Артефакты съёмки: смаз, обрезанный край ячейки.
Соберите папку hard examples — 50–200 ячеек, где все модели ошибаются. Дообучение на hard set или ручная проверка этих кейсов часто даёт больше, чем смена Large на Base.
Цикл улучшения:
обычные примеры → ошибки на val → hard examples →
аугментация / дообучение / правила → повтор
Наблюдаемость такого конвейера — логирование уверенности, сохранение кропов с низким score, дашборд CER по полю и по оператору — пересекается с практиками из AI observability.
Data augmentation: имитация камеры, не фантазия
Аугментация помогает при нехватке данных, если она имитирует реальные условия съёмки:
- небольшой поворот и сдвиг;
- масштаб;
- изменение контраста и яркости;
- гауссов шум;
- лёгкое размытие;
- небольшая перспективная деформация;
- случайная обрезка с сохранением цифры в кадре.
Плохая аугментация — та, которой нет в production: экстремальные искажения, инверсия цвета без смысла, наложение случайных линий не там, где бывают линии таблицы.
Если линии таблицы — главный источник ошибок, лучше вырезать их на этапе предобработки (морфология, цветовая маска, inpainting линий), чем надеяться, что модель «привыкнет».
Semi-supervised learning и human-in-the-loop
Когда baseline уже работает, можно замкнуть цикл:
уверенное распознавание → pseudo-label → новые training samples → переобучение
Confidence threshold
Если confidence > threshold (например, 0.95 для CNN или низкий CER beam search для CRNN), пример добавляется в пул с псевдоразметкой. Ниже порога — в очередь человеку.
Human-in-the-loop
model → confidence → high: accept / low: human review → БД
Это снижает стоимость разметки и не даёт модели тихо деградировать: доля ручных проверок — метрика здоровья системы.
Осторожно с feedback loop: если модель систематически путает 5 и 6, псевдоразметка усилит ошибку. Периодически подмешивайте верифицированные человеком примеры и мониторьте confusion matrix на свежем потоке.
Production pipeline: больше, чем нейросеть
Итоговая цепочка для структурированного бланка:
photograph → document detection → perspective correction →
template alignment → cell extraction → preprocessing →
OCR model → confidence → validation rules → database
Где работают детерминированные правила
Нейросеть не должна решать то, что задано бизнес-логикой:
- поле допускает только цифры;
- значение в диапазоне
0–100; - фиксированное количество знаков (например, показание счётчика — ровно пять цифр);
- контрольная сумма или повторное чтение соседнего поля.
Если OCR вернул 783 для поля с максимумом 100, правило отклоняет результат независимо от softmax.
Каскад моделей
Не обязательно выбирать одну архитектуру:
быстрый CNN → уверен? → accept
↓ нет
CRNN или TrOCR → уверен? → accept
↓ нет
human review
Почему это часто лучше одной большой модели:
- дешевле в среднем на ячейку;
- быстрее на «лёгких» примерах;
- проще масштабировать нагрузку;
- сложные случаи получают больше вычислений, а не все подряд.
Что выбрать для реальной системы
| Задача | Рекомендация |
|---|---|
| Одна цифра в ячейке | CNN |
| Несколько цифр, свой датасет | CRNN + CTC |
| Смешанный текст, расширение алфавита | TrOCR fine-tune |
| Фиксированный бланк | Geometry + узкая модель на ячейку |
Архитектурная карта в одном блоке:
ОДНА ЦИФРА → CNN
НЕСКОЛЬКО ЦИФР → CRNN + CTC
СЛОЖНЫЙ ТЕКСТ → TrOCR
СТРУКТУРИРОВАННЫЙ → Document AI + geometry + OCR на ячейках
ДОКУМЕНТ
Что эта задача говорит о выборе нейросетей
Не начинайте с вопроса «какая сейчас самая мощная модель?». Начинайте с «какова структура моей задачи?»
- 10 классов, одна цифра — не нужен generative OCR.
- Последовательность символов без посимвольных box — CTC / CRNN.
- Сложный текст и перенос pretrained — Transformer-модели вроде TrOCR.
- Известный шаблон — Document AI на уровне геометрии снимает половину ML-сложности.
Архитектура должна следовать структуре информации, а не популярности модели в ленте.
Что сделать сегодня
- Зафиксируйте постановку: одна цифра или строка? Какой максимальной длины?
- Нарисуйте пайплайн до ML: выравнивание бланка и нарезка ячеек в приоритете.
- Проверьте источник разметки: можно ли связать фото с БД?
- Обучите CNN baseline и сохраните confusion matrix.
- Если в ячейке несколько цифр — CRNN + CTC на тех же кропах.
- Сравните с TrOCR Small только после честного сплита по документам.
- Введите порог уверенности и очередь ручной проверки до «полного автомата».
Дальнейшее чтение
- Загрузка документов в RAG: PDF, таблицы, OCR — корпоративный контур распознавания и версий документов.
- Промышленная инженерия RAG — куда попадают извлечённые поля после OCR.
- Оценка корпоративного ИИ — метрики, сплиты, регрессии качества.
- AI observability — логирование уверенности и мониторинг деградации.
- Chartographer и VLM для графиков — соседний класс задач «картинка → структурированный смысл».
FAQ
Можно ли начать с готового Tesseract?
Да, как с очень грубым baseline на вырезанных ячейках. Tesseract рассчитан на строки текста; на одиночных рукописных цифрах в ячейке с линиями сетки часто проигрывает маленькой CNN. Имеет смысл для быстрой проверки «есть ли сигнал в данных», но редко как финальное решение для полевых бланков.
Сколько данных нужно для CNN?
Порядок величины: от нескольких сотен примеров на класс для простого домена до нескольких тысяч, если почерк сильно разнороден. Без аугментации и без группового val/test цифры будут обманчивыми.
CTC работает для пустой ячейки?
Нужен явный класс «пусто» или отдельный детектор пустоты до OCR. Иначе модель будет «галлюцинировать» цифры на шуме и линиях таблицы.
TrOCR Base или Small для тысячи ячеек?
Начните со Small: быстрее итерации, меньше риск переобучения. Base имеет смысл, если Small упирается в CER на validation, а не на train.
Как связать OCR с ERP?
После валидации поля пишите в API ERP или в промежуточную очередь с идемпотентным ключом document_id + field_id. Паттерны интеграции — в ERP integration.
Нужен ли GPU в production?
Для CNN на одной ячейке — часто нет, CPU хватает. Для батча TrOCR на сервере — желательно. Каскад «CNN на CPU → TrOCR на GPU для сложных» — типичный компромисс.
Как не смешать train и test одного бланка?
Храните document_id в метаданных каждого кропа и используйте GroupKFold или явный сплит по списку документов. Любой случайный train_test_split без групп — красный флаг.
Что делать, если accuracy высокая, а пользователи жалуются?
Смотрите ошибки на репрезентативном тесте (новые люди, новые условия съёмки), а не на случайных кропах. Высокая accuracy на «лёгком» тесте при плохом опыте в поле — классический симптом leakage или смещённой выборки.

