Дообучение OLMocr 2 на своих данных: полный гайд по датасету и пайплайну | AiManual
AiManual Logo Ai / Manual.
15 Янв 2026 Гайд

OLMocr 2: как заставить модель читать ваши документы лучше вас

Пошаговый гайд по подготовке датасета и дообучению OLMocr 2 для распознавания ваших документов. От сбора данных до финального пайплайна.

Почему OLMocr 2 не читает ваши документы и как это исправить

Вы загружаете сканы договоров, накладных, медицинских карт в OLMocr 2 и получаете на выходе абракадабру. Модель, обученная на общих данных, спотыкается о вашу специфику - особые шрифты, бланки, сокращения, печати. Типичная ситуация: OCR работает идеально на тестовых примерах, но в продакшене дает ошибки в 30% случаев.

Проблема не в модели. Проблема в том, что OLMocr 2 не знает, как выглядят ВАШИ документы. Дообучение - не опция, а необходимость, если хотите точность выше 95%. Но большинство пытаются делать это неправильно: кидают в модель 100 случайных изображений и удивляются, почему ничего не работает.

Типичная ошибка: начинают с кода обучения, а не с данных. Результат - потраченное время и нулевое улучшение. В OCR 80% успеха - это качественный датасет, 20% - правильные гиперпараметры.

1 Собираем данные, которые действительно учат

Первое правило: не берите все подряд. Возьмите 10-20 самых проблемных типов документов, где OLMocr 2 ошибается чаще всего. Это могут быть:

  • Документы с печатями и штампами
  • Бланки со сложным форматированием
  • Текст в таблицах (особенно с границами)
  • Рукописные пометки поверх машинного текста
  • Документы плохого качества (сжатые, размытые, с тенями)

Ключевой момент: разнообразие. Если у вас только идеальные сканы с белым фоном - модель не научится работать с реальными условиями. Добавьте:

Тип вариации Зачем нужно Примеры
Разное качество Модель учится работать с реальными документами Размытые, сжатые, с артефактами JPEG
Разный фон Помогает отделить текст от фона Цветной фон, текстура бумаги, линии разлиновки
Разные углы Учет перспективных искажений Поворот на 1-5 градусов, перспектива
💡
Не гонитесь за количеством. 500 хорошо подобранных изображений работают лучше, чем 5000 случайных. Каждый пример должен чему-то учить модель - новому шрифту, формату, артефакту.

2 Разметка: как не сойти с ума и не разориться

Разметка текста на изображениях - самая муторная часть. Ручная разметка 1000 документов займет недели. Есть три пути:

  1. Использовать сам OLMocr 2 для преразметки - запускаете модель на ваших данных, исправляете только ошибки. Экономит 70% времени.
  2. Аугментация существующей разметки - если есть часть размеченных данных, генерируйте на их основе новые примеры с искажениями.
  3. Использовать инструменты вроде PPOCRLabel - полуавтоматическая разметка с предсказаниями модели.

Формат разметки для OLMocr 2:

{
  "image_path": "documents/contract_001.jpg",
  "text": "ДОГОВОР № 123 от 15.01.2024 г.\nг. Москва\n\n1. ПРЕДМЕТ ДОГОВОРА...",
  "bboxes": [
    [10, 20, 100, 30, "ДОГОВОР"],
    [110, 20, 200, 30, "№"],
    [210, 20, 250, 30, "123"]
  ]
}

Важно: сохраняйте оригинальную кодировку и специальные символы. Если в документах есть знаки рубля (₽), товарные знаки (™) - они должны быть в разметке. OLMocr 2 поддерживает Unicode.

Если разметка пугает масштабами, посмотрите мой гайд про автоматизацию разметки датасетов - там техники, которые сокращают работу с недель до часов.

3 Подготовка пайплайна: от сырых данных до готового датасета

Собранные изображения и разметка - это сырье. Теперь нужно построить конвейер, который превратит это в готовый датасет для обучения. Вот полный пайплайн:

import os
import json
from pathlib import Path
from PIL import Image
import numpy as np

