karma689 commited on
Commit
18c257a
ยท
verified ยท
1 Parent(s): ad0e002

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +60 -158
README.md CHANGED
@@ -1,186 +1,88 @@
1
  ---
2
  language:
3
- - bo
4
  license: apache-2.0
5
  library_name: transformers
6
  tags:
7
- - image-classification
8
- - dinov3
9
- - tibetan
10
- - script-classification
11
- - paleography
12
- - fine-tuned
13
- - document-analysis
14
  base_model: facebook/dinov3-vits16-pretrain-lvd1689m
15
  datasets:
16
- - custom
17
  metrics:
18
- - f1
19
- - accuracy
20
  pipeline_tag: image-classification
21
  model-index:
22
- - name: Tibetan Script Classifier (DINOv3 ViT-S)
23
- results:
24
- - task:
25
- type: image-classification
26
- name: Tibetan Script Classification
27
- metrics:
28
- - name: Macro F1 (whole page)
29
- type: f1
30
- value: 0.512
31
- - name: Accuracy (whole page)
32
- type: accuracy
33
- value: 0.571
34
- - name: Macro F1 (CLAHE patches, page-level)
35
- type: f1
36
- value: 0.529
37
  ---
38
 
39
  # Tibetan Script Classifier (DINOv3)
40
 
41
- This repository contains fine-tuned Tibetan script classification checkpoints for 18 classes, trained from the DINOv3 ViT-S backbone:
42
 
43
- - Backbone: `facebook/dinov3-vits16-pretrain-lvd1689m`
44
- - Task: 18-way script classification
45
- - Training script included: `finetune_dinov3.py`
 
 
 
46
 
