Exemple #1
0
    def chain_string(self):
        """Basic info about the items in the chain.

        Returns
        -------
        str

        """

        output = ''

        ui = FancyStringifier()
        output += ui.row('ID',
                         'Processor',
                         'Input',
                         'Output',
                         'Init parameters set',
                         widths=[5, 30, 18, 18, 50]) + '\n'
        output += ui.row('-', '-', '-', '-', '-') + '\n'
        if len(self):
            for item_id, item in enumerate(self):
                if isinstance(item, ProcessingChainItem):
                    current_processor = self.processor_class_reference(
                        processor_name=item['processor_name'])
                    processor_name = item['processor_name'].split('.')[-1]

                    output += ui.row(
                        item_id, processor_name, current_processor.input_type,
                        current_processor.output_type, ','.join(
                            item.get('init_parameters', {}).keys())) + '\n'

        else:
            output += ui.row('Empty', widths=[95])

        return output
Exemple #2
0
    def chain_string(self):
        """Basic info about the items in the chain.

        Returns
        -------
        str

        """

        output = ''

        ui = FancyStringifier()
        output += ui.row('ID',
                         'Processor',
                         'INPUT',
                         'OUTPUT',
                         'INIT',
                         widths=[5, 52, 18, 18, 25]) + '\n'
        output += ui.row('-', '-', '-', '-', '-') + '\n'
        if len(self):
            for item_id, item in enumerate(self):
                if isinstance(item, ProcessingChainItem):
                    current_processor = self.processor_class_reference(
                        processor_name=item['processor_name'])

                    output += ui.row(item_id, item['processor_name'],
                                     current_processor.input_type,
                                     current_processor.output_type,
                                     item.get('init_parameters')) + '\n'

        else:
            output += ui.row('Empty', widths=[95])

        return output
