(self, run_dir: Path, n_train_examples: int)
| 290 | # shutil.copy(checkpoint_path, checkpoint_dir / "latest-checkpoint.pt") |
| 291 | |
| 292 | def run_setup(self, run_dir: Path, n_train_examples: int) -> None: |
| 293 | # Iteratively Assemble FSDP Wrapping Policy by fetching the wrapping policies for each backbone/constituent |
| 294 | vlm_fsdp_wrapping_policy = self.vlm.get_fsdp_wrapping_policy() |
| 295 | |
| 296 | # Assemble the Default FSDP Mixed Precision Policy |
| 297 | if self.enable_mixed_precision_training and self.mixed_precision_dtype == torch.bfloat16: |
| 298 | # MixedPrecision `param_dtype` specifies *compute* dtype (for forward/backward only) |
| 299 | # => Reference: https://pytorch.org/docs/stable/fsdp.html#torch.distributed.fsdp.MixedPrecision |
| 300 | reduce_buffer_dtype = torch.bfloat16 if not self.reduce_in_full_precision else torch.float32 |
| 301 | fsdp_precision_policy = MixedPrecision( |
| 302 | param_dtype=torch.bfloat16, reduce_dtype=reduce_buffer_dtype, buffer_dtype=reduce_buffer_dtype |
| 303 | ) |
| 304 | |
| 305 | # When running FSDP with a frozen vision backbone --> move to half precision! |
| 306 | if self.stage not in {"full-finetune", "vla-full-train", "vla-sandwich-train"}: |
| 307 | overwatch.info("Casting Vision Backbone to *Half Precision* via `.to(dtype=...)`") |
| 308 | self.vlm.vision_backbone.to(dtype=self.vlm.vision_backbone.half_precision_dtype) |
| 309 | |
| 310 | else: |
| 311 | # If we're not using mixed precision, everything is in default full precision! |
| 312 | fsdp_precision_policy = MixedPrecision( |
| 313 | param_dtype=torch.float32, reduce_dtype=torch.float32, buffer_dtype=torch.float32 |
| 314 | ) |
| 315 | |
| 316 | # <FSDP> => note that FSDP will automatically take care of device placement (similar to `autocast`) |
| 317 | self.vlm = FSDP( |
| 318 | self.vlm, |
| 319 | auto_wrap_policy=vlm_fsdp_wrapping_policy, |
| 320 | mixed_precision=fsdp_precision_policy, |
| 321 | sharding_strategy=self.fsdp_sharding_strategy, |
| 322 | device_id=torch.cuda.current_device(), |
| 323 | limit_all_gathers=True, |
| 324 | use_orig_params=True, |
| 325 | ) |
| 326 | |
| 327 | # Gradient Checkpoint Setup |
| 328 | if self.enable_gradient_checkpointing: |
| 329 | # For Gradient Checkpointing under FSDP --> we make the same assumption as in the DDP/other strategies; the |
| 330 | # bulk of activation memory is taken up by the LLM activations. However, unlike other strategies, we |
| 331 | # cannot rely on the HF Transformers default `gradient_checkpointing_enable()` --> FSDP breaks semantics! |
| 332 | # |
| 333 | # Instead, we need to write our own *NO-REENTRANT* wrapper, and apply it to the LLM's Transformer Layer. |
| 334 | non_reentrant_wrapper = partial(checkpoint_wrapper, checkpoint_impl=CheckpointImpl.NO_REENTRANT) |
| 335 | |
| 336 | def check_fn(submodule: nn.Module) -> bool: |
| 337 | return isinstance(submodule, self.llm_transformer_layer_cls) |
| 338 | |
| 339 | # Note that the terms "activation checkpointing" and "gradient checkpointing" are synonymous! |
| 340 | apply_activation_checkpointing(self.vlm, checkpoint_wrapper_fn=non_reentrant_wrapper, check_fn=check_fn) |
| 341 | |
| 342 | # Barrier =>> Sharding takes a minute? |
| 343 | dist.barrier() |
| 344 | |
| 345 | # Create Optimizer and LR Scheduler =>> note that most of the LR Schedulers we use require `max_steps/epochs` |
| 346 | # => Optimizer should only operate on parameters that are *unfrozen* / trainable! |
| 347 | n_train_examples = math.ceil(n_train_examples / self.global_batch_size) * self.global_batch_size |
| 348 | if self.max_steps is None: |
| 349 | num_training_steps = (n_train_examples * self.epochs) // self.global_batch_size |
no test coverage detected