Ми стикалися з ситуацією, коли бухгалтерія витрачає до 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 документах і покажемо метрики. Отримайте консультацію інженера.







