← Все статьи

Компактная CRNN для замеров УЗТ: 640 тысяч параметров и пустые клетки

Полевые заметки: локальная CRNN на кропах толщины, CTC, 640 тыс. параметров, конфликт пустых клеток и почему Transformer не первый шаг.

Компактная CRNN для замеров УЗТ: 640 тысяч параметров и пустые клетки
Содержание

На бланке ультразвукового контроля толщины (УЗТ) толщина стенки — это не «текст вообще», а короткое рукописное число в уже вырезанной клетке: 12,3, 8,0, иногда пусто. Ошибка в десятых уходит в учётную систему предприятия (ERP). В пилоте мы проверяем, насколько далеко можно уехать маленькой свёрточно-рекуррентной сетью на таких кропах — без облака, без универсального трансформера и без обещания «готово к цеху».

Это продолжение полевых заметок, не замена соседних статей. Контур всего листа — геометрия, модель зрения, оценка надёжности, отчёт оператора — разобран в «почему одной модели мало». Общая механика «цифра против последовательности» и когда оправдан TrOCR — в разборе от свёртки до трансформера. Здесь — конкретная голова MeasurementCrnn из пилота: вход 1 × 48 × 160, алфавит 0123456789,, порядка 640 тысяч обучаемых параметров, и урок, который сеть получила на пустых клетках.

Репозиторий закрытый; публичный контур — на странице портфолио. Ниже нет сырых бланков заказчика. Цифры прогонов — измерения пилота, не норматив приёмки.

Ключевые выводы

Это не универсальное распознавание. Шаблон известен, клетка уже вырезана, алфавит узкий. Универсальная модель на весь лист здесь лишняя как первый шаг.

CRNN — не конкурент свёртке, а свёртка плюс последовательность. CNN достаёт штрихи, двунаправленная LSTM держит порядок цифр, CTC снимает разметку границ каждого символа.

Около 640 тысяч параметров хватает на рабочий базовый уровень. Большая доля весов сидит в рекуррентной части, а не в «огромном кодировщике».

Полное совпадение жёстче точности по символам. Четыре верные цифры из пяти дают высокую посимвольную оценку и неверную толщину.

Пустая клетка — отдельная задача. Одна голова, которая одновременно решает «есть ли число» и «какое именно», снижает ложные срабатывания ценой качества заполненных полей.

Не нужна модель на каждое поле. Делить следует по типу визуальной задачи: замер, печатная дата, печатный текст, рукописный комментарий, пусто/заполнено.

Трансформер подключаем как запасной контур, если компактный предел измерен и не закрывает требования — не как стартовый стек.

Известная геометрия — не универсальное распознавание

В пилоте бланк уже разбирается локальным компьютерным зрением (CV, Computer Vision): перспектива, таблица, карта ячеек, кропы. К моменту распознавания замера мы чаще всего смотрим не на фотографию листа, а на маленький прямоугольник «здесь должно быть число или пусто».

Это меняет постановку. Универсальное оптическое распознавание символов (OCR) обязано найти текст где угодно. У нас координаты поля известны заранее. Значит, можно собрать узкую систему: кроп → пусто или заполнено → распознать содержимое → проверить диапазон толщины → структурированное поле. Одну сеть не заставляем одновременно искать таблицу, читать фамилию и угадывать десятые.

flowchart TB
  form[UT_form]
  form --> layout[Layout_and_crop]
  layout --> empty[Empty_or_value]
  empty -->|empty| skip[No_OCR]
  empty -->|value| crnn[Measurement_CRNN]
  crnn --> rules[Range_and_format]
  rules --> json[Structured_field]

Печатные колонки (зона, диаметр, тип элемента) и шапка — другие визуальные задачи. Их закрывают печатный OCR и отдельные правила, а не расширение алфавита замерной головы. Тот же принцип «сначала геометрия, потом модель» уже стоит в системной статье про бланк; здесь он сужается до одной клетки замера.

Почему сначала компактная модель, а не трансформер

В пилоте уже есть облачные модели зрения на вырезанных фрагментах. Они полезны как сильный, но платный контур. Компактная CRNN отвечает на другой вопрос: можно ли читать замер внутри предприятия, на обычном процессоре, без отправки бланка во внешний API, с предсказуемым размером артефакта и своей версией весов.

