hitit-cuneiform-ocr / code /src /enhancements /curriculum_learning.py
savastakan's picture
Initial upload: code + 5 record checkpoints + fuse
f211247 verified
Raw
History Blame Contribute Delete
2.83 kB
#!/usr/bin/env python3
"""#16 Curriculum Learning — easy-to-hard scheduling.
Reference: Bengio 2009; Hacohen 2019; SuperLoss (Castells 2020).
Düşük-uncertainty / yüksek-quality örneklerle başla, zorlaşır.
Kullanılan alanlar:
- quality_score (composite)
- uncertainty_score (heuristic / model-based)
- class_frequency_tier
"""
import json
from pathlib import Path
ROOT = Path("/arf/scratch/stakan/hitit-proje")
class CurriculumSampler:
"""Epoch'a göre sample pool'u genişleyen curriculum sampler."""
def __init__(self, records, difficulty_fn=None):
self.records = records
self.difficulty_fn = difficulty_fn or self._default_difficulty
self.sorted_indices = self._sort_by_difficulty()
def _default_difficulty(self, r):
"""Low = easy, high = hard."""
qs = r.get('quality_score', 0.5)
us = r.get('uncertainty_score', 0.5)
tier = r.get('class_frequency_tier', 'mid')
tier_bonus = {'head': 0.0, 'mid': 0.2, 'tail': 0.5, 'rare': 0.8}.get(tier, 0.3)
# Hard: low quality + high uncertainty + rare tier
return (1 - qs) * 0.4 + us * 0.3 + tier_bonus * 0.3
def _sort_by_difficulty(self):
difficulties = [(i, self.difficulty_fn(r)) for i, r in enumerate(self.records)]
difficulties.sort(key=lambda x: x[1]) # ascending: easy first
return [i for i, _ in difficulties]
def epoch_indices(self, epoch, total_epochs=100, warmup_ratio=0.2):
"""Curriculum: warmup_ratio kadar sadece en kolay, sonra linear genişle."""
n = len(self.records)
warmup_epochs = int(total_epochs * warmup_ratio)
if epoch < warmup_epochs:
# Sadece en kolay %50
cutoff = n // 2
else:
# Linear expansion: warmup_epochs'tan sonra full pool
progress = min(1.0, (epoch - warmup_epochs) / (total_epochs - warmup_epochs))
cutoff = int(n // 2 + progress * (n - n // 2))
return self.sorted_indices[:cutoff]
def main():
# Sadece recipe dökümanı
recipe = {
"name": "Curriculum learning — quality+uncertainty+tier difficulty",
"difficulty_formula": "(1 - quality_score) * 0.4 + uncertainty_score * 0.3 + tier_bonus * 0.3",
"tier_bonus": {"head": 0.0, "mid": 0.2, "tail": 0.5, "rare": 0.8},
"schedule": "First 20% epochs: easiest 50%. Then linear expansion to full pool by end.",
"expected_gain": "+0.2-0.5% (literature mixed results)",
"fields_used": ["quality_score", "uncertainty_score", "class_frequency_tier"],
}
with open(ROOT / "datasets/processed/curriculum_recipe.json", 'w') as f:
json.dump(recipe, f, indent=2, ensure_ascii=False)
print("Curriculum sampler hazır")
if __name__ == '__main__':
main()