init - 初始化项目

This commit is contained in:
Lee Nony
2022-05-06 01:58:53 +08:00
commit 90a5cc7cb6
6772 changed files with 2837787 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
from abc import ABC, ABCMeta, abstractmethod
class AbstractModel(ABC):
@abstractmethod
def get_prepared_models(self):
pass
class Framework(object):
in_blob_name = ''
out_blob_name = ''
__metaclass__ = ABCMeta
@abstractmethod
def get_name(self):
pass
@abstractmethod
def get_output(self, input_blob):
pass

View File

@@ -0,0 +1,96 @@
import sys
import time
import numpy as np
from ...utils import get_final_summary_info
class ClsAccEvaluation:
log = sys.stdout
img_classes = {}
batch_size = 0
def __init__(self, log_path, img_classes_file, batch_size):
self.log = open(log_path, 'w')
self.img_classes = self.read_classes(img_classes_file)
self.batch_size = batch_size
# collect the accuracies for both models
self.general_quality_metric = []
self.general_inference_time = []
@staticmethod
def read_classes(img_classes_file):
result = {}
with open(img_classes_file) as file:
for l in file.readlines():
result[l.split()[0]] = int(l.split()[1])
return result
def get_correct_answers(self, img_list, net_output_blob):
correct_answers = 0
for i in range(len(img_list)):
indexes = np.argsort(net_output_blob[i])[-5:]
correct_index = self.img_classes[img_list[i]]
if correct_index in indexes:
correct_answers += 1
return correct_answers
def process(self, frameworks, data_fetcher):
sorted_imgs_names = sorted(self.img_classes.keys())
correct_answers = [0] * len(frameworks)
samples_handled = 0
blobs_l1_diff = [0] * len(frameworks)
blobs_l1_diff_count = [0] * len(frameworks)
blobs_l_inf_diff = [sys.float_info.min] * len(frameworks)
inference_time = [0.0] * len(frameworks)
for x in range(0, len(sorted_imgs_names), self.batch_size):
sublist = sorted_imgs_names[x:x + self.batch_size]
batch = data_fetcher.get_batch(sublist)
samples_handled += len(sublist)
fw_accuracy = []
fw_time = []
frameworks_out = []
for i in range(len(frameworks)):
start = time.time()
out = frameworks[i].get_output(batch)
end = time.time()
correct_answers[i] += self.get_correct_answers(sublist, out)
fw_accuracy.append(100 * correct_answers[i] / float(samples_handled))
frameworks_out.append(out)
inference_time[i] += end - start
fw_time.append(inference_time[i] / samples_handled * 1000)
print(samples_handled, 'Accuracy for', frameworks[i].get_name() + ':', fw_accuracy[i], file=self.log)
print("Inference time, ms ", frameworks[i].get_name(), fw_time[i], file=self.log)
self.general_quality_metric.append(fw_accuracy)
self.general_inference_time.append(fw_time)
for i in range(1, len(frameworks)):
log_str = frameworks[0].get_name() + " vs " + frameworks[i].get_name() + ':'
diff = np.abs(frameworks_out[0] - frameworks_out[i])
l1_diff = np.sum(diff) / diff.size
print(samples_handled, "L1 difference", log_str, l1_diff, file=self.log)
blobs_l1_diff[i] += l1_diff
blobs_l1_diff_count[i] += 1
if np.max(diff) > blobs_l_inf_diff[i]:
blobs_l_inf_diff[i] = np.max(diff)
print(samples_handled, "L_INF difference", log_str, blobs_l_inf_diff[i], file=self.log)
self.log.flush()
for i in range(1, len(blobs_l1_diff)):
log_str = frameworks[0].get_name() + " vs " + frameworks[i].get_name() + ':'
print('Final l1 diff', log_str, blobs_l1_diff[i] / blobs_l1_diff_count[i], file=self.log)
print(
get_final_summary_info(
self.general_quality_metric,
self.general_inference_time,
"accuracy"
),
file=self.log
)

