class_docstr: ShardedGradScaler helps perform gradient scaling in a shard aware manner. It extends
  functionality from GradScaler:
  * Supports Pytorch DDP and FSDP implementations
  * Support CPU offloaded tensors (as used in fully sharded data parallel[FSDP])
  * Supports the custom Mixed Precision loss dtype (fp16, bf16) that FSDP returns
  * Sync inf/nan for scaled gradient tensors on any torch.device (where tensors are placed) across
  nodes
  
  Example::
  
      # Creates a ShardedGradScaler once at the beginning of training.
      scaler = ShardedGradScaler()
  
      for epoch in epochs:
          for input, target in data:
              optimizer.zero_grad()
              output = model(input)
              loss = loss_fn(output, target)
  
              # Scales loss.  Calls backward() on scaled loss to create scaled gradients.
              scaler.scale(loss).backward()
  
              # scaler.step() first unscales gradients of the optimizer's params.
              # If gradients don't contain infs/NaNs, optimizer.step() is then called,
              # otherwise, optimizer.step() is skipped.
              scaler.step(optimizer)
  
              # Updates the scale for next iteration.
              scaler.update()
  
  See :class:`GradScaler` for explanation of scaling/unscaling and more use cases.
  
  Args:
      init_scale (float, optional, default=2.**16):  Initial scale factor.
      growth_factor (float, optional, default=2.0):  Factor by which the scale is multiplied during
          :meth:`update` if no inf/NaN gradients occur for ``growth_interval`` consecutive iterations.
      backoff_factor (float, optional, default=0.5):  Factor by which the scale is multiplied during
          :meth:`update` if inf/NaN gradients occur in an iteration.
      growth_interval (int, optional, default=2000):  Number of consecutive iterations without inf/NaN gradients
          that must occur for the scale to be multiplied by ``growth_factor``.
      enabled (bool, optional):  If ``False``, disables gradient scaling. :meth:`step` simply
          invokes the underlying ``optimizer.step()``, and other methods become no-ops.
          Default: ``True``
      process_group (ProcessGroup, optional, default=torch.distributed.group.WORLD):
          process group for sharding
code: def _foreach_non_finite_check_and_unscale_cpu_(
      self,
      grads: Sequence[torch.Tensor],
      found_inf: torch.Tensor,
      inv_scale: torch.Tensor,
  ) -> None:
      if len(grads) == 0:
          return
      assert (
          inv_scale.numel() == 1
      ), "inv_scale must be a 1-element tensor."
      assert (
          found_inf.numel() == 1
      ), "found_inf must be a 1-element tensor."
  
      for grad in grads:
          if grad.device.type != "cpu":
              log.error(
                  "tensor device is %s but was expected to be ``cpu``",
                  grad.device,
              )
              raise ValueError(
                  "Gradients were found on a non-CPU device when"
                  " expected to be on CPU."
              )
          if (
              torch.isinf(grad).any().item() is True
              or torch.isnan(grad).any().item() is True
          ):
              found_inf.data = torch.tensor([1.0])
              break
          else:
              grad.data *= inv_scale.item()
  
imports: import logging
  from collections import abc, defaultdict
  from typing import Any, Dict, Iterable, List, Optional, overload, Sequence, Tuple, Union
  import torch
  import torch.distributed
  from torch.cuda.amp.grad_scaler import _MultiDeviceReplicator, GradScaler, OptState
  from torch.distributed.distributed_c10d import ProcessGroup
label_desc: Use image node_id 7 for calling the ShardedGradScaler obj's underlying member method code with example usage: obj._foreach_non_finite_check_and_unscale_cpu_(grads, found_inf, inv_scale) without return types