code: def _f_preactivations(self, X):
          
  
          preactivations_list = list()
          net = X
          preactivations_list.append(X)
  
          for W, b, i in zip(self.W_list, self.b_list, np.arange(len(self.W_list))):
              net = np.dot(net, W)
              net = net + b
              if i < len(self.W_list) - 1:
                  preactivations_list.append(net)
                  net = self.act(net)
          return preactivations_list
docs: Computes the network preactivations, i.e. the results of all intermediate linear layers before applying the
          activation function on them
          :param X: input data
          :return: list of preactivations [X, XW+b, f(XW+b)W+b, ...]