Download src/conveyer/config.py from sumitk/diffusion-conveyer-llm: direct link, hf CLI and curl.
- Browser
- Download file 7.82 kB
-
https://huggingface.co/sumitk/diffusion-conveyer-llm/resolve/main/src/conveyer/config.py
- Command line
-
hf download hf://sumitk/diffusion-conveyer-llm/src/conveyer/config.py
-
curl -L -o config.py https://huggingface.co/sumitk/diffusion-conveyer-llm/resolve/main/src/conveyer/config.py
7.82 kB
| from __future__ import annotations | |
| from dataclasses import asdict, dataclass, field, fields, is_dataclass | |
| import json | |
| from pathlib import Path | |
| from typing import Any | |
| try: | |
| import yaml # type: ignore | |
| except ImportError: | |
| yaml = None | |
| class ModelConfig: | |
| vocab_size: int = 128 | |
| d_model: int = 256 | |
| n_heads: int = 4 | |
| n_layers: int = 6 | |
| d_ff: int = 688 | |
| max_seq_len: int = 512 | |
| dropout: float = 0.0 | |
| rope_theta: float = 10000.0 | |
| # Conveyor belt parameters | |
| belt_size: int = 64 | |
| front_conveyer_slots: int = 16 # Slots 0..15 shift left by 1 every tick; Slots 16..63 (75%) are Elastic Planning Zone | |
| plan_horizon_stride: int = 2 # Back 75% planning slots span 2x longer horizon while sentences expand in middle | |
| tau_min: float = 0.5 | |
| tau_max: float = 4.0 | |
| tau_gamma: float = 1.5 | |
| gate_bias_init: float = 1.0 | |
| spawn_noise_std: float = 0.5 | |
| thought_noise_std: float = 0.25 | |
| # Ablation & efficiency switches | |
| use_thought_highway: bool = True | |
| use_elastic_planning: bool = True # Enables 75% paused/elastic planning belt with mid-belt expansion | |
| causal_belt_only: bool = False # If True, disables bidirectional belt attention (AR baseline mode) | |
| grad_checkpointing: bool = False # Enables activation checkpointing for long unroll horizons (U=24..32) | |
| class TrainConfig: | |
| out_dir: str = "runs/synthetic" | |
| seed: int = 42 | |
| batch_size: int = 64 | |
| lr: float = 5e-4 | |
| weight_decay: float = 0.01 | |
| grad_clip: float = 1.0 | |
| max_steps: int = 1200 | |
| max_hours: float = 0.0 # Optional wall-clock training budget in hours (0.0 = use max_steps only) | |
| warmup_steps: int = 80 | |
| eval_every: int = 100 | |
| save_every: int = 500 # Periodic crash-safe checkpoint interval (steps) | |
| log_every: int = 25 | |
| unroll_steps: int = 16 | |
| prefix_lm_weight: float = 1.0 # Weight on causal next-token LM loss across the prompt/sequence | |
| step_cooldown_ms: int = 0 # Optional GPU thermal pacing sleep per step in ms | |
| max_gpu_temp_c: int = 80 # Automatic thermal throttle threshold in Celsius | |
| # Corrupted-belt seeding & recovery parameters | |
| seed_sigma_max: float = 3.5 | |
| seed_sigma_eta: float = 1.25 | |
| recovery_prob: float = 0.35 # DAgger / self-conditioned prefix + belt recovery probability | |
| belt_corrupt_prob: float = 0.20 # Mid-unroll belt perturbation rate to train active belt recovery | |
| # Horizon loss weights: w_k = w_min + (1 - w_min) * (1 - k / (B - 1))^w_beta | |
| loss_w_min: float = 0.05 | |
| loss_w_beta: float = 1.5 | |
| entropy_reg: float = 0.001 | |
| # Ablations | |
| no_belt_bptt: bool = False | |
| no_thought_highway: bool = False | |
| ar_baseline: bool = False | |
| overfit_batches: int = 0 | |
| class InferConfig: | |
| prompt_overlap: int = 12 | |
| bake_steps: int = 64 | |
| temperature: float = 0.0 # 0.0 = greedy argmax at slot 0 | |
| max_new_tokens: int = 64 | |
| class DataConfig: | |
| dataset: str = "synthetic" # "synthetic" or "tinystories" | |
| task: str = "all" # "reverse", "path", "constraint", "all", "lm" | |
| seq_len: int = 192 | |
| num_train: int = 24000 | |
| num_val: int = 1024 | |
| data_dir: str = "data/synthetic" | |
| vocab_size: int = 128 | |
| class ConveyerConfig: | |
| model: ModelConfig = field(default_factory=ModelConfig) | |
| train: TrainConfig = field(default_factory=TrainConfig) | |
| infer: InferConfig = field(default_factory=InferConfig) | |
| data: DataConfig = field(default_factory=DataConfig) | |
| def to_dict(self) -> dict[str, Any]: | |
| return asdict(self) | |
| def _parse_scalar(val_str: str) -> Any: | |
| s = val_str.strip() | |
| if not s: | |
| return "" | |
| if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")): | |
| return s[1:-1] | |
| low = s.lower() | |
| if low == "true": | |
| return True | |
| if low == "false": | |
| return False | |
| if low in ("null", "none"): | |
| return None | |
| try: | |
| if "." in s or "e" in low: | |
| return float(s) | |
| return int(s) | |
| except ValueError: | |
| return s | |
| def _simple_yaml_parse(text: str) -> dict[str, Any]: | |
| """Minimal dependency-free YAML parser for 2-level config files.""" | |
| if yaml is not None: | |
| return yaml.safe_load(text) or {} | |
| text_stripped = text.strip() | |
| if text_stripped.startswith("{"): | |
| return json.loads(text_stripped) | |
| root: dict[str, Any] = {} | |
| current_section: dict[str, Any] | None = None | |
| for raw_line in text.splitlines(): | |
| line = raw_line.split("#", 1)[0].rstrip() | |
| if not line.strip(): | |
| continue | |
| indent = len(line) - len(line.lstrip(" ")) | |
| stripped = line.strip() | |
| if ":" not in stripped: | |
| continue | |
| key, val_part = stripped.split(":", 1) | |
| key = key.strip() | |
| val_part = val_part.strip() | |
| if indent == 0: | |
| if val_part == "": | |
| current_section = {} | |
| root[key] = current_section | |
| else: | |
| root[key] = _parse_scalar(val_part) | |
| current_section = None | |
| else: | |
| if current_section is None: | |
| raise ValueError(f"Unexpected indentation in YAML line: {raw_line}") | |
| current_section[key] = _parse_scalar(val_part) | |
| return root | |
| def _merge_dataclass(dc_instance: Any, updates: dict[str, Any]) -> None: | |
| valid_fields = {f.name: f for f in fields(dc_instance)} | |
| for k, v in updates.items(): | |
| if k not in valid_fields: | |
| continue | |
| current_val = getattr(dc_instance, k) | |
| if is_dataclass(current_val) and isinstance(v, dict): | |
| _merge_dataclass(current_val, v) | |
| else: | |
| setattr(dc_instance, k, v) | |
| def _cast_value(raw: str, target_type: Any) -> Any: | |
| if target_type is bool or raw.lower() in ("true", "false"): | |
| return raw.lower() in ("true", "1", "yes") | |
| val = _parse_scalar(raw) | |
| if target_type is float and isinstance(val, int): | |
| return float(val) | |
| if target_type is int and isinstance(val, float): | |
| return int(val) | |
| if target_type is str: | |
| return str(val) | |
| return val | |
| def apply_overrides(cfg: ConveyerConfig, overrides: list[str]) -> ConveyerConfig: | |
| for item in overrides: | |
| if "=" not in item: | |
| raise ValueError(f"Override must be in key=value format, got: {item}") | |
| key_path, raw_val = item.split("=", 1) | |
| parts = key_path.strip().split(".") | |
| obj: Any = cfg | |
| for p in parts[:-1]: | |
| if not hasattr(obj, p): | |
| raise KeyError(f"Unknown config section '{p}' in '{key_path}'") | |
| obj = getattr(obj, p) | |
| leaf = parts[-1] | |
| if not hasattr(obj, leaf): | |
| raise KeyError(f"Unknown config key '{leaf}' in '{key_path}'") | |
| cur = getattr(obj, leaf) | |
| val = _cast_value(raw_val.strip(), type(cur)) | |
| setattr(obj, leaf, val) | |
| _sync_config(cfg) | |
| return cfg | |
| def _sync_config(cfg: ConveyerConfig) -> None: | |
| cfg.model.vocab_size = cfg.data.vocab_size | |
| if cfg.train.no_thought_highway: | |
| cfg.model.use_thought_highway = False | |
| if cfg.train.ar_baseline: | |
| cfg.model.causal_belt_only = True | |
| def load_config(path: str | Path | None = None, overrides: list[str] | None = None) -> ConveyerConfig: | |
| cfg = ConveyerConfig() | |
| if path is not None: | |
| raw = _simple_yaml_parse(Path(path).read_text(encoding="utf-8")) | |
| _merge_dataclass(cfg, raw) | |
| if overrides: | |
| apply_overrides(cfg, overrides) | |
| _sync_config(cfg) | |
| return cfg | |