Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit f404a72

Browse files
Object Detection Refactored
1 parent 3458a5b commit f404a72

File tree

4 files changed

+122
-185
lines changed

4 files changed

+122
-185
lines changed

tftrt/examples/benchmark_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def aggregate_data(self, y_pred, y):
183183
self._total_samples_processed += step_batch_size
184184
idx_stop = self._total_samples_processed
185185

186-
with timed_section("Copy Time",
186+
with timed_section("Numpy Copy Time",
187187
activate=self._args.debug_performance,
188188
start_end_mode=False):
189189
for key, val in self._predicted.items():

tftrt/examples/image_classification/image_classification.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,9 @@
1515
# limitations under the License.
1616
# =============================================================================
1717

18-
import multiprocessing
1918
import os
2019
import sys
2120

22-
import multiprocessing
23-
24-
from functools import partial
25-
2621
import numpy as np
2722

2823
import tensorflow as tf
@@ -169,7 +164,7 @@ def preprocess_sample_fn(record):
169164

170165
dataset = dataset.interleave(
171166
tf.data.TFRecordDataset,
172-
cycle_length=min(8, multiprocessing.cpu_count()),
167+
cycle_length=tf.data.experimental.AUTOTUNE,
173168
block_length=max(self._args.batch_size, 32)
174169
)
175170

@@ -181,7 +176,7 @@ def preprocess_sample_fn(record):
181176

182177
dataset = dataset.map(
183178
map_func=preprocess_fn,
184-
num_parallel_calls=min(8, multiprocessing.cpu_count())
179+
num_parallel_calls=tf.data.experimental.AUTOTUNE,
185180
)
186181

187182
dataset = dataset.batch(self._args.batch_size, drop_remainder=False)

tftrt/examples/object_detection/object_detection.py

Lines changed: 103 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,12 @@
1616
# =============================================================================
1717

1818
import os
19-
import sys
20-
21-
import logging
22-
import time
2319
import shutil
20+
import sys
2421

25-
from functools import partial
22+
import numpy as np
2623
import ujson as json
2724

28-
import numpy as np
2925
import tensorflow as tf
3026

3127
from pycocotools.coco import COCO
@@ -46,7 +42,7 @@
4642

4743
class CommandLineAPI(BaseCommandLineAPI):
4844

49-
SAMPLES_IN_VALIDATION_SET = 5000
45+
SAMPLES_IN_VALIDATION_SET = 50000
5046

5147
def __init__(self):
5248
super(CommandLineAPI, self).__init__()
@@ -66,40 +62,110 @@ def __init__(self):
6662
)
6763

6864

65+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
66+
# %%%%%%%%%%%%%%%%% IMPLEMENT MODEL-SPECIFIC FUNCTIONS HERE %%%%%%%%%%%%%%%%%% #
67+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #
68+
69+
6970
class BenchmarkRunner(BaseBenchmarkRunner):
7071

