Nidan / server /tasks /task_base.py
Saivats's picture
feat: enhance environment with curriculum rewards and multi-metric graders
3ba0ffc
Raw
History Blame Contribute Delete
3.11 kB
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, Optional
import numpy as np
@dataclass
class TaskConfig:
task_id: str
name: str
classes: List[str]
pool_size: int
budget: int
success_threshold: float
rare_classes: List[str]
modality: str = "xray"
body_part: str = "chest"
variable_cost: bool = False
base_annotation_cost: int = 1
rare_annotation_cost: int = 2
@dataclass
class TaskState:
config: TaskConfig
pool_embeddings: np.ndarray
pool_labels: np.ndarray
pool_image_ids: List[str]
val_embeddings: np.ndarray
val_labels: np.ndarray
labeled_indices: List[int] = field(default_factory=list)
labeled_labels: List[str] = field(default_factory=list)
unlabeled_indices: List[int] = field(default_factory=list)
budget_used: int = 0
annotation_cost_spent: int = 0
current_auc: float = 0.5
step_count: int = 0
cumulative_reward: float = 0.0
auc_trajectory: List[float] = field(default_factory=list)
classes_discovered: List[str] = field(default_factory=list)
def get_labeled_embeddings(self) -> np.ndarray:
if not self.labeled_indices:
return np.zeros((0, self.pool_embeddings.shape[1]), dtype=np.float32)
return self.pool_embeddings[self.labeled_indices]
def get_unlabeled_embeddings(self) -> np.ndarray:
if not self.unlabeled_indices:
return np.zeros((0, self.pool_embeddings.shape[1]), dtype=np.float32)
return self.pool_embeddings[self.unlabeled_indices]
def get_unlabeled_image_ids(self) -> List[str]:
return [self.pool_image_ids[i] for i in self.unlabeled_indices]
def find_index_by_image_id(self, image_id: str) -> Optional[int]:
try:
pool_pos = self.pool_image_ids.index(image_id)
if pool_pos in self.unlabeled_indices:
return pool_pos
return None
except ValueError:
return None
def get_budget_phase(self) -> str:
if self.config.budget == 0:
return "late"
progress = self.budget_used / self.config.budget
if progress < 0.3:
return "early"
if progress < 0.7:
return "mid"
return "late"
def get_class_distribution(self) -> Dict[str, int]:
distribution: Dict[str, int] = {cls: 0 for cls in self.config.classes}
for lbl in self.labeled_labels:
if lbl in distribution:
distribution[lbl] += 1
return distribution
def get_class_coverage_ratio(self) -> float:
discovered = sum(1 for cls in self.config.classes if cls in self.classes_discovered)
return discovered / len(self.config.classes) if self.config.classes else 0.0
def compute_annotation_cost(self, label: str) -> int:
if not self.config.variable_cost:
return self.config.base_annotation_cost
if label in self.config.rare_classes:
return self.config.rare_annotation_cost
return self.config.base_annotation_cost