OpenTransformer/agillm41-checkpoints / code /agillm4 /training_bench /agillm4_export_bench_packages.py
OpenTransformer's picture
download
raw
12.6 kB
#!/usr/bin/env python3
"""Export AGILLM4 DBlock benchmark packages from a full checkpoint.
The packages are intentionally non-destructive: workers train a copied slice and
write update/state stats, but the active checkpoint is not modified.
"""
from __future__ import annotations
import argparse
import importlib.util
import json
import os
from pathlib import Path
import random
import sys
import time
from typing import Any
import torch
def parse_workers(spec: str) -> list[tuple[str, int]]:
out: list[tuple[str, int]] = []
for item in spec.split(","):
item = item.strip()
if not item:
continue
name, block = item.rsplit(":", 1)
out.append((name.strip(), int(block)))
return out
def dblock_layers(total_layers: int, blocks: int) -> list[list[int]]:
span = max(1, total_layers // blocks)
assign = [list(range(i * span, (i + 1) * span)) for i in range(blocks)]
assign[-1] = list(range((blocks - 1) * span, total_layers))
return assign
def local_block_state(core_state: dict[str, Any], layers: list[int]) -> dict[str, Any]:
out: dict[str, Any] = {}
for local_i, global_i in enumerate(layers):
src_prefix = f"blocks.{global_i}."
dst_prefix = f"blocks.{local_i}."
for key, value in core_state.items():
if isinstance(key, str) and key.startswith(src_prefix):
out[dst_prefix + key[len(src_prefix) :]] = value.detach().cpu()
return out
def token_batches(vocab: int, steps: int, batch_size: int, block_size: int, seed: int) -> torch.Tensor:
gen = torch.Generator(device="cpu")
gen.manual_seed(int(seed))
# Keep clear of special tokens; this is a compute benchmark, not a quality run.
return torch.randint(2, int(vocab), (int(steps), int(batch_size), int(block_size)), generator=gen, dtype=torch.long)
def load_runtime(path: str | Path):
path = Path(path).resolve()
os.environ.setdefault("TOKENIZER_ID", "deepseek-ai/DeepSeek-V4-Pro")
parent = str(path.parent)
if parent not in sys.path:
sys.path.insert(0, parent)
spec = importlib.util.spec_from_file_location("agillm41_export_runtime", path)
if spec is None or spec.loader is None:
raise RuntimeError(f"cannot import AGILLM4.1 runtime from {path}")
module = importlib.util.module_from_spec(spec)
sys.modules["agillm41_export_runtime"] = module
spec.loader.exec_module(module)
return module
def real_token_batches(runtime: Any, source: str, steps: int, batch_size: int, block_size: int, seed: int) -> torch.Tensor:
if source == "__default__":
source = getattr(runtime, "DEFAULT_PRETRAIN_SOURCES")
total = int(steps) * int(batch_size) * int(block_size)
stream = runtime.token_stream(source, total, seed=int(seed), streaming=True)
data = []
for _ in range(total):
data.append(int(next(stream)))
return torch.tensor(data, dtype=torch.long).view(int(steps), int(batch_size), int(block_size))
def main() -> int:
ap = argparse.ArgumentParser(description="Export AGILLM4 all-node benchmark packages")
ap.add_argument("--ckpt", required=True)
ap.add_argument("--out-dir", required=True)
ap.add_argument("--workers", required=True, help="name:block_id comma list")
ap.add_argument("--dblock-blocks", type=int, default=4)
ap.add_argument("--steps", type=int, default=1)
ap.add_argument("--batch-size", type=int, default=1)
ap.add_argument("--block-size", type=int, default=128)
ap.add_argument("--max-layers", type=int, default=0, help="export only N layers of the block (rotating window) for low-RAM nodes")
ap.add_argument("--layer-offset", type=int, default=0, help="rotating start offset within block layer list")
ap.add_argument("--seed", type=int, default=20260602)
ap.add_argument("--runtime", default="agillm41.py", help="AGILLM4.1 runtime path used when --source is set")
ap.add_argument("--source", default="", help="Real token source. Use __default__ for the runtime default pretrain mix; empty keeps synthetic benchmark IDs.")
ap.add_argument("--attn-backend", choices=["manual", "sdpa", "sublinear"], default="manual")
ap.add_argument("--sublinear-window", type=int, default=128)
ap.add_argument("--sublinear-stride", type=int, default=128)
ap.add_argument("--sublinear-max-anchors", type=int, default=128)
ap.add_argument("--sublinear-chunk", type=int, default=128)
ap.add_argument("--sublinear-sinks", type=int, default=4)
ap.add_argument("--sublinear-recent-anchors", type=int, default=64)
ap.add_argument("--sublinear-pooled-landmarks", action="store_true")
ap.add_argument("--objective-mode", choices=["stochastic", "periodic"], default="stochastic")
ap.add_argument("--ar-prob", type=float, default=0.70)
ap.add_argument("--sat-prob", type=float, default=0.15)
ap.add_argument("--nat-prob", type=float, default=0.15)
ap.add_argument("--ar-loss-tokens", type=int, default=128)
ap.add_argument("--sat-loss-tokens", type=int, default=0)
ap.add_argument("--nat-loss-tokens", type=int, default=128)
ap.add_argument("--nat-mask-ratio", type=float, default=0.5)
ap.add_argument("--nat-max-tokens", type=int, default=128)
ap.add_argument("--amp", action=argparse.BooleanOptionalAction, default=None)
ap.add_argument("--grad-checkpoint", action=argparse.BooleanOptionalAction, default=None)
ap.add_argument("--dblock-checkpoint-stride", type=int, default=None)
ap.add_argument("--dblock-checkpoint-skip-tail", type=int, default=None)
ap.add_argument("--dblock-activation-offload", action=argparse.BooleanOptionalAction, default=None)
ap.add_argument("--dblock-activation-offload-min-mb", type=float, default=None)
args = ap.parse_args()
ckpt = Path(args.ckpt)
out_dir = Path(args.out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
workers = parse_workers(args.workers)
start = time.time()
ck = torch.load(ckpt, map_location="cpu", weights_only=False)
if "cfg" in ck:
cfg = dict(ck["cfg"])
elif "seed_meta" in ck:
cfg = dict(ck["seed_meta"].get("v4_preset") or ck["seed_meta"].get("v3_preset", {}))
if not cfg:
raise KeyError("Neither cfg nor seed_meta presets found in checkpoint")
else:
raise KeyError("Neither cfg nor seed_meta found in checkpoint")
core = ck["core"]
vocab = int(core["emb.weight"].shape[0])
assignments = dblock_layers(int(cfg["layers"]), int(args.dblock_blocks))
tie_weights = bool(ck.get("tie_weights", False))
runtime = load_runtime(args.runtime) if args.source else None
shared = {
"kind": "agillm4_bench_shared_v1",
"cfg": cfg,
"tie_weights": tie_weights,
"tokenizer_id": ck.get("tokenizer_id"),
"vocab": vocab,
# AGILLM_FP16_FROZEN: fp16 the frozen embedding (662MB->331MB): halves the
# repeated per-round volunteer download and drops it under CF Free 512MB cache.
# Frozen (not trained); worker copy_ casts back to fp32, amp uses bf16 anyway.
"emb_weight": core["emb.weight"].detach().cpu().to(torch.float16),
"ln_weight": core["ln.weight"].detach().cpu(),
"ln_bias": core["ln.bias"].detach().cpu(),
}
if not tie_weights:
shared["ar"] = {k: v.detach().cpu() for k, v in ck.get("ar", {}).items()}
shared["sat"] = {k: v.detach().cpu() for k, v in ck.get("sat", {}).items()}
shared["nat"] = {k: v.detach().cpu() for k, v in ck.get("nat", {}).items()}
else:
sat = ck.get("sat", {})
if "gate.weight" in sat and "gate.bias" in sat:
shared["sat_gate"] = {
"gate.weight": sat["gate.weight"].detach().cpu(),
"gate.bias": sat["gate.bias"].detach().cpu(),
}
shared_path = out_dir / "shared_frozen.pt"
tmp = shared_path.with_suffix(".pt.tmp")
torch.save(shared, tmp, _use_new_zipfile_serialization=False)
tmp.replace(shared_path)
manifest = {
"kind": "agillm4_dblock_bench_manifest_v1",
"created_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"source_ckpt": str(ckpt),
"source_step": int(ck.get("step", 0) or 0),
"source_seen_tok": int(ck.get("seen_tok", 0) or 0),
"cfg": cfg,
"tie_weights": tie_weights,
"tokenizer_id": ck.get("tokenizer_id"),
"vocab": vocab,
"dblock_blocks": int(args.dblock_blocks),
"steps": int(args.steps),
"batch_size": int(args.batch_size),
"block_size": int(args.block_size),
"shared": str(shared_path),
"packages": [],
}
for idx, (worker_id, block_id) in enumerate(workers):
layers = assignments[int(block_id)]
if int(args.max_layers) > 0 and len(layers) > int(args.max_layers):
_n = int(args.max_layers); _off = int(args.layer_offset) % len(layers)
_rot = layers[_off:] + layers[:_off]
layers = sorted(_rot[:_n])
batch_seed = args.seed + idx * 1009
if runtime is not None:
ids = real_token_batches(runtime, args.source, args.steps, args.batch_size, args.block_size, batch_seed)
data_mode = "real"
else:
ids = token_batches(vocab, args.steps, args.batch_size, args.block_size, batch_seed)
data_mode = "synthetic"
runtime_args = {
"attn_backend": args.attn_backend,
"sublinear_window": int(args.sublinear_window),
"sublinear_stride": int(args.sublinear_stride),
"sublinear_max_anchors": int(args.sublinear_max_anchors),
"sublinear_chunk": int(args.sublinear_chunk),
"sublinear_sinks": int(args.sublinear_sinks),
"sublinear_recent_anchors": int(args.sublinear_recent_anchors),
"sublinear_pooled_landmarks": bool(args.sublinear_pooled_landmarks),
"dblock_objective_mode": args.objective_mode,
"dblock_ar_prob": float(args.ar_prob),
"dblock_sat_prob": float(args.sat_prob),
"dblock_nat_prob": float(args.nat_prob),
"dblock_ar_loss_tokens": int(args.ar_loss_tokens),
"dblock_sat_loss_tokens": int(args.sat_loss_tokens),
"dblock_nat_loss_tokens": int(args.nat_loss_tokens),
"nat_mask_ratio": float(args.nat_mask_ratio),
"nat_max_tokens": int(args.nat_max_tokens),
}
optional_runtime_args = {
"amp": args.amp,
"grad_checkpoint": args.grad_checkpoint,
"dblock_checkpoint_stride": args.dblock_checkpoint_stride,
"dblock_checkpoint_skip_tail": args.dblock_checkpoint_skip_tail,
"dblock_activation_offload": args.dblock_activation_offload,
"dblock_activation_offload_min_mb": args.dblock_activation_offload_min_mb,
}
runtime_args.update({k: v for k, v in optional_runtime_args.items() if v is not None})
pkg = {
"kind": "agillm4_dblock_bench_package_v1",
"worker_id": worker_id,
"block_id": int(block_id),
"layers": layers,
"cfg": cfg,
"tie_weights": tie_weights,
"tokenizer_id": ck.get("tokenizer_id"),
"vocab": vocab,
"dblock_blocks": int(args.dblock_blocks),
"steps": int(args.steps),
"batch_size": int(args.batch_size),
"block_size": int(args.block_size),
"data_mode": data_mode,
"source": args.source,
"ids_batches": ids,
"block_state": local_block_state(core, layers),
"runtime_args": runtime_args,
}
out = out_dir / f"lease_{worker_id}_block{block_id}_agillm4bench.pt"
tmp = out.with_suffix(".pt.tmp")
torch.save(pkg, tmp, _use_new_zipfile_serialization=False)
tmp.replace(out)
manifest["packages"].append(
{
"worker_id": worker_id,
"block_id": int(block_id),
"layers": layers,
"path": str(out),
"bytes": out.stat().st_size,
}
)
print(json.dumps({"event": "save_package", **manifest["packages"][-1]}), flush=True)
manifest["wall_sec"] = round(time.time() - start, 3)
(out_dir / "manifest.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8")
print(json.dumps({"event": "done", "out_dir": str(out_dir), "wall_sec": manifest["wall_sec"]}, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())

Xet Storage Details

Size:
12.6 kB
·
Xet hash:
bb76297e07cbe3d8062818a2f4ac664098e723eaa51bd6e453470eb9eb8c071a

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.