View File

@@ -0,0 +1,87 @@
import os
from abc import ABCMeta, abstractmethod
import cv2
import numpy as np
from ...img_utils import read_rgb_img, get_pytorch_preprocess
from ...test.configs.default_preprocess_config import PYTORCH_RSZ_HEIGHT, PYTORCH_RSZ_WIDTH
class DataFetch(object):
imgs_dir = ''
frame_size = 0
bgr_to_rgb = False
__metaclass__ = ABCMeta
@abstractmethod
def preprocess(self, img):
pass
@staticmethod
def reshape_img(img):
img = img[:, :, 0:3].transpose(2, 0, 1)
return np.expand_dims(img, 0)
def center_crop(self, img):
cols = img.shape[1]
rows = img.shape[0]
y1 = round((rows - self.frame_size) / 2)
y2 = round(y1 + self.frame_size)
x1 = round((cols - self.frame_size) / 2)
x2 = round(x1 + self.frame_size)
return img[y1:y2, x1:x2]
def initial_preprocess(self, img):
min_dim = min(img.shape[-3], img.shape[-2])
resize_ratio = self.frame_size / float(min_dim)
img = cv2.resize(img, (0, 0), fx=resize_ratio, fy=resize_ratio)
img = self.center_crop(img)
return img
def get_preprocessed_img(self, img_path):
image_data = read_rgb_img(img_path, self.bgr_to_rgb)
image_data = self.preprocess(image_data)
return self.reshape_img(image_data)
def get_batch(self, img_names):
assert type(img_names) is list
batch = np.zeros((len(img_names), 3, self.frame_size, self.frame_size)).astype(np.float32)
for i in range(len(img_names)):
img_name = img_names[i]
img_file = os.path.join(self.imgs_dir, img_name)
assert os.path.exists(img_file)
batch[i] = self.get_preprocessed_img(img_file)
return batch
class PyTorchPreprocessedFetch(DataFetch):
def __init__(self, pytorch_cls_config, preprocess_input=None):
self.imgs_dir = pytorch_cls_config.img_root_dir
self.frame_size = pytorch_cls_config.frame_size
self.bgr_to_rgb = pytorch_cls_config.bgr_to_rgb
self.preprocess_input = preprocess_input
def preprocess(self, img):
img = cv2.resize(img, (PYTORCH_RSZ_WIDTH, PYTORCH_RSZ_HEIGHT))
img = self.center_crop(img)
if self.preprocess_input:
return self.presprocess_input(img)
return get_pytorch_preprocess(img)
class TFPreprocessedFetch(DataFetch):
def __init__(self, tf_cls_config, preprocess_input):
self.imgs_dir = tf_cls_config.img_root_dir
self.frame_size = tf_cls_config.frame_size
self.bgr_to_rgb = tf_cls_config.bgr_to_rgb
self.preprocess_input = preprocess_input
def preprocess(self, img):
img = self.initial_preprocess(img)
return self.preprocess_input(img)

View File

@@ -0,0 +1,19 @@
import cv2
import numpy as np
from .test.configs.default_preprocess_config import BASE_IMG_SCALE_FACTOR
def read_rgb_img(img_file, is_bgr_to_rgb=True):
img = cv2.imread(img_file, cv2.IMREAD_COLOR)
if is_bgr_to_rgb:
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
return img
def get_pytorch_preprocess(img):
img = img.astype(np.float32)
img *= BASE_IMG_SCALE_FACTOR
img -= [0.485, 0.456, 0.406]
img /= [0.229, 0.224, 0.225]
return img

View File

