Лекция 3. Пример вывода обученной нейронной сети с использованием TensorFlow Lite¶
1. Загрузка пакетов¶
import tensorflow.lite as tflite
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt
2. Загрузка модели¶
Далее в качестве тестовых моделей используются DenseNet-121 и MobileNetV2.
Для загрузки указанных моделей предлагается использовать инструмент
Model Downloader в составе
OpenVINO toolkit - Open Model Zoo.
Предполагается, что тестовые модели загружаются в директорию ./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 после загрузки соответствующей
модели.
Для конвертации загруженных моделей из формата фреймворка TensorFlow в формат TensorFlow Lite предлагается воспользоваться конвертером в составе Deep Learning Inference Benchmark (DLI). Чтобы воспользоваться функционалом указанного конвертера, требуется загрузить скрипты конвертера, установить необходимые пакеты для его запуска и выполнить конвертацию моделей с использованием командных строк, приведенных ниже.
# клонирование репозитория, содержащего конвертер
git clone https://github.com/itlab-vision/dl-benchmark
# переход в директорию с конвертером
cd dl-benchmark/src/model_converters/tf2tflite
# создание витуальной среды и установка необходимых пакетов в этой среде
conda create -n tflite_converter python=3.9 -y
conda activate tflite_converter
pip install tensorflow==2.14.0
pip install tf-keras==2.15.0
pip install onnx-tf==1.10.0
pip install tensorflow-probability==0.22.0
# запуск конвертера (${MODELS_PATH} - путь до директории, куда были загружены модели)
python tflite_converter.py \
--model ${MODELS_PATH}/trained_models/public/densenet-121-tf/densenet-121.savedmodel \
--source-framework tf
python tflite_converter.py \
--model ${MODELS_PATH}/trained_models/public/mobilenet-v2-1.4-224/mobilenet_v2_1.4_224_frozen.pb \
--source-framework tf --input-names input
conda deactivate
# Путь до модели DenseNet-121
model_path = './trained_models/public/densenet-121-tf/densenet-121.tflite'
# Путь до модели MobileNet-v2
# model_path = './trained_models/public/mobilenet-v2-1.4-224/mobilenet_v2_1.4_224_frozen.tflite'
# Размер пачки данных, обрабатываемых за проход
batch_size = 2
# Размер обрабатываемого входного тензора
input_shape = [batch_size, 224, 224, 3]
# Функция печати информации о входах модели
def print_input_details(model):
input_details = model.get_input_details()
for idx in range(len(input_details)):
print(f'\tInput {idx}: {input_details[idx]}')
# Загрузка модели
model = tflite.Interpreter(model_path=model_path)
print('Input details for the model (before input reshape):')
print_input_details(model)
# Изменение размеров входного тензора (тестовые модели имеют только один
# вход - input_details[0])
input_details = model.get_input_details()
model.resize_tensor_input(input_details[0]['index'], input_shape)
print('Input details for the model (after input reshape):')
print_input_details(model)
# Выделение памяти для хранения тензоров
model.allocate_tensors()
Input details for the model (before input reshape): Input 0: {'name': 'input_1', 'index': 0, 'shape': array([ 1, 224, 224, 3]), 'shape_signature': array([ -1, 224, 224, 3]), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}} Input details for the model (after input reshape): Input 0: {'name': 'input_1', 'index': 0, 'shape': array([ 2, 224, 224, 3]), 'shape_signature': array([ -1, 224, 224, 3]), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}}
3. Загрузка данных¶
Для чтения и подготовки изображений используется функционал библиотеки компьютерного зрения OpenCV:
cv2.read(...)
- функция, обеспечивающая загрузку изображения в объект типаMat
.cv2.resize(...)
- функция, обеспечивающая масштабирование изображения в соответствии с размерами входного тензора нейронной сети.
Наряду с этим, для каждого загруженного изображения выполняется вычитание средней
интенсивности mean
, посчитанной по тренировочной выборке, и деление на среднеквадратическое
отклонение scale
с использованием перегруженных операций вычитания и деления
для объектов класса Mat
.
images_dir = '.\data'
# Параметры mean и scale для модели DenseNet-121
mean = [123.68, 116.78, 103.94]
scale = [58.395, 57.12, 57.375]
# Параметры mean и scale для модели MobileNet-v2
# mean = [127.5, 127.5, 127.5]
# scale = [127.5, 127.5, 127.5]
# Функция загрузки и предварительной обработки изображений
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}')
image = (image - mean) / scale
images.append(image)
return files, images
files, images = prepare_input(images_dir, input_shape[1: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)
4. Установка входных тензоров и запуск вывода¶
При установке входных тензоров используются первые batch_size
изображений
из набора загруженных изображений.
model.set_tensor(input_details[0]['index'], np.array(images[0:batch_size], dtype=np.float32))
model.invoke()
5. Обработка результатов вывода¶
В качестве тестовых нейронных сетей используются модели, обеспечивающие решение
задачи классификации с большим числом категорий на наборе данных ImageNet.
На выходе этих сетей формируется вектор достоверностей принадлежности входного
изоображения каждому из допустимых 1000 классов, представленных в наборе ImageNet
(перечень классов доступен в файле ./labels/image_net_synset.txt
).
Отметим, что некоторые модели строят на выходе вектор из 1001 элемента,
поскольку учитывается класс фона (перечень классов доступен в файле
./labels/image_net_synset_first_class_base.txt
).
Обработка выходного вектора достоверностей состоит в выборе topk классов, для которых получена максимальная достоверность, и вывод названий этих классов.
# Файл меток для модели DenseNet-121
labels_file_name = './labels/image_net_synset.txt'
# Файл меток для модели MobileNet-v2
# 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)
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))
# Получение выходов модели
output_details = model.get_output_details()
print('Output details:')
for idx in range(len(output_details)):
print(f'\tOutput {idx}: {output_details[idx]}')
# Получение тензора с первого выхода
output_data = model.get_tensor(output_details[0]['index'])
# Обработка полученного тензора
process_output(files, output_data, labels_file_name, topk)
Output details: Output 0: {'name': 'Identity', 'index': 623, 'shape': array([ 2, 1000]), 'shape_signature': array([ -1, 1000]), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}} ILSVRC2012_val_00000023.JPEG 948 0.9525880 Granny Smith 950 0.0132317 orange 951 0.0123400 lemon 954 0.0028143 banana 719 0.0020238 piggy bank, penny bank ILSVRC2012_val_00000247.JPEG 13 0.9847551 junco, snowbird 19 0.0068679 chickadee 10 0.0034510 brambling, Fringilla montifringilla 20 0.0015685 water ouzel, dipper 14 0.0012343 indigo bunting, indigo finch, indigo bird, Passerina cyanea