Skip to content

Commit 932628f

Browse files
Merge pull request #417 from AI-Hypercomputer:prisha/flux_training
PiperOrigin-RevId: 943350888
2 parents 0984457 + 25f1a08 commit 932628f

11 files changed

Lines changed: 594 additions & 246 deletions

File tree

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,12 +398,32 @@ After installation completes, run the training script.
398398

399399
| Model | Accelerator | Sharding Strategy | Per Device Batch Size | Global Batch Size | Step Time (secs) |
400400
| --- | --- | --- | --- | --- | --- |
401-
| Flux-dev | v5p-8 | DDP | 1 | 4 | 1.31 |
401+
| Flux-dev | v5p-8 | FSDP | 2 | 8 | 1.769 |
402402

403403
Flux finetuning has only been tested on TPU v5p.
404404

405-
```bash
406-
python src/maxdiffusion/train_flux.py src/maxdiffusion/configs/base_flux_dev.yml run_name="test-flux-train" output_dir="gs://<your-gcs-bucket>/" save_final_checkpoint=True jax_cache_dir="/tmp/jax_cache"
405+
To run the Flux training benchmark on v5p-8, use:
406+
407+
```bash
408+
python src/maxdiffusion/train_flux.py src/maxdiffusion/configs/base_flux_dev.yml \
409+
run_name="flux-training" \
410+
output_dir="gs://<your-gcs-bucket>/" \
411+
jax_cache_dir="/tmp/jax_cache" \
412+
save_final_checkpoint=False \
413+
max_train_steps=100 \
414+
dataset_type=synthetic \
415+
ici_data_parallelism=1 \
416+
ici_fsdp_parallelism=4 \
417+
ici_tensor_parallelism=1 \
418+
train_new_flux=True \
419+
resolution=1024 \
420+
attention_sharding_uniform=False \
421+
attention=tokamax_flash \
422+
per_device_batch_size=2 \
423+
enable_profiler=False \
424+
reuse_example_batch=True \
425+
write_metrics=False \
426+
use_base2_exp=True
407427
```
408428

409429
To generate images with a finetuned checkpoint, run:

src/maxdiffusion/checkpointing/flux_checkpointer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ def load_diffusers_checkpoint(self):
214214
dtype=self.config.activations_dtype,
215215
weights_dtype=self.config.weights_dtype,
216216
precision=max_utils.get_precision(self.config),
217+
use_base2_exp=self.config.use_base2_exp,
218+
use_experimental_scheduler=self.config.use_experimental_scheduler,
219+
remat_policy=self.config.remat_policy,
220+
names_which_can_be_saved=self.config.names_which_can_be_saved,
221+
names_which_can_be_offloaded=self.config.names_which_can_be_offloaded,
217222
)
218223
transformer_eval_params = transformer.init_weights(
219224
rngs=self.rng, max_sequence_length=self.config.max_sequence_length, eval_only=True
@@ -279,6 +284,11 @@ def load_checkpoint(self, step=None, scheduler_class=None):
279284
weights_dtype=self.config.weights_dtype,
280285
precision=max_utils.get_precision(self.config),
281286
from_pt=self.config.from_pt,
287+
use_base2_exp=self.config.use_base2_exp,
288+
use_experimental_scheduler=self.config.use_experimental_scheduler,
289+
remat_policy=self.config.remat_policy,
290+
names_which_can_be_saved=self.config.names_which_can_be_saved,
291+
names_which_can_be_offloaded=self.config.names_which_can_be_offloaded,
282292
)
283293

284294
pipeline = FluxPipeline(

src/maxdiffusion/configs/base_flux_dev.yml

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ jit_initializers: True
6363
from_pt: True
6464
split_head_dim: True
6565
attention: 'flash' # Supported attention: dot_product, flash, cudnn_flash_te
66+
use_base2_exp: False
67+
use_experimental_scheduler: False
6668
# If mask_padding_tokens is True, we pass in segment ids to splash attention to avoid attending to padding tokens.
6769
# Else we do not pass in segment ids and on vpu bound hardware like trillium this is faster.
6870
# However, when padding tokens are significant, this will lead to worse quality and should be set to True.
@@ -73,18 +75,18 @@ mask_padding_tokens: True
7375
# in cross attention q.
7476
attention_sharding_uniform: True
7577

76-
flash_block_sizes: {}
78+
#flash_block_sizes: {}
7779
# Use the following flash_block_sizes on v6e (Trillium) due to larger vmem.
78-
# flash_block_sizes: {
79-
# "block_q" : 1536,
80-
# "block_kv_compute" : 1536,
81-
# "block_kv" : 1536,
82-
# "block_q_dkv" : 1536,
83-
# "block_kv_dkv" : 1536,
84-
# "block_kv_dkv_compute" : 1536,
85-
# "block_q_dq" : 1536,
86-
# "block_kv_dq" : 1536
87-
# }
80+
flash_block_sizes: {
81+
"block_q" : 1536,
82+
"block_kv_compute" : 1536,
83+
"block_kv" : 1536,
84+
"block_q_dkv" : 1536,
85+
"block_kv_dkv" : 1536,
86+
"block_kv_dkv_compute" : 1536,
87+
"block_q_dq" : 1536,
88+
"block_kv_dq" : 1536
89+
}
8890
# GroupNorm groups
8991
norm_num_groups: 32
9092

@@ -147,9 +149,11 @@ mesh_axes: ['data', 'fsdp', 'context', 'tensor']
147149
# conv_in : conv.shape[2] weight
148150
# conv_out : conv.shape[-1] weight
149151
logical_axis_rules: [
150-
['batch', 'data'],
152+
['batch', ['data','fsdp']],
151153
['activation_batch', ['data','fsdp']],
152154
['activation_heads', 'tensor'],
155+
['activation_length', 'context'],
156+
['activation_kv_length', 'context'],
153157
['activation_kv', 'tensor'],
154158
['mlp','tensor'],
155159
['embed','fsdp'],
@@ -188,7 +192,7 @@ dataset_type: 'tfrecord' # Options: 'tfrecord', 'hf', 'tf', 'grain', 'synthetic
188192
# 2. Optionally set synthetic_num_samples (null=infinite, or a number like 10000)
189193
# 3. Optionally override dimensions
190194
#
191-
# synthetic_num_samples: null # null for infinite, or set a number
195+
synthetic_num_samples: 1000 # null for infinite, or set a number
192196
#
193197
# Optional dimension overrides:
194198
# resolution: 512
@@ -218,6 +222,21 @@ transform_images_num_proc: 4
218222
reuse_example_batch: False
219223
enable_data_shuffling: True
220224

225+
# Defines the type of gradient checkpoint to enable.
226+
# NONE - means no gradient checkpoint
227+
# FULL - means full gradient checkpoint, whenever possible (minimum memory usage)
228+
# MATMUL_WITHOUT_BATCH - means gradient checkpoint for every linear/matmul operation,
229+
# except for ones that involve batch dimension - that means that all attention and projection
230+
# layers will have gradient checkpoint, but not the backward with respect to the parameters.
231+
# OFFLOAD_MATMUL_WITHOUT_BATCH - same as MATMUL_WITHOUT_BATCH but offload instead of recomputing.
232+
# CUSTOM - set names to offload and save.
233+
remat_policy: "FLUX_OPTIMIZED"
234+
# For CUSTOM policy set below, current annotations are for: attn_output, query_proj, key_proj, value_proj
235+
# xq_out, xk_out, ffn_activation
236+
names_which_can_be_saved: []
237+
names_which_can_be_offloaded: []
238+
flash_min_seq_length: 0
239+
221240
# checkpoint every number of samples, -1 means don't checkpoint.
222241
checkpoint_every: -1
223242
# enables one replica to read the ckpt then broadcast to the rest

src/maxdiffusion/generate_flux.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,9 @@ def run(config):
314314
dtype=config.activations_dtype,
315315
weights_dtype=config.weights_dtype,
316316
precision=get_precision(config),
317+
remat_policy=config.remat_policy,
318+
names_which_can_be_saved=config.names_which_can_be_saved,
319+
names_which_can_be_offloaded=config.names_which_can_be_offloaded,
317320
)
318321

319322
num_channels_latents = transformer.in_channels // 4

src/maxdiffusion/models/attention_flax.py

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,6 +2283,8 @@ class FlaxFluxAttention(nn.Module):
22832283
out_axis_names: AxisNames = (BATCH, LENGTH, EMBED)
22842284
precision: jax.lax.Precision = None
22852285
qkv_bias: bool = False
2286+
use_base2_exp: bool = False
2287+
use_experimental_scheduler: bool = False
22862288

22872289
def setup(self):
22882290
if self.attention_kernel in {"flash", "cudnn_flash_te"} and self.mesh is None:
@@ -2302,6 +2304,8 @@ def setup(self):
23022304
flash_block_sizes=self.flash_block_sizes,
23032305
dtype=self.dtype,
23042306
float32_qk_product=False,
2307+
use_base2_exp=self.use_base2_exp,
2308+
use_experimental_scheduler=self.use_experimental_scheduler,
23052309
)
23062310

23072311
kernel_axes = ("embed", "heads")
@@ -2382,41 +2386,59 @@ def __call__(
23822386
attention_mask=None,
23832387
image_rotary_emb=None,
23842388
):
2385-
qkv_proj = self.qkv(hidden_states)
23862389
B, L = hidden_states.shape[:2]
2387-
H, D, K = self.heads, qkv_proj.shape[-1] // (self.heads * 3), 3
2388-
qkv_proj = qkv_proj.reshape(B, L, K, H, D).transpose(2, 0, 3, 1, 4)
2389-
query_proj, key_proj, value_proj = qkv_proj
2390+
# Deduce dimensions cleanly from class attributes
2391+
H, D = self.heads, self.dim_head
23902392

2391-
query_proj = self.query_norm(query_proj)
2393+
qkv_proj = self.qkv(hidden_states)
2394+
qkv_proj = checkpoint_name(qkv_proj, "img_qkv_proj")
2395+
2396+
qkv_proj = qkv_proj.reshape(B, L, 3, H, D)
2397+
query_proj, key_proj, value_proj = jnp.split(qkv_proj, 3, axis=2)
2398+
query_proj = query_proj.squeeze(2)
2399+
key_proj = key_proj.squeeze(2)
2400+
value_proj = value_proj.squeeze(2)
23922401

2402+
query_proj = self.query_norm(query_proj)
23932403
key_proj = self.key_norm(key_proj)
23942404

23952405
if encoder_hidden_states is not None:
2406+
B_enc, L_txt = encoder_hidden_states.shape[:2]
23962407
encoder_qkv_proj = self.encoder_qkv(encoder_hidden_states)
2397-
B, L = encoder_hidden_states.shape[:2]
2398-
H, D, K = self.heads, encoder_qkv_proj.shape[-1] // (self.heads * 3), 3
2399-
encoder_qkv_proj = encoder_qkv_proj.reshape(B, L, K, H, D).transpose(2, 0, 3, 1, 4)
2400-
encoder_query_proj, encoder_key_proj, encoder_value_proj = encoder_qkv_proj
2408+
encoder_qkv_proj = checkpoint_name(encoder_qkv_proj, "txt_qkv_proj")
2409+
encoder_qkv_proj = encoder_qkv_proj.reshape(B_enc, L_txt, 3, H, D)
2410+
enc_query_proj, enc_key_proj, enc_value_proj = jnp.split(encoder_qkv_proj, 3, axis=2)
2411+
enc_query_proj = enc_query_proj.squeeze(2)
2412+
enc_key_proj = enc_key_proj.squeeze(2)
2413+
enc_value_proj = enc_value_proj.squeeze(2)
24012414

2402-
encoder_query_proj = self.encoder_query_norm(encoder_query_proj)
2415+
encoder_query_proj = self.encoder_query_norm(enc_query_proj)
2416+
encoder_key_proj = self.encoder_key_norm(enc_key_proj)
24032417

2404-
encoder_key_proj = self.encoder_key_norm(encoder_key_proj)
2418+
query_proj = jnp.concatenate((encoder_query_proj, query_proj), axis=1)
2419+
key_proj = jnp.concatenate((encoder_key_proj, key_proj), axis=1)
2420+
value_proj = jnp.concatenate((enc_value_proj, value_proj), axis=1)
24052421

2406-
query_proj = jnp.concatenate((encoder_query_proj, query_proj), axis=2)
2407-
key_proj = jnp.concatenate((encoder_key_proj, key_proj), axis=2)
2408-
value_proj = jnp.concatenate((encoder_value_proj, value_proj), axis=2)
2409-
2410-
query_proj = nn.with_logical_constraint(query_proj, self.query_axis_names)
2411-
key_proj = nn.with_logical_constraint(key_proj, self.key_axis_names)
2412-
value_proj = nn.with_logical_constraint(value_proj, self.value_axis_names)
2422+
# query_proj = nn.with_logical_constraint(query_proj, self.query_axis_names)
2423+
# key_proj = nn.with_logical_constraint(key_proj, self.key_axis_names)
2424+
# value_proj = nn.with_logical_constraint(value_proj, self.value_axis_names)
24132425

24142426
image_rotary_emb = rearrange(image_rotary_emb, "n d (i j) -> n d i j", i=2, j=2)
2427+
2428+
query_proj = query_proj.swapaxes(1, 2)
2429+
key_proj = key_proj.swapaxes(1, 2)
24152430
query_proj, key_proj = apply_rope(query_proj, key_proj, image_rotary_emb)
2431+
query_proj = query_proj.swapaxes(1, 2)
2432+
key_proj = key_proj.swapaxes(1, 2)
2433+
2434+
query_proj = query_proj.reshape(B, -1, H * D)
2435+
key_proj = key_proj.reshape(B, -1, H * D)
2436+
value_proj = value_proj.reshape(B, -1, H * D)
24162437

2417-
query_proj = query_proj.transpose(0, 2, 1, 3).reshape(query_proj.shape[0], query_proj.shape[2], -1)
2418-
key_proj = key_proj.transpose(0, 2, 1, 3).reshape(key_proj.shape[0], key_proj.shape[2], -1)
2419-
value_proj = value_proj.transpose(0, 2, 1, 3).reshape(value_proj.shape[0], value_proj.shape[2], -1)
2438+
if encoder_hidden_states is not None:
2439+
query_proj = nn.with_logical_constraint(query_proj, self.query_axis_names)
2440+
key_proj = nn.with_logical_constraint(key_proj, self.key_axis_names)
2441+
value_proj = nn.with_logical_constraint(value_proj, self.value_axis_names)
24202442

24212443
attn_output = self.attention_op.apply_attention(query_proj, key_proj, value_proj, attention_mask=attention_mask)
24222444
context_attn_output = None

0 commit comments

Comments
 (0)