Skip to content

Commit 8fe8db9

Browse files
[Train] Support qwen dmd-lora training (#1076)
Co-authored-by: helloyongyang <yongyang1030@163.com>
1 parent 524de26 commit 8fe8db9

7 files changed

Lines changed: 657 additions & 5 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
model:
2+
name: qwen_image
3+
pretrained_model_name_or_path: /path/to/Qwen/Qwen-Image
4+
max_sequence_length: 1024
5+
running_dtype: bf16
6+
7+
data:
8+
train:
9+
name: image_dataset
10+
num_workers: 8
11+
prompt_dropout_rate: 0.0
12+
target_area: 1048576 # 1024 * 1024
13+
shuffle: true
14+
data_path:
15+
- /path/to/LightX2V_train_data_examples/dataset_v1/train.jsonl
16+
val:
17+
name: image_dataset
18+
num_workers: 8
19+
shuffle: false
20+
data_path:
21+
- /path/to/LightX2V_train_data_examples/dataset_v1/val.jsonl
22+
23+
scheduler:
24+
num_train_timesteps: 1000
25+
time_shift_settings:
26+
do_time_shift: true
27+
shift_type: linear
28+
# shift function: "linear" => mu/(mu+(1/t-1)^p), "exponential" => exp(mu)/(exp(mu)+(1/t-1)^p)
29+
time_shift_power: 1.0
30+
dynamic_shift: false
31+
time_shift_mu: 3.0
32+
33+
training:
34+
method: dmd_lora
35+
max_train_iters: 1000
36+
gradient_accumulation_iters: 1
37+
gradient_checkpointing: true
38+
max_grad_norm: 1.0
39+
lr_scheduler: constant
40+
lr_warmup_iters: 10
41+
save_every_iters: 100
42+
save_total_limit: 10
43+
dmd:
44+
num_inference_steps: 4
45+
fake_update_ratio: 2
46+
image_sizes:
47+
- [1024, 1024]
48+
- [768, 1344]
49+
- [1344, 768]
50+
renoise_sigma_min: 0.02
51+
renoise_sigma_max: 1.0
52+
renoise_discrete_samples: 1000
53+
renoise_shift: 5.0
54+
lora:
55+
rank: 32
56+
alpha: 32
57+
target_modules:
58+
- to_k
59+
- to_q
60+
- to_v
61+
- to_out.0
62+
# - add_q_proj
63+
# - add_k_proj
64+
# - add_v_proj
65+
# - to_add_out
66+
# - img_mlp.net.0.proj
67+
# - img_mlp.net.2
68+
# - txt_mlp.net.0.proj
69+
# - txt_mlp.net.2
70+
student:
71+
optimizer:
72+
learning_rate: 0.0001
73+
adam_beta1: 0.9
74+
adam_beta2: 0.999
75+
weight_decay: 0.001
76+
adam_epsilon: 0.00000001
77+
fake:
78+
optimizer:
79+
learning_rate: 0.00002
80+
adam_beta1: 0.9
81+
adam_beta2: 0.999
82+
weight_decay: 0.001
83+
adam_epsilon: 0.00000001
84+
teacher:
85+
guidance_scale: 4.0
86+
negative_prompt: " "
87+
cfg_norm: layer_norm
88+
output_dir: ./output_train/qwen_image_dmd_lora
89+
90+
inference:
91+
method: image_infer
92+
default_width: 1024
93+
default_height: 1024
94+
num_inference_steps: 4
95+
cfg_guidance_scale: 4.0
96+
negative_prompt: " "
97+
enable_cfg: false
98+
output_dir: ./output_infer/qwen_image_dmd_lora
99+
infer_every_iters: ${training.save_every_iters}
100+
101+
logging:
102+
rank_zero_only: true
103+
train_log_every_iters: 10
104+
infer_log_every_steps: 10
105+
106+
resume:
107+
auto_resume: true

lightx2v_train/lightx2v_train/model_zoo/qwen_image.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,15 @@ class QwenImageModel(BaseModel):
2626

2727
pipeline_cls = QwenImagePipeline
2828

29-
def load_components(self):
29+
def load_components(self, transformer_only=False, reference_model=None):
30+
if transformer_only:
31+
if reference_model is not None:
32+
self.text_pipeline = reference_model.text_pipeline
33+
self.vae = reference_model.vae
34+
self.vae_scale_factor = reference_model.vae_scale_factor
35+
self.image_processor = reference_model.image_processor
36+
self.transformer = self.load_transformer()
37+
return
3038
model_path = self.config["model"]["pretrained_model_name_or_path"]
3139
self.text_pipeline = QwenImagePipeline.from_pretrained(
3240
model_path,
@@ -35,13 +43,17 @@ def load_components(self):
3543
torch_dtype=self.running_dtype,
3644
).to(self.device)
3745
self.vae = AutoencoderKLQwenImage.from_pretrained(model_path, subfolder="vae").to(self.device, dtype=self.running_dtype)
38-
self.transformer = QwenImageTransformer2DModel.from_pretrained(model_path, subfolder="transformer").to(self.device, dtype=self.running_dtype)
46+
self.transformer = self.load_transformer()
3947

4048
self.text_pipeline.text_encoder.requires_grad_(False)
4149
self.vae.requires_grad_(False)
4250
self.vae_scale_factor = 2 ** len(self.vae.temperal_downsample)
4351
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
4452

53+
def load_transformer(self):
54+
model_path = self.config["model"]["pretrained_model_name_or_path"]
55+
return QwenImageTransformer2DModel.from_pretrained(model_path, subfolder="transformer").to(self.device, dtype=self.running_dtype)
56+
4557
def denoiser_module(self):
4658
return self.transformer
4759

@@ -69,6 +81,9 @@ def encode_to_latent(self, sample):
6981

7082
def encode_condition(self, sample):
7183
prompt = sample["prompt"]
84+
return self.encode_prompt_condition(prompt)
85+
86+
def encode_prompt_condition(self, prompt):
7287
prompt_embed, prompt_embed_mask = self.text_pipeline.encode_prompt(
7388
prompt=prompt,
7489
device=self.device,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .dmd_scheduler import DMDFlowMatchingScheduler
2+
from .flow_matching import RectifiedFlowMatchingScheduler
3+
4+
__all__ = ["DMDFlowMatchingScheduler", "RectifiedFlowMatchingScheduler"]
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import torch
2+
3+
from .flow_matching import RectifiedFlowMatchingScheduler
4+
5+
6+
class DMDFlowMatchingScheduler(RectifiedFlowMatchingScheduler):
7+
def __init__(self, config, dmd_config={}):
8+
super().__init__(config)
9+
self.renoise_shift = float(dmd_config.get("renoise_shift", 5.0))
10+
self.renoise_sigma_min = float(dmd_config.get("renoise_sigma_min", dmd_config.get("sigma_min", 0.02)))
11+
self.renoise_sigma_max = float(dmd_config.get("renoise_sigma_max", dmd_config.get("sigma_max", 1.0)))
12+
self.renoise_discrete_samples = int(dmd_config.get("renoise_discrete_samples", dmd_config.get("discrete_samples", 1000)))
13+
14+
@staticmethod
15+
def linear_shift(mu, t):
16+
return mu / (mu + (1 / t - 1))
17+
18+
def set_timesteps(self, num_inference_steps, sigmas=None, latent_hw=None, device=None):
19+
super().set_timesteps(num_inference_steps, sigmas=sigmas, latent_hw=latent_hw)
20+
if device is not None:
21+
self.infer_sigmas = self.infer_sigmas.to(device)
22+
self.infer_timesteps = self.infer_timesteps.to(device)
23+
self.sigmas = self.infer_sigmas
24+
self.timesteps = self.infer_timesteps
25+
26+
def sigma_at(self, step_idx, batch_size, device=None, dtype=None):
27+
sigma = self.sigmas[int(step_idx)].expand(int(batch_size))
28+
if device is not None or dtype is not None:
29+
sigma = sigma.to(device=device, dtype=dtype)
30+
return sigma
31+
32+
def sample_renoise_sigma(self, batch_size, device=None, dtype=None):
33+
device = device or self.device
34+
raw = torch.rand((int(batch_size),), device=device, dtype=torch.float32)
35+
if self.renoise_discrete_samples > 0:
36+
raw = torch.ceil(raw * self.renoise_discrete_samples) / self.renoise_discrete_samples
37+
raw = torch.clamp(raw, 1e-7, 1 - 1e-7)
38+
sigma = torch.clamp(self.linear_shift(self.renoise_shift, raw), self.renoise_sigma_min, self.renoise_sigma_max)
39+
if dtype is not None:
40+
sigma = sigma.to(dtype=dtype)
41+
return sigma
42+
43+
def add_noise(self, latent, noise, sigmas):
44+
sigmas = sigmas.to(device=latent.device)
45+
sigmas = self._expand_to_ndim(sigmas, latent.ndim)
46+
return ((1.0 - sigmas) * latent + sigmas * noise).to(dtype=latent.dtype)
47+
48+
def step_by_index(self, velocity, step_idx, sample):
49+
sigma = self.sigma_at(step_idx, sample.shape[0], device=sample.device)
50+
sigma_next = self.sigma_at(int(step_idx) + 1, sample.shape[0], device=sample.device)
51+
sigma = self._expand_to_ndim(sigma, sample.ndim)
52+
sigma_next = self._expand_to_ndim(sigma_next, sample.ndim)
53+
next_sample = sample + (sigma_next - sigma) * velocity
54+
x0 = sample - sigma * velocity
55+
return next_sample.to(sample.dtype), x0.to(sample.dtype)
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from lightx2v_train.utils.registry import build_trainer
22

3+
from .dmd_lora import DmdLoraTrainer
34
from .lora import LoraTrainer
45

5-
__all__ = ["build_trainer", "LoraTrainer"]
6+
__all__ = ["build_trainer", "DmdLoraTrainer", "LoraTrainer"]

0 commit comments

Comments
 (0)