Files
caption-test/src/models/sofia_v71/verify.py
2026-04-29 08:04:33 +03:00

159 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Verify SOFIA v7.1 model scales: param count, memory footprint, forward pass.
Run:
python -m sofia_v71.verify
python -m sofia_v71.verify --preset L
python -m sofia_v71.verify --preset M --benchmark
"""
from __future__ import annotations
import argparse
import time
from typing import Optional
import torch
from .blocks import is_mamba_ssm_available, is_mamba2_available
from .config import sofia_m_config, sofia_l_config, sofia_tiny_config
from .model import SOFIAv71
def count_parameters(model: torch.nn.Module) -> dict:
"""Count parameters per named child."""
counts = {}
for name, param in model.named_parameters():
top = name.split(".")[0]
counts[top] = counts.get(top, 0) + param.numel()
counts["_total"] = sum(counts.values())
return counts
def estimate_quantized_size(n_params: int, precision: str = "int8") -> float:
"""Estimate on-disk / VRAM weight storage in MB."""
bytes_per_param = {"fp32": 4, "fp16": 2, "int8": 1, "int4": 0.5}[precision]
return n_params * bytes_per_param / (1024 ** 2)
def test_forward(model: SOFIAv71, device: str = "cpu", batch_size: int = 1) -> dict:
"""Dry forward pass, return output shapes."""
model = model.to(device).eval()
cfg = model.cfg
sat = torch.randn(batch_size, 3, cfg.input_size, cfg.input_size, device=device)
uav = torch.randn(batch_size, 3, cfg.input_size, cfg.input_size, device=device)
altitude = torch.rand(batch_size, device=device) * 300.0 + 50.0
shapes = {}
with torch.no_grad():
out = model(sat=sat, uav=uav, altitude=altitude)
for k, v in out.items():
if isinstance(v, torch.Tensor):
shapes[k] = tuple(v.shape)
elif isinstance(v, list):
shapes[k] = f"list[{len(v)} × {tuple(v[0].shape)}]"
elif isinstance(v, dict):
for kk, vv in v.items():
if isinstance(vv, torch.Tensor):
shapes[f"{k}.{kk}"] = tuple(vv.shape)
return shapes
def benchmark(model: SOFIAv71, device: str = "cpu", n_warmup: int = 3, n_runs: int = 20) -> dict:
"""Measure forward pass latency."""
model = model.to(device).eval()
cfg = model.cfg
sat = torch.randn(1, 3, cfg.input_size, cfg.input_size, device=device)
uav = torch.randn(1, 3, cfg.input_size, cfg.input_size, device=device)
altitude = torch.tensor([150.0], device=device)
# Warmup
with torch.no_grad():
for _ in range(n_warmup):
_ = model(sat=sat, uav=uav, altitude=altitude)
if device.startswith("cuda"):
torch.cuda.synchronize()
# Measured runs
times = []
for _ in range(n_runs):
if device.startswith("cuda"):
torch.cuda.synchronize()
t0 = time.perf_counter()
_ = model(sat=sat, uav=uav, altitude=altitude)
if device.startswith("cuda"):
torch.cuda.synchronize()
times.append(time.perf_counter() - t0)
times_ms = [t * 1000 for t in times]
return {
"mean_ms": sum(times_ms) / len(times_ms),
"min_ms": min(times_ms),
"max_ms": max(times_ms),
"std_ms": (sum((t - sum(times_ms) / len(times_ms)) ** 2 for t in times_ms) / len(times_ms)) ** 0.5,
}
def main() -> None:
parser = argparse.ArgumentParser(description="Verify SOFIA v7.1 model")
parser.add_argument("--preset", choices=["Tiny", "M", "L"], default="Tiny",
help="Model preset (default: Tiny for fast smoke test)")
parser.add_argument("--device", default="cpu")
parser.add_argument("--benchmark", action="store_true")
parser.add_argument("--batch-size", type=int, default=1)
args = parser.parse_args()
cfg_fn = {"Tiny": sofia_tiny_config, "M": sofia_m_config, "L": sofia_l_config}[args.preset]
cfg = cfg_fn()
# Report mamba_ssm availability
print(f"\n=== Environment ===")
print(f" mamba_ssm v1 (Mamba-1 selective scan): "
f"{'available' if is_mamba_ssm_available() else 'NOT available'}")
print(f" mamba_ssm Mamba-2 (SSD dual form): "
f"{'available' if is_mamba2_available() else 'NOT available'}")
print(f" configured variant: {cfg.mamba_variant}, backend: {cfg.mamba_backend}")
print(f"\n=== SOFIA-{args.preset} configuration ===")
print(cfg.summary())
print("\n=== Building model ===")
model = SOFIAv71(cfg)
counts = count_parameters(model)
total = counts["_total"]
print(f"\nTotal params: {total / 1e6:.2f} M")
print("\nPer-module breakdown:")
for k, v in sorted(counts.items(), key=lambda x: -x[1]):
if k == "_total":
continue
pct = 100 * v / total
print(f" {k:30s} {v / 1e6:>8.3f} M ({pct:5.1f}%)")
print("\n=== Memory footprint estimates ===")
for prec in ["fp32", "fp16", "int8", "int4"]:
mb = estimate_quantized_size(total, prec)
print(f" {prec.upper():6s} weights: {mb:>8.1f} MB ({mb / 1024:.2f} GB)")
print("\n=== Forward pass test ===")
try:
shapes = test_forward(model, device=args.device, batch_size=args.batch_size)
for k, v in shapes.items():
print(f" out[{k}]: {v}")
except Exception as e:
print(f" ERROR during forward: {type(e).__name__}: {e}")
import traceback
traceback.print_exc()
if args.benchmark:
print("\n=== Latency benchmark ===")
try:
stats = benchmark(model, device=args.device)
print(f" mean: {stats['mean_ms']:.2f} ms min: {stats['min_ms']:.2f} "
f"max: {stats['max_ms']:.2f} std: {stats['std_ms']:.2f}")
except Exception as e:
print(f" ERROR during benchmark: {type(e).__name__}: {e}")
if __name__ == "__main__":
main()