Skip to content

Commit

Permalink
add resnet 50 v1 in pytorch framework (#52)
Browse files Browse the repository at this point in the history
* add pytorch runner

* wip

* wip

* add proper preprocessing, clean up for PR

* cleanup

* make 'NHWC' a Default Argument Value for dataset class and imagenet class

* add setting the threads number

* Framework name messages

Co-authored-by: Jan Grzybek <52271793+jangrzybek@users.noreply.github.com>
  • Loading branch information
MarcelWilnicki and jan-grzybek-ampere committed Aug 23, 2021
1 parent bdd7ef8 commit beb50cc
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 9 deletions.
2 changes: 1 addition & 1 deletion classification/mobilenet_v2/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def main():
args.model_path, args.batch_size, args.num_runs, args.timeout, args.images_path, args.labels_path
)
else:
assert False
assert False, f"Behaviour undefined for precision {args.precision}"


if __name__ == "__main__":
Expand Down
66 changes: 66 additions & 0 deletions classification/resnet_50_v1/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import argparse

import numpy as np

from utils.benchmark import run_model
from utils.imagenet import ImageNet
from utils.pytorch import PyTorchRunner

PYTORCH_MODEL_NAME = 'resnet50'


def parse_args():
parser = argparse.ArgumentParser(description="Run ResNet-50 v1 model.")
parser.add_argument("-p", "--precision",
type=str, choices=["fp32"], required=True,
help="precision of the model provided")
parser.add_argument("-b", "--batch_size",
type=int, default=1,
help="batch size to feed the model with")
parser.add_argument("--timeout",
type=float, default=60.0,
help="timeout in seconds")
parser.add_argument("--num_runs",
type=int,
help="number of passes through network to execute")
parser.add_argument("--images_path",
type=str,
help="path to directory with ImageNet validation images")
parser.add_argument("--labels_path",
type=str,
help="path to file with validation labels")
return parser.parse_args()


def run_torch_fp32(batch_size, num_of_runs, timeout, images_path, labels_path):

def run_single_pass(pytorch_runner, imagenet):
shape = (224, 224)
output = pytorch_runner.run(imagenet.get_input_array(shape))

for i in range(batch_size):
imagenet.submit_predictions(
i,
imagenet.extract_top1(output[i]),
imagenet.extract_top5(output[i])
)

dataset = ImageNet(batch_size, "RGB", images_path, labels_path,
pre_processing='Inception', is1001classes=False, order='NCHW')
runner = PyTorchRunner(PYTORCH_MODEL_NAME)

return run_model(run_single_pass, runner, dataset, batch_size, num_of_runs, timeout)


def main():
args = parse_args()
if args.precision == "fp32":
run_torch_fp32(
args.batch_size, args.num_runs, args.timeout, args.images_path, args.labels_path
)
else:
assert False, f"Behaviour undefined for precision {args.precision}"


if __name__ == "__main__":
main()
5 changes: 3 additions & 2 deletions classification/resnet_50_v2/run.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import argparse

from utils.benchmark import run_model
from utils.imagenet import ImageNet
from utils.tf import TFFrozenModelRunner
from utils.tflite import TFLiteRunner
from utils.benchmark import run_model


def parse_args():
Expand Down Expand Up @@ -83,7 +84,7 @@ def main():
args.model_path, args.batch_size, args.num_runs, args.timeout, args.images_path, args.labels_path
)
else:
assert False
assert False, f"Behaviour undefined for precision {args.precision}"


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion utils/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_intra_op_parallelism_threads():
utils.print_goodbye_message_and_die("Number of intra threads to use is not set!"
"\nUse DLS_NUM_THREADS or OMP_NUM_THREADS flags.")

print(f"\nRunning with {intra_op_parallelism_threads} threads\n")
print(f"\nIntraop parallelism set to {intra_op_parallelism_threads} threads\n")

return intra_op_parallelism_threads

Expand Down
10 changes: 8 additions & 2 deletions utils/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ def __resize_image(self, image_array, target_shape):
horizontal_ratio = target_shape[1] / image_array.shape[1]
return cv2.resize(image_array, target_shape), (vertical_ratio, horizontal_ratio)

def __load_image(self, image_path, target_shape, color_model: str):
def __load_image(self, image_path, target_shape, color_model: str, order="NHWC"):
"""
A function loading image available under the supplied path and then applying proper rescaling/resizing.
:param image_path: PathLib.PurePath or str, path to the image
:param target_shape: tuple of intended image shape (height, width)
:param color_model: str, color model of image data, possible values: ["RGB", "BGR"]
:param order: str, the order of image data, possible values: ["NCHW", "NHWC"]
:return: numpy array with resized image data in NHWC/NCHW format
"""
if color_model not in ["RGB", "BGR"]:
Expand All @@ -47,4 +48,9 @@ def __load_image(self, image_path, target_shape, color_model: str):
if color_model == "RGB":
image_array = image_array[:, :, [2, 1, 0]] # cv2 loads images in BGR order
image_array, resize_ratios = self.__resize_image(image_array, target_shape)
return np.expand_dims(image_array, axis=0), resize_ratios
image = np.expand_dims(image_array, axis=0)

