Training OCR-моделей: PaddleOCR, TrOCR, EasyOCR
Общий OCR — задача решённая: Tesseract, Google Vision, Azure Read API хорошо справляются со стандартным печатным текстом. Кастомное обучение OCR нужно для: специфических шрифтов (рукопись, промышленная маркировка, нестандартные символы), редких языков, деградированных документов (факсы, старые архивы), специализированных форматов (матрицы, химические формулы).
PaddleOCR — архитектура и fine-tuning
PaddleOCR состоит из трёх независимых компонентов: detection (DBNet), direction classifier, recognition (SVTR/PP-OCR). Fine-tuning можно применять к каждому отдельно.
# Конфигурация fine-tuning recognition модели в PaddleOCR
# configs/rec/PP-OCRv4/en_PP-OCRv4_rec.yml (модифицированный)
Global:
use_gpu: true
epoch_num: 200
log_smooth_window: 20
print_batch_step: 10
save_model_dir: ./output/rec_custom/
save_epoch_step: 10
eval_batch_step: [0, 2000]
cal_metric_during_train: true
pretrained_model: ./pretrain_models/en_PP-OCRv4_rec_train/best_accuracy
checkpoints: null
save_inference_dir: null
character_dict_path: ./my_dict.txt # кастомный символьный словарь
max_text_length: 40
infer_mode: false
use_space_char: true
Train:
dataset:
name: SimpleDataSet
data_dir: ./train_data/rec/
label_file_list: ['./train_data/rec/train_labels.txt']
transforms:
- DecodeImage: {img_mode: BGR, channel_first: false}
- RecAug: # аугментации для recognition
use_tia: true # thin-plate spline деформация
- CTCLabelEncode:
max_text_length: 40
character_dict_path: ./my_dict.txt
- RecResizeImg:
image_shape: [3, 48, 320] # H=48 фиксированная
- KeepKeys:
keep_keys: ['image', 'label', 'length']
loader:
shuffle: true
batch_size_per_card: 128
drop_last: true
num_workers: 8
Optimizer:
name: Adam
beta1: 0.9
beta2: 0.999
lr:
name: Piecewise
decay_epochs: [100, 150]
values: [0.001, 0.0001, 0.00001]
TrOCR — трансформерный OCR для сложных случаев
TrOCR (Microsoft) — encoder-decoder архитектура на базе ViT + RoBERTa. Превосходит PaddleOCR на рукописном тексте и низком качестве изображений:
from transformers import (
TrOCRProcessor,
VisionEncoderDecoderModel,
Seq2SeqTrainer,
Seq2SeqTrainingArguments
)
from torch.utils.data import Dataset
from PIL import Image
import torch
class OCRDataset(Dataset):
def __init__(self, image_paths: list, labels: list,
processor: TrOCRProcessor):
self.image_paths = image_paths
self.labels = labels
self.processor = processor
def __len__(self): return len(self.image_paths)
def __getitem__(self, idx):
image = Image.open(self.image_paths[idx]).convert('RGB')
pixel_values = self.processor(
image, return_tensors='pt'
).pixel_values.squeeze()
# Токенизация текстовой метки
with self.processor.tokenizer.as_target_tokenizer():
labels = self.processor.tokenizer(
self.labels[idx],
padding='max_length',
max_length=64,
truncation=True,
return_tensors='pt'
).input_ids.squeeze()
labels[labels == self.processor.tokenizer.pad_token_id] = -100
return {'pixel_values': pixel_values, 'labels': labels}
def finetune_trocr(
train_paths: list, train_labels: list,
val_paths: list, val_labels: list,
base_model: str = 'microsoft/trocr-base-handwritten',
output_dir: str = './trocr_custom'
) -> VisionEncoderDecoderModel:
processor = TrOCRProcessor.from_pretrained(base_model)
model = VisionEncoderDecoderModel.from_pretrained(base_model)
# Конфигурация decoder
model.config.decoder_start_token_id = processor.tokenizer.cls_token_id
model.config.pad_token_id = processor.tokenizer.pad_token_id
model.config.vocab_size = model.config.decoder.vocab_size
model.config.eos_token_id = processor.tokenizer.sep_token_id
model.config.max_length = 64
model.config.early_stopping = True
model.config.no_repeat_ngram_size = 3
model.config.length_penalty = 2.0
model.config.num_beams = 4
train_dataset = OCRDataset(train_paths, train_labels, processor)
val_dataset = OCRDataset(val_paths, val_labels, processor)
training_args = Seq2SeqTrainingArguments(
output_dir=output_dir,
per_device_train_batch_size=8,
per_device_eval_batch_size=8,
num_train_epochs=50,
learning_rate=4e-5,
warmup_steps=500,
weight_decay=0.01,
fp16=True,
predict_with_generate=True,
evaluation_strategy='epoch',
save_strategy='epoch',
load_best_model_at_end=True,
metric_for_best_model='cer' # Character Error Rate
)
trainer = Seq2SeqTrainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset
)
trainer.train()
return model
Metrics OCR
CER (Character Error Rate) — основная метрика. Levenshtein distance / длина референсного текста. WER (Word Error Rate) — для задач, где важна точность слов.
| Применение | Приемлемый CER | Инструмент |
|---|---|---|
| Цифровые документы (invoice, ID) | < 0.5% | PaddleOCR PP-OCRv4 |
| Рукописный текст | < 3% | TrOCR-large |
| Промышленная маркировка | < 1% | PaddleOCR fine-tuned |
| Исторические документы | < 5% | TrOCR + domain adapt. |
| Номерные знаки | < 0.3% | ALPR специализированный |
Типичные проблемы
- Распознавание нулей и букв O — критично для серийных номеров. Решение: расширенный словарь с контекстными правилами (only digits для серийных полей)
- Сложная фоновая текстура — накладывается на символы. Решение: STN (Spatial Transformer Network) перед recognition для выравнивания
- Вертикальный текст — PaddleOCR плохо работает без direction classifier. Нужен отдельный классификатор ориентации
Сроки
| Задача | Срок |
|---|---|
| Fine-tuning PaddleOCR на кастомный шрифт/язык | 2–3 недели |
| TrOCR для рукописи с нуля | 4–6 недель |
| Полный OCR-pipeline с preprocessing | 6–10 недель |







