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, ...]

def _f_preactivations(self, X):
        """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, ...]
        """

        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