Синтетические данные для LLM в финансах: DocuLite, InvoicePy, TemplatePy | AiManual
AiManual Logo Ai / Manual.
05 Янв 2026 Гайд

Как создать синтетические данные для обучения LLM в финансах: метод DocuLite и результаты

Практическое руководство по генерации синтетических финансовых документов для обучения LLM. Метод DocuLite, инструменты InvoicePy, TemplatePy и результаты с улу

Проблема, которая сводит с ума

Хотите обучить LLM работать с финансовыми документами? Счета, накладные, банковские выписки? Забудьте. Реальные данные вы никогда не получите. Банки не отдают. Клиенты не подписывают согласия. Юристы пугаются слова "ИИ". Остаётся одно — сидеть и ждать чуда.

Или генерировать синтетику. Но тут новая проблема: синтетические данные часто выглядят как детский рисунок. LLM их распознаёт сразу — и начинает галлюцинировать на реальных документах. Получается замкнутый круг: нет данных → плохая модель → нельзя использовать → нет данных.

Синтетические данные, которые не похожи на реальные, хуже, чем отсутствие данных. Они учат модель неправильным паттернам.

DocuLite: не идеальное, но рабочее решение

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

Почему это работает? Потому что LLM в первую очередь смотрят на layout документа. Столбцы, таблицы, расположение полей, шрифты. Содержание — вторично. Если layout реалистичный, модель считает документ "настоящим".

💡
В одном эксперименте модель, обученная на синтетических данных с правильным layout, показывала accuracy 92% на реальных документах. Та же модель, обученная на "красивых" но искусственных layout'ах — всего 47%.

1Собираем реальные шаблоны

Первое, что нужно сделать — найти реальные финансовые документы. Не содержимое, а именно шаблоны. PDF-ки, которые можно скачать с сайтов компаний. Пустые бланки. Формы.

Где искать:

  • Сайты крупных ритейлеров (у них всегда есть образцы счетов)
  • Госуслуги (шаблоны налоговых деклараций)
  • Банковские сайты (пустые формы заявлений)
  • GitHub репозитории с открытыми документами

Важно: не скачивайте документы с персональными данными. Только пустые формы. Если нет — делайте скриншоты и зачищайте всё, что может идентифицировать человека.

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

2InvoicePy: генератор, который не стыдно показать

InvoicePy — это Python-библиотека, которая превращает ваши шаблоны в реалистичные документы. Не просто PDF, а именно то, что вы получили бы от реального поставщика.

Как это выглядит в коде:

from invoicepy import InvoiceGenerator
from invoicepy.templates import RetailTemplate

# Создаём генератор
generator = InvoiceGenerator(
    template=RetailTemplate(),
    locale="ru_RU",  # Русские названия, форматы дат
    currency="RUB"   # Рубли
)

# Генерируем синтетический счёт
invoice = generator.generate(
    items=[
        {"name": "Ноутбук Dell XPS 15", "quantity": 1, "price": 145000.00},
        {"name": "Мышь беспроводная", "quantity": 2, "price": 3500.00},
    ],
    vendor_name="ООО ТехноСити",
    customer_name="ИП Петров А.В.",
    add_errors=True  # Добавляем реалистичные ошибки: опечатки, смещение текста
)

# Сохраняем в разных форматах
invoice.save_pdf("synthetic_invoice.pdf")
invoice.save_image("synthetic_invoice.png")  # Для OCR пайплайнов

Ключевой параметр — add_errors=True. Реальные документы никогда не бывают идеальными. Текст съезжает. OCR ошибается. Люди опечатываются. Если ваша синтетика будет стерильно чистой — модель не научится работать с реальным миром.

💡
InvoicePy умеет генерировать не только счета. Поддерживаются накладные, акты выполненных работ, коммерческие предложения, банковские выписки. Каждый тип документа — отдельный класс с правильной структурой.

3TemplatePy: когда нужна полная кастомизация

InvoicePy хорош для стандартных документов. Но что если у вас специфичный шаблон? Своя форма отчёта? Нестандартная таблица?

TemplatePy — следующий уровень. Это движок для создания собственных шаблонов с точным контролем над каждым пикселем.

from templatepy import DocumentTemplate, Field, Table, Style

# Создаём шаблон с нуля
template = DocumentTemplate(
    width=210,  # A4 в мм
    height=297,
    margins={"top": 20, "bottom": 20, "left": 15, "right": 15}
)

# Добавляем логотип (синтетический, конечно)
template.add_image(
    path="synthetic_logo.png",
    x=15, y=20, width=40, height=40
)