Мы не утверждаем, что свёрточно-рекуррентная сеть «лучше трансформера вообще». Утверждение уже: сначала измеряется простой специализированный вариант. Архитектуру усложняют, когда базовый уровень упёрся в потолок, а не потому что «в 2026 так принято». Для узкого алфавита и короткой строки это честнее, чем сразу арендовать GPU под дообучение TrOCR.

Локальный контур даёт сопровождаемость: чекпоинт лежит рядом с кодом, алфавит зафиксирован, входной размер известен. Платить за каждый запрос не нужно. Цена — сами данные и дисциплина экспериментов. Это ближе к инженерии наборов, чем к выбору «самой современной» сети.

Свёртка, двунаправленная память и выравнивание в одной голове

Свёрточная сеть (CNN, Convolutional Neural Network) ищет визуальные признаки: сначала линии и штрихи, затем изгибы, затем формы, похожие на цифры. Это не «компактная нейросеть в маркетинговом смысле»: компактность в нашем тексте — про размер модели, CNN — про свёртку.

Одной классификации картинки мало. Замер — последовательность: 5 → 8 → 3 → 2 → 1, часто с запятой. Сеть должна помнить, что уже прочитала слева, и не перепутать порядок. Здесь появляется LSTM (Long Short-Term Memory): рекуррентный слой, который пропускает уже увиденные шаги в текущий. В коде bidirectional=True, поэтому память смотрит в обе стороны: будущая цифра помогает разобрать предыдущую, если почерк сомнительный.

CRNN (Convolutional Recurrent Neural Network) в пилоте — это как раз связка:

кроп
  → CNN (признаки)
  → BiLSTM (порядок)
  → Linear (12 классов)
  → CTC
  → "12,3"

CTC (Connectionist Temporal Classification) позволяет не размечать границы каждой цифры. Достаточно пары «картинка клетки → строка». На каждом шаге времени сеть выдаёт распределение по символам плюс служебный blank. Декодер схлопывает повторы и выкидывает пустые шаги: 5 5 blank 8 8 blank 3 становится 583. В коде BLANK_INDEX = 0, функция ошибки — CTCLoss. На маленькой выборке сеть иначе быстро учится предсказывать одни пробелы: поэтому смещение класса blank заранее сдвинуто в отрицательную сторону (-2.0), чтобы цифры вообще имели шанс в первых эпохах.

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

MeasurementCrnn: 48×160 и шестьсот тысяч параметров

Конкретная сеть в пилоте принимает одноканальный тензор 1 × 48 × 160. Четыре блока свертка → нормализация → ReLU → пулинг. Последний пулинг схлопывает высоту в один пиксель и оставляет 20 шагов вдоль ширины — как раз временная ось для LSTM. Дальше два слоя двунаправленной памяти, hidden_size = 96, dropout = 0.2. Голова линейная: 12 классов — 11 символов алфавита 0123456789, плюс blank.

Точка в исходнике приводится к запятой: normalize_label(" 7.3 ") даёт 7,3. Это доменное правило толщины, не «ещё один класс».

Ориентир по обучаемым параметрам — порядка 640 тысяч. Грубо: свёртка ≈ 241 тыс., двунаправленная LSTM ≈ 396 тыс., линейный слой ≈ 2 тыс. Большая часть весов — в последовательности, и при этом модель остаётся крошечной рядом с трансформером зрения.

Параметры — не сохранённые картинки и не база готовых ответов. Это числовые коэффициенты, которые подбираются на парах «кроп → строка». После обучения в файл measurement-crnn.pt кладутся веса, алфавит, hidden_size и размеры входа. Архитектуру можно загрузить на процессоре и гонять новые клетки без повторного обучения. Обучение периодическое; вывод — на каждом новом бланке.

Как готовим кроп и зачем аугментация

Сырой вырез клетки не кормят сети как есть. Цепочка из кода: оттенки серого, автоконтраст, масштаб с полями, холст 48 × 160 с центрированием, инверсия яркости (штрих становится светлым на тёмном), тензор. На обучении добавляется слабая аугментация: поворот ±3°, сдвиг на несколько пикселей, контраст и яркость в узком диапазоне.

Зачем это. Почерк, толщина ручки, тень от телефона и линия сетки меняются от листа к листу. Если сеть запомнит конкретные 200 кропов без вариаций, она удивится третьему почерку. Аугментация не заменяет разнообразие бланков — она не даёт модели выучить один сканер как «единственно верный мир».

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