71-
ACCURACY_METRIC_NAME = "mAP"
72-
73-
def before_benchmark(self, **kwargs):
74-
self._output_name_map = (
75-
# <tf.Tensor 'detection_boxes:0' shape=(8, None, None) dtype=float32>
76-
(0, 'boxes'),
77-
# <tf.Tensor 'detection_classes:0' shape=(8, None) dtype=float32>
78-
(1, 'classes'),
79-
# <tf.Tensor 'num_detections:0' shape=(8,) dtype=float32>
80-
(2, 'num_detections'),
81-
# <tf.Tensor 'detection_scores:0' shape=(8, None) dtype=float32>
82-
(3, 'scores'),
72+
def get_dataset_batches(self):
73+
"""Returns a list of batches of input samples.
74+
75+
Each batch should be in the form [x, y], where
76+
x is a numpy array of the input samples for the batch, and
77+
y is a numpy array of the expected model outputs for the batch
78+
79+
Returns:
80+
- dataset: a TF Dataset object
81+
- bypass_data_to_eval: any object type that will be passed unmodified to
82+
`evaluate_result()`. If not necessary: `None`
83+
84+
Note: script arguments can be accessed using `self._args.attr`
85+
"""
86+
87+
coco_api = COCO(annotation_file=self._args.annotation_path)
88+
image_ids = coco_api.getImgIds()
89+
90+
image_paths = []
91+
for image_id in image_ids:
92+
coco_img = coco_api.imgs[image_id]
93+
image_paths.append(
94+
os.path.join(self._args.data_dir, coco_img['file_name'])
95+
)
96+
97+
dataset = tf.data.Dataset.from_tensor_slices(image_paths)
98+
99+
def load_image_op(path):
100+
image = tf.io.read_file(path)
101+
image = tf.image.decode_jpeg(image, channels=3)
102+
103+
return tf.data.Dataset.from_tensor_slices([image])
104+
105+
dataset = dataset.interleave(
106+
load_image_op,
107+
cycle_length=tf.data.experimental.AUTOTUNE,
108+
block_length=8,
109+
num_parallel_calls=tf.data.experimental.AUTOTUNE
83110
)
84111

85-
def compute_accuracy_metric(self, predictions, expected, **kwargs):
86-
return self._eval_model(
87-
predictions=predictions,
88-
image_ids=kwargs["image_ids"],
89-
annotation_path=kwargs["annotation_path"]
112+
def preprocess_fn(image):
113+
if self._args.input_size is not None:
114+
image = tf.image.resize(
115+
image,
116+
size=(self._args.input_size, self._args.input_size)
117+
)
118+
image = tf.cast(image, tf.uint8)
119+
return image
120+
121+
dataset = dataset.map(
122+
map_func=preprocess_fn,
123+
num_parallel_calls=tf.data.experimental.AUTOTUNE,
90124
)
91125

92-
def _eval_model(self, predictions, image_ids, annotation_path):
126+
dataset = dataset.batch(self._args.batch_size, drop_remainder=False)
127+
128+
dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
129+
130+
return dataset, None
131+
132+
def preprocess_model_inputs(self, data_batch):
133+
"""This function prepare the `data_batch` generated from the dataset.
134+
Returns:
135+
x: input of the model
136+
y: data to be used for model evaluation
137+
138+
Note: script arguments can be accessed using `self._args.attr`
139+
"""
140+
141+
return data_batch, np.array([])
142+
143+
def postprocess_model_outputs(self, predictions, expected):
144+
"""Post process if needed the predictions and expected tensors. At the
145+
minimum, this function transforms all TF Tensors into a numpy arrays.
146+
Most models will not need to modify this function.
147+
148+
Note: script arguments can be accessed using `self._args.attr`
149+
"""
93150

94-
# for key in predictions:
95-
# predictions[key] = np.vstack(predictions[key])
96-
# if key == 'num_detections':
97-
# predictions[key] = predictions[key].ravel()
151+
predictions = {k: t.numpy() for k, t in predictions.items()}
152+
153+
return predictions, expected
154+
155+
def evaluate_model(self, predictions, expected, bypass_data_to_eval):
156+
"""Evaluate result predictions for entire dataset.
157+
158+
This computes overall accuracy, mAP, etc. Returns the
159+
metric value and a metric_units string naming the metric.
160+
161+
Note: script arguments can be accessed using `args.attr`
162+
"""
163+
coco_api = COCO(annotation_file=self._args.annotation_path)
164+
image_ids = coco_api.getImgIds()
98165

99-
coco = COCO(annotation_file=annotation_path)
100166
coco_detections = []
101167
for i, image_id in enumerate(image_ids):
102-
coco_img = coco.imgs[image_id]
168+
coco_img = coco_api.imgs[image_id]
103169
image_width = coco_img['width']
104170
image_height = coco_img['height']
105171

@@ -127,163 +193,27 @@ def _eval_model(self, predictions, image_ids, annotation_path):
127193
coco_detections_path = os.path.join(tmp_dir, 'coco_detections.json')
128194
with open(coco_detections_path, 'w') as f:
129195
json.dump(coco_detections, f)
130-
cocoDt = coco.loadRes(coco_detections_path)
196+
197+
cocoDt = coco_api.loadRes(coco_detections_path)
131198

132199
shutil.rmtree(tmp_dir)
133200

134201
# compute coco metrics
135-
eval = COCOeval(coco, cocoDt, 'bbox')
202+
eval = COCOeval(coco_api, cocoDt, 'bbox')
136203
eval.params.imgIds = image_ids
137204

138205
eval.evaluate()
139206
eval.accumulate()
140207
eval.summarize()
141208

142-
return eval.stats[0]
143-
144-
def process_model_output(self, outputs, **kwargs):
145-
# outputs = graph_func(batch_images)
146-
if isinstance(outputs, dict):
147-
outputs = {k: t.numpy() for k, t in outputs.items()}
148-
else:
149-
outputs = {
150-
name: outputs[idx].numpy()
151-
for idx, name in self._output_name_map
152-
}
153-
154-
return outputs
155-
156-
157-
def get_dataset(
158-
batch_size, images_dir, image_ids, input_size, use_synthetic_data
159-
):
160-
161-
image_paths = []
162-
163-
for image_id in image_ids:
164-
coco_img = coco.imgs[image_id]
165-
image_paths.append(os.path.join(images_dir, coco_img['file_name']))
166-
167-
dataset = tf.data.Dataset.from_tensor_slices(image_paths)
168-
169-
def load_image_op(path):
170-
image = tf.io.read_file(path)
171-
image = tf.image.decode_jpeg(image, channels=3)
172-
173-
return tf.data.Dataset.from_tensor_slices([image])
174-
175-
dataset = dataset.interleave(
176-
lambda path: load_image_op(path),
177-
cycle_length=tf.data.experimental.AUTOTUNE,
178-
block_length=8,
179-
num_parallel_calls=tf.data.experimental.AUTOTUNE
180-
)
181-
182-
def preprocess_fn(image):
183-
if input_size is not None:
184-
image = tf.image.resize(image, size=(input_size, input_size))
185-
image = tf.cast(image, tf.uint8)
186-
return image
187-
188-
dataset = dataset.apply(
189-
tf.data.experimental.map_and_batch(
190-
map_func=preprocess_fn,
191-
batch_size=batch_size,
192-
num_parallel_calls=tf.data.experimental.AUTOTUNE,
193-
drop_remainder=True
194-
)
195-
)
196-
197-
if use_synthetic_data:
198-
dataset = dataset.take(count=1) # loop over 1 batch
199-
dataset = dataset.cache()
200-
dataset = dataset.repeat()
201-
202-
dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
203-
204-
return dataset
209+
return eval.stats[0] * 100, "mAP %"
205210

206211

207212
if __name__ == '__main__':
208213

209214
cmdline_api = CommandLineAPI()
210215
args = cmdline_api.parse_args()
211216

212-
coco = COCO(annotation_file=args.annotation_path)
213-
image_ids = coco.getImgIds()
214-
215-
def _input_fn(input_data_dir, build_steps, model_phase):
216-
217-
dataset = get_dataset(
218-
batch_size=args.batch_size,
219-
images_dir=input_data_dir,
220-
image_ids=image_ids,
221-
input_size=args.input_size,
222-
# even when using synthetic data, we need to
223-
# build and/or calibrate using real training data
224-
# to be in a realistic scenario
225-
use_synthetic_data=False,
226-
)
217+
runner = BenchmarkRunner(args)
227218

228-
for i, batch_images in enumerate(dataset):
229-
if i >= build_steps:
230-
break
231-
232-
print("* [%s] - step %04d/%04d" % (model_phase, i + 1, build_steps))
233-
yield batch_images,
234-
235-
calibration_input_fn = partial(
236-
_input_fn,
237-
input_data_dir=args.calib_data_dir,
238-
build_steps=args.num_calib_batches // args.batch_size,
239-
model_phase="Calibration"
240-
)
241-
242-
optimize_offline_input_fn = partial(
243-
_input_fn,
244-
input_data_dir=args.data_dir,
245-
build_steps=1,
246-
model_phase="Building"
247-
)
248-
249-
runner = BenchmarkRunner(
250-
input_saved_model_dir=args.input_saved_model_dir,
251-
output_saved_model_dir=args.output_saved_model_dir,
252-
allow_build_at_runtime=args.allow_build_at_runtime,
253-
calibration_input_fn=calibration_input_fn,
254-
debug=args.debug,
255-
gpu_mem_cap=args.gpu_mem_cap,
256-
input_signature_key=args.input_signature_key,
257-
max_workspace_size_bytes=args.max_workspace_size,
258-
minimum_segment_size=args.minimum_segment_size,
259-
num_calib_batches=args.num_calib_batches,
260-
optimize_offline=args.optimize_offline,
261-
optimize_offline_input_fn=optimize_offline_input_fn,
262-
output_tensor_names=args.output_tensor_names,
263-
precision_mode=args.precision,
264-
use_dynamic_shape=args.use_dynamic_shape,
265-
use_tftrt=args.use_tftrt
266-
)
267-
268-
get_benchmark_input_fn = partial(
269-
get_dataset,
270-
images_dir=args.data_dir,
271-
image_ids=image_ids,
272-
input_size=args.input_size
273-
)
274-
275-
runner.execute_benchmark(
276-
batch_size=args.batch_size,
277-
display_every=args.display_every,
278-
get_benchmark_input_fn=get_benchmark_input_fn,
279-
num_iterations=args.num_iterations,
280-
num_warmup_iterations=args.num_warmup_iterations,
281-
skip_accuracy_testing=(
282-
args.use_synthetic_data or args.skip_accuracy_testing
283-
),
284-
use_synthetic_data=args.use_synthetic_data,
285-
use_xla=args.use_xla,
286-
########### Additional Settings ############
287-
image_ids=image_ids,
288-
annotation_path=args.annotation_path
289-
)
219+
runner.execute_benchmark()

0 commit comments

Comments
 (0)