code: def _add_grad_hook(
      grad, *, rec_grad, rec_param_update, param_name, tensor
  ):
      """Helper function for adding gradient hooks"""
      # if the param is already there, the entry is from the previous training
      # step, thus start a new one
  
      # very first batch
      if not rec_grad:
          rec_grad.append({})
      if not rec_param_update:
          rec_param_update.append({})
  
      if param_name in rec_grad[-1]:
          rec_grad.append({})
      if param_name in rec_param_update[-1]:
          rec_param_update.append({})
  
      # We need to clone the grad here because otherwise, the recorded gradients
      # can be overridden by later gradients despite them being pulled to numpy!
      # This only happens on CPU, which suggests it's a strange bug that's perhaps
      # related to caching and/or memory-mapping.
      grad = grad.clone()
  
      rec_grad[-1][param_name] = to_numpy(grad)
  
      eps = 1e-9  # prevent divide by 0
      rec_param_update[-1][param_name] = (
          grad.std() / (eps + tensor.std())
      ).item()
  
imports: from collections.abc import Mapping
  from functools import partial
  import numpy
  from sklearn.exceptions import NotFittedError
  from sklearn.utils.validation import check_is_fitted
  import torch
  from skorch.exceptions import NotInitializedError
  from skorch.utils import to_numpy
label_desc: Use image node_id 4 for calling a global function with example usage: _add_grad_hook(grad) without return types