class OLMocrDatasetPipeline:
    def __init__(self, raw_data_dir, output_dir):
        self.raw_data_dir = Path(raw_data_dir)
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(exist_ok=True)
    
    def validate_images(self):
        """Проверяем, что все изображения читаются"""
        valid_images = []
        for img_path in self.raw_data_dir.glob("*.jpg"):
            try:
                with Image.open(img_path) as img:
                    img.verify()
                valid_images.append(img_path)
            except:
                print(f"Повреждено: {img_path.name}")
        return valid_images
    
    def create_train_val_split(self, images, val_ratio=0.2):
        """Разделяем на тренировочную и валидационную выборки"""
        np.random.shuffle(images)
        split_idx = int(len(images) * (1 - val_ratio))
        return images[:split_idx], images[split_idx:]
    
    def generate_annotations(self, image_paths, split_name):
        """Генерируем аннотации в формате OLMocr 2"""
        annotations = []
        for img_path in image_paths:
            # Загружаем соответствующую разметку
            ann_path = img_path.with_suffix('.json')
            if ann_path.exists():
                with open(ann_path, 'r', encoding='utf-8') as f:
                    ann_data = json.load(f)
                
                # Конвертируем в нужный формат
                annotation = {
                    "image": str(img_path),
                    "text": ann_data["text"],
                    "bboxes": ann_data["bboxes"]
                }
                annotations.append(annotation)
        
        # Сохраняем
        output_file = self.output_dir / f"{split_name}_annotations.json"
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(annotations, f, ensure_ascii=False, indent=2)
        
        return output_file

Это базовая структура. В реальном пайплайне добавьте:

  • Аугментацию данных (повороты, изменение яркости, добавление шума)
  • Балансировку классов (если есть разные типы документов)
  • Проверку качества разметки (выборочную верификацию)

4 Дообучение: что менять, а что оставить как есть

Теперь самая интересная часть - собственно обучение. OLMocr 2 построен на архитектуре Transformer, и у вас есть несколько стратегий:

Стратегия Когда использовать Ресурсы
Fine-tuning всей модели Документы сильно отличаются от тренировочных данных OLMocr 2 Много GPU памяти, время
LoRA адаптеры Хотите сохранить базовые способности, добавив специфику Мало ресурсов, быстро
Только последние слои Документы похожи, но есть особенности форматирования Умеренные ресурсы

Конфигурация обучения (основные параметры):

# config_finetune.yaml
model:
  pretrained: "olmednard/olmocr-2-base"
  
training:
  batch_size: 8  # Зависит от GPU памяти
  num_epochs: 10  # Для fine-tuning обычно достаточно
  learning_rate: 5e-5
  warmup_steps: 100
  
data:
  train_annotations: "data/train_annotations.json"
  val_annotations: "data/val_annotations.json"
  image_size: [1024, 1024]  # OLMocr 2 работает с квадратными изображениями
  
augmentation:
  rotation_range: [-5, 5]  # Небольшие повороты
  brightness_range: [0.9, 1.1]
  contrast_range: [0.9, 1.1]
💡
Начинайте с маленького learning rate (5e-5). Большие значения "забывают" базовые знания модели. Проверяйте loss на валидации после каждой эпохи - если не уменьшается, возможно, данные плохого качества или learning rate слишком высокий.

5 Валидация: как понять, что модель действительно улучшилась

Самый опасный момент - когда метрики растут, а на реальных данных ничего не меняется. OLMocr 2 использует метрику CER (Character Error Rate), но она может обманывать.

Что нужно проверять обязательно:

  1. CER на валидационной выборке - должна снижаться
  2. Точность на критичных полях - номера документов, суммы, даты
  3. Скорость работы - не должна значительно ухудшиться
  4. Распознавание на "сложных" документах - тех, которые изначально плохо читались

Скрипт для комплексной валидации:

