Download code/src/preprocessing/uncertainty_dinov3.py from savastakan/hitit-cuneiform-ocr: direct link, hf CLI and curl.
- Browser
- Download file 6.93 kB
-
https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/preprocessing/uncertainty_dinov3.py
- Command line
-
hf download hf://savastakan/hitit-cuneiform-ocr/code/src/preprocessing/uncertainty_dinov3.py
-
curl -L -o uncertainty_dinov3.py https://huggingface.co/savastakan/hitit-cuneiform-ocr/resolve/main/code/src/preprocessing/uncertainty_dinov3.py
6.93 kB
| #!/usr/bin/env python3 | |
| """DINOv3 tabanlı uncertainty score. | |
| Yaklaşım: DINOv3 backbone'u ile feature çıkar, her unified_label'ın centroid'ini hesapla, | |
| her kayıt için (1 - max_similarity_to_own_centroid) → uncertainty. | |
| Hard cases (düşük similarity) = yüksek uncertainty = tekrar annotate önerilir. | |
| """ | |
| import json, os, argparse, time | |
| from pathlib import Path | |
| import numpy as np | |
| ROOT = Path("/arf/scratch/stakan/hitit-proje") | |
| SOURCES = ROOT / "datasets" / "sources" | |
| def main(): | |
| import torch | |
| from PIL import Image | |
| from collections import defaultdict | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument('--batch', type=int, default=64) | |
| ap.add_argument('--model', default='dinov2_vitb14') # DINOv3 eğer torch hub'da varsa dinov3_vitb16 | |
| ap.add_argument('--sample', type=int, default=0) | |
| args = ap.parse_args() | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| print(f"Device: {device}", flush=True) | |
| # DINOv3 (Meta) — Py3.11 + ai-tools-kolyoz-1.0 env'i ile çalışır. | |
| # Fallbacks: DINOv2 → timm DINOv2 (en kötü senaryo). | |
| try: | |
| model = torch.hub.load('facebookresearch/dinov3', 'dinov3_vitb16', | |
| trust_repo=True).eval().to(device) | |
| emb_dim = 768 | |
| input_size = 224 | |
| model_name = "dinov3_vitb16" | |
| except Exception as e: | |
| print(f"DINOv3 yüklenemedi ({type(e).__name__}: {str(e)[:80]}), DINOv2'ye düşüldü", flush=True) | |
| try: | |
| model = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitb14', | |
| trust_repo=True).eval().to(device) | |
| emb_dim = 768 | |
| input_size = 224 | |
| model_name = "dinov2_vitb14" | |
| except Exception as e2: | |
| print(f"DINOv2 de yüklenemedi ({e2}), timm DINOv2'ye düşüldü", flush=True) | |
| import timm | |
| model = timm.create_model('vit_base_patch14_dinov2.lvd142m', | |
| pretrained=True, num_classes=0).eval().to(device) | |
| emb_dim = 768 | |
| input_size = 518 | |
| model_name = "dinov2_vitb14_timm" | |
| from torchvision import transforms | |
| tf = transforms.Compose([ | |
| transforms.Resize(256), | |
| transforms.CenterCrop(input_size), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]), | |
| ]) | |
| # Topla: sadece classification | |
| items = [] | |
| for d in sorted(SOURCES.iterdir()): | |
| if not d.is_dir(): continue | |
| mp = d / "manifest_classification.jsonl" | |
| if not mp.exists(): continue | |
| with open(mp) as f: | |
| for line in f: | |
| r = json.loads(line) | |
| p = r.get('path') | |
| ul = r.get('unified_label') | |
| if p and ul and r.get('integrity_ok') is True and os.path.exists(p): | |
| items.append((r['id'], p, ul)) | |
| if args.sample: | |
| import random; random.seed(42) | |
| items = random.sample(items, min(args.sample, len(items))) | |
| print(f"Feature çıkarılacak: {len(items):,}", flush=True) | |
| # Feature extraction | |
| features = np.zeros((len(items), emb_dim), dtype=np.float32) | |
| labels_list = [None] * len(items) | |
| ids_list = [None] * len(items) | |
| t0 = time.time() | |
| idx = 0 | |
| for bi in range(0, len(items), args.batch): | |
| batch = items[bi:bi+args.batch] | |
| imgs = [] | |
| for rid, path, ul in batch: | |
| try: | |
| img = tf(Image.open(path).convert('RGB')) | |
| imgs.append(img) | |
| except: | |
| imgs.append(None) | |
| valid = [(i,img) for i,img in enumerate(imgs) if img is not None] | |
| if not valid: continue | |
| x = torch.stack([v[1] for v in valid]).to(device) | |
| with torch.no_grad(), torch.amp.autocast('cuda', enabled=(device=="cuda")): | |
| feats = model(x) | |
| if isinstance(feats, dict): feats = feats.get('x_norm_clstoken', feats.get('cls', list(feats.values())[0])) | |
| feats = feats.cpu().numpy().astype(np.float32) | |
| feats /= (np.linalg.norm(feats, axis=1, keepdims=True) + 1e-8) | |
| for k, (i, _) in enumerate(valid): | |
| rid, path, ul = batch[i] | |
| features[idx] = feats[k] | |
| labels_list[idx] = ul | |
| ids_list[idx] = rid | |
| idx += 1 | |
| if (bi // args.batch) % 50 == 0: | |
| print(f" {idx}/{len(items)} ({100*idx/len(items):.1f}%) {idx/max(time.time()-t0,1):.0f} img/s", flush=True) | |
| features = features[:idx] | |
| labels_list = labels_list[:idx] | |
| ids_list = ids_list[:idx] | |
| # Per-class centroid | |
| from collections import defaultdict | |
| sums = defaultdict(lambda: np.zeros(emb_dim, dtype=np.float32)) | |
| counts = defaultdict(int) | |
| for f, l in zip(features, labels_list): | |
| sums[l] += f | |
| counts[l] += 1 | |
| centroids = {l: sums[l] / counts[l] for l in sums} | |
| # Normalize | |
| centroids = {l: c / (np.linalg.norm(c) + 1e-8) for l, c in centroids.items()} | |
| # Uncertainty = 1 - cos_sim(feature, own_centroid) | |
| uncertainty = {} | |
| for f, l, rid in zip(features, labels_list, ids_list): | |
| c = centroids.get(l) | |
| if c is None: continue | |
| sim = float(f @ c) | |
| uncertainty[rid] = max(0.0, min(1.0, 1.0 - sim)) | |
| # Manifest güncelle | |
| updated = 0 | |
| for d in sorted(SOURCES.iterdir()): | |
| if not d.is_dir(): continue | |
| for mf in ['manifest.jsonl', 'manifest_classification.jsonl', 'manifest_detection.jsonl']: | |
| mp = d / mf | |
| if not mp.exists(): continue | |
| records = [] | |
| with open(mp) as f: | |
| for line in f: | |
| r = json.loads(line) | |
| u = uncertainty.get(r.get('id')) | |
| if u is not None: | |
| r['uncertainty_score'] = round(u, 4) | |
| r['uncertainty_source'] = f'{model_name}_centroid_v1' | |
| updated += 1 | |
| records.append(r) | |
| with open(mp, 'w') as f: | |
| for r in records: | |
| f.write(json.dumps(r, ensure_ascii=False) + '\n') | |
| print(f"\nuncertainty_score güncellendi: {updated:,} kayıt") | |
| # Summary | |
| vals = list(uncertainty.values()) | |
| vals_sorted = sorted(vals) | |
| n = len(vals_sorted) | |
| out = { | |
| "model": model_name, | |
| "n_scored": n, | |
| "percentiles": {f"p{p}": round(vals_sorted[int(n*p/100)], 4) for p in [5,25,50,75,95]} if n else {}, | |
| "n_classes_with_centroid": len(centroids), | |
| "high_uncertainty_threshold": 0.5, | |
| "n_above_threshold": sum(1 for v in vals if v > 0.5), | |
| } | |
| with open(ROOT / "datasets" / "processed" / "uncertainty_dinov3_report.json", 'w') as f: | |
| json.dump(out, f, indent=2, ensure_ascii=False) | |
| print(f"\n{json.dumps(out, indent=2)}") | |
| if __name__ == '__main__': | |
| main() | |