Лекция 3. Пример вывода обученной нейронной сети с использованием OpenVINO toolkit (Async API)¶
1. Предварительная подготовка окружения¶
1.1. Загрузка пакетов¶
import openvino as ov
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
1.2. Создание и настройка окружения OpenVINO Runtime Core¶
device = 'CPU'
# Функция создания и настройки окружения
def create_core(device):
# создание объекта окружения
core = ov.Core()
# проверка, что заданное устройство входит в число доступных устройств для запуска
available_devices = core.available_devices
if device not in available_devices:
print(f'Unsupported device \'{device}\'. Available devices: {available_devices}')
print(f'{core}')
return core
core = create_core(device)
<Core: available plugins[CPU, GPU]>
2. Загрузка и компиляция модели¶
Далее в качестве тестовых моделей используются DenseNet-121 и MobileNetV2.
Для загрузки и конвертации указанных моделей предлагается использовать инструменты
Model Downloader и Model Converter в составе
OpenVINO toolkit - Open Model Zoo.
Для подготовки моделей к выводу средствами OpenVINo toolkit необходимо
выполнить последовательность команд, приведенную ниже. Предполагается,
что сконвертированные тестовые модели находятся в директории ./trained_models
.
conda create -n openvino_converter python=3.10
conda activate openvino_converter
pip install openvino-dev
pip install openvino-dev[tensorflow2]
mkdir trained_models
cd trained_models/
omz_downloader --name densenet-121-tf
omz_converter --name densenet-121-tf
omz_downloader --name mobilenet-v2-1.4-224
omz_converter --name mobilenet-v2-1.4-224
conda deactivate
Вложенная директория public/<model_name>
(<model_name>
- название модели)
создается автоматически инструментом Model Downloader.
В результате конвертации модели внутри директории trained_models/public/<model_name>
создаются вложенные директории FP16
и FP32
, содержащие модель с названием <model_name>
,
сконвертированную в промежуточное представление OpenVINO с весами FP16 и FP32 соответственно.
#model_xml = './trained_models/public/densenet-121-tf/FP32/densenet-121-tf.xml'
#model_bin = './trained_models/public/densenet-121-tf/FP32/densenet-121-tf.bin'
model_xml = './trained_models/public/mobilenet-v2-1.4-224/FP32/mobilenet-v2-1.4-224.xml'
model_bin = './trained_models/public/mobilenet-v2-1.4-224/FP32/mobilenet-v2-1.4-224.bin'
batch_size = 1
input_shape = [batch_size, 224, 224, 3]
def prepare_model(model_xml, model_bin, input_shape):
# чтение модели
model = core.read_model(model=model_xml, weights=model_bin)
# изменение размера входа в соответствии с размером пачки
model.reshape(input_shape)
# компиляция модели
compiled_model = core.compile_model(model, device)
return model, compiled_model
model, compiled_model = prepare_model(model_xml, model_bin, input_shape)
print(f'Model: {model}')
print(f'Compiled model: {compiled_model}')
Model: <Model: 'TensorFlow_Frontend_IR' inputs[ <ConstOutput: names[input] shape[1,224,224,3] type: f32> ] outputs[ <ConstOutput: names[MobilenetV2/Predictions/Reshape_1:0] shape[1,1001] type: f32> ]> Compiled model: <CompiledModel: inputs[ <ConstOutput: names[input] shape[1,224,224,3] type: f32> ] outputs[ <ConstOutput: names[MobilenetV2/Predictions/Reshape_1:0] shape[1,1001] type: f32> ]>
3. Создание очереди запросов на вывод¶
Создание очереди запросов предполагает создание объекта класса AsyncInferQueue
.
Для получения результатов выполнения запросов регистрируется функция обратного
вызова с помощью метода set_callback(...)
, которая вызывается по завершении запроса.
В качестве тестовых нейронных сетей используются модели, обеспечивающие решение
задачи классификации с большим числом категорий на наборе данных ImageNet.
На выходе этих сетей формируется вектор достоверностей принадлежности входного
изоображения каждому из допустимых 1000 классов, представленных в наборе ImageNet
(перечень классов доступен в файле ./labels/image_net_synset.txt
).
Отметим, что некоторые модели строят на выходе вектор из 1001 элемента,
поскольку учитывается класс фона (перечень классов доступен в файле
./labels/image_net_synset_first_class_base.txt
). Обработка выходного вектора
достоверностей состоит в выборе K классов, для которых получена максимальная
достоверность, и вывод названий этих классов.
max_num_requests = 4
#labels_file_name = './labels/image_net_synset.txt'
labels_file_name = './labels/image_net_synset_first_class_base.txt'
topk = 5
# функция загрузки меток классов
def load_labels_map(labels_file_name):
with open(labels_file_name, 'r') as f:
labels_map = [line.strip() for line in f]
return labels_map
# функция печати topk-классов и соответствующих достоверностей
def process_output(files, output_data, labels_file_name, topk=5):
labels_map = load_labels_map(labels_file_name)
print('Accuracy:')
for i in range(len(output_data)):
file_name = files[i]
probs_ = np.squeeze(output_data[i])
top_ind = np.argsort(probs_)[-topk:][::-1]
print(f'{file_name}')
for id_ in top_ind:
det_label = labels_map[id_] if labels_map else '#{0}'.format(id_)
print('\t{}\t{:.7f}\t{}'.format(id_, probs_[id_], det_label))
# функция обратного вызова, которая вызывается при завершении выполнения запроса
def infer_request_ended_callback(request, userdata):
output_data = request.get_output_tensor().data
print(f'User data of the request:\n{userdata}')
process_output(userdata['files'], output_data, userdata['labels_file_name'], userdata['topk'])
infer_queue = ov.AsyncInferQueue(compiled_model, max_num_requests)
infer_queue.set_callback(infer_request_ended_callback)
print(f'{infer_queue}')
<AsyncInferQueue: 4 jobs>
4. Чтение данных¶
Для чтения и подготовки изображений используется функционал библиотеки компьютерного зрения OpenCV:
cv2.read(...)
- функция, обеспечивающая загрузку изображения.cv2.resize(...)
- функция, обеспечивающая масштабирование изображения в соответствии с размерами входного тензора нейронной сети.
images_dir = '.\data'
def prepare_input(images_dir, input_image_resolution):
files = [f for f in os.listdir(images_dir) if os.path.isfile(os.path.join(images_dir, f))]
print(f'List of available images: {files}')
images = []
for file in files:
print(f'{file}')
image = cv2.imread(os.path.join(images_dir, file), cv2.IMREAD_COLOR)
plt.imshow(image)
plt.show()
print(f'\tShape of the image {file} before resize: {image.shape}')
image = cv2.resize(image, input_image_resolution)
print(f'\tShape of the image {file} after resize: {image.shape}')
images.append(image)
return files, images
print(f'Input shape {input_shape}')
files, images = prepare_input(images_dir, input_shape[1:3])
Input shape [1, 224, 224, 3] List of available images: ['ILSVRC2012_val_00000023.JPEG', 'ILSVRC2012_val_00000247.JPEG', 'ILSVRC2012_val_00018592.JPEG'] ILSVRC2012_val_00000023.JPEG
Shape of the image ILSVRC2012_val_00000023.JPEG before resize: (510, 709, 3) Shape of the image ILSVRC2012_val_00000023.JPEG after resize: (224, 224, 3) ILSVRC2012_val_00000247.JPEG
Shape of the image ILSVRC2012_val_00000247.JPEG before resize: (500, 500, 3) Shape of the image ILSVRC2012_val_00000247.JPEG after resize: (224, 224, 3) ILSVRC2012_val_00018592.JPEG
Shape of the image ILSVRC2012_val_00018592.JPEG before resize: (500, 333, 3) Shape of the image ILSVRC2012_val_00018592.JPEG after resize: (224, 224, 3)
5. Назначение входных тензоров и запуск вывода¶
Запуск вывода в асинхронном режиме предполагает получение очередного
простаивающего запроса, назначение ему входной пачки данных для вывода
и последующий асинхронный запуск. Предполагается, что количество имеющихся
в директории изображений len(files)
кратно размеру пачки данных batch_size
.
Количество пачек для обработки num_batches
является параметром. Как правило,
num_batches
больше, чем размер очереди вызовов max_num_requests
.
Если файлов недостаточно, чтобы сформировать указанное количество пачек,
то реализуется кольцевой обход по изображениям.
num_batches = 8
iteration = 0
start_batch_idx = 0
kimages = len(files) # kimages % batch_size = 0
while iteration < num_batches:
idle_id = infer_queue.get_idle_request_id() # id простаивающего запроса
if idle_id < 0:
infer_queue.wait(num_requests=1) # ожидание освобождения запроса
finish_batch_idx = start_batch_idx + batch_size
infer_queue.start_async(ov.Tensor(np.array(images[start_batch_idx:finish_batch_idx], dtype=np.float32)),
{'files': files[start_batch_idx:finish_batch_idx],
'labels_file_name': labels_file_name,
'topk': topk}) # запуск запроса в асинхонном режиме
start_batch_idx = finish_batch_idx % kimages
iteration += 1
infer_queue.wait_all()
User data of the request: {'files': ['ILSVRC2012_val_00000023.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000023.JPEG 949 0.8810009 Granny Smith 953 0.0252363 fig 956 0.0074156 jackfruit, jak, jack 957 0.0054692 custard apple 952 0.0037351 lemon User data of the request: {'files': ['ILSVRC2012_val_00000247.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000247.JPEG 14 0.8708088 junco, snowbird 15 0.0020907 indigo bunting, indigo finch, indigo bird, Passerina cyanea 21 0.0017842 water ouzel, dipper 11 0.0014236 brambling, Fringilla montifringilla 20 0.0013358 chickadee User data of the request: {'files': ['ILSVRC2012_val_00018592.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00018592.JPEG 626 0.8931450 lifeboat 511 0.0078881 container ship, containership, container vessel 629 0.0038566 liner, ocean liner 537 0.0020695 dock, dockage, docking facility 725 0.0017923 pirate, pirate ship User data of the request: {'files': ['ILSVRC2012_val_00000023.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000023.JPEG 949 0.8810009 Granny Smith 953 0.0252363 fig 956 0.0074156 jackfruit, jak, jack 957 0.0054692 custard apple 952 0.0037351 lemon User data of the request: {'files': ['ILSVRC2012_val_00000247.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000247.JPEG 14 0.8708088 junco, snowbird 15 0.0020907 indigo bunting, indigo finch, indigo bird, Passerina cyanea 21 0.0017842 water ouzel, dipper 11 0.0014236 brambling, Fringilla montifringilla 20 0.0013358 chickadee User data of the request: {'files': ['ILSVRC2012_val_00018592.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00018592.JPEG 626 0.8931450 lifeboat 511 0.0078881 container ship, containership, container vessel 629 0.0038566 liner, ocean liner 537 0.0020695 dock, dockage, docking facility 725 0.0017923 pirate, pirate ship User data of the request: {'files': ['ILSVRC2012_val_00000023.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000023.JPEG 949 0.8810009 Granny Smith 953 0.0252363 fig 956 0.0074156 jackfruit, jak, jack 957 0.0054692 custard apple 952 0.0037351 lemon User data of the request: {'files': ['ILSVRC2012_val_00000247.JPEG'], 'labels_file_name': './labels/image_net_synset_first_class_base.txt', 'topk': 5} Accuracy: ILSVRC2012_val_00000247.JPEG 14 0.8708088 junco, snowbird 15 0.0020907 indigo bunting, indigo finch, indigo bird, Passerina cyanea 21 0.0017842 water ouzel, dipper 11 0.0014236 brambling, Fringilla montifringilla 20 0.0013358 chickadee
6. Удаление рабочих объектов¶
del model
del compiled_model
del core