Image Classification
Transformers
Tibetan
tibetan
script-classification
dinov3
binary
karma689 commited on
Commit
e50372c
·
verified ·
1 Parent(s): bbf8dd6

Add Gyuyig vs Tsugdri binary classifier weights, metrics, and training history

Browse files
README.md ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - bo
4
+ license: apache-2.0
5
+ tags:
6
+ - image-classification
7
+ - tibetan
8
+ - script-classification
9
+ - dinov3
10
+ - binary
11
+ library_name: transformers
12
+ pipeline_tag: image-classification
13
+ base_model: facebook/dinov3-vits16-pretrain-lvd1689m
14
+ datasets:
15
+ - BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset
16
+ metrics:
17
+ - f1
18
+ - accuracy
19
+ - auc
20
+ ---
21
+
22
+ # Gyuyig vs Tsugdri Binary Script Classifier (DINOv3 ViT-S)
23
+
24
+ Fine-tuned [DINOv3 ViT-S](https://huggingface.co/facebook/dinov3-vits16-pretrain-lvd1689m) for parent script classification:
25
+
26
+ **Gyuyig**, **Tsugdri**
27
+
28
+ **Experiment:** `dinov3_gyuyig_tsugdri_sub_warmstart` (`gyuyig_tsugdri_binary_classification`)
29
+ **Pooling:** ViT **CLS token** (`last_hidden_state[:, 0, :]`)
30
+ **Weights:** `final_model.pt` (best validation macro-F1 across stages A/B/C)
31
+
32
+ **Warm-start:** [BDRC/4-class-balanced-script-classifier](https://huggingface.co/BDRC/4-class-balanced-script-classifier) (`final_model.pt` — prior test acc 82.6%, macro-F1 0.833)
33
+
34
+ ## Data
35
+
36
+ | Split | Source |
37
+ |-------|--------|
38
+ | Train / val / test | [BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset](https://huggingface.co/datasets/BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset) |
39
+
40
+ Test split: balanced benchmark (60 images per parent class, held out of training).
41
+
42
+ ## Preprocessing
43
+
44
+ | Split | Mode | Size |
45
+ |-------|------|-----:|
46
+ | train | `resize_letterbox` | 448 |
47
+ | val | `resize_letterbox` | 448 |
48
+ | test | `resize_letterbox` | 448 |
49
+
50
+ ## Validation metrics (n=60)
51
+
52
+ | Metric | Value |
53
+ |--------|------:|
54
+ | Accuracy | 85.0% |
55
+ | Macro F1 | 0.850 |
56
+ | Weighted F1 | 0.850 |
57
+ | AUC-ROC | 0.902 |
58
+ | Loss | 0.4520 |
59
+
60
+ **Best checkpoint:** `best_stage_c_last_blocks.pt` epoch 7 val macro-F1 0.850
61
+
62
+ ### Per-class (validation)
63
+
64
+ ```
65
+ precision recall f1-score support
66
+
67
+ Gyuyig 0.89 0.80 0.84 30
68
+ Tsugdri 0.82 0.90 0.86 30
69
+
70
+ accuracy 0.85 60
71
+ macro avg 0.85 0.85 0.85 60
72
+ weighted avg 0.85 0.85 0.85 60
73
+ ```
74
+
75
+ ## Test / benchmark metrics (n=120)
76
+
77
+ | Metric | Value |
78
+ |--------|------:|
79
+ | Accuracy | 80.8% |
80
+ | Macro F1 | 0.808 |
81
+ | Weighted F1 | 0.808 |
82
+ | AUC-ROC | 0.868 |
83
+ | Loss | 0.5354 |
84
+
85
+ ### Per-class (test)
86
+
87
+ ```
88
+ precision recall f1-score support
89
+
90
+ Gyuyig 0.79 0.83 0.81 60
91
+ Tsugdri 0.82 0.78 0.80 60
92
+
93
+ accuracy 0.81 120
94
+ macro avg 0.81 0.81 0.81 120
95
+ weighted avg 0.81 0.81 0.81 120
96
+ ```
97
+
98
+ ## Training
99
+
100
+ | Stage | Epochs | LR head | LR backbone | Unfrozen blocks |
101
+ |-------|-------:|--------:|------------:|----------------:|
102
+ | A | 7 | 0.0005 | — | 0 |
103
+ | B | 10 | 0.0001 | 1e-05 | 4 |
104
+ | C | 12 | 5e-05 | 1.5e-05 | 8 |
105
+
106
+ | Setting | Value |
107
+ |---------|-------|
108
+ | Scheduler | `cosine_warmup` |
109
+ | Class weights | `custom` |
110
+ | Label smoothing | 0.05 |
111
+ | Dropout | 0.1 |
112
+
113
+ ## Confusion matrix (test)
114
+
115
+ ![Confusion matrix](confusion_matrix.png)
116
+
117
+ | True \ Pred | Gyuyig | Tsugdri |
118
+ |---|---:|---:|
119
+ | **Gyuyig** | 50 | 10 |
120
+ | **Tsugdri** | 13 | 47 |
121
+
122
+ ## Files
123
+
124
+ | File | Description |
125
+ |------|-------------|
126
+ | `final_model.pt` | Best val-F1 weights + label maps |
127
+ | `results.json` | Full metrics, history, warm-start info |
128
+ | `config.yaml` | Training config |
129
+ | `model_card.json` | Summary metadata |
130
+ | `confusion_matrix.json` / `.png` | Test CM |
131
+ | `training_history.png` | Stage loss / val F1 curves |
132
+ | `split_stats.json` / `.md` | Per-class split counts |
133
+ | `inference.py` | Classify image paths |
134
+ | `requirements-inference.txt` | Pip deps |
135
+
136
+ ## Inference
137
+
138
+ ```bash
139
+ pip install -r requirements-inference.txt
140
+ python inference.py --checkpoint final_model.pt --image path/to/page.jpg --preprocess resize_letterbox --preprocess-size 448
141
+ ```
142
+
143
+ ## Reproduce training
144
+
145
+ ```bash
146
+ python experiments/gyuyig-tsugdri/train.py --config experiments/gyuyig-tsugdri/config_warmstart.yaml
147
+ ```
148
+
149
+ **Model repo:** [BDRC/gyuyig-tsugdri-binary-script-classifier](https://huggingface.co/BDRC/gyuyig-tsugdri-binary-script-classifier)
config.yaml ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ experiment: dinov3_gyuyig_tsugdri_sub_warmstart
2
+ task: gyuyig_tsugdri_binary_classification
3
+
4
+ balanced_dataset_repo: BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset
5
+ val_ratio: 0.15
6
+
7
+ warmstart_repo: BDRC/4-class-balanced-script-classifier
8
+ warmstart_checkpoint_file: final_model.pt
9
+
10
+ output_dir: checkpoints
11
+ model_id: facebook/dinov3-vits16-pretrain-lvd1689m
12
+ seed: 42
13
+ batch_size: 16
14
+ num_workers: 8
15
+ no_amp: false
16
+ no_weighted_sampler: true
17
+ skip_stage_c: false
18
+ gradient_checkpointing: true
19
+
20
+ train_preprocess: resize_letterbox
21
+ val_preprocess: resize_letterbox
22
+ test_preprocess: resize_letterbox
23
+ preprocess_size: 448
24
+
25
+ pooling: cls_token
26
+
27
+ epochs_a: 7
28
+ epochs_b: 10
29
+ epochs_c: 12
30
+ unfreeze_blocks_b: 4
31
+ unfreeze_blocks_c: 8
32
+
33
+ lr_head_a: 5.0e-4
34
+ lr_backbone_b: 1.0e-5
35
+ lr_head_b: 1.0e-4
36
+ lr_backbone_c: 1.5e-5
37
+ lr_head_c: 5.0e-5
38
+
39
+ scheduler: cosine_warmup
40
+ warmup_epochs_a: 0
41
+ warmup_epochs_b: 1
42
+ warmup_epochs_c: 1
43
+ warmup_start_factor: 0.01
44
+ min_lr_ratio: 0.01
45
+
46
+ weight_decay: 0.02
47
+ grad_clip_norm: 1.0
48
+ dropout: 0.1
49
+ label_smoothing: 0.05
50
+ class_weight_mode: sqrt_inverse_freq
51
+
52
+ early_stop_patience: 6
53
+ early_stop_min_delta: 0.001
confusion_matrix.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "split": "test",
3
+ "labels": [
4
+ "Gyuyig",
5
+ "Tsugdri"
6
+ ],
7
+ "matrix": [
8
+ [
9
+ 50,
10
+ 10
11
+ ],
12
+ [
13
+ 13,
14
+ 47
15
+ ]
16
+ ],
17
+ "test_metrics": {
18
+ "loss": 0.5353951374689738,
19
+ "accuracy": 0.8083333333333333,
20
+ "macro_f1": 0.8082134667500521,
21
+ "weighted_f1": 0.8082134667500521,
22
+ "auc_roc": 0.8677777777777778
23
+ },
24
+ "val_metrics": {
25
+ "loss": 0.45204214652379354,
26
+ "accuracy": 0.85,
27
+ "macro_f1": 0.849624060150376,
28
+ "weighted_f1": 0.849624060150376,
29
+ "auc_roc": 0.9022222222222223
30
+ },
31
+ "preprocess": {
32
+ "train": "resize_letterbox",
33
+ "val": "resize_letterbox",
34
+ "test": "resize_letterbox",
35
+ "size": 448
36
+ },
37
+ "train_dataset": "BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset",
38
+ "benchmark_per_parent": 60,
39
+ "experiment": "dinov3_gyuyig_tsugdri_sub_warmstart",
40
+ "repo_id": "BDRC/gyuyig-tsugdri-binary-script-classifier"
41
+ }
confusion_matrix.png ADDED
final_model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6753c4b49f6fa52b6d4580b926070cdea8dba7908f0e3a562f60dd42512e3148
3
+ size 86670182
inference.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Standalone DINOv3 script classifier inference (copied to Hub model repo as ``inference.py``)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ from pathlib import Path
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ from PIL import Image
12
+ from transformers import AutoImageProcessor, AutoModel
13
+
14
+ DINOV3_MODEL_ID = "facebook/dinov3-vits16-pretrain-lvd1689m"
15
+
16
+
17
+ class DINOv3Classifier(nn.Module):
18
+ def __init__(self, model_id: str, num_classes: int, dropout: float = 0.1):
19
+ super().__init__()
20
+ self.backbone = AutoModel.from_pretrained(model_id)
21
+ hidden = self.backbone.config.hidden_size
22
+ self.head = nn.Sequential(
23
+ nn.LayerNorm(hidden),
24
+ nn.Dropout(dropout),
25
+ nn.Linear(hidden, 128),
26
+ nn.GELU(),
27
+ nn.Dropout(dropout),
28
+ nn.Linear(128, num_classes),
29
+ )
30
+
31
+ def forward(self, pixel_values):
32
+ out = self.backbone(pixel_values=pixel_values)
33
+ cls = out.last_hidden_state[:, 0, :]
34
+ return self.head(cls)
35
+
36
+
37
+ def _resize_short_edge(img: Image.Image, target: int) -> Image.Image:
38
+ w, h = img.size
39
+ if h <= w:
40
+ new_h = target
41
+ new_w = max(1, int(w * target / h))
42
+ else:
43
+ new_w = target
44
+ new_h = max(1, int(h * target / w))
45
+ return img.resize((new_w, new_h), Image.BICUBIC)
46
+
47
+
48
+ def _center_crop(img: Image.Image, size: int = 224) -> Image.Image:
49
+ img = _resize_short_edge(img, size)
50
+ w, h = img.size
51
+ left = max(0, (w - size) // 2)
52
+ top = max(0, (h - size) // 2)
53
+ crop = img.crop((left, top, left + size, top + size))
54
+ if crop.size != (size, size):
55
+ padded = Image.new("RGB", (size, size), (255, 255, 255))
56
+ padded.paste(crop, (0, 0))
57
+ return padded
58
+ return crop
59
+
60
+
61
+ def apply_preprocess(img: Image.Image, mode: str | None, *, size: int = 224) -> Image.Image:
62
+ if not mode or mode == "none":
63
+ return img
64
+ if mode in ("center_crop", "center_crop_whole_page"):
65
+ return _center_crop(img, size)
66
+ raise ValueError(f"Unknown preprocess mode: {mode!r}")
67
+
68
+
69
+ def label_order(ckpt: dict) -> list[str]:
70
+ idx = ckpt.get("idx_to_label") or {}
71
+ if idx:
72
+ return [str(idx[k]) for k in sorted(idx.keys(), key=lambda x: int(x))]
73
+ raw = ckpt.get("label_to_idx") or {}
74
+ if raw:
75
+ return sorted(raw.keys(), key=lambda k: raw[k])
76
+ raise KeyError("checkpoint missing idx_to_label / label_to_idx")
77
+
78
+
79
+ @torch.no_grad()
80
+ def predict(model, processor, image_path: Path, device, *, preprocess: str | None, size: int):
81
+ img = Image.open(image_path).convert("RGB")
82
+ img = apply_preprocess(img, preprocess, size=size)
83
+ pv = processor(images=img, return_tensors="pt")["pixel_values"].to(device)
84
+ logits = model(pv)
85
+ probs = torch.softmax(logits, dim=1).squeeze(0).cpu()
86
+ pred = int(probs.argmax())
87
+ return pred, probs.tolist()
88
+
89
+
90
+ def main() -> None:
91
+ ap = argparse.ArgumentParser(description="DINOv3 Tibetan script page classifier")
92
+ ap.add_argument(
93
+ "--checkpoint",
94
+ type=Path,
95
+ default=Path("final_model.pt"),
96
+ help="Weights file (default: final_model.pt in cwd)",
97
+ )
98
+ ap.add_argument("--image", type=Path, nargs="+", required=True)
99
+ ap.add_argument(
100
+ "--preprocess",
101
+ default="none",
102
+ help="none (full page, DINO processor resize) | center_crop (224 short edge)",
103
+ )
104
+ ap.add_argument("--preprocess-size", type=int, default=224)
105
+ ap.add_argument("--model-id", default=DINOV3_MODEL_ID)
106
+ args = ap.parse_args()
107
+
108
+ ckpt = torch.load(args.checkpoint, map_location="cpu", weights_only=False)
109
+ classes = label_order(ckpt)
110
+ idx_to_label = {i: lab for i, lab in enumerate(classes)}
111
+
112
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
113
+ model = DINOv3Classifier(args.model_id, num_classes=len(classes)).to(device)
114
+ model.load_state_dict(ckpt["model_state_dict"])
115
+ model.eval()
116
+ processor = AutoImageProcessor.from_pretrained(args.model_id)
117
+ prep = None if args.preprocess in ("none", "") else args.preprocess
118
+
119
+ for path in args.image:
120
+ pred, probs = predict(
121
+ model, processor, path, device, preprocess=prep, size=args.preprocess_size
122
+ )
123
+ name = idx_to_label[pred]
124
+ conf = probs[pred]
125
+ print(f"{path.name}: {name} ({conf:.3f})")
126
+ top3 = sorted(enumerate(probs), key=lambda x: -x[1])[:3]
127
+ for i, p in top3:
128
+ print(f" {idx_to_label[i]:16s} {p:.3f}")
129
+
130
+
131
+ if __name__ == "__main__":
132
+ main()
model_card.json ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "repo_id": "BDRC/gyuyig-tsugdri-binary-script-classifier",
3
+ "train_dataset_id": "BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset",
4
+ "warmstart_repo": "BDRC/4-class-balanced-script-classifier",
5
+ "task": "gyuyig_tsugdri_binary_classification",
6
+ "experiment": "dinov3_gyuyig_tsugdri_sub_warmstart",
7
+ "classes": [
8
+ "Gyuyig",
9
+ "Tsugdri"
10
+ ],
11
+ "pooling": "cls_token",
12
+ "preprocess": {
13
+ "train": "resize_letterbox",
14
+ "val": "resize_letterbox",
15
+ "test": "resize_letterbox",
16
+ "size": 448
17
+ },
18
+ "warmstart": {
19
+ "warmstart_repo": "BDRC/4-class-balanced-script-classifier",
20
+ "warmstart_checkpoint": null,
21
+ "warmstart_checkpoint_file": "final_model.pt",
22
+ "checkpoint_test_metrics": {
23
+ "loss": 0.6574946736847913,
24
+ "accuracy": 0.825925925925926,
25
+ "macro_f1": 0.8326187473728457,
26
+ "weighted_f1": 0.82908384875598
27
+ },
28
+ "warmstart_pooling": "cls_token"
29
+ },
30
+ "training_config": {
31
+ "lr_head_a": 0.0005,
32
+ "lr_backbone_b": 1e-05,
33
+ "lr_head_b": 0.0001,
34
+ "lr_backbone_c": 1.5e-05,
35
+ "lr_head_c": 5e-05,
36
+ "epochs_a": 7,
37
+ "epochs_b": 10,
38
+ "epochs_c": 12,
39
+ "unfreeze_blocks_b": 4,
40
+ "unfreeze_blocks_c": 8,
41
+ "skip_stage_c": false,
42
+ "scheduler": "cosine_warmup",
43
+ "warmup_epochs_a": 0,
44
+ "warmup_epochs_b": 1,
45
+ "warmup_epochs_c": 1,
46
+ "warmup_start_factor": 0.01,
47
+ "min_lr_ratio": 0.01,
48
+ "weight_decay": 0.02,
49
+ "grad_clip_norm": 1.0,
50
+ "class_weight_mode": "custom",
51
+ "class_weights": [
52
+ 1.0,
53
+ 1.0
54
+ ],
55
+ "label_smoothing": 0.05,
56
+ "dropout": 0.1,
57
+ "early_stop_patience": 6,
58
+ "early_stop_min_delta": 0.001,
59
+ "resolved_class_weights": [
60
+ 1.0,
61
+ 1.0
62
+ ],
63
+ "pooling": "cls_token",
64
+ "lambda_consistency": 0.0,
65
+ "flip_op": "rot180"
66
+ },
67
+ "best_checkpoint": {
68
+ "path": "best_stage_c_last_blocks.pt",
69
+ "epoch": 7,
70
+ "val_macro_f1": 0.849624060150376
71
+ },
72
+ "val_metrics": {
73
+ "loss": 0.45204214652379354,
74
+ "accuracy": 0.85,
75
+ "macro_f1": 0.849624060150376,
76
+ "weighted_f1": 0.849624060150376,
77
+ "auc_roc": 0.9022222222222223
78
+ },
79
+ "test_metrics": {
80
+ "loss": 0.5353951374689738,
81
+ "accuracy": 0.8083333333333333,
82
+ "macro_f1": 0.8082134667500521,
83
+ "weighted_f1": 0.8082134667500521,
84
+ "auc_roc": 0.8677777777777778
85
+ }
86
+ }
requirements-inference.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ torch>=2.0
2
+ transformers>=4.40
3
+ pillow>=10.0
results.json ADDED
@@ -0,0 +1,454 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "experiment": "dinov3_gyuyig_tsugdri_sub_warmstart",
3
+ "run_subdir": null,
4
+ "task": "gyuyig_tsugdri_binary_classification",
5
+ "balanced_parquet_dir": null,
6
+ "balanced_dataset_repo": "BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset",
7
+ "data_source": "hf",
8
+ "benchmark_dataset_repo": null,
9
+ "benchmark_per_parent": 60,
10
+ "preprocess": {
11
+ "train": "resize_letterbox",
12
+ "val": "resize_letterbox",
13
+ "test": "resize_letterbox",
14
+ "size": 448
15
+ },
16
+ "pooling": "cls_token",
17
+ "training_config": {
18
+ "lr_head_a": 0.0005,
19
+ "lr_backbone_b": 1e-05,
20
+ "lr_head_b": 0.0001,
21
+ "lr_backbone_c": 1.5e-05,
22
+ "lr_head_c": 5e-05,
23
+ "epochs_a": 7,
24
+ "epochs_b": 10,
25
+ "epochs_c": 12,
26
+ "unfreeze_blocks_b": 4,
27
+ "unfreeze_blocks_c": 8,
28
+ "skip_stage_c": false,
29
+ "scheduler": "cosine_warmup",
30
+ "warmup_epochs_a": 0,
31
+ "warmup_epochs_b": 1,
32
+ "warmup_epochs_c": 1,
33
+ "warmup_start_factor": 0.01,
34
+ "min_lr_ratio": 0.01,
35
+ "weight_decay": 0.02,
36
+ "grad_clip_norm": 1.0,
37
+ "class_weight_mode": "custom",
38
+ "class_weights": [
39
+ 1.0,
40
+ 1.0
41
+ ],
42
+ "label_smoothing": 0.05,
43
+ "dropout": 0.1,
44
+ "early_stop_patience": 6,
45
+ "early_stop_min_delta": 0.001,
46
+ "resolved_class_weights": [
47
+ 1.0,
48
+ 1.0
49
+ ],
50
+ "pooling": "cls_token",
51
+ "lambda_consistency": 0.0,
52
+ "flip_op": "rot180"
53
+ },
54
+ "resolved_class_weights": [
55
+ 1.0,
56
+ 1.0
57
+ ],
58
+ "warmstart": {
59
+ "warmstart_repo": "BDRC/4-class-balanced-script-classifier",
60
+ "warmstart_checkpoint": null,
61
+ "warmstart_checkpoint_file": "final_model.pt",
62
+ "checkpoint_test_metrics": {
63
+ "loss": 0.6574946736847913,
64
+ "accuracy": 0.825925925925926,
65
+ "macro_f1": 0.8326187473728457,
66
+ "weighted_f1": 0.82908384875598
67
+ },
68
+ "warmstart_pooling": "cls_token"
69
+ },
70
+ "checkpoint_selection": {
71
+ "method": "best_val_macro_f1"
72
+ },
73
+ "best_checkpoint": {
74
+ "path": "best_stage_c_last_blocks.pt",
75
+ "epoch": 7,
76
+ "val_macro_f1": 0.849624060150376
77
+ },
78
+ "val_metrics": {
79
+ "loss": 0.45204214652379354,
80
+ "accuracy": 0.85,
81
+ "macro_f1": 0.849624060150376,
82
+ "weighted_f1": 0.849624060150376,
83
+ "auc_roc": 0.9022222222222223
84
+ },
85
+ "val_confusion_matrix": [
86
+ [
87
+ 24,
88
+ 6
89
+ ],
90
+ [
91
+ 3,
92
+ 27
93
+ ]
94
+ ],
95
+ "val_report": " precision recall f1-score support\n\n Gyuyig 0.89 0.80 0.84 30\n Tsugdri 0.82 0.90 0.86 30\n\n accuracy 0.85 60\n macro avg 0.85 0.85 0.85 60\nweighted avg 0.85 0.85 0.85 60\n",
96
+ "test_metrics": {
97
+ "loss": 0.5353951374689738,
98
+ "accuracy": 0.8083333333333333,
99
+ "macro_f1": 0.8082134667500521,
100
+ "weighted_f1": 0.8082134667500521,
101
+ "auc_roc": 0.8677777777777778
102
+ },
103
+ "test_confusion_matrix": [
104
+ [
105
+ 50,
106
+ 10
107
+ ],
108
+ [
109
+ 13,
110
+ 47
111
+ ]
112
+ ],
113
+ "test_report": " precision recall f1-score support\n\n Gyuyig 0.79 0.83 0.81 60\n Tsugdri 0.82 0.78 0.80 60\n\n accuracy 0.81 120\n macro avg 0.81 0.81 0.81 120\nweighted avg 0.81 0.81 0.81 120\n",
114
+ "history": {
115
+ "stage_a": [
116
+ {
117
+ "epoch": 1,
118
+ "train_loss": 0.868421577271961,
119
+ "train_acc": 0.39880952380952384,
120
+ "lr_head": 5e-06,
121
+ "val_loss": 0.8907806555430094,
122
+ "val_accuracy": 0.3,
123
+ "val_macro_f1": 0.28,
124
+ "val_weighted_f1": 0.28
125
+ },
126
+ {
127
+ "epoch": 2,
128
+ "train_loss": 0.6925251398767743,
129
+ "train_acc": 0.5744047619047619,
130
+ "lr_head": 0.0005,
131
+ "val_loss": 0.6001647631327311,
132
+ "val_accuracy": 0.6166666666666667,
133
+ "val_macro_f1": 0.5911111111111111,
134
+ "val_weighted_f1": 0.5911111111111111
135
+ },
136
+ {
137
+ "epoch": 3,
138
+ "train_loss": 0.5732808538845607,
139
+ "train_acc": 0.6964285714285714,
140
+ "lr_head": 0.0004668412874366486,
141
+ "val_loss": 0.5495736837387085,
142
+ "val_accuracy": 0.7166666666666667,
143
+ "val_macro_f1": 0.7165879410947485,
144
+ "val_weighted_f1": 0.7165879410947485
145
+ },
146
+ {
147
+ "epoch": 4,
148
+ "train_loss": 0.5409448828016009,
149
+ "train_acc": 0.7351190476190477,
150
+ "lr_head": 0.00037624999999999996,
151
+ "val_loss": 0.5596829652786255,
152
+ "val_accuracy": 0.7166666666666667,
153
+ "val_macro_f1": 0.7127569698676429,
154
+ "val_weighted_f1": 0.7127569698676429
155
+ },
156
+ {
157
+ "epoch": 5,
158
+ "train_loss": 0.517706477925891,
159
+ "train_acc": 0.7380952380952381,
160
+ "lr_head": 0.0002525,
161
+ "val_loss": 0.570094374815623,
162
+ "val_accuracy": 0.6833333333333333,
163
+ "val_macro_f1": 0.6723196320781835,
164
+ "val_weighted_f1": 0.6723196320781833
165
+ },
166
+ {
167
+ "epoch": 6,
168
+ "train_loss": 0.5244638267017546,
169
+ "train_acc": 0.7529761904761905,
170
+ "lr_head": 0.00012875000000000007,
171
+ "val_loss": 0.5446342428525289,
172
+ "val_accuracy": 0.7166666666666667,
173
+ "val_macro_f1": 0.7159565580618212,
174
+ "val_weighted_f1": 0.7159565580618211
175
+ },
176
+ {
177
+ "epoch": 7,
178
+ "train_loss": 0.49209926100004286,
179
+ "train_acc": 0.7797619047619048,
180
+ "lr_head": 3.815871256335142e-05,
181
+ "val_loss": 0.5444834152857463,
182
+ "val_accuracy": 0.7166666666666667,
183
+ "val_macro_f1": 0.7159565580618212,
184
+ "val_weighted_f1": 0.7159565580618211
185
+ }
186
+ ],
187
+ "stage_b": [
188
+ {
189
+ "epoch": 1,
190
+ "train_loss": 0.5235937989893413,
191
+ "train_acc": 0.75,
192
+ "lr_head": 1.0000000000000002e-06,
193
+ "lr_backbone": 1.0000000000000001e-07,
194
+ "val_loss": 0.5491747617721557,
195
+ "val_accuracy": 0.7166666666666667,
196
+ "val_macro_f1": 0.7165879410947485,
197
+ "val_weighted_f1": 0.7165879410947485
198
+ },
199
+ {
200
+ "epoch": 2,
201
+ "train_loss": 0.5453660388787588,
202
+ "train_acc": 0.7529761904761905,
203
+ "lr_head": 0.0001,
204
+ "lr_backbone": 1e-05,
205
+ "val_loss": 0.5492973128954569,
206
+ "val_accuracy": 0.7333333333333333,
207
+ "val_macro_f1": 0.7306397306397306,
208
+ "val_weighted_f1": 0.7306397306397305
209
+ },
210
+ {
211
+ "epoch": 3,
212
+ "train_loss": 0.49581416731788996,
213
+ "train_acc": 0.7916666666666666,
214
+ "lr_head": 9.701478472890248e-05,
215
+ "lr_backbone": 9.701478472890248e-06,
216
+ "val_loss": 0.538078244527181,
217
+ "val_accuracy": 0.7166666666666667,
218
+ "val_macro_f1": 0.7165879410947485,
219
+ "val_weighted_f1": 0.7165879410947485
220
+ },
221
+ {
222
+ "epoch": 4,
223
+ "train_loss": 0.4958783601011549,
224
+ "train_acc": 0.7678571428571429,
225
+ "lr_head": 8.84191999343894e-05,
226
+ "lr_backbone": 8.841919993438941e-06,
227
+ "val_loss": 0.5364969690640767,
228
+ "val_accuracy": 0.7333333333333333,
229
+ "val_macro_f1": 0.7321428571428572,
230
+ "val_weighted_f1": 0.7321428571428572
231
+ },
232
+ {
233
+ "epoch": 5,
234
+ "train_loss": 0.4718715477557409,
235
+ "train_acc": 0.7946428571428571,
236
+ "lr_head": 7.525e-05,
237
+ "lr_backbone": 7.525e-06,
238
+ "val_loss": 0.5267467339833577,
239
+ "val_accuracy": 0.7333333333333333,
240
+ "val_macro_f1": 0.7321428571428572,
241
+ "val_weighted_f1": 0.7321428571428572
242
+ },
243
+ {
244
+ "epoch": 6,
245
+ "train_loss": 0.4493213253361838,
246
+ "train_acc": 0.8244047619047619,
247
+ "lr_head": 5.909558479451306e-05,
248
+ "lr_backbone": 5.909558479451306e-06,
249
+ "val_loss": 0.5186658461888631,
250
+ "val_accuracy": 0.7166666666666667,
251
+ "val_macro_f1": 0.7159565580618212,
252
+ "val_weighted_f1": 0.7159565580618211
253
+ },
254
+ {
255
+ "epoch": 7,
256
+ "train_loss": 0.43445002465021043,
257
+ "train_acc": 0.8184523809523809,
258
+ "lr_head": 4.190441520548695e-05,
259
+ "lr_backbone": 4.190441520548696e-06,
260
+ "val_loss": 0.5154884020487468,
261
+ "val_accuracy": 0.7166666666666667,
262
+ "val_macro_f1": 0.7159565580618212,
263
+ "val_weighted_f1": 0.7159565580618211
264
+ },
265
+ {
266
+ "epoch": 8,
267
+ "train_loss": 0.42955496197655085,
268
+ "train_acc": 0.8273809523809523,
269
+ "lr_head": 2.5750000000000013e-05,
270
+ "lr_backbone": 2.575000000000001e-06,
271
+ "val_loss": 0.5194342851638794,
272
+ "val_accuracy": 0.7666666666666667,
273
+ "val_macro_f1": 0.7643097643097643,
274
+ "val_weighted_f1": 0.7643097643097643
275
+ },
276
+ {
277
+ "epoch": 9,
278
+ "train_loss": 0.4368310116586231,
279
+ "train_acc": 0.8154761904761905,
280
+ "lr_head": 1.2580800065610596e-05,
281
+ "lr_backbone": 1.2580800065610596e-06,
282
+ "val_loss": 0.5142494519551595,
283
+ "val_accuracy": 0.7333333333333333,
284
+ "val_macro_f1": 0.7321428571428572,
285
+ "val_weighted_f1": 0.7321428571428572
286
+ },
287
+ {
288
+ "epoch": 10,
289
+ "train_loss": 0.4370355563504355,
290
+ "train_acc": 0.8125,
291
+ "lr_head": 3.985215271097539e-06,
292
+ "lr_backbone": 3.985215271097539e-07,
293
+ "val_loss": 0.5129753907521566,
294
+ "val_accuracy": 0.7333333333333333,
295
+ "val_macro_f1": 0.7321428571428572,
296
+ "val_weighted_f1": 0.7321428571428572
297
+ }
298
+ ],
299
+ "stage_c": [
300
+ {
301
+ "epoch": 1,
302
+ "train_loss": 0.4291627889587766,
303
+ "train_acc": 0.8273809523809523,
304
+ "lr_head": 5.000000000000001e-07,
305
+ "lr_backbone": 1.5000000000000002e-07,
306
+ "val_loss": 0.5183799107869466,
307
+ "val_accuracy": 0.7666666666666667,
308
+ "val_macro_f1": 0.7643097643097643,
309
+ "val_weighted_f1": 0.7643097643097643
310
+ },
311
+ {
312
+ "epoch": 2,
313
+ "train_loss": 0.42413380600157236,
314
+ "train_acc": 0.8154761904761905,
315
+ "lr_head": 5e-05,
316
+ "lr_backbone": 1.5e-05,
317
+ "val_loss": 0.5204356749852498,
318
+ "val_accuracy": 0.7166666666666667,
319
+ "val_macro_f1": 0.7101449275362319,
320
+ "val_weighted_f1": 0.7101449275362319
321
+ },
322
+ {
323
+ "epoch": 3,
324
+ "train_loss": 0.37433484338578726,
325
+ "train_acc": 0.875,
326
+ "lr_head": 4.899745109695881e-05,
327
+ "lr_backbone": 1.4699235329087644e-05,
328
+ "val_loss": 0.48167786995569867,
329
+ "val_accuracy": 0.7666666666666667,
330
+ "val_macro_f1": 0.7643097643097643,
331
+ "val_weighted_f1": 0.7643097643097643
332
+ },
333
+ {
334
+ "epoch": 4,
335
+ "train_loss": 0.335602682970819,
336
+ "train_acc": 0.8839285714285714,
337
+ "lr_head": 4.6071024937571735e-05,
338
+ "lr_backbone": 1.3821307481271522e-05,
339
+ "val_loss": 0.45847638448079425,
340
+ "val_accuracy": 0.8166666666666667,
341
+ "val_macro_f1": 0.8153846153846154,
342
+ "val_weighted_f1": 0.8153846153846154
343
+ },
344
+ {
345
+ "epoch": 5,
346
+ "train_loss": 0.2807346156665257,
347
+ "train_acc": 0.9255952380952381,
348
+ "lr_head": 4.145780316514581e-05,
349
+ "lr_backbone": 1.2437340949543742e-05,
350
+ "val_loss": 0.4557582457860311,
351
+ "val_accuracy": 0.8,
352
+ "val_macro_f1": 0.7991071428571428,
353
+ "val_weighted_f1": 0.7991071428571428
354
+ },
355
+ {
356
+ "epoch": 6,
357
+ "train_loss": 0.2691161029395603,
358
+ "train_acc": 0.9375,
359
+ "lr_head": 3.5531521571796694e-05,
360
+ "lr_backbone": 1.0659456471539008e-05,
361
+ "val_loss": 0.44928998152414956,
362
+ "val_accuracy": 0.8333333333333334,
363
+ "val_macro_f1": 0.8331479421579533,
364
+ "val_weighted_f1": 0.8331479421579533
365
+ },
366
+ {
367
+ "epoch": 7,
368
+ "train_loss": 0.23485895210788363,
369
+ "train_acc": 0.9375,
370
+ "lr_head": 2.877229224726381e-05,
371
+ "lr_backbone": 8.631687674179142e-06,
372
+ "val_loss": 0.45204214652379354,
373
+ "val_accuracy": 0.85,
374
+ "val_macro_f1": 0.849624060150376,
375
+ "val_weighted_f1": 0.849624060150376
376
+ },
377
+ {
378
+ "epoch": 8,
379
+ "train_loss": 0.22606890329292842,
380
+ "train_acc": 0.9553571428571429,
381
+ "lr_head": 2.1727707752736196e-05,
382
+ "lr_backbone": 6.518312325820858e-06,
383
+ "val_loss": 0.4554001530011495,
384
+ "val_accuracy": 0.7833333333333333,
385
+ "val_macro_f1": 0.783273131425396,
386
+ "val_weighted_f1": 0.783273131425396
387
+ },
388
+ {
389
+ "epoch": 9,
390
+ "train_loss": 0.19964813192685446,
391
+ "train_acc": 0.9672619047619048,
392
+ "lr_head": 1.4968478428203314e-05,
393
+ "lr_backbone": 4.490543528460994e-06,
394
+ "val_loss": 0.45547234614690146,
395
+ "val_accuracy": 0.8333333333333334,
396
+ "val_macro_f1": 0.8325892857142857,
397
+ "val_weighted_f1": 0.8325892857142857
398
+ },
399
+ {
400
+ "epoch": 10,
401
+ "train_loss": 0.21034001807371774,
402
+ "train_acc": 0.9583333333333334,
403
+ "lr_head": 9.042196834854196e-06,
404
+ "lr_backbone": 2.712659050456259e-06,
405
+ "val_loss": 0.45674578348795575,
406
+ "val_accuracy": 0.8333333333333334,
407
+ "val_macro_f1": 0.8325892857142857,
408
+ "val_weighted_f1": 0.8325892857142857
409
+ },
410
+ {
411
+ "epoch": 11,
412
+ "train_loss": 0.1949910166717711,
413
+ "train_acc": 0.9702380952380952,
414
+ "lr_head": 4.428975062428262e-06,
415
+ "lr_backbone": 1.3286925187284787e-06,
416
+ "val_loss": 0.4595174193382263,
417
+ "val_accuracy": 0.8166666666666667,
418
+ "val_macro_f1": 0.8153846153846154,
419
+ "val_weighted_f1": 0.8153846153846154
420
+ },
421
+ {
422
+ "epoch": 12,
423
+ "train_loss": 0.1842061841771716,
424
+ "train_acc": 0.9761904761904762,
425
+ "lr_head": 1.502548903041193e-06,
426
+ "lr_backbone": 4.5076467091235787e-07,
427
+ "val_loss": 0.4608180999755859,
428
+ "val_accuracy": 0.8166666666666667,
429
+ "val_macro_f1": 0.8153846153846154,
430
+ "val_weighted_f1": 0.8153846153846154
431
+ }
432
+ ]
433
+ },
434
+ "confusion_matrix": [
435
+ [
436
+ 50,
437
+ 10
438
+ ],
439
+ [
440
+ 13,
441
+ 47
442
+ ]
443
+ ],
444
+ "report": " precision recall f1-score support\n\n Gyuyig 0.79 0.83 0.81 60\n Tsugdri 0.82 0.78 0.80 60\n\n accuracy 0.81 120\n macro avg 0.81 0.81 0.81 120\nweighted avg 0.81 0.81 0.81 120\n",
445
+ "idx_to_label": {
446
+ "0": "Gyuyig",
447
+ "1": "Tsugdri"
448
+ },
449
+ "label_to_idx": {
450
+ "Gyuyig": 0,
451
+ "Tsugdri": 1
452
+ },
453
+ "model_id": "facebook/dinov3-vits16-pretrain-lvd1689m"
454
+ }
split_stats.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "source": "hf",
3
+ "classes": [
4
+ "Gyuyig",
5
+ "Tsugdri"
6
+ ],
7
+ "num_classes": 2,
8
+ "image_counts_per_class": {
9
+ "train": {
10
+ "Gyuyig": 171,
11
+ "Tsugdri": 171
12
+ },
13
+ "val": {
14
+ "Gyuyig": 30,
15
+ "Tsugdri": 30
16
+ },
17
+ "test": {}
18
+ },
19
+ "images_per_split": {
20
+ "train": 342,
21
+ "val": 60,
22
+ "test": 0
23
+ },
24
+ "images_total": 402,
25
+ "image_counts_total_per_class": {
26
+ "Gyuyig": 201,
27
+ "Tsugdri": 201
28
+ },
29
+ "balanced_parquet_dir": "None",
30
+ "balanced_dataset_repo": "BDRC/gyuyig-tsugdri-binary-balanced-script-classification-dataset",
31
+ "task": "gyuyig_tsugdri_binary_classification",
32
+ "benchmark_dataset_repo": null,
33
+ "benchmark_per_parent": 60,
34
+ "test_size": 120
35
+ }
split_stats.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Split statistics
2
+
3
+ - **Source:** `hf`
4
+ - **Total images:** 402
5
+
6
+
7
+ ## Images per split
8
+
9
+ | Split | Total |
10
+ |-------|------:|
11
+ | train | 342 |
12
+ | val | 60 |
13
+ | test | 0 |
14
+
15
+ ## Images per class (per split)
16
+
17
+ | Class | train | val | test | **All** |
18
+ |-------|------:|------:|------:|------:|
19
+ | Gyuyig | 171 | 30 | 0 | 201 |
20
+ | Tsugdri | 171 | 30 | 0 | 201 |
training_history.png ADDED