| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import argparse |
| import os |
| import sys |
|
|
| _PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__)) |
| while _PROJECT_ROOT and not os.path.isdir(os.path.join(_PROJECT_ROOT, "model")): |
| _PARENT = os.path.dirname(_PROJECT_ROOT) |
| if _PARENT == _PROJECT_ROOT: |
| break |
| _PROJECT_ROOT = _PARENT |
| _MODEL_ROOT = os.path.join(_PROJECT_ROOT, "model") |
| _ONESCIENCE_ROOT = os.environ.get("ONESCIENCE_ROOT") |
| for _path in (_MODEL_ROOT, _PROJECT_ROOT): |
| if os.path.exists(_path) and _path not in sys.path: |
| sys.path.insert(0, _path) |
| if _ONESCIENCE_ROOT: |
| _ONESCIENCE_SRC = os.path.join(_ONESCIENCE_ROOT, "src") |
| for _path in (_ONESCIENCE_SRC, _ONESCIENCE_ROOT): |
| if os.path.exists(_path) and _path not in sys.path: |
| sys.path.insert(0, _path) |
| from pathlib import Path |
| from typing import List, Optional |
|
|
| |
| |
| import torch |
| from lightning.pytorch.callbacks import LearningRateMonitor, RichModelSummary |
| from megatron.core.distributed import DistributedDataParallelConfig |
| from megatron.core.optimizer import OptimizerConfig |
| from nemo import lightning as nl |
| from nemo.collections import llm |
| from nemo.collections.llm.gpt.data import MockDataModule, PreTrainingDataModule |
| from nemo.collections.llm.gpt.data.megatron.hyena.config import parse_dataset_config |
| from nemo.collections.llm.gpt.data.megatron.hyena.evo2_dataset import ( |
| Evo2Dataset, |
| Evo2DatasetPadEodLossMask, |
| ) |
| from nemo.collections.llm.gpt.model.hyena import HYENA_MODEL_OPTIONS |
| from nemo.collections.llm.recipes.tp_overlap_configs.userbuffers import ( |
| userbuffers_bf16_h100_h8192_tp4_mbs1_seqlen8192, |
| userbuffers_fp8_h100_h8192_tp4_mbs1_seqlen8192, |
| ) |
| from nemo.collections.nlp.modules.common.tokenizer_utils import get_nmt_tokenizer |
| from nemo.lightning.pytorch import callbacks as nl_callbacks |
| from nemo.lightning.pytorch.callbacks.flops_callback import FLOPsMeasurementCallback |
| from nemo.lightning.pytorch.callbacks.megatron_comm_overlap import ( |
| MegatronCommOverlapCallback, |
| ) |
| from nemo.lightning.pytorch.optim import CosineAnnealingScheduler |
| from nemo.lightning.pytorch.optim.megatron import MegatronOptimizerModule |
| from nemo.lightning.pytorch.strategies.utils import RestoreConfig |
| from nemo.utils.exp_manager import TimingCallback |
|
|
| from evo2.utils.datamodule_utils import infer_global_batch_size |
| from evo2.utils.logger_utils import ( |
| WandbConfig, |
| setup_nemo_lightning_logger, |
| ) |
|
|
|
|
| torch._dynamo.config.suppress_errors = True |
|
|
| DEFAULT_DATASET_DIR = os.environ.get( |
| "EVO2_DATASET_DIR", |
| str(Path(_PROJECT_ROOT) / "data" / "data_mini" / "genome_data"), |
| ) |
| DEFAULT_CKPT_DIR = None |
|
|
|
|
| def parse_args(args: Optional[List[str]] = None) -> argparse.Namespace: |
| """Parse arguments for Evo2 model training.""" |
| parser = argparse.ArgumentParser( |
| description="Train a Hyena model using NeMo 2.0.", |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| ) |
| data_group = parser.add_mutually_exclusive_group(required=True) |
|
|
| data_group.add_argument( |
| "-d", |
| "--dataset-config", |
| type=str, |
| help="Path to the blended / weighted training dataset configuration YAML.", |
| ) |
| data_group.add_argument( |
| "--mock-data", |
| action="store_true", |
| help="Train with Mock data (for testing/debugging), either set this or provide a dataset config.", |
| ) |
|
|
| parser.add_argument( |
| "--dataset-dir", |
| type=str, |
| default=DEFAULT_DATASET_DIR, |
| help="Absolute path to the dataset directory. Defaults to using the absolute or relative paths (dataset_prefix) specified in the dataset config YAML.", |
| ) |
|
|
| parser.add_argument( |
| "--num-nodes", |
| type=int, |
| default=1, |
| help="Number of nodes to use for training, defaults to 1.", |
| ) |
| parser.add_argument( |
| "--devices", |
| type=int, |
| default=1, |
| help="Number of devices to use for training, defaults to 1.", |
| ) |
| parser.add_argument( |
| "--seq-length", type=int, default=8192, help="Training sequence length" |
| ) |
| parser.add_argument( |
| "--tensor-parallel-size", |
| type=int, |
| default=1, |
| help="Order of tensor parallelism. Defaults to 1.", |
| ) |
| parser.add_argument( |
| "--pipeline-model-parallel-size", |
| type=int, |
| default=1, |
| help="Order of pipeline parallelism. Defaults to 1.", |
| ) |
| parser.add_argument( |
| "--context-parallel-size", |
| type=int, |
| default=1, |
| help="Order of context parallelism. Defaults to 1.", |
| ) |
| parser.add_argument( |
| "--create-tensorboard-logger", |
| action="store_true", |
| default=False, |
| help="Create a tensorboard logger.", |
| ) |
| parser.add_argument( |
| "--wandb-entity", type=str, default=None, help="The team posting this run" |
| ) |
| parser.add_argument( |
| "--wandb-project", type=str, default=None, help="Wandb project name " |
| ) |
| parser.add_argument( |
| "--wandb-tags", |
| nargs="+", |
| type=str, |
| default=None, |
| help="Tags associated with this run", |
| ) |
| parser.add_argument( |
| "--wandb-group", |
| type=str, |
| default=None, |
| help="A unique string shared by all runs in a given group", |
| ) |
| parser.add_argument( |
| "--wandb-job-type", |
| type=str, |
| default=None, |
| help="A unique string representing a type of run, which is useful when you're grouping runs together into larger experiments using group.", |
| ) |
| parser.add_argument( |
| "--wandb-run-name", |
| type=str, |
| default=None, |
| help="A unique string representing the name of the wandb run. If not provided, the name will be generated from the model and training specifications.", |
| ) |
|
|
| parser.add_argument( |
| "--wandb-id", |
| type=str, |
| default=None, |
| help="Sets the version, mainly used to resume a previous run", |
| ) |
| parser.add_argument( |
| "--wandb-anonymous", |
| action="store_true", |
| help="Enable or explicitly disable anonymous logging", |
| ) |
| parser.add_argument( |
| "--wandb-log-model", |
| action="store_true", |
| help="Save checkpoints in wandb dir to upload on W&B servers", |
| ) |
| parser.add_argument( |
| "--wandb-offline", action="store_true", help="Use wandb in offline mode" |
| ) |
| parser.add_argument( |
| "--sequence-parallel", |
| action="store_true", |
| help="Set to enable sequence parallelism.", |
| ) |
| parser.add_argument("--fp8", action="store_true", help="Set to enable FP8") |
| parser.add_argument( |
| "--micro-batch-size", |
| type=int, |
| default=1, |
| help="Micro-batch size for data-parallel training.", |
| ) |
| parser.add_argument( |
| "--global-batch-size", |
| type=int, |
| default=None, |
| help="Global batch size for training. If set to None, infer it from the TP, CP, and PP parameters.", |
| ) |
| parser.add_argument( |
| "--grad-acc-batches", |
| type=int, |
| default=1, |
| help="Number of batches to accumulate gradients over.", |
| ) |
| parser.add_argument( |
| "--max-steps", |
| type=int, |
| help="Number of training optimizer update steps. This controls the total number of steps as well as the " |
| "shape of the learning rate curve.", |
| default=500000, |
| ) |
| parser.add_argument( |
| "--constant-steps", |
| type=int, |
| help="Number of steps to keep the learning rate constant before annealing. This controls the " |
| "shape of the learning rate curve.", |
| default=80000, |
| ) |
| parser.add_argument( |
| "--early-stop-on-step", |
| type=int, |
| help="Stop training on this step, if set. This may be useful for testing or debugging purposes.", |
| ) |
| parser.add_argument( |
| "--val-check-interval", |
| type=int, |
| help="Number of steps between validation measurements and model checkpoints.", |
| ) |
| parser.add_argument( |
| "--grad-reduce-in-fp32", |
| action="store_true", |
| default=False, |
| help="Gradient reduce in FP32.", |
| ) |
| parser.add_argument( |
| "--fp8-wgrad", |
| action="store_true", |
| default=False, |
| help="Faster option that is maybe less accurate (TBD) when using fp8.", |
| ) |
| parser.add_argument( |
| "--use-megatron-comm-overlap-llama3-8k", action="store_true", default=False |
| ) |
| parser.add_argument( |
| "--tp-comm-overlap-backend", |
| type=str, |
| choices=["nccl", "mpi", "gloo"], |
| default="nccl", |
| help="TP communication backend to use. Defaults to 'nccl'.", |
| ) |
| parser.add_argument("--align-param-gather", action="store_true", default=False) |
| |
| parser.add_argument( |
| "--model-size", |
| type=str, |
| choices=sorted(HYENA_MODEL_OPTIONS.keys()), |
| default="7b", |
| help="Model architecture to use, choose between 7b, 40b, or test (a sub-model of 4 layers, less than 1B " |
| "parameters). '_arc_1m' models have GLU / FFN dimensions that support 1M context length when trained " |
| "with TP<=8.", |
| ) |
| parser.add_argument( |
| "--add-bias-output", |
| action="store_true", |
| default=False, |
| help="Add bias to the output layer to enable learning a simple prior.", |
| ) |
| parser.add_argument( |
| "--result-dir", |
| type=Path, |
| required=False, |
| default=Path("./results"), |
| help="Path to the result directory.", |
| ) |
| parser.add_argument( |
| "--experiment-name", |
| type=str, |
| required=False, |
| default="evo2", |
| help="Name of the experiment.", |
| ) |
|
|
| parser.add_argument( |
| "--limit-val-batches", |
| type=int, |
| default=20, |
| help="Number of validation steps", |
| ) |
| parser.add_argument( |
| "--log-every-n-steps", |
| type=int, |
| default=1, |
| required=False, |
| help="Number of steps between logging.", |
| ) |
| parser.add_argument( |
| "--ckpt-dir", |
| type=str, |
| default=DEFAULT_CKPT_DIR, |
| help="Directory to restore an initial checkpoint from. Use this for supervised fine-tuning.", |
| ) |
| parser.add_argument( |
| "--wd", type=float, default=0.01, help="Weight decay for optimizer." |
| ) |
| parser.add_argument( |
| "--restore-optimizer-from-ckpt", |
| action="store_true", |
| help="Restore optimizer state from initial checkpoint. Defaults to False.", |
| ) |
| parser.add_argument( |
| "--no-average-in-collective", |
| action="store_true", |
| default=False, |
| help="Avaerage optimizer state in collective rather than dividing by dp size and summing.", |
| ) |
| parser.add_argument( |
| "--seed", type=int, default=1234, help="Set random seed for training." |
| ) |
| parser.add_argument( |
| "--workers", |
| type=int, |
| default=8, |
| help="Number of workers to use for data loading.", |
| ) |
| parser.add_argument( |
| "--gc-interval", |
| type=int, |
| default=0, |
| help="Set to a value > 0 if you want to synchronize garbage collection, will do gc every gc-interval steps.", |
| ) |
| parser.add_argument( |
| "--enable-preemption", |
| action="store_true", |
| default=False, |
| help="Enable preemption hooks. If enabled this will save a checkpoint whenever slurm exits.", |
| ) |
| parser.add_argument( |
| "--ckpt-async-save", |
| action="store_true", |
| default=False, |
| ) |
| parser.add_argument( |
| "--ckpt-format", |
| type=str, |
| choices=["torch_dist", "zarr"], |
| default="torch_dist", |
| help="Specify checkpoint format to use. Defaults to 'torch_dist', as 'zarr' is deprecated. Only use if " |
| "resuming training from a zarr checkpoint.", |
| ) |
| parser.add_argument( |
| "--eod-pad-in-loss-mask", |
| action="store_true", |
| default=False, |
| help="Do not predict EOD/Pad tokens (typical default, but not default in original evo2).", |
| ) |
| parser.add_argument( |
| "--cross-entropy-loss-fusion", |
| action="store_true", |
| default=False, |
| help="Use the faster, but maybe less accurate fused form of cross entropy, " |
| "which also has bf16 grads internally.", |
| ) |
| parser.add_argument( |
| "--no-fp32-residual-connection", |
| action="store_true", |
| default=False, |
| help="If set, turn off fp32 residual connections which may be faster but may impact accuracy.", |
| ) |
| parser.add_argument( |
| "--debug-ddp-parity-freq", |
| type=int, |
| default=0, |
| help="Set to value > 0 to debug DDP weight parity between ranks.", |
| ) |
| parser.add_argument( |
| "--hybrid-override-pattern", |
| type=str, |
| help="Override the hybrid override pattern in the config (specifies hyena layer ordering and type).", |
| ) |
| parser.add_argument( |
| "--num-layers", |
| type=int, |
| help="If set, override the number of layers specified in the requested config.", |
| ) |
| parser.add_argument( |
| "--create-tflops-callback", |
| action="store_true", |
| default=False, |
| help="Enable tflops calculation callback for Hyena / Evo2. Defaults to False.", |
| ) |
| parser.add_argument( |
| "--log-parameters-and-shapes", |
| action="store_true", |
| default=False, |
| help="Log training parameters shapes and dtypes for debugging.", |
| ) |
| parser.add_argument("--lr", type=float, default=3e-4, help="Learning rate.") |
| parser.add_argument( |
| "--min-lr", |
| type=float, |
| default=3e-5, |
| help="Min learning rate in cosine annealing.", |
| ) |
| parser.add_argument( |
| "--warmup-steps", |
| type=int, |
| default=2500, |
| help="Number of warmup steps in cosine annealing", |
| ) |
| |
| parser.add_argument( |
| "--nsys-profiling", |
| action="store_true", |
| default=False, |
| help="Enable targeted `nsys` profiling on the training loop for a defined step range. To actually get profiling" |
| " output you must run the whole program with `nsys`. For example: " |
| " `nsys profile -s none -o output_report_name -t cuda,nvtx --force-overwrite true " |
| "--capture-range=cudaProfilerApi --capture-range-end=stop [regular python command here]`", |
| ) |
| |
| parser.add_argument( |
| "--nsys-start-step", |
| type=int, |
| required=False, |
| default=0, |
| help="Start nsys profiling after this step.", |
| ) |
| parser.add_argument( |
| "--nsys-end-step", |
| type=int, |
| required=False, |
| help="End nsys profiling after this step.", |
| ) |
| parser.add_argument( |
| "--no-renormalize-loss", |
| action="store_true", |
| default=False, |
| help="Do not renormalize the loss weights.", |
| ) |
| |
| parser.add_argument( |
| "--nsys-ranks", |
| type=int, |
| nargs="+", |
| required=False, |
| default=[0], |
| help="Enable nsys profiling for these ranks.", |
| ) |
| parser.add_argument( |
| "--activation-checkpoint-recompute-num-layers", |
| type=int, |
| help="If set, override the default value set in the config.", |
| ) |
| parser.add_argument( |
| "--disable-checkpointing", |
| action="store_false", |
| default=True, |
| dest="create_checkpoint_callback", |
| help="Disable creating a ModelCheckpoint callback.", |
| ) |
| parser.add_argument( |
| "--clip-grad", |
| type=float, |
| default=1.0, |
| help="Grad clip value. Note that when using DDP this may need to be inflated.", |
| ) |
| parser.add_argument( |
| "--seq-len-interpolation-factor", |
| type=float, |
| help="Adjusts the linear scaling of ROPE (Rotary Position Embedding) for context extension. " |
| "Set this factor relative to your base context length e.g., for an original context length of 8192 and " |
| "an extended context length of 524288, use 524288/8192 = 64.", |
| ) |
| parser.add_argument( |
| "--overlap-param-gather", |
| action="store_true", |
| default=False, |
| help="Overlap the parameter gather with the optimizer step. This is currently disabled due to a NeMo bug " |
| "when using DDP. Making this an option defaulting to False is a temporary solution until the bug is fixed.", |
| ) |
| parser.add_argument( |
| "--overlap-grad-reduce", |
| action="store_true", |
| default=False, |
| help="Overlap the gradient reduce with the optimizer step.", |
| ) |
| parser.add_argument( |
| "--hidden-dropout", |
| type=float, |
| default=0.0, |
| help="Dropout probability for the hyena layers", |
| ) |
| parser.add_argument( |
| "--attention-dropout", |
| type=float, |
| default=0.0, |
| help="Dropout probability for the attention layers.", |
| ) |
| parser.add_argument( |
| "--save-top-k", |
| type=int, |
| default=5, |
| help="Number of best checkpoints to keep. Set to -1 to save all checkpoints.", |
| ) |
| parser.add_argument( |
| "--metric-to-monitor-for-checkpoints", |
| type=str, |
| default="val_loss", |
| help="Metric to monitor for checkpoints.", |
| ) |
| parser.add_argument( |
| "--save-last-checkpoint", |
| action="store_true", |
| default=True, |
| help="Save the last checkpoint.", |
| ) |
| parser.add_argument( |
| "--no-save-last-checkpoint", |
| action="store_false", |
| dest="save_last_checkpoint", |
| default=True, |
| help="Disable saving the last checkpoint.", |
| ) |
| recompute_group = parser.add_mutually_exclusive_group(required=False) |
| recompute_group.add_argument( |
| "--no-activation-checkpointing", action="store_true", default=False |
| ) |
| recompute_group.add_argument( |
| "--selective-activation-checkpointing", action="store_true", default=False |
| ) |
| return parser.parse_args(args=args) |
|
|
|
|
| def train(args: argparse.Namespace) -> nl.Trainer: |
| """Main function to run Evo2 training.""" |
| |
| tokenizer = get_nmt_tokenizer( |
| "byte-level", |
| ) |
|
|
| |
| global_batch_size = args.global_batch_size |
| if global_batch_size is None: |
| global_batch_size = infer_global_batch_size( |
| micro_batch_size=args.micro_batch_size, |
| num_nodes=args.num_nodes, |
| devices=args.devices, |
| accumulate_grad_batches=args.grad_acc_batches, |
| tensor_model_parallel_size=args.tensor_parallel_size, |
| pipeline_model_parallel_size=args.pipeline_model_parallel_size, |
| context_model_parallel_size=args.context_parallel_size, |
| ) |
| if args.mock_data: |
| data_module = MockDataModule( |
| seq_length=args.seq_length, |
| micro_batch_size=args.micro_batch_size, |
| global_batch_size=global_batch_size, |
| num_workers=args.workers, |
| tokenizer=tokenizer, |
| ) |
| else: |
| blended_dataset_config = parse_dataset_config( |
| dataset_config_path=args.dataset_config, dataset_path=args.dataset_dir |
| ) |
| Evo2DatasetPadEodLossMask if args.eod_pad_in_loss_mask else Evo2Dataset |
| |
| data_module = PreTrainingDataModule( |
| paths=blended_dataset_config, |
| |
| seq_length=args.seq_length, |
| micro_batch_size=args.micro_batch_size, |
| global_batch_size=global_batch_size, |
| seed=args.seed, |
| num_workers=args.workers, |
| tokenizer=tokenizer, |
| eod_mask_loss=args.eod_pad_in_loss_mask, |
| ) |
|
|
| if args.no_activation_checkpointing: |
| activation_checkpointing_args = { |
| "recompute_granularity": None, |
| "recompute_method": None, |
| "recompute_num_layers": None, |
| } |
| elif args.selective_activation_checkpointing: |
| activation_checkpointing_args = { |
| "recompute_granularity": "selective", |
| "recompute_method": None, |
| "recompute_num_layers": None, |
| } |
| else: |
| if args.activation_checkpoint_recompute_num_layers is not None: |
| activation_checkpointing_args = { |
| "recompute_num_layers": args.activation_checkpoint_recompute_num_layers, |
| } |
| else: |
| activation_checkpointing_args = {} |
|
|
| |
| config_modifiers_init = { |
| "tp_comm_overlap": args.use_megatron_comm_overlap_llama3_8k, |
| "seq_length": args.seq_length, |
| "hidden_dropout": args.hidden_dropout, |
| "attention_dropout": args.attention_dropout, |
| "to_upper": "weighted" if args.no_renormalize_loss else "normalized_weighted", |
| "distribute_saved_activations": False if args.sequence_parallel else True, |
| "cross_entropy_loss_fusion": args.cross_entropy_loss_fusion, |
| "fp32_residual_connection": not args.no_fp32_residual_connection, |
| "add_bias_output": args.add_bias_output, |
| **activation_checkpointing_args, |
| } |
| if args.hybrid_override_pattern: |
| config_modifiers_init["hybrid_override_pattern"] = args.hybrid_override_pattern |
| if args.num_layers: |
| config_modifiers_init["num_layers"] = args.num_layers |
|
|
| if args.model_size not in HYENA_MODEL_OPTIONS: |
| raise ValueError(f"Invalid model size: {args.model_size}") |
| evo2_config = HYENA_MODEL_OPTIONS[args.model_size](**config_modifiers_init) |
|
|
| |
| model = llm.HyenaModel(evo2_config, tokenizer=data_module.tokenizer) |
|
|
| |
| callbacks = [ |
| RichModelSummary(max_depth=4), |
| LearningRateMonitor(), |
| TimingCallback(), |
| ] |
|
|
| if args.enable_preemption: |
| callbacks.append(nl_callbacks.PreemptionCallback()) |
| if args.debug_ddp_parity_freq > 0: |
| callbacks.append( |
| nl_callbacks.DdpParityChecker(interval=args.debug_ddp_parity_freq) |
| ) |
| if args.log_parameters_and_shapes: |
| callbacks.append(nl_callbacks.ParameterDebugger()) |
| if args.create_tflops_callback: |
| |
| flop_meas_callback = FLOPsMeasurementCallback( |
| evo2_config, |
| data_module, |
| "hyena", |
| ) |
| callbacks.append(flop_meas_callback) |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| if args.use_megatron_comm_overlap_llama3_8k: |
| |
| if args.fp8: |
| tp_comm_overlap_cfg = userbuffers_fp8_h100_h8192_tp4_mbs1_seqlen8192 |
| else: |
| tp_comm_overlap_cfg = userbuffers_bf16_h100_h8192_tp4_mbs1_seqlen8192 |
| callbacks.append( |
| MegatronCommOverlapCallback( |
| tp_comm_overlap=evo2_config.tp_comm_overlap, |
| tp_comm_overlap_cfg=tp_comm_overlap_cfg, |
| tp_comm_bootstrap_backend=args.tp_comm_overlap_backend, |
| wgrad_deferral_limit=22, |
| overlap_param_gather_with_optimizer_step=False, |
| align_param_gather=args.align_param_gather, |
| ) |
| ) |
|
|
| if args.gc_interval > 0: |
| callbacks.append( |
| nl_callbacks.GarbageCollectionCallback( |
| gc_interval_train=args.gc_interval, gc_interval_val=args.gc_interval |
| ) |
| ) |
| if args.nsys_profiling: |
| if args.nsys_end_step is None: |
| nsys_end_step = args.max_steps |
| else: |
| nsys_end_step = args.nsys_end_step |
| callbacks.append( |
| nl_callbacks.NsysCallback( |
| start_step=args.nsys_start_step, |
| end_step=nsys_end_step, |
| ranks=args.nsys_ranks, |
| gen_shape=True, |
| ) |
| ) |
|
|
| wandb_run_name = ( |
| f"evo2-size-{args.model_size}-TP{args.tensor_parallel_size}-" |
| f"PP{args.pipeline_model_parallel_size}-CP{args.context_parallel_size}" |
| f"-GBS{global_batch_size}-MBS{args.micro_batch_size}-SkipLossRenorm{args.no_renormalize_loss}" |
| f"-NOAC{args.no_activation_checkpointing}-SELAC{args.selective_activation_checkpointing}" |
| f"-ACRNL{evo2_config.recompute_num_layers}" |
| f"-PAT{evo2_config.hybrid_override_pattern}" |
| f"-F32R{evo2_config.fp32_residual_connection}" |
| f"-FCE{evo2_config.cross_entropy_loss_fusion}" |
| f"-AIC{not args.no_average_in_collective}" |
| f"-PEOD{args.eod_pad_in_loss_mask}" |
| f"-BO{args.add_bias_output}" |
| f"-GCLP{args.clip_grad}" |
| f"-HDO{args.hidden_dropout}" |
| f"-ADO{args.attention_dropout}" |
| f"-LR{args.lr}-MINLR{args.min_lr}-WUSTEPS{args.warmup_steps}-CONSTSTEPS{args.constant_steps}-WD{args.wd}" |
| f"-GRFP32{args.grad_reduce_in_fp32}-FP8WG{args.fp8_wgrad and args.fp8}" |
| f"-OGR{args.overlap_grad_reduce}-OPG{args.overlap_param_gather}" |
| f"-NODES{args.num_nodes}-FP8{args.fp8}" |
| ) |
|
|
| wandb_config: Optional[WandbConfig] = ( |
| None |
| if args.wandb_project is None |
| else WandbConfig( |
| offline=args.wandb_offline, |
| project=args.wandb_project, |
| name=( |
| args.wandb_run_name |
| if args.wandb_run_name is not None |
| else wandb_run_name |
| ), |
| entity=args.wandb_entity, |
| tags=args.wandb_tags, |
| group=args.wandb_group, |
| job_type=args.wandb_job_type, |
| id=args.wandb_id, |
| anonymous=args.wandb_anonymous, |
| log_model=args.wandb_log_model, |
| ) |
| ) |
| |
| nemo_logger = setup_nemo_lightning_logger( |
| root_dir=args.result_dir, |
| name=args.experiment_name, |
| initialize_tensorboard_logger=args.create_tensorboard_logger, |
| wandb_config=wandb_config, |
| ) |
|
|
| if args.create_checkpoint_callback: |
| checkpoint_path = str(Path(nemo_logger.save_dir) / "checkpoints") |
| checkpoint_callback = nl_callbacks.ModelCheckpoint( |
| dirpath=checkpoint_path, |
| save_last=args.save_last_checkpoint, |
| monitor=args.metric_to_monitor_for_checkpoints, |
| save_top_k=args.save_top_k, |
| every_n_train_steps=args.val_check_interval, |
| always_save_context=True, |
| filename="{epoch}-{step}-{consumed_samples}", |
| save_weights_only=False, |
| save_optim_on_train_end=True, |
| save_context_on_train_end=True, |
| ) |
| callbacks.append(checkpoint_callback) |
|
|
| auto_resume = nl.AutoResume( |
| resume_if_exists=True, |
| resume_ignore_no_checkpoint=True, |
| resume_past_end=False, |
| resume_from_directory=checkpoint_path, |
| restore_config=( |
| RestoreConfig( |
| path=args.ckpt_dir, |
| load_model_state=True, |
| load_optim_state=args.restore_optimizer_from_ckpt, |
| ) |
| if args.ckpt_dir |
| else None |
| ), |
| ) |
| else: |
| auto_resume = None |
|
|
| ddp: DistributedDataParallelConfig = DistributedDataParallelConfig( |
| check_for_nan_in_grad=True, |
| overlap_grad_reduce=args.overlap_grad_reduce, |
| overlap_param_gather=args.overlap_param_gather, |
| grad_reduce_in_fp32=args.grad_reduce_in_fp32, |
| align_param_gather=args.align_param_gather, |
| average_in_collective=not args.no_average_in_collective, |
| ) |
| |
| |
| strategy = nl.MegatronStrategy( |
| ddp=ddp, |
| tensor_model_parallel_size=args.tensor_parallel_size, |
| pipeline_model_parallel_size=args.pipeline_model_parallel_size, |
| context_parallel_size=args.context_parallel_size, |
| pipeline_dtype=torch.bfloat16, |
| sequence_parallel=args.sequence_parallel, |
| ckpt_load_optimizer=True, |
| ckpt_save_optimizer=True, |
| ckpt_async_save=args.ckpt_async_save, |
| save_ckpt_format=args.ckpt_format, |
| ckpt_load_strictness="log_all", |
| ) |
| from lightning.fabric.plugins.environments import LightningEnvironment |
|
|
|
|
| trainer = nl.Trainer( |
| devices=args.devices, |
| num_nodes=args.num_nodes, |
| max_steps=( |
| args.max_steps |
| if args.early_stop_on_step is None |
| else args.early_stop_on_step |
| ), |
| accelerator="gpu", |
| strategy=strategy, |
| callbacks=callbacks, |
| log_every_n_steps=args.log_every_n_steps, |
| limit_val_batches=args.limit_val_batches, |
| num_sanity_val_steps=0, |
| use_distributed_sampler=False, |
| plugins=[ |
| nl.MegatronMixedPrecision( |
| precision="bf16-mixed", |
| params_dtype=torch.bfloat16, |
| grad_reduce_in_fp32=args.grad_reduce_in_fp32, |
| fp8="hybrid" if args.fp8 else None, |
| fp8_amax_history_len=16 if args.fp8 else 1, |
| fp8_amax_compute_algo="max" if args.fp8 else "most_recent", |
| fp8_wgrad=args.fp8 |
| and ( |
| args.fp8_wgrad or args.use_megatron_comm_overlap_llama3_8k |
| ), |
| ), |
| LightningEnvironment(), |
| ], |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| val_check_interval=args.val_check_interval, |
| enable_checkpointing=args.create_checkpoint_callback, |
| ) |
|
|
| |
| nemo_logger.setup( |
| trainer, |
| resume_if_exists=True, |
| ) |
|
|
| if auto_resume is not None: |
| auto_resume.setup(trainer, model) |
|
|
| |
| opt_config = OptimizerConfig( |
| optimizer="adam", |
| lr=args.lr, |
| adam_beta1=0.9, |
| adam_beta2=0.95, |
| weight_decay=args.wd, |
| clip_grad=args.clip_grad, |
| use_distributed_optimizer=True, |
| bf16=True, |
| ) |
|
|
| sched = CosineAnnealingScheduler( |
| max_steps=trainer.max_steps, |
| warmup_steps=args.warmup_steps, |
| min_lr=args.min_lr, |
| constant_steps=args.constant_steps, |
| ) |
| |
| opt = MegatronOptimizerModule( |
| opt_config, |
| sched, |
| no_weight_decay_cond=evo2_config.hyena_no_weight_decay_cond_fn, |
| ) |
| opt.connect(model) |
|
|
| |
| trainer.fit(model, data_module) |
| return trainer |
|
|
|
|
| def main(): |
| """Parsing args and running evo2 training.""" |
| args = parse_args() |
| if args.ckpt_dir and not os.path.isdir(args.ckpt_dir): |
| raise SystemExit(f"ERROR: Evo2 checkpoint directory does not exist: {args.ckpt_dir}") |
| train(args=args) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|