Safemotion Lib
Loading...
Searching...
No Matches
evaluator.py
Go to the documentation of this file.
1# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2import datetime
3import logging
4import time
5from contextlib import contextmanager
6
7import torch
8
9from fastreid.utils.logger import log_every_n_seconds
10
11
13 """
14 Base class for a dataset evaluator.
15 The function :func:`inference_on_dataset` runs the model over
16 all samples in the dataset, and have a DatasetEvaluator to process the inputs/outputs.
17 This class will accumulate information of the inputs/outputs (by :meth:`process`),
18 and produce evaluation results in the end (by :meth:`evaluate`).
19 """
20
21 def reset(self):
22 """
23 Preparation for a new round of evaluation.
24 Should be called before starting a round of evaluation.
25 """
26 pass
27
28 def preprocess_inputs(self, inputs):
29 pass
30
31 def process(self, inputs, outputs):
32 """
33 Process an input/output pair.
34 Args:
35 inputs: the inputs that's used to call the model.
36 outputs: the return value of `model(input)`
37 """
38 pass
39
40 def evaluate(self):
41 """
42 Evaluate/summarize the performance, after processing all input/output pairs.
43 Returns:
44 dict:
45 A new evaluator class can return a dict of arbitrary format
46 as long as the user can process the results.
47 In our train_net.py, we expect the following format:
48 * key: the name of the task (e.g., bbox)
49 * value: a dict of {metric name: score}, e.g.: {"AP50": 80}
50 """
51 pass
52
53
54# class DatasetEvaluators(DatasetEvaluator):
55# def __init__(self, evaluators):
56# assert len(evaluators)
57# super().__init__()
58# self._evaluators = evaluators
59#
60# def reset(self):
61# for evaluator in self._evaluators:
62# evaluator.reset()
63#
64# def process(self, input, output):
65# for evaluator in self._evaluators:
66# evaluator.process(input, output)
67#
68# def evaluate(self):
69# results = OrderedDict()
70# for evaluator in self._evaluators:
71# result = evaluator.evaluate()
72# if is_main_process() and result is not None:
73# for k, v in result.items():
74# assert (
75# k not in results
76# ), "Different evaluators produce results with the same key {}".format(k)
77# results[k] = v
78# return results
79
80
81def inference_on_dataset(model, data_loader, evaluator):
82 """
83 Run model on the data_loader and evaluate the metrics with evaluator.
84 The model will be used in eval mode.
85 Args:
86 model (nn.Module): a module which accepts an object from
87 `data_loader` and returns some outputs. It will be temporarily set to `eval` mode.
88 If you wish to evaluate a model in `training` mode instead, you can
89 wrap the given model and override its behavior of `.eval()` and `.train()`.
90 data_loader: an iterable object with a length.
91 The elements it generates will be the inputs to the model.
92 evaluator (DatasetEvaluator): the evaluator to run. Use
93 :class:`DatasetEvaluators([])` if you only want to benchmark, but
94 don't want to do any evaluation.
95 Returns:
96 The return value of `evaluator.evaluate()`
97 """
98 logger = logging.getLogger(__name__)
99 logger.info("Start inference on {} images".format(len(data_loader.dataset)))
100
101 total = len(data_loader) # inference data loader must have a fixed length
102 evaluator.reset()
103
104 num_warmup = min(5, total - 1)
105 start_time = time.perf_counter()
106 total_compute_time = 0
107 with inference_context(model), torch.no_grad():
108 for idx, inputs in enumerate(data_loader):
109 if idx == num_warmup:
110 start_time = time.perf_counter()
111 total_compute_time = 0
112
113 start_compute_time = time.perf_counter()
114 outputs = model(inputs)
115 total_compute_time += time.perf_counter() - start_compute_time
116 evaluator.process(inputs, outputs)
117
118 idx += 1
119 iters_after_start = idx + 1 - num_warmup * int(idx >= num_warmup)
120 seconds_per_batch = total_compute_time / iters_after_start
121 if idx >= num_warmup * 2 or seconds_per_batch > 30:
122 total_seconds_per_img = (time.perf_counter() - start_time) / iters_after_start
123 eta = datetime.timedelta(seconds=int(total_seconds_per_img * (total - idx - 1)))
124 log_every_n_seconds(
125 logging.INFO,
126 "Inference done {}/{}. {:.4f} s / batch. ETA={}".format(
127 idx + 1, total, seconds_per_batch, str(eta)
128 ),
129 n=30,
130 )
131
132 # Measure the time only for this worker (before the synchronization barrier)
133 total_time = time.perf_counter() - start_time
134 total_time_str = str(datetime.timedelta(seconds=total_time))
135 # NOTE this format is parsed by grep
136 logger.info(
137 "Total inference time: {} ({:.6f} s / batch per device)".format(
138 total_time_str, total_time / (total - num_warmup)
139 )
140 )
141 total_compute_time_str = str(datetime.timedelta(seconds=int(total_compute_time)))
142 logger.info(
143 "Total inference pure compute time: {} ({:.6f} s / batch per device)".format(
144 total_compute_time_str, total_compute_time / (total - num_warmup)
145 )
146 )
147 results = evaluator.evaluate()
148 # An evaluator may return None when not in main process.
149 # Replace it by an empty dict instead to make it easier for downstream code to handle
150 if results is None:
151 results = {}
152 return results
153
154
155@contextmanager
157 """
158 A context where the model is temporarily changed to eval mode,
159 and restored to previous mode afterwards.
160 Args:
161 model: a torch Module
162 """
163 training_mode = model.training
164 model.eval()
165 yield
166 model.train(training_mode)
inference_on_dataset(model, data_loader, evaluator)
Definition evaluator.py:81