if order == 'NCHW':
image = np.transpose(image, (0, 3, 1, 2))

return image, resize_ratios
12 changes: 9 additions & 3 deletions utils/imagenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ImageNet(utils_ds.ImageDataset):
"""

def __init__(self, batch_size: int, color_model: str,
images_path=None, labels_path=None, pre_processing=None, is1001classes=False):
images_path=None, labels_path=None, pre_processing=None, is1001classes=False, order="NHWC"):

if images_path is None:
env_var = "IMAGENET_IMG_PATH"
Expand All @@ -33,6 +33,7 @@ def __init__(self, batch_size: int, color_model: str,
self.__top_1_count = 0
self.__top_5_count = 0
self.path_to_latest_image = None
self.__order = order
super().__init__()

def __parse_val_file(self, labels_path, is1001classes):
Expand Down Expand Up @@ -84,14 +85,19 @@ def get_input_array(self, target_shape):
:return: numpy array containing rescaled, pre-processed image data of batch size requested at class
initialization
"""
input_array = np.empty([self.__batch_size, *target_shape, 3]) # NHWC order
if self.__order == 'NCHW':
input_array = np.empty([self.__batch_size, 3, *target_shape]) # NCHW order
else:
input_array = np.empty([self.__batch_size, *target_shape, 3]) # NHWC order
for i in range(self.__batch_size):
self.path_to_latest_image = self.__get_path_to_img()
input_array[i], _ = self._ImageDataset__load_image(
self.path_to_latest_image, target_shape, self.__color_model
self.path_to_latest_image, target_shape, self.__color_model, self.__order
)

if self.__pre_processing:
input_array = pp.pre_process(input_array, self.__pre_processing, self.__color_model)

return input_array

def extract_top1(self, output_array):
Expand Down
1 change: 1 addition & 0 deletions utils/pre_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,5 @@ def pre_process_inception(input_array):
input_array /= 255.
input_array -= 0.5
input_array *= 2.

return input_array
58 changes: 58 additions & 0 deletions utils/pytorch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import torchvision
import torch
import utils.misc as utils
import time
import utils.benchmark as bench_utils
import numpy as np
import sys


class PyTorchRunner:
"""
A class providing facilities to run PyTorch model (as pretrained torchvision model).
"""

def __init__(self, model: str):

torch.set_num_threads(bench_utils.get_intra_op_parallelism_threads())

if model not in torchvision.models.__dict__:
utils.print_goodbye_message_and_die(
f"{model} not supported by torchvision!")

self.__model = torchvision.models.__dict__[model](pretrained=True)
self.__model.eval()
self.__warm_up_run_latency = 0.0
self.__total_inference_time = 0.0
self.__times_invoked = 0

print("\nRunning with PyTorch\n")

def run(self, input):
"""
A function assigning values to input tensor, executing single pass over the network, measuring the time needed
and finally returning the output.
:return: dict, output dictionary with tensor names and corresponding output
"""

input_tensor = torch.from_numpy(input)

with torch.no_grad():

start = time.time()
output_tensor = self.__model(input_tensor)
finish = time.time()
output_tensor = output_tensor.detach().numpy()

self.__total_inference_time += finish - start
if self.__times_invoked == 0:
self.__warm_up_run_latency += finish - start
self.__times_invoked += 1

return output_tensor

def print_performance_metrics(self, batch_size):
perf = bench_utils.print_performance_metrics(
self.__warm_up_run_latency, self.__total_inference_time, self.__times_invoked, batch_size)
return perf
2 changes: 2 additions & 0 deletions utils/tf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def __init__(self, path_to_model: str, output_names: list):
self.__total_inference_time = 0.0
self.__times_invoked = 0

print("\nRunning with TensorFlow\n")

def __create_config(self, intra_threads: int, inter_threads=1):
"""
A function creating TF config for given num of threads.
Expand Down
2 changes: 2 additions & 0 deletions utils/tflite.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ def __init__(self, path_to_model: str):
self.__total_inference_time = 0.0
self.__times_invoked = 0

print("\nRunning with TensorFlow Lite\n")

def set_input_tensor(self, input_index: int, input_array):
"""
A function assigning given numpy input array to the tensor under the provided input index.
Expand Down

0 comments on commit beb50cc

Please sign in to comment.