def log_text(self, log_name, x, y=None, timestamp=None): """Log text data in Neptune | If a log with provided ``log_name`` does not exist, it is created automatically. | If log exists (determined by ``log_name``), then new value is appended to it. Args: log_name (:obj:`str`): The name of log, i.e. `mse`, `my_text_data`, `timing_info`. x (:obj:`double` or :obj:`str`): Depending, whether ``y`` parameter is passed: * ``y`` not passed: The value of the log (data-point). Must be ``str``. * ``y`` passed: Index of log entry being appended. Must be strictly increasing. y (:obj:`str`, optional, default is ``None``): The value of the log (data-point). timestamp (:obj:`time`, optional, default is ``None``): Timestamp to be associated with log entry. Must be Unix time. If ``None`` is passed, `time.time() <https://docs.python.org/3.6/library/time.html#time.time>`_ (Python 3.6 example) is invoked to obtain timestamp. Example: Assuming that `experiment` is an instance of :class:`~neptune.experiments.Experiment`: .. code:: python3 # common case, where log name and data are passed neptune.log_text('my_text_data', str(data_item)) # log_name, x and timestamp are passed neptune.log_text(log_name='logging_losses_as_text', x=str(val_loss), timestamp=1560430912) Note: For efficiency, logs are uploaded in batches via a queue. Hence, if you log a lot of data, you may experience slight delays in Neptune web application. Note: Passing ``x`` coordinate as NaN or +/-inf causes this log entry to be ignored. Warning is printed to ``stdout``. """ x, y = self._get_valid_x_y(x, y) if x is not None and is_nan_or_inf(x): x = None if not isinstance(y, six.string_types): raise InvalidChannelValue(expected_type="str", actual_type=type(y).__name__) if x is not None and is_nan_or_inf(x): _logger.warning( "Invalid metric x-coordinate: %s for channel %s. " "Metrics with nan or +/-inf x-coordinates will not be sent to server", x, log_name, ) else: value = ChannelValue(x, dict(text_value=y), timestamp) self._channels_values_sender.send(log_name, ChannelType.TEXT.value, value)
def send_text(self, channel_name, x, y=None, timestamp=None): x, y = self._get_valid_x_y(x, y) if not isinstance(y, six.string_types): raise InvalidChannelValue(expected_type='str', actual_type=type(y).__name__) value = ChannelValue(x, dict(text_value=y), timestamp) self._channels_values_sender.send(channel_name, 'text', value)
def send_metric(self, channel_name, x, y=None, timestamp=None): x, y = self._get_valid_x_y(x, y) if not is_float(y): raise InvalidChannelValue(expected_type='float', actual_type=type(y).__name__) value = ChannelValue(x, dict(numeric_value=y), timestamp) self._channels_values_sender.send(channel_name, 'numeric', value)
def _get_valid_x_y(x, y): if x is None: raise NoChannelValue() if y is None: y = x x = None elif not is_float(x): raise InvalidChannelValue(expected_type='float', actual_type=type(x).__name__) return x, y
def get_image_content(image): if isinstance(image, six.string_types): if not os.path.exists(image): raise FileNotFound(image) with open(image, 'rb') as image_file: return image_file.read() elif isinstance(image, Image.Image): with io.BytesIO() as image_buffer: image.save(image_buffer, format='PNG') return image_buffer.getvalue() raise InvalidChannelValue(expected_type='image', actual_type=type(image).__name__)
def _get_valid_x_y(x, y): """ The goal of this function is to allow user to call experiment.log_* with any of: - single parameter treated as y value - both parameters (named/unnamed) - single named y parameter If intended X-coordinate is provided, it is validated to be a float value """ if x is None and y is None: raise NoChannelValue() if x is None and y is not None: return None, y if x is not None and y is None: return None, x if x is not None and y is not None: if not is_float(x): raise InvalidChannelValue(expected_type='float', actual_type=type(x).__name__) return x, y
def get_image_content(image): if isinstance(image, six.string_types): if not os.path.exists(image): raise FileNotFound(image) with open(image, 'rb') as image_file: return image_file.read() elif isinstance(image, numpy.ndarray): shape = image.shape if len(shape) == 2: return _get_pil_image_data( Image.fromarray(image.astype(numpy.uint8))) if len(shape) == 3: if shape[2] == 1: array2d = numpy.array([[col[0] for col in row] for row in image]) return _get_pil_image_data( Image.fromarray(array2d.astype(numpy.uint8))) if shape[2] in (3, 4): return _get_pil_image_data( Image.fromarray(image.astype(numpy.uint8))) raise ValueError( "Incorrect size of numpy.ndarray. Should be 2-dimensional or" "3-dimensional with 3rd dimension of size 1, 3 or 4.") elif isinstance(image, Image.Image): return _get_pil_image_data(image) else: try: from matplotlib import figure if isinstance(image, figure.Figure): return _get_pil_image_data(_figure_to_pil_image(image)) except ImportError: pass raise InvalidChannelValue(expected_type='image', actual_type=type(image).__name__)
def get_image_content(image): if isinstance(image, six.string_types): if not os.path.exists(image): raise FileNotFound(image) with open(image, 'rb') as image_file: return image_file.read() elif isinstance(image, numpy.ndarray): return _get_numpy_as_image(image) elif isinstance(image, Image.Image): return _get_pil_image_data(image) else: try: from matplotlib import figure if isinstance(image, figure.Figure): return _get_figure_as_image(image) except ImportError: pass try: from torch import Tensor as TorchTensor if isinstance(image, TorchTensor): return _get_numpy_as_image(image.detach().numpy()) except ImportError: pass try: from tensorflow import Tensor as TensorflowTensor if isinstance(image, TensorflowTensor): return _get_numpy_as_image(image.numpy()) except ImportError: pass raise InvalidChannelValue(expected_type='image', actual_type=type(image).__name__)
def log_metric(self, log_name, x, y=None, timestamp=None): """Log metrics (numeric values) in Neptune | If a log with provided ``log_name`` does not exist, it is created automatically. | If log exists (determined by ``log_name``), then new value is appended to it. Args: log_name (:obj:`str`): The name of log, i.e. `mse`, `loss`, `accuracy`. x (:obj:`double`): Depending, whether ``y`` parameter is passed: * ``y`` not passed: The value of the log (data-point). * ``y`` passed: Index of log entry being appended. Must be strictly increasing. y (:obj:`double`, optional, default is ``None``): The value of the log (data-point). timestamp (:obj:`time`, optional, default is ``None``): Timestamp to be associated with log entry. Must be Unix time. If ``None`` is passed, `time.time() <https://docs.python.org/3.6/library/time.html#time.time>`_ (Python 3.6 example) is invoked to obtain timestamp. Example: Assuming that `experiment` is an instance of :class:`~neptune.experiments.Experiment` and 'accuracy' log does not exists: .. code:: python3 # Both calls below have the same effect # Common invocation, providing log name and value experiment.log_metric('accuracy', 0.5) experiment.log_metric('accuracy', 0.65) experiment.log_metric('accuracy', 0.8) # Providing both x and y params experiment.log_metric('accuracy', 0, 0.5) experiment.log_metric('accuracy', 1, 0.65) experiment.log_metric('accuracy', 2, 0.8) Note: For efficiency, logs are uploaded in batches via a queue. Hence, if you log a lot of data, you may experience slight delays in Neptune web application. Note: Passing either ``x`` or ``y`` coordinate as NaN or +/-inf causes this log entry to be ignored. Warning is printed to ``stdout``. """ x, y = self._get_valid_x_y(x, y) if not is_float(y): raise InvalidChannelValue(expected_type='float', actual_type=type(y).__name__) if is_nan_or_inf(y): _logger.warning( 'Invalid metric value: %s for channel %s. ' 'Metrics with nan or +/-inf values will not be sent to server', y, log_name) elif x is not None and is_nan_or_inf(x): _logger.warning( 'Invalid metric x-coordinate: %s for channel %s. ' 'Metrics with nan or +/-inf x-coordinates will not be sent to server', x, log_name) else: value = ChannelValue(x, dict(numeric_value=y), timestamp) self._channels_values_sender.send(log_name, ChannelType.NUMERIC.value, value)