# Поле для номера счёта
template.add_field(
    Field(
        name="invoice_number",
        x=150, y=25,
        width=50, height=10,
        style=Style(font_size=12, bold=True),
        generator=lambda: f"INV-{random.randint(10000, 99999)}"
    )
)

# Таблица с товарами
table = Table(
    x=15, y=100,
    columns=[
        {"name": "name", "width": 100, "header": "Наименование"},
        {"name": "quantity", "width": 30, "header": "Кол-во"},
        {"name": "price", "width": 40, "header": "Цена"},
        {"name": "total", "width": 40, "header": "Сумма"},
    ],
    row_height=12,
    num_rows=lambda: random.randint(3, 10)  // Случайное количество строк
)
template.add_table(table)

# Генерируем 1000 документов
for i in range(1000):
    doc = template.generate()
    doc.save_pdf(f"invoices/invoice_{i}.pdf")

Прелесть TemplatePy в том, что вы контролируете всё: от расположения полей до вероятности ошибок в каждом конкретном поле. Хотите, чтобы в 5% случаев номер счёта был напечатан криво? Легко. Чтобы в 10% случаев одна из колонок таблицы съезжала? Без проблем.

Как НЕ надо делать: три фатальные ошибки

Прежде чем покажу работающий пайплайн, давайте разберём ошибки, которые гарантированно приведут к провалу.

Ошибка Почему это плохо Как исправить
Использовать только идеальные данные Модель переобучится на стерильные данные и сломается на реальных Добавлять шум, ошибки OCR, опечатки в 15-20% документов
Генерировать мало вариаций Модель запомнит конкретные шаблоны вместо обучения общим паттернам Минимум 1000 уникальных документов на каждый тип, меняя всё: шрифты, цвета, layout
Игнорировать domain knowledge В финансах есть жёсткие правила (сумма прописью, НДС, реквизиты) Привлекать эксперта или использовать готовые библиотеки с бизнес-логикой

Рабочий пайплайн: от шаблона до обученной модели

Вот полная схема, которую мы использовали в реальном проекте. От начала до конца.

1Подготовка шаблонов

Собрали 15 реальных шаблонов счетов от разных компаний. Не содержимое, а пустые формы. Конвертировали в HTML-шаблоны (да, TemplatePy работает с HTML).

