File size: 20,360 Bytes
4d74557 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 | """
FORGE-3B: Master Configuration
All hyperparameters, paths, and GPU optimization settings in one place.
"""
from __future__ import annotations
import os
import json
import math
from dataclasses import dataclass, field, asdict
from typing import Optional, List, Literal
from pathlib import Path
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# GPU GLOBAL OPTIMIZATION FLAGS
# These are applied at import time across the entire process.
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
import torch
# Allow TF32 for matmuls and convolutions β massive speedup on Ampere/Hopper
# with negligible precision loss for language model training.
if torch.cuda.is_available():
torch.backends.cuda.matmul.allow_tf32 = True
torch.backends.cudnn.allow_tf32 = True
torch.backends.cudnn.benchmark = True # auto-tune cuDNN kernels
try:
torch.backends.cuda.enable_flash_sdp(True) # Flash SDP in PyTorch 2.x
torch.backends.cuda.enable_mem_efficient_sdp(True)
torch.backends.cuda.enable_math_sdp(False) # disable slow math SDP
except Exception:
pass
# NCCL tuning for 64x H100 GPU cluster (8x 8-GPU nodes via NVLink + 3.2 Tbps InfiniBand)
os.environ.setdefault("NCCL_MIN_NCHANNELS", "32")
os.environ.setdefault("NCCL_MAX_NCHANNELS", "64")
os.environ.setdefault("NCCL_NSOCKS_PERTHREAD", "8")
os.environ.setdefault("NCCL_SOCKET_NTHREADS", "8")
os.environ.setdefault("NCCL_BUFFSIZE", "16777216") # 16MB ring buffer for high-bandwidth AllReduce
os.environ.setdefault("NCCL_NET_GDR_LEVEL", "5") # GPUDirect RDMA Level 5 for NVLink + InfiniBand
os.environ.setdefault("NCCL_CROSS_NIC", "1") # Multi-NIC rail-optimized routing
os.environ.setdefault("NCCL_IB_DISABLE", "0") # InfiniBand enabled
os.environ.setdefault("NCCL_P2P_DISABLE", "0") # NVLink P2P enabled
os.environ.setdefault("NCCL_NVLS_ENABLE", "1") # NVLink SHARP collective offload
os.environ.setdefault("CUDA_DEVICE_MAX_CONNECTIONS", "1") # Precise CUDA stream scheduling
# CUDA memory allocator β reduce fragmentation for large model + optimizer states
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF",
"expandable_segments:True,max_split_size_mb:128,garbage_collection_threshold:0.8")
# Tokenizer parallelism β disable HF tokenizer parallelism (we use CRAYON)
os.environ["TOKENIZERS_PARALLELISM"] = "false"
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# MODEL CONFIGURATION
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@dataclass
class ForgeModelConfig:
"""Complete FORGE-500M architecture configuration. 500.02M total params."""
# ββ Core Dimensions ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
vocab_size: int = 206_464 # CRAYON standard profile (206,373) padded to Γ128
d_model: int = 1024 # 500M: 1024 (down from 1280)
n_layers: int = 18 # 500M: 18 layers (15 ARG + 3 MHA)
max_seq_len: int = 4096
# ββ Layer Distribution ββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Layer 0-4 = ARG; Layer 5 = MHA; repeat Γ3 across 18 layers
mha_layer_indices: List[int] = field(default_factory=lambda: [5, 11, 17])
# Dense FFN on odd layers, HSE on even layers
dense_ffn_layer_indices: List[int] = field(default_factory=lambda: list(range(1, 18, 2)))
hse_ffn_layer_indices: List[int] = field(default_factory=lambda: list(range(0, 18, 2)))
# ββ ARG (Adaptive Recurrent Gating) ββββββββββββββββββββββββββββββββββββββ
arg_d_inner: int = 1024 # matches d_model for 500M
arg_d_state: int = 48
arg_d_rank: int = 48 # dt projection rank
arg_conv_kernel: int = 4
arg_local_window: int = 64 # local attention window (phase 1+2)
arg_local_n_heads: int = 8
arg_local_n_kv_heads: int = 2
arg_head_dim: int = 64 # 1024 / 16 heads
arg_scalar_gate: bool = True # scalar gate per token (vs d_model-dim)
# ββ MHA (Multi-Head Attention) ββββββββββββββββββββββββββββββββββββββββββββ
mha_n_heads: int = 16
mha_n_kv_heads: int = 4 # GQA: 4:1 ratio
mha_head_dim: int = 64 # 1024 / 16 heads
rope_theta: float = 500_000.0 # long-context RoPE base
rope_scaling_type: Optional[str] = None # None | "yarn"
rope_scaling_factor: float = 1.0
# ββ Dense FFN (SwiGLU) ββββββββββββββββββββββββββββββββββββββββββββββββββββ
dense_d_ff: int = 2560 # 2.5 Γ d_model, multiple of 128
# ββ HSE FFN (Hierarchical Sparse Expert) ββββββββββββββββββββββββββββββββββ
hse_n_domains: int = 4
hse_n_experts_per_domain: int = 6 # total 24 experts (down from 32)
hse_top_k: int = 2 # active per token
hse_d_ff_expert: int = 210 # scaled for 500M total params
hse_capacity_factor: float = 1.25
hse_expert_dropout: float = 0.1
hse_aux_loss_alpha: float = 0.01
hse_gumbel_tau_init: float = 1.0
hse_gumbel_tau_final: float = 0.1
hse_gumbel_tau_warmup_tokens: int = 10_000_000_000 # anneal over 10B tokens
# ββ Normalization βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
norm_type: str = "dgn" # "dgn" or "rmsnorm"
dgn_n_groups: int = 16 # 1024 / 16 = 64 per group
norm_eps: float = 1e-6
# ββ Initialization ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
init_std: float = 0.02
init_std_residual_scale: bool = True # scale by 1/sqrt(2n_layers)
# ββ Misc ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
dropout: float = 0.0 # 0.0 during pretraining
tie_word_embeddings: bool = True
use_cache: bool = True # KV cache for inference
pad_token_id: int = 0
bos_token_id: int = 1
eos_token_id: int = 2
# ββ Compile/Optimization Flags ββββββββββββββββββββββββββββββββββββ
use_flash_attention: bool = True
use_triton_kernels: bool = True # Triton fused ops
use_torch_compile: bool = True
compile_mode: str = "max-autotune" # max throughput on H100
use_gradient_checkpointing: bool = False # 500M fits easily in 80GB without checkpointing
gradient_checkpointing_ratio: float = 0.0
def n_params_total(self) -> int:
"""Approximate total parameter count."""
embed = self.vocab_size * self.d_model
n_arg = len([i for i in range(self.n_layers) if i not in self.mha_layer_indices])
n_mha = len(self.mha_layer_indices)
n_dense = len(self.dense_ffn_layer_indices)
n_hse = len(self.hse_ffn_layer_indices)
arg_per = (
self.d_model * 2 * self.arg_d_inner + # in_proj
self.arg_d_inner * self.arg_conv_kernel + # conv1d
self.arg_d_inner * (self.arg_d_rank + 2 * self.arg_d_state) + # x_proj
self.arg_d_rank * self.arg_d_inner + # dt_proj
self.arg_d_state * 2 + # nu, theta
self.arg_d_inner + # D
self.arg_d_inner * self.d_model + # out_proj
self.arg_local_n_heads * self.arg_head_dim * self.d_model + # Q
self.arg_local_n_kv_heads * self.arg_head_dim * self.d_model * 2 + # KV
self.arg_local_n_heads * self.arg_head_dim * self.d_model + # O
self.d_model * self.arg_d_state # CPB
)
mha_per = (
self.d_model * self.mha_n_heads * self.mha_head_dim + # Q
self.d_model * self.mha_n_kv_heads * self.mha_head_dim * 2 + # KV
self.d_model * self.mha_n_heads * self.mha_head_dim # O
)
dense_per = 3 * self.d_model * self.dense_d_ff
n_experts_total = self.hse_n_domains * self.hse_n_experts_per_domain
hse_per = n_experts_total * 3 * self.d_model * self.hse_d_ff_expert
total = embed + n_arg * arg_per + n_mha * mha_per + n_dense * dense_per + n_hse * hse_per
return total
def to_json(self, path: str):
with open(path, "w") as f:
json.dump(asdict(self), f, indent=2)
@classmethod
def from_json(cls, path: str) -> "ForgeModelConfig":
import inspect
with open(path) as f:
data = json.load(f)
sig = inspect.signature(cls)
filtered_data = {k: v for k, v in data.items() if k in sig.parameters}
return cls(**filtered_data)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# TRAINING CONFIGURATION
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@dataclass
class PretrainConfig:
"""Phase-aware pretraining configuration for FORGE-500M.
Budget math (1x H100 SXM @ $3.29/hr):
- Phase 1 (vocab warmup): 2B tokens @ 80k tok/s β ~7h β $23
- Phase 2 (core pretrain): 16B tokens @ 80k tok/s β ~56h β $184
- Phase 3 (ctx extension): 2B tokens @ 60k tok/s β ~9h β $30
PRETRAIN TOTAL: ~72h β ~$237
SFT: 1.4B tokens β ~5h β $16
DPO: 0.8B tokens β ~3h β $10
GRAND TOTAL: ~$263 of $400 budget. $137 buffer.
"""
output_dir: str = "./checkpoints/forge_500m_pretrain"
data_dir: str = "Phase-Technologies/forge-3b-pretrain-data" # same tokenized data
resume_from_checkpoint: Optional[str] = None
# ββ Token Budget (15B Tokens for 500M params: ~$245.59 total cost @ $3.50/GPU/hr on 32x H100) ββ
# 15B tokens directly in Phase 2 at seq=2048 (Phase 1 & Phase 3 omitted)
phase1_tokens: int = 0 # Omitted (start directly on Phase 2 @ seq=2048)
phase2_tokens: int = 15_000_000_000 # 15.0B core pretrain (seq=2048) -> ~2.08h ($233.33)
phase3_tokens: int = 0 # Omitted (pure seq=2048 pretraining)
total_tokens: int = 15_000_000_000 # 15.0B total pretrain
# ββ Batch Config (Tuned for 32x H100 SXM β 1,048,576 tokens/step) βββββββββ
# With 32 GPUs @ micro_batch=16 (seq=2048): 32 Γ 16 Γ 2048 = 1,048,576 tokens per pass!
# Exactly 1 gradient accumulation step (zero idle wait, maximum NVLink saturation)
phase1_global_batch_tokens: int = 1_048_576 # 1M tokens/step
phase2_global_batch_tokens: int = 1_048_576 # 1M tokens/step (Phase 2)
phase3_global_batch_tokens: int = 1_048_576 # 1M tokens/step (Phase 3)
micro_batch_size_per_gpu: int = 16 # default fallback
phase1_micro_batch_per_gpu: int = 64 # 64 seqs Γ 512 = 32,768 tokens/GPU
phase2_micro_batch_per_gpu: int = 16 # 16 seqs Γ 2048 = 32,768 tokens/GPU (1 accum step across 32 GPUs)
phase3_micro_batch_per_gpu: int = 8 # 8 seqs Γ 4096 = 32,768 tokens/GPU
# ββ Context Lengths βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
phase1_seq_len: int = 512
phase2_seq_len: int = 2048
phase3_seq_len: int = 2048
# ββ Learning Rate βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
lr_max: float = 3e-4
lr_min: float = 3e-5
lr_warmup_tokens: int = 500_000_000 # 500M tokens warmup directly in Phase 2
lr_schedule: str = "cosine"
# ββ AdamW βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
beta1: float = 0.9
beta2: float = 0.95
eps: float = 1e-8
weight_decay: float = 0.1
grad_clip: float = 1.0
# ββ Parameter Groups LR Multipliers ββββββββββββββββββββββββββββββββββββββ
embedding_lr_mult: float = 0.5
ssm_lr_mult: float = 0.3
router_lr_mult: float = 0.5
# ββ Checkpointing (Hyper-Resilient: saves every 20-30 mins OR 500M tokens)
save_every_n_tokens: int = 500_000_000 # checkpoint every 500M tokens (~8 mins on 32x GPUs)
save_every_minutes: float = 20.0 # time-based checkpoint guard (every 20 minutes)
keep_last_n_checkpoints: int = 10 # keep 10 historical snapshots
# ββ Logging βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
log_every_n_steps: int = 1 # log every step for full metrics visibility
eval_every_n_steps: int = 200 # eval perplexity every 200 steps
wandb_project: str = "forge_500m_pretrain"
wandb_entity: Optional[str] = None
# ββ GPU/Distributed βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
num_gpus: int = 1 # default 1x H100; set to 16 for 16x
bf16: bool = True
fp8: bool = False # H100 FP8 (experimental)
deepspeed_config: str = "./configs/ds_zero3.json"
torch_compile: bool = True
compile_mode: str = "max-autotune" # peak throughput
# ββ Data ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
num_dataloader_workers: int = 4
prefetch_factor: int = 4
seed: int = 42
@property
def gradient_accumulation_steps_phase2(self) -> int:
"""Accumulation steps for phase 2 global batch."""
tokens_per_gpu_step = self.micro_batch_size_per_gpu * self.phase2_seq_len
tokens_per_step_all_gpus = tokens_per_gpu_step * self.num_gpus
return self.phase2_global_batch_tokens // tokens_per_step_all_gpus
@dataclass
class SFTConfig:
"""Supervised Fine-Tuning configuration for FORGE-500M."""
base_model_path: str = "./checkpoints/forge_500m_pretrain/final"
output_dir: str = "./checkpoints/forge_500m_sft"
data_dir: str = "Phase-Technologies/forge-3b-sft-data"
# ββ Training ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
total_tokens: int = 1_400_000_000
global_batch_tokens: int = 524_288 # 512K per step
micro_batch_size_per_gpu: int = 4
seq_len: int = 4096
# ββ Optimizer βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
lr_max: float = 1e-5
lr_min: float = 1e-6
lr_schedule: str = "cosine"
beta1: float = 0.9
beta2: float = 0.95
eps: float = 1e-8
weight_decay: float = 0.0
grad_clip: float = 0.5
epochs: int = 1
loss_on_prompt: bool = False # only compute loss on assistant turns
save_every_n_steps: int = 100
wandb_project: str = "forge_500m_sft"
deepspeed_config: str = "./configs/ds_zero3_sft.json"
num_gpus: int = 1
bf16: bool = True
torch_compile: bool = True
compile_mode: str = "max-autotune"
@dataclass
class DPOConfig:
"""Direct Preference Optimization configuration for FORGE-500M."""
base_model_path: str = "./checkpoints/forge_500m_sft/final"
output_dir: str = "./checkpoints/forge_500m_dpo"
data_dir: str = "Phase-Technologies/forge-3b-dpo-data"
# ββ DPO βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
beta: float = 0.1 # KL penalty coefficient
loss_type: str = "dpo" # "dpo" | "ipo" | "cdpo"
reference_free: bool = False
# ββ Training ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
n_preference_pairs: int = 200_000
batch_size_pairs: int = 8 # preference pairs per GPU step
gradient_accumulation_steps: int = 4
seq_len: int = 4096
# ββ Optimizer βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
lr: float = 5e-7
beta1: float = 0.9
beta2: float = 0.99
eps: float = 1e-8
weight_decay: float = 0.0
grad_clip: float = 0.3
n_epochs: int = 1
save_every_n_steps: int = 100
wandb_project: str = "forge_500m_dpo"
deepspeed_config: str = "./configs/ds_zero3_sft.json"
num_gpus: int = 1
bf16: bool = True
torch_compile: bool = True
compile_mode: str = "max-autotune"
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# TOKENIZER CONFIGURATION
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@dataclass
class CrayonConfig:
"""CRAYON tokenizer configuration."""
profile: str = "standard" # "standard" (206k) | "lite" (50k)
device: str = "cpu" # "cpu" is faster for tokenization (23M vs 1.2M tok/s)
pad_token: str = "<|pad|>"
bos_token: str = "<|bos|>"
eos_token: str = "<|eos|>"
sep_token: str = "<|sep|>"
sys_token: str = "<|sys|>"
usr_token: str = "<|usr|>"
asst_token: str = "<|asst|>"
tool_token: str = "<|tool|>"
tool_resp_token: str = "<|tool_resp|>" |