Complete captured source · line numbers include instrumentation · run labels normalized
Source alone does not establish which branches executed or prove the cause.
1# Copyright (c) Microsoft Corporation.2# SPDX-License-Identifier: Apache-2.034# DeepSpeed Team56from opguard.runtime_tracer import flush_trace_buffer7from opguard.wrapped_ops import make_wrapper8import operator9opguard_wrapped__align_dense_tensors_flatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')10opguard_wrapped__all_norms_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')11opguard_wrapped__allreduced_unflatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')12opguard_wrapped__bit16_groups_flat_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')13opguard_wrapped__buf_0_empty = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')14opguard_wrapped__buf_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')15opguard_wrapped__cpu_data_cpu = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')16opguard_wrapped__data_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')17opguard_wrapped__data_mul_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')18opguard_wrapped__data_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')19opguard_wrapped__data_view_as = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')20opguard_wrapped__err_tensor = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')21opguard_wrapped__float_max = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')22opguard_wrapped__float_x_float = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')23opguard_wrapped__grad_slice_narrow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')24opguard_wrapped__grads_in_partition_empty = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')25opguard_wrapped__inf_isinf = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')26opguard_wrapped__inf_or_nan_logical_or = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')27opguard_wrapped__invalid_grad_count__has_inf_or_nan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')28opguard_wrapped__invalid_grad_count_bool = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')29opguard_wrapped__invalid_grad_count_zeros = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')30opguard_wrapped__meta_tensors_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')31opguard_wrapped__nan_isnan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')32opguard_wrapped__narrow_average_tensor = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')33opguard_wrapped__new_grad_tensor_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')34opguard_wrapped__new_grad_tensor_narrow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')35opguard_wrapped__norm_is_inf_isinf = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')36opguard_wrapped__norm_is_nan_isnan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')37opguard_wrapped__overflow_gpu_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')38opguard_wrapped__overflow_item = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')39opguard_wrapped__partitions_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')40opguard_wrapped__single_grad_partition_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')41opguard_wrapped__single_grad_partition_zeros = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')42opguard_wrapped__stack_norm = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')43opguard_wrapped__tensor_div_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')44opguard_wrapped__tensor_flatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')45opguard_wrapped__total_norm_float = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')46opguard_wrapped__total_norm_logical_not = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')47opguard_wrapped__total_norm_pow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')48opguard_wrapped__updated_params_unflatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')49opguard_wrapped__weights_partition_detach = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')50opguard_wrapped_scaled_global_grad_norm_truediv_prev_scale = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')51opguard_wrapped_total_norm_truediv_clip_grad = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run1/deps/deepspeed/runtime/zero/stage_1_and_2.py')52import torch53from deepspeed import comm as dist54from packaging import version as pkg_version55from collections import OrderedDict56from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors5758from deepspeed.runtime.base_optimizer import ZeROOptimizer59from deepspeed.runtime.fp16.loss_scaler import CreateLossScaler60from deepspeed.runtime.utils import (empty_cache, see_memory_usage, inf, is_model_parallel_parameter,61align_dense_tensors, all_gather_dp_groups)62from deepspeed.runtime.zero.config import ZeroStageEnum63from deepspeed.runtime.zero.offload_config import OffloadDeviceEnum64from deepspeed.ops.adam import DeepSpeedCPUAdam65from deepspeed.utils import logger66from deepspeed.utils.bwc import bwc_tensor_model_parallel_rank67from deepspeed.moe.utils import is_moe_param68from deepspeed.git_version_info import version6970from deepspeed.runtime.constants import PIPE_REPLICATED71from deepspeed.accelerator import get_accelerator7273from deepspeed.checkpoint.constants import (DS_VERSION, GROUP_PADDINGS, PARTITION_COUNT, LOSS_SCALER,74SINGLE_PARTITION_OF_FP32_GROUPS, BASE_OPTIMIZER_STATE,75BASE_OPTIMIZER_STATE_STEP, CLIP_GRAD, ZERO_STAGE, PARAM_SLICE_MAPPINGS)76from deepspeed.utils import link_hp_params, lazy_init_hp_params_optimizer_state77from deepspeed.checkpoint import enable_universal_checkpoint7879from deepspeed.utils import groups80# Toggle this to true to enable correctness test81# with gradient partitioning and without82pg_correctness_test = False8384OPTIMIZER_ALLGATHER_TIMER = 'optimizer_allgather'85OPTIMIZER_GRADIENTS_TIMER = 'optimizer_gradients'86OPTIMIZER_STEP_TIMER = 'optimizer_step'87OPTIMIZER_TIMERS = [OPTIMIZER_ALLGATHER_TIMER, OPTIMIZER_GRADIENTS_TIMER, OPTIMIZER_STEP_TIMER]888990def input(msg):91return929394def split_half_float_double(tensors):95device_type = get_accelerator().device_name()96dtypes = [97"torch.{}.HalfTensor".format(device_type), "torch.{}.FloatTensor".format(device_type),98"torch.{}.DoubleTensor".format(device_type), "torch.{}.BFloat16Tensor".format(device_type)99]100buckets = []101for i, dtype in enumerate(dtypes):102bucket = [t for t in tensors if t.type() == dtype]103if bucket:104buckets.append(bucket)105return buckets106107108def isclose(a, b, rtol=1e-09, atol=0.0):109return abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)110111112def lcm(x, y):113from fractions import gcd # or can import gcd from `math` in Python 3114return x * y // gcd(x, y)115116117def get_alignment_padding(tensor_list, alignment):118num_elements = sum([tensor.numel() for tensor in tensor_list])119remainder = num_elements % alignment120return (alignment - remainder) if remainder else remainder121122123def print_rank_msg(msg):124print(f"rank {dist.get_rank()} - {msg}")125126127def _get_padded_tensor(src_tensor, size):128if src_tensor.numel() >= size:129return src_tensor130padded_tensor = torch.zeros(size, dtype=src_tensor.dtype, device=src_tensor.device)131slice_tensor = torch.narrow(padded_tensor, 0, 0, src_tensor.numel())132slice_tensor.data.copy_(src_tensor.data)133return padded_tensor134135136def _pad_tensor_by_size(src_tensor, pad_size, dtype, device):137padded_tensor = torch.zeros(src_tensor.numel() + pad_size, dtype=dtype, device=device)138padded_tensor.data[:src_tensor.numel()].copy_(src_tensor.data)139return padded_tensor140141142class DeepSpeedZeroOptimizer(ZeROOptimizer):143"""144DeepSpeedZeroOptimizer designed to reduce the memory footprint145required for training large deep learning models.146147For more details please see ZeRO: Memory Optimization Towards Training A Trillion Parameter Models148https://arxiv.org/abs/1910.02054149150For usage examples, refer to TODO: DeepSpeed Tutorial151152"""153154def __init__(self,155init_optimizer,156param_names,157timers,158static_loss_scale=1.0,159dynamic_loss_scale=False,160dynamic_loss_args=None,161verbose=True,162contiguous_gradients=True,163reduce_bucket_size=500000000,164use_multi_rank_bucket_allreduce=True,165allgather_bucket_size=5000000000,166dp_process_group=None,167expert_parallel_group=None,168expert_data_parallel_group=None,169reduce_scatter=True,170overlap_comm=False,171offload_optimizer_config=None,172mpu=None,173clip_grad=0.0,174gradient_accumulation_dtype=torch.float32,175communication_data_type=torch.float16,176postscale_gradients=True,177gradient_predivide_factor=1.0,178gradient_accumulation_steps=1,179ignore_unused_parameters=True,180partition_grads=True,181round_robin_gradients=False,182has_moe_layers=False,183fp16_master_weights_and_gradients=False,184elastic_checkpoint=False):185186if offload_optimizer_config is not None and offload_optimizer_config.device != OffloadDeviceEnum.none:187self.cpu_offload = True188self.cpu_offload_pin_memory = offload_optimizer_config.pin_memory189else:190self.cpu_offload = False191self.cpu_offload_pin_memory = False192193if dist.get_rank() == 0:194logger.info(f"Reduce bucket size {reduce_bucket_size}")195logger.info(f"Allgather bucket size {allgather_bucket_size}")196logger.info(f"CPU Offload: {self.cpu_offload}")197logger.info(f'Round robin gradient partitioning: {round_robin_gradients}')198# The fused optimizer does all the work. We need this layer for two reason:199# 1. maintain same user API from apex.fp16_utils200# 2. keep common stuff here in case we need to add ne552w fused optimizer later201202self.elastic_checkpoint = elastic_checkpoint203self.param_names = param_names204self.mpu = mpu205# differences from apex.fp16_utils:206# - assume all model params in fp16207# - assume all params requires grad208# - flat by groups, not keeping state. TODO: remove state explicitly?209# - master grad and unflat master weight never exist. TODO: a way to save out unflat master?210if not get_accelerator().is_available():211raise SystemError("Accelerator is not detected, cannot perform low precision training (e.g., fp16, bf16).")212self.optimizer = init_optimizer213214# Use torch (un)flatten ops215self.flatten = _flatten_dense_tensors216self.unflatten = _unflatten_dense_tensors217218# ZeRO stage 1 (False) or 2 (True)219self.partition_gradients = partition_grads220self.zero_stage_string = "ZeRO-2" if partition_grads else "ZeRO-1"221222self.timers = timers223224self.reduce_scatter = reduce_scatter225226self.overlap_comm = overlap_comm227228self.deepspeed_adam_offload = self.cpu_offload229230self.device = get_accelerator().current_device_name() if not self.cpu_offload else 'cpu'231232self.dp_process_group = dp_process_group233self.sequence_parallel_size = groups._get_sequence_parallel_world_size()234#expert parallel group235self.ep_process_group = expert_parallel_group236237#data parallel group for experts238self.expert_dp_process_group = expert_data_parallel_group239240#data parallel size for non-experts241dp_size = dist.get_world_size(group=self.dp_process_group)242243#For MoE models this maybe different for different param group244#It will be modified during MoE setup later in the init245self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))]246self.partition_count = [dp_size for i in range(len(self.optimizer.param_groups))]247248self.is_gradient_accumulation_boundary = True249250# CPU-Offload requires contiguous gradients251self.contiguous_gradients = contiguous_gradients or self.cpu_offload252253self.has_moe_layers = has_moe_layers254if self.has_moe_layers:255self._configure_moe_settings()256self._global_grad_norm = 0.257258if mpu is None:259self.model_parallel_group = None260self.model_parallel_world_size = 1261self.model_parallel_rank = 0262else:263self.model_parallel_group = mpu.get_model_parallel_group()264self.model_parallel_world_size = mpu.get_model_parallel_world_size()265self.model_parallel_rank = bwc_tensor_model_parallel_rank(mpu)266267self.overflow = False268self.clip_grad = clip_grad269self.communication_data_type = communication_data_type270self.gradient_predivide_factor = gradient_predivide_factor271self.postscale_gradients = postscale_gradients272self.gradient_accumulation_steps = gradient_accumulation_steps273self.micro_step_id = 0274self.ignore_unused_parameters = ignore_unused_parameters275self.round_robin_gradients = round_robin_gradients276277self.extra_large_param_to_reduce = None278self.fp16_master_weights_and_gradients = fp16_master_weights_and_gradients279280if self.fp16_master_weights_and_gradients:281assert self.cpu_offload and type(self.optimizer) in [DeepSpeedCPUAdam], \282f"fp16_master_and_gradients requires optimizer to support keeping fp16 master and gradients while keeping the optimizer states in fp32."\283f"Currently only supported using ZeRO-Offload with DeepSpeedCPUAdam. But current setting is ZeRO-Offload:{self.cpu_offload} and optimizer type {type(self.optimizer)}." \284f"Either disable fp16_master_weights_and_gradients or enable {self.zero_stage_string} Offload with DeepSpeedCPUAdam."285286if self.reduce_scatter and self.partition_gradients:287valid_reduce_scatter_dtypes = (torch.float16, torch.bfloat16, torch.float32)288assert self.communication_data_type in valid_reduce_scatter_dtypes, f"{self.zero_stage_string} supports {valid_reduce_scatter_dtypes} communication_data_type with reduce scatter enabled. Got: '{self.communication_data_type}'"289assert self.gradient_predivide_factor == 1.0, f"gradient_predivide_factor != 1.0 is not yet supported with {self.zero_stage_string} with reduce scatter enabled"290assert self.postscale_gradients, f"pre-scale gradients is not yet supported with {self.zero_stage_string} with reduce scatter enabled"291292# param flattened by groups293self.bit16_groups = []294self.bit16_groups_flat = []295296# param partitioned by data parallel degree297# this will contain a list of equal sized tensors298# each of which will be updated by a different process299self.parallel_partitioned_bit16_groups = []300301# a single 32-bit partition of the parallel partitioned parameters302# that this process will update303self.single_partition_of_fp32_groups = []304305# param partition info306307# These are the parameters in each group that will not be updated by this process directly308self.params_not_in_partition = []309310# These are the parameters that will be updated by this process directly311self.params_in_partition = []312313# Offset from the first parameter in the self.params_in_partition314# the parameter boundaries may not align with partition boundaries315# so we need to keep track of the offset316self.first_offset = []317318# number of elements per partition in each group319self.partition_size = []320321# align nccl all-gather send buffers to 4-byte boundary322self.nccl_start_alignment_factor = 2 # 4-byte alignment/sizeof(fp16) = 2323324assert (325allgather_bucket_size % self.nccl_start_alignment_factor == 0326), f"allgather_bucket_size must be a multiple of nccl_start_alignment_factor, {self.nccl_start_alignment_factor} "327328self.all_reduce_print = False329self.dtype = self.optimizer.param_groups[0]['params'][0].dtype330self.gradient_accumulation_dtype = gradient_accumulation_dtype331332if self.dtype != self.gradient_accumulation_dtype:333self.use_separate_grad_accum = True334else:335self.use_separate_grad_accum = False336if self.use_separate_grad_accum and not self.partition_gradients:337self.use_grad_accum_attribute = True338else:339self.use_grad_accum_attribute = False340341self.round_robin_bit16_groups = []342self.round_robin_bit16_indices = []343self.round_robin_bit16_meta = []344345# Use different parallel to do all_to_all_reduce related things346# padding on each partition for alignment purposes347self.groups_padding = []348# loop to deal with groups349for i, param_group in enumerate(self.optimizer.param_groups):350partition_id = dist.get_rank(group=self.real_dp_process_group[i])351352# push this group to list before modify353# TODO: Explore simplification that avoids the extra book-keeping by pushing the reordered group354trainable_parameters = []355for param in param_group['params']:356if param.requires_grad:357param.grad_accum = None358trainable_parameters.append(param)359self.bit16_groups.append(trainable_parameters)360361# not sure why apex was cloning the weights before flattening362# removing cloning here363364see_memory_usage(f"Before moving param group {i} to CPU")365# move all the parameters to cpu to free up GPU space for creating flat buffer366367# Create temp CPU param copies, free accelerator tensors368orig_group_numel = 0369for param in self.bit16_groups[i]:370orig_group_numel += param.numel()371param.cpu_data = opguard_wrapped__cpu_data_cpu(325)(param.data.cpu)372param.data = opguard_wrapped__data_to(326)(torch.empty(1).to, param.device)373374empty_cache()375see_memory_usage(f"After moving param group {i} to CPU", force=False)376377# Reorder group parameters for load balancing of gradient partitioning during backward among ranks.378# This ensures that gradients are reduced in a fashion such that ownership round robins among the ranks.379# For example, rather than 3 gradients (g_n+2, g_n+1, g_n) that are reduced consecutively belonging380# to the same rank, instead they will belong to 3 ranks (r_m+2, r_m+1, r_m).381if self.round_robin_gradients:382round_robin_tensors, round_robin_indices = self._round_robin_reorder(383self.bit16_groups[i], dist.get_world_size(group=self.real_dp_process_group[i]))384else:385round_robin_tensors = self.bit16_groups[i]386round_robin_indices = list(range(len(self.bit16_groups[i])))387388self.round_robin_bit16_groups.append(round_robin_tensors)389self.round_robin_bit16_indices.append(round_robin_indices)390391# Create meta tensors list, ordered according to round_robin_tensors392meta_tensors = []393for param in round_robin_tensors:394opguard_wrapped__meta_tensors_append(348)(meta_tensors.append, torch.zeros_like(param.cpu_data, device="meta"))395self.round_robin_bit16_meta.append(meta_tensors)396397# create flat buffer in CPU398flattened_buffer = self.flatten_dense_tensors_aligned(399self.round_robin_bit16_groups[i],400self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i]),401use_cpu_data=True)402403# free temp CPU params404for param in self.bit16_groups[i]:405del param.cpu_data406407# Move CPU flat tensor to the accelerator memory.408opguard_wrapped__bit16_groups_flat_append(362)(self.bit16_groups_flat.append, flattened_buffer.to(get_accelerator().current_device_name()))409del flattened_buffer410411see_memory_usage(f"After flattening and moving param group {i} to GPU", force=False)412413# Record padding required for alignment414if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1:415padding = self.bit16_groups_flat[i].numel() - orig_group_numel416else:417padding = 0418self.groups_padding.append(padding)419420if dist.get_rank(group=self.real_dp_process_group[i]) == 0:421see_memory_usage(f"After Flattening and after emptying param group {i} cache", force=False)422423# set model bit16 weight to slices of flattened buffer424self._update_model_bit16_weights(i)425426# divide the flat weights into near equal partition equal to the data parallel degree427# each process will compute on a different part of the partition428data_parallel_partitions = self.get_data_parallel_partitions(self.bit16_groups_flat[i], i)429self.parallel_partitioned_bit16_groups.append(data_parallel_partitions)430431# verify that data partition start locations are 4-byte aligned432for partitioned_data in data_parallel_partitions:433assert (partitioned_data.data_ptr() % (2 * self.nccl_start_alignment_factor) == 0)434435# A partition of the fp32 master weights that will be updated by this process.436# Note that the params in single_partition_of_fp32_groups is cloned and detached437# from the origin params of the model.438if not fp16_master_weights_and_gradients:439weights_partition = opguard_wrapped__weights_partition_detach(393)(self.parallel_partitioned_bit16_groups[i][partition_id].to(440self.device).clone().float().detach)441else:442weights_partition = opguard_wrapped__weights_partition_detach(396)(self.parallel_partitioned_bit16_groups[i][partition_id].to(443self.device).clone().half().detach)444445if self.cpu_offload:446weights_partition = get_accelerator().pin_memory(weights_partition)447448self.single_partition_of_fp32_groups.append(weights_partition)449450# Set local optimizer to have flat params of its own partition.451# After this, the local optimizer will only contain its own partition of params.452# In that case, the local optimizer only saves the states(momentum, variance, etc.) related to its partition's params(zero stage1).453self.single_partition_of_fp32_groups[454i].requires_grad = True # keep this in case internal optimizer uses it455param_group['params'] = [self.single_partition_of_fp32_groups[i]]456457partition_size = len(self.bit16_groups_flat[i]) / dist.get_world_size(group=self.real_dp_process_group[i])458params_in_partition, params_not_in_partition, first_offset = self.get_partition_info(459self.round_robin_bit16_groups[i], partition_size, partition_id)460461self.partition_size.append(partition_size)462self.params_in_partition.append(params_in_partition)463self.params_not_in_partition.append(params_not_in_partition)464self.first_offset.append(first_offset)465466self.reduce_bucket_size = int(reduce_bucket_size)467self.use_multi_rank_bucket_allreduce = use_multi_rank_bucket_allreduce468self.allgather_bucket_size = int(allgather_bucket_size)469470self.reduction_stream = None if get_accelerator().is_synchronized_device() else get_accelerator().Stream()471#self.copy_grad_stream = get_accelerator().Stream()472self.callback_queued = False473474self.param_dict = {}475476# map between param_id and bool to specify if a param is in this partition477self.is_param_in_current_partition = {}478479self.grads_in_ipg_bucket = []480self.params_in_ipg_bucket = []481self.elements_in_ipg_bucket = 0482self.params_already_reduced = []483self._release_ipg_buffers()484self.previous_reduced_grads = None485self.ipg_bucket_has_moe_params = False486487# simplified param id488self.param_id = {}489490#interesting code: unique ids being assigned to individual parameters491largest_param_numel = 0492count = 0493for i, params_group in enumerate(self.bit16_groups):494for param in params_group:495unique_id = id(param)496self.param_id[unique_id] = count497self.param_dict[count] = param498self.params_already_reduced.append(False)499if param.numel() > largest_param_numel:500largest_param_numel = param.numel()501count = count + 1502503for param_group in self.params_in_partition:504for param in param_group:505self.is_param_in_current_partition[self.get_param_id(param)] = True506507for param_group in self.params_not_in_partition:508for param in param_group:509self.is_param_in_current_partition[self.get_param_id(param)] = False510511if self.cpu_offload:512self.accumulated_grads_in_cpu = {}513self.norm_for_param_grads = {}514self.local_overflow = False515self.grad_position = {}516self.temp_grad_buffer_for_cpu_offload = torch.zeros(largest_param_numel,517device=self.device,518dtype=self.dtype)519if self.cpu_offload_pin_memory:520self.temp_grad_buffer_for_cpu_offload = get_accelerator().pin_memory(521self.temp_grad_buffer_for_cpu_offload)522self.temp_grad_buffer_for_gpu_offload = torch.zeros(largest_param_numel,523device=get_accelerator().current_device_name(),524dtype=self.dtype)525for i, params_group in enumerate(self.bit16_groups):526self.get_grad_position(i, self.params_in_partition[i], self.first_offset[i], self.partition_size[i])527528# mapping from parameter to partition that it belongs to529self.param_to_partition_ids = {}530531# stores if a partition has been reduced in this step532self.is_partition_reduced = {}533534# number of grads in partition that still need to be computed535self.remaining_grads_in_partition = {}536537# total number of grads in partition538self.total_grads_in_partition = {}539540# stores if a grad in a partition has been computed or not541self.is_grad_computed = {}542543# stores the offset at which a parameter gradient needs to be inserted in a partition544self.grad_partition_insertion_offset = {}545546# the offset in the gradient at which it must be inserted at the beginning of the partition547self.grad_start_offset = {}548549# will store the averaged gradients required by this partition550self.averaged_gradients = {}551552# For cpu_offload, will store the averaged gradients required by this partition553self.offload_gradient_dict = {}554555# store index of first parameter in each partition556self.first_param_index_in_partition = {}557558# initializes all data structures for implementing gradient partitioning559self.initialize_gradient_partitioning_data_structures()560561# resets the data structure value for the next backward propagation562self.reset_partition_gradient_structures()563564# creates backward hooks for gradient partitioning565self._grad_acc_hooks = []566if self.partition_gradients or self.overlap_comm:567self.create_reduce_and_remove_grad_hooks()568569self.custom_loss_scaler = False570self.external_loss_scale = None571572# we may have a way of fusing dynamic scale. Do not support for now573self.loss_scaler = CreateLossScaler(dtype=self.dtype,574static_loss_scale=static_loss_scale,575dynamic_scaling=dynamic_loss_scale,576dynamic_loss_args=dynamic_loss_args)577self.dynamic_loss_scale = self.loss_scaler.dynamic578579if self.dtype != torch.float16:580# Only fp16 should use dynamic loss scaling581assert self.loss_scaler.cur_scale == 1.0582assert not self.dynamic_loss_scale583584see_memory_usage("Before initializing optimizer states", force=True)585self.initialize_optimizer_states()586see_memory_usage("After initializing optimizer states", force=True)587588if dist.get_rank() == 0:589logger.info(f"optimizer state initialized")590591if dist.get_rank(group=self.dp_process_group) == 0:592see_memory_usage(f"After initializing ZeRO optimizer", force=True)593594self._link_all_hp_params()595self._hp_optimizer_states_linked = False596597self._enable_universal_checkpoint()598self._param_slice_mappings = self._create_param_mapping()599600def destroy(self):601for hook in self._grad_acc_hooks:602hook.remove()603self.print_rank_0("Removed grad acc hooks")604605def _enable_universal_checkpoint(self):606for lp_param_group in self.bit16_groups:607enable_universal_checkpoint(param_list=lp_param_group)608609def _create_param_mapping(self):610param_mapping = []611for i, _ in enumerate(self.optimizer.param_groups):612param_mapping_per_group = OrderedDict()613for lp in self.bit16_groups[i]:614if lp._hp_mapping is not None:615lp_name = self.param_names[lp]616param_mapping_per_group[lp_name] = lp._hp_mapping.get_hp_fragment_address()617param_mapping.append(param_mapping_per_group)618619return param_mapping620621def _link_all_hp_params(self):622dp_world_size = dist.get_world_size(group=self.dp_process_group)623if self.cpu_offload:624self._get_offload_gradient_dict()625626for i, _ in enumerate(self.optimizer.param_groups):627# Link bit16 and fp32 params in partition628partition_id = dist.get_rank(group=self.real_dp_process_group[i])629partition_size = self.bit16_groups_flat[i].numel() // dp_world_size630flat_hp_partition = self.single_partition_of_fp32_groups[i]631link_hp_params(lp_param_list=self.bit16_groups[i],632flat_hp_partition=flat_hp_partition,633gradient_dict=self.averaged_gradients,634offload_gradient_dict=self.offload_gradient_dict,635use_offload=self.cpu_offload,636param_group_index=i,637partition_start=partition_id * partition_size,638partition_size=partition_size,639dp_group=self.real_dp_process_group[i])640641def _lazy_init_hp_params_optimizer_state(self):642if not self._hp_optimizer_states_linked:643for i, _ in enumerate(self.optimizer.param_groups):644lazy_init_hp_params_optimizer_state(self.bit16_groups[i], self.single_partition_of_fp32_groups[i],645self.optimizer.state)646self._hp_optimizer_states_linked = True647648def is_moe_group(self, group):649return 'moe' in group and group['moe']650651def _configure_moe_settings(self):652# if we're using ZeRO stage 2, ensure contiguous gradients are used653if self.partition_gradients:654assert self.contiguous_gradients, "Contiguous Gradients in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE"655# NOTE: To run ZeRO stage 1 with MoE, we need to set self.contiguous_gradients to True or ignore the assertion656if not self.partition_gradients and not self.contiguous_gradients:657logger.warn(658"ZeRO Stage 1 has not been thoroughly tested with MoE. This configuration is still experimental.")659assert self.reduce_scatter, "Reduce Scatter in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE"660661assert any(662[self.is_moe_group(group) for group in self.optimizer.param_groups]663), "The model has moe layers, but None of the param groups are marked as MoE. Create a param group with 'moe' key set to True before creating optimizer"664self.is_moe_param_group = []665for i, group in enumerate(self.optimizer.param_groups):666if self.is_moe_group(group):667assert all([is_moe_param(param)668for param in group['params']]), "All params in MoE group must be MoE params"669self.real_dp_process_group[i] = self.expert_dp_process_group[group['name']]670self.partition_count[i] = dist.get_world_size(group=self.expert_dp_process_group[group['name']])671self.is_moe_param_group.append(True)672else:673self.is_moe_param_group.append(False)674675assert self.expert_dp_process_group is not None, "Expert data parallel group should be configured with MoE"676assert self.ep_process_group is not None, "Expert parallel group should be configured with MoE"677678def _update_model_bit16_weights(self, group_index):679updated_params = opguard_wrapped__updated_params_unflatten(633)(self.unflatten, self.bit16_groups_flat[group_index], self.round_robin_bit16_meta[group_index])680for p, q in zip(self.round_robin_bit16_groups[group_index], updated_params):681p.data = q.data682683# set model fp16 weight to slices of reordered flattened buffer684for param_index, param in enumerate(self.bit16_groups[group_index]):685new_index = self.round_robin_bit16_indices[group_index][param_index]686param.data = self.round_robin_bit16_groups[group_index][new_index].data687688def _round_robin_reorder(self, tensor_list, num_partitions):689690# disable round robin if need to debug something691# return tensor_list, list(range(len(tensor_list)))692693partition_tensors = {}694695for i, tensor in enumerate(tensor_list):696j = i % num_partitions697if not j in partition_tensors:698partition_tensors[j] = []699partition_tensors[j].append((i, tensor))700701reordered_tensors = []702reordered_indices = {}703704for partition_index in partition_tensors.keys():705for i, (original_index, tensor) in enumerate(partition_tensors[partition_index]):706reordered_indices[original_index] = len(reordered_tensors)707reordered_tensors.append(tensor)708709return reordered_tensors, reordered_indices710711def _release_ipg_buffers(self):712if self.contiguous_gradients:713self.ipg_buffer = None714self.grads_in_partition = None715self.grads_in_partition_offset = 0716717def initialize_optimizer_states(self):718719for i, group in enumerate(self.bit16_groups):720single_grad_partition = opguard_wrapped__single_grad_partition_zeros(674)(torch.zeros, int(self.partition_size[i]),721dtype=self.single_partition_of_fp32_groups[i].dtype,722device=self.device)723self.single_partition_of_fp32_groups[i].grad = get_accelerator().pin_memory(724single_grad_partition) if self.cpu_offload_pin_memory else single_grad_partition725726# Initialize the optimizer states with the flattened fp32 partition.727# State initialization for the Adagrad optimizer occurs at construction as opposed to other optimizers728# which do lazy initialization of the state at the first call to step.729if isinstance(self.optimizer, torch.optim.Adagrad):730self.optimizer = torch.optim.Adagrad(self.single_partition_of_fp32_groups, **self.optimizer.defaults)731732if not self.cpu_offload:733for group in self.single_partition_of_fp32_groups:734group.grad = None #class init735736return737738#########################################################################739#################### ZeRO Stage 1 - reduce gradients ####################740#########################################################################741def reduce_gradients(self, pipeline_parallel=False):742world_size = dist.get_world_size(self.dp_process_group)743my_rank = dist.get_rank(self.dp_process_group)744745# with PP we must create ipg buffer, since backward is handled outside zero746if pipeline_parallel and self.contiguous_gradients:747self.ipg_buffer = []748buf_0 = opguard_wrapped__buf_0_empty(702)(torch.empty, int(self.reduce_bucket_size),749dtype=self.dtype,750device=get_accelerator().current_device_name())751self.ipg_buffer.append(buf_0)752self.ipg_index = 0753754if not self.overlap_comm:755for i, group in enumerate(self.bit16_groups):756for param in group:757grad_reduc = self.get_gradient_for_reduction(param)758if grad_reduc is not None:759self.reduce_ready_partitions_and_remove_grads(param, i)760# reduce any pending grads in either hook/non-hook case761self.overlapping_partition_gradients_reduce_epilogue()762763#########################################################################764#########################ZeRO Partition Gradients########################765#########################################################################766767def get_first_param_index(self, group_id, param_group, partition_id):768for index, param in enumerate(param_group):769param_id = self.get_param_id(param)770if partition_id in self.param_to_partition_ids[group_id][param_id]:771return index772return None773774def initialize_gradient_partitioning_data_structures(self):775776for i, param_group in enumerate(self.round_robin_bit16_groups):777total_partitions = dist.get_world_size(group=self.real_dp_process_group[i])778779self.param_to_partition_ids[i] = {}780self.is_partition_reduced[i] = {}781self.total_grads_in_partition[i] = {}782self.remaining_grads_in_partition[i] = {}783self.is_grad_computed[i] = {}784self.grad_partition_insertion_offset[i] = {}785self.grad_start_offset[i] = {}786self.first_param_index_in_partition[i] = {}787788for partition_id in range(total_partitions):789self.is_grad_computed[i][partition_id] = {}790self.grad_partition_insertion_offset[i][partition_id] = {}791self.grad_start_offset[i][partition_id] = {}792self.total_grads_in_partition[i][partition_id] = 0793self.initialize_gradient_partition(i, param_group, partition_id)794self.is_partition_reduced[i][partition_id] = False795self.first_param_index_in_partition[i][partition_id] = self.get_first_param_index(796i, param_group, partition_id)797798def independent_gradient_partition_epilogue(self):799self.report_ipg_memory_usage(f"In ipg_epilogue before reduce_ipg_grads", 0)800self.reduce_ipg_grads()801self.report_ipg_memory_usage(f"In ipg_epilogue after reduce_ipg_grads", 0)802803# if dist.get_rank() == 0:804# logger.info("Params already reduced %s", self.params_already_reduced)805for i in range(len(self.params_already_reduced)):806self.params_already_reduced[i] = False807808if self.overlap_comm:809if not get_accelerator().resolves_data_dependency():810get_accelerator().synchronize()811# It is safe to clear previously reduced grads of other partitions812self._clear_previous_reduced_grads()813814if self.cpu_offload is False:815for i, _ in enumerate(self.bit16_groups):816817if not i in self.averaged_gradients or self.averaged_gradients[i] is None:818self.averaged_gradients[i] = self.get_flat_partition(819self.params_in_partition[i],820self.first_offset[i],821self.partition_size[i],822dtype=self.gradient_accumulation_dtype,823device=get_accelerator().current_device_name(),824return_tensor_list=True)825else:826avg_new = self.get_flat_partition(self.params_in_partition[i],827self.first_offset[i],828self.partition_size[i],829dtype=self.gradient_accumulation_dtype,830device=get_accelerator().current_device_name(),831return_tensor_list=True)832833for accumulated_grad, new_avg_grad in zip(self.averaged_gradients[i], avg_new):834accumulated_grad.add_(new_avg_grad)835836self._release_ipg_buffers()837838# No need to keep the gradients anymore.839# All gradients required by the step840# are in self.averaged_gradients841self.zero_grad(set_to_none=True)842see_memory_usage(f"End ipg_epilogue")843844# resets all partition to no reduced845# sets remaining grads to the total number of grads in each partition846# set is grad computed to false for all grads in partition847def reset_partition_gradient_structures(self):848for i, _ in enumerate(self.bit16_groups):849total_partitions = dist.get_world_size(group=self.real_dp_process_group[i])850for partition_id in range(total_partitions):851self.is_partition_reduced[i][partition_id] = False852self.remaining_grads_in_partition[i][partition_id] = self.total_grads_in_partition[i][partition_id]853854for param_id in self.is_grad_computed[i][partition_id]:855self.is_grad_computed[i][partition_id][param_id] = False856857def initialize_gradient_partition(self, i, param_group, partition_id):858859def set_key_value_list(dictionary, key, value):860if key in dictionary:861dictionary[key].append(value)862else:863dictionary[key] = [value]864865def increment_value(dictionary, key):866if key in dictionary:867dictionary[key] += 1868else:869dictionary[key] = 1870871partition_size = self.partition_size[i]872873start_index = partition_size * partition_id874end_index = partition_size * (partition_id + 1)875876current_index = 0877first_offset = 0878879for param in param_group:880881param_size = param.numel()882param_id = self.get_param_id(param)883884if start_index <= current_index < end_index:885set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id)886increment_value(self.total_grads_in_partition[i], partition_id)887888self.is_grad_computed[i][partition_id][param_id] = False889890self.grad_partition_insertion_offset[i][partition_id][param_id] = current_index - start_index891self.grad_start_offset[i][partition_id][param_id] = 0892893elif current_index < start_index < (current_index + param_size):894assert (first_offset == 0895), "This can happen either zero or only once as this must be the first tensor in the partition"896first_offset = start_index - current_index897898set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id)899increment_value(self.total_grads_in_partition[i], partition_id)900901self.is_grad_computed[i][partition_id][param_id] = False902903self.grad_partition_insertion_offset[i][partition_id][param_id] = 0904self.grad_start_offset[i][partition_id][param_id] = first_offset905906current_index = current_index + param_size907908def overlapping_partition_gradients_reduce_epilogue(self):909self.independent_gradient_partition_epilogue()910911def fill_grad_accum_attribute(self):912for group in self.bit16_groups:913for param in group:914if param.grad is not None:915if param.grad_accum is None:916param.grad_accum = param.grad.to(self.gradient_accumulation_dtype)917else:918param.grad_accum.add_(919param.grad.to(self.gradient_accumulation_dtype).view(param.grad_accum.shape))920param.grad = None921922def get_gradient_for_reduction(self, param):923if self.use_grad_accum_attribute:924return param.grad_accum.to(self.dtype) if param.grad_accum is not None else None925else:926return param.grad927928def get_param_gradient_attribute(self, param):929return param.grad_accum if self.use_grad_accum_attribute else param.grad930931# Clear the tensor the reduction gradient attribute is pointing to932def clear_grad_attribute(self, param):933if self.use_grad_accum_attribute:934param.grad_accum = None935else:936param.grad = None937938def create_reduce_and_remove_grad_hooks(self):939self.grad_accs = []940for i, param_group in enumerate(self.bit16_groups):941for param in param_group:942if param.requires_grad:943944def wrapper(param, i):945param_tmp = param.expand_as(param)946grad_acc = param_tmp.grad_fn.next_functions[0][0]947948def reduce_partition_and_remove_grads(*notneeded):949self.reduce_ready_partitions_and_remove_grads(param, i)950951self._grad_acc_hooks.append(grad_acc.register_hook(reduce_partition_and_remove_grads))952self.grad_accs.append(grad_acc)953954wrapper(param, i)955956def get_param_id(self, param):957unique_id = id(param)958return self.param_id[unique_id]959960def report_ipg_memory_usage(self, tag, param_elems):961elem_count = self.elements_in_ipg_bucket + param_elems962percent_of_bucket_size = (100.0 * elem_count) // self.reduce_bucket_size963see_memory_usage(964f"{tag}: elems in_bucket {self.elements_in_ipg_bucket} param {param_elems} max_percent {percent_of_bucket_size}"965)966967# create a flat tensor aligned at the alignment boundary968def flatten_dense_tensors_aligned(self, tensor_list, alignment, use_cpu_data=False):969tensor_list = [param.cpu_data for param in tensor_list] if use_cpu_data else tensor_list970return opguard_wrapped__align_dense_tensors_flatten(924)(self.flatten, align_dense_tensors(tensor_list, alignment))971972############### Independent Partition Gradient ########################973def reduce_independent_p_g_buckets_and_remove_grads(self, param, i):974975grad_reduc = self.get_gradient_for_reduction(param)976if self.elements_in_ipg_bucket + param.numel() > self.reduce_bucket_size:977self.report_ipg_memory_usage("In ipg_remove_grads before reduce_ipg_grads", param.numel())978self.reduce_ipg_grads()979if self.contiguous_gradients and self.overlap_comm:980# Swap ipg_index between 0 and 1981self.ipg_index = 1 - self.ipg_index982self.report_ipg_memory_usage("In ipg_remove_grads after reduce_ipg_grads", param.numel())983984param_id = self.get_param_id(param)985assert self.params_already_reduced[param_id] == False, \986f"The parameter {param_id} has already been reduced. \987Gradient computed twice for this partition. \988Multiple gradient reduction is currently not supported"989990if self.contiguous_gradients:991if param.numel() > self.reduce_bucket_size:992self.extra_large_param_to_reduce = param993else:994# keeping the gradients contiguous to prevent memory fragmentation, and avoid flattening995new_grad_tensor = opguard_wrapped__new_grad_tensor_narrow(949)(self.ipg_buffer[self.ipg_index].narrow, 0, self.elements_in_ipg_bucket, param.numel())996opguard_wrapped__new_grad_tensor_copy_(950)(new_grad_tensor.copy_, grad_reduc.view(-1))997grad_reduc.data = opguard_wrapped__data_view_as(951)(new_grad_tensor.data.view_as, grad_reduc)998999self.elements_in_ipg_bucket += param.numel()10001001assert grad_reduc is not None, f"rank {dist.get_rank()} - Invalid to reduce Param {param_id} with None gradient"10021003self.grads_in_ipg_bucket.append(grad_reduc)1004self.params_in_ipg_bucket.append((i, param, param_id))10051006#make sure the average tensor function knows how to average the gradients1007if is_moe_param(param):1008self.ipg_bucket_has_moe_params = True10091010self.report_ipg_memory_usage("End ipg_remove_grads", 0)10111012def print_rank_0(self, message):1013if dist.get_rank() == 0:1014logger.info(message)10151016def gradient_reduction_w_predivide(self, tensor):10171018dp_world_size = dist.get_world_size(group=self.dp_process_group)10191020tensor_to_allreduce = tensor10211022if self.communication_data_type != tensor.dtype:1023tensor_to_allreduce = tensor.to(self.communication_data_type)10241025if self.postscale_gradients:1026if self.gradient_predivide_factor != 1.0:1027tensor_to_allreduce.mul_(1. / self.gradient_predivide_factor)10281029dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group)10301031if self.gradient_predivide_factor != dp_world_size:1032tensor_to_allreduce.mul_(self.gradient_predivide_factor /1033(dp_world_size / float(self.sequence_parallel_size)))1034else:1035tensor_to_allreduce.div_(dp_world_size / float(self.sequence_parallel_size))1036dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group)10371038if self.communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce:1039tensor.copy_(tensor_to_allreduce)10401041return tensor10421043def allreduce_and_copy_with_multiple_ranks(self,1044small_bucket,1045log=None,1046divide=True,1047process_group=None,1048bucket_ranks=None):1049process_group = self.dp_process_group if process_group is None else process_group1050allreduced = self.allreduce_bucket(small_bucket, log=log, divide=divide, process_group=process_group)1051for buf, synced, bucket_rank in zip(small_bucket, opguard_wrapped__allreduced_unflatten(1005)(self.unflatten, allreduced, small_bucket), bucket_ranks):1052if dist.get_rank(group=process_group) == bucket_rank:1053opguard_wrapped__buf_copy_(1007)(buf.copy_, synced)10541055def allreduce_and_scatter(self, bucket, numel_per_bucket=500000000, log=None, divide=True, process_group=None):1056small_bucket = []1057small_bucket_ranks = []1058numel = 01059allreduce_sizes = []10601061for i, bucket_elem in enumerate(bucket):1062rank, tensor = bucket_elem1063small_bucket.append(tensor)1064small_bucket_ranks.append(rank)1065numel = numel + tensor.numel()1066if numel > numel_per_bucket:1067self.allreduce_and_copy_with_multiple_ranks(small_bucket,1068log=None,1069divide=divide,1070process_group=process_group,1071bucket_ranks=small_bucket_ranks)1072small_bucket = []1073small_bucket_ranks = []1074numel = 010751076if len(small_bucket) > 0:1077self.allreduce_and_copy_with_multiple_ranks(small_bucket,1078log=None,1079divide=divide,1080process_group=process_group,1081bucket_ranks=small_bucket_ranks)10821083def average_tensor(self, tensor):1084if self.overlap_comm:1085stream = self.reduction_stream1086if not get_accelerator().resolves_data_dependency():1087stream.wait_stream(get_accelerator().current_stream())1088else:1089stream = get_accelerator().current_stream()10901091with get_accelerator().stream(stream):1092if not self.reduce_scatter:1093self.gradient_reduction_w_predivide(tensor)1094return10951096# Accumulate destination ranks and bucket offsets for each gradient slice.1097# Note: potential future optimization, record access pattern of parameters1098# in backward pass and partition gradients w.r.t. access pattern so that our1099# bucket is guaranteed to be contiguous w.r.t. ranks1100rank_and_offsets = []1101real_dp_process_group = []1102curr_size = 01103prev_id, prev_process_group = -1, None11041105process_group = self.dp_process_group1106# count = 01107for i, param, param_id in self.params_in_ipg_bucket:11081109process_group = self.dp_process_group1110grad_reduc = self.get_gradient_for_reduction(param)1111#Averages gradients at parameter level if ipg has a moe param1112#Otherwise averaging is done at the entire buffer level at the end of the loop1113# MoE param have different groups1114if self.ipg_bucket_has_moe_params:1115process_group = self.expert_dp_process_group[param.group_name] if is_moe_param(1116param) else self.dp_process_group1117grad_reduc.data.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size))11181119partition_ids = self.param_to_partition_ids[i][param_id]1120assert all([p_id < dist.get_world_size(group=process_group) for p_id in partition_ids1121]), f"world size {dist.get_world_size(group=process_group)} and p_ids: {partition_ids}"1122partition_size = self.partition_size[i]1123# Get all partition ids + their offsets1124partition_ids_w_offsets = []1125for partition_id in partition_ids:1126offset = self.grad_start_offset[i][partition_id][param_id]1127partition_ids_w_offsets.append((partition_id, offset))1128partition_ids_w_offsets.sort(key=lambda t: t[1])11291130# Calculate rank and offsets for grad slices1131for idx in range(len(partition_ids_w_offsets)):1132partition_id, offset = partition_ids_w_offsets[idx]11331134# if dist.get_rank() == 0 and count < 100:1135# print(f"Rank {dist.get_rank()} rank offset id {idx} calculated dp size {dist.get_world_size(group=process_group)} real dp size {dist.get_world_size(self.real_dp_process_group[i])} and dst: {partition_id}")1136# count += 111371138# Calculate numel for grad slice depending on partition location1139if idx == len(partition_ids_w_offsets) - 1:1140# Last partition_id uses its own offset1141numel = param.numel() - offset1142else:1143# Set numel to next partition's offset1144numel = partition_ids_w_offsets[idx + 1][1] - offset11451146# Merge bucket ranges if they belong to the same rank1147if partition_id == prev_id and process_group == prev_process_group:1148prev_pid, prev_size, prev_numel = rank_and_offsets[-1]1149rank_and_offsets[-1] = (prev_pid, prev_size, prev_numel + numel)1150else:1151rank_and_offsets.append((partition_id, curr_size, numel))1152real_dp_process_group.append(process_group)1153curr_size += numel1154prev_id, prev_process_group = partition_id, process_group11551156if not self.ipg_bucket_has_moe_params:1157opguard_wrapped__tensor_div_(1111)(tensor.div_, dist.get_world_size(group=self.dp_process_group) / float(self.sequence_parallel_size))11581159buckets = {}1160for i, (dst, bucket_offset, numel) in enumerate(rank_and_offsets):1161grad_slice = opguard_wrapped__grad_slice_narrow(1115)(tensor.narrow, 0, int(bucket_offset), int(numel))1162bucket_key = real_dp_process_group[i] if self.use_multi_rank_bucket_allreduce else (1163dst, real_dp_process_group[i])1164if bucket_key not in buckets:1165buckets[bucket_key] = []1166if self.use_multi_rank_bucket_allreduce:1167buckets[bucket_key].append((dst, grad_slice))1168else:1169buckets[bucket_key].append(grad_slice)11701171for bucket_key in buckets:1172if self.use_multi_rank_bucket_allreduce:1173self.allreduce_and_scatter(buckets[bucket_key],1174numel_per_bucket=self.reduce_bucket_size,1175divide=False,1176process_group=bucket_key)1177else:1178dst, process_group = bucket_key1179self.allreduce_no_retain(buckets[bucket_key],1180numel_per_bucket=self.reduce_bucket_size,1181rank=dst,1182divide=False,1183process_group=process_group)11841185##############################################################################1186############################# CPU Offload Methods#############################1187##############################################################################1188def get_grad_position(self, group_id, tensor_list, first_offset, partition_size):1189current_offset = 011901191for i, tensor in enumerate(tensor_list):1192param_id = self.get_param_id(tensor)1193param_start_offset = 011941195num_elements = tensor.numel()11961197# we need to offset to get to the right element1198if i == 0 and first_offset > 0:1199tensor_offset = first_offset1200num_elements = num_elements - tensor_offset1201param_start_offset = first_offset12021203# we dont need all elements of the tensor1204if num_elements > (partition_size - current_offset):1205num_elements = partition_size - current_offset12061207self.grad_position[param_id] = [1208int(group_id), int(param_start_offset),1209int(current_offset), int(num_elements)1210]1211current_offset += num_elements12121213def update_overflow_tracker_for_param_grad(self, param):1214grad_accum = self.get_param_gradient_attribute(param)1215if grad_accum is not None and self._has_inf_or_nan(grad_accum.data):1216self.local_overflow = True12171218def _get_offload_gradient_dict(self):1219for param_group_index, _ in enumerate(self.optimizer.param_groups):1220self.offload_gradient_dict[param_group_index] = []1221for lp_param in self.params_in_partition[param_group_index]:1222param_id = self.get_param_id(lp_param)1223[_, _, dest_offset, num_elements] = self.grad_position[param_id]1224dest_tensor = self.single_partition_of_fp32_groups[param_group_index].grad.view(-1).narrow(12250, dest_offset, num_elements)1226self.offload_gradient_dict[param_group_index].append(dest_tensor)12271228def async_accumulate_grad_in_cpu_via_gpu(self, param):1229param_id = self.get_param_id(param)12301231[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]12321233# copy to a preexisiting buffer to avoid memory allocation penalty1234dest_buffer = self.temp_grad_buffer_for_gpu_offload.view(-1).narrow(0, 0, param.numel())12351236#buffer for storing gradients for this parameter in CPU1237def buffer_to_accumulate_to_in_cpu():1238if not self.fp16_master_weights_and_gradients:1239buffer = torch.zeros(param.numel(), dtype=param.dtype, device=self.device)1240return get_accelerator().pin_memory(buffer) if self.cpu_offload_pin_memory else buffer1241else:1242return self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements)12431244#accumulate gradients into param.grad_accum or parts of it that belongs to this partition1245def accumulate_gradients():1246grad_accum = self.get_param_gradient_attribute(param)1247if not self.fp16_master_weights_and_gradients:1248dest_buffer.copy_(self.accumulated_grads_in_cpu[param_id].view(-1), non_blocking=True)1249grad_accum.data.view(-1).add_(dest_buffer)1250else:1251dest_buffer.narrow(0, source_offset,1252num_elements).copy_(self.accumulated_grads_in_cpu[param_id].view(-1),1253non_blocking=True)1254grad_accum.data.view(-1).narrow(0, source_offset,1255num_elements).add_(dest_buffer.narrow(0, source_offset, num_elements))12561257#move accumulated gradients back to CPU1258def copy_gradients_to_cpu():1259grad_accum = self.get_param_gradient_attribute(param)1260if not self.fp16_master_weights_and_gradients:1261self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1), non_blocking=True)1262else:1263self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1).narrow(12640, source_offset, num_elements),1265non_blocking=True)12661267if param_id not in self.accumulated_grads_in_cpu:1268self.accumulated_grads_in_cpu[param_id] = buffer_to_accumulate_to_in_cpu()12691270if self.micro_step_id > 0:1271accumulate_gradients()12721273# at the boundary we will send 32bit directly1274if not self.is_gradient_accumulation_boundary:1275copy_gradients_to_cpu()12761277def set_norm_for_param_grad(self, param):1278param_id = self.get_param_id(param)1279grad_accum = self.get_param_gradient_attribute(param)1280accumulated_grad = self.accumulated_grads_in_cpu[1281param_id] if self.gradient_accumulation_steps > 1 else grad_accum12821283[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]12841285start = source_offset1286accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements)12871288self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2)12891290def set_norm_for_param_grad_in_gpu(self, param):1291param_id = self.get_param_id(param)1292grad_accum = self.get_param_gradient_attribute(param)1293if grad_accum is None:1294accumulated_grad = param.grad1295else:1296accumulated_grad = grad_accum12971298[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]12991300start = source_offset1301accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements)13021303self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2)13041305def async_inplace_copy_grad_to_fp32_buffer_from_gpu(self, param):1306param_id = self.get_param_id(param)13071308[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]13091310dest_tensor = self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements)13111312grad_accum = self.get_param_gradient_attribute(param)1313if grad_accum is None:1314src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements)1315else:1316src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements)1317if not self.fp16_master_weights_and_gradients:1318src_tensor = src_tensor.float()13191320dest_tensor.copy_(src_tensor, non_blocking=True)1321param.grad = None #offload only13221323def complete_grad_norm_calculation_for_cpu_offload(self, params):1324total_norm = 0.01325norm_type = 2.01326for p in params:1327# Pipeline parallelism may replicate parameters. Avoid multi-counting.1328if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated:1329continue13301331if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0):1332param_id = self.get_param_id(p)1333# as some model have trainable parameters but skipped in training,1334# their backward hooks in self.create_reduce_and_remove_grad_hooks() will not run,1335# so they have no norm_for_param_grads1336if param_id in self.norm_for_param_grads:1337param_norm = self.norm_for_param_grads[param_id]1338total_norm += param_norm.item()**21339else:1340# As unused parameters in modules may not be expected sometimes,1341# add an explicit error msg when it occurred and an option to1342# avoid the error1343assert self.ignore_unused_parameters, """1344This assert indicates that your module has parameters that1345were not used in producing loss.1346You can avoid this assert by1347(1) enable ignore_unused_parameters option in zero_optimization config;1348(2) making sure all trainable parameters and `forward` function1349outputs participate in calculating loss.1350"""13511352# Sum across all model parallel GPUs.1353total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)])1354dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=self.dp_process_group)13551356self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM)13571358total_norm = total_norm_cuda[0].item()**(1. / norm_type)13591360if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm:1361total_norm = -113621363return total_norm13641365############################################################################################1366def copy_grads_in_partition(self, param):1367if self.cpu_offload:13681369if self.gradient_accumulation_steps > 1:1370self.async_accumulate_grad_in_cpu_via_gpu(param)13711372if self.is_gradient_accumulation_boundary:1373self.set_norm_for_param_grad_in_gpu(param)13741375self.update_overflow_tracker_for_param_grad(param)13761377self.async_inplace_copy_grad_to_fp32_buffer_from_gpu(param)13781379return1380#print(f"ID {self.get_param_id(param)} grad norm {param.grad.norm()}")1381if self.grads_in_partition is None:1382self.grads_in_partition_offset = 01383total_size = 01384for group in self.params_in_partition:1385for param_in_partition in group:1386total_size += param_in_partition.numel()13871388see_memory_usage(f"before copying {total_size} gradients into partition")1389self.grads_in_partition = opguard_wrapped__grads_in_partition_empty(1343)(torch.empty, int(total_size),1390dtype=self.dtype,1391device=get_accelerator().current_device_name())1392see_memory_usage(f"after copying {total_size} gradients into partition")13931394grad_reduc = self.get_gradient_for_reduction(param)1395# The allreduce buffer will be rewritten. Copy the gradients in partition to a new buffer1396new_grad_tensor = opguard_wrapped__new_grad_tensor_narrow(1350)(self.grads_in_partition.view(-1).narrow, 0, self.grads_in_partition_offset, param.numel())1397opguard_wrapped__new_grad_tensor_copy_(1351)(new_grad_tensor.copy_, grad_reduc.view(-1))1398grad_reduc.data = opguard_wrapped__data_view_as(1352)(new_grad_tensor.data.view_as, grad_reduc)1399#print(f"Grad norm after copy to contiguous_buffer {param.grad.data.norm()}")1400self.grads_in_partition_offset += param.numel()14011402def reduce_ipg_grads(self):1403if self.contiguous_gradients:1404if self.extra_large_param_to_reduce is not None:1405assert len(self.params_in_ipg_bucket) == 1, "more than 1 param in ipg bucket, this shouldn't happen"1406_, _, param_id = self.params_in_ipg_bucket[0]1407assert self.get_param_id(self.extra_large_param_to_reduce1408) == param_id, "param in ipg bucket does not match extra-large param"1409extra_large_grad_reduc = self.get_gradient_for_reduction(self.extra_large_param_to_reduce)1410self.average_tensor(extra_large_grad_reduc.view(-1))1411self.extra_large_param_to_reduce = None1412else:1413opguard_wrapped__narrow_average_tensor(1367)(self.average_tensor, self.ipg_buffer[self.ipg_index].narrow(0, 0, self.elements_in_ipg_bucket))1414else:1415self.buffered_reduce_fallback(None,1416self.grads_in_ipg_bucket,1417elements_per_buffer=self.elements_in_ipg_bucket)14181419if self.overlap_comm:1420stream = self.reduction_stream1421elif self.cpu_offload:1422# TODO: copy_grad_stream is disabled because of race with reduce. This hurts perf and should be fixed.1423# get_accelerator().synchronize()1424# stream = self.copy_grad_stream1425stream = get_accelerator().current_stream()1426else:1427stream = get_accelerator().current_stream()14281429with get_accelerator().stream(stream):1430for _, param, param_id in self.params_in_ipg_bucket:14311432assert self.params_already_reduced[param_id] == False, \1433f"The parameter {param_id} has already been reduced. \1434Gradient computed twice for this partition. \1435Multiple gradient reduction is currently not supported"14361437self.params_already_reduced[param_id] = True1438if self.partition_gradients:1439if not self.is_param_in_current_partition[param_id]:1440if self.overlap_comm and self.contiguous_gradients is False:1441# Clear grads of other partitions during the next reduction1442# to avoid clearing them before the reduction is complete.1443if self.previous_reduced_grads is None:1444self.previous_reduced_grads = []1445self.previous_reduced_grads.append(param)1446else:1447self.clear_grad_attribute(param)1448elif self.contiguous_gradients:1449self.copy_grads_in_partition(param)1450else: # zero stage 1 - partition only optimizer state1451if self.contiguous_gradients and self.is_param_in_current_partition[param_id]:1452self.copy_grads_in_partition(param)14531454self.grads_in_ipg_bucket = []1455self.params_in_ipg_bucket = []1456self.ipg_bucket_has_moe_params = False1457self.elements_in_ipg_bucket = 01458#####################################################################14591460def reduce_ready_partitions_and_remove_grads(self, param, i):1461if self.partition_gradients or self.is_gradient_accumulation_boundary:1462self.reduce_independent_p_g_buckets_and_remove_grads(param, i)14631464def zero_reduced_gradients(self, partition_id, i):14651466def are_all_related_partitions_reduced(params_id):1467for partition_id in self.param_to_partition_ids[i][params_id]:1468if not self.is_partition_reduced[i][partition_id]:1469return False1470return True14711472for params_id in self.is_grad_computed[i][partition_id]:1473if are_all_related_partitions_reduced(params_id):1474self.param_dict[params_id].grad = None # dead code14751476def flatten_and_print(self, message, tensors, start=0, n=5):1477flatten_tensor = self.flatten(tensors)14781479def print_func():1480logger.info(flatten_tensor.contiguous().view(-1).narrow(0, start, n))14811482self.sequential_execution(print_func, message)14831484def get_grads_to_reduce(self, i, partition_id):14851486def get_reducible_portion(key):1487grad = self.param_dict[key].grad1488total_elements = grad.numel()1489start = self.grad_start_offset[i][partition_id][key]1490num_elements = min(total_elements - start,1491self.partition_size[i] - self.grad_partition_insertion_offset[i][partition_id][key])1492if not pg_correctness_test:1493if num_elements == total_elements:1494return grad1495else:1496return grad.contiguous().view(-1).narrow(0, int(start), int(num_elements))1497else:1498if num_elements == total_elements:1499return grad.clone()1500else:1501return grad.clone().contiguous().view(-1).narrow(0, int(start), int(num_elements))15021503grads_to_reduce = []1504for key in self.is_grad_computed[i][partition_id]:1505grad = get_reducible_portion(key)1506grads_to_reduce.append(grad)1507return grads_to_reduce15081509def sequential_execution(self, function, message, group=None):1510if group is None:1511group = self.dp_process_group1512if dist.get_rank(group=group) == 0:1513logger.info(message)1514for id in range(dist.get_world_size(group=group)):1515if id == dist.get_rank(group=group):1516function()1517dist.barrier(group=group)15181519def set_none_gradients_to_zero(self, i, partition_id):1520for param_id in self.is_grad_computed[i][partition_id]:1521param = self.param_dict[param_id]1522if param.grad is None:1523param.grad = torch.zero_like(param)15241525######################Reduction Related Methods##############################1526def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_group=None):1527tensor = opguard_wrapped__tensor_flatten(1481)(self.flatten, bucket)15281529process_group = self.dp_process_group if process_group is None else process_group15301531tensor_to_allreduce = tensor15321533if pg_correctness_test or self.sequence_parallel_size > 1:1534communication_data_type = torch.float321535else:1536communication_data_type = self.communication_data_type15371538if communication_data_type != tensor.dtype:1539tensor_to_allreduce = tensor.to(communication_data_type)15401541if divide:1542tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size))15431544if rank is None:1545# "All Reducing"1546dist.all_reduce(tensor_to_allreduce, group=process_group)1547else:1548global_rank = dist.get_global_rank(process_group, rank)1549dist.reduce(tensor_to_allreduce, global_rank, group=process_group)15501551if communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce:1552if rank is None or rank == dist.get_rank(group=process_group):1553tensor.copy_(tensor_to_allreduce)15541555return tensor15561557def _clear_previous_reduced_grads(self):1558if self.previous_reduced_grads is not None:1559for param in self.previous_reduced_grads:1560self.clear_grad_attribute(param)1561self.previous_reduced_grads = None15621563# if rank is specified do a reduction instead of an allreduce1564def allreduce_and_copy(self, small_bucket, rank=None, log=None, divide=True, process_group=None):1565process_group = self.dp_process_group if process_group is None else process_group1566if self.overlap_comm:1567if not get_accelerator().resolves_data_dependency():1568get_accelerator().synchronize()1569# It is safe to clear the previously reduced grads of other partitions1570self._clear_previous_reduced_grads()1571stream = self.reduction_stream1572else:1573stream = get_accelerator().current_stream()15741575with get_accelerator().stream(stream):1576allreduced = self.allreduce_bucket(1577small_bucket,1578rank=rank,1579log=log,1580divide=divide,1581process_group=process_group,1582)1583if rank is None or rank == dist.get_rank(group=self.dp_process_group):1584for buf, synced in zip(small_bucket, opguard_wrapped__allreduced_unflatten(1538)(self.unflatten, allreduced, small_bucket)):1585opguard_wrapped__buf_copy_(1539)(buf.copy_, synced)15861587def allreduce_no_retain(1588self,1589bucket,1590numel_per_bucket=500000000,1591rank=None,1592log=None,1593divide=True,1594process_group=None,1595):1596small_bucket = []1597numel = 01598for tensor in bucket:1599small_bucket.append(tensor)1600numel = numel + tensor.numel()1601if numel > numel_per_bucket:1602self.allreduce_and_copy(small_bucket, rank=rank, log=None, divide=divide, process_group=process_group)1603small_bucket = []1604numel = 016051606if len(small_bucket) > 0:1607self.allreduce_and_copy(small_bucket, rank=rank, log=log, divide=divide, process_group=process_group)16081609# allows using reduction of gradients instead of using all_reduce16101611def buffered_reduce_fallback(self, rank, grads, elements_per_buffer=500000000, log=None):1612split_buckets = split_half_float_double(grads)16131614for i, bucket in enumerate(split_buckets):1615self.allreduce_no_retain(bucket, numel_per_bucket=elements_per_buffer, rank=rank, log=log)16161617#############################################################################1618#############################################################################1619#############################################################################16201621# views the tensor as multiple partitions and returns1622# those partitions1623def get_data_parallel_partitions(self, tensor, group_id):1624partitions = []16251626dp = dist.get_world_size(group=self.real_dp_process_group[group_id])1627# dp_id = dist.get_rank(group=self.real_dp_process_group[group_id])16281629total_num_elements = tensor.numel()16301631base_size = total_num_elements // dp1632remaining = total_num_elements % dp16331634start = 01635for id in range(dp):1636partition_size = base_size1637if id < remaining:1638partition_size = partition_size + 11639opguard_wrapped__partitions_append(1593)(partitions.append, tensor.narrow(0, start, partition_size))1640start = start + partition_size1641return partitions16421643def get_partition_info(self, tensor_list, partition_size, partition_id):1644params_in_partition = []1645params_not_in_partition = []16461647start_index = partition_size * partition_id1648end_index = partition_size * (partition_id + 1)16491650current_index = 01651first_offset = 016521653for tensor in tensor_list:16541655tensor_size = tensor.numel()16561657if start_index <= current_index < end_index:1658params_in_partition.append(tensor)16591660elif current_index < start_index < (current_index + tensor_size):1661params_in_partition.append(tensor)16621663assert (first_offset == 01664), "This can happen either zero or only once as this must be the first tensor in the partition"1665first_offset = start_index - current_index16661667else:1668params_not_in_partition.append(tensor)16691670current_index = current_index + tensor_size16711672return params_in_partition, params_not_in_partition, first_offset16731674def zero_grad(self, set_to_none=True):1675"""1676Zero FP16 parameter grads.1677"""1678# FP32 grad should never exist.1679# For speed, set model fp16 grad to None by default1680# zero all pointers to grad tensors1681for group in self.bit16_groups:1682for p in group:1683if set_to_none:1684p.grad = None # epilogue and in step1685p.grad_accum = None1686else:1687if p.grad is not None:1688p.grad.detach_()1689p.grad.zero_()16901691def _model_parallel_all_reduce(self, tensor, op):1692""" Perform all reduce within model parallel group, if any.1693"""1694if self.model_parallel_group is None or self.model_parallel_world_size == 1:1695pass1696else:1697dist.all_reduce(tensor=tensor, op=op, group=self.model_parallel_group)16981699def get_grad_norm_direct(self, gradients, params, norm_type=2):1700"""Clips gradient norm of an iterable of parameters.17011702This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and1703added functionality to handle model parallel parameters. Note that1704the gradients are modified in place.17051706Arguments:1707parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a1708single Tensor that will have gradients normalized1709max_norm (float or int): max norm of the gradients1710norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for1711infinity norm.17121713Returns:1714Total norm of the parameters (viewed as a single vector).1715"""1716norm_type = float(norm_type)1717all_norms = []1718if norm_type == inf:1719for g in gradients:1720all_norms.append(g.data.abs().max().float())1721total_norm = torch.stack(all_norms).max()1722dist.all_reduce(total_norm, op=dist.ReduceOp.MAX, group=self.dp_process_group)17231724# Take max across all GPUs.1725self._model_parallel_all_reduce(tensor=total_norm, op=dist.ReduceOp.MAX)1726else:1727# if dist.get_rank() == 0:1728# logger.info(f"Total Norm beginning {total_norm}")1729for g, p in zip(gradients, params):1730# Pipeline parallelism may replicate parameters. Avoid multi-counting.1731if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated:1732continue1733if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0):1734opguard_wrapped__all_norms_append(1688)(1735all_norms.append, torch.norm(g.data.double().detach(), norm_type).to(get_accelerator().current_device_name()))1736if len(all_norms) > 0:1737total_norm = opguard_wrapped__total_norm_float(1691)(torch.stack(all_norms).square().sum().float)1738else:1739total_norm = torch.tensor(0.0, dtype=torch.float32).to(self.device)1740# Sum across all model parallel Device.1741dist.all_reduce(total_norm, op=dist.ReduceOp.SUM, group=self.dp_process_group)17421743self._model_parallel_all_reduce(tensor=total_norm, op=dist.ReduceOp.SUM)17441745total_norm = opguard_wrapped__total_norm_pow(1699)(total_norm.pow, 1. / norm_type)17461747norm_is_inf = opguard_wrapped__norm_is_inf_isinf(1701)(total_norm.isinf)1748norm_is_nan = opguard_wrapped__norm_is_nan_isnan(1702)(total_norm.isnan)1749inf_or_nan = opguard_wrapped__inf_or_nan_logical_or(1703)(norm_is_nan.logical_or, norm_is_inf)17501751err = opguard_wrapped__err_tensor(1705)(torch.tensor, -1.0, device=self.device, dtype=torch.float)1752total_norm = inf_or_nan * err + opguard_wrapped__total_norm_logical_not(1706)(inf_or_nan.logical_not) * total_norm1753return total_norm17541755# creates a flat fused tensor from the tensor list starting at the first_offset1756# in the first tensor of the list. If there are not enough elements in the tensor1757# list then the flat tensor will be padded with zeros1758def get_flat_partition(self, tensor_list, first_offset, partition_size, dtype, device, return_tensor_list=False):1759flat_tensor_list = []1760current_size = 017611762for i, tensor in enumerate(tensor_list):1763grad_accum = self.get_param_gradient_attribute(tensor)1764if grad_accum is None:1765grad_accum = torch.zeros_like(tensor, dtype=dtype)17661767tensor = grad_accum1768num_elements = tensor.numel()1769tensor_offset = 017701771# we need to offset to get to the right element1772if i == 0 and first_offset > 0:1773tensor_offset = first_offset1774num_elements = num_elements - tensor_offset17751776# we dont need all elements of the tensor1777if num_elements > (partition_size - current_size):1778num_elements = partition_size - current_size17791780# we need a narrow view of the tensor based on the tensor offset and number of elements that1781# we need from this tensor1782if tensor_offset > 0 or num_elements < tensor.numel():1783flat_tensor_list.append(tensor.contiguous().view(-1).narrow(0, int(tensor_offset), int(num_elements)))1784else:1785flat_tensor_list.append(tensor)17861787current_size = current_size + num_elements17881789# this means its the last partition and does not align with the dp boundary. We need to pad before flattening1790if current_size < partition_size:1791flat_tensor_list.append(torch.zeros(int(partition_size - current_size), dtype=dtype, device=device))17921793if return_tensor_list:1794return flat_tensor_list17951796return self.flatten(flat_tensor_list)17971798def free_grad_in_param_list(self, param_list):1799for p in param_list:1800p.grad = None # in step1801p.grad_accum = None18021803def reset_cpu_buffers(self):1804self.norm_for_param_grads = {}1805self.local_overflow = False18061807def set_lr(self, lr):1808"""Set the learning rate."""1809for param_group in self.optimizer.param_groups:1810param_group["lr"] = lr18111812def get_lr(self):1813"""Return the current learning rate."""1814return self.optimizer.param_groups[0]["lr"]18151816def override_loss_scale(self, loss_scale):1817if loss_scale != self.external_loss_scale:1818logger.info(f'[deepspeed] setting loss scale from {self.external_loss_scale} -> {loss_scale}')1819self.custom_loss_scaler = True1820self.external_loss_scale = loss_scale18211822def scaled_global_norm(self, norm_type=2):1823assert norm_type == 2, "only L2 norm supported"1824norm_groups = []1825for i, group in enumerate(self.bit16_groups):1826if self.cpu_offload:1827# complete complete_grad_norm_calculation_for_cpu_offload return python float, moving back to1828# torch.tensor as else statement returns tensor as well1829norm = torch.tensor(self.complete_grad_norm_calculation_for_cpu_offload(self.params_in_partition[i]),1830device=self.device)1831norm_groups.append(norm)1832else:1833norm_groups.append(self.get_grad_norm_direct(self.averaged_gradients[i], self.params_in_partition[i]))18341835if self.has_moe_layers:1836self._average_expert_grad_norms(norm_groups)18371838# calculating L2 norm1839return opguard_wrapped__stack_norm(1793)(torch.norm, torch.stack(norm_groups), p=norm_type)18401841def get_bit16_param_group(self, group_no):1842bit16_partitions = self.parallel_partitioned_bit16_groups[group_no]1843partition_id = dist.get_rank(group=self.real_dp_process_group[group_no])1844return [bit16_partitions[dist.get_rank(group=self.real_dp_process_group[group_no])]]18451846def _optimizer_step(self, group_no):1847original_param_groups = self.optimizer.param_groups1848self.optimizer.param_groups = [original_param_groups[group_no]]1849# Disabling this as the C++ side copy & synchronize is not working correctly1850#from deepspeed.ops.adam import DeepSpeedCPUAdam1851#if type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half:1852# self.optimizer.step(fp16_param_groups=[self.get_bit16_param_group(group_no)])1853#else:1854# self.optimizer.step()1855self.optimizer.step()1856self.optimizer.param_groups = original_param_groups18571858# We need to link optimizer state after the first step() call1859self._lazy_init_hp_params_optimizer_state()18601861def step(self, closure=None):1862"""1863Not supporting closure.1864"""1865self.micro_step_id = -118661867see_memory_usage(f"In step before checking overflow")18681869# First compute norm for all group so we know if there is overflow1870if self.dtype == torch.float16:1871self.check_overflow()18721873prev_scale = self.loss_scale1874self._update_scale(self.overflow)1875if self.overflow:1876see_memory_usage('After overflow before clearing gradients')1877self.zero_grad(set_to_none=True)1878if self.cpu_offload:1879self.reset_cpu_buffers()1880else:1881self.averaged_gradients = {}18821883see_memory_usage('After overflow after clearing gradients')18841885for timer in OPTIMIZER_TIMERS:1886self.timers(timer).start()1887self.timers(timer).stop()1888return18891890# Step 1:- Calculate gradient norm using bit-16 grads1891see_memory_usage('Before norm calculation')1892scaled_global_grad_norm = self.scaled_global_norm()1893self._global_grad_norm = opguard_wrapped_scaled_global_grad_norm_truediv_prev_scale(1847)(operator.truediv, scaled_global_grad_norm, prev_scale)1894see_memory_usage('After norm before optimizer')18951896# Step 2:- run optimizer and upscaling simultaneously1897for i, group in enumerate(self.bit16_groups):1898self.timers(OPTIMIZER_GRADIENTS_TIMER).start()1899partition_id = dist.get_rank(group=self.real_dp_process_group[i])1900if self.cpu_offload:1901single_grad_partition = self.single_partition_of_fp32_groups[i].grad1902self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm)19031904self.timers(OPTIMIZER_GRADIENTS_TIMER).stop()1905self.timers(OPTIMIZER_STEP_TIMER).start()1906self._optimizer_step(i)19071908# Disabled, this is not currently working1909#from deepspeed.ops.adam import DeepSpeedCPUAdam1910#if not (type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half):1911# bit16_partitions = self.parallel_partitioned_bit16_groups[i]1912# fp32_partition = self.single_partition_of_fp32_groups[i]1913# bit16_partitions[partition_id].data.copy_(fp32_partition.data)1914bit16_partitions = self.parallel_partitioned_bit16_groups[i]1915fp32_partition = self.single_partition_of_fp32_groups[i]1916opguard_wrapped__data_copy_(1870)(1917bit16_partitions[partition_id].data.copy_, fp32_partition.to(get_accelerator().current_device_name()).data)19181919self.timers(OPTIMIZER_STEP_TIMER).stop()1920else:1921# free gradients for all the parameters that are not updated by this process(ZeRO stage2)1922self.free_grad_in_param_list(self.params_not_in_partition[i])19231924# create a flat gradients for parameters updated by this process1925# If we are last partition, ensure we have same size grads and partition size, if not pad with zero tensors1926if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1:1927single_grad_partition = opguard_wrapped__single_grad_partition_to(1881)(self.flatten_dense_tensors_aligned(1928self.averaged_gradients[i],1929int(self.partition_size[i])).to, self.single_partition_of_fp32_groups[i].dtype)1930else:1931single_grad_partition = opguard_wrapped__single_grad_partition_to(1885)(1932self.flatten(self.averaged_gradients[i]).to, self.single_partition_of_fp32_groups[i].dtype)1933assert single_grad_partition.numel() == self.partition_size[i], \1934"averaged gradients have different number of elements that partition size {} {} {} {}".format(1935single_grad_partition.numel(), self.partition_size[i], i, partition_id)19361937self.single_partition_of_fp32_groups[i].grad = single_grad_partition1938# release all the gradient since we have already created a necessary copy in dp_grad_partition(ZeRO stage2)1939self.free_grad_in_param_list(self.params_in_partition[i])19401941self.averaged_gradients[i] = None19421943self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm)19441945self.timers(OPTIMIZER_GRADIENTS_TIMER).stop()19461947# Step 3:- run the optimizer if no offloading1948self.timers(OPTIMIZER_STEP_TIMER).start()1949self._optimizer_step(i)1950# Step 4:- get rid of the fp32 gradients. Not needed anymore1951self.single_partition_of_fp32_groups[i].grad = None1952del single_grad_partition1953bit16_partitions = self.parallel_partitioned_bit16_groups[i]1954fp32_partition = self.single_partition_of_fp32_groups[i]1955opguard_wrapped__data_copy_(1909)(bit16_partitions[partition_id].data.copy_, fp32_partition.data)1956self.timers(OPTIMIZER_STEP_TIMER).stop()19571958see_memory_usage('After optimizer before all-gather')1959if self.cpu_offload:1960self.reset_cpu_buffers()19611962self.timers(OPTIMIZER_ALLGATHER_TIMER).start()1963# Gather the updated weights from everyone.1964# Then all partitions of the model parameters are updated and ready for next round forward.1965all_gather_dp_groups(groups_flat=self.bit16_groups_flat,1966partitioned_param_groups=self.parallel_partitioned_bit16_groups,1967dp_process_group=self.real_dp_process_group,1968start_alignment_factor=self.nccl_start_alignment_factor,1969allgather_bucket_size=self.allgather_bucket_size)1970self.timers(OPTIMIZER_ALLGATHER_TIMER).stop()19711972# TODO: we probably don't need this? just to be safe1973for i in range(len(self.bit16_groups)):1974self._update_model_bit16_weights(i)19751976self.timers.log(OPTIMIZER_TIMERS)1977see_memory_usage('After zero_optimizer step')19781979return19801981@torch.no_grad()1982def update_lp_params(self):1983for i, (bit16_partitions, fp32_partition) in enumerate(1984zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)):1985partition_id = dist.get_rank(group=self.real_dp_process_group[i])1986opguard_wrapped__data_copy_(1940)(bit16_partitions[partition_id].data.copy_, fp32_partition.data)1987# print_rank_0(f'update_lp_params {i=} {partition_id=}', force=True)1988# if i == 0:1989# print_rank_0(f'{fp32_partition[:10]=}', force=True)1990all_gather_dp_groups(groups_flat=self.bit16_groups_flat,1991partitioned_param_groups=self.parallel_partitioned_bit16_groups,1992dp_process_group=self.real_dp_process_group,1993start_alignment_factor=self.nccl_start_alignment_factor,1994allgather_bucket_size=self.allgather_bucket_size)19951996def _average_expert_grad_norms(self, norm_groups):1997for i, norm in enumerate(norm_groups):1998if self.is_moe_param_group[i]:1999scaled_norm_tensor = norm * 1.0 / dist.get_world_size(group=self.real_dp_process_group[i])2000if self.device == 'cpu':2001scaled_norm_tensor = scaled_norm_tensor.to(get_accelerator().current_device_name())2002dist.all_reduce(scaled_norm_tensor, group=self.real_dp_process_group[i])2003norm_groups[i] = scaled_norm_tensor.to(self.device)20042005def unscale_and_clip_grads(self, grad_groups_flat, total_norm):2006# compute combined scale factor for this group2007combined_scale = self.loss_scale2008if self.clip_grad > 0.:2009# norm is in fact norm*scale2010clip = opguard_wrapped_total_norm_truediv_clip_grad(1964)(operator.truediv, ((total_norm / self.loss_scale) + 1e-6), self.clip_grad)2011if clip > 1:2012combined_scale = clip * self.loss_scale20132014for grad in grad_groups_flat:2015if isinstance(grad, list):2016sub_partitions = grad2017for g in sub_partitions:2018g.data.mul_(1. / combined_scale)2019else:2020opguard_wrapped__data_mul_(1974)(grad.data.mul_, 1. / combined_scale)20212022def _check_overflow(self, partition_gradients=True):2023self.overflow = self.has_overflow(partition_gradients)20242025# `params` is a list / generator of torch.Variable2026def has_overflow_serial(self, params):2027invalid_grad_count = opguard_wrapped__invalid_grad_count_zeros(1981)(torch.zeros, [1], dtype=torch.float, device=get_accelerator().current_device_name())2028for p in params:2029if p.grad is not None:2030invalid_grad_count += opguard_wrapped__invalid_grad_count__has_inf_or_nan(1984)(self._has_inf_or_nan, p.grad)2031return opguard_wrapped__invalid_grad_count_bool(1985)(invalid_grad_count.bool)20322033def has_overflow_partitioned_grads_serial(self):2034invalid_grad_count = opguard_wrapped__invalid_grad_count_zeros(1988)(torch.zeros, [1], dtype=torch.float, device=get_accelerator().current_device_name())2035for i in range(len(self.bit16_groups)):2036for j, grad in enumerate(self.averaged_gradients[i]):2037if grad is not None:2038invalid_grad_count += opguard_wrapped__invalid_grad_count__has_inf_or_nan(1992)(self._has_inf_or_nan, grad)2039return opguard_wrapped__invalid_grad_count_bool(1993)(invalid_grad_count.bool)20402041def has_overflow(self, partition_gradients=True):2042if partition_gradients:2043overflow = self.local_overflow if self.cpu_offload else self.has_overflow_partitioned_grads_serial()2044overflow_gpu = get_accelerator().ByteTensor([overflow]) if self.cpu_offload else opguard_wrapped__overflow_gpu_to(1998)(2045overflow.byte().to, get_accelerator().current_device_name())2046'''This will capture overflow across all data parallel and expert parallel process2047Since expert parallel process are a subset of data parallel process'''2048dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.dp_process_group)20492050else:2051params = []2052for group in self.bit16_groups:2053for param in group:2054params.append(param)2055overflow_gpu = opguard_wrapped__overflow_gpu_to(2009)(self.has_overflow_serial(params).byte().to, get_accelerator().current_device_name())20562057# Since each model parallel GPU carries only part of the model,2058# make sure overflow flag is synced across all the model parallel GPUs2059self._model_parallel_all_reduce(tensor=overflow_gpu, op=dist.ReduceOp.MAX)20602061overflow = opguard_wrapped__overflow_item(2015)(overflow_gpu[0].item)2062return bool(overflow)20632064# `x` is a torch.Tensor2065@staticmethod2066def _has_inf_or_nan(x, j=None):2067float_x = opguard_wrapped__float_x_float(2021)(x.float)2068nan = opguard_wrapped__nan_isnan(2022)(float_x.isnan)2069inf = opguard_wrapped__inf_isinf(2023)(float_x.isinf)2070inf_or_nan = opguard_wrapped__inf_or_nan_logical_or(2024)(nan.logical_or, inf)2071return opguard_wrapped__float_max(2025)(inf_or_nan.float().max)20722073def backward(self, loss, retain_graph=False):2074"""2075:attr:`backward` performs the following steps:207620771. fp32_loss = loss.float()20782. scaled_loss = fp32_loss*loss_scale20793. scaled_loss.backward(), which accumulates scaled gradients into the ``.grad`` attributes of the model's fp16 leaves2080"""2081self.micro_step_id += 120822083if self.contiguous_gradients:2084self.ipg_buffer = []2085buf_0 = opguard_wrapped__buf_0_empty(2039)(torch.empty, int(self.reduce_bucket_size),2086dtype=self.dtype,2087device=get_accelerator().current_device_name())2088self.ipg_buffer.append(buf_0)20892090# Use double buffers to avoid data access conflict when overlap_comm is enabled.2091if self.overlap_comm:2092buf_1 = torch.empty(int(self.reduce_bucket_size),2093dtype=self.dtype,2094device=get_accelerator().current_device_name())2095self.ipg_buffer.append(buf_1)2096self.ipg_index = 020972098if self.custom_loss_scaler:2099scaled_loss = self.external_loss_scale * loss2100scaled_loss.backward()2101else:2102self.loss_scaler.backward(loss.float(), retain_graph=retain_graph)21032104# Only for Stage 1, Mode 22105if self.use_grad_accum_attribute:2106self.fill_grad_accum_attribute()21072108def check_overflow(self, partition_gradients=True):2109self._check_overflow(partition_gradients)21102111def _update_scale(self, has_overflow=False):2112self.loss_scaler.update_scale(has_overflow)21132114# Promote state so it can be retrieved or set via "fp16_optimizer_instance.state"2115def _get_state(self):2116return self.optimizer.state21172118def _set_state(self, value):2119self.optimizer.state = value21202121state = property(_get_state, _set_state)21222123# Promote param_groups so it can be retrieved or set via "fp16_optimizer_instance.param_groups"2124# (for example, to adjust the learning rate)2125def _get_param_groups(self):2126return self.optimizer.param_groups21272128def _set_param_groups(self, value):2129self.optimizer.param_groups = value21302131param_groups = property(_get_param_groups, _set_param_groups)21322133# Promote loss scale so it can be retrieved or set via "fp16_optimizer_instance.loss_scale"2134def _get_loss_scale(self):2135if self.custom_loss_scaler:2136return self.external_loss_scale2137else:2138return self.loss_scaler.cur_scale21392140def _set_loss_scale(self, value):2141self.loss_scaler.cur_scale = value21422143loss_scale = property(_get_loss_scale, _set_loss_scale)2144cur_scale = property(_get_loss_scale, _set_loss_scale)21452146# Return group tensor after removing paddings that are added for alignment to DP world size.2147# This method works on the assumption that each group contains a single flattened tensor.2148def _get_groups_without_padding(self, groups_with_padding):2149groups_without_padding = []2150for i, group in enumerate(groups_with_padding):2151lean_length = group.numel() - self.groups_padding[i]2152groups_without_padding.append(group[:lean_length])21532154return groups_without_padding21552156# Return optimizer state after removing paddings that are added for alignment.2157def _get_state_without_padding(self, state_with_padding, padding):2158lean_state = {}2159for key, value in state_with_padding.items():2160if torch.is_tensor(value):2161lean_length = value.numel() - padding2162lean_state[key] = value[:lean_length]2163else:2164lean_state[key] = value21652166return lean_state21672168# Return base optimizer states.2169# This method assumes that each param group contains a single flattened tensor.2170def _get_base_optimizer_state(self):2171optimizer_groups_state = []2172for i, group in enumerate(self.optimizer.param_groups):2173p = group['params'][0]2174lean_optimizer_state = self._get_state_without_padding(self.optimizer.state[p], self.groups_padding[i])2175optimizer_groups_state.append(lean_optimizer_state)21762177return optimizer_groups_state21782179def state_dict(self):2180"""2181Returns a dict containing the current state of this :class:`FP16_Optimizer` instance.2182This dict contains attributes of :class:`FP16_Optimizer`, as well as the state_dict2183of the contained Pytorch optimizer.2184Example::2185checkpoint = {}2186checkpoint['model'] = model.state_dict()2187checkpoint['optimizer'] = optimizer.state_dict()2188torch.save(checkpoint, "saved.pth")2189"""2190state_dict = {}2191state_dict[LOSS_SCALER] = self.loss_scaler2192state_dict['dynamic_loss_scale'] = self.dynamic_loss_scale2193state_dict['overflow'] = self.overflow2194state_dict[CLIP_GRAD] = self.clip_grad21952196if self.elastic_checkpoint:2197state_dict[BASE_OPTIMIZER_STATE] = self._get_base_optimizer_state()21982199if "step" in self.optimizer.param_groups[0]:2200# Assuming "step" is the only item that changes through training iterations2201assert all(group["step"] == self.optimizer.param_groups[0]["step"]2202for group in self.optimizer.param_groups), "All param groups must have the same step value"2203state_dict[BASE_OPTIMIZER_STATE_STEP] = self.optimizer.param_groups[0]["step"]2204else:2205state_dict[BASE_OPTIMIZER_STATE] = self.optimizer.state_dict()22062207# Remove paddings for DP alignment to enable loading for other alignment values2208fp32_groups_without_padding = self._get_groups_without_padding(self.single_partition_of_fp32_groups)2209state_dict[SINGLE_PARTITION_OF_FP32_GROUPS] = fp32_groups_without_padding22102211state_dict[2212ZERO_STAGE] = ZeroStageEnum.gradients if self.partition_gradients else ZeroStageEnum.optimizer_states2213state_dict[GROUP_PADDINGS] = self.groups_padding2214state_dict[PARTITION_COUNT] = self.partition_count22152216state_dict[DS_VERSION] = version2217state_dict[PARAM_SLICE_MAPPINGS] = self._param_slice_mappings22182219return state_dict22202221# Restore base optimizer fp32 weights from elastic checkpoint by:2222# 1) Merging fp32 weights from checkpoints of all partitions2223# 2) Extracting fp32 weights for current partition from merged weights2224# 3) Using extracted weights to update base optimizer weights directly.2225def _restore_from_elastic_fp32_weights(self, all_state_dict):2226merged_single_partition_of_fp32_groups = []22272228for i in range(len(self.single_partition_of_fp32_groups)):2229partition_id = dist.get_rank(group=self.real_dp_process_group[i])2230merged_partitions = [sd[SINGLE_PARTITION_OF_FP32_GROUPS][i] for sd in all_state_dict]2231if self.is_moe_group(self.optimizer.param_groups[i]):2232ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name'])2233merged_partitions = [merged_partitions[i] for i in ranks]2234flat_merged_partitions = self.flatten_dense_tensors_aligned(2235merged_partitions,2236self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i]))2237dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, i)2238merged_single_partition_of_fp32_groups.append(dp_partitions[partition_id])22392240for current, saved in zip(self.single_partition_of_fp32_groups, merged_single_partition_of_fp32_groups):2241current.data.copy_(saved.data)22422243# Restore base optimizer fp32 weights from ZeRO fp16 or bfloat16 weights2244def _restore_from_bit16_weights(self):2245for group_id, (bit16_partitions, fp32_partition) in enumerate(2246zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)):2247partition_id = dist.get_rank(group=self.real_dp_process_group[group_id])2248fp32_partition.data.copy_(bit16_partitions[partition_id].data)22492250# Refresh the fp32 master params from the fp16 or bfloat16 copies.2251def refresh_fp32_params(self):2252self._restore_from_bit16_weights()22532254# Extract optimizer state for current partition from merged states of all partitions2255def _partition_base_optimizer_state(self, state_key, all_partition_states, group_id):2256partition_id = dist.get_rank(group=self.real_dp_process_group[group_id])2257alignment = self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[group_id])2258if torch.is_tensor(all_partition_states[0]):2259flat_merged_partitions = self.flatten_dense_tensors_aligned(all_partition_states, alignment)2260dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, group_id)2261return dp_partitions[partition_id]2262else:2263# Assume non-tensor states are not partitioned and equal across ranks, so return first one2264return all_partition_states[0]22652266def _restore_step_from_elastic_checkpoint(self, all_state_dict):2267assert BASE_OPTIMIZER_STATE_STEP in all_state_dict[0]2268assert all(sd[BASE_OPTIMIZER_STATE_STEP] == all_state_dict[0][BASE_OPTIMIZER_STATE_STEP]2269for sd in all_state_dict), "State dicts of all partitions must have the same step value"2270return all_state_dict[0][BASE_OPTIMIZER_STATE_STEP]22712272def _restore_base_optimizer_state(self, base_optimizer_group_states, base_optimizer_state_step, group_paddings):2273if type(base_optimizer_group_states) == dict:2274base_optimizer_group_states = base_optimizer_group_states['state']22752276saved_keys = base_optimizer_group_states[0].keys()22772278for i, group in enumerate(self.optimizer.param_groups):2279p = group['params'][0]2280padding = 0 if group_paddings is None else group_paddings[i]2281for key in saved_keys:2282saved = base_optimizer_group_states[i][key]22832284if torch.is_tensor(saved):2285if key in self.optimizer.state[p]:2286dst_tensor = self.optimizer.state[p][key]2287src_tensor = _get_padded_tensor(saved, dst_tensor.numel())2288self.optimizer.state[p][key].data.copy_(src_tensor.data)2289else:2290self.optimizer.state[p][key] = _pad_tensor_by_size(2291saved, padding, torch.float32,2292torch.device('cpu') if self.cpu_offload else self.device)2293else:2294self.optimizer.state[p][key] = saved22952296for param_group in self.optimizer.param_groups:2297param_group['step'] = base_optimizer_state_step22982299def get_ep_ranks(self, rank=0, group_name=None):2300from deepspeed.utils import groups2301expert_parallel_size_ = groups._get_expert_parallel_world_size(group_name)2302world_size = groups._get_data_parallel_world_size()2303rank = groups._get_expert_parallel_rank(group_name)2304ranks = range(rank, world_size, expert_parallel_size_)2305return list(ranks)23062307# Restore base optimizer state from elastic checkpoint by2308# 1) Merging optimizer state from checkpoints of all partitions2309# 2) Extracting optimizer state for current partition from the merged state2310# 3) Using the extracted value to directly update the base optimizer.2311def _restore_elastic_base_optimizer_state(self, all_state_dict):2312base_optimizer_group_states = []2313for i in range(len(self.optimizer.param_groups)):2314partition_states = {}2315all_partition_group_states = [sd[BASE_OPTIMIZER_STATE][i] for sd in all_state_dict]23162317if self.is_moe_group(self.optimizer.param_groups[i]):2318ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name'])2319all_partition_group_states = [all_partition_group_states[i] for i in ranks]23202321for key in all_partition_group_states[0].keys():2322all_partition_states = [all_states[key] for all_states in all_partition_group_states]2323partition_states[key] = self._partition_base_optimizer_state(key, all_partition_states, i)2324base_optimizer_group_states.append(partition_states)23252326self._restore_base_optimizer_state(base_optimizer_group_states,2327self._restore_step_from_elastic_checkpoint(all_state_dict), None)23282329def load_state_dict(self,2330state_dict_list,2331load_optimizer_states=True,2332load_from_fp32_weights=False,2333checkpoint_folder=None,2334load_serial=None):2335if checkpoint_folder:2336self._load_universal_checkpoint(checkpoint_folder, load_optimizer_states, load_from_fp32_weights)2337else:2338self._load_legacy_checkpoint(state_dict_list, load_optimizer_states, load_from_fp32_weights)23392340def _load_universal_checkpoint(self, checkpoint_folder, load_optimizer_states, load_from_fp32_weights):2341self.load_hp_checkpoint_state_from_checkpoint_dir("bit16_groups", checkpoint_folder)23422343@property2344def param_groups(self):2345"""Forward the wrapped optimizer's parameters."""2346return self.optimizer.param_groups23472348def _load_global_state(self, sd):2349self.loss_scaler = sd.get(LOSS_SCALER, self.loss_scaler)2350self.dynamic_loss_scale = sd.get('dynamic_loss_scale', self.dynamic_loss_scale)2351self.overflow = sd.get('overflow', self.overflow)2352self.clip_grad = sd.get(CLIP_GRAD, self.clip_grad)23532354ckpt_version = sd.get(DS_VERSION, False)2355assert ckpt_version, f"Empty ds_version in checkpoint, not clear how to proceed"2356ckpt_version = pkg_version.parse(ckpt_version)23572358# zero stage 1 mode2359if not self.partition_gradients:2360required_version = pkg_version.parse("0.3.17")2361error_str = f"ZeRO stage 1 changed in {required_version} and is not backwards compatible " \2362"with older stage 1 checkpoints. If you'd like to load an old ZeRO-1 checkpoint " \2363"please use an older version of DeepSpeed (<= 0.5.8) and set 'legacy_stage1': true in your zero config json."2364assert required_version <= ckpt_version, f"Old version: {ckpt_version} {error_str}"23652366def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, load_from_fp32_weights=False):2367r"""Loading ZeRO checkpoint23682369Arguments:2370state_dict_list: List of all saved ZeRO checkpoints, one for each saved partition.2371Note that the number of saved partitions may differ from number of loading partitions to support2372changing GPU count, specifically DP world size, between saving and loading checkpoints.2373load_optimizer_states: Boolean indicating whether or not to load base optimizer states2374load_from_fp32_weights: Boolean indicating whether to initialize fp32 master weights from fp322375copies in checkpoints (no precision loss) or from model's fp16 copies (with precision loss).2376"""2377"""2378Loads a state_dict created by an earlier call to state_dict().2379If ``fp16_optimizer_instance`` was constructed from some ``init_optimizer``,2380whose parameters in turn came from ``model``, it is expected that the user2381will call ``model.load_state_dict()`` before2382``fp16_optimizer_instance.load_state_dict()`` is called.2383Example::2384model = torch.nn.Linear(D_in, D_out).to(get_accelerator().device_name()).half()2385optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)2386optimizer = FP16_Optimizer(optimizer, static_loss_scale = 128.0)2387...2388checkpoint = torch.load("saved.pth")2389model.load_state_dict(checkpoint['model'])2390optimizer.load_state_dict(checkpoint['optimizer'])2391"""23922393# I think it should actually be ok to reload the optimizer before the model.2394dp_rank = dist.get_rank(group=self.dp_process_group)2395current_rank_sd = state_dict_list[dp_rank]2396self._load_global_state(current_rank_sd)23972398ckpt_is_rigid = isinstance(current_rank_sd[BASE_OPTIMIZER_STATE], dict)23992400# padding is always at the last rank/partition2401# if DP=1024 and param-group elems=16 -> padding will be 1024-16 across all but one rank2402# scenario-1 (shrink): saving w. 4 gpus -> loading w. 2 gpus2403# scenario-2 (expand): saving w. 2 gpus -> loading w. 4 gpus2404# if load_optimizer_states:2405# if new_dp_size:2406# self.strip_padding()2407# self.add_padding_w_new_dp_size()2408# self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE])24092410if load_optimizer_states:2411if ckpt_is_rigid:2412# loading rigid ckpt into either rigid or elastic exec2413self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE])2414else:2415if self.elastic_checkpoint:2416# loading elastic into elastic exec2417self._restore_elastic_base_optimizer_state(state_dict_list)2418else:2419# loading an elastic checkpoint into rigid exec2420self._restore_base_optimizer_state(current_rank_sd[BASE_OPTIMIZER_STATE],2421current_rank_sd[BASE_OPTIMIZER_STATE_STEP],2422current_rank_sd[GROUP_PADDINGS])24232424# At this point, the optimizer's references to the model's fp32 parameters are up to date.2425# The optimizer's hyperparameters and internal buffers are also up to date.2426# However, the fp32 master copies of the model's fp16 params stored by the optimizer are still2427# out of date. There are two options.2428# 1: Refresh the master params from the model's fp16 params.2429# This requires less storage but incurs precision loss.2430# 2: Save and restore the fp32 master copies separately.2431# We choose option 1 if changing DP degree and option 2 otherwise.2432#2433# Pytorch Optimizer.load_state_dict casts saved buffers (e.g. momentum) to the type and device2434# of their associated parameters, because it's possible those buffers might not exist yet in2435# the current optimizer instance. In our case, as long as the current FP16_Optimizer has been2436# constructed in the same way as the one whose state_dict we are loading, the same master params2437# are guaranteed to exist, so we can just copy_() from the saved master params.24382439if load_from_fp32_weights:2440# option 2 from above2441if self.elastic_checkpoint and not ckpt_is_rigid:2442self._restore_from_elastic_fp32_weights(state_dict_list)2443else:2444# For non-elastic checkpoint, simply copying from saved weights of current rank is sufficient.2445for current, saved in zip(self.single_partition_of_fp32_groups,2446current_rank_sd[SINGLE_PARTITION_OF_FP32_GROUPS]):2447src_tensor = _get_padded_tensor(saved, current.numel())2448current.data.copy_(src_tensor.data)2449else:2450# option 1 from above2451self._restore_from_bit16_weights()24522453if load_optimizer_states:2454self._link_all_hp_params()245524562457def _handle_overflow(cpu_sum, x, i):2458import math2459rank = dist.get_rank()2460if rank == 0:2461t_i = -12462for v_i, v in enumerate(x.data.contiguous().view(-1)):2463if not math.isfinite(float(v)):2464t_i = v_i2465break2466logger.info(f"rank {rank} detected overflow {cpu_sum} in tensor {i}:{t_i} shape {x.shape}")246724682469def estimate_zero2_model_states_mem_needs(total_params,2470num_gpus_per_node=1,2471num_nodes=1,2472cpu_offload=True,2473additional_buffer_factor=1.5):24742475total_gpus = num_nodes * num_gpus_per_node24762477if cpu_offload:2478gpu_mem = 2 * total_params2479cpu_mem = total_params * max(4 * total_gpus, 16) * additional_buffer_factor2480else:2481gpu_mem = 4 * total_params + int(16 * total_params / total_gpus)2482cpu_mem = total_params * 4 * num_gpus_per_node * additional_buffer_factor24832484return int(cpu_mem), int(gpu_mem)248524862487def model_to_params(model):2488# shared params calculated only once2489total_params = sum(dict((p.data_ptr(), p.numel()) for p in model.parameters()).values())2490return total_params249124922493def estimate_zero2_model_states_mem_needs_all_live(model,2494num_gpus_per_node=1,2495num_nodes=1,2496additional_buffer_factor=1.5):2497"""2498Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients2499for a given ``model`` and hardware setup.25002501If you have an actual model object, use this function and everything will be derived2502automatically.25032504If it's a hypothetical model, use ``estimate_zero2_model_states_mem_needs_all_cold`` where you have to pass2505the ``total_params`` explicitly.25062507Args:2508- ``model``: ``nn.Module`` object2509- ``num_gpus_per_node``: how many gpus per node (defaults to 1)2510- ``num_nodes``: how many nodes (defaults to 1),2511- ``additional_buffer_factor``: estimation factor (defaults to 1.5):25122513"""25142515total_params = model_to_params(model)25162517estimate_zero2_model_states_mem_needs_all_cold(total_params=total_params,2518num_gpus_per_node=num_gpus_per_node,2519num_nodes=num_nodes,2520additional_buffer_factor=additional_buffer_factor)252125222523def estimate_zero2_model_states_mem_needs_all_cold(total_params,2524num_gpus_per_node=1,2525num_nodes=1,2526additional_buffer_factor=1.5):2527"""2528Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients2529for a given ``model`` and hardware setup.25302531If it's a hypothetical model, use this function where you have to pass2532the ``total_params`` and ``largest_layer_params`` explicitly.25332534If you have an actual model object, use ``estimate_zero2_model_states_mem_needs_all_live`` and everything2535will be derived automatically.25362537Args:2538- ``total_params``: total model params2539- ``num_gpus_per_node``: how many gpus per node (defaults to 1)2540- ``num_nodes``: how many nodes (defaults to 1),2541- ``additional_buffer_factor``: estimation factor (defaults to 1.5):25422543"""25442545def format_options(cpu_offload):2546enabled = []2547device = f'{OffloadDeviceEnum.cpu:4}' if cpu_offload else "none"2548enabled.append(f"offload_optimizer={device}")2549return ", ".join(enabled)25502551nodes_str = "nodes" if num_nodes > 1 else "node"2552gpus_str = "GPUs" if num_gpus_per_node > 1 else "GPU"2553print("Estimated memory needed for params, optim states and gradients for a:\n"2554f"HW: Setup with {num_nodes} {nodes_str}, {num_gpus_per_node} {gpus_str} per node.\n"2555f"SW: Model with {int(total_params/1e6)}M total params.")2556print(" per CPU | per GPU | Options")2557for cpu_offload in [True, False]:2558cpu_mem, gpu_mem = estimate_zero2_model_states_mem_needs(total_params=total_params,2559num_gpus_per_node=num_gpus_per_node,2560num_nodes=num_nodes,2561cpu_offload=cpu_offload,2562additional_buffer_factor=additional_buffer_factor)25632564options_str = format_options(cpu_offload=cpu_offload)2565print(f" {cpu_mem/2**30:7.2f}GB | {gpu_mem/2**30:6.2f}GB | {options_str}")