def METHOD_NAME(self, X, y, init_params=True):
    self._check_target_array(y)
    if init_params:
        if self.n_classes is None:
            self.n_classes = np.max(y) + 1
        self._n_features = X.shape[1]
        self.b_, self.w_ = self._init_params(
            weights_shape=(self._n_features, self.n_classes),
            bias_shape=(self.n_classes,),
            random_seed=self.random_seed,
        )
        self.cost_ = []
    y_enc = self._one_hot(y=y, n_labels=self.n_classes, dtype=np.float_)
    self.init_time_ = time()
    rgen = np.random.RandomState(self.random_seed)
    for i in range(self.epochs):
        for idx in self._yield_minibatches_idx(
            rgen=rgen, n_batches=self.minibatches, data_ary=y, shuffle=True
        ):
            # net_input, softmax and diff -> n_samples x n_classes:
            y_probas = self._forward(X[idx])
            # w_ -> n_feat x n_classes
            # b_  -> n_classes
            grad_loss_wrt_w, grad_loss_wrt_b = self._backward(
                X[idx], y_true=y_enc[idx], y_probas=y_probas
            )
            # update in opp. direction of the cost gradient
            l2_reg = self.l2 * self.w_
            self.w_ += self.eta * (-grad_loss_wrt_w - l2_reg)
            self.b_ += self.eta * -grad_loss_wrt_b
        # compute cost of the whole epoch
        y_probas = self._forward(X)
        cross_ent = self._cross_entropy(output=y_probas, y_target=y_enc)
        cost = self._cost(cross_ent)
        self.cost_.append(cost)
        if self.print_progress:
            self._print_progress(iteration=i + 1, n_iter=self.epochs, cost=cost)
    return self