Проблема: 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
- Командам, которые извлекают параметры из чертежей — там дегенерация губит числовые значения.
- Разработчикам, которые строят ADE-пайплайны — любая ошибка распознавания валится на downstream.
- Тем, кто пытается внедрить Granite-Docling и сталкивается с тем, что модель «застревает» на длинных последовательностях.
Ещё один неочевидный совет: применяйте DPO не только на уровне текста, но и на уровне разметки layout. Если модель путает колонки — сгенерируйте пары, где правильный layout — верный порядок строк, а неправильный — перепутанные колонки. Это даст ещё больше выгоды.
Мы уже движемся в сторону оптимизации DPO в FP8 — возможно, скоро хватит одной видеокарты уровня RTX 4090. А пока DPO — лучший способ заставить VLM меньше врать и не зацикливаться.