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/run2/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/run2/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/run2/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/run2/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/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')14opguard_wrapped__buf_1_empty = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')15opguard_wrapped__buf_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')16opguard_wrapped__cpu_data_cpu = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')17opguard_wrapped__data_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')18opguard_wrapped__data_mul_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')19opguard_wrapped__data_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')20opguard_wrapped__data_view_as = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')21opguard_wrapped__err_tensor = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')22opguard_wrapped__float_max = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')23opguard_wrapped__float_x_float = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')24opguard_wrapped__grad_slice_narrow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')25opguard_wrapped__grads_in_partition_empty = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')26opguard_wrapped__inf_isinf = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')27opguard_wrapped__inf_or_nan_logical_or = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')28opguard_wrapped__invalid_grad_count__has_inf_or_nan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')29opguard_wrapped__invalid_grad_count_bool = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')30opguard_wrapped__invalid_grad_count_zeros = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')31opguard_wrapped__meta_tensors_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')32opguard_wrapped__nan_isnan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')33opguard_wrapped__narrow_average_tensor = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')34opguard_wrapped__new_grad_tensor_copy_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')35opguard_wrapped__new_grad_tensor_narrow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')36opguard_wrapped__norm_is_inf_isinf = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')37opguard_wrapped__norm_is_nan_isnan = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')38opguard_wrapped__overflow_gpu_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')39opguard_wrapped__overflow_item = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')40opguard_wrapped__param_wrapper = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')41opguard_wrapped__partitions_append = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')42opguard_wrapped__single_grad_partition_to = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')43opguard_wrapped__single_grad_partition_zeros = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')44opguard_wrapped__stack_norm = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')45opguard_wrapped__tensor_div_ = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')46opguard_wrapped__tensor_flatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')47opguard_wrapped__total_norm_float = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')48opguard_wrapped__total_norm_logical_not = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')49opguard_wrapped__total_norm_pow = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')50opguard_wrapped__updated_params_unflatten = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')51opguard_wrapped__weights_partition_detach = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')52opguard_wrapped_scaled_global_grad_norm_truediv_prev_scale = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')53opguard_wrapped_total_norm_truediv_clip_grad = make_wrapper(tag_prefix = '/opt/opguard/OpGuard-runs/demo-case5-paired-20261019/workloads/run2/deps/deepspeed/runtime/zero/stage_1_and_2.py')54import torch55from deepspeed import comm as dist56from packaging import version as pkg_version57from collections import OrderedDict58from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors5960from deepspeed.runtime.base_optimizer import ZeROOptimizer61from deepspeed.runtime.fp16.loss_scaler import CreateLossScaler62from deepspeed.runtime.utils import (empty_cache, see_memory_usage, inf, is_model_parallel_parameter,63align_dense_tensors, all_gather_dp_groups)64from deepspeed.runtime.zero.config import ZeroStageEnum65from deepspeed.runtime.zero.offload_config import OffloadDeviceEnum66from deepspeed.ops.adam import DeepSpeedCPUAdam67from deepspeed.utils import logger68from deepspeed.utils.bwc import bwc_tensor_model_parallel_rank69from deepspeed.moe.utils import is_moe_param70from deepspeed.git_version_info import version7172from deepspeed.runtime.constants import PIPE_REPLICATED73from deepspeed.accelerator import get_accelerator7475from deepspeed.checkpoint.constants import (DS_VERSION, GROUP_PADDINGS, PARTITION_COUNT, LOSS_SCALER,76SINGLE_PARTITION_OF_FP32_GROUPS, BASE_OPTIMIZER_STATE,77BASE_OPTIMIZER_STATE_STEP, CLIP_GRAD, ZERO_STAGE, PARAM_SLICE_MAPPINGS)78from deepspeed.utils import link_hp_params, lazy_init_hp_params_optimizer_state79from deepspeed.checkpoint import enable_universal_checkpoint8081from deepspeed.utils import groups82# Toggle this to true to enable correctness test83# with gradient partitioning and without84pg_correctness_test = False8586OPTIMIZER_ALLGATHER_TIMER = 'optimizer_allgather'87OPTIMIZER_GRADIENTS_TIMER = 'optimizer_gradients'88OPTIMIZER_STEP_TIMER = 'optimizer_step'89OPTIMIZER_TIMERS = [OPTIMIZER_ALLGATHER_TIMER, OPTIMIZER_GRADIENTS_TIMER, OPTIMIZER_STEP_TIMER]909192def input(msg):93return949596def split_half_float_double(tensors):97device_type = get_accelerator().device_name()98dtypes = [99"torch.{}.HalfTensor".format(device_type), "torch.{}.FloatTensor".format(device_type),100"torch.{}.DoubleTensor".format(device_type), "torch.{}.BFloat16Tensor".format(device_type)101]102buckets = []103for i, dtype in enumerate(dtypes):104bucket = [t for t in tensors if t.type() == dtype]105if bucket:106buckets.append(bucket)107return buckets108109110def isclose(a, b, rtol=1e-09, atol=0.0):111return abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol)112113114def lcm(x, y):115from fractions import gcd # or can import gcd from `math` in Python 3116return x * y // gcd(x, y)117118119def get_alignment_padding(tensor_list, alignment):120num_elements = sum([tensor.numel() for tensor in tensor_list])121remainder = num_elements % alignment122return (alignment - remainder) if remainder else remainder123124125def print_rank_msg(msg):126print(f"rank {dist.get_rank()} - {msg}")127128129def _get_padded_tensor(src_tensor, size):130if src_tensor.numel() >= size:131return src_tensor132padded_tensor = torch.zeros(size, dtype=src_tensor.dtype, device=src_tensor.device)133slice_tensor = torch.narrow(padded_tensor, 0, 0, src_tensor.numel())134slice_tensor.data.copy_(src_tensor.data)135return padded_tensor136137138def _pad_tensor_by_size(src_tensor, pad_size, dtype, device):139padded_tensor = torch.zeros(src_tensor.numel() + pad_size, dtype=dtype, device=device)140padded_tensor.data[:src_tensor.numel()].copy_(src_tensor.data)141return padded_tensor142143144class DeepSpeedZeroOptimizer(ZeROOptimizer):145"""146DeepSpeedZeroOptimizer designed to reduce the memory footprint147required for training large deep learning models.148149For more details please see ZeRO: Memory Optimization Towards Training A Trillion Parameter Models150https://arxiv.org/abs/1910.02054151152For usage examples, refer to TODO: DeepSpeed Tutorial153154"""155156def __init__(self,157init_optimizer,158param_names,159timers,160static_loss_scale=1.0,161dynamic_loss_scale=False,162dynamic_loss_args=None,163verbose=True,164contiguous_gradients=True,165reduce_bucket_size=500000000,166use_multi_rank_bucket_allreduce=True,167allgather_bucket_size=5000000000,168dp_process_group=None,169expert_parallel_group=None,170expert_data_parallel_group=None,171reduce_scatter=True,172overlap_comm=False,173offload_optimizer_config=None,174mpu=None,175clip_grad=0.0,176gradient_accumulation_dtype=torch.float32,177communication_data_type=torch.float16,178postscale_gradients=True,179gradient_predivide_factor=1.0,180gradient_accumulation_steps=1,181ignore_unused_parameters=True,182partition_grads=True,183round_robin_gradients=False,184has_moe_layers=False,185fp16_master_weights_and_gradients=False,186elastic_checkpoint=False):187188if offload_optimizer_config is not None and offload_optimizer_config.device != OffloadDeviceEnum.none:189self.cpu_offload = True190self.cpu_offload_pin_memory = offload_optimizer_config.pin_memory191else:192self.cpu_offload = False193self.cpu_offload_pin_memory = False194195if dist.get_rank() == 0:196logger.info(f"Reduce bucket size {reduce_bucket_size}")197logger.info(f"Allgather bucket size {allgather_bucket_size}")198logger.info(f"CPU Offload: {self.cpu_offload}")199logger.info(f'Round robin gradient partitioning: {round_robin_gradients}')200# The fused optimizer does all the work. We need this layer for two reason:201# 1. maintain same user API from apex.fp16_utils202# 2. keep common stuff here in case we need to add ne552w fused optimizer later203204self.elastic_checkpoint = elastic_checkpoint205self.param_names = param_names206self.mpu = mpu207# differences from apex.fp16_utils:208# - assume all model params in fp16209# - assume all params requires grad210# - flat by groups, not keeping state. TODO: remove state explicitly?211# - master grad and unflat master weight never exist. TODO: a way to save out unflat master?212if not get_accelerator().is_available():213raise SystemError("Accelerator is not detected, cannot perform low precision training (e.g., fp16, bf16).")214self.optimizer = init_optimizer215216# Use torch (un)flatten ops217self.flatten = _flatten_dense_tensors218self.unflatten = _unflatten_dense_tensors219220# ZeRO stage 1 (False) or 2 (True)221self.partition_gradients = partition_grads222self.zero_stage_string = "ZeRO-2" if partition_grads else "ZeRO-1"223224self.timers = timers225226self.reduce_scatter = reduce_scatter227228self.overlap_comm = overlap_comm229230self.deepspeed_adam_offload = self.cpu_offload231232self.device = get_accelerator().current_device_name() if not self.cpu_offload else 'cpu'233234self.dp_process_group = dp_process_group235self.sequence_parallel_size = groups._get_sequence_parallel_world_size()236#expert parallel group237self.ep_process_group = expert_parallel_group238239#data parallel group for experts240self.expert_dp_process_group = expert_data_parallel_group241242#data parallel size for non-experts243dp_size = dist.get_world_size(group=self.dp_process_group)244245#For MoE models this maybe different for different param group246#It will be modified during MoE setup later in the init247self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))]248self.partition_count = [dp_size for i in range(len(self.optimizer.param_groups))]249250self.is_gradient_accumulation_boundary = True251252# CPU-Offload requires contiguous gradients253self.contiguous_gradients = contiguous_gradients or self.cpu_offload254255self.has_moe_layers = has_moe_layers256if self.has_moe_layers:257self._configure_moe_settings()258self._global_grad_norm = 0.259260if mpu is None:261self.model_parallel_group = None262self.model_parallel_world_size = 1263self.model_parallel_rank = 0264else:265self.model_parallel_group = mpu.get_model_parallel_group()266self.model_parallel_world_size = mpu.get_model_parallel_world_size()267self.model_parallel_rank = bwc_tensor_model_parallel_rank(mpu)268269self.overflow = False270self.clip_grad = clip_grad271self.communication_data_type = communication_data_type272self.gradient_predivide_factor = gradient_predivide_factor273self.postscale_gradients = postscale_gradients274self.gradient_accumulation_steps = gradient_accumulation_steps275self.micro_step_id = 0276self.ignore_unused_parameters = ignore_unused_parameters277self.round_robin_gradients = round_robin_gradients278279self.extra_large_param_to_reduce = None280self.fp16_master_weights_and_gradients = fp16_master_weights_and_gradients281282if self.fp16_master_weights_and_gradients:283assert self.cpu_offload and type(self.optimizer) in [DeepSpeedCPUAdam], \284f"fp16_master_and_gradients requires optimizer to support keeping fp16 master and gradients while keeping the optimizer states in fp32."\285f"Currently only supported using ZeRO-Offload with DeepSpeedCPUAdam. But current setting is ZeRO-Offload:{self.cpu_offload} and optimizer type {type(self.optimizer)}." \286f"Either disable fp16_master_weights_and_gradients or enable {self.zero_stage_string} Offload with DeepSpeedCPUAdam."287288if self.reduce_scatter and self.partition_gradients:289valid_reduce_scatter_dtypes = (torch.float16, torch.bfloat16, torch.float32)290assert 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}'"291assert 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"292assert self.postscale_gradients, f"pre-scale gradients is not yet supported with {self.zero_stage_string} with reduce scatter enabled"293294# param flattened by groups295self.bit16_groups = []296self.bit16_groups_flat = []297298# param partitioned by data parallel degree299# this will contain a list of equal sized tensors300# each of which will be updated by a different process301self.parallel_partitioned_bit16_groups = []302303# a single 32-bit partition of the parallel partitioned parameters304# that this process will update305self.single_partition_of_fp32_groups = []306307# param partition info308309# These are the parameters in each group that will not be updated by this process directly310self.params_not_in_partition = []311312# These are the parameters that will be updated by this process directly313self.params_in_partition = []314315# Offset from the first parameter in the self.params_in_partition316# the parameter boundaries may not align with partition boundaries317# so we need to keep track of the offset318self.first_offset = []319320# number of elements per partition in each group321self.partition_size = []322323# align nccl all-gather send buffers to 4-byte boundary324self.nccl_start_alignment_factor = 2 # 4-byte alignment/sizeof(fp16) = 2325326assert (327allgather_bucket_size % self.nccl_start_alignment_factor == 0328), f"allgather_bucket_size must be a multiple of nccl_start_alignment_factor, {self.nccl_start_alignment_factor} "329330self.all_reduce_print = False331self.dtype = self.optimizer.param_groups[0]['params'][0].dtype332self.gradient_accumulation_dtype = gradient_accumulation_dtype333334if self.dtype != self.gradient_accumulation_dtype:335self.use_separate_grad_accum = True336else:337self.use_separate_grad_accum = False338if self.use_separate_grad_accum and not self.partition_gradients:339self.use_grad_accum_attribute = True340else:341self.use_grad_accum_attribute = False342343self.round_robin_bit16_groups = []344self.round_robin_bit16_indices = []345self.round_robin_bit16_meta = []346347# Use different parallel to do all_to_all_reduce related things348# padding on each partition for alignment purposes349self.groups_padding = []350# loop to deal with groups351for i, param_group in enumerate(self.optimizer.param_groups):352partition_id = dist.get_rank(group=self.real_dp_process_group[i])353354# push this group to list before modify355# TODO: Explore simplification that avoids the extra book-keeping by pushing the reordered group356trainable_parameters = []357for param in param_group['params']:358if param.requires_grad:359param.grad_accum = None360trainable_parameters.append(param)361self.bit16_groups.append(trainable_parameters)362363# not sure why apex was cloning the weights before flattening364# removing cloning here365366see_memory_usage(f"Before moving param group {i} to CPU")367# move all the parameters to cpu to free up GPU space for creating flat buffer368369# Create temp CPU param copies, free accelerator tensors370orig_group_numel = 0371for param in self.bit16_groups[i]:372orig_group_numel += param.numel()373param.cpu_data = opguard_wrapped__cpu_data_cpu(325)(param.data.cpu)374param.data = opguard_wrapped__data_to(326)(torch.empty(1).to, param.device)375376empty_cache()377see_memory_usage(f"After moving param group {i} to CPU", force=False)378379# Reorder group parameters for load balancing of gradient partitioning during backward among ranks.380# This ensures that gradients are reduced in a fashion such that ownership round robins among the ranks.381# For example, rather than 3 gradients (g_n+2, g_n+1, g_n) that are reduced consecutively belonging382# to the same rank, instead they will belong to 3 ranks (r_m+2, r_m+1, r_m).383if self.round_robin_gradients:384round_robin_tensors, round_robin_indices = self._round_robin_reorder(385self.bit16_groups[i], dist.get_world_size(group=self.real_dp_process_group[i]))386else:387round_robin_tensors = self.bit16_groups[i]388round_robin_indices = list(range(len(self.bit16_groups[i])))389390self.round_robin_bit16_groups.append(round_robin_tensors)391self.round_robin_bit16_indices.append(round_robin_indices)392393# Create meta tensors list, ordered according to round_robin_tensors394meta_tensors = []395for param in round_robin_tensors:396opguard_wrapped__meta_tensors_append(348)(meta_tensors.append, torch.zeros_like(param.cpu_data, device="meta"))397self.round_robin_bit16_meta.append(meta_tensors)398399# create flat buffer in CPU400flattened_buffer = self.flatten_dense_tensors_aligned(401self.round_robin_bit16_groups[i],402self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i]),403use_cpu_data=True)404405# free temp CPU params406for param in self.bit16_groups[i]:407del param.cpu_data408409# Move CPU flat tensor to the accelerator memory.410opguard_wrapped__bit16_groups_flat_append(362)(self.bit16_groups_flat.append, flattened_buffer.to(get_accelerator().current_device_name()))411del flattened_buffer412413see_memory_usage(f"After flattening and moving param group {i} to GPU", force=False)414415# Record padding required for alignment416if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1:417padding = self.bit16_groups_flat[i].numel() - orig_group_numel418else:419padding = 0420self.groups_padding.append(padding)421422if dist.get_rank(group=self.real_dp_process_group[i]) == 0:423see_memory_usage(f"After Flattening and after emptying param group {i} cache", force=False)424425# set model bit16 weight to slices of flattened buffer426self._update_model_bit16_weights(i)427428# divide the flat weights into near equal partition equal to the data parallel degree429# each process will compute on a different part of the partition430data_parallel_partitions = self.get_data_parallel_partitions(self.bit16_groups_flat[i], i)431self.parallel_partitioned_bit16_groups.append(data_parallel_partitions)432433# verify that data partition start locations are 4-byte aligned434for partitioned_data in data_parallel_partitions:435assert (partitioned_data.data_ptr() % (2 * self.nccl_start_alignment_factor) == 0)436437# A partition of the fp32 master weights that will be updated by this process.438# Note that the params in single_partition_of_fp32_groups is cloned and detached439# from the origin params of the model.440if not fp16_master_weights_and_gradients:441weights_partition = opguard_wrapped__weights_partition_detach(393)(self.parallel_partitioned_bit16_groups[i][partition_id].to(442self.device).clone().float().detach)443else:444weights_partition = opguard_wrapped__weights_partition_detach(396)(self.parallel_partitioned_bit16_groups[i][partition_id].to(445self.device).clone().half().detach)446447if self.cpu_offload:448weights_partition = get_accelerator().pin_memory(weights_partition)449450self.single_partition_of_fp32_groups.append(weights_partition)451452# Set local optimizer to have flat params of its own partition.453# After this, the local optimizer will only contain its own partition of params.454# In that case, the local optimizer only saves the states(momentum, variance, etc.) related to its partition's params(zero stage1).455self.single_partition_of_fp32_groups[456i].requires_grad = True # keep this in case internal optimizer uses it457param_group['params'] = [self.single_partition_of_fp32_groups[i]]458459partition_size = len(self.bit16_groups_flat[i]) / dist.get_world_size(group=self.real_dp_process_group[i])460params_in_partition, params_not_in_partition, first_offset = self.get_partition_info(461self.round_robin_bit16_groups[i], partition_size, partition_id)462463self.partition_size.append(partition_size)464self.params_in_partition.append(params_in_partition)465self.params_not_in_partition.append(params_not_in_partition)466self.first_offset.append(first_offset)467468self.reduce_bucket_size = int(reduce_bucket_size)469self.use_multi_rank_bucket_allreduce = use_multi_rank_bucket_allreduce470self.allgather_bucket_size = int(allgather_bucket_size)471472self.reduction_stream = None if get_accelerator().is_synchronized_device() else get_accelerator().Stream()473#self.copy_grad_stream = get_accelerator().Stream()474self.callback_queued = False475476self.param_dict = {}477478# map between param_id and bool to specify if a param is in this partition479self.is_param_in_current_partition = {}480481self.grads_in_ipg_bucket = []482self.params_in_ipg_bucket = []483self.elements_in_ipg_bucket = 0484self.params_already_reduced = []485self._release_ipg_buffers()486self.previous_reduced_grads = None487self.ipg_bucket_has_moe_params = False488489# simplified param id490self.param_id = {}491492#interesting code: unique ids being assigned to individual parameters493largest_param_numel = 0494count = 0495for i, params_group in enumerate(self.bit16_groups):496for param in params_group:497unique_id = id(param)498self.param_id[unique_id] = count499self.param_dict[count] = param500self.params_already_reduced.append(False)501if param.numel() > largest_param_numel:502largest_param_numel = param.numel()503count = count + 1504505for param_group in self.params_in_partition:506for param in param_group:507self.is_param_in_current_partition[self.get_param_id(param)] = True508509for param_group in self.params_not_in_partition:510for param in param_group:511self.is_param_in_current_partition[self.get_param_id(param)] = False512513if self.cpu_offload:514self.accumulated_grads_in_cpu = {}515self.norm_for_param_grads = {}516self.local_overflow = False517self.grad_position = {}518self.temp_grad_buffer_for_cpu_offload = torch.zeros(largest_param_numel,519device=self.device,520dtype=self.dtype)521if self.cpu_offload_pin_memory:522self.temp_grad_buffer_for_cpu_offload = get_accelerator().pin_memory(523self.temp_grad_buffer_for_cpu_offload)524self.temp_grad_buffer_for_gpu_offload = torch.zeros(largest_param_numel,525device=get_accelerator().current_device_name(),526dtype=self.dtype)527for i, params_group in enumerate(self.bit16_groups):528self.get_grad_position(i, self.params_in_partition[i], self.first_offset[i], self.partition_size[i])529530# mapping from parameter to partition that it belongs to531self.param_to_partition_ids = {}532533# stores if a partition has been reduced in this step534self.is_partition_reduced = {}535536# number of grads in partition that still need to be computed537self.remaining_grads_in_partition = {}538539# total number of grads in partition540self.total_grads_in_partition = {}541542# stores if a grad in a partition has been computed or not543self.is_grad_computed = {}544545# stores the offset at which a parameter gradient needs to be inserted in a partition546self.grad_partition_insertion_offset = {}547548# the offset in the gradient at which it must be inserted at the beginning of the partition549self.grad_start_offset = {}550551# will store the averaged gradients required by this partition552self.averaged_gradients = {}553554# For cpu_offload, will store the averaged gradients required by this partition555self.offload_gradient_dict = {}556557# store index of first parameter in each partition558self.first_param_index_in_partition = {}559560# initializes all data structures for implementing gradient partitioning561self.initialize_gradient_partitioning_data_structures()562563# resets the data structure value for the next backward propagation564self.reset_partition_gradient_structures()565566# creates backward hooks for gradient partitioning567self._grad_acc_hooks = []568if self.partition_gradients or self.overlap_comm:569self.create_reduce_and_remove_grad_hooks()570571self.custom_loss_scaler = False572self.external_loss_scale = None573574# we may have a way of fusing dynamic scale. Do not support for now575self.loss_scaler = CreateLossScaler(dtype=self.dtype,576static_loss_scale=static_loss_scale,577dynamic_scaling=dynamic_loss_scale,578dynamic_loss_args=dynamic_loss_args)579self.dynamic_loss_scale = self.loss_scaler.dynamic580581if self.dtype != torch.float16:582# Only fp16 should use dynamic loss scaling583assert self.loss_scaler.cur_scale == 1.0584assert not self.dynamic_loss_scale585586see_memory_usage("Before initializing optimizer states", force=True)587self.initialize_optimizer_states()588see_memory_usage("After initializing optimizer states", force=True)589590if dist.get_rank() == 0:591logger.info(f"optimizer state initialized")592593if dist.get_rank(group=self.dp_process_group) == 0:594see_memory_usage(f"After initializing ZeRO optimizer", force=True)595596self._link_all_hp_params()597self._hp_optimizer_states_linked = False598599self._enable_universal_checkpoint()600self._param_slice_mappings = self._create_param_mapping()601602def destroy(self):603for hook in self._grad_acc_hooks:604hook.remove()605self.print_rank_0("Removed grad acc hooks")606607def _enable_universal_checkpoint(self):608for lp_param_group in self.bit16_groups:609enable_universal_checkpoint(param_list=lp_param_group)610611def _create_param_mapping(self):612param_mapping = []613for i, _ in enumerate(self.optimizer.param_groups):614param_mapping_per_group = OrderedDict()615for lp in self.bit16_groups[i]:616if lp._hp_mapping is not None:617lp_name = self.param_names[lp]618param_mapping_per_group[lp_name] = lp._hp_mapping.get_hp_fragment_address()619param_mapping.append(param_mapping_per_group)620621return param_mapping622623def _link_all_hp_params(self):624dp_world_size = dist.get_world_size(group=self.dp_process_group)625if self.cpu_offload:626self._get_offload_gradient_dict()627628for i, _ in enumerate(self.optimizer.param_groups):629# Link bit16 and fp32 params in partition630partition_id = dist.get_rank(group=self.real_dp_process_group[i])631partition_size = self.bit16_groups_flat[i].numel() // dp_world_size632flat_hp_partition = self.single_partition_of_fp32_groups[i]633link_hp_params(lp_param_list=self.bit16_groups[i],634flat_hp_partition=flat_hp_partition,635gradient_dict=self.averaged_gradients,636offload_gradient_dict=self.offload_gradient_dict,637use_offload=self.cpu_offload,638param_group_index=i,639partition_start=partition_id * partition_size,640partition_size=partition_size,641dp_group=self.real_dp_process_group[i])642643def _lazy_init_hp_params_optimizer_state(self):644if not self._hp_optimizer_states_linked:645for i, _ in enumerate(self.optimizer.param_groups):646lazy_init_hp_params_optimizer_state(self.bit16_groups[i], self.single_partition_of_fp32_groups[i],647self.optimizer.state)648self._hp_optimizer_states_linked = True649650def is_moe_group(self, group):651return 'moe' in group and group['moe']652653def _configure_moe_settings(self):654# if we're using ZeRO stage 2, ensure contiguous gradients are used655if self.partition_gradients:656assert self.contiguous_gradients, "Contiguous Gradients in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE"657# NOTE: To run ZeRO stage 1 with MoE, we need to set self.contiguous_gradients to True or ignore the assertion658if not self.partition_gradients and not self.contiguous_gradients:659logger.warn(660"ZeRO Stage 1 has not been thoroughly tested with MoE. This configuration is still experimental.")661assert self.reduce_scatter, "Reduce Scatter in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE"662663assert any(664[self.is_moe_group(group) for group in self.optimizer.param_groups]665), "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"666self.is_moe_param_group = []667for i, group in enumerate(self.optimizer.param_groups):668if self.is_moe_group(group):669assert all([is_moe_param(param)670for param in group['params']]), "All params in MoE group must be MoE params"671self.real_dp_process_group[i] = self.expert_dp_process_group[group['name']]672self.partition_count[i] = dist.get_world_size(group=self.expert_dp_process_group[group['name']])673self.is_moe_param_group.append(True)674else:675self.is_moe_param_group.append(False)676677assert self.expert_dp_process_group is not None, "Expert data parallel group should be configured with MoE"678assert self.ep_process_group is not None, "Expert parallel group should be configured with MoE"679680def _update_model_bit16_weights(self, group_index):681updated_params = opguard_wrapped__updated_params_unflatten(633)(self.unflatten, self.bit16_groups_flat[group_index], self.round_robin_bit16_meta[group_index])682for p, q in zip(self.round_robin_bit16_groups[group_index], updated_params):683p.data = q.data684685# set model fp16 weight to slices of reordered flattened buffer686for param_index, param in enumerate(self.bit16_groups[group_index]):687new_index = self.round_robin_bit16_indices[group_index][param_index]688param.data = self.round_robin_bit16_groups[group_index][new_index].data689690def _round_robin_reorder(self, tensor_list, num_partitions):691692# disable round robin if need to debug something693# return tensor_list, list(range(len(tensor_list)))694695partition_tensors = {}696697for i, tensor in enumerate(tensor_list):698j = i % num_partitions699if not j in partition_tensors:700partition_tensors[j] = []701partition_tensors[j].append((i, tensor))702703reordered_tensors = []704reordered_indices = {}705706for partition_index in partition_tensors.keys():707for i, (original_index, tensor) in enumerate(partition_tensors[partition_index]):708reordered_indices[original_index] = len(reordered_tensors)709reordered_tensors.append(tensor)710711return reordered_tensors, reordered_indices712713def _release_ipg_buffers(self):714if self.contiguous_gradients:715self.ipg_buffer = None716self.grads_in_partition = None717self.grads_in_partition_offset = 0718719def initialize_optimizer_states(self):720721for i, group in enumerate(self.bit16_groups):722single_grad_partition = opguard_wrapped__single_grad_partition_zeros(674)(torch.zeros, int(self.partition_size[i]),723dtype=self.single_partition_of_fp32_groups[i].dtype,724device=self.device)725self.single_partition_of_fp32_groups[i].grad = get_accelerator().pin_memory(726single_grad_partition) if self.cpu_offload_pin_memory else single_grad_partition727728# Initialize the optimizer states with the flattened fp32 partition.729# State initialization for the Adagrad optimizer occurs at construction as opposed to other optimizers730# which do lazy initialization of the state at the first call to step.731if isinstance(self.optimizer, torch.optim.Adagrad):732self.optimizer = torch.optim.Adagrad(self.single_partition_of_fp32_groups, **self.optimizer.defaults)733734if not self.cpu_offload:735for group in self.single_partition_of_fp32_groups:736group.grad = None #class init737738return739740#########################################################################741#################### ZeRO Stage 1 - reduce gradients ####################742#########################################################################743def reduce_gradients(self, pipeline_parallel=False):744world_size = dist.get_world_size(self.dp_process_group)745my_rank = dist.get_rank(self.dp_process_group)746747# with PP we must create ipg buffer, since backward is handled outside zero748if pipeline_parallel and self.contiguous_gradients:749self.ipg_buffer = []750buf_0 = opguard_wrapped__buf_0_empty(702)(torch.empty, int(self.reduce_bucket_size),751dtype=self.dtype,752device=get_accelerator().current_device_name())753self.ipg_buffer.append(buf_0)754self.ipg_index = 0755756if not self.overlap_comm:757for i, group in enumerate(self.bit16_groups):758for param in group:759grad_reduc = self.get_gradient_for_reduction(param)760if grad_reduc is not None:761self.reduce_ready_partitions_and_remove_grads(param, i)762# reduce any pending grads in either hook/non-hook case763self.overlapping_partition_gradients_reduce_epilogue()764765#########################################################################766#########################ZeRO Partition Gradients########################767#########################################################################768769def get_first_param_index(self, group_id, param_group, partition_id):770for index, param in enumerate(param_group):771param_id = self.get_param_id(param)772if partition_id in self.param_to_partition_ids[group_id][param_id]:773return index774return None775776def initialize_gradient_partitioning_data_structures(self):777778for i, param_group in enumerate(self.round_robin_bit16_groups):779total_partitions = dist.get_world_size(group=self.real_dp_process_group[i])780781self.param_to_partition_ids[i] = {}782self.is_partition_reduced[i] = {}783self.total_grads_in_partition[i] = {}784self.remaining_grads_in_partition[i] = {}785self.is_grad_computed[i] = {}786self.grad_partition_insertion_offset[i] = {}787self.grad_start_offset[i] = {}788self.first_param_index_in_partition[i] = {}789790for partition_id in range(total_partitions):791self.is_grad_computed[i][partition_id] = {}792self.grad_partition_insertion_offset[i][partition_id] = {}793self.grad_start_offset[i][partition_id] = {}794self.total_grads_in_partition[i][partition_id] = 0795self.initialize_gradient_partition(i, param_group, partition_id)796self.is_partition_reduced[i][partition_id] = False797self.first_param_index_in_partition[i][partition_id] = self.get_first_param_index(798i, param_group, partition_id)799800def independent_gradient_partition_epilogue(self):801self.report_ipg_memory_usage(f"In ipg_epilogue before reduce_ipg_grads", 0)802self.reduce_ipg_grads()803self.report_ipg_memory_usage(f"In ipg_epilogue after reduce_ipg_grads", 0)804805# if dist.get_rank() == 0:806# logger.info("Params already reduced %s", self.params_already_reduced)807for i in range(len(self.params_already_reduced)):808self.params_already_reduced[i] = False809810if self.overlap_comm:811if not get_accelerator().resolves_data_dependency():812get_accelerator().synchronize()813# It is safe to clear previously reduced grads of other partitions814self._clear_previous_reduced_grads()815816if self.cpu_offload is False:817for i, _ in enumerate(self.bit16_groups):818819if not i in self.averaged_gradients or self.averaged_gradients[i] is None:820self.averaged_gradients[i] = self.get_flat_partition(821self.params_in_partition[i],822self.first_offset[i],823self.partition_size[i],824dtype=self.gradient_accumulation_dtype,825device=get_accelerator().current_device_name(),826return_tensor_list=True)827else:828avg_new = self.get_flat_partition(self.params_in_partition[i],829self.first_offset[i],830self.partition_size[i],831dtype=self.gradient_accumulation_dtype,832device=get_accelerator().current_device_name(),833return_tensor_list=True)834835for accumulated_grad, new_avg_grad in zip(self.averaged_gradients[i], avg_new):836accumulated_grad.add_(new_avg_grad)837838self._release_ipg_buffers()839840# No need to keep the gradients anymore.841# All gradients required by the step842# are in self.averaged_gradients843self.zero_grad(set_to_none=True)844see_memory_usage(f"End ipg_epilogue")845846# resets all partition to no reduced847# sets remaining grads to the total number of grads in each partition848# set is grad computed to false for all grads in partition849def reset_partition_gradient_structures(self):850for i, _ in enumerate(self.bit16_groups):851total_partitions = dist.get_world_size(group=self.real_dp_process_group[i])852for partition_id in range(total_partitions):853self.is_partition_reduced[i][partition_id] = False854self.remaining_grads_in_partition[i][partition_id] = self.total_grads_in_partition[i][partition_id]855856for param_id in self.is_grad_computed[i][partition_id]:857self.is_grad_computed[i][partition_id][param_id] = False858859def initialize_gradient_partition(self, i, param_group, partition_id):860861def set_key_value_list(dictionary, key, value):862if key in dictionary:863dictionary[key].append(value)864else:865dictionary[key] = [value]866867def increment_value(dictionary, key):868if key in dictionary:869dictionary[key] += 1870else:871dictionary[key] = 1872873partition_size = self.partition_size[i]874875start_index = partition_size * partition_id876end_index = partition_size * (partition_id + 1)877878current_index = 0879first_offset = 0880881for param in param_group:882883param_size = param.numel()884param_id = self.get_param_id(param)885886if start_index <= current_index < end_index:887set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id)888increment_value(self.total_grads_in_partition[i], partition_id)889890self.is_grad_computed[i][partition_id][param_id] = False891892self.grad_partition_insertion_offset[i][partition_id][param_id] = current_index - start_index893self.grad_start_offset[i][partition_id][param_id] = 0894895elif current_index < start_index < (current_index + param_size):896assert (first_offset == 0897), "This can happen either zero or only once as this must be the first tensor in the partition"898first_offset = start_index - current_index899900set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id)901increment_value(self.total_grads_in_partition[i], partition_id)902903self.is_grad_computed[i][partition_id][param_id] = False904905self.grad_partition_insertion_offset[i][partition_id][param_id] = 0906self.grad_start_offset[i][partition_id][param_id] = first_offset907908current_index = current_index + param_size909910def overlapping_partition_gradients_reduce_epilogue(self):911self.independent_gradient_partition_epilogue()912913def fill_grad_accum_attribute(self):914for group in self.bit16_groups:915for param in group:916if param.grad is not None:917if param.grad_accum is None:918param.grad_accum = param.grad.to(self.gradient_accumulation_dtype)919else:920param.grad_accum.add_(921param.grad.to(self.gradient_accumulation_dtype).view(param.grad_accum.shape))922param.grad = None923924def get_gradient_for_reduction(self, param):925if self.use_grad_accum_attribute:926return param.grad_accum.to(self.dtype) if param.grad_accum is not None else None927else:928return param.grad929930def get_param_gradient_attribute(self, param):931return param.grad_accum if self.use_grad_accum_attribute else param.grad932933# Clear the tensor the reduction gradient attribute is pointing to934def clear_grad_attribute(self, param):935if self.use_grad_accum_attribute:936param.grad_accum = None937else:938param.grad = None939940def create_reduce_and_remove_grad_hooks(self):941self.grad_accs = []942for i, param_group in enumerate(self.bit16_groups):943for param in param_group:944if param.requires_grad:945946def wrapper(param, i):947param_tmp = param.expand_as(param)948grad_acc = param_tmp.grad_fn.next_functions[0][0]949950def reduce_partition_and_remove_grads(*notneeded):951self.reduce_ready_partitions_and_remove_grads(param, i)952953self._grad_acc_hooks.append(grad_acc.register_hook(reduce_partition_and_remove_grads))954self.grad_accs.append(grad_acc)955956opguard_wrapped__param_wrapper(908)(wrapper, param, i)957958def get_param_id(self, param):959unique_id = id(param)960return self.param_id[unique_id]961962def report_ipg_memory_usage(self, tag, param_elems):963elem_count = self.elements_in_ipg_bucket + param_elems964percent_of_bucket_size = (100.0 * elem_count) // self.reduce_bucket_size965see_memory_usage(966f"{tag}: elems in_bucket {self.elements_in_ipg_bucket} param {param_elems} max_percent {percent_of_bucket_size}"967)968969# create a flat tensor aligned at the alignment boundary970def flatten_dense_tensors_aligned(self, tensor_list, alignment, use_cpu_data=False):971tensor_list = [param.cpu_data for param in tensor_list] if use_cpu_data else tensor_list972return opguard_wrapped__align_dense_tensors_flatten(924)(self.flatten, align_dense_tensors(tensor_list, alignment))973974############### Independent Partition Gradient ########################975def reduce_independent_p_g_buckets_and_remove_grads(self, param, i):976977grad_reduc = self.get_gradient_for_reduction(param)978if self.elements_in_ipg_bucket + param.numel() > self.reduce_bucket_size:979self.report_ipg_memory_usage("In ipg_remove_grads before reduce_ipg_grads", param.numel())980self.reduce_ipg_grads()981if self.contiguous_gradients and self.overlap_comm:982# Swap ipg_index between 0 and 1983self.ipg_index = 1 - self.ipg_index984self.report_ipg_memory_usage("In ipg_remove_grads after reduce_ipg_grads", param.numel())985986param_id = self.get_param_id(param)987assert self.params_already_reduced[param_id] == False, \988f"The parameter {param_id} has already been reduced. \989Gradient computed twice for this partition. \990Multiple gradient reduction is currently not supported"991992if self.contiguous_gradients:993if param.numel() > self.reduce_bucket_size:994self.extra_large_param_to_reduce = param995else:996# keeping the gradients contiguous to prevent memory fragmentation, and avoid flattening997new_grad_tensor = opguard_wrapped__new_grad_tensor_narrow(949)(self.ipg_buffer[self.ipg_index].narrow, 0, self.elements_in_ipg_bucket, param.numel())998opguard_wrapped__new_grad_tensor_copy_(950)(new_grad_tensor.copy_, grad_reduc.view(-1))999grad_reduc.data = opguard_wrapped__data_view_as(951)(new_grad_tensor.data.view_as, grad_reduc)10001001self.elements_in_ipg_bucket += param.numel()10021003assert grad_reduc is not None, f"rank {dist.get_rank()} - Invalid to reduce Param {param_id} with None gradient"10041005self.grads_in_ipg_bucket.append(grad_reduc)1006self.params_in_ipg_bucket.append((i, param, param_id))10071008#make sure the average tensor function knows how to average the gradients1009if is_moe_param(param):1010self.ipg_bucket_has_moe_params = True10111012self.report_ipg_memory_usage("End ipg_remove_grads", 0)10131014def print_rank_0(self, message):1015if dist.get_rank() == 0:1016logger.info(message)10171018def gradient_reduction_w_predivide(self, tensor):10191020dp_world_size = dist.get_world_size(group=self.dp_process_group)10211022tensor_to_allreduce = tensor10231024if self.communication_data_type != tensor.dtype:1025tensor_to_allreduce = tensor.to(self.communication_data_type)10261027if self.postscale_gradients:1028if self.gradient_predivide_factor != 1.0:1029tensor_to_allreduce.mul_(1. / self.gradient_predivide_factor)10301031dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group)10321033if self.gradient_predivide_factor != dp_world_size:1034tensor_to_allreduce.mul_(self.gradient_predivide_factor /1035(dp_world_size / float(self.sequence_parallel_size)))1036else:1037tensor_to_allreduce.div_(dp_world_size / float(self.sequence_parallel_size))1038dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group)10391040if self.communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce:1041tensor.copy_(tensor_to_allreduce)10421043return tensor10441045def allreduce_and_copy_with_multiple_ranks(self,1046small_bucket,1047log=None,1048divide=True,1049process_group=None,1050bucket_ranks=None):1051process_group = self.dp_process_group if process_group is None else process_group1052allreduced = self.allreduce_bucket(small_bucket, log=log, divide=divide, process_group=process_group)1053for buf, synced, bucket_rank in zip(small_bucket, opguard_wrapped__allreduced_unflatten(1005)(self.unflatten, allreduced, small_bucket), bucket_ranks):1054if dist.get_rank(group=process_group) == bucket_rank:1055opguard_wrapped__buf_copy_(1007)(buf.copy_, synced)10561057def allreduce_and_scatter(self, bucket, numel_per_bucket=500000000, log=None, divide=True, process_group=None):1058small_bucket = []1059small_bucket_ranks = []1060numel = 01061allreduce_sizes = []10621063for i, bucket_elem in enumerate(bucket):1064rank, tensor = bucket_elem1065small_bucket.append(tensor)1066small_bucket_ranks.append(rank)1067numel = numel + tensor.numel()1068if numel > numel_per_bucket:1069self.allreduce_and_copy_with_multiple_ranks(small_bucket,1070log=None,1071divide=divide,1072process_group=process_group,1073bucket_ranks=small_bucket_ranks)1074small_bucket = []1075small_bucket_ranks = []1076numel = 010771078if len(small_bucket) > 0:1079self.allreduce_and_copy_with_multiple_ranks(small_bucket,1080log=None,1081divide=divide,1082process_group=process_group,1083bucket_ranks=small_bucket_ranks)10841085def average_tensor(self, tensor):1086if self.overlap_comm:1087stream = self.reduction_stream1088if not get_accelerator().resolves_data_dependency():1089stream.wait_stream(get_accelerator().current_stream())1090else:1091stream = get_accelerator().current_stream()10921093with get_accelerator().stream(stream):1094if not self.reduce_scatter:1095self.gradient_reduction_w_predivide(tensor)1096return10971098# Accumulate destination ranks and bucket offsets for each gradient slice.1099# Note: potential future optimization, record access pattern of parameters1100# in backward pass and partition gradients w.r.t. access pattern so that our1101# bucket is guaranteed to be contiguous w.r.t. ranks1102rank_and_offsets = []1103real_dp_process_group = []1104curr_size = 01105prev_id, prev_process_group = -1, None11061107process_group = self.dp_process_group1108# count = 01109for i, param, param_id in self.params_in_ipg_bucket:11101111process_group = self.dp_process_group1112grad_reduc = self.get_gradient_for_reduction(param)1113#Averages gradients at parameter level if ipg has a moe param1114#Otherwise averaging is done at the entire buffer level at the end of the loop1115# MoE param have different groups1116if self.ipg_bucket_has_moe_params:1117process_group = self.expert_dp_process_group[param.group_name] if is_moe_param(1118param) else self.dp_process_group1119grad_reduc.data.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size))11201121partition_ids = self.param_to_partition_ids[i][param_id]1122assert all([p_id < dist.get_world_size(group=process_group) for p_id in partition_ids1123]), f"world size {dist.get_world_size(group=process_group)} and p_ids: {partition_ids}"1124partition_size = self.partition_size[i]1125# Get all partition ids + their offsets1126partition_ids_w_offsets = []1127for partition_id in partition_ids:1128offset = self.grad_start_offset[i][partition_id][param_id]1129partition_ids_w_offsets.append((partition_id, offset))1130partition_ids_w_offsets.sort(key=lambda t: t[1])11311132# Calculate rank and offsets for grad slices1133for idx in range(len(partition_ids_w_offsets)):1134partition_id, offset = partition_ids_w_offsets[idx]11351136# if dist.get_rank() == 0 and count < 100:1137# 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}")1138# count += 111391140# Calculate numel for grad slice depending on partition location1141if idx == len(partition_ids_w_offsets) - 1:1142# Last partition_id uses its own offset1143numel = param.numel() - offset1144else:1145# Set numel to next partition's offset1146numel = partition_ids_w_offsets[idx + 1][1] - offset11471148# Merge bucket ranges if they belong to the same rank1149if partition_id == prev_id and process_group == prev_process_group:1150prev_pid, prev_size, prev_numel = rank_and_offsets[-1]1151rank_and_offsets[-1] = (prev_pid, prev_size, prev_numel + numel)1152else:1153rank_and_offsets.append((partition_id, curr_size, numel))1154real_dp_process_group.append(process_group)1155curr_size += numel1156prev_id, prev_process_group = partition_id, process_group11571158if not self.ipg_bucket_has_moe_params:1159opguard_wrapped__tensor_div_(1111)(tensor.div_, dist.get_world_size(group=self.dp_process_group) / float(self.sequence_parallel_size))11601161buckets = {}1162for i, (dst, bucket_offset, numel) in enumerate(rank_and_offsets):1163grad_slice = opguard_wrapped__grad_slice_narrow(1115)(tensor.narrow, 0, int(bucket_offset), int(numel))1164bucket_key = real_dp_process_group[i] if self.use_multi_rank_bucket_allreduce else (1165dst, real_dp_process_group[i])1166if bucket_key not in buckets:1167buckets[bucket_key] = []1168if self.use_multi_rank_bucket_allreduce:1169buckets[bucket_key].append((dst, grad_slice))1170else:1171buckets[bucket_key].append(grad_slice)11721173for bucket_key in buckets:1174if self.use_multi_rank_bucket_allreduce:1175self.allreduce_and_scatter(buckets[bucket_key],1176numel_per_bucket=self.reduce_bucket_size,1177divide=False,1178process_group=bucket_key)1179else:1180dst, process_group = bucket_key1181self.allreduce_no_retain(buckets[bucket_key],1182numel_per_bucket=self.reduce_bucket_size,1183rank=dst,1184divide=False,1185process_group=process_group)11861187##############################################################################1188############################# CPU Offload Methods#############################1189##############################################################################1190def get_grad_position(self, group_id, tensor_list, first_offset, partition_size):1191current_offset = 011921193for i, tensor in enumerate(tensor_list):1194param_id = self.get_param_id(tensor)1195param_start_offset = 011961197num_elements = tensor.numel()11981199# we need to offset to get to the right element1200if i == 0 and first_offset > 0:1201tensor_offset = first_offset1202num_elements = num_elements - tensor_offset1203param_start_offset = first_offset12041205# we dont need all elements of the tensor1206if num_elements > (partition_size - current_offset):1207num_elements = partition_size - current_offset12081209self.grad_position[param_id] = [1210int(group_id), int(param_start_offset),1211int(current_offset), int(num_elements)1212]1213current_offset += num_elements12141215def update_overflow_tracker_for_param_grad(self, param):1216grad_accum = self.get_param_gradient_attribute(param)1217if grad_accum is not None and self._has_inf_or_nan(grad_accum.data):1218self.local_overflow = True12191220def _get_offload_gradient_dict(self):1221for param_group_index, _ in enumerate(self.optimizer.param_groups):1222self.offload_gradient_dict[param_group_index] = []1223for lp_param in self.params_in_partition[param_group_index]:1224param_id = self.get_param_id(lp_param)1225[_, _, dest_offset, num_elements] = self.grad_position[param_id]1226dest_tensor = self.single_partition_of_fp32_groups[param_group_index].grad.view(-1).narrow(12270, dest_offset, num_elements)1228self.offload_gradient_dict[param_group_index].append(dest_tensor)12291230def async_accumulate_grad_in_cpu_via_gpu(self, param):1231param_id = self.get_param_id(param)12321233[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]12341235# copy to a preexisiting buffer to avoid memory allocation penalty1236dest_buffer = self.temp_grad_buffer_for_gpu_offload.view(-1).narrow(0, 0, param.numel())12371238#buffer for storing gradients for this parameter in CPU1239def buffer_to_accumulate_to_in_cpu():1240if not self.fp16_master_weights_and_gradients:1241buffer = torch.zeros(param.numel(), dtype=param.dtype, device=self.device)1242return get_accelerator().pin_memory(buffer) if self.cpu_offload_pin_memory else buffer1243else:1244return self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements)12451246#accumulate gradients into param.grad_accum or parts of it that belongs to this partition1247def accumulate_gradients():1248grad_accum = self.get_param_gradient_attribute(param)1249if not self.fp16_master_weights_and_gradients:1250dest_buffer.copy_(self.accumulated_grads_in_cpu[param_id].view(-1), non_blocking=True)1251grad_accum.data.view(-1).add_(dest_buffer)1252else:1253dest_buffer.narrow(0, source_offset,1254num_elements).copy_(self.accumulated_grads_in_cpu[param_id].view(-1),1255non_blocking=True)1256grad_accum.data.view(-1).narrow(0, source_offset,1257num_elements).add_(dest_buffer.narrow(0, source_offset, num_elements))12581259#move accumulated gradients back to CPU1260def copy_gradients_to_cpu():1261grad_accum = self.get_param_gradient_attribute(param)1262if not self.fp16_master_weights_and_gradients:1263self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1), non_blocking=True)1264else:1265self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1).narrow(12660, source_offset, num_elements),1267non_blocking=True)12681269if param_id not in self.accumulated_grads_in_cpu:1270self.accumulated_grads_in_cpu[param_id] = buffer_to_accumulate_to_in_cpu()12711272if self.micro_step_id > 0:1273accumulate_gradients()12741275# at the boundary we will send 32bit directly1276if not self.is_gradient_accumulation_boundary:1277copy_gradients_to_cpu()12781279def set_norm_for_param_grad(self, param):1280param_id = self.get_param_id(param)1281grad_accum = self.get_param_gradient_attribute(param)1282accumulated_grad = self.accumulated_grads_in_cpu[1283param_id] if self.gradient_accumulation_steps > 1 else grad_accum12841285[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]12861287start = source_offset1288accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements)12891290self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2)12911292def set_norm_for_param_grad_in_gpu(self, param):1293param_id = self.get_param_id(param)1294grad_accum = self.get_param_gradient_attribute(param)1295if grad_accum is None:1296accumulated_grad = param.grad1297else:1298accumulated_grad = grad_accum12991300[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]13011302start = source_offset1303accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements)13041305self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2)13061307def async_inplace_copy_grad_to_fp32_buffer_from_gpu(self, param):1308param_id = self.get_param_id(param)13091310[i, source_offset, dest_offset, num_elements] = self.grad_position[param_id]13111312dest_tensor = self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements)13131314grad_accum = self.get_param_gradient_attribute(param)1315if grad_accum is None:1316src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements)1317else:1318src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements)1319if not self.fp16_master_weights_and_gradients:1320src_tensor = src_tensor.float()13211322dest_tensor.copy_(src_tensor, non_blocking=True)1323param.grad = None #offload only13241325def complete_grad_norm_calculation_for_cpu_offload(self, params):1326total_norm = 0.01327norm_type = 2.01328for p in params:1329# Pipeline parallelism may replicate parameters. Avoid multi-counting.1330if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated:1331continue13321333if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0):1334param_id = self.get_param_id(p)1335# as some model have trainable parameters but skipped in training,1336# their backward hooks in self.create_reduce_and_remove_grad_hooks() will not run,1337# so they have no norm_for_param_grads1338if param_id in self.norm_for_param_grads:1339param_norm = self.norm_for_param_grads[param_id]1340total_norm += param_norm.item()**21341else:1342# As unused parameters in modules may not be expected sometimes,1343# add an explicit error msg when it occurred and an option to1344# avoid the error1345assert self.ignore_unused_parameters, """1346This assert indicates that your module has parameters that1347were not used in producing loss.1348You can avoid this assert by1349(1) enable ignore_unused_parameters option in zero_optimization config;1350(2) making sure all trainable parameters and `forward` function1351outputs participate in calculating loss.1352"""13531354# Sum across all model parallel GPUs.1355total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)])1356dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=self.dp_process_group)13571358self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM)13591360total_norm = total_norm_cuda[0].item()**(1. / norm_type)13611362if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm:1363total_norm = -113641365return total_norm13661367############################################################################################1368def copy_grads_in_partition(self, param):1369if self.cpu_offload:13701371if self.gradient_accumulation_steps > 1:1372self.async_accumulate_grad_in_cpu_via_gpu(param)13731374if self.is_gradient_accumulation_boundary:1375self.set_norm_for_param_grad_in_gpu(param)13761377self.update_overflow_tracker_for_param_grad(param)13781379self.async_inplace_copy_grad_to_fp32_buffer_from_gpu(param)13801381return1382#print(f"ID {self.get_param_id(param)} grad norm {param.grad.norm()}")1383if self.grads_in_partition is None:1384self.grads_in_partition_offset = 01385total_size = 01386for group in self.params_in_partition:1387for param_in_partition in group:1388total_size += param_in_partition.numel()13891390see_memory_usage(f"before copying {total_size} gradients into partition")1391self.grads_in_partition = opguard_wrapped__grads_in_partition_empty(1343)(torch.empty, int(total_size),1392dtype=self.dtype,1393device=get_accelerator().current_device_name())1394see_memory_usage(f"after copying {total_size} gradients into partition")13951396grad_reduc = self.get_gradient_for_reduction(param)1397# The allreduce buffer will be rewritten. Copy the gradients in partition to a new buffer1398new_grad_tensor = opguard_wrapped__new_grad_tensor_narrow(1350)(self.grads_in_partition.view(-1).narrow, 0, self.grads_in_partition_offset, param.numel())1399opguard_wrapped__new_grad_tensor_copy_(1351)(new_grad_tensor.copy_, grad_reduc.view(-1))1400grad_reduc.data = opguard_wrapped__data_view_as(1352)(new_grad_tensor.data.view_as, grad_reduc)1401#print(f"Grad norm after copy to contiguous_buffer {param.grad.data.norm()}")1402self.grads_in_partition_offset += param.numel()14031404def reduce_ipg_grads(self):1405if self.contiguous_gradients:1406if self.extra_large_param_to_reduce is not None:1407assert len(self.params_in_ipg_bucket) == 1, "more than 1 param in ipg bucket, this shouldn't happen"1408_, _, param_id = self.params_in_ipg_bucket[0]1409assert self.get_param_id(self.extra_large_param_to_reduce1410) == param_id, "param in ipg bucket does not match extra-large param"1411extra_large_grad_reduc = self.get_gradient_for_reduction(self.extra_large_param_to_reduce)1412self.average_tensor(extra_large_grad_reduc.view(-1))1413self.extra_large_param_to_reduce = None1414else:1415opguard_wrapped__narrow_average_tensor(1367)(self.average_tensor, self.ipg_buffer[self.ipg_index].narrow(0, 0, self.elements_in_ipg_bucket))1416else:1417self.buffered_reduce_fallback(None,1418self.grads_in_ipg_bucket,1419elements_per_buffer=self.elements_in_ipg_bucket)14201421if self.overlap_comm:1422stream = self.reduction_stream1423elif self.cpu_offload:1424# TODO: copy_grad_stream is disabled because of race with reduce. This hurts perf and should be fixed.1425# get_accelerator().synchronize()1426# stream = self.copy_grad_stream1427stream = get_accelerator().current_stream()1428else:1429stream = get_accelerator().current_stream()14301431with get_accelerator().stream(stream):1432for _, param, param_id in self.params_in_ipg_bucket:14331434assert self.params_already_reduced[param_id] == False, \1435f"The parameter {param_id} has already been reduced. \1436Gradient computed twice for this partition. \1437Multiple gradient reduction is currently not supported"14381439self.params_already_reduced[param_id] = True1440if self.partition_gradients:1441if not self.is_param_in_current_partition[param_id]:1442if self.overlap_comm and self.contiguous_gradients is False:1443# Clear grads of other partitions during the next reduction1444# to avoid clearing them before the reduction is complete.1445if self.previous_reduced_grads is None:1446self.previous_reduced_grads = []1447self.previous_reduced_grads.append(param)1448else:1449self.clear_grad_attribute(param)1450elif self.contiguous_gradients:1451self.copy_grads_in_partition(param)1452else: # zero stage 1 - partition only optimizer state1453if self.contiguous_gradients and self.is_param_in_current_partition[param_id]:1454self.copy_grads_in_partition(param)14551456self.grads_in_ipg_bucket = []1457self.params_in_ipg_bucket = []1458self.ipg_bucket_has_moe_params = False1459self.elements_in_ipg_bucket = 01460#####################################################################14611462def reduce_ready_partitions_and_remove_grads(self, param, i):1463if self.partition_gradients or self.is_gradient_accumulation_boundary:1464self.reduce_independent_p_g_buckets_and_remove_grads(param, i)14651466def zero_reduced_gradients(self, partition_id, i):14671468def are_all_related_partitions_reduced(params_id):1469for partition_id in self.param_to_partition_ids[i][params_id]:1470if not self.is_partition_reduced[i][partition_id]:1471return False1472return True14731474for params_id in self.is_grad_computed[i][partition_id]:1475if are_all_related_partitions_reduced(params_id):1476self.param_dict[params_id].grad = None # dead code14771478def flatten_and_print(self, message, tensors, start=0, n=5):1479flatten_tensor = self.flatten(tensors)14801481def print_func():1482logger.info(flatten_tensor.contiguous().view(-1).narrow(0, start, n))14831484self.sequential_execution(print_func, message)14851486def get_grads_to_reduce(self, i, partition_id):14871488def get_reducible_portion(key):1489grad = self.param_dict[key].grad1490total_elements = grad.numel()1491start = self.grad_start_offset[i][partition_id][key]1492num_elements = min(total_elements - start,1493self.partition_size[i] - self.grad_partition_insertion_offset[i][partition_id][key])1494if not pg_correctness_test:1495if num_elements == total_elements:1496return grad1497else:1498return grad.contiguous().view(-1).narrow(0, int(start), int(num_elements))1499else:1500if num_elements == total_elements:1501return grad.clone()1502else:1503return grad.clone().contiguous().view(-1).narrow(0, int(start), int(num_elements))15041505grads_to_reduce = []1506for key in self.is_grad_computed[i][partition_id]:1507grad = get_reducible_portion(key)1508grads_to_reduce.append(grad)1509return grads_to_reduce15101511def sequential_execution(self, function, message, group=None):1512if group is None:1513group = self.dp_process_group1514if dist.get_rank(group=group) == 0:1515logger.info(message)1516for id in range(dist.get_world_size(group=group)):1517if id == dist.get_rank(group=group):1518function()1519dist.barrier(group=group)15201521def set_none_gradients_to_zero(self, i, partition_id):1522for param_id in self.is_grad_computed[i][partition_id]:1523param = self.param_dict[param_id]1524if param.grad is None:1525param.grad = torch.zero_like(param)15261527######################Reduction Related Methods##############################1528def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_group=None):1529tensor = opguard_wrapped__tensor_flatten(1481)(self.flatten, bucket)15301531process_group = self.dp_process_group if process_group is None else process_group15321533tensor_to_allreduce = tensor15341535if pg_correctness_test or self.sequence_parallel_size > 1:1536communication_data_type = torch.float321537else:1538communication_data_type = self.communication_data_type15391540if communication_data_type != tensor.dtype:1541tensor_to_allreduce = tensor.to(communication_data_type)15421543if divide:1544tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size))15451546if rank is None:1547# "All Reducing"1548dist.all_reduce(tensor_to_allreduce, group=process_group)1549else:1550global_rank = dist.get_global_rank(process_group, rank)1551dist.reduce(tensor_to_allreduce, global_rank, group=process_group)15521553if communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce:1554if rank is None or rank == dist.get_rank(group=process_group):1555tensor.copy_(tensor_to_allreduce)15561557return tensor15581559def _clear_previous_reduced_grads(self):1560if self.previous_reduced_grads is not None:1561for param in self.previous_reduced_grads:1562self.clear_grad_attribute(param)1563self.previous_reduced_grads = None15641565# if rank is specified do a reduction instead of an allreduce1566def allreduce_and_copy(self, small_bucket, rank=None, log=None, divide=True, process_group=None):1567process_group = self.dp_process_group if process_group is None else process_group1568if self.overlap_comm:1569if not get_accelerator().resolves_data_dependency():1570get_accelerator().synchronize()1571# It is safe to clear the previously reduced grads of other partitions1572self._clear_previous_reduced_grads()1573stream = self.reduction_stream1574else:1575stream = get_accelerator().current_stream()15761577with get_accelerator().stream(stream):1578allreduced = self.allreduce_bucket(1579small_bucket,1580rank=rank,1581log=log,1582divide=divide,1583process_group=process_group,1584)1585if rank is None or rank == dist.get_rank(group=self.dp_process_group):1586for buf, synced in zip(small_bucket, opguard_wrapped__allreduced_unflatten(1538)(self.unflatten, allreduced, small_bucket)):1587opguard_wrapped__buf_copy_(1539)(buf.copy_, synced)15881589def allreduce_no_retain(1590self,1591bucket,1592numel_per_bucket=500000000,1593rank=None,1594log=None,1595divide=True,1596process_group=None,1597):1598small_bucket = []1599numel = 01600for tensor in bucket:1601small_bucket.append(tensor)1602numel = numel + tensor.numel()1603if numel > numel_per_bucket:1604self.allreduce_and_copy(small_bucket, rank=rank, log=None, divide=divide, process_group=process_group)1605small_bucket = []1606numel = 016071608if len(small_bucket) > 0:1609self.allreduce_and_copy(small_bucket, rank=rank, log=log, divide=divide, process_group=process_group)16101611# allows using reduction of gradients instead of using all_reduce16121613def buffered_reduce_fallback(self, rank, grads, elements_per_buffer=500000000, log=None):1614split_buckets = split_half_float_double(grads)16151616for i, bucket in enumerate(split_buckets):1617self.allreduce_no_retain(bucket, numel_per_bucket=elements_per_buffer, rank=rank, log=log)16181619#############################################################################1620#############################################################################1621#############################################################################16221623# views the tensor as multiple partitions and returns1624# those partitions1625def get_data_parallel_partitions(self, tensor, group_id):1626partitions = []16271628dp = dist.get_world_size(group=self.real_dp_process_group[group_id])1629# dp_id = dist.get_rank(group=self.real_dp_process_group[group_id])16301631total_num_elements = tensor.numel()16321633base_size = total_num_elements // dp1634remaining = total_num_elements % dp16351636start = 01637for id in range(dp):1638partition_size = base_size1639if id < remaining:1640partition_size = partition_size + 11641opguard_wrapped__partitions_append(1593)(partitions.append, tensor.narrow(0, start, partition_size))1642start = start + partition_size1643return partitions16441645def get_partition_info(self, tensor_list, partition_size, partition_id):1646params_in_partition = []1647params_not_in_partition = []16481649start_index = partition_size * partition_id1650end_index = partition_size * (partition_id + 1)16511652current_index = 01653first_offset = 016541655for tensor in tensor_list:16561657tensor_size = tensor.numel()16581659if start_index <= current_index < end_index:1660params_in_partition.append(tensor)16611662elif current_index < start_index < (current_index + tensor_size):1663params_in_partition.append(tensor)16641665assert (first_offset == 01666), "This can happen either zero or only once as this must be the first tensor in the partition"1667first_offset = start_index - current_index16681669else:1670params_not_in_partition.append(tensor)16711672current_index = current_index + tensor_size16731674return params_in_partition, params_not_in_partition, first_offset16751676def zero_grad(self, set_to_none=True):1677"""1678Zero FP16 parameter grads.1679"""1680# FP32 grad should never exist.1681# For speed, set model fp16 grad to None by default1682# zero all pointers to grad tensors1683for group in self.bit16_groups:1684for p in group:1685if set_to_none:1686p.grad = None # epilogue and in step1687p.grad_accum = None1688else:1689if p.grad is not None:1690p.grad.detach_()1691p.grad.zero_()16921693def _model_parallel_all_reduce(self, tensor, op):1694""" Perform all reduce within model parallel group, if any.1695"""1696if self.model_parallel_group is None or self.model_parallel_world_size == 1:1697pass1698else:1699dist.all_reduce(tensor=tensor, op=op, group=self.model_parallel_group)17001701def get_grad_norm_direct(self, gradients, params, norm_type=2):1702"""Clips gradient norm of an iterable of parameters.17031704This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and1705added functionality to handle model parallel parameters. Note that1706the gradients are modified in place.17071708Arguments:1709parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a1710single Tensor that will have gradients normalized1711max_norm (float or int): max norm of the gradients1712norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for1713infinity norm.17141715Returns:1716Total norm of the parameters (viewed as a single vector).1717"""1718norm_type = float(norm_type)1719all_norms = []1720if norm_type == inf:1721for g in gradients:1722all_norms.append(g.data.abs().max().float())1723total_norm = torch.stack(all_norms).max()1724dist.all_reduce(total_norm, op=dist.ReduceOp.MAX, group=self.dp_process_group)17251726# Take max across all GPUs.1727self._model_parallel_all_reduce(tensor=total_norm, op=dist.ReduceOp.MAX)1728else:1729# if dist.get_rank() == 0:1730# logger.info(f"Total Norm beginning {total_norm}")1731for g, p in zip(gradients, params):1732# Pipeline parallelism may replicate parameters. Avoid multi-counting.1733if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated:1734continue1735if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0):1736opguard_wrapped__all_norms_append(1688)(1737all_norms.append, torch.norm(g.data.double().detach(), norm_type).to(get_accelerator().current_device_name()))1738if len(all_norms) > 0:1739total_norm = opguard_wrapped__total_norm_float(1691)(torch.stack(all_norms).square().sum().float)1740else:1741total_norm = torch.tensor(0.0, dtype=torch.float32).to(self.device)1742# Sum across all model parallel Device.1743dist.all_reduce(total_norm, op=dist.ReduceOp.SUM, group=self.dp_process_group)17441745self._model_parallel_all_reduce(tensor=total_norm, op=dist.ReduceOp.SUM)17461747total_norm = opguard_wrapped__total_norm_pow(1699)(total_norm.pow, 1. / norm_type)17481749norm_is_inf = opguard_wrapped__norm_is_inf_isinf(1701)(total_norm.isinf)1750norm_is_nan = opguard_wrapped__norm_is_nan_isnan(1702)(total_norm.isnan)1751inf_or_nan = opguard_wrapped__inf_or_nan_logical_or(1703)(norm_is_nan.logical_or, norm_is_inf)17521753err = opguard_wrapped__err_tensor(1705)(torch.tensor, -1.0, device=self.device, dtype=torch.float)1754total_norm = inf_or_nan * err + opguard_wrapped__total_norm_logical_not(1706)(inf_or_nan.logical_not) * total_norm1755return total_norm17561757# creates a flat fused tensor from the tensor list starting at the first_offset1758# in the first tensor of the list. If there are not enough elements in the tensor1759# list then the flat tensor will be padded with zeros1760def get_flat_partition(self, tensor_list, first_offset, partition_size, dtype, device, return_tensor_list=False):1761flat_tensor_list = []1762current_size = 017631764for i, tensor in enumerate(tensor_list):1765grad_accum = self.get_param_gradient_attribute(tensor)1766if grad_accum is None:1767grad_accum = torch.zeros_like(tensor, dtype=dtype)17681769tensor = grad_accum1770num_elements = tensor.numel()1771tensor_offset = 017721773# we need to offset to get to the right element1774if i == 0 and first_offset > 0:1775tensor_offset = first_offset1776num_elements = num_elements - tensor_offset17771778# we dont need all elements of the tensor1779if num_elements > (partition_size - current_size):1780num_elements = partition_size - current_size17811782# we need a narrow view of the tensor based on the tensor offset and number of elements that1783# we need from this tensor1784if tensor_offset > 0 or num_elements < tensor.numel():1785flat_tensor_list.append(tensor.contiguous().view(-1).narrow(0, int(tensor_offset), int(num_elements)))1786else:1787flat_tensor_list.append(tensor)17881789current_size = current_size + num_elements17901791# this means its the last partition and does not align with the dp boundary. We need to pad before flattening1792if current_size < partition_size:1793flat_tensor_list.append(torch.zeros(int(partition_size - current_size), dtype=dtype, device=device))17941795if return_tensor_list:1796return flat_tensor_list17971798return self.flatten(flat_tensor_list)17991800def free_grad_in_param_list(self, param_list):1801for p in param_list:1802p.grad = None # in step1803p.grad_accum = None18041805def reset_cpu_buffers(self):1806self.norm_for_param_grads = {}1807self.local_overflow = False18081809def set_lr(self, lr):1810"""Set the learning rate."""1811for param_group in self.optimizer.param_groups:1812param_group["lr"] = lr18131814def get_lr(self):1815"""Return the current learning rate."""1816return self.optimizer.param_groups[0]["lr"]18171818def override_loss_scale(self, loss_scale):1819if loss_scale != self.external_loss_scale:1820logger.info(f'[deepspeed] setting loss scale from {self.external_loss_scale} -> {loss_scale}')1821self.custom_loss_scaler = True1822self.external_loss_scale = loss_scale18231824def scaled_global_norm(self, norm_type=2):1825assert norm_type == 2, "only L2 norm supported"1826norm_groups = []1827for i, group in enumerate(self.bit16_groups):1828if self.cpu_offload:1829# complete complete_grad_norm_calculation_for_cpu_offload return python float, moving back to1830# torch.tensor as else statement returns tensor as well1831norm = torch.tensor(self.complete_grad_norm_calculation_for_cpu_offload(self.params_in_partition[i]),1832device=self.device)1833norm_groups.append(norm)1834else:1835norm_groups.append(self.get_grad_norm_direct(self.averaged_gradients[i], self.params_in_partition[i]))18361837if self.has_moe_layers:1838self._average_expert_grad_norms(norm_groups)18391840# calculating L2 norm1841return opguard_wrapped__stack_norm(1793)(torch.norm, torch.stack(norm_groups), p=norm_type)18421843def get_bit16_param_group(self, group_no):1844bit16_partitions = self.parallel_partitioned_bit16_groups[group_no]1845partition_id = dist.get_rank(group=self.real_dp_process_group[group_no])1846return [bit16_partitions[dist.get_rank(group=self.real_dp_process_group[group_no])]]18471848def _optimizer_step(self, group_no):1849original_param_groups = self.optimizer.param_groups1850self.optimizer.param_groups = [original_param_groups[group_no]]1851# Disabling this as the C++ side copy & synchronize is not working correctly1852#from deepspeed.ops.adam import DeepSpeedCPUAdam1853#if type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half:1854# self.optimizer.step(fp16_param_groups=[self.get_bit16_param_group(group_no)])1855#else:1856# self.optimizer.step()1857self.optimizer.step()1858self.optimizer.param_groups = original_param_groups18591860# We need to link optimizer state after the first step() call1861self._lazy_init_hp_params_optimizer_state()18621863def step(self, closure=None):1864"""1865Not supporting closure.1866"""1867self.micro_step_id = -118681869see_memory_usage(f"In step before checking overflow")18701871# First compute norm for all group so we know if there is overflow1872if self.dtype == torch.float16:1873self.check_overflow()18741875prev_scale = self.loss_scale1876self._update_scale(self.overflow)1877if self.overflow:1878see_memory_usage('After overflow before clearing gradients')1879self.zero_grad(set_to_none=True)1880if self.cpu_offload:1881self.reset_cpu_buffers()1882else:1883self.averaged_gradients = {}18841885see_memory_usage('After overflow after clearing gradients')18861887for timer in OPTIMIZER_TIMERS:1888self.timers(timer).start()1889self.timers(timer).stop()1890return18911892# Step 1:- Calculate gradient norm using bit-16 grads1893see_memory_usage('Before norm calculation')1894scaled_global_grad_norm = self.scaled_global_norm()1895self._global_grad_norm = opguard_wrapped_scaled_global_grad_norm_truediv_prev_scale(1847)(operator.truediv, scaled_global_grad_norm, prev_scale)1896see_memory_usage('After norm before optimizer')18971898# Step 2:- run optimizer and upscaling simultaneously1899for i, group in enumerate(self.bit16_groups):1900self.timers(OPTIMIZER_GRADIENTS_TIMER).start()1901partition_id = dist.get_rank(group=self.real_dp_process_group[i])1902if self.cpu_offload:1903single_grad_partition = self.single_partition_of_fp32_groups[i].grad1904self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm)19051906self.timers(OPTIMIZER_GRADIENTS_TIMER).stop()1907self.timers(OPTIMIZER_STEP_TIMER).start()1908self._optimizer_step(i)19091910# Disabled, this is not currently working1911#from deepspeed.ops.adam import DeepSpeedCPUAdam1912#if not (type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half):1913# bit16_partitions = self.parallel_partitioned_bit16_groups[i]1914# fp32_partition = self.single_partition_of_fp32_groups[i]1915# bit16_partitions[partition_id].data.copy_(fp32_partition.data)1916bit16_partitions = self.parallel_partitioned_bit16_groups[i]1917fp32_partition = self.single_partition_of_fp32_groups[i]1918opguard_wrapped__data_copy_(1870)(1919bit16_partitions[partition_id].data.copy_, fp32_partition.to(get_accelerator().current_device_name()).data)19201921self.timers(OPTIMIZER_STEP_TIMER).stop()1922else:1923# free gradients for all the parameters that are not updated by this process(ZeRO stage2)1924self.free_grad_in_param_list(self.params_not_in_partition[i])19251926# create a flat gradients for parameters updated by this process1927# If we are last partition, ensure we have same size grads and partition size, if not pad with zero tensors1928if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1:1929single_grad_partition = opguard_wrapped__single_grad_partition_to(1881)(self.flatten_dense_tensors_aligned(1930self.averaged_gradients[i],1931int(self.partition_size[i])).to, self.single_partition_of_fp32_groups[i].dtype)1932else:1933single_grad_partition = opguard_wrapped__single_grad_partition_to(1885)(1934self.flatten(self.averaged_gradients[i]).to, self.single_partition_of_fp32_groups[i].dtype)1935assert single_grad_partition.numel() == self.partition_size[i], \1936"averaged gradients have different number of elements that partition size {} {} {} {}".format(1937single_grad_partition.numel(), self.partition_size[i], i, partition_id)19381939self.single_partition_of_fp32_groups[i].grad = single_grad_partition1940# release all the gradient since we have already created a necessary copy in dp_grad_partition(ZeRO stage2)1941self.free_grad_in_param_list(self.params_in_partition[i])19421943self.averaged_gradients[i] = None19441945self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm)19461947self.timers(OPTIMIZER_GRADIENTS_TIMER).stop()19481949# Step 3:- run the optimizer if no offloading1950self.timers(OPTIMIZER_STEP_TIMER).start()1951self._optimizer_step(i)1952# Step 4:- get rid of the fp32 gradients. Not needed anymore1953self.single_partition_of_fp32_groups[i].grad = None1954del single_grad_partition1955bit16_partitions = self.parallel_partitioned_bit16_groups[i]1956fp32_partition = self.single_partition_of_fp32_groups[i]1957opguard_wrapped__data_copy_(1909)(bit16_partitions[partition_id].data.copy_, fp32_partition.data)1958self.timers(OPTIMIZER_STEP_TIMER).stop()19591960see_memory_usage('After optimizer before all-gather')1961if self.cpu_offload:1962self.reset_cpu_buffers()19631964self.timers(OPTIMIZER_ALLGATHER_TIMER).start()1965# Gather the updated weights from everyone.1966# Then all partitions of the model parameters are updated and ready for next round forward.1967all_gather_dp_groups(groups_flat=self.bit16_groups_flat,1968partitioned_param_groups=self.parallel_partitioned_bit16_groups,1969dp_process_group=self.real_dp_process_group,1970start_alignment_factor=self.nccl_start_alignment_factor,1971allgather_bucket_size=self.allgather_bucket_size)1972self.timers(OPTIMIZER_ALLGATHER_TIMER).stop()19731974# TODO: we probably don't need this? just to be safe1975for i in range(len(self.bit16_groups)):1976self._update_model_bit16_weights(i)19771978self.timers.log(OPTIMIZER_TIMERS)1979see_memory_usage('After zero_optimizer step')19801981return19821983@torch.no_grad()1984def update_lp_params(self):1985for i, (bit16_partitions, fp32_partition) in enumerate(1986zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)):1987partition_id = dist.get_rank(group=self.real_dp_process_group[i])1988opguard_wrapped__data_copy_(1940)(bit16_partitions[partition_id].data.copy_, fp32_partition.data)1989# print_rank_0(f'update_lp_params {i=} {partition_id=}', force=True)1990# if i == 0:1991# print_rank_0(f'{fp32_partition[:10]=}', force=True)1992all_gather_dp_groups(groups_flat=self.bit16_groups_flat,1993partitioned_param_groups=self.parallel_partitioned_bit16_groups,1994dp_process_group=self.real_dp_process_group,1995start_alignment_factor=self.nccl_start_alignment_factor,1996allgather_bucket_size=self.allgather_bucket_size)19971998def _average_expert_grad_norms(self, norm_groups):1999for i, norm in enumerate(norm_groups):2000if self.is_moe_param_group[i]:2001scaled_norm_tensor = norm * 1.0 / dist.get_world_size(group=self.real_dp_process_group[i])2002if self.device == 'cpu':2003scaled_norm_tensor = scaled_norm_tensor.to(get_accelerator().current_device_name())2004dist.all_reduce(scaled_norm_tensor, group=self.real_dp_process_group[i])2005norm_groups[i] = scaled_norm_tensor.to(self.device)20062007def unscale_and_clip_grads(self, grad_groups_flat, total_norm):2008# compute combined scale factor for this group2009combined_scale = self.loss_scale2010if self.clip_grad > 0.:2011# norm is in fact norm*scale2012clip = opguard_wrapped_total_norm_truediv_clip_grad(1964)(operator.truediv, ((total_norm / self.loss_scale) + 1e-6), self.clip_grad)2013if clip > 1:2014combined_scale = clip * self.loss_scale20152016for grad in grad_groups_flat:2017if isinstance(grad, list):2018sub_partitions = grad2019for g in sub_partitions:2020g.data.mul_(1. / combined_scale)2021else:2022opguard_wrapped__data_mul_(1974)(grad.data.mul_, 1. / combined_scale)20232024def _check_overflow(self, partition_gradients=True):2025self.overflow = self.has_overflow(partition_gradients)20262027# `params` is a list / generator of torch.Variable2028def has_overflow_serial(self, params):2029invalid_grad_count = opguard_wrapped__invalid_grad_count_zeros(1981)(torch.zeros, [1], dtype=torch.float, device=get_accelerator().current_device_name())2030for p in params:2031if p.grad is not None:2032invalid_grad_count += opguard_wrapped__invalid_grad_count__has_inf_or_nan(1984)(self._has_inf_or_nan, p.grad)2033return opguard_wrapped__invalid_grad_count_bool(1985)(invalid_grad_count.bool)20342035def has_overflow_partitioned_grads_serial(self):2036invalid_grad_count = opguard_wrapped__invalid_grad_count_zeros(1988)(torch.zeros, [1], dtype=torch.float, device=get_accelerator().current_device_name())2037for i in range(len(self.bit16_groups)):2038for j, grad in enumerate(self.averaged_gradients[i]):2039if grad is not None:2040invalid_grad_count += opguard_wrapped__invalid_grad_count__has_inf_or_nan(1992)(self._has_inf_or_nan, grad)2041return opguard_wrapped__invalid_grad_count_bool(1993)(invalid_grad_count.bool)20422043def has_overflow(self, partition_gradients=True):2044if partition_gradients:2045overflow = self.local_overflow if self.cpu_offload else self.has_overflow_partitioned_grads_serial()2046overflow_gpu = get_accelerator().ByteTensor([overflow]) if self.cpu_offload else opguard_wrapped__overflow_gpu_to(1998)(2047overflow.byte().to, get_accelerator().current_device_name())2048'''This will capture overflow across all data parallel and expert parallel process2049Since expert parallel process are a subset of data parallel process'''2050dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.dp_process_group)20512052else:2053params = []2054for group in self.bit16_groups:2055for param in group:2056params.append(param)2057overflow_gpu = opguard_wrapped__overflow_gpu_to(2009)(self.has_overflow_serial(params).byte().to, get_accelerator().current_device_name())20582059# Since each model parallel GPU carries only part of the model,2060# make sure overflow flag is synced across all the model parallel GPUs2061self._model_parallel_all_reduce(tensor=overflow_gpu, op=dist.ReduceOp.MAX)20622063overflow = opguard_wrapped__overflow_item(2015)(overflow_gpu[0].item)2064return bool(overflow)20652066# `x` is a torch.Tensor2067@staticmethod2068def _has_inf_or_nan(x, j=None):2069float_x = opguard_wrapped__float_x_float(2021)(x.float)2070nan = opguard_wrapped__nan_isnan(2022)(float_x.isnan)2071inf = opguard_wrapped__inf_isinf(2023)(float_x.isinf)2072inf_or_nan = opguard_wrapped__inf_or_nan_logical_or(2024)(nan.logical_or, inf)2073return opguard_wrapped__float_max(2025)(inf_or_nan.float().max)20742075def backward(self, loss, retain_graph=False):2076"""2077:attr:`backward` performs the following steps:207820791. fp32_loss = loss.float()20802. scaled_loss = fp32_loss*loss_scale20813. scaled_loss.backward(), which accumulates scaled gradients into the ``.grad`` attributes of the model's fp16 leaves2082"""2083self.micro_step_id += 120842085if self.contiguous_gradients:2086self.ipg_buffer = []2087buf_0 = opguard_wrapped__buf_0_empty(2039)(torch.empty, int(self.reduce_bucket_size),2088dtype=self.dtype,2089device=get_accelerator().current_device_name())2090self.ipg_buffer.append(buf_0)20912092# Use double buffers to avoid data access conflict when overlap_comm is enabled.2093if self.overlap_comm:2094buf_1 = opguard_wrapped__buf_1_empty(2046)(torch.empty, int(self.reduce_bucket_size),2095dtype=self.dtype,2096device=get_accelerator().current_device_name())2097self.ipg_buffer.append(buf_1)2098self.ipg_index = 020992100if self.custom_loss_scaler:2101scaled_loss = self.external_loss_scale * loss2102scaled_loss.backward()2103else:2104self.loss_scaler.backward(loss.float(), retain_graph=retain_graph)21052106# Only for Stage 1, Mode 22107if self.use_grad_accum_attribute:2108self.fill_grad_accum_attribute()21092110def check_overflow(self, partition_gradients=True):2111self._check_overflow(partition_gradients)21122113def _update_scale(self, has_overflow=False):2114self.loss_scaler.update_scale(has_overflow)21152116# Promote state so it can be retrieved or set via "fp16_optimizer_instance.state"2117def _get_state(self):2118return self.optimizer.state21192120def _set_state(self, value):2121self.optimizer.state = value21222123state = property(_get_state, _set_state)21242125# Promote param_groups so it can be retrieved or set via "fp16_optimizer_instance.param_groups"2126# (for example, to adjust the learning rate)2127def _get_param_groups(self):2128return self.optimizer.param_groups21292130def _set_param_groups(self, value):2131self.optimizer.param_groups = value21322133param_groups = property(_get_param_groups, _set_param_groups)21342135# Promote loss scale so it can be retrieved or set via "fp16_optimizer_instance.loss_scale"2136def _get_loss_scale(self):2137if self.custom_loss_scaler:2138return self.external_loss_scale2139else:2140return self.loss_scaler.cur_scale21412142def _set_loss_scale(self, value):2143self.loss_scaler.cur_scale = value21442145loss_scale = property(_get_loss_scale, _set_loss_scale)2146cur_scale = property(_get_loss_scale, _set_loss_scale)21472148# Return group tensor after removing paddings that are added for alignment to DP world size.2149# This method works on the assumption that each group contains a single flattened tensor.2150def _get_groups_without_padding(self, groups_with_padding):2151groups_without_padding = []2152for i, group in enumerate(groups_with_padding):2153lean_length = group.numel() - self.groups_padding[i]2154groups_without_padding.append(group[:lean_length])21552156return groups_without_padding21572158# Return optimizer state after removing paddings that are added for alignment.2159def _get_state_without_padding(self, state_with_padding, padding):2160lean_state = {}2161for key, value in state_with_padding.items():2162if torch.is_tensor(value):2163lean_length = value.numel() - padding2164lean_state[key] = value[:lean_length]2165else:2166lean_state[key] = value21672168return lean_state21692170# Return base optimizer states.2171# This method assumes that each param group contains a single flattened tensor.2172def _get_base_optimizer_state(self):2173optimizer_groups_state = []2174for i, group in enumerate(self.optimizer.param_groups):2175p = group['params'][0]2176lean_optimizer_state = self._get_state_without_padding(self.optimizer.state[p], self.groups_padding[i])2177optimizer_groups_state.append(lean_optimizer_state)21782179return optimizer_groups_state21802181def state_dict(self):2182"""2183Returns a dict containing the current state of this :class:`FP16_Optimizer` instance.2184This dict contains attributes of :class:`FP16_Optimizer`, as well as the state_dict2185of the contained Pytorch optimizer.2186Example::2187checkpoint = {}2188checkpoint['model'] = model.state_dict()2189checkpoint['optimizer'] = optimizer.state_dict()2190torch.save(checkpoint, "saved.pth")2191"""2192state_dict = {}2193state_dict[LOSS_SCALER] = self.loss_scaler2194state_dict['dynamic_loss_scale'] = self.dynamic_loss_scale2195state_dict['overflow'] = self.overflow2196state_dict[CLIP_GRAD] = self.clip_grad21972198if self.elastic_checkpoint:2199state_dict[BASE_OPTIMIZER_STATE] = self._get_base_optimizer_state()22002201if "step" in self.optimizer.param_groups[0]:2202# Assuming "step" is the only item that changes through training iterations2203assert all(group["step"] == self.optimizer.param_groups[0]["step"]2204for group in self.optimizer.param_groups), "All param groups must have the same step value"2205state_dict[BASE_OPTIMIZER_STATE_STEP] = self.optimizer.param_groups[0]["step"]2206else:2207state_dict[BASE_OPTIMIZER_STATE] = self.optimizer.state_dict()22082209# Remove paddings for DP alignment to enable loading for other alignment values2210fp32_groups_without_padding = self._get_groups_without_padding(self.single_partition_of_fp32_groups)2211state_dict[SINGLE_PARTITION_OF_FP32_GROUPS] = fp32_groups_without_padding22122213state_dict[2214ZERO_STAGE] = ZeroStageEnum.gradients if self.partition_gradients else ZeroStageEnum.optimizer_states2215state_dict[GROUP_PADDINGS] = self.groups_padding2216state_dict[PARTITION_COUNT] = self.partition_count22172218state_dict[DS_VERSION] = version2219state_dict[PARAM_SLICE_MAPPINGS] = self._param_slice_mappings22202221return state_dict22222223# Restore base optimizer fp32 weights from elastic checkpoint by:2224# 1) Merging fp32 weights from checkpoints of all partitions2225# 2) Extracting fp32 weights for current partition from merged weights2226# 3) Using extracted weights to update base optimizer weights directly.2227def _restore_from_elastic_fp32_weights(self, all_state_dict):2228merged_single_partition_of_fp32_groups = []22292230for i in range(len(self.single_partition_of_fp32_groups)):2231partition_id = dist.get_rank(group=self.real_dp_process_group[i])2232merged_partitions = [sd[SINGLE_PARTITION_OF_FP32_GROUPS][i] for sd in all_state_dict]2233if self.is_moe_group(self.optimizer.param_groups[i]):2234ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name'])2235merged_partitions = [merged_partitions[i] for i in ranks]2236flat_merged_partitions = self.flatten_dense_tensors_aligned(2237merged_partitions,2238self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i]))2239dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, i)2240merged_single_partition_of_fp32_groups.append(dp_partitions[partition_id])22412242for current, saved in zip(self.single_partition_of_fp32_groups, merged_single_partition_of_fp32_groups):2243current.data.copy_(saved.data)22442245# Restore base optimizer fp32 weights from ZeRO fp16 or bfloat16 weights2246def _restore_from_bit16_weights(self):2247for group_id, (bit16_partitions, fp32_partition) in enumerate(2248zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)):2249partition_id = dist.get_rank(group=self.real_dp_process_group[group_id])2250fp32_partition.data.copy_(bit16_partitions[partition_id].data)22512252# Refresh the fp32 master params from the fp16 or bfloat16 copies.2253def refresh_fp32_params(self):2254self._restore_from_bit16_weights()22552256# Extract optimizer state for current partition from merged states of all partitions2257def _partition_base_optimizer_state(self, state_key, all_partition_states, group_id):2258partition_id = dist.get_rank(group=self.real_dp_process_group[group_id])2259alignment = self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[group_id])2260if torch.is_tensor(all_partition_states[0]):2261flat_merged_partitions = self.flatten_dense_tensors_aligned(all_partition_states, alignment)2262dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, group_id)2263return dp_partitions[partition_id]2264else:2265# Assume non-tensor states are not partitioned and equal across ranks, so return first one2266return all_partition_states[0]22672268def _restore_step_from_elastic_checkpoint(self, all_state_dict):2269assert BASE_OPTIMIZER_STATE_STEP in all_state_dict[0]2270assert all(sd[BASE_OPTIMIZER_STATE_STEP] == all_state_dict[0][BASE_OPTIMIZER_STATE_STEP]2271for sd in all_state_dict), "State dicts of all partitions must have the same step value"2272return all_state_dict[0][BASE_OPTIMIZER_STATE_STEP]22732274def _restore_base_optimizer_state(self, base_optimizer_group_states, base_optimizer_state_step, group_paddings):2275if type(base_optimizer_group_states) == dict:2276base_optimizer_group_states = base_optimizer_group_states['state']22772278saved_keys = base_optimizer_group_states[0].keys()22792280for i, group in enumerate(self.optimizer.param_groups):2281p = group['params'][0]2282padding = 0 if group_paddings is None else group_paddings[i]2283for key in saved_keys:2284saved = base_optimizer_group_states[i][key]22852286if torch.is_tensor(saved):2287if key in self.optimizer.state[p]:2288dst_tensor = self.optimizer.state[p][key]2289src_tensor = _get_padded_tensor(saved, dst_tensor.numel())2290self.optimizer.state[p][key].data.copy_(src_tensor.data)2291else:2292self.optimizer.state[p][key] = _pad_tensor_by_size(2293saved, padding, torch.float32,2294torch.device('cpu') if self.cpu_offload else self.device)2295else:2296self.optimizer.state[p][key] = saved22972298for param_group in self.optimizer.param_groups:2299param_group['step'] = base_optimizer_state_step23002301def get_ep_ranks(self, rank=0, group_name=None):2302from deepspeed.utils import groups2303expert_parallel_size_ = groups._get_expert_parallel_world_size(group_name)2304world_size = groups._get_data_parallel_world_size()2305rank = groups._get_expert_parallel_rank(group_name)2306ranks = range(rank, world_size, expert_parallel_size_)2307return list(ranks)23082309# Restore base optimizer state from elastic checkpoint by2310# 1) Merging optimizer state from checkpoints of all partitions2311# 2) Extracting optimizer state for current partition from the merged state2312# 3) Using the extracted value to directly update the base optimizer.2313def _restore_elastic_base_optimizer_state(self, all_state_dict):2314base_optimizer_group_states = []2315for i in range(len(self.optimizer.param_groups)):2316partition_states = {}2317all_partition_group_states = [sd[BASE_OPTIMIZER_STATE][i] for sd in all_state_dict]23182319if self.is_moe_group(self.optimizer.param_groups[i]):2320ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name'])2321all_partition_group_states = [all_partition_group_states[i] for i in ranks]23222323for key in all_partition_group_states[0].keys():2324all_partition_states = [all_states[key] for all_states in all_partition_group_states]2325partition_states[key] = self._partition_base_optimizer_state(key, all_partition_states, i)2326base_optimizer_group_states.append(partition_states)23272328self._restore_base_optimizer_state(base_optimizer_group_states,2329self._restore_step_from_elastic_checkpoint(all_state_dict), None)23302331def load_state_dict(self,2332state_dict_list,2333load_optimizer_states=True,2334load_from_fp32_weights=False,2335checkpoint_folder=None,2336load_serial=None):2337if checkpoint_folder:2338self._load_universal_checkpoint(checkpoint_folder, load_optimizer_states, load_from_fp32_weights)2339else:2340self._load_legacy_checkpoint(state_dict_list, load_optimizer_states, load_from_fp32_weights)23412342def _load_universal_checkpoint(self, checkpoint_folder, load_optimizer_states, load_from_fp32_weights):2343self.load_hp_checkpoint_state_from_checkpoint_dir("bit16_groups", checkpoint_folder)23442345@property2346def param_groups(self):2347"""Forward the wrapped optimizer's parameters."""2348return self.optimizer.param_groups23492350def _load_global_state(self, sd):2351self.loss_scaler = sd.get(LOSS_SCALER, self.loss_scaler)2352self.dynamic_loss_scale = sd.get('dynamic_loss_scale', self.dynamic_loss_scale)2353self.overflow = sd.get('overflow', self.overflow)2354self.clip_grad = sd.get(CLIP_GRAD, self.clip_grad)23552356ckpt_version = sd.get(DS_VERSION, False)2357assert ckpt_version, f"Empty ds_version in checkpoint, not clear how to proceed"2358ckpt_version = pkg_version.parse(ckpt_version)23592360# zero stage 1 mode2361if not self.partition_gradients:2362required_version = pkg_version.parse("0.3.17")2363error_str = f"ZeRO stage 1 changed in {required_version} and is not backwards compatible " \2364"with older stage 1 checkpoints. If you'd like to load an old ZeRO-1 checkpoint " \2365"please use an older version of DeepSpeed (<= 0.5.8) and set 'legacy_stage1': true in your zero config json."2366assert required_version <= ckpt_version, f"Old version: {ckpt_version} {error_str}"23672368def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, load_from_fp32_weights=False):2369r"""Loading ZeRO checkpoint23702371Arguments:2372state_dict_list: List of all saved ZeRO checkpoints, one for each saved partition.2373Note that the number of saved partitions may differ from number of loading partitions to support2374changing GPU count, specifically DP world size, between saving and loading checkpoints.2375load_optimizer_states: Boolean indicating whether or not to load base optimizer states2376load_from_fp32_weights: Boolean indicating whether to initialize fp32 master weights from fp322377copies in checkpoints (no precision loss) or from model's fp16 copies (with precision loss).2378"""2379"""2380Loads a state_dict created by an earlier call to state_dict().2381If ``fp16_optimizer_instance`` was constructed from some ``init_optimizer``,2382whose parameters in turn came from ``model``, it is expected that the user2383will call ``model.load_state_dict()`` before2384``fp16_optimizer_instance.load_state_dict()`` is called.2385Example::2386model = torch.nn.Linear(D_in, D_out).to(get_accelerator().device_name()).half()2387optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)2388optimizer = FP16_Optimizer(optimizer, static_loss_scale = 128.0)2389...2390checkpoint = torch.load("saved.pth")2391model.load_state_dict(checkpoint['model'])2392optimizer.load_state_dict(checkpoint['optimizer'])2393"""23942395# I think it should actually be ok to reload the optimizer before the model.2396dp_rank = dist.get_rank(group=self.dp_process_group)2397current_rank_sd = state_dict_list[dp_rank]2398self._load_global_state(current_rank_sd)23992400ckpt_is_rigid = isinstance(current_rank_sd[BASE_OPTIMIZER_STATE], dict)24012402# padding is always at the last rank/partition2403# if DP=1024 and param-group elems=16 -> padding will be 1024-16 across all but one rank2404# scenario-1 (shrink): saving w. 4 gpus -> loading w. 2 gpus2405# scenario-2 (expand): saving w. 2 gpus -> loading w. 4 gpus2406# if load_optimizer_states:2407# if new_dp_size:2408# self.strip_padding()2409# self.add_padding_w_new_dp_size()2410# self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE])24112412if load_optimizer_states:2413if ckpt_is_rigid:2414# loading rigid ckpt into either rigid or elastic exec2415self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE])2416else:2417if self.elastic_checkpoint:2418# loading elastic into elastic exec2419self._restore_elastic_base_optimizer_state(state_dict_list)2420else:2421# loading an elastic checkpoint into rigid exec2422self._restore_base_optimizer_state(current_rank_sd[BASE_OPTIMIZER_STATE],2423current_rank_sd[BASE_OPTIMIZER_STATE_STEP],2424current_rank_sd[GROUP_PADDINGS])24252426# At this point, the optimizer's references to the model's fp32 parameters are up to date.2427# The optimizer's hyperparameters and internal buffers are also up to date.2428# However, the fp32 master copies of the model's fp16 params stored by the optimizer are still2429# out of date. There are two options.2430# 1: Refresh the master params from the model's fp16 params.2431# This requires less storage but incurs precision loss.2432# 2: Save and restore the fp32 master copies separately.2433# We choose option 1 if changing DP degree and option 2 otherwise.2434#2435# Pytorch Optimizer.load_state_dict casts saved buffers (e.g. momentum) to the type and device2436# of their associated parameters, because it's possible those buffers might not exist yet in2437# the current optimizer instance. In our case, as long as the current FP16_Optimizer has been2438# constructed in the same way as the one whose state_dict we are loading, the same master params2439# are guaranteed to exist, so we can just copy_() from the saved master params.24402441if load_from_fp32_weights:2442# option 2 from above2443if self.elastic_checkpoint and not ckpt_is_rigid:2444self._restore_from_elastic_fp32_weights(state_dict_list)2445else:2446# For non-elastic checkpoint, simply copying from saved weights of current rank is sufficient.2447for current, saved in zip(self.single_partition_of_fp32_groups,2448current_rank_sd[SINGLE_PARTITION_OF_FP32_GROUPS]):2449src_tensor = _get_padded_tensor(saved, current.numel())2450current.data.copy_(src_tensor.data)2451else:2452# option 1 from above2453self._restore_from_bit16_weights()24542455if load_optimizer_states:2456self._link_all_hp_params()245724582459def _handle_overflow(cpu_sum, x, i):2460import math2461rank = dist.get_rank()2462if rank == 0:2463t_i = -12464for v_i, v in enumerate(x.data.contiguous().view(-1)):2465if not math.isfinite(float(v)):2466t_i = v_i2467break2468logger.info(f"rank {rank} detected overflow {cpu_sum} in tensor {i}:{t_i} shape {x.shape}")246924702471def estimate_zero2_model_states_mem_needs(total_params,2472num_gpus_per_node=1,2473num_nodes=1,2474cpu_offload=True,2475additional_buffer_factor=1.5):24762477total_gpus = num_nodes * num_gpus_per_node24782479if cpu_offload:2480gpu_mem = 2 * total_params2481cpu_mem = total_params * max(4 * total_gpus, 16) * additional_buffer_factor2482else:2483gpu_mem = 4 * total_params + int(16 * total_params / total_gpus)2484cpu_mem = total_params * 4 * num_gpus_per_node * additional_buffer_factor24852486return int(cpu_mem), int(gpu_mem)248724882489def model_to_params(model):2490# shared params calculated only once2491total_params = sum(dict((p.data_ptr(), p.numel()) for p in model.parameters()).values())2492return total_params249324942495def estimate_zero2_model_states_mem_needs_all_live(model,2496num_gpus_per_node=1,2497num_nodes=1,2498additional_buffer_factor=1.5):2499"""2500Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients2501for a given ``model`` and hardware setup.25022503If you have an actual model object, use this function and everything will be derived2504automatically.25052506If it's a hypothetical model, use ``estimate_zero2_model_states_mem_needs_all_cold`` where you have to pass2507the ``total_params`` explicitly.25082509Args:2510- ``model``: ``nn.Module`` object2511- ``num_gpus_per_node``: how many gpus per node (defaults to 1)2512- ``num_nodes``: how many nodes (defaults to 1),2513- ``additional_buffer_factor``: estimation factor (defaults to 1.5):25142515"""25162517total_params = model_to_params(model)25182519estimate_zero2_model_states_mem_needs_all_cold(total_params=total_params,2520num_gpus_per_node=num_gpus_per_node,2521num_nodes=num_nodes,2522additional_buffer_factor=additional_buffer_factor)252325242525def estimate_zero2_model_states_mem_needs_all_cold(total_params,2526num_gpus_per_node=1,2527num_nodes=1,2528additional_buffer_factor=1.5):2529"""2530Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients2531for a given ``model`` and hardware setup.25322533If it's a hypothetical model, use this function where you have to pass2534the ``total_params`` and ``largest_layer_params`` explicitly.25352536If you have an actual model object, use ``estimate_zero2_model_states_mem_needs_all_live`` and everything2537will be derived automatically.25382539Args:2540- ``total_params``: total model params2541- ``num_gpus_per_node``: how many gpus per node (defaults to 1)2542- ``num_nodes``: how many nodes (defaults to 1),2543- ``additional_buffer_factor``: estimation factor (defaults to 1.5):25442545"""25462547def format_options(cpu_offload):2548enabled = []2549device = f'{OffloadDeviceEnum.cpu:4}' if cpu_offload else "none"2550enabled.append(f"offload_optimizer={device}")2551return ", ".join(enabled)25522553nodes_str = "nodes" if num_nodes > 1 else "node"2554gpus_str = "GPUs" if num_gpus_per_node > 1 else "GPU"2555print("Estimated memory needed for params, optim states and gradients for a:\n"2556f"HW: Setup with {num_nodes} {nodes_str}, {num_gpus_per_node} {gpus_str} per node.\n"2557f"SW: Model with {int(total_params/1e6)}M total params.")2558print(" per CPU | per GPU | Options")2559for cpu_offload in [True, False]:2560cpu_mem, gpu_mem = estimate_zero2_model_states_mem_needs(total_params=total_params,2561num_gpus_per_node=num_gpus_per_node,2562num_nodes=num_nodes,2563cpu_offload=cpu_offload,2564additional_buffer_factor=additional_buffer_factor)25652566options_str = format_options(cpu_offload=cpu_offload)2567print(f" {cpu_mem/2**30:7.2f}GB | {gpu_mem/2**30:6.2f}GB | {options_str}")