Первый прогон: полное совпадение и точность по символам

Первый компактный прогон в пилоте: порядка тысячи числовых значений, обучение около 19 минут, 70,9% полного совпадения строки и 89% точности по символам. Это не «модель на 89% готова к цеху». Это два разных контракта.

Эталон 58321, прогноз 58327: по символам четыре из пяти верны, замер целиком неверен. Толщина в миллиметрах живёт как целое значение. Оператору и ERP нужна строка, а не средняя доля угаданных знаков.

Грубая иллюстрация, не прогноз нашей сети: если каждый символ независим с вероятностью 0,89, то пять символов подряд дают порядка 0,89⁵ ≈ 56% полного совпадения. На практике символы зависимы, длины разные, запятая критична — но направление верное: полное совпадение всегда жёстче посимвольной метрики. Для промышленного OCR её надо смотреть отдельно, вместе с ошибками по длине и по отдельным цифрам.

Позже, на более широкой выборке заполненных клеток, полный совпад поднимался выше (в одном из прогонов на цифрах — 85,4%). Это всё ещё пилотные замеры на наших кропах, не обещание чужому архиву.

Пустая клетка ломает распознаватель

Старая голова, обученная почти только на заполненных замерах, на пустых клетках давала 100% ложных срабатываний. Фантомы узнаваемы: 8,0, 8,9, 5,0, 11,1. Линия сетки, пыль, след ручки в соседней клетке для распознавателя выглядят как «почти цифра».

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

Эксперимент с отрицательными примерами: 75 проверенных листов; обучение 10 960 кропов, проверка 1 977; среди обучения 1 912 чисел и 191 пустая клетка (доля пустых в загрузчике по умолчанию 0,10). Сравнение пилотных прогонов:

Прогон Цифры, полное совпадение Ложные срабатывания на пустых
Базовый, почти без пустых 85,4% 100%
С нуля, с пустыми 78,6% 0,1%
Дообучение на пустых 83,2% 0,5%

Добавление отрицательных примеров резко бьёт по фантомам и отбирает качество на заполненных. Одна CRNN начинает решать две конфликтующие задачи: «есть ли здесь значение?» и «какое именно?». Веса, которые учатся игнорировать сетку, хуже читают тонкий хвост девятки.

Следующий шаг, который ещё не закрыт: крошечный классификатор «пусто / есть значение» перед замерной головой. Пустое — не кормим OCR. Заполненное — можно вернуть более «цифровой» снимок весов (measurement-crnn.prev.pt) без компромисса в одной голове. Это тот же урок, что с ложными тревогами на рентгенограммах: сложные отрицательные примеры — отдельная очередь, не «ещё один класс внутри той же сети». Как мы уже классифицируем пустое, повтор и зачёркивание в компьютерном зрении — в следующей заметке.

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

Одна модель на тип задачи, не на каждое поле

Искушение промышленного бланка — отдельная сеть на дату, на фамилию, на каждую колонку замера. Это четыре деплоя и одна и та же путаница в четырёх копиях. Лучше резать по визуальному контракту.

Тип задачи Что в пилоте
Рукописный замер MeasurementCrnn + проверка диапазона толщины
Пусто / заполнено эвристика чернил сейчас; отдельный маленький классификатор — следующий шаг
Печатная дата печатный OCR + день 1–31, месяц 1–12, разумный год
Печатный текст шапки и служебных полей общий печатный OCR
Рукописный комментарий / фамилия другая задача, другой алфавит; не расширять замерную голову
Галочка, повтор, зачёркивание отдельные правила / маленький классификатор

Нейросеть распознаёт то, что нельзя надёжно посчитать. Обычный код проверяет то, что проверяется детерминированно. Дата 12.09.2026 не требует второй CRNN: достаточно цифр и правил. Рукописная фамилия — наоборот, не должна ехать в алфавит из десяти цифр и запятой.

Трансформер имеет смысл, когда компактный предел измерен и не хватает: разные типы документов, слабая предсказуемость вёрстки, широкий алфавит, достаточно данных. Практичная схема — не «сразу тяжёлое распознавание», а специализированная голова → оценка надёжности → при низкой уверенности тяжёлый контур или человек.

Чекпоинт, обучение и метрики цеха

