HRM_sudoku / eval_dummy.py
Code2aum's picture
Upload folder using huggingface_hub
5dc80b3 verified
Raw
History Blame Contribute Delete
10.4 kB
#!/usr/bin/env python3
"""
eval_dummy.py β€” Evaluate Tiered vs Baseline HRM using dummy (random) tensors.
No datasets or checkpoints needed. This measures pure model throughput,
latency, and memory usage on synthetic inputs.
Usage:
source venv/bin/activate
python eval_dummy.py
python eval_dummy.py --batch-size 64 --seq-len 256 --hidden-size 1024
python eval_dummy.py --iterations 50 --plot
"""
import argparse
import json
import os
import sys
import time
from dataclasses import dataclass, asdict
import torch
import torch.nn.functional as F
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from models.memory_tier import MemoryTierManager
from models.hrm.hrm_tiered import HRM_Tiered
from models.hrm.hrm_act_v1 import HierarchicalReasoningModel_ACTV1
# =====================================================================
# Dummy batch generator
# =====================================================================
def make_batch(batch_size, seq_len, vocab_size, device):
return {
"inputs": torch.randint(0, vocab_size, (batch_size, seq_len), device=device),
"labels": torch.randint(0, vocab_size, (batch_size, seq_len), device=device),
"puzzle_identifiers": torch.arange(batch_size, device=device),
}
# =====================================================================
# Single-model evaluation
# =====================================================================
@dataclass
class EvalResult:
model_name: str
batch_size: int
seq_len: int
hidden_size: int
param_count: int
latency_mean_ms: float
latency_std_ms: float
throughput_sps: float
gpu_memory_mb: float
def eval_model(model, model_name, batch, device, warmup=5, iterations=20):
"""Run inference on dummy data and collect timing stats."""
model.eval()
bs = batch["inputs"].shape[0]
sl = batch["inputs"].shape[1]
param_count = sum(p.numel() for p in model.parameters())
# Warmup
with torch.no_grad():
for _ in range(warmup):
carry = model.initial_carry(batch)
carry.inner_carry.z_H = carry.inner_carry.z_H.to(device)
carry.inner_carry.z_L = carry.inner_carry.z_L.to(device)
carry.steps = carry.steps.to(device)
carry.halted = carry.halted.to(device)
carry.current_data = {k: v.to(device) for k, v in carry.current_data.items()}
model(carry, batch)
if torch.cuda.is_available():
torch.cuda.reset_peak_memory_stats(device)
torch.cuda.synchronize()
# Timed iterations
latencies = []
with torch.no_grad():
for _ in range(iterations):
carry = model.initial_carry(batch)
carry.inner_carry.z_H = carry.inner_carry.z_H.to(device)
carry.inner_carry.z_L = carry.inner_carry.z_L.to(device)
carry.steps = carry.steps.to(device)
carry.halted = carry.halted.to(device)
carry.current_data = {k: v.to(device) for k, v in carry.current_data.items()}
if torch.cuda.is_available():
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
model(carry, batch)
if torch.cuda.is_available():
end.record()
torch.cuda.synchronize()
latencies.append(start.elapsed_time(end))
else:
pass # CPU fallback handled by perf_counter
mean_ms = sum(latencies) / len(latencies)
std_ms = (sum((x - mean_ms)**2 for x in latencies) / max(len(latencies)-1, 1)) ** 0.5
throughput = bs / (mean_ms / 1000) if mean_ms > 0 else 0
gpu_mem = torch.cuda.max_memory_allocated(device) / (1024**2) if torch.cuda.is_available() else 0
return EvalResult(
model_name=model_name,
batch_size=bs, seq_len=sl,
hidden_size=model.config.hidden_size,
param_count=param_count,
latency_mean_ms=mean_ms,
latency_std_ms=std_ms,
throughput_sps=throughput,
gpu_memory_mb=gpu_mem,
)
# =====================================================================
# Main comparison
# =====================================================================
def main():
parser = argparse.ArgumentParser(description="HRM Dummy Tensor Evaluation")
parser.add_argument("--batch-size", type=int, default=8)
parser.add_argument("--seq-len", type=int, default=81, help="Sudoku=81, or any length")
parser.add_argument("--hidden-size", type=int, default=512)
parser.add_argument("--num-heads", type=int, default=8)
parser.add_argument("--H-cycles", type=int, default=2)
parser.add_argument("--L-cycles", type=int, default=2)
parser.add_argument("--H-layers", type=int, default=4)
parser.add_argument("--L-layers", type=int, default=4)
parser.add_argument("--warmup", type=int, default=5)
parser.add_argument("--iterations", type=int, default=20)
parser.add_argument("--output", type=str, default="benchmark_results/eval_dummy.json")
parser.add_argument("--plot", action="store_true")
args = parser.parse_args()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
vocab_size = 32
config_dict = {
"batch_size": args.batch_size,
"seq_len": args.seq_len,
"puzzle_emb_ndim": 0,
"num_puzzle_identifiers": args.batch_size,
"vocab_size": vocab_size,
"H_cycles": args.H_cycles,
"L_cycles": args.L_cycles,
"H_layers": args.H_layers,
"L_layers": args.L_layers,
"hidden_size": args.hidden_size,
"expansion": 4.0,
"num_heads": args.num_heads,
"pos_encodings": "rope",
"halt_max_steps": 1,
"halt_exploration_prob": 0.0,
}
batch = make_batch(args.batch_size, args.seq_len, vocab_size, device)
print(f"\n{'='*64}")
print(f" HRM Dummy Tensor Evaluation")
print(f" Device: {device} ({torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'})")
print(f" Batch: {args.batch_size}")
print(f" Seq Len: {args.seq_len}")
print(f" Hidden: {args.hidden_size}")
print(f" H/L cycles: {args.H_cycles}/{args.L_cycles}")
print(f" H/L layers: {args.H_layers}/{args.L_layers}")
print(f" Iterations: {args.iterations} (warmup: {args.warmup})")
print(f"{'='*64}")
results = []
# ── Baseline ──
print(f"\n β†’ Evaluating Baseline (hrm_act_v1)...")
baseline_model = HierarchicalReasoningModel_ACTV1(config_dict).to(device)
r_baseline = eval_model(baseline_model, "Baseline", batch, device, args.warmup, args.iterations)
results.append(r_baseline)
del baseline_model
torch.cuda.empty_cache()
# ── Tiered ──
print(f" β†’ Evaluating Tiered (SRAM/DRAM Triton)...")
mem_mgr = MemoryTierManager(device=device, enable_tracking=True)
tiered_model = HRM_Tiered(config_dict, memory_manager=mem_mgr).to(device)
r_tiered = eval_model(tiered_model, "Tiered", batch, device, args.warmup, args.iterations)
# Grab tier-specific stats
tier_timing = tiered_model.get_timing_stats()
tier_mem = mem_mgr.get_stats()
results.append(r_tiered)
del tiered_model
torch.cuda.empty_cache()
# ── Print comparison table ──
print(f"\n{'='*64}")
print(f" {'Model':<12} {'Params':>10} {'Latency(ms)':>13} {'Β±Οƒ':>8} {'Throughput':>12} {'GPU MB':>8}")
print(f" {'-'*58}")
for r in results:
print(f" {r.model_name:<12} {r.param_count/1e6:>9.1f}M {r.latency_mean_ms:>13.2f} {r.latency_std_ms:>8.2f} {r.throughput_sps:>12.1f} {r.gpu_memory_mb:>8.1f}")
# Speedup
speedup = r_baseline.latency_mean_ms / r_tiered.latency_mean_ms if r_tiered.latency_mean_ms > 0 else 0
mem_diff = r_baseline.gpu_memory_mb - r_tiered.gpu_memory_mb
print(f"\n Speedup: {speedup:.2f}x")
print(f" Memory saving: {mem_diff:.1f} MB")
# Tier-specific stats
if tier_timing:
l_us = tier_timing.get("L_forward_us", {}).get("mean_us", 0)
h_us = tier_timing.get("H_forward_us", {}).get("mean_us", 0)
ratio = h_us / l_us if l_us > 0 else 0
print(f"\n L-level (SRAM): {l_us:.1f} ΞΌs")
print(f" H-level (DRAM): {h_us:.1f} ΞΌs")
print(f" H/L ratio: {ratio:.2f}x")
if tier_mem:
print(f" SRAM hit rate: {tier_mem['sram']['hit_rate']:.2%}")
print(f"{'='*64}\n")
# ── Save ──
os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
with open(args.output, "w") as f:
json.dump([asdict(r) for r in results], f, indent=2)
print(f" Results saved β†’ {args.output}")
# ── Plots ──
if args.plot:
try:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle("HRM Dummy Eval: Baseline vs Tiered", fontweight="bold")
names = [r.model_name for r in results]
colors = ["#e74c3c", "#2ecc71"]
# Latency
axes[0].bar(names, [r.latency_mean_ms for r in results], color=colors)
axes[0].set_ylabel("Latency (ms)")
axes[0].set_title("Inference Latency")
axes[0].grid(axis="y", alpha=0.3)
# Throughput
axes[1].bar(names, [r.throughput_sps for r in results], color=colors)
axes[1].set_ylabel("Samples/sec")
axes[1].set_title("Throughput")
axes[1].grid(axis="y", alpha=0.3)
# Memory
axes[2].bar(names, [r.gpu_memory_mb for r in results], color=colors)
axes[2].set_ylabel("GPU Memory (MB)")
axes[2].set_title("Peak Memory")
axes[2].grid(axis="y", alpha=0.3)
plt.tight_layout()
plot_dir = os.path.dirname(args.output) or "benchmark_results"
plot_path = os.path.join(plot_dir, "eval_dummy_comparison.png")
plt.savefig(plot_path, dpi=150)
plt.close()
print(f" Plot saved β†’ {plot_path}")
except ImportError:
print(" (matplotlib not found β€” skipping plots)")
print(" Done!\n")
if __name__ == "__main__":
main()