[LoRA] add LoRA training for Qwen Image 2.1 - #14808
linoytsaban wants to merge 6 commits into
Conversation
Add DreamBooth LoRA training for Qwen-Image 2.1, text-to-image and image-to-image, with fast tests and a README section. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qnMKe5MVc7B4XXHGPvXuZ
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
`--rank` and `--lora_alpha` are independent arguments, so raising the rank alone leaves the update scaled by `lora_alpha / rank`. Default both to 16, which keeps the scale at 1, and add a README section explaining the ratio. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qnMKe5MVc7B4XXHGPvXuZ
There was a problem hiding this comment.
🟡 Changes recommended
One or more issues must be addressed before approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds DreamBooth LoRA training for Qwen-Image 2.1 text-to-image and image-to-image workflows.
Changes:
- Added both training scripts with caching, bucketing, quantization, validation, and checkpointing.
- Added smoke tests for LoRA output, metadata, caching, and aspect-ratio buckets.
- Added usage documentation.
File summaries
| File | Description |
|---|---|
| examples/dreambooth/train_dreambooth_lora_qwenimage21.py | Updated as part of this pull request. |
| examples/dreambooth/train_dreambooth_lora_qwenimage21_img2img.py | Updated as part of this pull request. |
| examples/dreambooth/test_dreambooth_lora_qwenimage21.py | Updated as part of this pull request. |
| examples/dreambooth/test_dreambooth_lora_qwenimage21_img2img.py | Updated as part of this pull request. |
| examples/dreambooth/README_qwenimage21.md | Updated as part of this pull request. |
Review details
Suppressed comments (3)
examples/dreambooth/train_dreambooth_lora_qwenimage21.py:1772
- When
--cache_latentsis not enabled,pixel_valuesremains on the CPU, whilevaehas been moved toaccelerator.device(andoffload_modelsrestores it there). This makesvae.encode(pixel_values)fail with a device-mismatch error in the default training path. Move the batch toaccelerator.devicehere, as is already done for the cached path.
with offload_models(vae, device=accelerator.device, offload=args.offload):
pixel_values = batch["pixel_values"].to(dtype=vae.dtype)
model_input = vae.encode(pixel_values).latent_dist.sample()
examples/dreambooth/train_dreambooth_lora_qwenimage21_img2img.py:1852
- The target tensor is not moved to
accelerator.device, unlike the condition tensor below it. With the normal GPU setup (and no latent cache),vaeis on the accelerator device andvae.encode(pixel_values)therefore fails due to the CPU/GPU mismatch. Adddevice=accelerator.deviceto this transfer.
with offload_models(vae, device=accelerator.device, offload=args.offload):
pixel_values = batch["pixel_values"].to(dtype=vae.dtype)
cond_pixel_values = batch["cond_pixel_values"].to(device=accelerator.device, dtype=vae.dtype)
model_input = vae.encode(pixel_values).latent_dist.sample()
cond_model_input = vae.encode(cond_pixel_values).latent_dist.mode()
examples/dreambooth/train_dreambooth_lora_qwenimage21_img2img.py:1657
- The final-only validation option is not encoded here: this block runs only when
--validation_promptis set. If a user supplies only--final_validation_prompt, the later final-inference path still runs but passes no prompt embeddings to a pipeline whose text encoder isNone, so final validation fails instead of generating an image.
validation_pipeline_args = {}
validation_image_pad_mask = None
if args.validation_prompt is not None:
if args.validation_image is None:
raise ValueError("`--validation_prompt` needs `--validation_image`, the image the edit is applied to.")
validation_image = load_image(args.validation_image)
# Encoded at the size the pipeline will resize to, so vision tokens and latents line up.
width, height, _ = calculate_dimensions(
args.resolution * args.resolution, validation_image.size[0] / validation_image.size[1]
)
resized_validation_image = validation_image.resize((width, height))
with offload_models(text_encoding_pipeline, device=accelerator.device, offload=args.offload):
embeds, embeds_mask, image_pad_mask = compute_text_embeddings(
args.validation_prompt, text_encoding_pipeline, resized_validation_image
)
validation_pipeline_args = {
"prompt_embeds": embeds,
"prompt_embeds_mask": embeds_mask,
"image": validation_image,
- Files reviewed: 5/5 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Can we add:
|
|
Need to also fix the failing tests (for examples). |
… runs Two fixes from review: `QwenImage21ValidationPipeline` substituted the cached image-pad mask after calling the base `encode_prompt`, but that call raises for a missing mask when it is handed `prompt_embeds` together with a condition image, so the substitution never ran and image-to-image validation failed. Pass the mask in before the call instead. `--final_validation_prompt` was accepted by the final-inference guard, but the prompt embeddings were only built under `--validation_prompt`, so the final pass reached a pipeline whose text encoder is `None` with no prompt at all. Build the embeddings for whichever prompt is set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qnMKe5MVc7B4XXHGPvXuZ
`vae.encode` sat outside the `offload_models` block, so the context manager had
already moved the VAE back to the CPU by the time it ran, while the prepared
dataloader hands the batch over on the accelerator:
RuntimeError: Input type (torch.cuda.FloatTensor) and weight type
(torch.FloatTensor) should be the same
Move the call inside the block and cover that flag combination, which is the
only path that encodes pixels inside the training loop. The image-to-image
trainer already had it right.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qnMKe5MVc7B4XXHGPvXuZ
Follow the Flux layout and reuse `LoraTesterMixin` and `LoraMemoryTesterMixin`, so the adapters these trainers produce are covered by the pipeline's own loading, fusing and memory-offload tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011qnMKe5MVc7B4XXHGPvXuZ
|
@sayakpaul re:the failing checks:
Example tests (CPU + CUDA) - |
|
Can we fix the failing tests in https://github.com/huggingface/diffusers/actions/runs/35349539904/job/105614536678?pr=14808 and https://github.com/huggingface/diffusers/actions/runs/35349539897/job/105614547820?pr=14808? For failing LoRA test, does using a higher tolerance help? I think we also need to verify the effectiveness with one successful run for each of T2I and I2I? |
add t2i and i2i lora training scripts for Qwen Image 2.1