@@ -0,0 +1,60 @@
from .configs.test_config import TestClsConfig, TestClsModuleConfig
from .model_test_pipeline import ModelTestPipeline
from ..evaluation.classification.cls_accuracy_evaluator import ClsAccEvaluation
from ..utils import get_test_module
class ClsModelTestPipeline(ModelTestPipeline):
def __init__(
self,
network_model,
model_processor,
dnn_model_processor,
data_fetcher,
img_processor=None,
cls_args_parser=None,
default_input_blob_preproc=None
):
super(ClsModelTestPipeline, self).__init__(
network_model,
model_processor,
dnn_model_processor
)
if cls_args_parser:
self._parser = cls_args_parser
self.test_config = TestClsConfig()
parser_args = self._parser.parse_args()
if parser_args.test:
self._test_module_config = TestClsModuleConfig()
self._test_module = get_test_module(
self._test_module_config.test_module_name,
self._test_module_config.test_module_path
)
if parser_args.default_img_preprocess:
self._default_input_blob_preproc = default_input_blob_preproc
if parser_args.evaluate:
self._data_fetcher = data_fetcher(self.test_config, img_processor)
def _configure_test_module_params(self):
self._test_module_param_list.extend((
'--crop', self._test_module_config.crop,
'--std', *self._test_module_config.std
))
if self._test_module_config.rsz_height and self._test_module_config.rsz_width:
self._test_module_param_list.extend((
'--initial_height', self._test_module_config.rsz_height,
'--initial_width', self._test_module_config.rsz_width,
))
def _configure_acc_eval(self, log_path):
self._accuracy_evaluator = ClsAccEvaluation(
log_path,
self.test_config.img_cls_file,
self.test_config.batch_size
)

View File

@@ -0,0 +1,37 @@
BASE_IMG_SCALE_FACTOR = 1 / 255.0
PYTORCH_RSZ_HEIGHT = 256
PYTORCH_RSZ_WIDTH = 256
pytorch_resize_input_blob = {
"mean": ["123.675", "116.28", "103.53"],
"scale": str(BASE_IMG_SCALE_FACTOR),
"std": ["0.229", "0.224", "0.225"],
"crop": "True",
"rgb": True,
"rsz_height": str(PYTORCH_RSZ_HEIGHT),
"rsz_width": str(PYTORCH_RSZ_WIDTH)
}
pytorch_input_blob = {
"mean": ["123.675", "116.28", "103.53"],
"scale": str(BASE_IMG_SCALE_FACTOR),
"std": ["0.229", "0.224", "0.225"],
"crop": "True",
"rgb": True
}
tf_input_blob = {
"scale": str(1 / 127.5),
"mean": ["127.5", "127.5", "127.5"],
"std": [],
"crop": "True",
"rgb": True
}
tf_model_blob_caffe_mode = {
"mean": ["103.939", "116.779", "123.68"],
"scale": "1.0",
"std": [],
"crop": "True",
"rgb": False
}

View File

@@ -0,0 +1,40 @@
import os
from dataclasses import dataclass, field
from typing import List
@dataclass
class CommonConfig:
output_data_root_dir: str = "dnn_model_runner/dnn_conversion"
logs_dir: str = os.path.join(output_data_root_dir, "logs")
log_file_path: str = os.path.join(logs_dir, "{}_log.txt")
@dataclass
class TestClsConfig:
batch_size: int = 1
frame_size: int = 224
img_root_dir: str = "./ILSVRC2012_img_val"
# location of image-class matching
img_cls_file: str = "./val.txt"
bgr_to_rgb: bool = True
@dataclass
class TestClsModuleConfig:
cls_test_data_dir: str = "../data"
test_module_name: str = "classification"
test_module_path: str = "classification.py"
input_img: str = os.path.join(cls_test_data_dir, "squirrel_cls.jpg")
model: str = ""
frame_height: str = str(TestClsConfig.frame_size)
frame_width: str = str(TestClsConfig.frame_size)
scale: str = "1.0"
mean: List[str] = field(default_factory=lambda: ["0.0", "0.0", "0.0"])
std: List[str] = field(default_factory=list)
crop: str = "False"
rgb: str = "True"
rsz_height: str = ""
rsz_width: str = ""
classes: str = os.path.join(cls_test_data_dir, "dnn", "classification_classes_ILSVRC2012.txt")

View File

