ScalarMetricLogEntry.__init__()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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