Infarondus commited on
Commit
7d48698
·
verified ·
1 Parent(s): a28e517

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +285 -3
README.md CHANGED
@@ -1,3 +1,285 @@
1
- ---
2
- license: apache-2.0
3
- ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ ---
3
+ license: apache-2.0
4
+ language:
5
+ - ru
6
+ - en
7
+ base_model:
8
+ - facebook/convnext-tiny-224
9
+ - facebook/dinov2-small
10
+ pipeline_tag: image-classification
11
+ tags:
12
+ - manuscript
13
+ - bookbinding
14
+ - cultural-heritage
15
+ - digital-humanities
16
+ - convnext
17
+ - fine-tuned
18
+ library_name: timm
19
+ ---
20
+
21
+ # Kleine-Marchen — Binding Detector (ConvNeXt-Tiny)
22
+
23
+ Модель на базе **ConvNeXt-Tiny (ImageNet-22k)** для определения является ли изображение фотографией переплёта рукописи.
24
+
25
+ Используется как **первая ступень** в двухэтапном пайплайне классификации переплётов рукописей РГБ.
26
+
27
+ ---
28
+
29
+ ## Назначение
30
+
31
+ Бинарная классификация:
32
+
33
+ - **Класс 1 — переплёт**: изображение является фотографией крышки переплёта рукописи
34
+ - **Класс 0 — не переплёт**: страница текста, разворот, предметная съёмка и т.д.
35
+
36
+ Модель решает задачу фильтрации при массовом скачивании изображений из цифровых архивов рукописей, где первые страницы оцифровки содержат как переплёты, так и текстовые страницы.
37
+
38
+ ---
39
+
40
+ ## Метрики
41
+
42
+ | Метрика | Значение |
43
+ |---|---|
44
+ | Accuracy (валидация) | **96%** |
45
+
46
+ ---
47
+
48
+ ## Данные
49
+
50
+ - **Источник**: фотографии из фондов РГБ
51
+ - **Размер**: ~500 изображений на каждый класс
52
+ - **Классы**: переплёт / не переплёт (страницы текста, развороты)
53
+ - **Разрешение при обучении**: 320×320 px
54
+
55
+ ---
56
+
57
+ ## Архитектура
58
+
59
+ **Базовая модель**: `convnext_tiny.fb_in22k` (ImageNet-22k pretrain, 28M параметров)
60
+
61
+ **Стратегия обучения**: обучение только классификационной головы при замороженном бэкбоне.
62
+
63
+ ---
64
+
65
+ ## Использование
66
+
67
+ Готовые скрипты доступны в репозитории:
68
+
69
+ **[https://github.com/Infarondus/Kleine-marchen](https://github.com/Infarondus/Kleine-marchen)**
70
+
71
+ ### Быстрый старт
72
+
73
+ ```python
74
+ import torch
75
+ import timm
76
+ import numpy as np
77
+ from PIL import Image
78
+ import albumentations as A
79
+ from albumentations.pytorch import ToTensorV2
80
+
81
+ IMAGE_SIZE = 320
82
+
83
+ transform = A.Compose([
84
+ A.LongestMaxSize(max_size=IMAGE_SIZE),
85
+ A.PadIfNeeded(min_height=IMAGE_SIZE, min_width=IMAGE_SIZE,
86
+ border_mode=0, value=[255, 255, 255]),
87
+ A.Normalize(mean=[0.485, 0.456, 0.406],
88
+ std=[0.229, 0.224, 0.225]),
89
+ ToTensorV2(),
90
+ ])
91
+
92
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
93
+ model = timm.create_model('convnext_tiny_in22k', pretrained=False, num_classes=2)
94
+ ckpt = torch.load('kleine-marchen_preprocess.pth', map_location=device, weights_only=False)
95
+ model.load_state_dict(ckpt['model_state_dict'])
96
+ model.to(device).eval()
97
+
98
+ img = np.array(Image.open('page.jpg').convert('RGB'))
99
+ tensor = transform(image=img)['image'].unsqueeze(0).to(device)
100
+ with torch.no_grad():
101
+ probs = torch.softmax(model(tensor), dim=1)[0]
102
+
103
+ print(f"Не переплёт: {probs[0]:.1%} | Переплёт: {probs[1]:.1%}")
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Место в пайплайне
109
+
110
+ Эта модель используется совместно с `binding-srednik-dinov2`:
111
+
112
+ ```
113
+ Изображение → [ConvNeXt: переплёт?] → [DINOv2: есть средник?] → Результат
114
+ ```
115
+
116
+ Скрипт двухступенчатого скрапера `scrape_bindings_v2.py` из репозитория **Kleine-Marchen** реализует этот пайплайн полностью.
117
+
118
+ ---
119
+
120
+ ## Ограничения
121
+
122
+ - Не предназначена для определения типа переплёта или его элементов — только факт наличия переплёта на снимке
123
+ - При сильно нестандартных условиях съёмки (белый фон, крупный план фрагмента) точность может снижаться
124
+
125
+ ---
126
+
127
+ # Kleine-Marchen — Binding Srednik Detector (DINOv2 Ensemble)
128
+
129
+ Ансамбль из 5 моделей на базе **DINOv2 ViT-Small** для классификации переплётов рукописей по наличию **средника** — центрального декоративного элемента крышки переплёта.
130
+
131
+ Модель разработана в рамках исследования рукописного фонда Российской государственной библиотеки (РГБ).
132
+
133
+ ---
134
+
135
+ ## Назначение
136
+
137
+ Модель решает задачу бинарной классификации изображений переплётов:
138
+
139
+ - **Класс 1 — со средником**: на крышке переплёта присутствует средник
140
+ - **Класс 0 — без средника**: средник отсутствует
141
+
142
+ Модель предназначена для автоматической предварительной сортировки больших коллекций фотографий переплётов рукописей. Окончательная верификация результатов производится специалистом.
143
+
144
+ ---
145
+
146
+ ## Метрики
147
+
148
+ Оценка проводилась методом 5-fold стратифицированной кросс-валидации.
149
+
150
+ | Метрика | Значение |
151
+ |---|---|
152
+ | Accuracy (OOF Ensemble) | **94.50%** |
153
+ | F1-macro (OOF Ensemble) | **0.9450** |
154
+ | Precision | 0.9454 |
155
+ | Recall | 0.9450 |
156
+
157
+ **Confusion Matrix (OOF, все 5 фолдов):**
158
+
159
+ | | Предсказано: без средника | Предсказано: со средником |
160
+ |---|---|---|
161
+ | **Реально: без средника** | 471 | 20 |
162
+ | **Реально: со средником** | 34 | 457 |
163
+
164
+ ---
165
+
166
+ ## Данные
167
+
168
+ - **Источник**: фотографии переплётов рукописей из фондов РГБ
169
+ - **Размер обучающей выборки**: 567 изображений на каждый класс (1134 итого)
170
+ - **Формат**: цветные фотографии переплётов на тёмном фоне
171
+ - **Разрешение при обучении**: 280×280 px
172
+
173
+ Датасет собирался итеративно: после каждого цикла обучения производился анализ ошибок и доразметка сложных случаев (изношенные переплёты, пограничные экземпляры).
174
+
175
+ ---
176
+
177
+ ## Архитектура
178
+
179
+ **Базовая модель**: `vit_small_patch14_dinov2.lvd142m` (DINOv2 ViT-Small, 22M параметров)
180
+
181
+ **Голова классификатора**:
182
+ ```
183
+ LayerNorm(384) → Linear(384→256) → GELU → Dropout(0.3) → Linear(256→2)
184
+ ```
185
+
186
+ **Стратегия обучения**: двухфазный fine-tuning
187
+ - Фаза 1 (6 эпох): обучение только головы, LR = 5e-4
188
+ - Фаза 2 (20 эпох): голова + последние 4 блока ViT, дифференциальный LR (голова: 3e-5, бэкбон: 2e-6)
189
+
190
+ **Ансамбль**: 5 моделей (по одной на каждый фолд) с усреднением вероятностей + TTA (5 аугментаций)
191
+
192
+ ---
193
+
194
+ ## Использование
195
+
196
+ Готовые скрипты для обучения, оценки и инференса доступны в репозитории:
197
+
198
+ **[https://github.com/Infarondus/Kleine-marchen](https://github.com/Infarondus/Kleine-marchen)**
199
+
200
+ ### Быстрый старт
201
+
202
+ ```python
203
+ import torch
204
+ import torch.nn as nn
205
+ import torch.nn.functional as F
206
+ import timm
207
+ import numpy as np
208
+ from PIL import Image
209
+ import albumentations as A
210
+ from albumentations.pytorch import ToTensorV2
211
+
212
+ MODEL_NAME = 'vit_small_patch14_dinov2.lvd142m'
213
+ IMAGE_SIZE = 280
214
+ DINO_MEAN = [0.485, 0.456, 0.406]
215
+ DINO_STD = [0.229, 0.224, 0.225]
216
+
217
+ def build_model():
218
+ backbone = timm.create_model(
219
+ MODEL_NAME, pretrained=False, num_classes=0,
220
+ img_size=IMAGE_SIZE, dynamic_img_size=True,
221
+ )
222
+ head = nn.Sequential(
223
+ nn.LayerNorm(backbone.embed_dim),
224
+ nn.Linear(backbone.embed_dim, 256),
225
+ nn.GELU(),
226
+ nn.Dropout(0.3),
227
+ nn.Linear(256, 2),
228
+ )
229
+ class DinoClassifier(nn.Module):
230
+ def __init__(self, b, h):
231
+ super().__init__()
232
+ self.backbone, self.head = b, h
233
+ def forward(self, x):
234
+ return self.head(self.backbone(x))
235
+ return DinoClassifier(backbone, head)
236
+
237
+ transform = A.Compose([
238
+ A.LongestMaxSize(max_size=IMAGE_SIZE),
239
+ A.PadIfNeeded(min_height=IMAGE_SIZE, min_width=IMAGE_SIZE,
240
+ border_mode=0, value=[255, 255, 255]),
241
+ A.Normalize(mean=DINO_MEAN, std=DINO_STD),
242
+ ToTensorV2(),
243
+ ])
244
+
245
+ # Загрузка модели
246
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
247
+ model = build_model().to(device)
248
+ ckpt = torch.load('fold_1_km.pth', map_location=device, weights_only=False)
249
+ model.load_state_dict(ckpt['model_state_dict'])
250
+ model.eval()
251
+
252
+ # Инференс
253
+ img = np.array(Image.open('binding.jpg').convert('RGB'))
254
+ tensor = transform(image=img)['image'].unsqueeze(0).to(device)
255
+ with torch.no_grad():
256
+ probs = F.softmax(model(tensor), dim=1)[0]
257
+
258
+ print(f"Без средника: {probs[0]:.1%} | Со средником: {probs[1]:.1%}")
259
+ ```
260
+
261
+ ### Рекомендуемый порог
262
+
263
+ При использовании ансамбля рекомендуется порог **0.55–0.75** для класса «со средником» в зависимости от допустимого уровня ложных срабатываний.
264
+
265
+ ---
266
+
267
+ ## Ограничения
268
+
269
+ - Модель обучена на фотографиях переплётов РГБ и может хуже работать на изображениях из других коллекций (domain shift)
270
+ - Сильно изношенные переплёты с плохо читаемым средником являются наиболее сложными случаями
271
+ - Не предназначена для работы с изображениями страниц, не являющихся переплётами — для фильтрации используйте модель `binding-detector-convnext` на первом этапе
272
+
273
+ ---
274
+ ## Цитирование
275
+ Если вы используете эту модель в исследовании, пожалуйста, укажите репозиторий:
276
+ ```
277
+ @misc{kleine-marchen-binding,
278
+ author = {Infarondus},
279
+ title = {Kleine-Marchen — Binding Detector (ConvNeXt-Tiny)},
280
+ year = {2026},
281
+ url = {https://github.com/Infarondus/Kleine-marchen}
282
+ }
283
+ ```
284
+
285
+ ---