Run a single epoch.

        This function shuffles the data internally,
        as this improves performance.

        Parameters
        ----------
        X : numpy array
            The training data.
        epoch_idx : int
            The current epoch
        batch_size : int
            The batch size
        updates_epoch : int
            The number of updates to perform per epoch
        constants : dict
            A dictionary containing the constants with which to update the
            parameters in self.parameters.
        show_progressbar : bool
            Whether to show a progressbar during training.

def _epoch(self,
               X,
               epoch_idx,
               batch_size,
               updates_epoch,
               constants,
               show_progressbar):
        """
        Run a single epoch.

        This function shuffles the data internally,
        as this improves performance.

        Parameters
        ----------
        X : numpy array
            The training data.
        epoch_idx : int
            The current epoch
        batch_size : int
            The batch size
        updates_epoch : int
            The number of updates to perform per epoch
        constants : dict
            A dictionary containing the constants with which to update the
            parameters in self.parameters.
        show_progressbar : bool
            Whether to show a progressbar during training.

        """
        # Create batches
        X_ = self._create_batches(X, batch_size)
        X_len = np.prod(X.shape[:-1])

        # Initialize the previous activation
        prev = self._init_prev(X_)
        prev = self.distance_function(X_[0], self.weights)[0]
        influences = self._update_params(prev)

        # Iterate over the training data
        for idx, x in enumerate(tqdm(X_, disable=not show_progressbar)):

            # Our batches are padded, so we need to
            # make sure we know when we hit the padding
            # so we don't inadvertently learn zeroes.
            diff = X_len - (idx * batch_size)
            if diff and diff < batch_size:
                x = x[:diff]
                # Prev_activation may be None
                if prev is not None:
                    prev = prev[:diff]

            # if idx > 0 and idx % update_step == 0:
            influences = self._update_params(prev)
            prev = self._propagate(x,
                                   influences,
                                   prev_activation=prev)