Мы сталкивались с ситуацией, когда бухгалтерия тратит до 40 % времени на ручной ввод накладных, а при проверке ИНН выясняется, что в документах опечатки и расхождения. Одна опечатка в ИНН может привести к проблемам с налоговой и финансовым потерям. Транспортные накладные (ТТН, CMR), товарные накладные (ТОРГ-12), акты выполненных работ — документы с жёсткой структурой, но большой вариативностью заполнения: рукописные поля, печати поверх текста, сканы низкого качества, смешанное заполнение (часть полей — машинопись, часть — от руки). Автоматизация этого процесса с помощью AI позволяет снизить нагрузку на сотрудников и исключить ошибки учёта. Мы разработали решение на базе LayoutLMv3, TrOCR и кастомных валидаторов, которое справляется с извлечением данных из любых накладных.
Как мы решаем проблему извлечения данных из накладных?
Мы используем комбинацию LayoutLMv3 для распознавания макета документа, отдельные OCR для печатного и рукописного текста, а также валидацию реквизитов. Перед подачей в модели изображения проходят предобработку: бинаризация, устранение перекоса, удаление шумов. Это повышает точность распознавания на сканах низкого качества на 5–7 %. Для повышения устойчивости к дефектам сканирования применяем аугментацию: случайный поворот, размытие, изменение контраста. Как отмечается в официальной документации LayoutLMv3, модель достигает F1 >0.95 на табличных документах, что подтверждает её применимость для нашей задачи. Это даёт точность >99 % на стандартных бланках и до 95 % на рукописных образцах после дообучения.
Задача NER на LayoutLM для накладных
Накладная — табличный документ: шапка (реквизиты сторон), таблица товарных позиций, подписи. LayoutLMv3 обрабатывает всё это через token classification с учётом координат текста.
from transformers import LayoutLMv3Processor, LayoutLMv3ForTokenClassification
from datasets import Dataset
import torch
# Полный набор меток для ТОРГ-12 / ТТН
WAYBILL_LABELS = [
'O',
'B-DOC_NUMBER', 'I-DOC_NUMBER',
'B-DOC_DATE', 'I-DOC_DATE',
'B-SENDER_NAME', 'I-SENDER_NAME',
'B-SENDER_INN', 'I-SENDER_INN',
'B-SENDER_ADDRESS', 'I-SENDER_ADDRESS',
'B-RECEIVER_NAME', 'I-RECEIVER_NAME',
'B-RECEIVER_INN', 'I-RECEIVER_INN',
'B-RECEIVER_ADDRESS', 'I-RECEIVER_ADDRESS',
'B-CARRIER_NAME', 'I-CARRIER_NAME',
'B-VEHICLE_REG', 'I-VEHICLE_REG', # гос. номер ТС
'B-ITEM_NAME', 'I-ITEM_NAME',
'B-ITEM_QTY', 'I-ITEM_QTY',
'B-ITEM_UNIT', 'I-ITEM_UNIT',
'B-ITEM_PRICE', 'I-ITEM_PRICE',
'B-ITEM_TOTAL', 'I-ITEM_TOTAL',
'B-TOTAL_QTY', 'I-TOTAL_QTY',
'B-TOTAL_AMOUNT', 'I-TOTAL_AMOUNT',
'B-DRIVER_NAME', 'I-DRIVER_NAME',
]
def prepare_waybill_dataset(
image_paths: list,
annotations: list, # список dict с keys: words, boxes, labels
processor: LayoutLMv3Processor
) -> Dataset:
"""
Подготовка датасета для fine-tuning.
annotations[i]['boxes']: нормализованные bbox [0..1000] для LayoutLM.
"""
label2id = {l: i for i, l in enumerate(WAYBILL_LABELS)}
features_list = []
for img_path, ann in zip(image_paths, annotations):
from PIL import Image as PILImage
image = PILImage.open(img_path).convert('RGB')
encoding = processor(
image,
text=ann['words'],
boxes=ann['boxes'],
word_labels=[label2id[l] for l in ann['labels']],
truncation=True,
padding='max_length',
max_length=512,
return_tensors='pt'
)
features_list.append({
k: v.squeeze().tolist() for k, v in encoding.items()
})
return Dataset.from_list(features_list)
Обработка рукописных полей: почему TrOCR лучше PaddleOCR?
В накладных часто встречаются рукописные даты, количества и подписи. PaddleOCR для печатного текста на рукописных полях ошибается — падает точность до 60 %. Мы используем детектор рукописи + TrOCR для рукописного распознавания, который даёт на 20 % выше F1 на реальных данных. Для дообучения TrOCR на корпоративных почерках требуется не менее 300 рукописных записей на оператора.
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
from PIL import Image
import torch
class HandwritingOCR:
def __init__(self):
self.processor = TrOCRProcessor.from_pretrained(
'microsoft/trocr-base-handwritten'
)
self.model = VisionEncoderDecoderModel.from_pretrained(
'microsoft/trocr-base-handwritten'
).eval().cuda()
@torch.no_grad()
def recognize(self, image: Image.Image) -> str:
pixel_values = self.processor(
image, return_tensors='pt'
).pixel_values.to('cuda')
generated_ids = self.model.generate(
pixel_values,
max_new_tokens=64,
num_beams=4,
early_stopping=True
)
return self.processor.batch_decode(
generated_ids, skip_special_tokens=True
)[0]
class HybridWaybillOCR:
"""
Определяем тип текста (печать / рукопись) → выбираем OCR.
Признаки рукописи: большая дисперсия высоты символов, нет serif-паттернов.
"""
def __init__(self):
self.handwriting_ocr = HandwritingOCR()
# PaddleOCR для печатного
from paddleocr import PaddleOCR
self.printed_ocr = PaddleOCR(use_angle_cls=True, lang='ru')
def is_handwritten(self, text_region: Image.Image) -> bool:
"""Простая эвристика: variance of stroke width"""
import numpy as np
img_array = np.array(text_region.convert('L'))
# Бинаризация
_, binary = cv2.threshold(img_array, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Дисперсия ширины строк как признак рукописи
col_density = (binary == 0).mean(axis=0)
return float(col_density.std()) > 0.15 # эмпирический порог
def recognize_region(self, image: Image.Image) -> str:
if self.is_handwritten(image):
return self.handwriting_ocr.recognize(image)
else:
result = self.printed_ocr.ocr(np.array(image))
return ' '.join([line[1][0] for line in result[0] or []])
Валидация реквизитов: ИНН, суммы, даты
Извлечённые данные проходят валидацию: проверка контрольной суммы ИНН, формата даты, арифметики итогов. Автоматически проверяем, что сумма товарных позиций совпадает с итоговой суммой, а количество — с суммой количеств. Расхождения логируются. Это снижает количество ошибок в учёте на 90 %.
import re
def validate_russian_inn(inn: str) -> bool:
"""Проверка контрольной цифры ИНН (РФ)"""
if not re.match(r'^\d{10}$|^\d{12}$', inn):
return False
digits = [int(d) for d in inn]
if len(inn) == 10:
check = sum(d * w for d, w in zip(digits[:9], [2,4,10,3,5,9,4,6,8])) % 11 % 10
return digits[9] == check
else:
c1 = sum(d * w for d, w in zip(digits[:11], [7,2,4,10,3,5,9,4,6,8,0])) % 11 % 10
c2 = sum(d * w for d, w in zip(digits[:11], [3,7,2,4,10,3,5,9,4,6,8])) % 11 % 10
return digits[10] == c1 and digits[11] == c2
Что входит в работу?
Мы предоставляем:
- обученную модель LayoutLMv3 на ваших шаблонах
- REST API или Python-пакет для инференса
- модуль интеграции с 1С (обмен через HTTP)
- дашборд логирования и мониторинга (prometheus + grafana — опционально)
- документацию по дообучению и развёртыванию
- обучение персонала и поддержку на этапе внедрения
Сравнение: наше решение vs rule-based парсинг
| Характеристика | Rule-based | Наше AI-решение |
|---|---|---|
| Точность на стандартных бланках | 85–90 % | >99 % |
| Устойчивость к сканам низкого качества | Низкая | Высокая (data augmentation) |
| Обработка рукописного текста | Нет | Да (TrOCR) |
| Время настройки под новый шаблон | дни | часы (few-shot) |
| Стоимость поддержки | Высокая (патчи под каждый шаблон) | Низкая (one model) |
Типичные ошибки при внедрении и как их избежать
- Недостаток разметки для рукописных полей. Минимум 200 образцов на почерк каждого оператора — иначе TrOCR даёт <80 % accuracy.
- Игнорирование валидации ИНН. Ошибка в одной цифре ведёт к проблемам с налоговой — мы всегда проверяем контрольную сумму.
- Смешивание печатного и рукописного OCR. Без детектора рукописи модель будет путаться — наша эвристика с порогом 0.15 стабильно работает.
- Отсутствие мониторинга в продакшене. Мы рекомендуем считать метрики (F1, latency p99) и настроить алерты при падении качества.
Почему стоит выбрать нас?
У нас 5+ лет опыта в Computer Vision и NLP, десятки внедрений OCR в документообороте. Мы даём гарантию на точность модели и предоставляем полную документацию. Если у вас есть нестандартные накладные — мы оценим проект за 1 день. Свяжитесь с нами, чтобы обсудить вашу задачу: мы подберём оптимальное решение под ваш бюджет.
Ориентировочные сроки
| Этап | Срок |
|---|---|
| Извлечение полей ТОРГ-12 / ТТН (стандартные форматы) | 2–3 недели |
| Fine-tuning LayoutLMv3 на корпоративные накладные | 5–7 недель |
| Полная система с рукописью + валидацией + 1С-интеграцией | 8–14 недель |
Закажите пилотное внедрение: мы развернём решение на ваших 100 документах и покажем метрики. Получите консультацию инженера.







