class_docstr: Multi-layer Perceptron.
  
  The `_forward` method propagates input forward through the network and stores the outputs
  of each layer along the way. The `_backward` method then goes through each layer in reverse
  order and updates the weights within each layer.
docstr: Execute a backward pass through the neural network, aka. backpropagation.
  
  Parameters
  ----------
  z
      The output of each layer.
  a
      The output of each activation function.
  y
      A DataFrame of shape (batch_size, n_targets).
docstr_tok: ["Execute","a","backward","pass","through","the","neural","network",",","aka",".","backpropagation",".","Parameters","--","--","--","--","--","z","The","output","of","each","layer",".","a","The","output","of","each","activation","function",".","y","A","DataFrame","of","shape","(","batch_size",",","n_targets",")","."]
code: def _backward(self, z: dict, a: dict, y: pd.DataFrame):
      """Execute a backward pass through the neural network, aka. backpropagation.
  
      Parameters
      ----------
      z
          The output of each layer.
      a
          The output of each activation function.
      y
          A DataFrame of shape (batch_size, n_targets).
  
      """
  
      y = y.values[:, np.argsort(y.columns.to_numpy())]
  
      # Determine the partial derivative and delta for the output layer
      y_pred = a[self.n_layers]
      final_activation = self.activations[self.n_layers - 2]
      delta = self.loss.gradient(y, y_pred) * final_activation.gradient(
          y_pred
      )
      dw = np.dot(a[self.n_layers - 1].T, delta)
  
      update_params = {self.n_layers - 1: (dw, delta)}
  
      # Go through the layers in reverse order
      for i in range(self.n_layers - 2, 0, -1):
          delta = np.dot(delta, self.w[i + 1].T) * self.activations[
              i - 1
          ].gradient(z[i + 1])
          dw = np.dot(a[i].T, delta)
          update_params[i] = (dw, delta)
  
      # Update the parameters
      for i, (dw, delta) in update_params.items():
          self._optimizers[i, 0].step(w=self.w[i], g=dw)
          self._optimizers[i, 1].step(
              w=self.b[i], g=np.mean(delta, axis=0)
          )
  
imports: from __future__ import annotations
  import collections
  import copy
  import functools
  import numpy
  import pandas
  from river import base, optim
label_desc: Use image node_id 4 for calling the MLP obj's underlying member method code with example usage: obj._backward(z, a, y) without return types