MOSS-Image / aoti.py
Phospheneser's picture yhzx233's picture
init
ee22178
Raw History Blame Contribute Delete
3.18 kB
"""Compile repeated transformer blocks once on the Space's actual ZeroGPU device."""
import logging
from pathlib import Path
import time
import spaces
import torch
from spaces.zero.torch.aoti import LazyAOTIModel
logger = logging.getLogger(__name__)
BLOCK_GROUPS = ("text_embedder.blocks", "layers")
def export_block(block, args):
# The transformer evaluates each sample separately at its true sequence length.
sequence = torch.export.Dim("sequence", min=1)
shapes = ({1: sequence}, {1: sequence}) + ({},) * (len(args) - 2)
return torch.export.export(block, args=args, dynamic_shapes=shapes)
def prepare_aoti(pipelines, package_dir=".cache/aoti"):
pipelines = tuple(pipelines)
transformer = pipelines[0].transformer
package_dir = Path(package_dir)
@spaces.GPU(duration=1500)
@torch.no_grad()
def compile_blocks():
started = time.monotonic()
logger.info("AOT compilation starting on %s", torch.cuda.get_device_name())
cfg = transformer.config
dtype = next(transformer.parameters()).dtype
# Capture real input layouts/dtypes, without encoding a prompt or decoding an image.
inputs = dict(
hidden_states=torch.zeros(1, cfg.latent_channels, 64, 64, device="cuda"),
timestep=torch.tensor([500.0], device="cuda"),
encoder_hidden_states=torch.zeros(1, 32, cfg.text_dim, device="cuda", dtype=dtype),
)
for name in BLOCK_GROUPS:
block = transformer.get_submodule(name)[0]
with spaces.aoti_capture(block) as call:
transformer(**inputs)
exported = export_block(block, call.args)
spaces.aoti_compile_and_save(
package_dir, exported, submodule=name,
inductor_configs={"dynamic_scale_rblock": False},
)
# Verify that the shared graph uses each model's own weights.
compiled = LazyAOTIModel(package_dir / "submodules" / name / "package.pt2")
for pipe in pipelines:
candidate = pipe.transformer.get_submodule(name)[-1]
expected = candidate(*call.args).float()
actual = compiled.with_weights(candidate.state_dict())(*call.args).float()
relative_error = ((actual - expected).square().mean().sqrt()
/ expected.square().mean().sqrt().clamp_min(1e-6)).item()
if not torch.isfinite(actual).all() or relative_error > 0.02:
raise RuntimeError(f"AOT validation failed for {name}: relative RMS error {relative_error}")
logger.info("AOT validated %s revision=%s relative_rms_error=%.6f",
name, pipe._moss_revision, relative_error)
logger.info("AOT compiled %s in %.1fs", name, time.monotonic() - started)
return round(time.monotonic() - started, 3)
elapsed = compile_blocks()
for pipe in pipelines:
spaces.aoti_load_from_package_dir(pipe.transformer, package_dir)
pipe._execution_backend = "regional-aoti"
logger.info("AOT ready for both models; startup compilation took %.1fs", elapsed)