Calibration Cache with Auto Caching (Pro)

This tutorial shows how to use calibration cache mode with auto caching on FLUX.1-dev. Unlike Taylor or reuse modes, calibration fits linear coefficients from real denoiser outputs on a small prompt set.

Attach a caption datamodule to SmashConfig before smashing a fresh model. Coefficients are stored in memory on pipe.cache_helper.calibrated_coeffs and persisted as caching_coeffs.pkl when you save. On load, inference reuses those coefficients only — recalibration is not supported (prompt data is not saved with the model).

1. Loading the Flux Model

First, load FLUX.1-dev.

[ ]:
import torch
from diffusers import FluxPipeline

pipe = FluxPipeline.from_pretrained("black-forest-labs/FLUX.1-dev", torch_dtype=torch.bfloat16)
pipe.to("cuda")
# pipe.enable_model_cpu_offload()  # save VRAM; remove if you have enough GPU memory

2. Calibration Prompts and Smash Config

DrawBench is test-only in Pruna. We shuffle the test split with a fixed seed and keep a subset of prompts for coefficient fitting.

[ ]:
from pruna.data.pruna_datamodule import PrunaDataModule

from pruna_pro import SmashConfig

CALIBRATION_SEED = 42
NUM_CALIBRATION_PROMPTS = 50

drawbench = PrunaDataModule.from_string("DrawBench", seed=CALIBRATION_SEED)
calibration_prompts = drawbench.test_dataset.shuffle(seed=CALIBRATION_SEED).select(
    range(NUM_CALIBRATION_PROMPTS)
)
_ = calibration_prompts.select([0])

smash_config = SmashConfig({
    "auto": {
        "cache_mode": "calibration",
        "speed_factor": 0.4,
    },
    "torch_compile": {},
})
smash_config.add_data((calibration_prompts, _, _), "prompt_collate")

3. Smashing the Model

Replace the token with the one provided by PrunaAI.

[ ]:
from pruna_pro import smash

pipe = smash(
    model=pipe,
    token="<your_pruna_token>",
    smash_config=smash_config,
)

4. Running Inference

The first call with a given num_inference_steps calibrates coefficients (full generations over the prompt set, then a least-squares fit), then runs your request. Later calls with the same step count and schedule will reuse the in-memory coefficients.

[ ]:
prompt = (
    "An anime illustration of Sydney Opera House sitting next to Eiffel tower, under a blue night sky of "
    "roiling energy, exploding yellow stars, and radiating swirls of blue."
)
num_inference_steps = 50

# First call: calibrates coefficients for this step count (slow).
pipe(prompt, num_inference_steps=num_inference_steps).images[0]
[ ]:
# Second call: reuses calibrated coefficients (fast).
pipe(prompt, num_inference_steps=num_inference_steps).images[0]

5. Inspecting Calibrated Coefficients

Fitted coefficients live on the cache helper. Keys are (schedule_id, calibration_order) where schedule_id encodes the caching schedule (which steps compute vs reuse). Values are CalibratedCoeffs objects with the per-step linear weights used at inference.

After saving the smashed model, the same data is written to caching_coeffs.pkl and restored on load.

[ ]:
pipe.cache_helper.calibrated_coeffs

The speed factor can be adjusted on the fly. A new schedule triggers recalibration (the prompt dataset is reused; only the fit is rerun).

[ ]:
pipe.cache_helper.set_params(speed_factor=0.33)
pipe(prompt, num_inference_steps=num_inference_steps).images[0]
[ ]:
pipe.cache_helper.calibrated_coeffs

6. Calibration Order

calibration_order controls how many prior compute steps are used to approximate each skipped step (default 2). Changing it adds a new entry under calibrated_coeffs. Use a fixed generator seed when comparing orders on the same prompt.

[ ]:
pipe.cache_helper.set_params(calibration_order=3, speed_factor=0.25)
pipe(
    prompt,
    num_inference_steps=num_inference_steps,
    generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]
[ ]:
pipe.cache_helper.calibrated_coeffs

calibration_order=None uses all prior compute steps in the fit. This can help on some schedules but is more expensive and may overfit small calibration sets.

[ ]:
pipe.cache_helper.set_params(calibration_order=None)
pipe(
    prompt,
    num_inference_steps=num_inference_steps,
    generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]
[ ]:
pipe.cache_helper.calibrated_coeffs

Changing num_inference_steps builds or reuses a calibration dataset for that length, then fits coefficients for the current schedule and order.

[ ]:
pipe.cache_helper.set_params(calibration_order=15)
pipe(
    prompt,
    num_inference_steps=28,
    generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]
[ ]:
pipe.cache_helper.calibrated_coeffs

7. Saving the Smashed Model

Saving writes caching_coeffs.pkl and registers caching_coeffs in smash_config.load_artifacts_fns so coefficients are restored automatically on load.

[ ]:
save_dir = "flux-calibration-cache"
pipe.save_pretrained(save_dir)

8. Loading and Inference-Only Use

Reload with from_pretrained. No prompt datamodule is required: load_caching_coeffs restores pipe.cache_helper.calibrated_coeffs from disk.

Before loading, free the in-memory model (del pipe, then gc.collect() and torch.cuda.empty_cache()) so reload does not run out of memory.

Loaded models are inference-only for calibration mode. Changing schedule/order without matching persisted coefficients raises an error.

[ ]:
import gc

from pruna_pro import PrunaProModel

del pipe
gc.collect()
torch.cuda.empty_cache()

loaded = PrunaProModel.from_pretrained(save_dir)
loaded.cache_helper.calibrated_coeffs
[ ]:
# Inference reuses persisted coefficients (no calibration dataset needed).
loaded(
    prompt,
    num_inference_steps=num_inference_steps,
    generator=torch.Generator(device="cuda").manual_seed(42),
).images[0]

Wrap Up

You enabled auto caching with calibration mode, calibrated on DrawBench prompts during a fresh smash, saved caching_coeffs.pkl, and loaded the model for inference-only use without re-attaching data.