Обучение 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 Метрики 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 недель |







