Black Forest Labs FLUX.3 Image Klein 发布并在 Diffusers 首日支持

Hugging Face Blog2026-10-15 08:00
Black Forest Labs FLUX.3 Image Klein 发布并在 Diffusers 首日支持

Black Forest Labs FLUX.3 Image Klein 来了,从第一天起就能在 Diffusers 中运行。一个模型同时完成文生图和多参考图生图,还带来了新东西:布局控制。你在提示词中写入边界框,说明图像的每个部分应该放在哪里,或者参考图中的哪些部分需要移动。 两个开放权重检查点都在 FLUX.3 Image Klein collection 中: 检查点 说明 默认值 black-forest-labs/flux-3-image-klein 时间步蒸馏版,用于快速推理 16 步,无 CFG black-forest-labs/flux-3-image-klein-base 基础模型 50 步,CFG a red fox in a snowy forest (text-to-image), the same fox re-rendered from that image (image-to-image), a black stippled running silhouette on a chartreuse background, drawn inside its labeled bounding box, and a knitted knight moved from a "from" box to a "to" box next to a knitted dragon (layout editing) 你可以直接前往 Hugging Face space 立即生成一些图像! TL;DR - 一个模型即可完成文生图、多参考图生图和边界框布局控制。 - 蒸馏版检查点在 H100 上生成一张 1024 × 1024 图像约需 2 秒。 - NVFP4 和 FP8 权重可通过 from_single_file 加载,适用于较小的 GPU。 - 提供用于文生图和图像编辑的 LoRA 训练脚本。 - 以 Modular Diffusers 流水线形式发布,因此你可以取出模块或添加自己的模块。 目录 - 快速开始 - FLUX.3 能做什么 - 基础模型和量化变体 - LoRA 微调 - 运行成本 - 架构 - Modular Diffusers - 试一试 快速开始 安装最新的 diffusers 和 transformers(用于 Qwen3-VL 文本编码器): pip install -U diffusers transformers 加载蒸馏版检查点并生成图像: import torch from diffusers import ModularPipeline pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein") pipe.load_components(dtype=torch.bfloat16) pipe.to("cuda") pipe(prompt="a red fox", output="images")[0].save("fox.png") FLUX.3 能做什么 文生图 FLUX.3 对包含主体、场景、光线、构图和风格的完整场景描述响应最好。Black Forest Labs 的官方提示词指南有更多技巧。蒸馏版检查点默认以 16 步生成 1024 × 1024 图像。传入 height、width 或 num_inference_steps 可更改这些设置。 prompt = ( "A wide-angle wildlife photograph of a red fox standing alert in a snow-covered forest clearing at golden hour. " "The fox is centered in the frame, its thick orange winter coat contrasting with the fresh white snow, breath " "visible in the cold air. Snow-laden pine trees fill the softly blurred background, and low warm sunlight rakes " "across the scene from the left, casting long blue shadows." ) fox = pipe( prompt=prompt, generator=torch.Generator().manual_seed(0), output="images" )[0] fox.save("fox.png") A red fox standing alert in a snow-covered forest clearing, lit by low golden sunlight from the left 文生图 图生图 通过 image 参数传入一张或多张参考图。将提示词写为你想要的图像说明,并包含所做的更改。 prompt = ( "The same red fox in the same snow-covered forest clearing, now at night under a full moon: cool blue " "moonlight, deep shadows, stars visible through the pine branches, the fox's eyes catching the light." ) night = pipe( prompt=prompt, image=fox, # from the previous text to image example generator=torch.Generator().manual_seed(0), output="images", )[0] night.save("night.png") The fox from the previous image, relit in cold blue light 图生图 使用多张参考图时,在提示词中按它们传入 image 的顺序将其称为 <ref_image_0>、<ref_image_1>……: red_fox = pipe(prompt="a red fox", generator=torch.Generator().manual_seed(0), output="images")[0] bench = pipe(prompt="a wooden bench", generator=torch.Generator().manual_seed(0), output="images")[0] combined = pipe( prompt="The fox from <ref_image_0> sitting on the wooden bench from <ref_image_1> in a snowy setting.", image=[red_fox, bench], output="images", )[0] combined.save("combined.png") A red fox sitting on dry grass A slatted wooden bench against a plain grey wall The same fox sitting on the same bench, with snow on the ground <ref_image_0>:一只红狐 <ref_image_1>:一条木凳 长凳上的狐狸 布局控制 在说明文字后添加一个 JSON 区域列表。每个区域有一个 id、一个 bbox 和一个 desc,说明文字中以 <id> 提及它。 框写作 [y_min, x_min, y_max, x_max],y 在前,采用 0–1000 的尺度。由于该尺度是归一化的,同一个框在任何图像尺寸下都适用。 在新图像中放置区域 layout_prompt = ( "Minimalist graphic illustration featuring a black silhouette of a person <silhouette_1> centered against a " "solid, vibrant chartreuse background <background_1>. The figure is captured in a dynamic, mid-stride running " "pose, facing toward the left side of the frame. " '[{"id": "background_1", "bbox": [0, 0, 1000, 1000], "desc": "A flat field of neon yellow-green with a subtle ' 'paper texture and fine, organic grain."}, {"id": "silhouette_1", "bbox": [150, 150, 850, 850], "desc": "A ' "black silhouette of a person in motion, made of dense stippled ink. The leading edges are solid; the trailing " 'arm and leg dissolve into coarse square pixels and scattered dots."}]' ) runner = pipe(prompt=layout_prompt, generator=torch.Generator().manual_seed(0), output="images")[0] Diagram: the caption and the two region descriptions on the left, the two boxes on a 0–1000 grid in the middle, and the generated image on the right, where a black stippled runner fills the silhouette box on a chartreuse background 每个区域的 desc 绘制在其 bbox 内 在参考图内移动区域 对于编辑,每个区域说明其内容来自哪里(参考图中的 src_bbox)以及去往哪里(输出中的 tgt_bbox)。两个框相同的区域保持原位。 这个示例将针织骑士向左上移动,并固定背景、悬崖、针、龙和火焰: from diffusers.utils import load_image knight_image = load_image("knight_orig.png") edit_prompt = ( "In <ref_image_0>, move the miniature grey amigurumi knight figure <knight_1> upwards and to the left along the " "yarn cliff <cliff_1>. Keep the rest of the image completely unchanged, preserving the blurred yarn backdrop " "<backdrop_1>, the large knitted dragon <dragon_1>, and the fiery orange thread <fire_1>. " '[{"id": "knight_1", "from": "ref_image_0", "src_bbox": [500, 150, 850, 350], "tgt_bbox": [194, 55, 544, 255], ' '"desc": "A miniature amigurumi knight crocheted from thick grey woolen yarn."}, ' '{"id": "backdrop_1", "from": "ref_image_0", "src_bbox": [0, 0, 1000, 1000], "tgt_bbox": [0, 0, 1000, 1000], ' '"desc": "A soft, out-of-focus backdrop of cream and indigo knitted yarn."}, ' '{"id": "cliff_1", "from": "ref_image_0", "src_bbox": [300, 0, 1000, 450], "tgt_bbox": [300, 0, 1000, 450], ' '"desc": "A steep cliff of stacked yarn skeins in mustard, terracotta, and indigo."}, ' '{"id": "needle_1", "from": "ref_image_0", "src_bbox": [420, 250, 550, 450], "tgt_bbox": [420, 250, 550, 450], ' '"desc": "An oversized polished steel tapestry needle."}, ' '{"id": "dragon_1", "from": "ref_image_0", "src_bbox": [100, 600, 950, 1000], "tgt_bbox": [100, 600, 950, 1000], ' '"desc": "A large, fluffy knitted dragon in terracotta and mustard yarn."}, ' '{"id": "fire_1", "from": "ref_image_0", "src_bbox": [350, 400, 650, 650], "tgt_bbox": [350, 400, 650, 650], ' '"desc": "Curled puffs of fiery orange angora thread suspended in mid-air."}]' ) moved = pipe(prompt=edit_prompt, image=knight_image, generator=torch.Generator().manual_seed(0), output="images")[0] Left: the reference with the knight's source box. Right: the output with an arrow from the source box to the target box; the pinned regions are outlined in white 骑士从其 src_bbox 移动到其 tgt_bbox,场景其余部分保持不动 基础模型和量化变体 基础模型 以上所有内容都使用蒸馏版检查点。基础检查点以无分类器引导运行 50 步,并且是适合微调的那个(参见LoRA 微调)。 pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein-base") pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}) pipe.to("cuda") pipe.guider = pipe.guider.new(guidance_scale=5.0) # optional image = pipe(prompt=prompt, output="images")[0] 单文件格式检查点 Black Forest Labs 还将该 transformer 作为单个文件发布,提供 bf16 以及两种量化格式:fp8r(逐行 FP8)和 nvfp4。用 from_single_file 加载其中任意一个,然后按名称加载其他组件,这样就不会同时下载 bf16 transformer。 from diffusers import BFLQuantizationConfig, Flux3Transformer2DModel, ModularPipeline model_id = "black-forest-labs/flux-3-image-klein-base" variant = "fp8r" # or "nvfp4" transformer = Flux3Transformer2DModel.from_single_file( f"https://huggingface.co/{model_id}/blob/main/variants/{variant}/model.safetensors", config=model_id, subfolder="transformer", quantization_config=BFLQuantizationConfig(compute_dtype=torch.bfloat16), dtype=torch.bfloat16, ) pipe = ModularPipeline.from_pretrained(model_id) pipe.load_components(dtype=torch.bfloat16) pipe.update_components(transformer=transformer) pipe.to("cuda") 对于未量化的单个文件,使用 https://huggingface.co/{model_id}/blob/main/model.safetensors 并去掉 quantization_config。 变体 快速内核运行于 其他 GPU fp8r 计算能力 8.9+(Ada、Hopper、Blackwell) 权重每步都会被反量化:内存更低,但无加速 nvfp4 Blackwell,且 flashinfer-python 和 flashinfer-cubin 为同一版本 同上 LoRA 微调 FLUX.3 同时适用于文生图和图生图,这使其成为自定义 LoRA 的优秀基础。diffusers 为这两类任务提供了 DreamBooth LoRA 训练脚本(你也可以使用 Ostris 的 AI Toolkit 进行训练)。 下面是一个文生图示例,它从 multimodalart/1920-raider-waite-tarot-public-domain 数据集中学习塔罗牌风格,并以 trtcrd 触发: accelerate launch train_dreambooth_lora_flux3_klein.py \ --pretrained_model_name_or_path="black-forest-labs/flux-3-image-klein-base" \ --dataset_name="multimodalart/1920-raider-waite-tarot-public-domain" \ --caption_column="caption" \ --instance_prompt="trtcrd" \ --output_dir="trained-flux3-tarot-lora" \ --mixed_precision="bf16" \ --resolution=1024 \ --use_aspect_ratio_buckets \ --train_batch_size=1 \ --rank=16 \ --lora_alpha=16 \ --learning_rate=1e-4 \ --lr_scheduler="constant" \ --max_train_steps=1500 \ --validation_prompt="a trtcrd of a cat, tarot style" \ --validation_epochs=25 \ --seed=0 \ --push_to_hub 为减少内存占用,可添加几个优化标志: --gradient_checkpointing \ --cache_latents \ --use_8bit_adam --gradient_checkpointing 在反向传播中重计算激活值(显存更低,速度略慢),--cache_latents 预编码图像并释放 VAE,--use_8bit_adam 以 8 位保存优化器状态。 有几个参数值得尝试。下面的网格使用不同的 LoRA rank 和优化器训练相同的塔罗风格;这些适配器发布在 Hub 上:diffusers-modular/flux3-tarot-lora。更多训练功能和配置记录在 DreamBooth FLUX.3 README 中。 FLUX.3 tarot-style LoRA across ranks and optimizers: the base model with no LoRA, then rank 16, rank 8 and rank 32 (AdamW, lr 1e-4) and a Prodigy run, on two held-out prompts 对于基于指令的编辑,图生图脚本(train_dreambooth_lora_flux3_klein_img2img.py)在(参考图像、目标图像、指令)三元组上训练,其中参考图像在提示词中以 <ref_image_0> 引用。 所有选项和完整的图生图示例请参见 DreamBooth FLUX.3 README。 运行成本 在本节中,我们将讨论模型的内存消耗和生成速度。 内存 名称 峰值分配(加载后)GiB 峰值分配(推理后)GiB nvfp4(RTX6000) 13.4 15.3 fp8r(H100) 14.35 16.27 bf16(H100) 18.80 20.72 如果你的内存更少: - 加载量化 transformer。 - 单独运行文本编码器并在之后释放它。它每张图像只运行一次,在去噪之前(参见编码一次,生成多次)。 - 开启自动 CPU 卸载,它仅在组件运行时将其移到 GPU 上(我们未对此进行基准测试) from diffusers import ComponentsManager, ModularPipeline manager = ComponentsManager() manager.enable_auto_cpu_offload(device="cuda") pipe = ModularPipeline.from_pretrained("black-forest-labs/flux-3-image-klein", components_manager=manager) pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}) # no pipe.to("cuda"): the manager handles device placement 速度 蒸馏检查点(16 步)的端到端耗时,在预热后取三次运行的平均值: 工作流 输出尺寸 时间 峰值内存 文生图 1024 × 1024 1.93 s 23.8 GiB 图生图,一张 1024 × 1024 参考图 1024 × 1024 3.87 s 23.8 GiB 布局编辑(骑士),一张 1360 × 768 参考图 1360 × 768 4.10 s 23.8 GiB 单张 NVIDIA H100 80GB,无卸载,默认注意力后端。 参考图像大约会使时间翻倍。参考 token 与文本和正在生成的图像加入同一序列,因此一张 1024 × 1024 的参考图会在每次 transformer 调用中增加 4,096 个 token Stacked bars of tokens per transformer call: text-to-image 4,256 tokens; image-to-image 8,272; layout text-to-image 4,336; layout image-to-image 8,800 每次 transformer 调用的 token 堆叠条形图:文生图 4,256 个 token;图生图 8,272;布局文生图 4,336;布局图生图 8,800 架构 让我们逐块拆解这个模型。该模型接收三种 token: 1. 文本 2. 正在生成的噪声图像 3. 以及可选的参考图像 从像素到 token FLUX.3 是一个用于图像生成的潜空间流模型。这意味着去噪器以潜变量为输入并输出潜变量。我们使用 VAE 将潜变量解码回像素。FLUX.3 使用与 Flux.2 相同的 VAE 架构。 FLUX.2 VAE 将图像在每个方向上压缩 8 倍,得到 32 个通道,然后该潜变量的每个 2 × 2 图块被打包成一个具有 128 个通道的 token。 因此,一个 token 覆盖 16 × 16 像素的图块,一张 1024 × 1024 的图像变成 64 × 64 的网格,共 4,096 个 token。带噪图像以恰好该形状的随机噪声开始。参考图像经过相同的 VAE 编码器和打包,并保持其自身尺寸。 从提示词到 token 文本编码器是 Qwen3-VL,一个视觉语言模型。FLUX.3 并不只取它的最后一层。它读取其 36 层中 八 层(第 4、8、…、32 层)的隐藏状态并将它们堆叠,因此每个文本 token 在通过一个线性层投影到 transformer 的宽度 3072 之前,变成一个 8 × 2560 = 20,480 维的向量。 这为 transformer 提供了来自语言模型多个深度的特征,而不仅仅是其最后一层。提示词被填充到 80 个 token 的倍数,并且 transformer 也会关注填充部分,这就是为什么该流水线每次调用只接受一个提示词。 两个阶段 transformer 分两个阶段处理 token: 1. 阶段 1:按模态分块。 每个流经过 5 个具有各自权重的块。文本有自己的块,带噪图像有自己的块,参考图像也有自己的块。 2. 阶段 2:联合块。 这些流被拼接成一个序列,[text | image | reference],该序列经过 28 个共享一组权重的块。 这两个阶段的区别在于 谁可以关注谁。 在阶段 1 中,每个流只关注自身,因此每种 token 首先按自身的方式被细化。在阶段 2 中,所有内容关注所有内容,图像从文本和参考中学习。作为对比,FLUX.2 的双流块也为文本和图像提供单独的权重,但让它们从一开始就相互关注。FLUX.3 首先将流完全分开,然后再合并它们。 最后,文本和参考 token 被直接丢弃。只有图像 token 经过输出层,输出层预测如何将它们向干净图像推进一步。 一个块的内部 无论哪个阶段,每个块都具有相同的形状。注意力和 MLP 并行 运行在相同的归一化输入上,它们的和被加回残差流。 每个块以一个 LayerNorm 开始,该 LayerNorm 没有自己的学习缩放或偏移。缩放、偏移和门控来自时间步嵌入(这被称为 调制)。在注意力内部,查询和键被 RMS 归一化并获得旋转位置嵌入(RoPE),我们将在下面讨论。 在联合块中,所有 token 共享相同的权重,但文本、图像和参考 token 各自获得自己的缩放、偏移和门控。 def block(x, shift, scale, gate): # shift, scale, gate come from the token's timestep embedding h = layer_norm(x) # no learned scale or shift h = (1 + scale) * h + shift q, k, v = to_q(h), to_k(h), to_v(h) q, k = rope(rms_norm(q)), rope(rms_norm(k)) out = attention(q, k, v) + mlp(h) # parallel, MLP is SwiGLU return x + gate * out 每个 token 都有自己的时间步 在 FLUX.1 和 FLUX.2 中,一个时间步嵌入调制每个 token。在 FLUX.3 中,调制取决于 token 的种类。 带噪图像 token 由当前时间步 t 调制。文本和参考 token 始终由时间步 0 调制,这意味着“干净,这里无需去噪”。因此,即使某个参考看起来与输出几乎相同,它也被标记为给定的上下文,而不是需要生成的内容。 蒸馏后的检查点是一个流映射。它也会被告知每一步跳转到的时间步。第二个时间步有自己的嵌入,会被加到每个 token 的调制中,因此它的 16 步中每一步都能做出更大的跳跃。 四个维度中的位置 注意力本身并不知道 token 在哪里(这一现象称为置换等变性)。RoPE 通过根据 token 的位置对每个 query 和 key 进行旋转来修正这一点。FLUX.3 使用四个位置轴,(t, h, w, l)(时间步、高度、宽度、序列长度),并将每个 128 维注意力头的四分之一(32 维)分配给每个轴: Token t:时间步 h:高度 w:宽度 l:序列长度 文本 token i 0 0 0 i 位于(行,列)的图像 token 0 行 / 2 列 / 2 0 位于(行,列)的参考 k 0 行 / 2 列 / 2 −(k + 1) 文本 token 沿 l 排列,就像句子中的单词。图像和参考是 (h, w) 中的二维网格,并沿 l 彼此分开。由于参考使用与输出相同的 (h, w) 坐标,“参考中的同一位置”对模型来说具有精确含义,这正是 src_bbox / tgt_bbox 编辑所依赖的。 去噪循环 FLUX.3 是一个流匹配模型。生成从纯噪声开始,在 50 步中的每一步,transformer 都会预测一个速度。这是从噪声指向干净图像的方向。调度器沿该方向前进一步。 在每一步,你还可以读出模型当前对最终图像的猜测。在几个步骤中解码该猜测,可以展示图像如何成形: step01 step05 step10 step15 step25 step50 第 1 步 第 5 步 第 10 步 第 15 步 第 25 步 第 50 步 到第 10 步时,狐狸、它的姿态和光线已经就位,而噪声水平仍为 0.97。覆雪的树枝和毛发的纹理只在最后几步才出现。FLUX.3 的调度器使用较大的时间偏移(6.93),这使噪声水平在大部分运行过程中保持较高:50 步中有 44 步发生在 σ = 0.5 以上。我们通过向 pipeline 添加一个小模块来构建这张图,这就引出了 Modular Diffusers。 如果你是流匹配的新手,我们有一个适合初学者的友好教程和一个视频,帮助你理解其工作原理。 Modular Diffusers FLUX.3 以 Modular Diffusers pipeline 的形式发布。 查看工作流 你可以列出工作流,并打印每个工作流的确切模块,而无需加载任何权重。 from diffusers import Flux3AutoBlocks blocks = Flux3AutoBlocks() print(blocks.available_workflows) # ['text2image', 'image_conditioned'] print(list(blocks.get_workflow("text2image").sub_blocks)) # ['text_encoder', 'denoise.input', 'denoise.prepare_latents', 'denoise.set_timesteps', # 'denoise.prepare_rope_inputs', 'denoise.denoise', 'denoise.after_denoise', 'decode'] get_workflow() 会解析自动分支,并给出将要运行的普通步骤序列。print(blocks.get_workflow("image_conditioned").doc) 会打印该工作流的组件、输入、默认值和输出。 编码一次,生成多次 弹出文本编码器,对提示编码一次,释放它,然后从缓存的嵌入生成。 import torch from diffusers import ModularPipeline model_id = "black-forest-labs/flux-3-image-klein-base" blocks = ModularPipeline.from_pretrained(model_id).blocks text_blocks = blocks.sub_blocks.pop("text_encoder") # `blocks` now starts at the VAE encoder text_pipe = text_blocks.init_pipeline(model_id) text_pipe.load_components(dtype=torch.bfloat16) text_pipe.to("cuda") state = text_pipe(prompt=prompt, negative_prompt="") # no guider here, so ask for the negative embeddings prompt_embeds = state.get("prompt_embeds") negative_prompt_embeds = state.get("negative_prompt_embeds") del text_pipe torch.cuda.empty_cache() image_pipe = blocks.init_pipeline(model_id) # no text encoder to load image_pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}) image_pipe.to("cuda") for seed in range(3): image = image_pipe( prompt_embeds=prompt_embeds, negative_prompt_embeds=negative_prompt_embeds, generator=torch.Generator().manual_seed(seed), output="images", )[0] image.save(f"{seed}.png") 在循环内添加一个步骤 上面的去噪图来自一个额外模块,它记录模型在每一步对干净图像的猜测: from diffusers.modular_pipelines import Flux3AutoBlocks, InputParam, ModularPipelineBlocks, OutputParam class Flux3PreviewStep(ModularPipelineBlocks): model_name = "flux3" @property def inputs(self): return [InputParam("latents"), InputParam("noise_pred")] @property def intermediate_outputs(self): return [OutputParam("x0_previews", type_hint=list)] @torch.no_grad() def __call__(self, components, block_state, i, t): if i == 0: block_state.x0_previews = [] sigma = t / 1000 # timesteps are sigmas scaled to [0, 1000] block_state.x0_previews.append(block_state.latents - sigma * block_state.noise_pred) # x0 = x_t - sigma * v return components, block_state blocks = Flux3AutoBlocks() denoise_loop = blocks.sub_blocks["denoise"].sub_blocks["text2image"].sub_blocks["denoise"] denoise_loop.sub_blocks.insert("preview", Flux3PreviewStep(), 1) # between "denoiser" and "after_denoiser" pipe = blocks.init_pipeline(model_id) pipe.load_components(dtype={"vae": torch.float32, "default": torch.bfloat16}) pipe.to("cuda") out = pipe(prompt=prompt, generator=torch.Generator().manual_seed(0), output=["x0_previews", "images"]) 试一试 - 模型: FLUX.3 Image Klein collection - 文档: FLUX.3 in Diffusers - 微调: DreamBooth LoRA scripts - 提示词: Black Forest Labs 提示词指南 用 FLUX.3 做了点什么?在 Hub 上分享你的 LoRA 和流水线吧。我们很想看看!😎

ai