def validate_model(model, test_dataset, critical_fields):
    """Проверяем модель на критически важных полях"""
    results = {
        'overall_cer': 0.0,
        'field_accuracy': {},
        'inference_time': []
    }
    
    for test_item in test_dataset:
        start_time = time.time()
        
        # Предсказание
        prediction = model.predict(test_item['image'])
        
        inference_time = time.time() - start_time
        results['inference_time'].append(inference_time)
        
        # Считаем CER для всего текста
        cer = calculate_cer(test_item['text'], prediction['text'])
        results['overall_cer'] += cer
        
        # Проверяем критичные поля
        for field_name, field_regex in critical_fields.items():
            original_field = extract_field(test_item['text'], field_regex)
            predicted_field = extract_field(prediction['text'], field_regex)
            
            if original_field and predicted_field:
                is_correct = (original_field == predicted_field)
                results['field_accuracy'].setdefault(field_name, []).append(is_correct)
    
    # Агрегируем результаты
    results['overall_cer'] /= len(test_dataset)
    for field_name in results['field_accuracy']:
        accuracy = sum(results['field_accuracy'][field_name]) / len(results['field_accuracy'][field_name])
        results['field_accuracy'][field_name] = accuracy
    
    results['avg_inference_time'] = sum(results['inference_time']) / len(results['inference_time'])
    
    return results

Типичные ошибки и как их избежать

За 5 лет работы с OCR-моделями я видел одни и те же ошибки снова и снова:

Ошибка 1: Обучают на идеальных данных, тестируют на идеальных данных, удивляются, почему в продакшене плохо. Решение: добавьте в тренировочные данные документы с теми же проблемами, что и в продакшене.

Ошибка 2: Слишком много эпох обучения. OLMocr 2 уже хорошо обучен, ему нужно только адаптироваться к вашим данным. 5-10 эпох обычно достаточно. После 20 эпох начинается переобучение.

Ошибка 3: Игнорируют классовый дисбаланс. Если у вас 90% договоров и 10% накладных, модель будет хуже распознавать накладные. Решение: oversampling редких классов или веса классов в loss function.

Интеграция в продакшен: не только модель

Обученная модель - только часть системы. В продакшене вам понадобится:

  • Предобработка изображений - выравнивание, повышение контраста, удаление шума
  • Постобработка текста - исправление типичных ошибок, приведение к стандартному формату
  • Мониторинг качества - отслеживание метрик в реальном времени
  • А/Б тестирование - постепенный rollout новой модели

Пример постобработки для исправления типичных ошибок OCR:

class OCRPostProcessor:
    def __init__(self, correction_rules):
        self.correction_rules = correction_rules  # Словарь замен
    
    def correct_common_errors(self, text):
        """Исправляем типичные ошибки OCR"""
        corrected = text
        
        # Замены символов, которые часто путает OCR
        common_mistakes = {
            '0': 'O',  # Ноль и буква O
            '1': 'I',  # Единица и I
            '5': 'S',  # Пять и S
            'B': '8',  # B и 8
        }
        
        # Контекстно-зависимые замены
        for wrong, correct in common_mistakes.items():
            corrected = corrected.replace(wrong, correct)
        
        # Исправление дат (01.02.2023 -> 01.02.2023)
        corrected = re.sub(r'(\d{2})[.,]?(\d{2})[.,]?(\d{4})', 
                          r'\1.\2.\3', corrected)
        
        return corrected

Если нужно строить сложные пайплайны обработки документов, посмотрите как мы делали RAG-систему для анализа таблиц - многие принципы похожи.

Что делать, если не хватает данных

Бывает, что документы конфиденциальные или их просто мало. Варианты:

  1. Синтетическая генерация - создавайте похожие документы с помощью шаблонов
  2. Дообучение на публичных данных - найдите похожие документы в открытых источниках
  3. Активное обучение - дообучайте модель постепенно, на самых сложных примерах
  4. Используйте Few-shot подход - OLMocr 2 может доучиваться на лету

Мало данных - не приговор. Иногда 50 хорошо подобранных примеров дают больший прирост качества, чем 5000 случайных.

Вместо заключения: когда дообучение не поможет

Знайте, когда нужно остановиться. Дообучение OLMocr 2 не поможет, если:

  • Документы настолько плохого качества, что человек их с трудом читает
  • Нужно распознавать рукописный текст (OLMocr 2 для этого не предназначен)
  • Требуется извлекать структурированные данные из сложных таблиц (лучше использовать специализированные инструменты)
  • Документы на языках, которых нет в предобучении OLMocr 2

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

Главное - начинайте с малого. Возьмите 50 самых проблемных документов, дообучите модель, проверьте результат. Если работает - масштабируйте. Если нет - анализируйте, почему. OCR - не магия, а инженерия. Каждая ошибка модели имеет причину, и обычно эту причину можно устранить.