Khám phá các backend lượng tử hóa trong Diffusers
Bài viết khám phá các backend lượng tử hóa khác nhau có sẵn trong Diffusers.
- 10 min read
Khám phá các Backend Lượng tử hóa trong Diffusers
Các mô hình khuếch tán lớn như Flux (mô hình tạo ảnh từ văn bản dựa trên luồng) có thể tạo ra những hình ảnh tuyệt đẹp, nhưng kích thước của chúng có thể là một trở ngại, đòi hỏi bộ nhớ và tài nguyên tính toán đáng kể. Lượng tử hóa cung cấp một giải pháp mạnh mẽ, thu nhỏ các mô hình này để giúp chúng dễ tiếp cận hơn mà không làm giảm đáng kể hiệu suất. Nhưng câu hỏi lớn luôn là: bạn có thực sự phân biệt được sự khác biệt trong hình ảnh cuối cùng không?
Trước khi đi sâu vào các chi tiết kỹ thuật về cách các backend lượng tử hóa khác nhau trong Hugging Face Diffusers hoạt động, tại sao không kiểm tra nhận thức của chính bạn?
Nhận diện Mô hình Lượng tử hóa
Chúng tôi đã tạo ra một thiết lập nơi bạn có thể cung cấp một lời nhắc và chúng tôi tạo ra kết quả bằng cả mô hình độ chính xác cao ban đầu (ví dụ: Flux-dev ở BF16) và một số phiên bản lượng tử hóa (BnB 4-bit, BnB 8-bit). Sau đó, các hình ảnh được tạo ra sẽ được trình bày cho bạn và thử thách của bạn là xác định hình ảnh nào đến từ các mô hình lượng tử hóa.
Hãy thử tại đây!
<gradio-app src="https://diffusers-flux-quant.hf.space"></gradio-app>
Thông thường, đặc biệt với lượng tử hóa 8-bit, sự khác biệt là rất nhỏ và có thể không đáng chú ý nếu không kiểm tra kỹ. Lượng tử hóa mạnh hơn như 4-bit hoặc thấp hơn có thể dễ nhận thấy hơn, nhưng kết quả vẫn có thể tốt, đặc biệt là khi xem xét khả năng tiết kiệm bộ nhớ lớn. NF4 thường mang lại sự cân bằng tốt nhất.
Bây giờ, hãy đi sâu hơn.
Backend Lượng tử hóa trong Diffusers
Dựa trên bài đăng trước của chúng tôi, “Bộ chuyển đổi khuếch tán tiết kiệm bộ nhớ với Quanto và Diffusers”, bài đăng này khám phá các backend lượng tử hóa đa dạng được tích hợp trực tiếp vào Hugging Face Diffusers. Chúng ta sẽ xem xét cách hỗ trợ bitsandbytes, GGUF, torchao, Quanto và FP8 gốc giúp các mô hình lớn và mạnh mẽ dễ tiếp cận hơn, đồng thời trình bày cách sử dụng chúng với Flux.
Trước khi đi sâu vào các backend lượng tử hóa, hãy giới thiệu FluxPipeline (sử dụng checkpoint black-forest-labs/FLUX.1-dev) và các thành phần của nó, mà chúng ta sẽ lượng tử hóa. Tải toàn bộ mô hình FLUX.1-dev ở độ chính xác BF16 yêu cầu khoảng 31.447 GB bộ nhớ. Các thành phần chính là:
- Bộ mã hóa văn bản (CLIP và T5):
- Chức năng: Xử lý các lời nhắc văn bản đầu vào. FLUX-dev sử dụng CLIP để hiểu ban đầu và T5 lớn hơn để hiểu sắc thái và hiển thị văn bản tốt hơn.
- Bộ nhớ: T5 - 9.52 GB; CLIP - 246 MB (ở BF16)
- Transformer (Mô hình chính - MMDiT):
- Chức năng: Phần tạo cốt lõi (Bộ chuyển đổi khuếch tán đa phương thức). Tạo hình ảnh trong không gian tiềm ẩn từ các nhúng văn bản.
- Bộ nhớ: 23.8 GB (ở BF16)
- Bộ mã hóa tự động biến phân (VAE):
- Chức năng: Dịch hình ảnh giữa không gian pixel và không gian tiềm ẩn. Giải mã biểu diễn tiềm ẩn được tạo thành hình ảnh dựa trên pixel.
- Bộ nhớ: 168 MB (ở BF16)
- Tiêu điểm của Lượng tử hóa: Các ví dụ sẽ chủ yếu tập trung vào
transformervàtext_encoder_2(T5) để tiết kiệm bộ nhớ đáng kể nhất.
prompts = [
"Phong cách Baroque, nội thất cung điện xa hoa với trần mạ vàng trang trí công phu, thảm trang trí phức tạp và ánh sáng ấn tượng trên một cầu thang lớn.",
"Phong cách tương lai, một sân bay vũ trụ năng động với các tàu vũ trụ bằng bạc bóng bẩy neo đậu tại các bệ góc cạnh, được bao quanh bởi các hành tinh xa xôi và các đường năng lượng phát sáng.",
"Phong cách Noir, một con hẻm tối tăm với những ngọn đèn đường nhấp nháy và một bóng người đơn độc mặc áo khoác trench, được bao bọc bởi những viên đá cuội ngâm trong mưa và các cửa hàng tối tăm.",
]
bitsandbytes (BnB)
bitsandbytes là một thư viện phổ biến và thân thiện với người dùng để lượng tử hóa 8-bit và 4-bit, được sử dụng rộng rãi cho LLM và tinh chỉnh QLoRA. Chúng ta cũng có thể sử dụng nó cho các mô hình khuếch tán và luồng dựa trên transformer.
| BF16 | BnB 4-bit | BnB 8-bit |
|---|---|---|
| So sánh trực quan các đầu ra của mô hình Flux-dev bằng cách sử dụng lượng tử hóa BF16 (trái), BnB 4-bit (giữa) và BnB 8-bit (phải). (Nhấp vào hình ảnh để phóng to) |
import torch
from diffusers import FluxPipeline
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
from diffusers.quantizers import PipelineQuantizationConfig
from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
model_id = "black-forest-labs/FLUX.1-dev"
pipeline_quant_config = PipelineQuantizationConfig(
quant_mapping={
"transformer": DiffusersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
"text_encoder_2": TransformersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
}
)
pipe = FluxPipeline.from_pretrained(
model_id,
quantization_config=pipeline_quant_config,
torch_dtype=torch.bfloat16
)
pipe.to("cuda")
prompt = "Phong cách Baroque, nội thất cung điện xa hoa với trần mạ vàng trang trí công phu, thảm trang trí phức tạp và ánh sáng ấn tượng trên một cầu thang lớn."
pipe_kwargs = {
"prompt": prompt,
"height": 1024,
"width": 1024,
"guidance_scale": 3.5,
"num_inference_steps": 50,
"max_sequence_length": 512,
}
print(f"Mức sử dụng bộ nhớ của Pipeline: {torch.cuda.max_memory_reserved() / 1024**3:.3f} GB")
image = pipe(
**pipe_kwargs, generator=torch.manual_seed(0),
).images[0]
print(f"Mức sử dụng bộ nhớ của Pipeline: {torch.cuda.max_memory_reserved() / 1024**3:.3f} GB")
image.save("flux-dev_bnb_4bit.png")
Lưu ý: Khi sử dụng
PipelineQuantizationConfigvớibitsandbytes, bạn cần nhập riêngDiffusersBitsAndBytesConfigtừdiffusersvàTransformersBitsAndBytesConfigtừtransformers. Điều này là do các thành phần này có nguồn gốc từ các thư viện khác nhau. Nếu bạn thích một thiết lập đơn giản hơn mà không cần quản lý các lần nhập riêng biệt này, bạn có thể sử dụng một phương pháp thay thế để lượng tử hóa cấp pipeline, một ví dụ về phương pháp này có trong tài liệu Diffusers về lượng tử hóa cấp Pipeline.
Để biết thêm thông tin, hãy xem tài liệu bitsandbytes.
torchao
torchao là một thư viện gốc PyTorch để tối ưu hóa kiến trúc, cung cấp lượng tử hóa, độ thưa và các loại dữ liệu tùy chỉnh, được thiết kế để tương thích với torch.compile và FSDP. Diffusers hỗ trợ một loạt các loại dữ liệu kỳ lạ của torchao, cho phép kiểm soát chi tiết việc tối ưu hóa mô hình.
| int4_weight_only | int8_weight_only | float8_weight_only |
|---|---|---|
| So sánh trực quan các đầu ra của mô hình Flux-dev bằng cách sử dụng lượng tử hóa torchao int4_weight_only (trái), int8_weight_only (giữa) và float8_weight_only (phải). (Nhấp vào hình ảnh để phóng to) |
@@
- from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
+ from diffusers import TorchAoConfig as DiffusersTorchAoConfig
- from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
+ from transformers import TorchAoConfig as TransformersTorchAoConfig
@@
pipeline_quant_config = PipelineQuantizationConfig(
quant_mapping={
- "transformer": DiffusersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
- "text_encoder_2": TransformersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
+ "transformer": DiffusersTorchAoConfig("int8_weight_only"),
+ "text_encoder_2": TransformersTorchAoConfig("int8_weight_only"),
}
)
@@
- from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
+ from diffusers import TorchAoConfig as DiffusersTorchAoConfig
- from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
+ from transformers import TorchAoConfig as TransformersTorchAoConfig
@@
pipeline_quant_config = PipelineQuantizationConfig(
quant_mapping={
- "transformer": DiffusersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
- "text_encoder_2": TransformersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
+ "transformer": DiffusersTorchAoConfig("int4_weight_only"),
+ "text_encoder_2": TransformersTorchAoConfig("int4_weight_only"),
}
)
pipe = FluxPipeline.from_pretrained(
model_id,
quantization_config=pipeline_quant_config,
torch_dtype=torch.bfloat16,
+ device_map="balanced"
)
- pipe.to("cuda")
Để biết thêm thông tin, hãy xem tài liệu torchao.
Quanto
Quanto là một thư viện lượng tử hóa được tích hợp với hệ sinh thái Hugging Face thông qua thư viện optimum.
| INT4 | INT8 | FP8 |
|---|---|---|
| So sánh trực quan các đầu ra của mô hình Flux-dev bằng cách sử dụng lượng tử hóa Quanto INT4 (trái), INT8 (giữa) và FP8 (phải). (Nhấp vào hình ảnh để phóng to) |
@@
- from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
+ from diffusers import QuantoConfig as DiffusersQuantoConfig
- from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
+ from transformers import QuantoConfig as TransformersQuantoConfig
@@
pipeline_quant_config = PipelineQuantizationConfig(
quant_mapping={
- "transformer": DiffusersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
- "text_encoder_2": TransformersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
+ "transformer": DiffusersQuantoConfig(weights_dtype="int8"),
+ "text_encoder_2": TransformersQuantoConfig(weights_dtype="int8"),
}
)
Lưu ý: Tại thời điểm viết bài này, để hỗ trợ float8 với Quanto, bạn cần
optimum-quanto<0.2.5và sử dụng trực tiếp quanto. Chúng tôi sẽ làm việc để khắc phục điều này.
import torch
from diffusers import AutoModel, FluxPipeline
from transformers import T5EncoderModel
from optimum.quanto import freeze, qfloat8, quantize
model_id = "black-forest-labs/FLUX.1-dev"
text_encoder_2 = T5EncoderModel.from_pretrained(
model_id,
subfolder="text_encoder_2",
torch_dtype=torch.bfloat16,
)
quantize(text_encoder_2, weights=qfloat8)
freeze(text_encoder_2)
transformer = AutoModel.from_pretrained(
model_id,
subfolder="transformer",
torch_dtype=torch.bfloat16,
)
quantize(transformer, weights=qfloat8)
freeze(transformer)
pipe = FluxPipeline.from_pretrained(
model_id,
transformer=transformer,
text_encoder_2=text_encoder_2,
torch_dtype=torch.bfloat16
).to("cuda")
Để biết thêm thông tin, hãy xem tài liệu Quanto.
GGUF
GGUF là một định dạng tệp phổ biến trong cộng đồng llama.cpp để lưu trữ các mô hình lượng tử hóa.
| Q2_k | Q4_1 | Q8_0 |
|---|---|---|
| So sánh trực quan các đầu ra của mô hình Flux-dev bằng cách sử dụng lượng tử hóa GGUF Q2_k (trái), Q4_1 (giữa) và Q8_0 (phải). (Nhấp vào hình ảnh để phóng to) |
import torch
from diffusers import FluxPipeline, FluxTransformer2DModel, GGUFQuantizationConfig
model_id = "black-forest-labs/FLUX.1-dev"
# Đường dẫn đến một tệp GGUF đã được lượng tử hóa trước
ckpt_path = "https://huggingface.co/city96/FLUX.1-dev-gguf/resolve/main/flux1-dev-Q4_1.gguf"
transformer = FluxTransformer2DModel.from_single_file(
ckpt_path,
quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
torch_dtype=torch.bfloat16,
)
pipe = FluxPipeline.from_pretrained(
model_id,
transformer=transformer,
torch_dtype=torch.bfloat16,
)
pipe.to("cuda")
Để biết thêm thông tin, hãy xem tài liệu GGUF.
FP8 Layerwise Casting (enable_layerwise_casting)
FP8 Layerwise Casting là một kỹ thuật tối ưu hóa bộ nhớ. Nó hoạt động bằng cách lưu trữ trọng số của mô hình ở định dạng FP8 (dấu phẩy động 8 bit) nhỏ gọn, sử dụng khoảng một nửa bộ nhớ so với độ chính xác FP16 hoặc BF16 tiêu chuẩn. Ngay trước khi một lớp thực hiện các tính toán của nó, các trọng số của nó sẽ được ép lên độ chính xác tính toán cao hơn (như FP16/BF16) một cách động. Ngay sau đó, các trọng số được ép trở lại FP8 để lưu trữ hiệu quả. Cách tiếp cận này hoạt động vì các tính toán cốt lõi vẫn giữ được độ chính xác cao và các lớp đặc biệt nhạy cảm với lượng tử hóa (như chuẩn hóa) thường bị bỏ qua. Kỹ thuật này cũng có thể được kết hợp với tải xuống theo nhóm để tiết kiệm bộ nhớ hơn nữa.
| FP8 (e4m3) |
|---|
| Đầu ra trực quan của mô hình Flux-dev bằng cách sử dụng lượng tử hóa FP8 Layerwise Casting (e4m3). |
import torch
from diffusers import AutoModel, FluxPipeline
model_id = "black-forest-labs/FLUX.1-dev"
transformer = AutoModel.from_pretrained(
model_id,
subfolder="transformer",
torch_dtype=torch.bfloat16
)
transformer.enable_layerwise_casting(storage_dtype=torch.float8_e4m3fn, compute_dtype=torch.bfloat16)
pipe = FluxPipeline.from_pretrained(model_id, transformer=transformer, torch_dtype=torch.bfloat16)
pipe.to("cuda")
Để biết thêm thông tin, hãy xem tài liệu Layerwise casting.
Kết hợp với Tối ưu hóa Bộ nhớ khác và torch.compile
Hầu hết các backend lượng tử hóa này có thể được kết hợp với các kỹ thuật tối ưu hóa bộ nhớ được cung cấp trong Diffusers. Hãy khám phá việc tải CPU, tải nhóm và torch.compile. Bạn có thể tìm hiểu thêm về các kỹ thuật này trong tài liệu Diffusers.
Lưu ý: Tại thời điểm viết bài này, bnb +
torch.compilecũng hoạt động nếu bnb được cài đặt từ nguồn và sử dụng pytorch nightly hoặc với fullgraph=False.
import torch
from diffusers import FluxPipeline
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig
from diffusers.quantizers import PipelineQuantizationConfig
from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig
model_id = "black-forest-labs/FLUX.1-dev"
pipeline_quant_config = PipelineQuantizationConfig(
quant_mapping={
"transformer": DiffusersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
"text_encoder_2": TransformersBitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16),
}
)
pipe = FluxPipeline.from_pretrained(
model_id,
quantization_config=pipeline_quant_config,
torch_dtype=torch.bfloat16
)
- pipe.to("cuda")
+ pipe.enable_model_cpu_offload()
Tải CPU Mô hình (enable_model_cpu_offload): Phương pháp này di chuyển toàn bộ các thành phần mô hình (như UNet, bộ mã hóa văn bản hoặc VAE) giữa CPU và GPU trong pipeline suy luận. Nó cung cấp khả năng tiết kiệm VRAM đáng kể và thường nhanh hơn so với việc tải chi tiết hơn vì nó liên quan đến ít lần truyền dữ liệu lớn hơn.
bnb + enable_model_cpu_offload:
import torch
from diffusers import FluxPipeline, AutoModel
model_id = "black-forest-labs/FLUX.1-dev"
transformer = AutoModel.from_pretrained(
model_id,
subfolder="transformer",
torch_dtype=torch.bfloat16,
# device_map="cuda"
)
transformer.enable_layerwise_casting(storage_dtype=torch.float8_e4m3fn, compute_dtype=torch.bfloat16)
+ transformer.enable_group_offload(onload_device=torch.device("cuda"), offload_device=torch.device("cpu"), offload_type="leaf_level", use_stream=True)
pipe = FluxPipeline.from_pretrained(model_id, transformer=transformer, torch_dtype=torch.bfloat16)
- pipe.to("cuda")
Tải nhóm (enable_group_offload cho các thành phần diffusers hoặc apply_group_offloading cho các torch.nn.Module chung): Nó di chuyển các nhóm lớp mô hình bên trong (như các phiên bản torch.nn.ModuleList hoặc torch.nn.Sequential) sang CPU. Cách
Link bài viết gốc
- Tags:
- Ai
- May 21, 2025
- Huggingface.co