47
- **Hugging Face access:** DINOv3 requires access approval at [huggingface.co/facebook/dinov3-vits16-pretrain-lvd1689m](https://huggingface.co/facebook/dinov3-vits16-pretrain-lvd1689m) before `from_pretrained` / downloads will work. Anyone cloning this repo will see the same gated-model error until their HF account is granted access and they are logged in (`huggingface-cli login` or `HF_TOKEN`).
48
 
49
- ## Label Set
 
 
 
 
50
 
51
- `dhumri`, `difficult`, `drathung`, `drudring`, `druring`, `druthung`, `khyuyig`, `multi_scripts`, `non_tibetan`, `peri`, `petsuk`, `trinyig`, `tsegdrig`, `tsugchung`, `tsumachug`, `uchen_sugdring`, `uchen_sugthung`, `yigchung`.
52
-
53
- ## Preprocessing (per experiment)
54
-
55
- Images for training were produced as follows (see `preprocess.py` in the parent project):
56
-
57
- - **`whole_page`:** resize so the **short edge is 224 px**, then **center crop** to 224ร—224 (one crop per source page).
58
- - **`patches_color`:** same short-edge resize to 224, then **sliding-window** 224ร—224 patches with **25% overlap** between windows (multiple crops per page).
59
- - **`patches_clahe`:** identical patch layout as `patches_color`; each patch is converted to grayscale and **CLAHE** contrast normalization is applied (`clipLimit=2.0`, `tileGridSize=(8,8)`), then saved as BGR/RGB for training.
60
-
61
- ## Training recipe
62
-
63
- - **Progressive unfreezing (defaults in `finetune_dinov3.py`):**
64
- - **Stage A โ€” head only:** 20 epochs, backbone frozen, classifier head LR **1e-3** (backbone LR 0).
65
- - **Stage B โ€” last 2 blocks:** 10 epochs, backbone LR **1e-5**, head LR **1e-3**.
66
- - **Stage C โ€” last 4 blocks:** 10 epochs, backbone LR **5e-6**, head LR **5e-4**.
67
- - **Loss:** class-weighted **cross-entropy** with inverse-frequency weights over the training split (`nn.CrossEntropyLoss(weight=...)`).
68
- - **Sampling:** the published runs use a standard `DataLoader` with **`shuffle=True`**. The script also defines **`get_weighted_sampler` โ†’ `WeightedRandomSampler`** if you want to switch the train loader to explicit class-balanced sampling.
69
- - **Document-aware augmentations (train only):** `RandomRotation` **ยฑ5ยฐ** (fill white), `ColorJitter` brightness/contrast **ยฑ20%** (`0.2`), plus `RandomResizedCrop` and light `RandomErasing` as in `ScriptDataset`; **no horizontal flip**.
70
-
71
- ## Class distribution (`whole_page` split totals)
72
-
73
- The whole-page split has 5,684 samples in total (train 3,996 / val 844 / test 844). 88 benchmark images (5 per class) were excluded from training and evaluation.
74
-
75
- | Class | Samples |
76
- |---|---:|
77
- | dhumri | 98 |
78
- | difficult | 170 |
79
- | drathung | 129 |
80
- | drudring | 132 |
81
- | druring | 119 |
82
- | druthung | 207 |
83
- | khyuyig | 113 |
84
- | multi_scripts | 235 |
85
- | non_tibetan | 192 |
86
- | peri | 614 |
87
- | petsuk | 1388 |
88
- | trinyig | 42 |
89
- | tsegdrig | 749 |
90
- | tsugchung | 77 |
91
- | tsumachug | 178 |
92
- | uchen_sugdring | 835 |
93
- | uchen_sugthung | 240 |
94
- | yigchung | 166 |
95
 
96
- ## Experiments Included
97
-
98
- ### 1) `whole_page`
99
-
100
- - Files: `whole_page/final_model.pt`, `results.json`, `confusion_matrix.png`, `confusion_matrix.csv`, `splits.json`
101
- - Test (image-level) macro-F1: **0.5124**
102
- - Test accuracy: **0.5711**
103
-
104
- ### 2) `patches_color`
105
-
106
- - Files: `patches_color/final_model.pt`, `results.json`, `confusion_matrix.png`, `checkpoint_page_eval.json`, `splits.json`
107
- - Test (patch-level) macro-F1: **0.4899**
108
- - Re-eval **page-level** macro-F1 for shipped `final_model.pt` (`checkpoint_page_eval.json`): **0.5017**
109
- - Best **page-level** macro-F1 among stage checkpoints on the same grid: **0.5043** (**Stage A**)
110
-
111
- ### 3) `patches_clahe`
112
-
113
- - Files: `patches_clahe/final_model.pt`, `results.json`, `confusion_matrix.png`, `checkpoint_page_eval.json`, `splits.json`
114
- - Test (patch-level) macro-F1: **0.4911**
115
- - Re-eval page-level macro-F1 for shipped `final_model.pt`: **0.5261**
116
- - Best **page-level** macro-F1 among stage checkpoints: **0.529** (**Stage B**)
117
-
118
- ## Which stage produced which checkpoint?
119
 
120
- - **`final_model.pt` in each folder** is the stage with the highest **validation macro-F1** among `best_stage_*.pt` checkpoints (see `best_val_checkpoint` in each `results.json`): **Stage B** for `whole_page`, **Stage C** for both `patches_color` and `patches_clahe`.
121
- - For **page-level** quality on the patch runs, the best single stage on the re-eval grid differs: **Stage A** (`patches_color`) and **Stage B** (`patches_clahe`) beat their respective `final_model.pt` page scoresโ€”use `checkpoint_page_eval.json` if you want to deploy a stage checkpoint instead of the val-selected default.
 
 
122
 
123
- ## Which experiment won?
 
 
 
 
124
 
125
- CLAHE patches achieved the highest **page-level** macro-F1 (**0.529** on the best stage checkpoint), while **whole page** achieved the best **image-level** macro-F1 (**0.512**). **Whole page** is recommended for production due to simpler inference.
126
 
127
- ## How To Load a Checkpoint
128
 
 
129
  ```python
130
  import torch
131
- from pathlib import Path
132
- from finetune_dinov3 import DINOv3Classifier, DINOV3_MODEL_ID
133
 
134
- ckpt_path = Path("whole_page/final_model.pt")
135
- payload = torch.load(ckpt_path, map_location="cpu")
136
-
137
- label_to_idx = payload["label_to_idx"]
138
- idx_to_label = {v: k for k, v in label_to_idx.items()}
139
- num_classes = len(label_to_idx)
140
-
141
- model = DINOv3Classifier(DINOV3_MODEL_ID, num_classes)
142
  model.load_state_dict(payload["model_state_dict"])
143
- model.eval()
144
- ```
145
-
146
- ## Inference (Single Image)
147
-
148
- ```python
149
- import torch
150
- from PIL import Image
151
- from transformers import AutoImageProcessor
152
-
153
- processor = AutoImageProcessor.from_pretrained(DINOV3_MODEL_ID)
154
- img = Image.open("example.png").convert("RGB")
155
- inputs = processor(images=img, return_tensors="pt")
156
-
157
- with torch.no_grad():
158
- logits = model(inputs["pixel_values"])
159
- probs = torch.softmax(logits, dim=1)[0].cpu().numpy()
160
- pred_idx = int(probs.argmax())
161
- pred_label = idx_to_label[pred_idx]
162
- print(pred_label, float(probs[pred_idx]))
163
- ```
164
-
165
- ## Page-Level Inference (Patch Aggregation)
166
-
167
- For patch experiments (`patches_color`, `patches_clahe`), aggregate by page stem:
168
-
169
- 1. group patch probabilities by page ID (strip `_pN` suffix),
170
- 2. average probabilities per page,
171
- 3. take `argmax` of averaged probabilities.
172
-
173
- This is the same logic used in the re-evaluation script output (`checkpoint_page_eval.json`).
174
-
175
- ## Known Limitations
176
-
177
- - Class imbalance is high (for example `petsuk` and `uchen_sugdring` dominate, while `trinyig` is small).
178
- - Results can vary by preprocessing variant and by patch vs page-level evaluation protocol.
179
- - Patch-level metrics and page-level metrics are not directly interchangeable.
180
- - The model expects Tibetan manuscript-style inputs; performance can drop on out-of-domain scans or mixed/noisy pages.
181
- - Checkpoints are tied to the exact label mapping saved in each payload (`label_to_idx`).
182
-
183
- ## Reproducibility Notes
184
-
185
- - Exclusion manifest support is enabled in training (`benchmark_page_ids.json`).
186
- - Full training code used for these artifacts is included at `finetune_dinov3.py`.
 
1
  ---
2
  language:
3
+ - bo
4
  license: apache-2.0
5
  library_name: transformers
6
  tags:
7
+ - image-classification
8
+ - dinov3
9
+ - tibetan
10
+ - script-classification
11
+ - paleography
12
+ - fine-tuned
13
+ - document-analysis
14
  base_model: facebook/dinov3-vits16-pretrain-lvd1689m
15
  datasets:
16
+ - openpecha/tibetan-script-images
17
  metrics:
18
+ - f1
19
+ - accuracy
20
  pipeline_tag: image-classification
21
  model-index:
22
+ - name: Tibetan Script Classifier (DINOv3 ViT-S)
23
+ results:
24
+ - task:
25
+ type: image-classification
26
+ name: Tibetan Script Classification
27
+ metrics:
28
+ - name: Macro F1 (whole page)
29
+ type: f1
30
+ value: 0.512
31
+ - name: Accuracy (whole page)
32
+ type: accuracy
33
+ value: 0.571
34
+ - name: Macro F1 (CLAHE patches, page-level)
35
+ type: f1
36
+ value: 0.529
37
  ---
38
 
39
  # Tibetan Script Classifier (DINOv3)
40
 
41
+ This repository contains fine-tuned checkpoints for identifying 18 distinct categories of Tibetan manuscript scripts. This research was conducted to develop automated paleographic identification tools for historical archives.
42
 
43
+ ## ๐Ÿ› Project Information
44
+ - **Project Name:** The BDRC Etext Corpus
45
+ - **Developed by:** Dharmaduta
46
+ - **Specifications provided by:** [Buddhist Digital Resource Center (BDRC)](https://www.bdrc.io)
47
+ - **Funded by:** Khyentse Foundation
48
+ - **Core Model:** DINOv3 ViT-S/16 (`facebook/dinov3-vits16-pretrain-lvd1689m`)
49
 
50
+ ## ๐Ÿ“Š Evaluation Results
51
 
52
+ | Experiment | Evaluation Level | Macro F1 | Accuracy |
53
+ | :--- | :--- | :---: | :---: |
54
+ | **whole_page** | Image-level | 0.512 | 57.11% |
55
+ | **patches_clahe** | Page-level (Aggregated) | **0.529** | 52.61% |
56
+ | **patches_color** | Page-level (Aggregated) | 0.504 | 50.17% |
57
 
58
+ *Note: The **whole_page** model is recommended for general use due to its balanced performance and simpler inference pipeline.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ ## ๐Ÿท Label Set (18 Classes)
61
+ The model is trained to recognize the following scripts:
62
+ `dhumri`, `difficult`, `drathung`, `drudring`, `druring`, `druthung`, `khyuyig`, `multi_scripts`, `non_tibetan`, `peri`, `petsuk`, `trinyig`, `tsegdrig`, `tsugchung`, `tsumachug`, `uchen_sugdring`, `uchen_sugthung`, `yigchung`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ ## ๐Ÿ›  Preprocessing Variants
65
+ - **whole_page**: Short-edge resize to 224px followed by a 224ร—224 center crop.
66
+ - **patches_color**: Sliding-window 224ร—224 patches with 25% overlap.
67
+ - **patches_clahe**: Same patch layout as above, but with **Contrast Limited Adaptive Histogram Equalization (CLAHE)** applied to grayscale inputs to enhance script visibility.
68
 
69
+ ## ๐Ÿงช Training Recipe
70
+ Training was executed via a 3-stage progressive unfreezing strategy:
71
+ 1. **Stage A (Head Only):** 20 epochs, backbone frozen (LR: 1e-3).
72
+ 2. **Stage B (Partial):** 10 epochs, unfreezing the last 2 Transformer blocks (Backbone LR: 1e-5).
73
+ 3. **Stage C (Full):** 10 epochs, unfreezing the last 4 Transformer blocks (Backbone LR: 5e-6).
74
 
75
+ Class-weighted cross-entropy loss was utilized to mitigate high dataset imbalance across script types.
76
 
77
+ ## ๐Ÿš€ How to Use
78
 
79
+ ### Loading the Model
80
  ```python
81
  import torch
82
+ from finetune_dinov3 import DINOv3Classifier
 
83
 
84
+ # Load Stage B Whole Page Checkpoint
85
+ payload = torch.load("whole_page/final_model.pt", map_location="cpu")
86
+ model = DINOv3Classifier("facebook/dinov3-vits16-pretrain-lvd1689m", num_classes=18)
 
 
 
 
 
87
  model.load_state_dict(payload["model_state_dict"])
88
+ model.eval()