Лекция 3. Пример вывода обученной нейронной сети с использованием OpenVINO toolkit (Async API)¶

1. Предварительная подготовка окружения¶

1.1. Загрузка пакетов¶

In [1]:
import openvino as ov
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt

1.2. Создание и настройка окружения OpenVINO Runtime Core¶

In [2]:
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 соответственно.

In [3]:
#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 классов, для которых получена максимальная достоверность, и вывод названий этих классов.

In [4]:
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:

  1. cv2.read(...) - функция, обеспечивающая загрузку изображения.
  2. cv2.resize(...) - функция, обеспечивающая масштабирование изображения в соответствии с размерами входного тензора нейронной сети.
In [5]:
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
No description has been provided for this image
	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
No description has been provided for this image
	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
No description has been provided for this image
	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. Если файлов недостаточно, чтобы сформировать указанное количество пачек, то реализуется кольцевой обход по изображениям.

In [6]:
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. Удаление рабочих объектов¶

In [7]:
del model
del compiled_model
del core