Image Classification
timm
Russian
English
manuscript
bookbinding
cultural-heritage
digital-humanities
convnext
dinov2
fine-tuned
Instructions to use Infarondus/Klein-marchen_Base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- timm
How to use Infarondus/Klein-marchen_Base with timm:
import timm model = timm.create_model("hf_hub:Infarondus/Klein-marchen_Base", pretrained=True) - Notebooks
- Google Colab
- Kaggle
| language: | |
| - ru | |
| - en | |
| license: apache-2.0 | |
| library_name: timm | |
| tags: | |
| - image-classification | |
| - manuscript | |
| - bookbinding | |
| - cultural-heritage | |
| - digital-humanities | |
| - convnext | |
| - dinov2 | |
| - fine-tuned | |
| pipeline_tag: image-classification | |
| # Kleine-Marchen — Binding Detector (ConvNeXt-Tiny) | |
| Модель на базе **ConvNeXt-Tiny (ImageNet-22k)** для определения, является ли изображение фотографией переплёта рукописи. | |
| Используется как **первая ступень** в двухэтапном пайплайне классификации переплётов рукописей РГБ. | |
| --- | |
| ## Назначение | |
| Бинарная классификация: | |
| - **Класс 1 — переплёт**: изображение является фотографией крышки переплёта рукописи | |
| - **Класс 0 — не переплёт**: страница текста, разворот, предметная съёмка и т.д. | |
| Модель решает задачу фильтрации при массовом скачивании изображений из цифровых архивов рукописей, где первые страницы оцифровки содержат как переплёты, так и текстовые страницы. | |
| --- | |
| ## Метрики | |
| Оценка проводилась на тестовом датасете из 1661 изображения (1136 переплётов + 525 не-переплётов) с применением TTA (5 трансформаций). | |
| | Метрика | Значение | | |
| | --- | --- | | |
| | Accuracy | **92.53%** | | |
| | F1-macro | **0.9139** | | |
| | Precision | 0.9127 | | |
| | Recall | 0.9152 | | |
| **Confusion Matrix:** | |
| | | Предсказано: не-переплёт | Предсказано: переплёт | | |
| | --- | --- | --- | | |
| | **Реально: не-переплёт** | 466 | 59 | | |
| | **Реально: переплёт** | 65 | 1071 | | |
| **Метрики на уверенных предсказаниях (confidence ≥ 0.6):** | |
| | Метрика | Значение | | |
| | --- | --- | | |
| | Доля уверенных предсказаний | 60.7% (1009 из 1661) | | |
| | Accuracy | **97.13%** | | |
| | F1-macro | **0.9689** | | |
| Использование порога уверенности позволяет автоматически делегировать сомнительные случаи на ручную проверку, при этом на уверенно классифицированных изображениях модель достигает точности выше 97%. | |
| --- | |
| ## Данные | |
| - **Источник**: фотографии из цифровых фондов РГБ | |
| - **Размер обучающей выборки**: ~525 изображений на каждый класс (75/25 hold-out validation) | |
| - **Классы**: переплёт / не переплёт (страницы текста, развороты, обложки-папки) | |
| - **Разрешение при обучении**: 320×320 px | |
| --- | |
| ## Архитектура | |
| **Базовая модель**: `convnext_tiny.fb_in22k` (ImageNet-22k pretrain, 28M параметров) | |
| **Стратегия обучения**: fine-tuning только классификационной головы при замороженном бэкбоне. Полная разморозка слоёв приводила к коллапсу обучения из-за ограниченного размера датасета. | |
| **Параметры обучения**: | |
| - Оптимизатор: AdamW (lr=3e-5, weight_decay=1e-4) | |
| - Scheduler: CosineAnnealingLR | |
| - Loss: CrossEntropyLoss | |
| - Batch size: 16 | |
| - Mixed precision training (AMP) | |
| **Аугментации** (Albumentations): RandomResizedCrop, HorizontalFlip, RandomRotate90, ShiftScaleRotate, ColorJitter, GridDistortion, GaussianBlur, ToGray. | |
| --- | |
| ## Использование | |
| Готовые скрипты доступны в репозитории: | |
| **<https://github.com/Infarondus/Kleine-marchen>** | |
| ### Быстрый старт | |
| ```python | |
| import torch | |
| import timm | |
| import numpy as np | |
| from PIL import Image | |
| import albumentations as A | |
| from albumentations.pytorch import ToTensorV2 | |
| IMAGE_SIZE = 320 | |
| transform = A.Compose([ | |
| A.LongestMaxSize(max_size=IMAGE_SIZE), | |
| A.PadIfNeeded(min_height=IMAGE_SIZE, min_width=IMAGE_SIZE, | |
| border_mode=0, value=[255, 255, 255]), | |
| A.Normalize(mean=[0.485, 0.456, 0.406], | |
| std=[0.229, 0.224, 0.225]), | |
| ToTensorV2(), | |
| ]) | |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
| model = timm.create_model('convnext_tiny_in22k', pretrained=False, num_classes=2) | |
| ckpt = torch.load('kleine-marchen_preprocess.pth', map_location=device, weights_only=False) | |
| model.load_state_dict(ckpt['model_state_dict']) | |
| model.to(device).eval() | |
| img = np.array(Image.open('page.jpg').convert('RGB')) | |
| tensor = transform(image=img)['image'].unsqueeze(0).to(device) | |
| with torch.no_grad(): | |
| probs = torch.softmax(model(tensor), dim=1)[0] | |
| print(f"Не переплёт: {probs[0]:.1%} | Переплёт: {probs[1]:.1%}") | |
| ``` | |
| --- | |
| ## Место в пайплайне | |
| Эта модель используется совместно с `binding-srednik-dinov2`: | |
| Изображение → [ConvNeXt: переплёт?] → [DINOv2: есть средник?] → Результат | |
| Скрипт двухступенчатого скрапера `scrape_bindings_v2.py` из репозитория **Kleine-Marchen** реализует этот пайплайн полностью. | |
| --- | |
| ## Ограничения | |
| - Не предназначена для определения типа переплёта или его элементов — только факт наличия переплёта на снимке | |
| - При сильно нестандартных условиях съёмки (белый фон, крупный план фрагмента) точность может снижаться | |
| - Около 39% входных изображений модель помечает как «сомнительные» (confidence < 0.6) — эти случаи рекомендуется проверять вручную или пропускать (так как большая часть пограничных случаев не является переплётом) | |
| --- | |
| # Kleine-Marchen — Binding Srednik Detector (DINOv2 Ensemble) | |
| Ансамбль из 5 моделей на базе **DINOv2 ViT-Small** для классификации переплётов рукописей по наличию **средника** — центрального декоративного элемента крышки переплёта. | |
| Модель разработана в рамках исследования рукописного фонда Российской государственной библиотеки (РГБ). | |
| --- | |
| ## Назначение | |
| Модель решает задачу бинарной классификации изображений переплётов: | |
| - **Класс 1 — со средником**: на крышке переплёта присутствует средник | |
| - **Класс 0 — без средника**: средник отсутствует | |
| Модель предназначена для автоматической предварительной сортировки больших коллекций фотографий переплётов рукописей. Окончательная верификация результатов производится специалистом. | |
| --- | |
| ## Метрики | |
| Оценка проводилась на полном датасете из 1136 изображений (568 со средником + 568 без средника) с применением ансамбля 5 моделей и TTA (5 трансформаций) — итого 25 предсказаний на изображение. | |
| | Метрика | Значение | | |
| | --- | --- | | |
| | Accuracy | **93.66%** | | |
| | F1-macro | **0.9366** | | |
| | Precision | 0.9368 | | |
| | Recall | 0.9366 | | |
| **Confusion Matrix:** | |
| | | Предсказано: без средника | Предсказано: со средником | | |
| | --- | --- | --- | | |
| | **Реально: без средника** | 537 | 31 | | |
| | **Реально: со средником** | 41 | 527 | | |
| **Анализ ошибок:** | |
| - Всего ошибочных предсказаний: 72 | |
| - False positive (без средника принят за со средником): 31 | |
| - False negative (со средником принят за без средника): 41 | |
| - Сомнительных предсказаний (confidence < 0.7): 123 (10.8% датасета) | |
| Симметричное распределение ошибок указывает на отсутствие смещения модели в сторону одного из классов. | |
| --- | |
| ## Данные | |
| - **Источник**: фотографии переплётов рукописей из цифровых фондов РГБ | |
| - **Размер обучающей выборки**: 568 изображений на каждый класс (1136 итого) | |
| - **Формат**: цветные фотографии переплётов | |
| - **Разрешение при обучении**: 280×280 px | |
| Датасет собирался итеративно: после каждого цикла обучения производился анализ ошибок и доразметка сложных случаев (изношенные переплёты, пограничные экземпляры). | |
| --- | |
| ## Архитектура | |
| **Базовая модель**: `vit_small_patch14_dinov2.lvd142m` (DINOv2 ViT-Small, 22M параметров, предобучен self-supervised на LVD-142M — 142 миллиона курированных изображений) | |
| **Голова классификатора**: | |
| LayerNorm(384) → Linear(384→256) → GELU → Dropout(0.3) → Linear(256→2) | |
| **Стратегия обучения**: двухфазный fine-tuning | |
| - Фаза 1 (6 эпох): обучение только головы, LR = 5e-4 | |
| - Фаза 2 (20 эпох): голова + последние 4 блока ViT, дифференциальный LR (голова: 3e-5, бэкбон: 2e-6) | |
| **Регуляризация**: drop_path_rate = 0.1, dropout = 0.3, label smoothing = 0.05 | |
| **Ансамбль**: 5 моделей (по одной на каждый фолд 5-fold стратифицированной кросс-валидации) с усреднением softmax-вероятностей. | |
| **TTA**: 5 трансформаций (оригинал, horizontal flip, vertical flip, resize ×1.05 + center crop, brightness/contrast jitter). | |
| --- | |
| ## Использование | |
| Готовые скрипты для обучения, оценки и инференса доступны в репозитории: | |
| **<https://github.com/Infarondus/Kleine-marchen>** | |
| ### Быстрый старт | |
| ```python | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import timm | |
| import numpy as np | |
| from PIL import Image | |
| import albumentations as A | |
| from albumentations.pytorch import ToTensorV2 | |
| MODEL_NAME = 'vit_small_patch14_dinov2.lvd142m' | |
| IMAGE_SIZE = 280 | |
| DINO_MEAN = [0.485, 0.456, 0.406] | |
| DINO_STD = [0.229, 0.224, 0.225] | |
| def build_model(): | |
| backbone = timm.create_model( | |
| MODEL_NAME, pretrained=False, num_classes=0, | |
| img_size=IMAGE_SIZE, dynamic_img_size=True, | |
| ) | |
| head = nn.Sequential( | |
| nn.LayerNorm(backbone.embed_dim), | |
| nn.Linear(backbone.embed_dim, 256), | |
| nn.GELU(), | |
| nn.Dropout(0.3), | |
| nn.Linear(256, 2), | |
| ) | |
| class DinoClassifier(nn.Module): | |
| def __init__(self, b, h): | |
| super().__init__() | |
| self.backbone, self.head = b, h | |
| def forward(self, x): | |
| return self.head(self.backbone(x)) | |
| return DinoClassifier(backbone, head) | |
| transform = A.Compose([ | |
| A.LongestMaxSize(max_size=IMAGE_SIZE), | |
| A.PadIfNeeded(min_height=IMAGE_SIZE, min_width=IMAGE_SIZE, | |
| border_mode=0, value=[255, 255, 255]), | |
| A.Normalize(mean=DINO_MEAN, std=DINO_STD), | |
| ToTensorV2(), | |
| ]) | |
| # Загрузка модели | |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
| model = build_model().to(device) | |
| ckpt = torch.load('fold_1_km.pth', map_location=device, weights_only=False) | |
| model.load_state_dict(ckpt['model_state_dict']) | |
| model.eval() | |
| # Инференс | |
| img = np.array(Image.open('binding.jpg').convert('RGB')) | |
| tensor = transform(image=img)['image'].unsqueeze(0).to(device) | |
| with torch.no_grad(): | |
| probs = F.softmax(model(tensor), dim=1)[0] | |
| print(f"Без средника: {probs[0]:.1%} | Со средником: {probs[1]:.1%}") | |
| ``` | |
| ### Рекомендуемый порог | |
| При использовании ансамбля рекомендуется порог **0.70–0.75** для отнесения изображения к одному из классов; при меньшей уверенности изображение целесообразно отправлять на ручную проверку специалистом. На уверенных предсказаниях точность ансамбля заметно превышает интегральную. | |
| --- | |
| ## Ограничения | |
| - Модель обучена на фотографиях переплётов РГБ и может хуже работать на изображениях из других коллекций (domain shift) | |
| - Сильно изношенные переплёты с плохо читаемым средником являются наиболее сложными случаями | |
| - Не предназначена для работы с изображениями страниц, не являющихся переплётами — для фильтрации используйте модель `klein-marchen_preprocess` на первом этапе | |
| - В обучающей выборке возможно присутствие небольшого количества ошибочно размеченных изображений (~2%), что отражается в нижней границе оценки точности | |
| --- | |
| ## Цитирование | |
| Если вы используете эту модель в исследовании, пожалуйста, укажите репозиторий: | |
| ```bibtex | |
| @misc{kleine-marchen-binding, | |
| author = {Infarondus}, | |
| title = {Kleine-Marchen — Binding Classification Pipeline for Manuscript Bindings}, | |
| year = {2026}, | |
| url = {https://github.com/Infarondus/Kleine-marchen} | |
| } | |
| ``` |