Add extra performance features for EMAModel, torch._foreach operations and better support for non-blocking CPU offloading (#7685)
* Add support for _foreach operations and non-blocking to EMAModel * default foreach to false * add non-blocking EMA offloading to SD1.5 T2I example script * fix whitespace * move foreach to cli argument * linting * Update README.md re: EMA weight training * correct args.foreach_ema * add tests for foreach ema * code quality * add foreach to from_pretrained * default foreach false * fix linting --------- Co-authored-by: Sayak Paul <spsayakpaul@gmail.com> Co-authored-by: drhead <a@a.a>
This commit is contained in:
@@ -170,11 +170,19 @@ For our small Narutos dataset, the effects of Min-SNR weighting strategy might n
|
||||
|
||||
Also, note that in this example, we either predict `epsilon` (i.e., the noise) or the `v_prediction`. For both of these cases, the formulation of the Min-SNR weighting strategy that we have used holds.
|
||||
|
||||
|
||||
#### Training with EMA weights
|
||||
|
||||
Through the `EMAModel` class, we support a convenient method of tracking an exponential moving average of model parameters. This helps to smooth out noise in model parameter updates and generally improves model performance. If enabled with the `--use_ema` argument, the final model checkpoint that is saved at the end of training will use the EMA weights.
|
||||
|
||||
EMA weights require an additional full-precision copy of the model parameters to be stored in memory, but otherwise have very little performance overhead. `--foreach_ema` can be used to further reduce the overhead. If you are short on VRAM and still want to use EMA weights, you can store them in CPU RAM by using the `--offload_ema` argument. This will keep the EMA weights in pinned CPU memory during the training step. Then, once every model parameter update, it will transfer the EMA weights back to the GPU which can then update the parameters on the GPU, before sending them back to the CPU. Both of these transfers are set up as non-blocking, so CUDA devices should be able to overlap this transfer with other computations. With sufficient bandwidth between the host and device and a sufficiently long gap between model parameter updates, storing EMA weights in CPU RAM should have no additional performance overhead, as long as no other calls force synchronization.
|
||||
|
||||
#### Training with DREAM
|
||||
|
||||
We support training epsilon (noise) prediction models using the [DREAM (Diffusion Rectification and Estimation-Adaptive Models) strategy](https://arxiv.org/abs/2312.00210). DREAM claims to increase model fidelity for the performance cost of an extra grad-less unet `forward` step in the training loop. You can turn on DREAM training by using the `--dream_training` argument. The `--dream_detail_preservation` argument controls the detail preservation variable p and is the default of 1 from the paper.
|
||||
|
||||
|
||||
|
||||
## Training with LoRA
|
||||
|
||||
Low-Rank Adaption of Large Language Models was first introduced by Microsoft in [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/abs/2106.09685) by *Edward J. Hu, Yelong Shen, Phillip Wallis, Zeyuan Allen-Zhu, Yuanzhi Li, Shean Wang, Lu Wang, Weizhu Chen*.
|
||||
|
||||
@@ -387,6 +387,8 @@ def parse_args():
|
||||
),
|
||||
)
|
||||
parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.")
|
||||
parser.add_argument("--offload_ema", action="store_true", help="Offload EMA model to CPU during training step.")
|
||||
parser.add_argument("--foreach_ema", action="store_true", help="Use faster foreach implementation of EMAModel.")
|
||||
parser.add_argument(
|
||||
"--non_ema_revision",
|
||||
type=str,
|
||||
@@ -624,7 +626,12 @@ def main():
|
||||
ema_unet = UNet2DConditionModel.from_pretrained(
|
||||
args.pretrained_model_name_or_path, subfolder="unet", revision=args.revision, variant=args.variant
|
||||
)
|
||||
ema_unet = EMAModel(ema_unet.parameters(), model_cls=UNet2DConditionModel, model_config=ema_unet.config)
|
||||
ema_unet = EMAModel(
|
||||
ema_unet.parameters(),
|
||||
model_cls=UNet2DConditionModel,
|
||||
model_config=ema_unet.config,
|
||||
foreach=args.foreach_ema,
|
||||
)
|
||||
|
||||
if args.enable_xformers_memory_efficient_attention:
|
||||
if is_xformers_available():
|
||||
@@ -655,9 +662,14 @@ def main():
|
||||
|
||||
def load_model_hook(models, input_dir):
|
||||
if args.use_ema:
|
||||
load_model = EMAModel.from_pretrained(os.path.join(input_dir, "unet_ema"), UNet2DConditionModel)
|
||||
load_model = EMAModel.from_pretrained(
|
||||
os.path.join(input_dir, "unet_ema"), UNet2DConditionModel, foreach=args.foreach_ema
|
||||
)
|
||||
ema_unet.load_state_dict(load_model.state_dict())
|
||||
ema_unet.to(accelerator.device)
|
||||
if args.offload_ema:
|
||||
ema_unet.pin_memory()
|
||||
else:
|
||||
ema_unet.to(accelerator.device)
|
||||
del load_model
|
||||
|
||||
for _ in range(len(models)):
|
||||
@@ -833,7 +845,10 @@ def main():
|
||||
)
|
||||
|
||||
if args.use_ema:
|
||||
ema_unet.to(accelerator.device)
|
||||
if args.offload_ema:
|
||||
ema_unet.pin_memory()
|
||||
else:
|
||||
ema_unet.to(accelerator.device)
|
||||
|
||||
# For mixed precision training we cast all non-trainable weights (vae, non-lora text_encoder and non-lora unet) to half-precision
|
||||
# as these weights are only used for inference, keeping weights in full precision is not required.
|
||||
@@ -1011,7 +1026,11 @@ def main():
|
||||
# Checks if the accelerator has performed an optimization step behind the scenes
|
||||
if accelerator.sync_gradients:
|
||||
if args.use_ema:
|
||||
if args.offload_ema:
|
||||
ema_unet.to(device="cuda", non_blocking=True)
|
||||
ema_unet.step(unet.parameters())
|
||||
if args.offload_ema:
|
||||
ema_unet.to(device="cpu", non_blocking=True)
|
||||
progress_bar.update(1)
|
||||
global_step += 1
|
||||
accelerator.log({"train_loss": train_loss}, step=global_step)
|
||||
|
||||
Reference in New Issue
Block a user