code: def perform(self, node, inputs, output_storage):
      x, dy, scale, x_mean, x_invstd, epsilon = inputs
      axes = self.axes
      if min(axes) < 0 or max(axes) >= x.ndim:
          raise ValueError(
              "axes should be less than ndim (<%d), but %s given"
              % (x.ndim, str(axes))
          )
  
      x_diff = x - x_mean
      mean_dy_x_diff = np.mean(dy * x_diff, axis=axes, keepdims=True)
      c = (dy * x_invstd) - (x_diff * mean_dy_x_diff * (x_invstd**3))
  
      g_wrt_inputs = scale * (c - np.mean(c, axis=axes, keepdims=True))
      g_wrt_scale = np.sum(
          dy * x_invstd * x_diff, axis=axes, keepdims=True
      )
      g_wrt_bias = np.sum(dy, axis=axes, keepdims=True)
  
      output_storage[0][0] = g_wrt_inputs
      output_storage[1][0] = g_wrt_scale
      output_storage[2][0] = g_wrt_bias
  
imports: from __future__ import absolute_import, print_function, division
  import numpy
  import theano
  from theano import Apply, Op
  from theano.gof import local_optimizer
  from theano.gof.opt import copy_stack_trace
  from theano.tensor import as_tensor_variable, TensorType
  from theano.tensor import basic
  from theano.tensor.opt import register_specialize_device
  from theano.scalar import Composite, as_common_dtype
  from theano.scalar import add, sub, true_div, mul
label_desc: Use image node_id 6 for calling the AbstractBatchNormTrainGrad obj's underlying member method code with example usage: obj.perform(node, inputs, output_storage) without return types