Обучение в пилоте: AdamW, скорость 1e-3, затухание весов 1e-4, пакет 16 примеров, обрезка градиента, планировщик ReduceLROnPlateau, ранняя остановка. Это скучный и достаточный контур. Эпоха — полный проход по обучающей выборке; лучший снимок пишется, когда растёт полное совпадение на проверке.

Вывод (ocr_crnn.py) читает чекпоинт на процессоре, декодирует жадно, нормализует толщину, при низкой уверенности может вернуть пусто. Движок рукописи переключается переменной окружения: PaddleOCR / EasyOCR по умолчанию, CRNN — отдельный режим. Это важно честно сказать: компактная сеть ещё не единственный путь в проде пилота. Основной контур листа по-прежнему держит локальное зрение, облачные проходы и отчёт оператора.

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

Набор важнее размера головы. Разные почерки, сканы и телефоны, реальные каверзные кропы, пустые клетки, жёсткие отрицательные примеры, разделение по листу, а не по файлу. Увеличение модели не лечит дырявый набор — это уже было верно для эталонных записей вообще.

Что сделать сегодня

Если у вас похожие бланки с фиксированной сеткой, не начинайте с выбора «CRNN или TrOCR».

Вырежьте 200–500 клеток замера с эталоном из учёта, включая пустые. Разделите по листу, не по случайным файлам.

Соберите самую узкую голову, которую можете объяснить: холст фиксированного размера, алфавит из цифр и разделителя, CTC. Сохраните чекпоинт вместе с алфавитом и размером входа.

Посчитайте отдельно полное совпадение, посимвольную точность и ложные срабатывания на пустых. Если пустые «рисуют» толщину — не дообучайте ту же голову до бесконечности: поставьте отсечку пусто/значение перед распознавателем.

Частые вопросы

Зачем CRNN, если уже есть модель зрения на фрагментах?

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

Почему не обучить сразу TrOCR?

Потому что сначала нужен измеримый предел узкой задачи. TrOCR оправдан, когда алфавит и вёрстка перестают быть узкими. Соседняя статья как раз про этот порог; здесь порог ещё не закрыт пустыми клетками и объёмом почерков.

70,9% полного совпадения — это мало или нормально?

Для приёмки цеха — мало. Для первого компактного прогона на тысяче значений — честный базовый уровень, от которого видно, куда уходят ошибки: последняя цифра, запятая, пустые поля.

Можно ли одной сетью читать и замер, и фамилию?

Технически да, практически это другой алфавит и другая длина. Замерная голова с запятой не должна «чуть-чуть» стать универсальным распознавателем. Отдельный контур рукописного текста — когда появится отдельный набор.

Почему ложные срабатывания на пустых важнее, чем ещё 2% на цифрах?

Пустая клетка, превращённая в 8,0, выглядит как уверенный замер. Оператор может не заметить. Недочитанная заполненная клетка чаще попадает в «проверить». Молчаливая галлюцинация опаснее явного пропуска.

Нужен ли GPU, чтобы этим пользоваться?

Для вывода в пилоте — нет, чекпоинт грузится на процессор. Обучение тысячей кропов у нас укладывалось в десятки минут. Аренда большого GPU имеет смысл позже, для универсальной модели, если компактный предел исчерпан.

Что почитать дальше

Системный контур бланка, человек в петле и почему нельзя скармливать весь лист одной модели — полевые заметки по УЗТ. Учебная механика CNN, CTC и когда брать TrOCRот свёртки до трансформера. Что делать с пустой клеткой, повтором и зачёркиванием до распознавания — классификатор содержимого клетки. Как не смешивать обучение и оценку — инженерия наборов данных. Зачем считать цену пропуска — экономика стоимости сбоя. Цикл экспериментов после базового прогона — качество не растёт от запуска обучения. Соседняя промышленная постановка «маленькая сеть, большая цена ошибки» — ИИ на рентгенограммах сварки.

Заключение

Фиксированная сетка бланка УЗТ позволяет не строить универсальный распознаватель. Компактная CRNN с CTC, входом 48 × 160 и примерно 640 тысячами параметров даёт честный локальный базовый уровень на рукописных замерах. Дальше качество упирается не в «добавить слоёв», а в пустые клетки, разнообразие почерка и разделение задач: классификатор пусто/значение, правила на дату, отдельный печатный контур, человек на хвост.

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