Completed
Pull Request — master (#184)
by Martin
44s
created

MetricsLogger.log_scalar_metric()   A

Complexity

Conditions 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
c 2
b 0
f 0
dl 0
loc 21
rs 9.3142
1
#!/usr/bin/env python
2
# coding=utf-8
3
import datetime
4
import sys
5
6
if sys.version_info[0] == 2:
7
    from Queue import Queue, Empty
8
else:
9
    from queue import Queue, Empty
10
11
12
class MetricsLogger(object):
13
    """MetricsLogger collects metrics measured during experiments.
14
15
    MetricsLogger is the (only) part of the Metrics API.
16
    An instance of the class should be created for the Run class, such that the
17
    log_scalar_metric method is accessible from running experiments using
18
    _run.metrics.log_scalar_metric.
19
    """
20
21
    def __init__(self):
22
        # Create a message queue that remembers
23
        # calls of the log_scalar_metric
24
        self._logged_metrics = Queue()
25
        self._metric_step_counter = {}
26
        """Remembers the last number of each metric."""
27
28
    def log_scalar_metric(self, metric_name, value, step=None):
29
        """
30
        Add a new measurement.
31
32
        The measurement will be processed by the MongoDB observer
33
        during a heartbeat event.
34
        Other observers are not yet supported.
35
36
        :param metric_name: The name of the metric, e.g. training.loss.
37
        :param value: The measured value.
38
        :param step: The step number (integer), e.g. the iteration number
39
                    If not specified, an internal counter for each metric
40
                    is used, incremented by one.
41
        """
42
        if step is None:
43
            step = self._metric_step_counter.get(metric_name, -1) + 1
44
        self._logged_metrics.put(
45
            ScalarMetricLogEntry(metric_name, step,
46
                                 datetime.datetime.utcnow(),
47
                                 value))
48
        self._metric_step_counter[metric_name] = step
49
50
    def get_last_metrics(self):
51
        """Read all measurement events since last call of the method.
52
53
        :return List[ScalarMetricLogEntry]
54
        """
55
        read_up_to = self._logged_metrics.qsize()
56
        messages = []
57
        for i in range(read_up_to):
58
            try:
59
                messages.append(self._logged_metrics.get_nowait())
60
            except Empty:
61
                pass
62
        return messages
63
64
65
class ScalarMetricLogEntry():
66
    """Container for measurements of scalar metrics.
67
68
    There is exactly one ScalarMetricLogEntry per logged scalar metric value.
69
    """
70
71
    def __init__(self, name, step, timestamp, value):
72
        self.name = name
73
        self.step = step
74
        self.timestamp = timestamp
75
        self.value = value
76
77
78
def linearize_metrics(logged_metrics):
79
    """
80
    Group metrics by name.
81
82
    Takes a list of individual measurements, possibly belonging
83
    to different metrics and groups them by name.
84
85
    :param logged_metrics: A list of ScalarMetricLogEntries
86
    :return: Measured values grouped by the metric name:
87
    {"metric_name1": {"steps": [0,1,2], "values": [4, 5, 6],
88
    "timestamps": [datetime, datetime, datetime]},
89
    "metric_name2": {...}}
90
    """
91
    metrics_by_name = {}
92
    for metric_entry in logged_metrics:
93
        if metric_entry.name not in metrics_by_name:
94
            metrics_by_name[metric_entry.name] = {
95
                "steps": [],
96
                "values": [],
97
                "timestamps": [],
98
                "name": metric_entry.name
99
            }
100
        metrics_by_name[metric_entry.name]["steps"] \
101
            .append(metric_entry.step)
102
        metrics_by_name[metric_entry.name]["values"] \
103
            .append(metric_entry.value)
104
        metrics_by_name[metric_entry.name]["timestamps"] \
105
            .append(metric_entry.timestamp)
106
    return metrics_by_name
107