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 forward pass through the neural network.
  
  Parameters
  ----------
  X
      A DataFrame of shape (batch_size, n_features).
  
  Returns
  -------
  The output of each layer.
  The output of each activation function.
docstr_tok: ["Execute","a","forward","pass","through","the","neural","network",".","Parameters","--","--","--","--","--","X","A","DataFrame","of","shape","(","batch_size",",","n_features",")",".","Returns","--","--","--","-","The","output","of","each","layer",".","The","output","of","each","activation","function","."]
code: def _forward(self, X: pd.DataFrame) -> tuple[dict, dict]:
      """Execute a forward pass through the neural network.
  
      Parameters
      ----------
      X
          A DataFrame of shape (batch_size, n_features).
  
      Returns
      -------
      The output of each layer.
      The output of each activation function.
  
      """
  
      z = {}  # z = w(x) + b
      a = {}  # a = f(z)
  
      # As a convention, we sort the columns of the input DataFrame. This allows the user to
      # pass DataFrames with different column orders without having to worry.
      a[1] = X.values[:, np.argsort(X.columns.to_numpy())]
  
      for i in range(2, self.n_layers + 1):
          z[i] = np.dot(a[i - 1], self.w[i - 1]) + self.b[i - 1]
          a[i] = self.activations[i - 2].apply(z[i])
  
      return z, a
  
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 3 for calling the MLP obj's underlying member method code with example usage: obj._forward(X) and returns: z,  a