#!/usr/bin/env python3 """Saturday Milestone 1 Verification Script. This script verifies that the Saturday-1M model is correctly implemented by running a complete forward pass and checking all tensor shapes. """ import sys import os sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import numpy as np from saturday_numpy.config import SaturdayConfig from saturday_numpy.model.saturday import SaturdayModel from saturday_numpy.training.loss import compute_loss_from_tokens from saturday_numpy.utils.hardware import detect_hardware, print_hardware_info def main(): print("=" * 60) print("Saturday - Milestone 1 Verification") print("=" * 60) # 1. Hardware detection print("\n--- Hardware Info ---") print_hardware_info() # 2. Load config print("\n--- Model Configuration ---") config = SaturdayConfig() # Default Saturday-1M param_info = config.count_parameters() for key, value in param_info.items(): print(f" {key}: {value:,}") # 3. Create model print("\n--- Creating Saturday-1M ---") model = SaturdayModel(config) total = model.count_parameters() print(f" Total parameters: {total:,}") print(f" Model size: {total * 4 / 1024 / 1024:.2f} MB (float32)") # 4. Forward pass print("\n--- Forward Pass ---") rng = np.random.default_rng(42) batch_size = 2 seq_len = 16 token_ids = rng.integers(0, config.vocab_size, size=(batch_size, seq_len)) print(f" Input shape: {token_ids.shape}") print(f" Input tokens: {token_ids[0, :8]}...") logits = model.forward(token_ids) print(f" Output logits shape: {logits.shape}") print(f" Expected shape: ({batch_size}, {seq_len}, {config.vocab_size})") assert logits.shape == (batch_size, seq_len, config.vocab_size), "Shape mismatch!" print(f" [OK] Shape correct") # 5. Check for NaN/Inf assert not np.any(np.isnan(logits)), "NaN in logits!" assert not np.any(np.isinf(logits)), "Inf in logits!" print(f" [OK] No NaN or Inf") # 6. Loss computation print("\n--- Loss Computation ---") loss, _ = compute_loss_from_tokens(model, token_ids) expected_random_loss = np.log(config.vocab_size) print(f" Loss: {loss:.4f}") print(f" Expected (random model): ~{expected_random_loss:.4f} = ln({config.vocab_size})") print(f" Ratio: {loss / expected_random_loss:.4f} (should be close to 1.0)") assert 0.5 * expected_random_loss < loss < 2.0 * expected_random_loss, \ f"Loss {loss:.4f} too far from expected {expected_random_loss:.4f}" print(f" [OK] Loss in expected range") # 7. Causal mask verification print("\n--- Causal Mask Verification ---") print(" Creating causal mask for seq_len=5:") mask = np.triu(np.full((5, 5), -np.inf), k=1) for i in range(5): row = [] for j in range(5): if mask[i, j] == -np.inf: row.append(" -inf ") else: row.append(" 0.00 ") print(f" [{', '.join(row)}]") print(f" [OK] Token at position i can only attend to positions 0..i") # 8. Determinism check print("\n--- Determinism Check ---") model2 = SaturdayModel(config) # Same seed logits2 = model2.forward(token_ids) max_diff = np.max(np.abs(logits - logits2)) print(f" Max difference between two models with same seed: {max_diff}") assert max_diff < 1e-6, "Models not deterministic!" print(f" [OK] Deterministic") # Summary print("\n" + "=" * 60) print("ALL MILESTONE 1 VERIFICATIONS PASSED [OK]") print("=" * 60) print(f"\nSaturday-1M is ready.") print(f" Parameters: {total:,}") print(f" Architecture: {config.num_layers} layers, {config.num_attention_heads} heads") print(f" Hidden size: {config.hidden_size}, FFN size: {config.intermediate_size}") print(f" Sequence length: {config.max_sequence_length}") print(f" Vocabulary: {config.vocab_size}") if __name__ == "__main__": main()