Exemple #3
0
class ProgressLoggerCallback(BaseCallback):
    """Keras callback to show metrics in logging interface. Implements Keras Callback API.

    This callback is very similar to standard ``ProgbarLogger`` Keras callback, however it adds support for logging
    interface, and external metrics (metrics calculated outside Keras training process).

    """
    def __init__(self,
                 manual_update=False,
                 epochs=None,
                 external_metric_labels=None,
                 metric=None,
                 loss=None,
                 manual_update_interval=1,
                 output_type='logging',
                 **kwargs):
        """Constructor

        Parameters
        ----------
        epochs : int
            Total amount of epochs
            Default value None

        metric : str
            Metric name
            Default value None

        manual_update : bool
            Manually update callback, use this to when injecting external metrics
            Default value False

        manual_update_interval : int
            Epoch interval for manual update, used anticipate updates
            Default value 1

        output_type : str
            Output type, either 'logging' or 'console'
            Default value 'logging'

        external_metric_labels : dict or OrderedDict
            Dictionary with {'metric_label': 'metric_name'}
            Default value None

        """

        kwargs.update({
            'manual_update': manual_update,
            'epochs': epochs,
            'external_metric_labels': external_metric_labels,
        })

        super(ProgressLoggerCallback, self).__init__(**kwargs)

        if isinstance(metric, str):
            self.metric = metric

        elif callable(metric):
            self.metric = metric.__name__

        self.loss = loss

        self.manual_update_interval = manual_update_interval

        self.output_type = output_type

        self.timer = Timer()
        self.ui = FancyStringifier()

        if self.output_type == 'logging':
            self.output_target = FancyLogger()

        elif self.output_type == 'console':
            self.output_target = FancyPrinter()

        self.seen = 0
        self.log_values = []

        self.most_recent_values = collections.OrderedDict()
        self.most_recent_values['l_tra'] = None
        self.most_recent_values['l_val'] = None
        self.most_recent_values['m_tra'] = None
        self.most_recent_values['m_val'] = None

        self.data = {
            'l_tra': numpy.empty((self.epochs, )),
            'l_val': numpy.empty((self.epochs, )),
            'm_tra': numpy.empty((self.epochs, )),
            'm_val': numpy.empty((self.epochs, )),
        }
        self.data['l_tra'][:] = numpy.nan
        self.data['l_val'][:] = numpy.nan
        self.data['m_tra'][:] = numpy.nan
        self.data['m_val'][:] = numpy.nan

        for metric_label in self.external_metric_labels:
            self.data[metric_label] = numpy.empty((self.epochs, ))
            self.data[metric_label][:] = numpy.nan

        self.header_shown = False
        self.last_update_epoch = 0

        self.target = None

    def on_train_begin(self, logs=None):
        if self.epochs is None:
            self.epochs = self.params['epochs']

        if not self.header_shown:
            output = ''
            output += self.ui.line('Training') + '\n'

            if self.external_metric_labels:
                output += self.ui.row(
                    '',
                    'Loss',
                    'Metric',
                    'Ext. metrics',
                    '',
                    widths=[
                        10, 26, 26,
                        len(self.external_metric_labels) * 15, 17
                    ]) + '\n'

                header2 = ['', self.loss, self.metric]
                header3 = ['Epoch', 'Train', 'Val', 'Train', 'Val']
                widths = [10, 13, 13, 13, 13]
                sep = ['-', '-', '-', '-', '-']
                for metric_label, metric_name in iteritems(
                        self.external_metric_labels):
                    header2.append('')
                    header3.append(metric_name)
                    widths.append(15)
                    sep.append('-')

                header2.append('')
                header3.append('time')
                widths.append(17)
                sep.append('-')

                output += self.ui.row(*header2) + '\n'
                output += self.ui.row(*header3, widths=widths) + '\n'
                output += self.ui.row(*sep)

            else:
                output += self.ui.row(
                    '', 'Loss', 'Metric', '', widths=[10, 26, 26, 17]) + '\n'
                output += self.ui.row('', self.loss, self.metric, '') + '\n'
                output += self.ui.row('Epoch',
                                      'Train',
                                      'Val',
                                      'Train',
                                      'Val',
                                      'Time',
                                      widths=[10, 13, 13, 13, 13, 17]) + '\n'
                output += self.ui.row('-', '-', '-', '-', '-', '-')

            self.output_target.line(output)

            # Show header only once
            self.header_shown = True

    def on_epoch_begin(self, epoch, logs=None):
        self.epoch = epoch + 1

        if 'steps' in self.params:
            self.target = self.params['steps']

        elif 'samples' in self.params:
            self.target = self.params['samples']

        self.seen = 0
        self.timer.start()

    def on_batch_begin(self, batch, logs=None):
        if self.target and self.seen < self.target:
            self.log_values = []

    def on_batch_end(self, batch, logs=None):
        logs = logs or {}
        batch_size = logs.get('size', 0)
        self.seen += batch_size

        for k in self.params['metrics']:
            if k in logs:
                self.log_values.append((k, logs[k]))

    def on_epoch_end(self, epoch, logs=None):
        self.timer.stop()
        self.epoch = epoch

        logs = logs or {}

        # Reset values
        self.most_recent_values['l_tra'] = None
        self.most_recent_values['l_val'] = None
        self.most_recent_values['m_tra'] = None
        self.most_recent_values['m_val'] = None

        # Collect values
        for k in self.params['metrics']:
            if k in logs:
                self.log_values.append((k, logs[k]))
                if k == 'loss':
                    self.data['l_tra'][self.epoch] = logs[k]
                    self.most_recent_values['l_tra'] = '{:4.3f}'.format(
                        logs[k])

                elif k == 'val_loss':
                    self.data['l_val'][self.epoch] = logs[k]
                    self.most_recent_values['l_val'] = '{:4.3f}'.format(
                        logs[k])

                elif self.metric and k.endswith(self.metric):
                    if k.startswith('val_'):
                        self.data['m_val'][self.epoch] = logs[k]
                        self.most_recent_values['m_val'] = '{:4.3f}'.format(
                            logs[k])

                    else:
                        self.data['m_tra'][self.epoch] = logs[k]
                        self.most_recent_values['m_tra'] = '{:4.3f}'.format(
                            logs[k])

        for metric_label in self.external_metric_labels:
            if metric_label in self.external_metric:
                metric_name = self.external_metric_labels[metric_label]
                value = self.external_metric[metric_label]
                if metric_name.endswith('f_measure') or metric_name.endswith(
                        'f_score'):
                    self.most_recent_values[metric_label] = '{:3.1f}'.format(
                        value * 100)
                else:
                    self.most_recent_values[metric_label] = '{:4.3f}'.format(
                        value)

        if (not self.manual_update
                or (self.epoch - self.last_update_epoch > 0 and
                    (self.epoch + 1) % self.manual_update_interval)):

            # Update logged progress
            self.update_progress_log()

    def update(self):
        """Update

        """
        self.update_progress_log()
        self.last_update_epoch = self.epoch

    def update_progress_log(self):
        """Update progress to logging interface

        """

        if self.epoch - self.last_update_epoch:
            data = [
                self.epoch, self.data['l_tra'][self.epoch],
                self.data['l_val'][self.epoch]
                if 'l_val' in self.most_recent_values else '-',
                self.data['m_tra'][self.epoch], self.data['m_val'][self.epoch]
                if self.most_recent_values['m_val'] else '-'
            ]
            types = ['int', 'float4', 'float4', 'float4', 'float4']
            for metric_label in self.external_metric_labels:
                if metric_label in self.external_metric:
                    value = self.data[metric_label][self.epoch]

                    if numpy.isnan(value):
                        value = ' ' * 10
                    else:
                        if self.external_metric_labels[metric_label].endswith(
                                'f_measure') or self.external_metric_labels[
                                    metric_label].endswith('f_score'):
                            value = float(value) * 100
                            types.append('float2')

                        else:
                            value = float(value)
                            types.append('float4')

                    data.append(value)
                else:
                    data.append('')

            data.append(self.timer.get_string())
            types.append('str')

            output = self.ui.row(*data, types=types)

            self.output_target.line(output)

    def add_external_metric(self, metric_id):
        """Add external metric to be monitored

        Parameters
        ----------
        metric_id : str
            Metric name

        """

        if metric_id not in self.external_metric_labels:
            self.external_metric_labels[metric_id] = metric_id

        if metric_id not in self.data:
            self.data[metric_id] = numpy.empty((self.epochs, ))
            self.data[metric_id][:] = numpy.nan

    def set_external_metric_value(self, metric_label, metric_value):
        """Add external metric value

        Parameters
        ----------
        metric_label : str
            Metric label

        metric_value : numeric
            Metric value

        """

        self.external_metric[metric_label] = metric_value
        self.data[metric_label][self.epoch] = metric_value
