    def on_epoch_end(self, epoch, losses, generator, discriminator=None, metrics={}):
        """
        Manages the operations that are taken at the end of each epoch:
        metric checks, weight saves, logging.
        """
        
        self.logger.info(losses)
        monitor_op = {'max': np.greater, 'min': np.less}
        extreme = {'max': -np.Inf, 'min': np.Inf}
        for metric in metrics:
            if metric in losses.keys():
                if metric not in self.best_metrics.keys():
                    self.best_metrics[metric] = extreme[metrics[metric]]
                
                if monitor_op[metrics[metric]](losses[metric], self.best_metrics[metric]):
                    self.logger.info(
                        '{} improved from {:10.5f} to {:10.5f}'.format(
                            metric, self.best_metrics[metric], losses[metric]
                        )
                    )
                    self.logger.info('Saving weights')
                    self.best_metrics[metric] = losses[metric]
                    self._save_weights(epoch, generator, discriminator, metric=metric, best=True)
                    self.since_last_epoch = 0
                    return True
                else:
                    self.logger.info('{} did not improve.'.format(metric))
                    if self.since_last_epoch >= self.fallback_save_every_n_epochs:
                        self.logger.info('Saving weights anyways.')
                        self._save_weights(epoch, generator, discriminator, best=False)
                        self.since_last_epoch = 0
                        return True
            
            else:
                self.logger.warning('{} is not monitored, cannot save weights.'.format(metric))
        self.since_last_epoch += 1
        return False
Manages the operations that are taken at the end of each epoch:
metric checks, weight saves, logging.
ISR/utils/train_helper.py