Download scripts/release.py from Lucasgitguy-co/forma3d-tiny: direct link, hf CLI and curl.
- Browser
- Download file 11.8 kB
-
https://huggingface.co/Lucasgitguy-co/forma3d-tiny/resolve/main/scripts/release.py
- Command line
-
hf download hf://Lucasgitguy-co/forma3d-tiny/scripts/release.py
-
curl -L -o release.py https://huggingface.co/Lucasgitguy-co/forma3d-tiny/resolve/main/scripts/release.py
11.8 kB
| """Evaluate a completed run, stage a self-contained release, optionally publish it.""" | |
| import argparse | |
| import hashlib | |
| import json | |
| import shutil | |
| from pathlib import Path | |
| import torch | |
| from torch.utils.data import DataLoader | |
| from image3d_student.data import CachedDataset, load_manifest | |
| from image3d_student.engine import device_for, evaluate, generate | |
| from image3d_student.model import Student | |
| def sha(path): | |
| return hashlib.sha256(Path(path).read_bytes()).hexdigest() | |
| def evaluate_release(args): | |
| run, dataset = Path(args.run), Path(args.data) | |
| meta = json.loads((run / "run.json").read_text()) | |
| checkpoint = torch.load(run / "best.pt", map_location="cpu", weights_only=True) | |
| last = torch.load(run / "last.pt", map_location="cpu", weights_only=True) | |
| if last["step"] != meta["steps_requested"]: | |
| raise ValueError("Training did not complete the requested steps") | |
| if checkpoint.get("step", 0) < 1 or not meta.get("initialization"): | |
| raise ValueError("Expected completed post-training from a base checkpoint") | |
| if sha(args.base) != meta["initialization"]["sha256"]: | |
| raise ValueError("Base checkpoint differs from the training initialization") | |
| if sha(dataset / "manifest.jsonl") != meta["manifest_sha256"]: | |
| raise ValueError("Training manifest changed") | |
| development = load_manifest(dataset / "manifest.jsonl") | |
| test = load_manifest(dataset / "test-manifest.jsonl") | |
| selected_ids = set(meta["train_ids"]) | set(meta["val_ids"]) | |
| if selected_ids != {r["id"] for r in development}: | |
| raise ValueError("Run split does not match development manifest") | |
| if {r["metadata"]["object_id"] for r in development} & {r["metadata"]["object_id"] for r in test}: | |
| raise ValueError("Test object leakage") | |
| for key in ["image_sha256", "target_sha256"]: | |
| if {r[key] for r in development} & {r[key] for r in test}: | |
| raise ValueError("Duplicate data across development and test") | |
| device = device_for(args.device) | |
| model = Student(**checkpoint["config"]).to(device) | |
| model.load_state_dict(checkpoint["model"]) | |
| loader = DataLoader(CachedDataset(dataset / "test-manifest.jsonl"), batch_size=1) | |
| final = evaluate(model, loader, device) | |
| base = torch.load(args.base, map_location="cpu", weights_only=True) | |
| initial = Student(**checkpoint["config"]).to(device) | |
| result = initial.load_state_dict({k: v for k, v in base["model"].items() if k not in ("coords", "centers")}, strict=False) | |
| if set(result.missing_keys) != {"coords", "centers"} or result.unexpected_keys: | |
| raise ValueError("Base architecture mismatch") | |
| baseline = evaluate(initial, loader, device) | |
| best_validation = min(json.loads(line)["validation"]["loss"] for line in | |
| (run / "metrics.jsonl").read_text().splitlines()) | |
| checks = {"validation_loss_improved": best_validation < meta["baseline_validation"]["loss"], | |
| "test_loss_improved": final["loss"] < baseline["loss"], | |
| "test_occupancy_iou_improved": final["occupancy_iou"] > baseline["occupancy_iou"], | |
| "test_occupancy_iou_at_least_005": final["occupancy_iou"] >= .05} | |
| report = {"checkpoint_sha256": sha(run / "best.pt"), "base_sha256": sha(args.base), | |
| "test_manifest_sha256": sha(dataset / "test-manifest.jsonl"), | |
| "test_objects": len({r["metadata"]["object_id"] for r in test}), | |
| "test_samples": len(test), "baseline_test": baseline, "posttrained_test": final, | |
| "checks": checks, "passed": all(checks.values()), | |
| "scope": "Rendered mesh-proxy evaluation only; does not establish real-photo or mesh reconstruction quality"} | |
| (run / "evaluation.json").write_text(json.dumps(report, indent=2)) | |
| print(json.dumps(report, indent=2), flush=True) | |
| # Keep qualitative inspection files local; all held-out objects, first view. | |
| example_dir = run / "inspection" | |
| example_dir.mkdir(exist_ok=True) | |
| seen = set() | |
| from image3d_student.splats import preview, read_ply | |
| from PIL import Image, ImageDraw | |
| for row in test: | |
| group = row["metadata"]["object_id"] | |
| if group in seen: | |
| continue | |
| seen.add(group) | |
| out = example_dir / (row["id"] + ".ply") | |
| if not out.exists(): | |
| try: | |
| generate(run / "best.pt", dataset / row["image"], out, args.device) | |
| except ValueError as exc: | |
| if "No occupied cells" not in str(exc): | |
| raise | |
| report["checks"]["nonempty_examples"] = False | |
| report["passed"] = False | |
| continue | |
| sheet = Image.new("RGB", (512, 152), "black") | |
| sheet.paste(Image.open(dataset / row["image"]).convert("RGB").resize((128, 128)), (0, 24)) | |
| splats = read_ply(out) | |
| for i, angle in enumerate([0, 1.5707963, 3.1415927]): | |
| sheet.paste(preview(splats, size=128, yaw=angle), (128 * (i + 1), 24)) | |
| ImageDraw.Draw(sheet).text((5, 5), "Input proxy | prediction front | side | back", fill="white") | |
| sheet.save(out.with_suffix(".png")) | |
| if len(seen) >= 8: | |
| break | |
| (run / "evaluation.json").write_text(json.dumps(report, indent=2)) | |
| if not report["passed"]: | |
| raise ValueError("Evaluation gate failed. Nothing was published. Inspect evaluation.json and improve data/training.") | |
| return checkpoint, meta, report | |
| def stage(args, checkpoint, meta, report): | |
| source = Path(__file__).resolve().parents[1] | |
| release = Path(args.release) | |
| if release.exists(): | |
| raise ValueError("Release directory already exists; choose a new --release path") | |
| release.mkdir(parents=True) | |
| shutil.copytree(source / "src", release / "src", ignore=shutil.ignore_patterns("__pycache__", "*.egg-info")) | |
| shutil.copytree(source / "scripts", release / "scripts", ignore=shutil.ignore_patterns("__pycache__")) | |
| shutil.copytree(source / "licenses", release / "licenses") | |
| for filename in ["pyproject.toml", "LICENSE", "NOTICE", "THIRD_PARTY.md", "DATASETS.md", "COMMANDS.md", "setup-mac.sh", "train-mac.sh", "requirements-tested-macos.txt"]: | |
| shutil.copyfile(source / filename, release / filename) | |
| # Publish inference weights only; optimizer and local absolute paths stay local. | |
| public_meta = {k: v for k, v in meta.items() if k != "dataset"} | |
| torch.save({"format": 1, "config": checkpoint["config"], "model": checkpoint["model"], | |
| "step": checkpoint["step"], "metadata": public_meta}, release / "model.pt") | |
| (release / "config.json").write_text(json.dumps(checkpoint["config"], indent=2)) | |
| (release / "training.json").write_text(json.dumps(public_meta, indent=2)) | |
| (release / "evaluation.json").write_text(json.dumps({**report, | |
| "published_model_sha256": sha(release / "model.pt")}, indent=2)) | |
| shutil.copyfile(Path(args.data) / "sources.json", release / "ATTRIBUTIONS.json") | |
| used = sorted({r["repo"] for r in json.loads((release / "ATTRIBUTIONS.json").read_text())}) | |
| datasets_yaml = "\n".join("- " + repo for repo in used) | |
| text = f'''--- | |
| license: apache-2.0 | |
| pipeline_tag: image-to-3d | |
| base_model: Lucasgitguy-co/image3d-student | |
| model_name: Forma3D-Tiny v0.2 | |
| datasets: | |
| {datasets_yaml} | |
| tags: | |
| - pytorch | |
| - gaussian-splatting | |
| - research-prototype | |
| - mesh-proxy-supervision | |
| --- | |
| # Forma3D-Tiny v0.2 | |
| An experimental compact image-to-Gaussian model post-trained from Image3D Student's | |
| synthetic demonstration checkpoint. This is a coarse research baseline, not a | |
| general-purpose photo-to-3D model. Its grid is {checkpoint["config"]["grid"]} cubed and | |
| it has {meta["parameters"]:,} parameters. Selected checkpoint: step {checkpoint["step"]}; | |
| completed run: {meta["steps_requested"]} steps. | |
| ## Actual training data and preprocessing | |
| Only the datasets listed above were used, with per-asset provenance in ATTRIBUTIONS.json. | |
| Scenario assets are CC-BY-4.0; the Objaverse subset is CC0. Original objects were centered | |
| and uniformly scaled into a 0.9-wide cube, surface sampled, and converted to isotropic | |
| Gaussian proxy targets. Orthographic images were rendered from these same proxy clouds. | |
| This is not teacher distillation from Idol, Hunyuan, InstantMesh, or Hi3D, and it did not | |
| train on natural photographs, MultiScan, 3D-Front, Show3D, or Voxel51 scene splats. | |
| Related source inputs and all views stay in the same object group. A separate 15% test | |
| partition is excluded from training and validation-based checkpoint selection. | |
| ## Measured results | |
| Independent test objects: {report["test_objects"]}; test views: {report["test_samples"]}. | |
| Base test loss: {report["baseline_test"]["loss"]:.6f}; post-trained test loss: | |
| {report["posttrained_test"]["loss"]:.6f}. Base occupancy IoU: | |
| {report["baseline_test"]["occupancy_iou"]:.6f}; post-trained occupancy IoU: | |
| {report["posttrained_test"]["occupancy_iou"]:.6f}. | |
| See evaluation.json for the full results and checks. These scores measure coarse proxy | |
| reconstruction on the rendered data distribution; they do not prove photo realism, | |
| watertight meshes, production quality, or texture fidelity. Near duplicates can remain | |
| despite source grouping and exact-byte deduplication. | |
| ## Run on a Mac | |
| Download this repository, then run `bash setup-mac.sh` and | |
| `source "$HOME/Library/Application Support/Forma3D/venv/bin/activate"`. | |
| ```sh | |
| image3d generate --checkpoint model.pt --image your-centered-cutout.png \\ | |
| --out results/object.ply --device mps | |
| image3d preview --ply results/object.ply --out results/preview.png | |
| ``` | |
| Inputs are centered objects on black, matching the training framing. Natural photos | |
| are outside the verified distribution. Output is Gaussian PLY, not a triangle mesh. | |
| CPU works with `--device cpu`; CUDA support is implemented but was not tested on the | |
| Mac used to prepare these scripts. | |
| ## License and attribution | |
| Source code and these weights are offered under Apache-2.0. Dataset and individual | |
| asset licenses remain separate; retain ATTRIBUTIONS.json and consult DATASETS.md. | |
| This license statement does not grant rights to third-party trademarks or likenesses. | |
| See COMMANDS.md for reproduction commands. | |
| ''' | |
| (release / "README.md").write_text(text) | |
| sums = [sha(p) + " " + str(p.relative_to(release)) for p in sorted(release.rglob("*")) if p.is_file()] | |
| (release / "SHA256SUMS").write_text("\n".join(sums) + "\n") | |
| return release | |
| def main(): | |
| p = argparse.ArgumentParser(description=__doc__) | |
| p.add_argument("--run", required=True) | |
| p.add_argument("--data", required=True) | |
| p.add_argument("--base", required=True) | |
| p.add_argument("--release", default="release/forma3d-tiny") | |
| p.add_argument("--repo", default="Lucasgitguy-co/forma3d-tiny") | |
| p.add_argument("--device", choices=["mps", "cpu", "cuda", "auto"], default="mps") | |
| p.add_argument("--publish", action="store_true") | |
| args = p.parse_args() | |
| checkpoint, meta, report = evaluate_release(args) | |
| release = stage(args, checkpoint, meta, report) | |
| if args.publish: | |
| from huggingface_hub import HfApi | |
| api = HfApi() | |
| # Refuse accidental replacement of a previously published model. | |
| if api.repo_exists(args.repo, repo_type="model"): | |
| raise ValueError("Repository already exists. Choose a new --repo; no overwrite was performed.") | |
| api.create_repo(args.repo, repo_type="model", private=True) | |
| commit = api.upload_folder(repo_id=args.repo, folder_path=release, | |
| commit_message="Publish evaluated Forma3D-Tiny v0.2") | |
| api.update_repo_settings(args.repo, private=False) | |
| print(f"Published https://huggingface.co/{args.repo} at {commit.oid}") | |
| else: | |
| print(f"Evaluated release staged at {release}; nothing published.") | |
| if __name__ == "__main__": | |
| main() | |