Download source/scripts/training/check_model_v4.py from Voltline/vimeml-tiny-ja-v4.2: direct link, hf CLI and curl.
- Browser
- Download file 7 kB
-
https://huggingface.co/Voltline/vimeml-tiny-ja-v4.2/resolve/main/source/scripts/training/check_model_v4.py
- Command line
-
hf download hf://Voltline/vimeml-tiny-ja-v4.2/source/scripts/training/check_model_v4.py
-
curl -L -o check_model_v4.py https://huggingface.co/Voltline/vimeml-tiny-ja-v4.2/resolve/main/source/scripts/training/check_model_v4.py
7 kB
| """Random-weight V4 validation; synthetic updates only, never corpus training.""" | |
| import argparse | |
| import json | |
| import subprocess | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[2] | |
| sys.path.insert(0, str(ROOT / "src")) | |
| import torch | |
| from vimeml.training.model_v4 import TinyGPTV4, PARAMETERS | |
| from vimeml.training.model_factory_v4 import FORMAT | |
| from vimeml.training.model_factory import model_from_checkpoint | |
| from vimeml.training.train import optimizer_for, atomic_checkpoint, tokenizer_identity_from_manifest | |
| from vimeml.training.data import file_sha, prepare_indexes, write_json, SentenceWindowDataset, make_loader | |
| from vimeml.training.infer_v4 import JapaneseLMV4 | |
| from vimeml.tokenizer.v4 import read_json | |
| def run(output, batch_size=128): | |
| if output.exists() and any(output.iterdir()): | |
| raise FileExistsError("Choose a fresh validation directory") | |
| output.mkdir(parents=True, exist_ok=True) | |
| tests = subprocess.run([sys.executable, "-X", "utf8", "-m", "unittest", "discover", "-s", "tests", "-p", "test_model_v4.py"], cwd=ROOT, capture_output=True, text=True, encoding="utf-8") | |
| (output / "tests.log").write_text(tests.stdout + tests.stderr, encoding="utf-8") | |
| if tests.returncode: | |
| raise RuntimeError(f"V4 tests failed; inspect {output / 'tests.log'}") | |
| torch.set_num_threads(4) | |
| torch.manual_seed(949) | |
| model = TinyGPTV4().eval() | |
| token_dir = ROOT / "artifacts/token-data/corpus-v4-8k" | |
| index_dir = ROOT / "artifacts/training-data/corpus-v4-c128" | |
| tokens = read_json(token_dir / "manifest.json") | |
| prepare_indexes(token_dir, index_dir, 128, splits=("train", "validation")) | |
| saved = {"format": FORMAT, "architecture": "tiny_gpt_v4", "model_config": model.configuration(), | |
| "model": model.state_dict(), "step": 0, "random_initialization_test_only": True, | |
| "config": {"architecture": "tiny_gpt_v4", "token_dir": "artifacts/token-data/corpus-v4-8k"}, | |
| "signatures": {"tokens": file_sha(token_dir / "manifest.json")}, | |
| "tokenizer_identity": tokenizer_identity_from_manifest(tokens)} | |
| checkpoint = output / "random-initialized.pt" | |
| atomic_checkpoint(checkpoint, saved) | |
| restored = model_from_checkpoint(torch.load(checkpoint, map_location="cpu", weights_only=True)).eval() | |
| for name, value in model.state_dict().items(): | |
| torch.testing.assert_close(restored.state_dict()[name], value, atol=0, rtol=0) | |
| lm = JapaneseLMV4(checkpoint, ROOT / "artifacts/tokenizers/v4-a/ja-unigram-8192") | |
| lm.next_pieces("今日は", 5) | |
| gpu = {"available": torch.cuda.is_available(), "corpus_training_started": False} | |
| if torch.cuda.is_available() and torch.cuda.is_bf16_supported(): | |
| gpu_model = TinyGPTV4().cuda().train() | |
| device = torch.device("cuda") | |
| settings = {"learning_rate": .0006, "weight_decay": .1, "beta1": .9, "beta2": .95} | |
| optimizer = optimizer_for(gpu_model, settings, device) | |
| # Worst context width and proposed batch size, entirely synthetic IDs. | |
| inputs = torch.randint(7, 8192, (batch_size, 128), device=device) | |
| labels = torch.randint(7, 8192, (batch_size, 128), device=device) | |
| torch.cuda.reset_peak_memory_stats() | |
| optimizer.zero_grad(set_to_none=True) | |
| with torch.autocast("cuda", dtype=torch.bfloat16): | |
| result = gpu_model(inputs, labels) | |
| loss = result["loss_sum"] / result["token_count"] | |
| loss.backward() | |
| grad_norm = torch.nn.utils.clip_grad_norm_(gpu_model.parameters(), 1.0) | |
| if not torch.isfinite(loss) or not torch.isfinite(grad_norm) or any( | |
| p.grad is None or not torch.isfinite(p.grad).all() for p in gpu_model.parameters()): | |
| raise ValueError("Nonfinite CUDA BF16 loss or gradients") | |
| optimizer.step() | |
| torch.cuda.synchronize() | |
| # Validate exact serialization/restoration of the optimizer and weights. | |
| synthetic = output / "synthetic-update.pt" | |
| atomic_checkpoint(synthetic, {"model": gpu_model.state_dict(), "optimizer": optimizer.state_dict(), "synthetic_test_only": True}) | |
| state = torch.load(synthetic, map_location=device, weights_only=True) | |
| other = TinyGPTV4().to(device) | |
| other.load_state_dict(state["model"]) | |
| second = optimizer_for(other, settings, device) | |
| second.load_state_dict(state["optimizer"]) | |
| for left, right in zip(gpu_model.parameters(), other.parameters(), strict=True): | |
| torch.testing.assert_close(left, right, atol=0, rtol=0) | |
| for key, value in optimizer.state[left].items(): | |
| actual = second.state[right][key] | |
| if torch.is_tensor(value): | |
| torch.testing.assert_close(value, actual.to(value.device), atol=0, rtol=0) | |
| gpu.update(status="passed", device=torch.cuda.get_device_name(0), precision="bf16", batch_size=batch_size, | |
| context_length=128, loss=float(loss.detach()), grad_norm=float(grad_norm), | |
| peak_allocated_bytes=torch.cuda.max_memory_allocated(), peak_reserved_bytes=torch.cuda.max_memory_reserved(), | |
| optimizer_roundtrip="exact", validation_scope="One synthetic AdamW update; not corpus training or model evaluation") | |
| del optimizer, second, other, gpu_model, state, inputs, labels, result, loss | |
| torch.cuda.empty_cache() | |
| # Read real training tensors for shape/label/finite-loss acceptance, no update. | |
| dataset = SentenceWindowDataset(token_dir, index_dir, "train") | |
| loader = make_loader(dataset, batch_size=8, num_workers=0, shuffle=False) | |
| batch = next(iter(loader)) | |
| with torch.no_grad(): | |
| actual = model(batch["input_ids"], batch["labels"]) | |
| if not torch.isfinite(actual["loss_sum"]): | |
| raise ValueError("Nonfinite real-data forward") | |
| dataset.close() | |
| report = {"status": "passed", "parameters": PARAMETERS, "model": model.configuration(), | |
| "local_model_tests": 5, "torch_version": str(torch.__version__), "gpu": gpu, | |
| "checkpoint_roundtrip": "exact", "real_data_forward": "passed; no optimizer update", | |
| "random_checkpoint_sha256": file_sha(checkpoint), "kv_shape": [7, 1, 6, 128, 64], | |
| "kv_fp32_bytes": 2_752_512, "fp32_atol": 3e-4, "fp32_rtol": 3e-4, | |
| "coreml_export": "not run; Apple runtime unavailable on Windows", | |
| "int8_block32_export": "not run; Apple runtime unavailable on Windows", | |
| "ios_latency_and_memory": "not measured", "formal_training_started": False} | |
| write_json(output / "report.json", report) | |
| print(json.dumps(report, indent=2)) | |
| return report | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument("--output", type=Path, default=ROOT / "outputs/model-checks/v4-a-20261014") | |
| parser.add_argument("--batch-size", type=int, default=128) | |
| args = parser.parse_args() | |
| if args.batch_size < 1: | |
| parser.error("batch-size must be positive") | |
| run(args.output, args.batch_size) | |