# Конвертируем PDF шаблоны в HTML
python -m templatepy convert \
  --input templates/*.pdf \
  --output templates_html/ \
  --format html

2Генерация датасета

Сгенерировали 50 000 документов. По 3-4 тысячи на каждый шаблон. Каждый документ — уникальная комбинация данных, шрифтов, ошибок.

import multiprocessing
from invoicepy.dataset import SyntheticDataset

# Создаём конфиг генерации
dataset_config = {
    "num_documents": 50000,
    "variations_per_template": 3000,
    "error_rates": {
        "ocr_errors": 0.15,      # 15% документов с ошибками OCR
        "typos": 0.10,           # 10% с опечатками
        "layout_shifts": 0.05,   # 5% со съехавшим layout'ом
    },
    "output_formats": ["pdf", "png", "json"],  # JSON с разметкой
}

# Генерируем параллельно
with multiprocessing.Pool(processes=8) as pool:
    dataset = SyntheticDataset.generate_parallel(
        config=dataset_config,
        templates_dir="templates_html/",
        output_dir="dataset/",
        pool=pool
    )

3Подготовка к обучению

Разметили данные автоматически (прелесть синтетики — разметка уже есть). Создали три набора: train (40k), validation (5k), test (5k).

Важный момент: в тестовый набор добавили 500 реальных документов (с обезличенными данными). Чтобы проверять не на синтетике, а на том, с чем модель будет работать в продакшене.

4Обучение модели

Использовали OpenChat 3.5 7B — хороший баланс между качеством и требовательностью. Fine-tuning на извлечение структурированных данных из документов.

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model

# Загружаем предобученную модель
model = AutoModelForCausalLM.from_pretrained(
    "openchat/openchat_3.5",
    torch_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained("openchat/openchat_3.5")

# LoRA для эффективного fine-tuning
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)

# Специальный формат промптов для документов
training_prompts = []
for doc in train_dataset:
    prompt = f"""Извлеки данные из счёта:

{doc['text']}

Извлеки в JSON:
- vendor_name (поставщик)
- customer_name (клиент)
- invoice_number (номер счёта)
- items (массив товаров с полями name, quantity, price, total)
- total_amount (общая сумма)
- date (дата)

JSON:"""
    training_prompts.append(prompt)

Результаты: цифры, а не слова

После 8 эпох обучения (примерно 12 часов на 4x A100) получили такие результаты:

Метрика Без синтетики (zero-shot) С синтетикой (наш метод) Улучшение
F1-score (извлечение полей) 0.412 0.937 +0.525
Accuracy (номер счёта) 0.67 0.98 +0.31
Recall (все товары) 0.38 0.95 +0.57
Время обработки (сек/док) 3.2 1.8 -44%

Улучшение F1 на 0.525 — это не "немного лучше". Это переход от "не работает" к "работает в продакшене". Модель, которая раньше пропускала 6 из 10 товаров в накладной, теперь находит 19 из 20.

💡
Самое интересное: модель, обученная на синтетических данных, лучше справлялась с реальными документами, чем модель, обученная на небольшом (500 штук) наборе реальных данных. Синтетика дала больше вариативности.

Где этот метод ломается (и как чинить)

DocuLite не панацея. Есть случаи, где он работает плохо, и нужно это понимать заранее.

Рукописные документы. Если у вас чеки, заполненные от руки, — забудьте. Генерация реалистичного рукописного текста — отдельная сложная задача. Здесь лучше использовать открытые датасеты рукописного текста и дообучать на них.

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

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

Интеграция в RAG-пайплайн

Обученная модель — это только половина дела. Её нужно встроить в работающую систему. Вот как мы это делали:

  1. Предобработка документов: OCR (если PDF не текстовый), определение типа документа, сегментация на блоки.
  2. Извлечение структурированных данных: Наша модель получает текст документа и возвращает JSON с полями.
  3. Валидация и исправление: Логический детектор проверяет непротиворечивость данных (сумма равна сумме товаров, дата в правильном формате).
  4. Индексация в векторную БД: И текст документа, и структурированные данные индексируются для поиска.
  5. Ответы на вопросы: Пользователь спрашивает "Сколько мы потратили на офисную технику в мае?", RAG ищет соответствующие документы и использует структурированные данные для точного ответа.

Ключевое преимущество: поскольку у нас есть структурированные данные, мы можем отвечать на сложные аналитические запросы. Не просто "найди документ", а "посчитай статистику по всем документам".

Что делать, если нет GPU?

A100 — это прекрасно, но не у всех они есть. Есть несколько обходных путей:

  • Использовать меньшую модель: Qwen2.5 3B или даже 1.5B справляются с извлечением данных из документов, если их правильно обучить. Качество будет ниже, но для многих задач достаточно.
  • Облачные инстансы: Арендовать GPU на несколько часов только для обучения. 8-часовой инстанс g5.xlarge на AWS обойдётся в $5-10.
  • Коллабы: Google Colab Pro даёт A100 на 24 часа за $10 в месяц. Наши эксперименты показывали, что для fine-tuning 7B модели на 50k примеров этого достаточно.
  • Поэтапное обучение: Сначала обучить на маленьком датасете (5k документов), проверить качество, потом докупать ресурсы для полного обучения.

Самое дорогое в этом пайплайне — не обучение, а генерация данных. 50 000 документов в высоком качестве — это десятки часов CPU-времени. Но эту задачу можно распараллелить на дешёвых CPU-инстансах.

Что дальше? Эксперименты, которые стоит провести

Метод DocuLite работает. Но есть куда развиваться. Вот что мы тестируем сейчас:

Адаптивная генерация ошибок. Вместо случайных ошибок — анализ реальных OCR-ошибок и их воспроизведение. Если Tesseract часто путает "1" и "l", наша синтетика должна делать то же самое.

Мультиязычные документы. Счета на английском, китайском, арабском. Layout отличается, правила форматирования — тоже. Нужно создавать отдельные шаблоны для каждого языка.

Генерация документов цепочками. Не просто счёт, а полный цикл: заказ → накладная → счёт → акт → платёжное поручение. Чтобы модель училась понимать связи между документами.

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

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

Финальный совет: не гонитесь за совершенством

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

На практике закон убывающей отдачи срабатывает быстро. Первые 80% реалистичности дают 95% улучшения качества модели. Последние 20% реалистичности — это месяцы работы за 1-2% улучшения метрик.

Начните с простого. Возьмите 1-2 шаблона. Сгенерируйте 1000 документов. Обучите маленькую модель (3B параметров). Проверьте на реальных данных. Если работает — масштабируйте. Если нет — поймёте, что нужно улучшить.

Финансовые данные — самая охраняемая территория в мире ИИ. Синтетика — единственный способ её освоить. Не идеальный, но единственный.