Exemple #4
0
def model_summary_string(keras_model, mode='keras'):
    """Model summary in a formatted string, similar to Keras model summary function.

    Parameters
    ----------
    keras_model : keras model
        Keras model

    mode : str
        Summary mode ['extended', 'keras']. In case 'keras', standard Keras summary is returned.
        Default value keras

    Returns
    -------
    str
        Model summary

    """

    ui = FancyStringifier()
    output = ''
    output += ui.line('Model summary') + '\n'

    if mode == 'extended':
        layer_name_map = {
            'BatchNormalization': 'BatchNorm',
        }
        import keras
        from distutils.version import LooseVersion
        import keras.backend as keras_backend

        output += ui.row('Layer type',
                         'Output',
                         'Param',
                         'Name',
                         'Connected to',
                         'Activ.',
                         'Init',
                         widths=[15, 25, 10, 20, 25, 10, 10],
                         indent=4) + '\n'
        output += ui.row('-', '-', '-', '-', '-', '-', '-') + '\n'

        for layer in keras_model.layers:
            connections = []
            if LooseVersion(keras.__version__) >= LooseVersion('2.1.3'):
                for node_index, node in enumerate(layer._inbound_nodes):
                    for i in range(len(node.inbound_layers)):
                        inbound_layer = node.inbound_layers[i].name
                        inbound_node_index = node.node_indices[i]
                        inbound_tensor_index = node.tensor_indices[i]
                        connections.append(inbound_layer + '[' +
                                           str(inbound_node_index) + '][' +
                                           str(inbound_tensor_index) + ']')

            else:
                for node_index, node in enumerate(layer.inbound_nodes):
                    for i in range(len(node.inbound_layers)):
                        inbound_layer = node.inbound_layers[i].name
                        inbound_node_index = node.node_indices[i]
                        inbound_tensor_index = node.tensor_indices[i]
                        connections.append(inbound_layer + '[' +
                                           str(inbound_node_index) + '][' +
                                           str(inbound_tensor_index) + ']')

            config = DictContainer(layer.get_config())
            layer_name = layer.__class__.__name__
            if layer_name in layer_name_map:
                layer_name = layer_name_map[layer_name]

            if config.get_path(
                    'kernel_initializer.class_name') == 'VarianceScaling':
                init = str(
                    config.get_path('kernel_initializer.config.distribution',
                                    '---'))

            elif config.get_path(
                    'kernel_initializer.class_name') == 'RandomUniform':
                init = 'uniform'

            else:
                init = '---'

            output += ui.row(
                layer_name, str(layer.output_shape), str(layer.count_params()),
                str(layer.name),
                str(connections[0]) if len(connections) > 0 else '---',
                str(config.get('activation', '---')), init) + '\n'

        trainable_count = int(
            numpy.sum([
                keras_backend.count_params(p)
                for p in set(keras_model.trainable_weights)
            ]))

        non_trainable_count = int(
            numpy.sum([
                keras_backend.count_params(p)
                for p in set(keras_model.non_trainable_weights)
            ]))

        output += ui.line('') + '\n'
        output += ui.line(
            'Parameters',
            indent=4,
        ) + '\n'
        output += ui.data(indent=6,
                          field='Total',
                          value=trainable_count + non_trainable_count) + '\n'
        output += ui.data(indent=6, field='Trainable',
                          value=trainable_count) + '\n'
        output += ui.data(
            indent=6, field='Non-Trainable', value=non_trainable_count) + '\n'

    else:
        output_buffer = []
        keras_model.summary(print_fn=output_buffer.append)
        for line in output_buffer:
            output += ui.line(line, indent=4) + '\n'

    output += ui.line('') + '\n'

    output += ui.data(
        indent=4, field='Input shape', value=keras_model.input_shape) + '\n'
    output += ui.data(
        indent=4, field='Output shape', value=keras_model.output_shape) + '\n'

    return output