Add safetensors -> FastVideo loader converter
Browse files- convert_to_fastvideo_pt.py +51 -0
convert_to_fastvideo_pt.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
"""Convert a FastH3 NVFP4 decoder ``decoder.safetensors`` into the ``.pt`` file FastVideo loads.
|
| 3 |
+
|
| 4 |
+
FastVideo's ``FASTVIDEO_H3_VAE_NVFP4_DECODER`` loader reads a
|
| 5 |
+
``fastvideo_h3_decoder_nvfp4_deploy_v1`` torch checkpoint. This script rebuilds
|
| 6 |
+
that checkpoint from the safetensors release without changing any tensor.
|
| 7 |
+
|
| 8 |
+
python convert_to_fastvideo_pt.py quality-36L/decoder.safetensors quality-36L.pt
|
| 9 |
+
"""
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
|
| 12 |
+
import argparse
|
| 13 |
+
import json
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
from safetensors import safe_open
|
| 17 |
+
from safetensors.torch import load_file
|
| 18 |
+
|
| 19 |
+
DEPLOY_FORMAT = "fastvideo_h3_decoder_nvfp4_deploy_v1"
|
| 20 |
+
GROUPS = ("decoder", "post_quant_conv")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def safetensors_to_checkpoint(path: str) -> dict:
|
| 24 |
+
with safe_open(path, framework="pt") as handle:
|
| 25 |
+
header = handle.metadata() or {}
|
| 26 |
+
if header.get("format") != DEPLOY_FORMAT:
|
| 27 |
+
raise ValueError(f"{path} is not a {DEPLOY_FORMAT} file (format: {header.get('format')!r})")
|
| 28 |
+
tensors = load_file(path)
|
| 29 |
+
checkpoint: dict = {"format": DEPLOY_FORMAT, "metadata": json.loads(header["nvfp4_metadata"])}
|
| 30 |
+
for group in GROUPS:
|
| 31 |
+
prefix = f"{group}."
|
| 32 |
+
checkpoint[group] = {key[len(prefix):]: value for key, value in tensors.items() if key.startswith(prefix)}
|
| 33 |
+
unknown = [key for key in tensors if not key.startswith(tuple(f"{g}." for g in GROUPS))]
|
| 34 |
+
if unknown:
|
| 35 |
+
raise ValueError(f"unexpected tensors in {path}: {unknown[:5]}")
|
| 36 |
+
return checkpoint
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def main() -> None:
|
| 40 |
+
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 41 |
+
parser.add_argument("safetensors", help="quality-36L/decoder.safetensors or fast-8L/decoder.safetensors")
|
| 42 |
+
parser.add_argument("output", help="destination .pt for FASTVIDEO_H3_VAE_NVFP4_DECODER")
|
| 43 |
+
args = parser.parse_args()
|
| 44 |
+
checkpoint = safetensors_to_checkpoint(args.safetensors)
|
| 45 |
+
torch.save(checkpoint, args.output)
|
| 46 |
+
print(f"wrote {args.output}: {len(checkpoint['decoder'])} decoder tensors, "
|
| 47 |
+
f"{checkpoint['metadata'].get('student_layers')} blocks")
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
main()
|