Кажущаяся конкуренция между отдельными процессами
Модель TensorFlow Lite оценивается на моем Windows ноутбуке (Windows 10, без графического процессора) примерно за 200 мс. Когда я запускаю второй экземпляр процесса оценки в отдельном командном окне, время выполнения в каждом экземпляре достигает почти 400 мс.
Дополнительные сведения
Процесс выполняется один:
Img 0, Eval time=244 ms. Invoke=242.
Img 1, Eval time=223 ms. Invoke=221.
Img 2, Eval time=220 ms. Invoke=218.
Img 3, Eval time=217 ms. Invoke=215.
Img 4, Eval time=221 ms. Invoke=219.
Img 5, Eval time=242 ms. Invoke=241.
Img 6, Eval time=218 ms. Invoke=216.
Img 7, Eval time=225 ms. Invoke=222.
Img 8, Eval time=229 ms. Invoke=227.
Img 9, Eval time=240 ms. Invoke=238.
Когда я запускаю 2-й экземпляр, вот примеры времени - оба экземпляра испытывают падение производительности:
Img 0, Eval time=446 ms. Invoke=446.
Img 1, Eval time=455 ms. Invoke=451.
Img 2, Eval time=490 ms. Invoke=480.
Img 3, Eval time=447 ms. Invoke=443.
Img 4, Eval time=411 ms. Invoke=406.
Img 5, Eval time=343 ms. Invoke=338.
Img 6, Eval time=346 ms. Invoke=343.
Img 7, Eval time=322 ms. Invoke=322.
Img 8, Eval time=297 ms. Invoke=295.
Img 9, Eval time=332 ms. Invoke=329.
Вот файл, который я использовал для своего теста, который я запускаю из командной строки Windows:
python evaluate.py
# evaluate.py
"""
Created on Thu Apr 16 23:32:03 2020
@author: MHerzo
"""
import numpy as np
import sys
import time
import tensorflow as tf # TF2
import cv2
#from object_detection.utils import visualization_utils as vis_util
import datetime
import pickle
# Model eval parameters
THRESHOLD = 0.1
#EVAL_FREQ = 0.5 # Time between model evaluations, in seconds
class EvalAutoML():
def __init__(self):
self.model_file = r'model\model.tflite'
# -------------------
# Initialize model
# -------------------
self.log_msg('Initizliaing model')
self.interpreter = tf.lite.Interpreter(model_path=self.model_file)
self.interpreter.allocate_tensors()
self.input_details = self.interpreter.get_input_details()
self.output_details = self.interpreter.get_output_details()
# NxHxWxC, H:1, W:2
self.height = self.input_details[0]['shape'][1]
self.width = self.input_details[0]['shape'][2]
self.log_msg01(f'input_details={self.input_details}')
self.log_msg(f'Init done')
def log_msg(self, msg):
self.log_msg01(f'{datetime.datetime.now():%Y-%m-%d %H:%M:%S}: {msg}')
def log_msg01(self, msg):
print(f'{msg}')
def get_images(self):
with open(r'video_input.pickle', 'rb') as f:
image_list = pickle.load(f)
while True:
for i, image_np in enumerate(image_list):
yield {'img_num':i,
#'img_size':image_np.size,
#'sum':np.sum(img),
'img':image_np}
def get_output(self, key):
output = self.interpreter.get_tensor(self.output_details[key]['index'])
output = output.squeeze()
return output
def evaluator(self, input_data):
expected_keys = set(['img_num', 'img'] )
img_num = input_data['img_num']
img = input_data['img']
#img_sum = input_data['sum']
# Output of quantized COCO SSD MobileNet v1 model
#https://www.tensorflow.org/lite/models/object_detection/overview#starter_model
#
# Index Name Description
# 0 Locations Multidimensional array of [10][4] floating point values between 0 and 1, the inner arrays representing bounding boxes in the form [top, left, bottom, right]
# 1 Classes Array of 10 integers (output as floating point values) each indicating the index of a class label from the labels file
# 2 Scores Array of 10 floating point values between 0 and 1 representing probability that a class was detected
# 3 Number and detections Array of length 1 containing a floating point value expressing the total number of detection results
eval_start_ns = time.time_ns()
orig_width, orig_height = img.shape[:2]
image_np_resized = cv2.resize(img,(round(self.width), round(self.height)),interpolation=cv2.INTER_AREA)
# Expand dimensions since the model expects images to have shape: [1, None, None, 3]
# add N dim
model_input_data = np.expand_dims(image_np_resized, axis=0)
self.interpreter.set_tensor(self.input_details[0]['index'], model_input_data)
invoke_start_ns = time.time_ns()
self.interpreter.invoke()
invoke_end_ns = time.time_ns()
boxes = self.get_output(0)
class_ids = self.get_output(1)
scores = self.get_output(2)
eval_end_ns = time.time_ns()
# Sec in ns = 10**9. Sec in ms = 10**3
eval_ms = round((eval_end_ns - eval_start_ns) / 1000000)
invoke_ms = round((invoke_end_ns - invoke_start_ns) / 1000000)
print(f'Img {img_num}, Eval time={eval_ms} ms. Invoke={invoke_ms}.')
results = dict()
results['img_num'] = img_num
results['boxes'] = boxes
results['classes'] = class_ids
results['scores'] = scores
#self.log_msg('Exiting')
return results
def main_par(self):
# Process images
for img_dict in self.get_images():
results = self.evaluator(img_dict)
if __name__ == '__main__':
# Python version: 3.7.4 (default, Aug 9 2019, 18:34:13) [MSC v.1915 64 bit (AMD64)]
print(f'Python version: {sys.version}')
eam = EvalAutoML()
eam.main_par()
Вот весь тест, включая модель и исходные изображения: https://github.com/mherzog01/Benchmark-TensorFlow-Windows
Моя версия TensorFlow - 2.1.0.
Загрузка процессора
Кажется, что каждый процесс полностью использует 1 ядро моего 4 виртуального ядра ноутбука. Запуск 1 процесса использует ~ 25% от общего объема процессора:
Запуск 2 процесса использует ~ 50%:
Мой вопрос
В моем текущем окружении (Python и Windows), могу ли я что-нибудь сделать для решения этой проблемы? Или я должен использовать Unix и / или клиент, такой как C ++, который лучше поддерживает многопоточность / многозадачность?