CoolFace
Apppublic

ALSv/self-forcing

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
vae_torch2trt.py309 linesDownload Raw Back to demo_utils
1# ---- INT8 (optional) ----2from demo_utils.vae import (3    VAEDecoderWrapperSingle,                         # main nn.Module4    ZERO_VAE_CACHE           # helper constants shipped with your code base5)6import pycuda.driver as cuda          # ← add7import pycuda.autoinit  # noqa8 9import sys10from pathlib import Path11 12import torch13import tensorrt as trt14 15from utils.dataset import ShardingLMDBDataset16 17data_path = "/mnt/localssd/wanx_14B_shift-3.0_cfg-5.0_lmdb_oneshard"18dataset = ShardingLMDBDataset(data_path, max_pair=int(1e8))19dataloader = torch.utils.data.DataLoader(20    dataset,21    batch_size=1,22    num_workers=023)24 25# ─────────────────────────────────────────────────────────26# 1️⃣  Bring the PyTorch model into scope27#     (all code you pasted lives in `vae_decoder.py`)28# ─────────────────────────────────────────────────────────29 30# --- dummy tensors (exact shapes you posted) ---31dummy_input = torch.randn(1, 1, 16, 60, 104).half().cuda()32is_first_frame = torch.tensor([1.0], device="cuda", dtype=torch.float16)33dummy_cache_input = [34    torch.randn(*s.shape).half().cuda() if isinstance(s, torch.Tensor) else s35    for s in ZERO_VAE_CACHE               # keep exactly the same ordering36]37inputs = [dummy_input, is_first_frame, *dummy_cache_input]38 39# ─────────────────────────────────────────────────────────40# 2️⃣  Export → ONNX41# ─────────────────────────────────────────────────────────42model = VAEDecoderWrapperSingle().half().cuda().eval()43 44vae_state_dict = torch.load('wan_models/Wan2.1-T2V-1.3B/Wan2.1_VAE.pth', map_location="cpu")45decoder_state_dict = {}46for key, value in vae_state_dict.items():47    if 'decoder.' in key or 'conv2' in key:48        decoder_state_dict[key] = value49model.load_state_dict(decoder_state_dict)50model = model.half().cuda().eval()                          # only batch dim dynamic51 52onnx_path = Path("vae_decoder.onnx")53feat_names = [f"vae_cache_{i}" for i in range(len(dummy_cache_input))]54all_inputs_names = ["z", "use_cache"] + feat_names55 56with torch.inference_mode():57    torch.onnx.export(58        model,59        tuple(inputs),                                        # must be a tuple60        onnx_path.as_posix(),61        input_names=all_inputs_names,62        output_names=["rgb_out", "cache_out"],63        opset_version=17,64        do_constant_folding=True,65        dynamo=True66    )67print(f"✅  ONNX graph saved to {onnx_path.resolve()}")68 69# (Optional) quick sanity-check with ONNX-Runtime70try:71    import onnxruntime as ort72    sess = ort.InferenceSession(onnx_path.as_posix(),73                                providers=["CUDAExecutionProvider"])74    ort_inputs = {n: t.cpu().numpy() for n, t in zip(all_inputs_names, inputs)}75    _ = sess.run(None, ort_inputs)76    print("✅  ONNX graph is executable")77except Exception as e:78    print("⚠️  ONNX check failed:", e)79 80# ─────────────────────────────────────────────────────────81# 3️⃣  Build the TensorRT engine82# ─────────────────────────────────────────────────────────83TRT_LOGGER = trt.Logger(trt.Logger.WARNING)84builder = trt.Builder(TRT_LOGGER)85network = builder.create_network(86    1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))87parser = trt.OnnxParser(network, TRT_LOGGER)88 89with open(onnx_path, "rb") as f:90    if not parser.parse(f.read()):91        for i in range(parser.num_errors):92            print(parser.get_error(i))93        sys.exit("❌  ONNX → TRT parsing failed")94 95config = builder.create_builder_config()96 97 98def set_workspace(config, bytes_):99    """Version-agnostic workspace limit."""100    if hasattr(config, "max_workspace_size"):                # TRT 8 / 9101        config.max_workspace_size = bytes_102    else:                                                    # TRT 10+103        config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, bytes_)104 105 106# …107config = builder.create_builder_config()108set_workspace(config, 4 << 30)          # 4 GB109# 4 GB110 111if builder.platform_has_fast_fp16:112    config.set_flag(trt.BuilderFlag.FP16)113 114# ---- INT8 (optional) ----115# provide a calibrator if you need an INT8 engine; comment this116# block if you only care about FP16.117# ─────────────────────────────────────────────────────────118# helper: version-agnostic workspace limit119# ─────────────────────────────────────────────────────────120 121 122def set_workspace(config: trt.IBuilderConfig, bytes_: int = 4 << 30):123    """124    TRT < 10.x  →  config.max_workspace_size125    TRT ≥ 10.x  →  config.set_memory_pool_limit(...)126    """127    if hasattr(config, "max_workspace_size"):                     # TRT 8 / 9128        config.max_workspace_size = bytes_129    else:                                                         # TRT 10+130        config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE,131                                     bytes_)132 133# ─────────────────────────────────────────────────────────134# (optional) INT-8 calibrator135# ─────────────────────────────────────────────────────────136# ‼ Only keep this block if you really need INT-8 ‼                      # gracefully skip if PyCUDA not present137 138 139class VAECalibrator(trt.IInt8EntropyCalibrator2):140    def __init__(self, loader, cache="calibration.cache", max_batches=10):141        super().__init__()142        self.loader = iter(loader)143        self.batch_size = loader.batch_size or 1144        self.max_batches = max_batches145        self.count = 0146        self.cache_file = cache147        self.stream = cuda.Stream()148        self.dev_ptrs = {}149 150    # --- TRT 10 needs BOTH spellings ---151    def get_batch_size(self):152        return self.batch_size153 154    def getBatchSize(self):155        return self.batch_size156 157    def get_batch(self, names):158        if self.count >= self.max_batches:159            return None160 161        # Randomly sample a number from 1 to 10162        import random163        vae_idx = random.randint(0, 10)164        data = next(self.loader)165 166        latent = data['ode_latent'][0][:, :1]167        is_first_frame = torch.tensor([1.0], device="cuda", dtype=torch.float16)168        feat_cache = ZERO_VAE_CACHE169        for i in range(vae_idx):170            inputs = [latent, is_first_frame, *feat_cache]171            with torch.inference_mode():172                outputs = model(*inputs)173            latent = data['ode_latent'][0][:, i + 1:i + 2]174            is_first_frame = torch.tensor([0.0], device="cuda", dtype=torch.float16)175            feat_cache = outputs[1:]176 177        # -------- ensure context is current --------178        z_np = latent.cpu().numpy().astype('float32')179 180        ptrs = []                # list[int] – one entry per name181        for name in names:         # <-- match TRT's binding order182            if name == "z":183                arr = z_np184            elif name == "use_cache":185                arr = is_first_frame.cpu().numpy().astype('float32')186            else:187                idx = int(name.split('_')[-1])   # "vae_cache_17" -> 17188                arr = feat_cache[idx].cpu().numpy().astype('float32')189 190            if name not in self.dev_ptrs:191                self.dev_ptrs[name] = cuda.mem_alloc(arr.nbytes)192 193            cuda.memcpy_htod_async(self.dev_ptrs[name], arr, self.stream)194            ptrs.append(int(self.dev_ptrs[name]))   # ***int() is required***195 196        self.stream.synchronize()197        self.count += 1198        print(f"Calibration batch {self.count}/{self.max_batches}")199        return ptrs200 201    # --- calibration-cache helpers (both spellings) ---202    def read_calibration_cache(self):203        try:204            with open(self.cache_file, "rb") as f:205                return f.read()206        except FileNotFoundError:207            return None208 209    def readCalibrationCache(self):210        return self.read_calibration_cache()211 212    def write_calibration_cache(self, cache):213        with open(self.cache_file, "wb") as f:214            f.write(cache)215 216    def writeCalibrationCache(self, cache):217        self.write_calibration_cache(cache)218 219 220# ─────────────────────────────────────────────────────────221# Builder-config + optimisation profile222# ─────────────────────────────────────────────────────────223config = builder.create_builder_config()224set_workspace(config, 4 << 30)                    # 4 GB225 226# ► enable FP16 if possible227if builder.platform_has_fast_fp16:228    config.set_flag(trt.BuilderFlag.FP16)229 230# ► enable INT-8  (delete this block if you don’t need it)231if cuda is not None:232    config.set_flag(trt.BuilderFlag.INT8)233    # supply any representative batch you like – here we reuse the latent z234    calib = VAECalibrator(dataloader)235    # TRT-10 renamed the setter:236    if hasattr(config, "set_int8_calibrator"):    # TRT 10+237        config.set_int8_calibrator(calib)238    else:                                         # TRT ≤ 9239        config.int8_calibrator = calib240 241# ---- optimisation profile ----242profile = builder.create_optimization_profile()243profile.set_shape(all_inputs_names[0],            # latent z244                  min=(1, 1, 16, 60, 104),245                  opt=(1, 1, 16, 60, 104),246                  max=(1, 1, 16, 60, 104))247profile.set_shape("use_cache",               # scalar flag248                  min=(1,), opt=(1,), max=(1,))249for name, tensor in zip(all_inputs_names[2:], dummy_cache_input):250    profile.set_shape(name, tensor.shape, tensor.shape, tensor.shape)251 252config.add_optimization_profile(profile)253 254# ─────────────────────────────────────────────────────────255# Build the engine  (API changed in TRT-10)256# ─────────────────────────────────────────────────────────257print("⚙️  Building engine … (can take a minute)")258 259if hasattr(builder, "build_serialized_network"):          # TRT 10+260    serialized_engine = builder.build_serialized_network(network, config)261    assert serialized_engine is not None, "build_serialized_network() failed"262    plan_path = Path("checkpoints/vae_decoder_int8.trt")263    plan_path.write_bytes(serialized_engine)264    engine_bytes = serialized_engine                      # keep for smoke-test265else:                                                     # TRT ≤ 9266    engine = builder.build_engine(network, config)267    assert engine is not None, "build_engine() returned None"268    plan_path = Path("checkpoints/vae_decoder_int8.trt")269    plan_path.write_bytes(engine.serialize())270    engine_bytes = engine.serialize()271 272print(f"✅  TensorRT engine written to {plan_path.resolve()}")273 274# ─────────────────────────────────────────────────────────275# 4️⃣  Quick smoke-test with the brand-new engine276# ─────────────────────────────────────────────────────────277with trt.Runtime(TRT_LOGGER) as rt:278    engine = rt.deserialize_cuda_engine(engine_bytes)279    context = engine.create_execution_context()280    stream = torch.cuda.current_stream().cuda_stream281 282    # pre-allocate device buffers once283    device_buffers, outputs = {}, []284    dtype_map = {trt.float32: torch.float32,285                 trt.float16: torch.float16,286                 trt.int8:    torch.int8,287                 trt.int32:   torch.int32}288 289    for name, tensor in zip(all_inputs_names, inputs):290        if -1 in engine.get_tensor_shape(name):            # dynamic input291            context.set_input_shape(name, tensor.shape)292        context.set_tensor_address(name, int(tensor.data_ptr()))293        device_buffers[name] = tensor294 295    context.infer_shapes()                                 # propagate ⇢ outputs296    for i in range(engine.num_io_tensors):297        name = engine.get_tensor_name(i)298        if engine.get_tensor_mode(name) == trt.TensorIOMode.OUTPUT:299            shape = tuple(context.get_tensor_shape(name))300            dtype = dtype_map[engine.get_tensor_dtype(name)]301            out = torch.empty(shape, dtype=dtype, device="cuda")302            context.set_tensor_address(name, int(out.data_ptr()))303            outputs.append(out)304            print(f"output {name} shape: {shape}")305 306    context.execute_async_v3(stream_handle=stream)307    torch.cuda.current_stream().synchronize()308    print("✅  TRT execution OK – first output shape:", outputs[0].shape)309