#!/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()