DPO для OCR: снижение дегенерации текста на 87% | AiManual
AiManual Logo Ai / Manual.
03 Июн 2026 Инструмент

DPO для OCR: как снизить дегенерацию текста на 87% с помощью Direct Preference Optimization

Как с помощью Direct Preference Optimization уменьшить повторения и галлюцинации в OCR. Пошаговый гайд с кодом на Python.

Проблема: OCR галлюцинирует и зацикливается

Вы когда-нибудь смотрели на распознанный текст из PDF и видели бесконечное повторение одной фразы? Или модель вдруг начинает выдумывать несуществующие слова, которых в оригинале не было и быть не могло? Это она — дегенерация текста. И чем сложнее документ (рукопись, мелкий шрифт, нестандартный макет), тем злее она кусается.

Классические OCR-движки вроде Tesseract страдают от этого реже, зато выдают дичь с рукописью. Современные Vision-Language модели (PaliGemma, Qwen2-VL) умнее, но именно из-за своей «болтливости» они начинают повторять фрагменты, пытаясь заполнить пробелы в картинке. SFT усугубляет ситуацию: модель запоминает шум и воспроизводит его при любом сомнении.

Пока индустрия билась над архитектурами, появился неочевидный герой — Direct Preference Optimization (DPO). Метод, который перевернул выравнивание LLM, оказался спасательным кругом и для OCR. Результаты на наших тестах: снижение дегенерации на 87%. Да, я не ошибся — именно 87%.

Почему DPO, а не очередной SFT?

Supervised Fine-Tuning учит модель копировать ответы из датасета. Если в датасете есть хоть немного мусора — модель скопирует и его. DPO же учит выбирать правильный ответ из пары, отбрасывая плохой. Это кардинально меняет поведение на граничных случаях.

В нашем обзоре методов выравнивания мы уже разобрали, чем DPO отличается от RLHF и SimPO. Если кратко: DPO не требует отдельной reward model, что упрощает пайплайн и снижает расход железа. А для OCR это ещё и вопрос скорости — гигантские датасеты изображений удобнее обрабатывать без промежуточного ранжирования.

Собираем датасет предпочтений для OCR

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

Мы поступили просто: взяли синтетические документы (счета, накладные, паспорта), прогнали через базовую модель (Qwen2-VL 7B) и собрали все её ошибки. Каждый неверный символ, каждое повторение — это «плохой» ответ. Затем вручную подготовили «хороший» вариант — правильную строку. В итоге получили 15 000 пар. Для сравнения — можно взять данные из статьи про тестирование open-source OCR и наложить на них разметку.

Важно: пары должны быть сбалансированы. Если в датасете слишком много «лёгких» примеров, где модель и так права, DPO не сработает. Нужен перекос в сторону ошибок.

Пошаговый пайплайн DPO-дообучения

1 Подготовка данных

Превращаем пары в формат, понятный библиотеке trl. Каждая запись — словарь с ключами prompt (изображение + текст запроса), chosen (правильная строка), rejected (ошибочная строка).

import json
from datasets import Dataset

# пример одной записи
data = [
    {
        "prompt": "What is the text in the invoice?",
        "chosen": "Invoice #12345 Total: $1,234.56",
        "rejected": "Invoice #12345 Total: $1,234.56 Invoice #12345"
    }
]
dataset = Dataset.from_list(data)

2 Загрузка модели и токенизатора

from transformers import Qwen2VLForConditionalGeneration, AutoProcessor

model = Qwen2VLForConditionalGeneration.from_pretrained(
    "Qwen/Qwen2-VL-7B-Instruct",
    trust_remote_code=True)
processor = AutoProcessor.from_pretrained("Qwen/Qwen2-VL-7B-Instruct")

3 Обучение DPO

from trl import DPOTrainer, DPOConfig

config = DPOConfig(
    per_device_train_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=5e-7,
    max_length=512,
    beta=0.1,
    num_train_epochs=3
)

trainer = DPOTrainer(
    model=model,
    ref_model=None,  # DPO без reference модели (simplified)
    args=config,
    train_dataset=dataset,
    tokenizer=processor.tokenizer
)

trainer.train()

4 Оценка результатов

Замерили метрики на тестовом наборе из 5000 изображений (рукопись, печать, таблицы). Сравнивали с baseline (SFT) и исходной моделью.

Метрика Исходная Qwen2-VL SFT DPO (наш)
CER 3.2% 2.9% 2.5%
WER 6.8% 6.4% 5.1%
Repetition Rate 2.3% 3.1% 0.3%

Repetition rate — доля строк, содержащих повторяющиеся фрагменты. Упал на 87% (с 2.3% до 0.3%). А вот SFT, как видно, даже увеличил дегенерацию — модель выучила «любимые» ошибки датасета.

DPO не панацея — он снижает именно повторения и галлюцинации, а не превращает распознавание в идеальное. Для сложных макетов может потребоваться комбинация с layout-aware подходом, как описано в статье про OCR для ипотеки.

Когда DPO не нужен?

Если ваши документы — однотипные A4 с крупным шрифтом (чеки, визитки), DPO — это выстрел из пушки по воробьям. Tesseract + постобработка на регулярках даст 99% точности за копейки. Но если вы работаете с историческими рукописями, формулами или нечитаемыми квитанциями — DPO окупит время на подготовку датасета.

Также стоит помнить, что DPO требует больше памяти, чем SFT, из-за хранения двух последовательностей (chosen/rejected). На практике для 7B модели требуется ~48 ГБ VRAM (с gradient checkpointing). Если железа нет — можно попробовать QLoRA, но качество может просесть.

Кому реально поможет DPO

Ещё один неочевидный совет: применяйте DPO не только на уровне текста, но и на уровне разметки layout. Если модель путает колонки — сгенерируйте пары, где правильный layout — верный порядок строк, а неправильный — перепутанные колонки. Это даст ещё больше выгоды.

Мы уже движемся в сторону оптимизации DPO в FP8 — возможно, скоро хватит одной видеокарты уровня RTX 4090. А пока DPO — лучший способ заставить VLM меньше врать и не зацикливаться.

Подписаться на канал