@@ -0,0 +1,126 @@
import os
import numpy as np
from .configs.test_config import CommonConfig
from ..utils import create_parser, plot_acc
class ModelTestPipeline:
def __init__(
self,
network_model,
model_processor,
dnn_model_processor
):
self._net_model = network_model
self._model_processor = model_processor
self._dnn_model_processor = dnn_model_processor
self._parser = create_parser()
self._test_module = None
self._test_module_config = None
self._test_module_param_list = None
self.test_config = None
self._data_fetcher = None
self._default_input_blob_preproc = None
self._accuracy_evaluator = None
def init_test_pipeline(self):
cmd_args = self._parser.parse_args()
model_dict = self._net_model.get_prepared_models()
model_names = list(model_dict.keys())
print(
"The model {} was successfully obtained and converted to OpenCV {}".format(model_names[0], model_names[1])
)
if cmd_args.test:
if not self._test_module_config.model:
self._test_module_config.model = self._net_model.model_path["full_path"]
if cmd_args.default_img_preprocess:
self._test_module_config.scale = self._default_input_blob_preproc["scale"]
self._test_module_config.mean = self._default_input_blob_preproc["mean"]
self._test_module_config.std = self._default_input_blob_preproc["std"]
self._test_module_config.crop = self._default_input_blob_preproc["crop"]
if "rsz_height" in self._default_input_blob_preproc and "rsz_width" in self._default_input_blob_preproc:
self._test_module_config.rsz_height = self._default_input_blob_preproc["rsz_height"]
self._test_module_config.rsz_width = self._default_input_blob_preproc["rsz_width"]
self._test_module_param_list = [
'--model', self._test_module_config.model,
'--input', self._test_module_config.input_img,
'--width', self._test_module_config.frame_width,
'--height', self._test_module_config.frame_height,
'--scale', self._test_module_config.scale,
'--mean', *self._test_module_config.mean,
'--std', *self._test_module_config.std,
'--classes', self._test_module_config.classes,
]
if self._default_input_blob_preproc["rgb"]:
self._test_module_param_list.append('--rgb')
self._configure_test_module_params()
self._test_module.main(
self._test_module_param_list
)
if cmd_args.evaluate:
original_model_name = model_names[0]
dnn_model_name = model_names[1]
self.run_test_pipeline(
[
self._model_processor(model_dict[original_model_name], original_model_name),
self._dnn_model_processor(model_dict[dnn_model_name], dnn_model_name)
],
original_model_name.replace(" ", "_")
)
def run_test_pipeline(
self,
models_list,
formatted_exp_name,
is_plot_acc=True
):
log_path, logs_dir = self._configure_eval_log(formatted_exp_name)
print(
"===== Running evaluation of the model with the following params:\n"
"\t* val data location: {}\n"
"\t* log file location: {}\n".format(
self.test_config.img_root_dir,
log_path
)
)
os.makedirs(logs_dir, exist_ok=True)
self._configure_acc_eval(log_path)
self._accuracy_evaluator.process(models_list, self._data_fetcher)
if is_plot_acc:
plot_acc(
np.array(self._accuracy_evaluator.general_inference_time),
formatted_exp_name
)
print("===== End of the evaluation pipeline =====")
def _configure_acc_eval(self, log_path):
pass
def _configure_test_module_params(self):
pass
@staticmethod
def _configure_eval_log(formatted_exp_name):
common_test_config = CommonConfig()
return common_test_config.log_file_path.format(formatted_exp_name), common_test_config.logs_dir

View File

