linoyts's picture
linoyts HF Staff
Upload folder using huggingface_hub
6bbf35c verified
Raw History Blame Contribute Delete
7.09 kB
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "aokit",
# "torch==2.11.0",
# "torchvision==0.26.0",
# "diffusers @ https://github.com/muhammadali-afridi/diffusers/archive/60f95274bf07ed705d675eb09d99b6ae125b601e.tar.gz",
# "transformers==5.20.1",
# "accelerate",
# "safetensors",
# "sentencepiece",
# "huggingface_hub>=1.0",
# "Pillow",
# ]
#
# [[tool.uv.index]]
# url = "https://download.pytorch.org/whl/cu130"
# ///
#
# Ahead-of-time compile the repeated transformer blocks of BriaFibo2Transformer2DModel
# with aokit (regional compilation), verify correctness, and push a PRIVATE package repo.
#
# Launch:
# hf jobs uv run job_fibo2_aoti.py \
# --flavor rtx-pro-6000 \
# --image pytorch/pytorch:2.9.1-cuda13.0-cudnn9-devel \
# --namespace diffusers-internal-dev \
# --secrets HF_TOKEN \
# -e FIBO2_MODEL=diffusers-internal-dev/Fibo-2.0-dev \
# -e OUTPUT_REPO_ID=diffusers-internal-dev/Fibo-2.0-dev-aoti-sm120 \
# -e STEPS=8
import inspect
import json
import os
import time
import traceback
from pathlib import Path
import torch
from huggingface_hub import HfApi
from PIL import Image
import aokit
from aokit.exporting import capture
MODEL = os.environ.get("FIBO2_MODEL", "diffusers-internal-dev/Fibo-2.0-dev")
OUT_REPO = os.environ["OUTPUT_REPO_ID"]
TOKEN = os.environ.get("HF_TOKEN")
STEPS = int(os.environ.get("STEPS", "8"))
GUIDANCE = float(os.environ.get("GUIDANCE", "5.0"))
PROMPT = os.environ.get("PROMPT", "a red bicycle leaning against a white brick wall on a sunny morning.")
H = W = 1024
DTYPE = torch.bfloat16
print(f"torch {torch.__version__} | cuda {torch.version.cuda} | device {torch.cuda.get_device_name()}", flush=True)
from diffusers import BriaFibo2Pipeline
pipe = BriaFibo2Pipeline.from_pretrained(MODEL, dtype=DTYPE, token=TOKEN).to("cuda")
pipe.set_progress_bar_config(disable=True)
t = pipe.transformer
INJ = list(getattr(t.config, "injection_layer_ids", (6, 14, 22, 30, 38)))
n_layers = len(t.layers)
plain_idx = next(i for i in range(n_layers) if i not in INJ)
inj_idx = INJ[0]
print(f"n_layers={n_layers} injection={INJ} plain_rep={plain_idx} inj_rep={inj_idx}", flush=True)
def gen(path, label):
img = pipe(PROMPT, num_inference_steps=STEPS, guidance_scale=GUIDANCE,
height=H, width=W, generator=torch.manual_seed(0)).images[0]
img.save(path)
print(f"sample[{label}] -> {path}", flush=True)
return img
def export_block(block, name):
"""Capture inputs for `block` from a real pipeline step, then export with the
sequence axes marked dynamic (batch kept static at the captured size)."""
with capture(block) as call:
pipe(PROMPT, num_inference_steps=1, guidance_scale=GUIDANCE,
height=H, width=W, generator=torch.manual_seed(0))
# Preserve the EXACT calling convention the transformer uses (positional args stay
# positional) so the saved graph's in_spec matches the runtime block(...) call.
args = tuple(call.args)
kwargs = dict(call.kwargs)
params = list(inspect.signature(block.forward).parameters)
bound = {}
for pname, val in zip(params, args): # name the positional args for spec + dynamic_shapes
bound[pname] = val
bound.update(kwargs)
x = bound["x"]
B, S = x.shape[0], x.shape[1]
enc = bound.get("encoder_hidden_states")
E = enc.shape[1] if torch.is_tensor(enc) else None
print(f" [{name}] captured B={B} S={S} E={E} "
f"args={[type(a).__name__ for a in args]} kw={[k for k in kwargs]}", flush=True)
seq = torch.export.Dim("seq", min=16, max=16384)
enc_dim = torch.export.Dim("enc", min=16, max=16384) if E and E != S else None
size_to_dim = {S: seq}
if enc_dim is not None:
size_to_dim[E] = enc_dim
def spec(v):
if not torch.is_tensor(v):
return None
d = {i: size_to_dim[sz] for i, sz in enumerate(v.shape) if i != 0 and sz in size_to_dim}
return d or None
# dynamic_shapes keyed by argument name (torch.export maps names to args+kwargs).
dynamic_shapes = {pname: spec(val) for pname, val in bound.items()}
with torch.no_grad():
ep = torch.export.export(block, args, kwargs, dynamic_shapes=dynamic_shapes)
return ep
STACKS = {
"plain": t.layers[plain_idx],
"injection": t.layers[inj_idx],
"refiner": t.noise_refiner[0],
}
api = HfApi(token=TOKEN)
work = Path("/tmp/fibo2_aoti")
pkg = work / "package"
samples = work / "samples"
pkg.mkdir(parents=True, exist_ok=True)
samples.mkdir(parents=True, exist_ok=True)
gen(samples / "eager.webp", "eager")
compiled = {}
errors = {}
for name, block in STACKS.items():
print(f"=== export+compile stack '{name}' ===", flush=True)
try:
t0 = time.perf_counter()
ep = export_block(block, name)
aokit.compile_and_save(package_dir=str(pkg), exported_program=ep, submodule=name)
compiled[name] = True
print(f" OK '{name}' in {time.perf_counter()-t0:.0f}s", flush=True)
except Exception as e:
errors[name] = repr(e)
print(f" FAILED '{name}': {e!r}", flush=True)
traceback.print_exc()
if not compiled:
raise RuntimeError(f"No stack compiled. Errors: {errors}")
# Load compiled stacks back with a per-index custom mapping and verify.
from aokit import LazyAOTIModel, patch
models = {name: LazyAOTIModel(pkg / "submodules" / name / "package.pt2") for name in compiled}
if "plain" in models or "injection" in models:
for i, blk in enumerate(t.layers):
m = models.get("injection") if i in INJ else models.get("plain")
if m is not None:
patch(blk, m)
if "refiner" in models:
for blk in t.noise_refiner:
patch(blk, models["refiner"])
gen(samples / "aoti.webp", "aoti")
cap = "".join(map(str, torch.cuda.get_device_capability()))
cu = "".join((torch.version.cuda or "0.0").split(".")[:2])
config = {
"base": MODEL,
"arch": f"sm{cap}",
"torch": torch.__version__,
"cuda": torch.version.cuda,
"device": torch.cuda.get_device_name(),
"resolution": H,
"steps": STEPS,
"guidance": GUIDANCE,
"injection_layer_ids": INJ,
"n_layers": n_layers,
"n_refiner_layers": len(t.noise_refiner),
"stacks": {name: "dynamic" for name in compiled},
"stack_targets": {
"plain": "layers[i] for i not in injection_layer_ids",
"injection": "layers[i] for i in injection_layer_ids",
"refiner": "noise_refiner[*]",
},
"errors": errors,
}
(work / "config.json").write_text(json.dumps(config, indent=2))
print("CONFIG:\n" + json.dumps(config, indent=2), flush=True)
# self-include this script
import shutil
main_file = getattr(__import__("__main__"), "__file__", None)
if main_file:
shutil.copyfile(main_file, work / "job.py")
api.create_repo(OUT_REPO, repo_type="model", private=True, exist_ok=True)
api.upload_folder(repo_id=OUT_REPO, folder_path=str(work), repo_type="model")
print(f"DONE -> https://huggingface.co/{OUT_REPO} (private) compiled={list(compiled)} errors={list(errors)}", flush=True)