| --- |
| --- |
| license: apache-2.0 |
| language: |
| - ru |
| - en |
| base_model: |
| - facebook/convnext-tiny-224 |
| - facebook/dinov2-small |
| pipeline_tag: image-classification |
| tags: |
| - manuscript |
| - bookbinding |
| - cultural-heritage |
| - digital-humanities |
| - convnext |
| - fine-tuned |
| library_name: timm |
| --- |
| |
| # Kleine-Marchen — Binding Detector (ConvNeXt-Tiny) |
|
|
| Модель на базе **ConvNeXt-Tiny (ImageNet-22k)** для определения является ли изображение фотографией переплёта рукописи. |
|
|
| Используется как **первая ступень** в двухэтапном пайплайне классификации переплётов рукописей РГБ. |
|
|
| --- |
|
|
| ## Назначение |
|
|
| Бинарная классификация: |
|
|
| - **Класс 1 — переплёт**: изображение является фотографией крышки переплёта рукописи |
| - **Класс 0 — не переплёт**: страница текста, разворот, предметная съёмка и т.д. |
|
|
| Модель решает задачу фильтрации при массовом скачивании изображений из цифровых архивов рукописей, где первые страницы оцифровки содержат как переплёты, так и текстовые страницы. |
|
|
| --- |
|
|
| ## Метрики |
|
|
| | Метрика | Значение | |
| |---|---| |
| | Accuracy (валидация) | **96%** | |
|
|
| --- |
|
|
| ## Данные |
|
|
| - **Источник**: фотографии из фондов РГБ |
| - **Размер**: ~500 изображений на каждый класс |
| - **Классы**: переплёт / не переплёт (страницы текста, развороты) |
| - **Разрешение при обучении**: 320×320 px |
|
|
| --- |
|
|
| ## Архитектура |
|
|
| **Базовая модель**: `convnext_tiny.fb_in22k` (ImageNet-22k pretrain, 28M параметров) |
|
|
| **Стратегия обучения**: обучение только классификационной головы при замороженном бэкбоне. |
|
|
| --- |
|
|
| ## Использование |
|
|
| Готовые скрипты доступны в репозитории: |
|
|
| **[https://github.com/Infarondus/Kleine-marchen](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** реализует этот пайплайн полностью. |
|
|
| --- |
|
|
| ## Ограничения |
|
|
| - Не предназначена для определения типа переплёта или его элементов — только факт наличия переплёта на снимке |
| - При сильно нестандартных условиях съёмки (белый фон, крупный план фрагмента) точность может снижаться |
|
|
| --- |
|
|
| # Kleine-Marchen — Binding Srednik Detector (DINOv2 Ensemble) |
|
|
| Ансамбль из 5 моделей на базе **DINOv2 ViT-Small** для классификации переплётов рукописей по наличию **средника** — центрального декоративного элемента крышки переплёта. |
|
|
| Модель разработана в рамках исследования рукописного фонда Российской государственной библиотеки (РГБ). |
|
|
| --- |
|
|
| ## Назначение |
|
|
| Модель решает задачу бинарной классификации изображений переплётов: |
|
|
| - **Класс 1 — со средником**: на крышке переплёта присутствует средник |
| - **Класс 0 — без средника**: средник отсутствует |
|
|
| Модель предназначена для автоматической предварительной сортировки больших коллекций фотографий переплётов рукописей. Окончательная верификация результатов производится специалистом. |
|
|
| --- |
|
|
| ## Метрики |
|
|
| Оценка проводилась методом 5-fold стратифицированной кросс-валидации. |
|
|
| | Метрика | Значение | |
| |---|---| |
| | Accuracy (OOF Ensemble) | **94.50%** | |
| | F1-macro (OOF Ensemble) | **0.9450** | |
| | Precision | 0.9454 | |
| | Recall | 0.9450 | |
|
|
| **Confusion Matrix (OOF, все 5 фолдов):** |
|
|
| | | Предсказано: без средника | Предсказано: со средником | |
| |---|---|---| |
| | **Реально: без средника** | 471 | 20 | |
| | **Реально: со средником** | 34 | 457 | |
|
|
| --- |
|
|
| ## Данные |
|
|
| - **Источник**: фотографии переплётов рукописей из фондов РГБ |
| - **Размер обучающей выборки**: 567 изображений на каждый класс (1134 итого) |
| - **Формат**: цветные фотографии переплётов на тёмном фоне |
| - **Разрешение при обучении**: 280×280 px |
|
|
| Датасет собирался итеративно: после каждого цикла обучения производился анализ ошибок и доразметка сложных случаев (изношенные переплёты, пограничные экземпляры). |
|
|
| --- |
|
|
| ## Архитектура |
|
|
| **Базовая модель**: `vit_small_patch14_dinov2.lvd142m` (DINOv2 ViT-Small, 22M параметров) |
|
|
| **Голова классификатора**: |
| ``` |
| 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) |
|
|
| **Ансамбль**: 5 моделей (по одной на каждый фолд) с усреднением вероятностей + TTA (5 аугментаций) |
|
|
| --- |
|
|
| ## Использование |
|
|
| Готовые скрипты для обучения, оценки и инференса доступны в репозитории: |
|
|
| **[https://github.com/Infarondus/Kleine-marchen](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.55–0.75** для класса «со средником» в зависимости от допустимого уровня ложных срабатываний. |
|
|
| --- |
|
|
| ## Ограничения |
|
|
| - Модель обучена на фотографиях переплётов РГБ и может хуже работать на изображениях из других коллекций (domain shift) |
| - Сильно изношенные переплёты с плохо читаемым средником являются наиболее сложными случаями |
| - Не предназначена для работы с изображениями страниц, не являющихся переплётами — для фильтрации используйте модель `binding-detector-convnext` на первом этапе |
|
|
| --- |
| ## Цитирование |
| Если вы используете эту модель в исследовании, пожалуйста, укажите репозиторий: |
| ``` |
| @misc{kleine-marchen-binding, |
| author = {Infarondus}, |
| title = {Kleine-Marchen — Binding Detector (ConvNeXt-Tiny)}, |
| year = {2026}, |
| url = {https://github.com/Infarondus/Kleine-marchen_Base} |
| } |
| ``` |
|
|
| --- |
|
|