@@ -0,0 +1,153 @@
import argparse
import importlib.util
import os
import random
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import torch
from .test.configs.test_config import CommonConfig
SEED_VAL = 42
DNN_LIB = "DNN"
# common path for model savings
MODEL_PATH_ROOT = os.path.join(CommonConfig().output_data_root_dir, "{}/models")
def get_full_model_path(lib_name, model_full_name):
model_path = MODEL_PATH_ROOT.format(lib_name)
return {
"path": model_path,
"full_path": os.path.join(model_path, model_full_name)
}
def plot_acc(data_list, experiment_name):
plt.figure(figsize=[8, 6])
plt.plot(data_list[:, 0], "r", linewidth=2.5, label="Original Model")
plt.plot(data_list[:, 1], "b", linewidth=2.5, label="Converted DNN Model")
plt.xlabel("Iterations ", fontsize=15)
plt.ylabel("Time (ms)", fontsize=15)
plt.title(experiment_name, fontsize=15)
plt.legend()
full_path_to_fig = os.path.join(CommonConfig().output_data_root_dir, experiment_name + ".png")
plt.savefig(full_path_to_fig, bbox_inches="tight")
def get_final_summary_info(general_quality_metric, general_inference_time, metric_name):
general_quality_metric = np.array(general_quality_metric)
general_inference_time = np.array(general_inference_time)
summary_line = "===== End of processing. General results:\n"
"\t* mean {} for the original model: {}\t"
"\t* mean time (min) for the original model inferences: {}\n"
"\t* mean {} for the DNN model: {}\t"
"\t* mean time (min) for the DNN model inferences: {}\n".format(
metric_name, np.mean(general_quality_metric[:, 0]),
np.mean(general_inference_time[:, 0]) / 60000,
metric_name, np.mean(general_quality_metric[:, 1]),
np.mean(general_inference_time[:, 1]) / 60000,
)
return summary_line
def set_common_reproducibility():
random.seed(SEED_VAL)
np.random.seed(SEED_VAL)
def set_pytorch_env():
set_common_reproducibility()
torch.manual_seed(SEED_VAL)
torch.set_printoptions(precision=10)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(SEED_VAL)
torch.backends.cudnn_benchmark_enabled = False
torch.backends.cudnn.deterministic = True
def set_tf_env(is_use_gpu=True):
set_common_reproducibility()
tf.random.set_seed(SEED_VAL)
os.environ["TF_DETERMINISTIC_OPS"] = "1"
if tf.config.list_physical_devices("GPU") and is_use_gpu:
gpu_devices = tf.config.list_physical_devices("GPU")
tf.config.experimental.set_visible_devices(gpu_devices[0], "GPU")
tf.config.experimental.set_memory_growth(gpu_devices[0], True)
os.environ["TF_USE_CUDNN"] = "1"
else:
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
def str_bool(input_val):
if input_val.lower() in ('yes', 'true', 't', 'y', '1'):
return True
elif input_val.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value was expected')
def get_formatted_model_list(model_list):
note_line = 'Please, choose the model from the below list:\n'
spaces_to_set = ' ' * (len(note_line) - 2)
return note_line + ''.join([spaces_to_set, '{} \n'] * len(model_list)).format(*model_list)
def model_str(model_list):
def type_model_list(input_val):
if input_val.lower() in model_list:
return input_val.lower()
else:
raise argparse.ArgumentTypeError(
'The model is currently unavailable for test.\n' +
get_formatted_model_list(model_list)
)
return type_model_list
def get_test_module(test_module_name, test_module_path):
module_spec = importlib.util.spec_from_file_location(test_module_name, test_module_path)
test_module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(test_module)
module_spec.loader.exec_module(test_module)
return test_module
def create_parser():
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument(
"--test",
type=str_bool,
help="Define whether you'd like to run the model with OpenCV for testing.",
default=False
),
parser.add_argument(
"--default_img_preprocess",
type=str_bool,
help="Define whether you'd like to preprocess the input image with defined"
" PyTorch or TF functions for model test with OpenCV.",
default=False
),
parser.add_argument(
"--evaluate",
type=str_bool,
help="Define whether you'd like to run evaluation of the models (ex.: TF vs OpenCV networks).",
default=True
)
return parser
def create_extended_parser(model_list):
parser = create_parser()
parser.add_argument(
"--model_name",
type=model_str(model_list=model_list),
help="\nDefine the model name to test.\n" +
get_formatted_model_list(model_list),
required=True
)
return parser