# 量化、编译和卸载

优化模型通常需要在[推理速度](./fp16)和[内存占用](./memory)之间进行权衡。例如，[缓存](./cache)虽然可以提升推理速度，但由于需要存储中间注意力层的输出，也会增加内存消耗。一种更为均衡的优化策略是将模型量化、[torch.compile](./fp16#torchcompile)以及多种[卸载方法](./memory#offloading)相结合。

> [!TIP]
> 请参阅 [torch.compile](./fp16#torchcompile) 指南，以了解有关编译的更多信息以及在此处如何应用它们。例如，区域编译可以在不牺牲任何加速效果的情况下显著缩短编译时间。

在图像生成中，将量化与[模型卸载](./memory#model-offloading)相结合，通常能在质量、速度和内存之间取得最佳平衡。组卸载在图像生成中效果不佳，因为如果计算内核完成得更快，通常无法*完全*重叠数据传输。这会导致 CPU 和 GPU 之间产生一定的通信开销。

对于视频生成，结合量化和 [group-offloading](./memory#group-offloading) 通常效果更好，因为视频模型更受计算限制。

下表提供了 Flux 优化策略组合及其对延迟和内存使用影响的比较。

| 组合 | 延迟（秒） | 内存使用量（GB） |
|---|---|---|
| 量化  | 32.602 | 14.9453 |
| 量化，torch.compile  | 25.847 | 14.9448 |
| 量化、torch.compile、模型 CPU 卸载 | 32.312 | 12.2369 |

这些结果是在 Flux 上使用 RTX 4090 进行基准测试得出的。transformer 和 text_encoder 组件已进行量化。如果您想评估自己的模型，请参阅基准测试脚本。

本指南将演示如何使用 [bitsandbytes](../quantization/bitsandbytes#torchcompile) 编译并卸载量化模型。请确保你使用的是 [PyTorch nightly](https://pytorch.org/get-started/locally/) 以及 bitsandbytes 的最新版本。

虽然本示例使用了 bitsandbytes，但其他量化后端（例如 [TorchAO](../quantization/torchao)）也支持这些功能。

```bash
pip install -U bitsandbytes
```

## 量化与 torch.compile

首先对模型进行[量化](../quantization/overview)以降低存储所需的内存，然后对其进行[编译](./fp16#torchcompile)以加速推理。

配置 [Dynamo](https://docs.pytorch.org/docs/stable/torch.compiler_dynamo_overview.html) 的 `capture_dynamic_output_shape_ops = True`，以在编译 bitsandbytes 模型时处理动态输出。

```py
import torch
from diffusers import DiffusionPipeline
from diffusers.quantizers import PipelineQuantizationConfig

torch._dynamo.config.capture_dynamic_output_shape_ops = True

# quantize
pipeline_quant_config = PipelineQuantizationConfig(
    quant_backend="bitsandbytes_4bit",
    quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.bfloat16},
    components_to_quantize=["transformer", "text_encoder_2"],
)
pipeline = DiffusionPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    quantization_config=pipeline_quant_config,
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

# compile
pipeline.transformer.to(memory_format=torch.channels_last)
pipeline.transformer.compile(mode="max-autotune", fullgraph=True)
pipeline("""
    cinematic film still of a cat sipping a margarita in a pool in Palm Springs, California
    highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain
"""
).images[0]
```

## 量化、torch.compile 和卸载

除了量化和 torch.compile，如果还需要进一步降低内存占用，可以尝试使用 offloading。Offloading 会根据计算需求，将各个层或模型组件在 CPU 和 GPU 之间进行移动。

在卸载过程中配置 [Dynamo](https://docs.pytorch.org/docs/stable/torch.compiler_dynamo_overview.html) 的 `cache_size_limit` 以避免过度重新编译，并设置 `capture_dynamic_output_shape_ops = True` 以在处理 bitsandbytes 模型的编译时应对动态输出。

[模型 CPU 卸载](./memory#model-offloading)会在需要计算时，将单个流水线组件（例如 transformer 模型）移至 GPU。否则，该组件会被卸载到 CPU。

```py
import torch
from diffusers import DiffusionPipeline
from diffusers.quantizers import PipelineQuantizationConfig

torch._dynamo.config.cache_size_limit = 1000
torch._dynamo.config.capture_dynamic_output_shape_ops = True

# quantize
pipeline_quant_config = PipelineQuantizationConfig(
    quant_backend="bitsandbytes_4bit",
    quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.bfloat16},
    components_to_quantize=["transformer", "text_encoder_2"],
)
pipeline = DiffusionPipeline.from_pretrained(
    "black-forest-labs/FLUX.1-dev",
    quantization_config=pipeline_quant_config,
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

# model CPU offloading
pipeline.enable_model_cpu_offload()

# compile
pipeline.transformer.compile()
pipeline(
    "cinematic film still of a cat sipping a margarita in a pool in Palm Springs, California, highly detailed, high budget hollywood movie, cinemascope, moody, epic, gorgeous, film grain"
).images[0]
```

[组卸载](./memory#group-offloading)会将单个流水线组件（例如 transformer 模型）的内部层移至 GPU 进行计算，并在不需要时将其卸载。同时，它利用 [CUDA stream](./memory#cuda-stream) 特性预取下一层以执行。

通过重叠计算和数据传输，它比模型 CPU 卸载更快，同时还能节省内存。

```py
# pip install ftfy
import torch
from diffusers import AutoModel, DiffusionPipeline
from diffusers.hooks import apply_group_offloading
from diffusers.utils import export_to_video
from diffusers.quantizers import PipelineQuantizationConfig
from transformers import UMT5EncoderModel

torch._dynamo.config.cache_size_limit = 1000
torch._dynamo.config.capture_dynamic_output_shape_ops = True

# quantize
pipeline_quant_config = PipelineQuantizationConfig(
    quant_backend="bitsandbytes_4bit",
    quant_kwargs={"load_in_4bit": True, "bnb_4bit_quant_type": "nf4", "bnb_4bit_compute_dtype": torch.bfloat16},
    components_to_quantize=["transformer", "text_encoder"],
)

text_encoder = UMT5EncoderModel.from_pretrained(
    "Wan-AI/Wan2.1-T2V-14B-Diffusers", subfolder="text_encoder", dtype=torch.bfloat16
)
pipeline = DiffusionPipeline.from_pretrained(
    "Wan-AI/Wan2.1-T2V-14B-Diffusers",
    quantization_config=pipeline_quant_config,
    dtype=torch.bfloat16,
).to("cuda")  # or "mps", "xpu", "cpu"

# group offloading
onload_device = torch.device("cuda")
offload_device = torch.device("cpu")

pipeline.transformer.enable_group_offload(
    onload_device=onload_device,
    offload_device=offload_device,
    offload_type="leaf_level",
    use_stream=True,
    non_blocking=True
)
pipeline.vae.enable_group_offload(
    onload_device=onload_device,
    offload_device=offload_device,
    offload_type="leaf_level",
    use_stream=True,
    non_blocking=True
)
apply_group_offloading(
    pipeline.text_encoder,
    onload_device=onload_device,
    offload_type="leaf_level",
    use_stream=True,
    non_blocking=True
)

# compile
pipeline.transformer.compile()

prompt = """
The camera rushes from far to near in a low-angle shot, 
revealing a white ferret on a log. It plays, leaps into the water, and emerges, as the camera zooms in 
for a close-up. Water splashes berry bushes nearby, while moss, snow, and leaves blanket the ground. 
Birch trees and a light blue sky frame the scene, with ferns in the foreground. Side lighting casts dynamic 
shadows and warm highlights. Medium composition, front view, low angle, with depth of field.
"""
negative_prompt = """
Bright tones, overexposed, static, blurred details, subtitles, style, works, paintings, images, static, overall gray, worst quality, 
low quality, JPEG compression residue, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn faces, deformed, disfigured, 
misshapen limbs, fused fingers, still picture, messy background, three legs, many people in the background, walking backwards
"""

output = pipeline(
    prompt=prompt,
    negative_prompt=negative_prompt,
    num_frames=81,
    guidance_scale=5.0,
).frames[0]
export_to_video(output, "output.mp4", fps=16)
```

