@@ -905,6 +905,7 @@ def __len__(self):
905905 def __getitem__ (self , index ):
906906 example = {}
907907 instance_image , bucket_idx = self .pixel_values [index % self .num_instance_images ]
908+ example ["index" ] = index
908909 example ["instance_images" ] = instance_image
909910 example ["bucket_idx" ] = bucket_idx
910911 if self .custom_instance_prompts :
@@ -957,7 +958,10 @@ def train_transform(self, image, size=(224, 224), center_crop=False, random_flip
957958
958959
959960def collate_fn (examples , with_prior_preservation = False ):
961+ indices = [example ["index" ] for example in examples ]
960962 pixel_values = [example ["instance_images" ] for example in examples ]
963+ # Keep instance_prompts unchanged for prompt cache precompute; prompts may be extended with class prompts below.
964+ instance_prompts = [example ["instance_prompt" ] for example in examples ]
961965 prompts = [example ["instance_prompt" ] for example in examples ]
962966
963967 # Concat class and instance examples for prior preservation.
@@ -969,18 +973,17 @@ def collate_fn(examples, with_prior_preservation=False):
969973 pixel_values = torch .stack (pixel_values )
970974 pixel_values = pixel_values .to (memory_format = torch .contiguous_format ).float ()
971975
972- batch = {"pixel_values" : pixel_values , "prompts" : prompts }
976+ batch = {
977+ "indices" : indices ,
978+ "pixel_values" : pixel_values ,
979+ "instance_prompts" : instance_prompts ,
980+ "prompts" : prompts ,
981+ }
973982 return batch
974983
975984
976985class BucketBatchSampler (BatchSampler ):
977- def __init__ (
978- self ,
979- dataset : DreamBoothDataset ,
980- batch_size : int ,
981- drop_last : bool = False ,
982- shuffle_batches_each_epoch : bool = True ,
983- ):
986+ def __init__ (self , dataset : DreamBoothDataset , batch_size : int , drop_last : bool = False , seed : int = None ):
984987 if not isinstance (batch_size , int ) or batch_size <= 0 :
985988 raise ValueError ("batch_size should be a positive integer value, but got batch_size={}" .format (batch_size ))
986989 if not isinstance (drop_last , bool ):
@@ -989,37 +992,33 @@ def __init__(
989992 self .dataset = dataset
990993 self .batch_size = batch_size
991994 self .drop_last = drop_last
992- self .shuffle_batches_each_epoch = shuffle_batches_each_epoch
995+ self .generator = random . Random ( seed ) if seed is not None else random
993996
994997 # Group indices by bucket
995998 self .bucket_indices = [[] for _ in range (len (self .dataset .buckets ))]
996999 for idx , (_ , bucket_idx ) in enumerate (self .dataset .pixel_values ):
9971000 self .bucket_indices [bucket_idx ].append (idx )
9981001
9991002 self .sampler_len = 0
1000- self .batches = []
1003+ for indices_in_bucket in self .bucket_indices :
1004+ num_batches , remainder = divmod (len (indices_in_bucket ), self .batch_size )
1005+ self .sampler_len += num_batches
1006+ if remainder > 0 and not self .drop_last :
1007+ self .sampler_len += 1
10011008
1002- # Pre-generate batches for each bucket
1009+ def __iter__ (self ):
1010+ batches = []
10031011 for indices_in_bucket in self .bucket_indices :
1004- # Shuffle indices within the bucket
1005- random .shuffle (indices_in_bucket )
1006- # Create batches
1007- for i in range (0 , len (indices_in_bucket ), self .batch_size ):
1008- batch = indices_in_bucket [i : i + self .batch_size ]
1012+ shuffled_indices = indices_in_bucket .copy ()
1013+ self .generator .shuffle (shuffled_indices )
1014+ for i in range (0 , len (shuffled_indices ), self .batch_size ):
1015+ batch = shuffled_indices [i : i + self .batch_size ]
10091016 if len (batch ) < self .batch_size and self .drop_last :
1010- continue # Skip partial batch if drop_last is True
1011- self .batches .append (batch )
1012- self .sampler_len += 1 # Count the number of batches
1013-
1014- if not self .shuffle_batches_each_epoch :
1015- # Shuffle the precomputed batches once to mix buckets while keeping
1016- # the order stable across epochs for step-indexed caches.
1017- random .shuffle (self .batches )
1017+ continue
1018+ batches .append (batch )
10181019
1019- def __iter__ (self ):
1020- if self .shuffle_batches_each_epoch :
1021- random .shuffle (self .batches )
1022- for batch in self .batches :
1020+ self .generator .shuffle (batches )
1021+ for batch in batches :
10231022 yield batch
10241023
10251024 def __len__ (self ):
@@ -1480,13 +1479,8 @@ def load_model_hook(models, input_dir):
14801479 center_crop = args .center_crop ,
14811480 buckets = buckets ,
14821481 )
1483- has_step_indexed_caches = precompute_latents = args .cache_latents or train_dataset .custom_instance_prompts
1484- batch_sampler = BucketBatchSampler (
1485- train_dataset ,
1486- batch_size = args .train_batch_size ,
1487- drop_last = True ,
1488- shuffle_batches_each_epoch = not has_step_indexed_caches ,
1489- )
1482+ precompute_latents = args .cache_latents or train_dataset .custom_instance_prompts
1483+ batch_sampler = BucketBatchSampler (train_dataset , batch_size = args .train_batch_size , drop_last = True , seed = args .seed )
14901484 train_dataloader = torch .utils .data .DataLoader (
14911485 train_dataset ,
14921486 batch_sampler = batch_sampler ,
@@ -1599,32 +1593,72 @@ def _encode_single(prompt: str):
15991593 if args .with_prior_preservation :
16001594 prompt_embeds = torch .cat ([prompt_embeds , class_prompt_hidden_states ], dim = 0 )
16011595 text_ids = torch .cat ([text_ids , class_text_ids ], dim = 0 )
1596+ static_prompt_embeds = prompt_embeds
1597+ static_text_ids = text_ids
16021598
16031599 # if cache_latents is set to True, we encode images to latents and store them.
16041600 # Similar to pre-encoding in the case of a single instance prompt, if custom prompts are provided
16051601 # we encode them in advance as well.
1602+ if args .cache_latents :
1603+ instance_latents_cache = [None ] * train_dataset .num_instance_images
1604+ class_latents_cache = [None ] * train_dataset .num_instance_images if args .with_prior_preservation else None
1605+ if train_dataset .custom_instance_prompts :
1606+ prompt_embeds_cache = [None ] * train_dataset .num_instance_images
1607+ text_ids_cache = [None ] * train_dataset .num_instance_images
16061608 if precompute_latents :
1607- prompt_embeds_cache = []
1608- text_ids_cache = []
1609- latents_cache = []
1610- for batch in tqdm (train_dataloader , desc = "Caching latents" ):
1609+ cache_batch_sampler = BucketBatchSampler (
1610+ train_dataset , batch_size = args .train_batch_size , drop_last = False , seed = args .seed
1611+ )
1612+ cache_dataloader = torch .utils .data .DataLoader (
1613+ train_dataset ,
1614+ batch_sampler = cache_batch_sampler ,
1615+ collate_fn = lambda examples : collate_fn (examples , args .with_prior_preservation ),
1616+ num_workers = args .dataloader_num_workers ,
1617+ )
1618+ for batch in tqdm (cache_dataloader , desc = "Caching latents" ):
16111619 with torch .no_grad ():
1620+ sample_indices = batch ["indices" ]
16121621 if args .cache_latents :
16131622 with offload_models (vae , device = accelerator .device , offload = args .offload ):
16141623 batch ["pixel_values" ] = batch ["pixel_values" ].to (
16151624 accelerator .device , non_blocking = True , dtype = vae .dtype
16161625 )
1617- latents_cache .append (vae .encode (batch ["pixel_values" ]).latent_dist )
1626+ latents = vae .encode (batch ["pixel_values" ]).latent_dist .mode ()
1627+ if args .with_prior_preservation :
1628+ instance_latents , class_latents = torch .chunk (latents , 2 , dim = 0 )
1629+ else :
1630+ instance_latents = latents
1631+ for i , idx in enumerate (sample_indices ):
1632+ instance_latents_cache [idx ] = instance_latents [i : i + 1 ]
1633+ if args .with_prior_preservation :
1634+ class_latents_cache [idx ] = class_latents [i : i + 1 ]
16181635 if train_dataset .custom_instance_prompts :
16191636 if args .remote_text_encoder :
1620- prompt_embeds , text_ids = compute_remote_text_embeddings (batch ["prompts " ])
1637+ prompt_embeds , text_ids = compute_remote_text_embeddings (batch ["instance_prompts " ])
16211638 elif args .fsdp_text_encoder :
1622- prompt_embeds , text_ids = compute_text_embeddings (batch ["prompts" ], text_encoding_pipeline )
1639+ prompt_embeds , text_ids = compute_text_embeddings (
1640+ batch ["instance_prompts" ], text_encoding_pipeline
1641+ )
16231642 else :
16241643 with offload_models (text_encoding_pipeline , device = accelerator .device , offload = args .offload ):
1625- prompt_embeds , text_ids = compute_text_embeddings (batch ["prompts" ], text_encoding_pipeline )
1626- prompt_embeds_cache .append (prompt_embeds )
1627- text_ids_cache .append (text_ids )
1644+ prompt_embeds , text_ids = compute_text_embeddings (
1645+ batch ["instance_prompts" ], text_encoding_pipeline
1646+ )
1647+ for i , idx in enumerate (sample_indices ):
1648+ prompt_embeds_cache [idx ] = prompt_embeds [i : i + 1 ]
1649+ text_ids_cache [idx ] = text_ids [i : i + 1 ]
1650+
1651+ if args .cache_latents :
1652+ assert all (latents is not None for latents in instance_latents_cache ), "Latent cache has unfilled entries."
1653+ if args .with_prior_preservation :
1654+ assert all (latents is not None for latents in class_latents_cache ), (
1655+ "Class latent cache has unfilled entries."
1656+ )
1657+ if train_dataset .custom_instance_prompts :
1658+ assert all (embeds is not None for embeds in prompt_embeds_cache ), (
1659+ "Prompt embedding cache has unfilled entries."
1660+ )
1661+ assert all (ids is not None for ids in text_ids_cache ), "Text ID cache has unfilled entries."
16281662
16291663 # move back to cpu before deleting to ensure memory is freed see: https://github.com/huggingface/diffusers/issues/11376#issue-3008144624
16301664 if args .cache_latents :
@@ -1748,25 +1782,36 @@ def get_sigmas(timesteps, n_dim=4, dtype=torch.float32):
17481782 for epoch in range (first_epoch , args .num_train_epochs ):
17491783 transformer .train ()
17501784
1751- for step , batch in enumerate ( train_dataloader ) :
1785+ for batch in train_dataloader :
17521786 models_to_accumulate = [transformer ]
1787+ sample_indices = batch ["indices" ]
17531788 prompts = batch ["prompts" ]
17541789
17551790 with accelerator .accumulate (models_to_accumulate ):
17561791 if train_dataset .custom_instance_prompts :
1757- prompt_embeds = prompt_embeds_cache [step ]
1758- text_ids = text_ids_cache [step ]
1792+ prompt_embeds = torch .cat ([prompt_embeds_cache [idx ] for idx in sample_indices ], dim = 0 )
1793+ text_ids = torch .cat ([text_ids_cache [idx ] for idx in sample_indices ], dim = 0 )
1794+ if args .with_prior_preservation :
1795+ prompt_embeds = torch .cat (
1796+ [prompt_embeds , class_prompt_hidden_states .repeat (len (sample_indices ), 1 , 1 )], dim = 0
1797+ )
1798+ text_ids = torch .cat ([text_ids , class_text_ids .repeat (len (sample_indices ), 1 , 1 )], dim = 0 )
17591799 else :
17601800 # With prior preservation, prompt_embeds/text_ids already contain [instance, class] entries,
17611801 # while collate_fn orders batches as [inst1..instB, class1..classB]. Repeat each entry along
17621802 # dim 0 to preserve that grouping instead of interleaving [inst, class, inst, class, ...].
17631803 num_repeat_elements = len (prompts ) // 2 if args .with_prior_preservation else len (prompts )
1764- prompt_embeds = prompt_embeds .repeat_interleave (num_repeat_elements , dim = 0 )
1765- text_ids = text_ids .repeat_interleave (num_repeat_elements , dim = 0 )
1804+ prompt_embeds = static_prompt_embeds .repeat_interleave (num_repeat_elements , dim = 0 )
1805+ text_ids = static_text_ids .repeat_interleave (num_repeat_elements , dim = 0 )
17661806
17671807 # Convert images to latent space
17681808 if args .cache_latents :
1769- model_input = latents_cache [step ].mode ()
1809+ model_input = torch .cat ([instance_latents_cache [idx ] for idx in sample_indices ], dim = 0 )
1810+ if args .with_prior_preservation :
1811+ model_input = torch .cat (
1812+ [model_input , torch .cat ([class_latents_cache [idx ] for idx in sample_indices ], dim = 0 )],
1813+ dim = 0 ,
1814+ )
17701815 else :
17711816 with offload_models (vae , device = accelerator .device , offload = args .offload ):
17721817 pixel_values = batch ["pixel_values" ].to (device = accelerator .device , dtype = vae .dtype )
0 commit comments