Практическая работа №2. Вывод глубокой модели средствами Apache TVM. Анализ и сравнение производительности вывода¶

1. Цели и задачи работы¶

Цель работы -- изучить программный интерфейс для вывода глубоких нейросетевых моделей, а также выполнить анализ и сравнение производительности вывода с использованием Apache TVM и некоторых других изученных фреймворков на процессорах архитектуры RISC-V.

Достижение указанной цели предполагает решение следующих задач:

  1. Формулировка задачи классификации изображений.
  2. Подготовка тестовых моделей для решения поставленной задачи. Изучение архитектуры выбранных моделей.
  3. Изучение программного интерфейса Graph Execution (GE API) для вывода глубоких моделей фреймворка Apache TVM.
  4. Программная реализация вывода выбранных моделей с использованием GE API фреймворка Apache TVM. Валидация корректности разработанной программной реализации.
  5. Программная реализация вывода выбранных моделей с использованием других фреймворков, которые могут быть запущены на процессорах архитектуры RISC-V. В данной работе в качестве примера такого фреймворка используется TensorFlow Lite, поскольку на момент подготовки практической работы он оптимизирован под RISC-V-архитектуры.
  6. Анализ качества решения задачи классификации с помощью выбранного набора моделей. При этом вывод запускается с использованием Apache TVM и TensorFlow Lite.
  7. Анализ производительности вывода и определение оптимальных параметров запуска вывода на устройстве с процессором RISC-V (размер пачки входных данных, количество потоков, другие при наличии).

2. Постановка задачи классификации изображений¶

Задача классификации изображений состоит в том, чтобы поставить в соответствие изображению класс объектов, содержащихся на этом изображении. Формализуем постановку задачи.

Исходное изображение, как правило, представляется набором интенсивностей пикселей

$$I=(I_{ij}^j)_{0 \le i < w, 0 \le j < h, 0 \le k < 3},$$

где $w$ и $h$ -- ширина и высота изображения, $k$ -- количество каналов, а $I_{ij}^j$ -- значение интенсивности пикселя с пространственными координатами $(i, j)$ по каналу $k$.

Предполагается, что определено множество наблюдаемых классов объектов на изображении:

$$C=\{0, 1,...,𝑁−1\}.$$

Множество идентификаторов классов однозначно соответствует множеству названий классов. Таким образом, задача классификации изображений состоит в том, чтобы каждому изображению поставить в соответствие класс, которому оно принадлежит:

$$\phi : I \rightarrow{} C.$$

3. Тестовые модели¶

3.1. Нейросетевая модель DenseNet-121¶

Модель DenseNet-121 реализуется посредством формирования последовательности "плотных" блоков (dense block). Каждый "плотный" блок содержит набор сверточных слоев. Вход каждого следующего слоя -- конкатенация карт признаков, построенных на предыдущих слоях.

dense_block

Общая структура моделей DenseNet имеет следующий вид:

densenet

Отметим, что слои между двумя смежными плотными блоками называются переходными слоями, они в отличие от плотных блоков изменяют пространственные размеры карты признаков.

Структура "плотных" блоков сети DenseNet-121:

  1. Dense Block 1: 6 x [Conv 1x1, Conv 3x3]
  2. Dense Block 2: 12 x [Conv 1x1, Conv 3x3]
  3. Dense Block 3: 24 x [Conv 1x1, Conv 3x3]
  4. Dense Block 4: 16 x [Conv 1x1, Conv 3x3]

Подробнее архитектура DenseNet-121 разобрана по ссылке.

3.2. Нейросетевая модель MobileNet-v3-large¶

Модель MobileNet-v3-large является представителем группы моделей MobileNetV3. Архитектура моделей MobileNetV3 содержит последовательность типовых блоков, которые сочетают идеи построения стандартного блока моделей MobileNetV2 и SE-блока. Структура блока приведена ниже (Dwise -- свертка, отделимая по глубине; NL -- нелинейная функция).

mobilenet_v3_bneck

Архитектура сети MobileNet-v3-large включает 8 таких блоков. Общая структура модели приведена ниже в виде таблицы, где атрибут Input означает размеры входной карты признаков, Operator -- тип оператора (bneck соответствует приведенному выше блоку), #out -- число каналов выходной карты признаков, SE -- наличие/отсутствие SE-блока, NL -- тип используемой нелинейности (HS = h-swish, RE = ReLU), s -- шаг (stride), а NBN соответствует отсутствию нормализации по пачке данных.

mobilenet_v3_large

4. Подготовка тестовых моделей¶

Предварительную подготовку моделей рекомендуется полностью выполнять на стороннем узле с архитектурой x86, поскольку конвертация и компиляция моделей может занимать длительное время.

4.1. Загрузка моделей¶

Далее в работе используются обученные модели, которые опубликованы в OpenVINO - Open Model Zoo Repository (OMZ). Для загрузки моделей можно использовать инструмент Model Downloader в составе пакета openvino-dev. Ниже приведены команды для установки данного пакета и загрузки моделей DenseNet-121 и MobileNet-v3-large. DenseNet-121 загружается в формате .saved_model исходного фреймворка TensorFlow, который далее может быть сконвертирован в интересующие форматы. MobileNet-v3-large загружается в формате *.h5. Чтобы получить модель в .saved_model, достаточно запустить Model Converter в составе openvino-dev.

conda create -n openvino_converter python==3.10
conda activate openvino_converter
pip install --upgrade pip
pip install openvino-dev
pip install openvino-dev[tensorflow2]

cd <work_dir>
omz_downloader --name densenet-121-tf

omz_downloader --name mobilenet-v3-large-1.0-224-tf
omz_converter --name mobilenet-v3-large-1.0-224-tf

conda deactivate

В результате выполнения указанной последовательности команд в директории <work_dir> создается набор вложенных директорий:

  • public/densenet-121-tf содержит файлы модели в формате *.savedmodel (TensorFlow).
  • public/mobilenet-v3-large-1.0-224-tf содержит файлы модели в формате *.h5 и *.savedmodel (TensorFlow).

4.2. Конвертация моделей в формат Apache TVM¶

Тестовые модели хранятся в формате *.savedmodel фреймворка TensorFlow. Для запуска этих моделей средствами Apache TVM разработчики рекомендуют сконвертировать их в формат ONNX с использованием пакета tf2onnx, после чего сконвертировать в формат .json+.params и скомпилировать в исполняемый модуль .tar с помощью внутренних средств Apache TVM, которые были рассмотрены в лекции 6. Для упрощения этапа подготовки моделей предлагается воспользоваться обертками над конвертером и компилятором моделей, реализованными в системе бенчмаркинга Deep Learning Inference Benchmark (DLI). Ниже приведена последовательность команд, которая предусматривает конвертацию загруженных моделей с разными размерами входных пачек данных, а также их компиляцию с разными значениями уровня оптимизации opt_level.

sudo apt update && sudo apt upgrade
sudo apt install gcc g++ llvm cmake

conda create --no-default-packages -n tvm_converter_riscv -y python==3.8
conda activate tvm_converter_riscv
pip install numpy opencv-python scipy
conda install -c conda-forge -y gcc=12.1.0
conda install -c conda-forge -y gxx_linux-64
pip install traitlets==5.9.0 decorator attrs typing-extensions psutil scipy pybind11

cd <work_dir>
git clone --recursive https://github.com/apache/tvm -b v0.17.0 tvm
cd tvm
mkdir build
cd build
cmake -DUSE_LLVM=ON ..
make -j
cd ../python
python setup.py install --user

cd <work_dir>
git clone https://github.com/itlab-vision/dl-benchmark
cd dl-benchmark/src/model_converters/tvm_converter

batch_sizes=(1 2 4 8 16 32)
opt_levels=(0 1 2 3)
dir_names=('densenet-121-tf' 'mobilenet-v3-large-1.0-224-tf')
model_names=('densenet-121-tf' 'mobilenet_v3_large_224_1.0_float')
target='llvm -mtriple=riscv64-unknown-linux-gnu -mcpu=generic-rv64 -mabi=lp64d -mattr=+64bit,+m,+a,+f,+d,+c'
src_framework='onnx'
width=224
height=224
nchannels=3

index=0
for model_name in ${model_names[@]}
do
    dir_name=${dir_names[$index]}
    for bs in ${batch_sizes[@]}
    do
        echo "Batch size: ${bs}"
        for ol in ${opt_levels[@]}
        do
            echo "    opt_level: ${ol}"
            python tvm_converter.py --model_name ${model_name} \
                                --model ../../../../public/${dir_name}/${model_name}.onnx \
                                --source_framework ${src_framework} \
                                --input_shape ${bs} ${width} ${height} ${nchannels} \
                                --batch_size ${bs} \
                                --output_dir ../../../../public/${dir_name}/tvm_riscv/bs_${bs}
            python tvm_compiler.py --mod ../../../../public/${dir_name}/tvm_riscv/bs_${bs}/${model_name}.json \
                               --params ../../../../public/${dir_name}/tvm_riscv/bs_${bs}/${model_name}.params \
                               --target "${target}" --opt_level ${ol} --lib_name ${model_name}_${ol}.tar \
                               --output_dir ../../../../public/${dir_name}/tvm_riscv/bs_${bs}/
        done
    done
    index=$(( index + 1 ))
done

conda deactivate

В результате выполнения приведенных команд в директории каждой модели создается директория tvm_riscv, содержащая набор вложенных директорий вида bs_${bs}, в которых находятся сконвертированные модели для разных размеров входной пачки данных (${bs} означает размер пачки). Также каждая директория bs_${bs} содержит набор исполняемых модулей ${model_name}_${ol}.tar, соответствующих скомпилированной с разными уровнями оптимизации opt_level модели (${model_name} -- название модели, ${ol} -- уровень оптимизации opt_level).

Примечание: для маломощных устройств, к каковым в настоящее время относится RISC-V, модели имеет смысл компилировать на сторонних узлах, указав корректное значение целевой платформы target. При запуске на маломощных устройствах Apache TVM позволяет устанавливать и использовать только runtime-модуль, с помощью которого можно загружать и запускать скомпилированные модели.

4.3. Конвертация моделей в формат TensorFlow Lite¶

Чтобы сконвертировать выбранную модель в формат TensorFlow Lite, можно воспользоваться конвертером из репозитория системы бенчмаркинга DLI. Далее используется tf2tflite-конвертер. Ниже приведена последовательность команд для установки необходимого окружения и запуска указанного конвертера моделей из формата библиотеки TensorFlow в TensorFlow Lite.

conda create -n tflite_converter python==3.9
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

cd <work_dir>
git clone https://github.com/itlab-vision/dl-benchmark
cd dl-benchmark/src/model_converters/tf2tflite

python tflite_converter.py \
     --model-path <work_dir>/public/densenet-121-tf/densenet-121.savedmodel \
     --source-framework tf

python tflite_converter.py \
       --model-path <work_dir>/public/mobilenet-v3-large-1.0-224-tf/mobilenet_v3_large_224_1.0_float.savedmodel/ \
       --source-framework tf --input-shapes [1,224,224,3]

conda deactivate

Примечание: для запуска конвертера требуется версия пакета numpy не ниже 1.23.5.

В результате успешного выполнения приведенной последовательности команд в соответствующих директориях моделей сформируются файлы <model_name>.tflite.

5. Подготовка витуальной среды для запуска экспериментов¶

Для проведения экспериментов необходимо подготовить виртуальную среду с установленными пакетами opencv для загрузки и предварительной обработки изображений, apache-tvm для вывода с использованием Apache TVM, tensorflow или tensorflow-runtime для вывода средствами TensorFlow Lite. Ниже приведена соответствующая последовательность команд.

python3 -m venv ~/riscv-envs/02_practice_env
source ~/riscv-envs/02_practice_env/bin/activate

# Сборка и установка Apache TVM
pip install -U pip setuptools
pip install six wheel numpy
cd <work_dir>
git clone --recursive https://github.com/apache/tvm
cd tvm
mkdir build
cd build
cmake -DCMAKE_SYSTEM_NAME=Linux \
      -DCMAKE_SYSTEM_VERSION=1 \
      -DCMAKE_C_COMPILER=gcc \
      -DCMAKE_CXX_COMPILER=g++ \
      ..
make runtime -j
export TVM_HOME=<work_dir>/tvm/
export PYTHONPATH=$TVM_HOME/python:${PYTHONPATH}

# Сборка и установка OpenCV
cd <work_dir>
git clone --depth 1 https://github.com/opencv/opencv/
cd opencv
mkdir build
cd build
cmake -G "Unix Makefiles" \
      -DCMAKE_INSTALL_PREFIX=../opencv_install \
      -DCMAKE_BUILD_TYPE=Release \
      -DBUILD_LIST=core,imgcodecs,dnn,python3 \
      -DBUILD_opencv_python3=ON \
      -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
      -DBUILD_NEW_PYTHON_SUPPORT=ON \
      ..
make -j
make install
cd ../opencv_install
source ./bin/setup_vars_opencv4.sh

# Загрузка Deep Learning Inference Benchmark
cd <work_dir>
git clone https://github.com/itlab-vision/dl-benchmark

# Сборка и установка TensorFlow Lite
python3 -m pip install pybind11 
git clone https://github.com/tensorflow/tensorflow -b v2.14.0
cd tensorflow
git apply <work_dir>/dl-benchmark/src/build_scripts/tflite/riscv64/riscv64.pip.patch
cd tensorflow/lite/tools/pip_package/
BUILD_NUM_JOBS=2 bash build_pip_package_with_cmake.sh rv64gc
cp gen/tflite_pip/python3/dist/tflite_runtime-2.14.0-cp311-cp311-linux_riscv64.whl ../../../../../
cd ../../../../../
python3 -m pip install tflite_runtime-2.14.0-cp311-cp311-linux_riscv64.whl

# Установка Matplotlib
pip install matplotlib

deactivate

Примечание: чтобы проверить корректность установки пакетов, можно выполнить команды, приведенные ниже. В комментариях после каждой команды приведены результаты их выполнения.

python3 -c "import tvm; print(tvm.__version__)"
# 0.18.dev0
python3 -c "import cv2; print(cv2.__version__)"
# 4.10.0-dev
python3 -c "import tflite_runtime; print(tflite_runtime.__version__)"
# 2.14.0

Чтобы созданная виртуальная среда 02_practice_env была доступна для последующего запуска в ней вывода из Jupiter Notebook, достаточно в ней выполнить последовательность команд, приведенную ниже.

source ~/riscv-envs/02_practice_env/bin/activate
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. "$HOME/.cargo/env"
pip install jupyterlab
pip install notebook

python -m ipykernel install --user --name 02_practice_env \
                            --display-name "Python (02_practice_env)"
deactivate

Примечание: директория 02_Practice_envs текущей практики содержит перечень установленных в каждой виртуальной среде пакетов с указанием их версий.

6. Программная реализация вспомогательных функций¶

6.1. Функции вычисления показателей производительности¶

In [1]:
# функция удаления заведомо некорректных времен
def delete_incorrect_time(times, min_correct_time=0.0):
    valid_times = []
    for i in range(len(times)):
        if times[i] >= min_correct_time:
            valid_times.append(times[i])
    return valid_times

# функция удаления времен, выходящих для пределы трех стандартных
# среднеквадратических отклонений (удаление выбросов)
def three_sigma_rule(times):
    average_time = np.mean(times)
    sigm = np.std(times)
    upper_bound = average_time + (3 * sigm)
    lower_bound = average_time - (3 * sigm)
    valid_times = []
    for i in range(len(times)):
        if lower_bound <= times[i] <= upper_bound:
            valid_times.append(times[i])
    return valid_times

# функция вычисления латентности - медианы набора корректных времен
def calculate_latency(times):
    latency = np.median(times)
    return latency

# функция вычисления метрики FPS - отношение произведения количества итераций
# и размера пачки данных к общему времени вывода
def calculate_fps(iter_num, batch_size, total_time):
    return iter_num * batch_size / total_time

# общая функция вычисления показателей производительности
def calculate_performance_metrics(times, batch_size, min_correct_time=0.0):
    valid_times = delete_incorrect_time(times, min_correct_time)
    valid_times = three_sigma_rule(valid_times)
    latency = calculate_latency(valid_times)
    fps = calculate_fps(len(valid_times), batch_size, sum(valid_times))
    return latency, fps

6.2. Функции обработки выхода классификационной сети¶

In [2]:
# функция загрузки меток классов
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))

6.3. Чтение и предварительная обработка данных¶

Для чтения и подготовки изображений используется функционал библиотеки компьютерного зрения OpenCV:

  1. cv2.imread(...) -- функция, обеспечивающая загрузку изображения.
  2. cv2.resize(...) -- функция, обеспечивающая масштабирование изображения в соответствии с размерами входного тензора нейронной сети.

В качестве данных для проверки корректности программной реализации вывода и последующего анализа производительности предлагается использовать набор изображений из валидационной части набора данных ImageNet, которая является открытой. Она содержит 50 000 изображений естественного мира, принадлежащих 1 000 классам. Далее принимается, что избранные изображения находятся в директории ../data. Примечание: по указанной ссылке скачивается архив с полным набором данных (~150 ГБ данных), чтобы загрузить только валидационную выборку можно воспользоваться неофициальными ресурсами, которые выдаются поисковой системой (например, можно использовать эту ссылку).

In [3]:
import cv2
import os
import numpy as np
from matplotlib import pyplot as plt

# размер пачки данных
batch_size = 2
# размеры входного тензора нейронной сети
input_shape = [batch_size, 224, 224, 3]
# директория, содержащая данные для проверки корректности и анализа
# производительности вывода (должна содержать не менее, чем batch_size
# изображений)
images_dir = '../data'


def prepare_input(images_dir, input_image_resolution, mean=(0, 0, 0),
                  scale=(1, 1, 1), bgr_to_rgb=False, show=False):
    files = [f for f in os.listdir(images_dir) if os.path.isfile(os.path.join(images_dir, f))]
    print(f'Number of available images: {len(files)}')
    images = []
    for file in files:
        image = cv2.imread(os.path.join(images_dir, file), cv2.IMREAD_COLOR)
        if bgr_to_rgb == True:
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        if show == True:
            plt.imshow(image)
            plt.show()
        image = cv2.resize(image, input_image_resolution)
        image = (image - mean) / scale
        images.append(image)
    return files, images

7. Программная реализация вывода средствами Apache TVM¶

Программная реализация вывода средствами фреймворка Apache TVM выполняется с использованием Graph Execution API (GE API).

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

In [4]:
import tvm
from tvm.contrib import graph_executor

7.2. Загрузка и предварительная обработка данных¶

In [5]:
# Параметры предобработки изображений для DenseNet-121
mean = (123.68, 116.78, 103.94)
scale = (58.395, 57.12, 57.375)
bgr_to_rgb = True

# Параметры предобработки изображений для MobileNet-v3-large
# mean = (0, 0, 0)
# scale = (1, 1, 1)
# bgr_to_rgb = True

print(f'Input shape: {input_shape}')
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
Input shape: [2, 224, 224, 3]
Number of available images: 64

7.3. Загрузка скомпилированной модели¶

In [6]:
# уровень оптимизации скомпилированной модели (для проверки корректности
# реализации вывода устанавливается равным 1)
opt_level = 1
# путь до скомпилированной модели
model_tvm = f'../public/densenet-121-tf/tvm_riscv/bs_{batch_size}/densenet-121-tf_{opt_level}.tar'
#model_tvm = f'../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_{batch_size}/mobilenet_v3_large_224_1.0_float_{opt_level}.tar'


def load_model(model_tvm):
    print(f'Load model: {model_tvm}')
    tvm_lib = tvm.runtime.load_module(model_tvm)

    # устройство для запуска
    dev = tvm.cpu(0)
    # создание исполняемого модуля для устройства
    module = graph_executor.GraphModule(tvm_lib["default"](dev))
    return module


module = load_model(model_tvm)
Load model: ../public/densenet-121-tf/tvm_riscv/bs_2/densenet-121-tf_1.tar

7.4. Вывод нейронной сети¶

In [7]:
from time import time

# количество повторений вывода для заданной пачки данных
iter_num = 3
# файл меток классов
labels_file_name = '../labels/image_net_synset.txt'
# параметр метрики top-k для оценки качества работы модели
topk = 5
# название входа
input_name = 'input_1'


# функция многократного запуска вывода для фиксированного размера пачки
# и сбор результатов работы сети и времени работы каждого запроса на вывод
def inference_tvm(iter_num, batch_size, files, images, module, input_name):
    results = dict()
    times = []
    istart = 0
    ifinish = batch_size
    for iteration in range(iter_num):
        # установка входного тензора для вывода
        module.set_input(input_name, np.array(images[istart:ifinish], dtype=np.float32))

        # запуск вывода
        ts = time()
        module.run()
        tf = time()

        # сохранение результатов вывода
        results[iteration] = (files[istart:ifinish], module.get_output(0).numpy())
        times.append(tf - ts)
        
        istart = ifinish
        ifinish = (istart + batch_size) % (len(images) + 1)
        if istart > ifinish:
            istart = 0
            ifinish = batch_size

    return results, times

# функция запуска теста и вычисления показателей производительности вывода
def inference_tvm_perf(iter_num, batch_size, files, images, module, input_name):
    results, times = inference_tvm(iter_num, batch_size, files, images,
                                   module, input_name)
    latency, fps = calculate_performance_metrics(times, batch_size)
    return results, latency, fps


results, latency, fps = inference_tvm_perf(iter_num, batch_size, files, images,
                                           module, input_name)
print('Performance metrics:')
print(f'\tLatency: {latency:.3f} s\n\tFPS:     {fps:.2f} fps')

print('Results:')
for iteration in range(len(results)):
    process_output(results[iteration][0], results[iteration][1], labels_file_name, topk)
Performance metrics:
	Latency: 2.849 s
	FPS:     0.70 fps
Results:
ILSVRC2012_val_00000023.JPEG
	948	0.9939488	Granny Smith
	951	0.0029237	lemon
	950	0.0014410	orange
	954	0.0006841	banana
	952	0.0001481	fig
ILSVRC2012_val_00000247.JPEG
	13	0.9960424	junco, snowbird
	10	0.0016738	brambling, Fringilla montifringilla
	14	0.0014138	indigo bunting, indigo finch, indigo bird, Passerina cyanea
	19	0.0004691	chickadee
	18	0.0000661	magpie
ILSVRC2012_val_00018592.JPEG
	625	0.9841733	lifeboat
	540	0.0021340	drilling platform, offshore rig
	628	0.0018786	liner, ocean liner
	913	0.0017149	wreck
	724	0.0016311	pirate, pirate ship
ILSVRC2012_val_00019000.JPEG
	752	0.9872146	racket, racquet
	852	0.0084676	tennis ball
	429	0.0032796	baseball
	981	0.0004642	ballplayer, baseball player
	890	0.0002705	volleyball
ILSVRC2012_val_00019001.JPEG
	532	0.9815800	dining table, board
	762	0.0055748	restaurant, eating house, eating place, eatery
	765	0.0032743	rocking chair, rocker
	559	0.0011969	folding chair
	495	0.0011608	china cabinet, china closet
ILSVRC2012_val_00019002.JPEG
	218	0.8885517	Welsh springer spaniel
	156	0.0818037	Blenheim spaniel
	217	0.0074693	English springer, English springer spaniel
	220	0.0061150	Sussex spaniel
	157	0.0058036	papillon

8. Программная реализация вывода средствами TensorFlow Lite¶

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

In [8]:
try:
    import tflite_runtime.interpreter as tflite
except ModuleNotFoundError:
    import tensorflow.lite as tflite

8.2. Загрузка и предварительная обработка данных¶

In [9]:
# Параметры предобработки изображений для DenseNet-121
mean = (123.68, 116.78, 103.94)
scale = (58.395, 57.12, 57.375)
bgr_to_rgb = True

# Параметры предобработки изображений для MobileNet-v3-large
# mean = (0, 0, 0)
# scale = (1, 1, 1)
# bgr_to_rgb = True

print(f'Input shape: {input_shape}')
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
Input shape: [2, 224, 224, 3]
Number of available images: 64

8.3. Загрузка модели¶

In [10]:
# путь до файла модели в формате TensorFlow Lite
model_tflite = '../public/densenet-121-tf/densenet-121.tflite'
# model_tflite = '../public/mobilenet-v3-large-1.0-224-tf/mobilenet_v3_large_224_1.0_float.tflite'


# функция печати информации о входах модели
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_interpreter = tflite.Interpreter(model_path=model_tflite)
print('Input details for the model (before input reshape):')
print_input_details(model_interpreter)

# изменение размеров входного тензора (тестовые модели имеют только один
# вход - input_details[0])
input_details = model_interpreter.get_input_details()
model_interpreter.resize_tensor_input(input_details[0]['index'], input_shape)
print('Input details for the model (after input reshape):')
print_input_details(model_interpreter)

# выделение памяти для хранения тензоров
model_interpreter.allocate_tensors()
Input details for the model (before input reshape):
	Input 0: {'name': 'input_1', 'index': 0, 'shape': array([  1, 224, 224,   3], dtype=int32), 'shape_signature': array([ -1, 224, 224,   3], dtype=int32), '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], dtype=int32), 'shape_signature': array([ -1, 224, 224,   3], dtype=int32), 'dtype': <class 'numpy.float32'>, 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}}
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.

8.4. Установка входных тензоров и запуск вывода¶

In [11]:
from time import time

# количество повторений вывода для заданной пачки данных
iter_num = 3
# файл меток классов
labels_file_name = '../labels/image_net_synset.txt'
# параметр метрики top-k для оценки качества работы модели
topk = 5


# функция многократного запуска вывода для фиксированного размера пачки
# и сбор результатов работы сети и времени работы каждого запроса на вывод
def inference_tflite(iter_num, batch_size, files, images, model):
    results = dict()
    times = []
    istart = 0
    ifinish = batch_size
    for iteration in range(iter_num):
        model.set_tensor(input_details[0]['index'], np.array(images[istart:ifinish], dtype=np.float32))

        ts = time()
        model.invoke()
        tf = time()

        output_details = model.get_output_details()
        results[iteration] = (files[istart:ifinish], 
                              model.get_tensor(output_details[0]['index']).copy())
        times.append(tf - ts)
        
        istart = ifinish
        ifinish = (istart + batch_size) % (len(images) + 1)
        if istart > ifinish:
            istart = 0
            ifinish = batch_size

    return results, times

# функция запуска теста и вычисления показателей производительности вывода
def inference_tflite_perf(iter_num, batch_size, files, images, model_interpreter):
    results, times = inference_tflite(iter_num, batch_size, files, images, model_interpreter)
    latency, fps = calculate_performance_metrics(times, batch_size)
    return results, latency, fps


results, latency, fps = inference_tflite_perf(iter_num, batch_size, files, images,
                                              model_interpreter)
print('Performance metrics:')
print(f'\tLatency: {latency:.3f} s\n\tFPS:     {fps:.2f} fps')

print('Results:')
for iteration in range(len(results)):
    process_output(results[iteration][0], results[iteration][1], labels_file_name, topk)
Performance metrics:
	Latency: 3.194 s
	FPS:     0.63 fps
Results:
ILSVRC2012_val_00000023.JPEG
	948	0.9939487	Granny Smith
	951	0.0029237	lemon
	950	0.0014410	orange
	954	0.0006841	banana
	952	0.0001481	fig
ILSVRC2012_val_00000247.JPEG
	13	0.9960377	junco, snowbird
	10	0.0016737	brambling, Fringilla montifringilla
	14	0.0014138	indigo bunting, indigo finch, indigo bird, Passerina cyanea
	19	0.0004691	chickadee
	18	0.0000661	magpie
ILSVRC2012_val_00018592.JPEG
	625	0.9841737	lifeboat
	540	0.0021340	drilling platform, offshore rig
	628	0.0018787	liner, ocean liner
	913	0.0017149	wreck
	724	0.0016311	pirate, pirate ship
ILSVRC2012_val_00019000.JPEG
	752	0.9872143	racket, racquet
	852	0.0084676	tennis ball
	429	0.0032796	baseball
	981	0.0004642	ballplayer, baseball player
	890	0.0002705	volleyball
ILSVRC2012_val_00019001.JPEG
	532	0.9815800	dining table, board
	762	0.0055748	restaurant, eating house, eating place, eatery
	765	0.0032743	rocking chair, rocker
	559	0.0011970	folding chair
	495	0.0011608	china cabinet, china closet
ILSVRC2012_val_00019002.JPEG
	218	0.8885490	Welsh springer spaniel
	156	0.0818038	Blenheim spaniel
	217	0.0074692	English springer, English springer spaniel
	220	0.0061150	Sussex spaniel
	157	0.0058036	papillon

9. Анализ качества решения задачи классификации¶

Инструменты для сбора показателей качества. Для анализа качества решения задачи классификации с использованием моделей, загруженных из OpenVINO - Open Model Zoo Repository (OMZ), а также некоторых других моделей, которые обучены средствами поддерживаемых фреймворков, можно воспользоваться инструментом Accuracy Checker в составе репозитория OMZ. Указанный инструмент позволяет запускать модели в разных форматах с использованием разных фреймворков глубокого обучения на широко известных наборах данных и собирать общепринятные показатели качества решения классических задач компьютерного зрения (computer vision), обработки естественного языка (natural language processing) и аудиоанализа.

Отметим, что запуск моделей в формате TensorFlow Lite поддерживается в рамках основного репозитория OMZ, в то время как для моделей в формате TVM имеется сторонний форки репозитория OMZ(ссылка на старую версию OMZ, ссылка на более новую версию OMZ).

Для упрощения массовых запусков инструмента Accuracy Checker для набора моделей предлагается использовать обертку, разработанную в рамках проекта Deep Learning Inference Benchmark (DLI), которая доступна по ссылке.

Набор данных. Измерение качества предполагается выполнять на валидационной части набора данных ImageNet, которая является открытой. Она содержит 50 000 изображений естественного мира, принадлежащих 1 000 классам. Примечание: по указанной ссылке скачивается архив с полным набором данных (~150 ГБ данных), чтобы загрузить только валидационную выборку можно воспользоваться неофициальными ресурсами, которые выдаются поисковой системой (например, можно использовать эту ссылку).

Показатели качества. Для анализа качества решения задачи классификации изображений используются общепринятые метрики точности top-1 и top-5. Точность top-k (top-k accuracy) -- отношение числа правильно проклассифицированных изображений к общему их количеству. На выходе классификационной нейронной сети имеется вектор достоверности принадлежности изображения каждому из допустимых классов. В случае top-1 изображение считается проклассифицированным правильно, если искомому классу соответствует максимальное значение достоверности, а в случае top-5 -- если искомому классу соответствует одно из пяти наибольших значений достоверностей.

Последовательность подготовки окружения и сбора показателей качества. Чтобы собрать значения показателей качества классификации с использованием описанных инструментов, необходимо выполнить следующую последовательность действий.

  1. Загрузить форк репозитория OMZ и установить инструмент Accuracy Checker для валидации моделей Apache TVM.

    python3 -m venv ~/riscv-envs/accuracy_checker_tvm
    source ~/riscv-envs/accuracy_checker_tvm/bin/activate
    pip install --upgrade pip
    
    # Библиотека OpenCV собрана, достаточно установить переменные окружения
    cd <work_dir>/opencv/opencv_install
    source ./bin/setup_vars_opencv4.sh
    cd ../..
    
    pip install -U setuptools
    pip install -U cmake
    pip install scikit-build
    pip install scikit-image
    pip install cython
    pip install docker
    pip install six
    
    # Загрузка и сборка Accuracy Checker
    mkdir open_model_zoo_tvm
    cd open_model_zoo_tvm/
    git clone https://github.com/a-sidorova/open_model_zoo
    cd open_model_zoo/
    git checkout tvm
    cd ./tools/accuracy_checker
    python3 -m pip install .
    
    # Библиотека TVM собрана, достаточно установить переменные окружения
    export TVM_HOME=<work_dir>/tvm/
    export PYTHONPATH=$TVM_HOME/python:${PYTHONPATH}
    
    # Установка пути до пакетов Accuracy Checker
    export PYTHONPATH=${PYTHONPATH}:~/riscv-envs/accuracy_checker_tvm/lib/python3.11/site-packages
    
    echo $PYTHONPATH
    # Примерный перечень путей в переменной окружения:
    # <work_dir>/tvm/python
    # <work_dir>/opencv/opencv_install/bin/../lib/python3.11/site-packages
    # ~/riscv-envs/accuracy_checker_tvm/lib/python3.11/site-packages
    
    deactivate
    

    После установки выполнение команды accuracy_check -h должно приводить к выводу справочной информации о параметрах запуска инструмента.

  2. Загрузить репозиторий OMZ и установить инструмент Accuracy Checker для валидации моделей TensorFlow Lite. Ниже приведена соответствующая последовательность команд. Подробнее установка описана здесь.

    python3 -m venv ~/riscv-envs/accuracy_checker_tflite
    source ~/riscv-envs/accuracy_checker_tflite/bin/activate
    pip install --upgrade pip
    
    # Библиотека OpenCV собрана, достаточно установить переменные окружения
    cd <work_dir>/opencv/opencv_install
    source ./bin/setup_vars_opencv4.sh
    cd ../..
    
    pip install -U setuptools
    pip install -U cmake
    pip install scikit-build
    pip install numpy
    pip install cython
    pip install docker
    
    # Библиотека TensorFlow Lite собрана, достаточно выполнить установку
    python3 -m pip install tflite_runtime-2.14.0-cp311-cp311-linux_riscv64.whl
    
    # Загрузка и сборка Accuracy Checker
    git clone https://github.com/openvinotoolkit/open_model_zoo
    cd ./open_model_zoo/tools/accuracy_checker
    python3 -m pip install .
    
    # Установка пути до пакетов Accuracy Checker
    export PYTHONPATH=$PYTHONPATH:~/riscv-envs/accuracy_checker_tflite/lib/python3.11/site-packages
    
    export PYTHONPATH=$PYTHONPATH:~/riscv-envs/accuracy_checker_tflite/lib/python3.11/site-packages/accuracy_checker
    
    echo $PYTHONPATH
    # Примерный перечень путей в переменной окружения:
    # <work_dir>/opencv/opencv_install/bin/../lib/python3.11/site-packages
    # ~/riscv-envs/accuracy_checker_tflite/lib/python3.11/site-packages
    # ~/riscv-envs/accuracy_checker_tflite/lib/python3.11/site-packages/accuracy_checker
    
    deactivate
    

    После установки выполнение команды accuracy_check -h также должно приводить к выводу справочной информации о параметрах запуска инструмента.

    Примечание: основная ветка Accuracy Checker содержит реализацию вывода, которая требует импорта библиотеки tensorflow в файле ~/riscv-envs/accuracy_checker_tflite/lib/python3.11/site-packages/accuracy_checker/launcher/tf_lite_launcher.py. В практической работе вместо полного пакета tensorflow используется сборка tensorflow-runtime. Чтобы обеспечить работоспособность Accuracy Checker с tensorflow-runtime, достаточно исправить импорты или заменить указанный файл на соответствующий файл из директории 02_Practice_fixed_files, которая приложена к настоящей практической работе.

  3. Загрузить репозиторий DLI, содержащий обертку компонента Accuracy Checker, которая далее используется для упрощения запуска Accuracy Checker при наличии набора моделей.

    cd <work_dir>
    git clone https://github.com/itlab-vision/dl-benchmark
    

    Примечание: данный шаг можно пропустить, если система DLI была использована для конвертации модели из формата TensorFlow в TensorFlow Lite.

  4. Загрузить валидационную выборку набора данных ImageNet, состоящую из 50 000 изображений, для которой опубликованы результаты качества классификации. Полный набор данных ImageNet доступен по ссылке. Для последующего запуска Accuracy Checker необходима директория с изображениями ILSVRC2012_img_val и файл val.txt, содержащий разметку этих изображений в формате <image_name> <class_id>, где <image_name> -- название файла, <class_id> -- идентификатор категории, которой принадлежит изображение. Далее для определенности будем считать, что они находятся в директории с названием <dataset_dir>. Поскольку на данный момент RISC-V-устройства являются маломощными, то обработка полной выборки с использованием некоторых моделей может занимать значительное время. Поэтому для получения оценки качества работы этих моделей на устройстве допустимо использовать произвольное подмножество из $N$ изображений указанной выборки. Для определенности в настоящей работе выбираются первые 1000 изображений. Соотственно файл val.txt должен содержать первые 1000 строк из исходного.

  5. Сформировать конфигурационный файл accuracy_checker_config_*.xml (* принимает значение tvm или tflite) для анализа качества работы модели на загруженном наборе данных.

    Apache TVM. Шаблонная структура конфигурационного файла для вывода средствами Apache TVM приведена ниже (файл accuracy_checker_config_template_tvm.xml дублирует эту структуру). Для использования этой конфигурации достаточно вставить корректное значение пути work_dir. Тег <Directory> должен содержать путь до файлов модели в формате Apache TVM, а тег <Config> -- полный путь до конфигурационного файла модели *.yml в загруженном репозитории OMZ (файлы densenet-121-tvm-riscv.yml и mobilenet-v3-large-1.0-224-tvm-riscv.yml, содержащие корректный набор параметров, можно найти в директории с файлами конфигурации 02_Practice_configs к данной практической работе). Примечание: указанные в xml-файле пути должны быть абсолютными.

    <?xml version="1.0" encoding="utf-8"?>
    <Tests>
        <Test>
            <Model>
                <Task>classification</Task>
                <Name>densenet-121-tf</Name>
                <Precision>FP32</Precision>
                <SourceFramework>TensorFlow</SourceFramework>
                <Directory>work_dir/public/densenet-121-tf/tvm_riscv/bs_1</Directory>
            </Model>
            <Parameters>
                <InferenceFramework>TVM</InferenceFramework>
                <Device>CPU</Device>
                <Config>work_dir/open_model_zoo_tvm/tools/accuracy_checker/configs/densenet-121-tvm.yml</Config>
            </Parameters>
        </Test>
        <Test>
            <Model>
                <Task>classification</Task>
                <Name>mobilenet-v3-large-1.0-224-tf</Name>
                <Precision>FP32</Precision>
                <SourceFramework>TensorFlow</SourceFramework>
                <Directory>work_dir/public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_1</Directory>
            </Model>
            <Parameters>
                <InferenceFramework>TVM</InferenceFramework>
                <Device>CPU</Device>
                <Config>work_dir/open_model_zoo_tvm/tools/accuracy_checker/configs/mobilenet-v3-large-1.0-224-tvm.yml</Config>
            </Parameters>
        </Test>
    </Tests>
    

    TensorFlow Lite. Шаблонная структура конфигурационного файла для вывода средствами TensorFlow Lite приведена ниже (файл accuracy_checker_config_template_tflite.xml дублирует эту структуру). Она отличается от ранее показанной только значениями тегов <Directory>, <InferenceFramework> и <Config>. Тег <Directory> содержит путь до файла модели в формате .tflite, куда была сконвертирована исходная модель, <InferenceFramework> -- название фреймворка для вывода, <Config> -- полное название файла с описанием конфигурации модели *.yml (корректные файлы также можно найти в директории 02_Practice_configs).

    <?xml version="1.0" encoding="utf-8"?>
    <Tests>
        <Test>
            <Model>
                <Task>classification</Task>
                <Name>densenet-121-tf</Name>
                <Precision>FP32</Precision>
                <SourceFramework>TensorFlow</SourceFramework>
                <Directory>work_dir/public/densenet-121-tf/</Directory>
            </Model>
            <Parameters>
                <InferenceFramework>TensorFlow_Lite</InferenceFramework>
                <Device>CPU</Device>
                <Config>work_dir/open_model_zoo/tools/accuracy_checker/configs/densenet-121-tflite.yml</Config>
            </Parameters>
        </Test>
        <Test>
            <Model>
                <Task>classification</Task>
                <Name>mobilenet-v3-large-1.0-224-tf</Name>
                <Precision>FP32</Precision>
                <SourceFramework>TensorFlow</SourceFramework>
                <Directory>work_dir/public/mobilenet-v3-large-1.0-224-tf/</Directory>
            </Model>
            <Parameters>
                <InferenceFramework>TensorFlow_Lite</InferenceFramework>
                <Device>CPU</Device>
                <Config>work_dir/open_model_zoo/tools/accuracy_checker/configs/mobilenet-v3-large-1.0-224-tflite.yml</Config>
            </Parameters>
        </Test>
    </Tests>
    

    Примечание: сбор показателей качества для двух моделей можно выполнить за один запуск обертки Accuracy Checker.

  6. Перейти в директорию с оберткой компонента Accuracy Checker и запустить ее с использованием командной строки, приведенной ниже. Передаваемые параметры:

    • -r - выходной файл с результатами точности классификации,
    • -с - сформированный конфигурационный файл,
    • -s - директория, содержащая загруженный набор данных,
    • -d - файл с определением параметров наборов данных,
    • --executor_type - окружение для исполнения (host_machine означает прямой запуск в окружении, установленном на устройстве, docker - запуск в докер- контейнере).

    Подробнее параметры командной строки описаны по ссылке. Ниже приведена примерная последовательность команд запуска.

    conda activate <accuracy_checker_env>
    cd <work_dir>/dl-benchmark/src/accuracy_checker
    
    python3 accuracy_checker.py \
     -r <work_dir>/accuracy_results.csv \
     -c <work_dir>/accuracy_checker_config.xml \
     -s <dataset_dir> \
     -d <work_dir>/open_model_zoo/tools/accuracy_checker/dataset_definitions.yml \
     --executor_type host_machine
    
    conda deactivate
    

    Примечание: в зависимости от фреймворка необходимо команду выполнять в виртуальной среде accuracy_checker_tvm или accuracy_checker_tflite. Конкретное название среды необходимо подставить в качестве значения параметра <accuracy_checker_env>.

    Отметим, что в стандартном выводе в строках accuracy@top1 и accuracy@top5 приведены посчитанные значения метрик top-1 и top-5 в процентах, которые округлены до второго знака после запятой. Также в этих строках указаны абсолютная и относительная разница по сравнению с референсными значения. Более точные значения логируются в промежуточный файл results.csv в директории <work_dir>/dl-benchmark/src/accuracy_checker.

    Наряду с этим, в результате запуска Accuracy Checker будет создан файл <work_dir>/accuracy_results.csv с результирующими показателями качества классификации (точность -- 2 знака после запятой).

    Примеры всех генерируемых файлов приложены к настоящей практической работе.

  7. Cравнить полученные в файле <work_dir>/accuracy_results.csv результаты точности классификации и значения метрик top-1 и top-5 с опубликованными в работах авторов модели. Референсные значения точностей можно найти в файле <work_dir>/open_model_zoo/tools/accuracy_checker/configs/<model_name>.yml. Для визуализации результатов можно воспользоваться построением гистограмм средствами пакета matplotlib.

In [12]:
import matplotlib.pyplot as plt
import numpy as np

def draw_hist_topK(ref_top1, ref_top5, tvm_computed_top1, tvm_computed_top5,
                   tflite_computed_top1, tflite_computed_top5, model_name):
    metrics = ("top-1", "top-5")
    values = {
        'Computed values (Apache TVM)':  (tvm_computed_top1, tvm_computed_top5),
        'Computed values (TensorFlow Lite)':  (tflite_computed_top1, tflite_computed_top5),
        'Reference values': (ref_top1, ref_top5),
    }

    # положения меток
    x = np.arange(len(metrics))
    # ширина бинов
    width = 0.3
    multiplier = 0

    fig, ax = plt.subplots(layout='constrained')
    fig.set_size_inches(5, 2.2)
    yax = ax.axes.get_yaxis()
    yax = yax.set_visible(False)

    for attribute, measurement in values.items():
        offset = width * multiplier
        rects = ax.bar(x + offset, measurement, width, label=attribute)
        ax.bar_label(rects, padding=2)
        multiplier += 1

    ax.set_title(f'top-1 and top-5 accuracies for {model_name}, %')
    ax.set_xticks(x + width, metrics)
    ax.legend(loc='upper left', ncols=1)
    ax.set_ylim(0, 210)

    plt.show()


# Результаты определения качества классификации для DenseNet-121
model_name = 'DenseNet-121'
# референсные значения
ref_top1 = 74.46
ref_top5 = 92.13
# подсчитанные значения
tvm_computed_top1 = 74.8
tvm_computed_top5 = 92.7
tflite_computed_top1 = 74.8
tflite_computed_top5 = 92.7
# отображение гистограммы
draw_hist_topK(ref_top1, ref_top5, tvm_computed_top1, tvm_computed_top5,
               tflite_computed_top1, tflite_computed_top5, model_name)


# Результаты определения качества классификации для DenseNet-121
model_name = 'MobileNet-v3-large'
# референсные значения
ref_top1 = 75.30
ref_top5 = 92.62
# подсчитанные значения
tvm_computed_top1 = 75.7
tvm_computed_top5 = 93.3
tflite_computed_top1 = 75.7
tflite_computed_top5 = 93.3
# отображение гистограммы
draw_hist_topK(ref_top1, ref_top5, tvm_computed_top1, tvm_computed_top5,
               tflite_computed_top1, tflite_computed_top5, model_name)
No description has been provided for this image
No description has been provided for this image

Из построенных гистограмм можно видеть, что исходная модель, сконвертированная в форматы Apache TVM и TensorFlow Lite, демонстрирует одинаковые показатели точности top-1 и top-5. Полученные результаты соответствуют референсным значениям.

10. Анализ и сравнение производительности вывода на RISC-V¶

10.1. Параметры тестовой инфраструктуры¶

В данной работе для получения информации о тестовой инфраструктуре используются функции, входящие в состав стандартных пакетов Python.

In [13]:
import os
import platform
import subprocess
import re
from collections import OrderedDict
from sys import version as python_formatted_version


def get_cpu_name():
    cpuname = 'Undefined'

    if platform.system() == 'Windows':
        return platform.processor()    
    elif platform.system() == 'Linux':
        command = 'cat /proc/cpuinfo'
        all_info = subprocess.check_output(command, shell=True).decode().strip()
        for line in all_info.split('\n'):
            if 'model name' in line:
                return re.sub( ".*model name.*: ", "", line, 1)

    return cpuname.strip()

def get_ram_size(ostype):
    ramsize = 'Undefined'
    if ostype == 'Windows':
        command = ['wmic', 'OS', 'get', 'TotalVisibleMemorySize', '/Value']
        p = subprocess.Popen(command, universal_newlines=True, shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        text = p.stdout.read()
        p.wait()
        text = text.split('=')
        ramsize = text[1].strip() + ' KB'
    elif ostype == 'Linux':
        command = ['cat', '/proc/meminfo']
        all_info = subprocess.check_output(command).strip().decode()
        for line in all_info.split('\n'):
            if 'MemTotal' in line:
                return line.split(':')[1].strip()
    return ramsize

def get_system_characteristics():
    ostype = platform.system()
    characteristics = OrderedDict()
    characteristics.update({'CPU': get_cpu_name()})
    characteristics.update({'Number of CPUs': os.cpu_count()})
    characteristics.update({'CPU family': platform.processor()})
    characteristics.update({'RAM size': get_ram_size(ostype)})
    characteristics.update({'OS family': platform.system()})
    characteristics.update({'OS version': platform.platform()})
    characteristics.update({'Python version': python_formatted_version})

    return characteristics


hardware_dict = get_system_characteristics()
for key, value in hardware_dict.items():
    print(f'{key:16}: {value}')
CPU             : Undefined
Number of CPUs  : 4
CPU family      : 
RAM size        : 16116960 kB
OS family       : Linux
OS version      : Linux-5.10.113-cluster-riscv64-with-glibc2.36
Python version  : 3.11.4 (main, Jun  7 2023, 10:13:09) [GCC 12.2.0]

10.2. Параметры для определения оптимальной производительности вывода¶

In [14]:
# размеры входных пачек данных
batch_sizes = [1, 2, 4, 8, 16]
# количество итераций для определения показателей латентности и FPS
iter_num = 10

# количество потоков для вывода средствами TensorFlow Lite
num_threads = [1, 2, 4]

# уровни оптимизации моделей Apache TVM
opt_levels = [0, 1, 2, 3]

10.3. Определение оптимальных параметров вывода с использованием Apache TVM¶

In [15]:
# Функция перебора параметров запуска вывода средствами Apache TVM
def tvm_perf_tests(model_tvm):
    tvm_latency = dict()
    tvm_fps = dict()
    # перебор по уровню оптимизации модели
    for opt_level in opt_levels:
        tvm_bs_latency = dict()
        tvm_bs_fps = dict()
        # перебор по размерам пачки входных данных
        for batch_size in batch_sizes:
            # загрузка модели с определенными размерами входного тензора
            # и скомпилированной с заданным уровнем оптимизации            
            module = load_model(model_tvm.format(batch_size=batch_size, opt_level=opt_level))
            # запуск многократного вывода для заданного набора параметров
            _, latency, fps = inference_tvm_perf(iter_num, batch_size, files, images,
                                                 module, input_name)
            tvm_bs_latency[batch_size] = latency
            tvm_bs_fps[batch_size] = fps

            # вывод показателей производительности
            print(f'\tbatch_size = {batch_size:2}: latency = {latency:.3f} s, '
                  f'FPS = {fps:.3f} frames/s')
        tvm_latency[opt_level] = tvm_bs_latency
        tvm_fps[opt_level] = tvm_bs_fps
    return tvm_latency, tvm_fps


print('Searching parameters for DenseNet-121...')
# Параметры предобработки изображения для DenseNet-121
mean = (123.68, 116.78, 103.94)
scale = (58.395, 57.12, 57.375)
bgr_to_rgb = True
# Загрузка и предварительная обработка изображений
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
# Запуск экспериментов для DenseNet-121
model_tvm = '../public/densenet-121-tf/tvm_riscv/bs_{batch_size}/densenet-121-tf_{opt_level}.tar'
densenet121_tvm_latency, densenet121_tvm_fps = tvm_perf_tests(model_tvm)


print('Searching parameters for MobileNet-v3-large...')
# Параметры предобработки изображения для MobileNet-v3-large
mean = (0, 0, 0)
scale = (1, 1, 1)
bgr_to_rgb = True
# Загрузка и предварительная обработка изображений
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
# Запуск экспериментов для MobileNet-v3-large
model_tvm = '../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_{batch_size}/mobilenet_v3_large_224_1.0_float_{opt_level}.tar'
mnetv3large_tvm_latency, mnetv3large_tvm_fps = tvm_perf_tests(model_tvm)
Searching parameters for DenseNet-121...
Number of available images: 64
Load model: ../public/densenet-121-tf/tvm_riscv/bs_1/densenet-121-tf_0.tar
	batch_size =  1: latency = 1.337 s, FPS = 0.738 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_2/densenet-121-tf_0.tar
	batch_size =  2: latency = 2.906 s, FPS = 0.683 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_4/densenet-121-tf_0.tar
	batch_size =  4: latency = 6.406 s, FPS = 0.622 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_8/densenet-121-tf_0.tar
	batch_size =  8: latency = 12.585 s, FPS = 0.635 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_16/densenet-121-tf_0.tar
	batch_size = 16: latency = 25.065 s, FPS = 0.637 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_1/densenet-121-tf_1.tar
	batch_size =  1: latency = 1.226 s, FPS = 0.811 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_2/densenet-121-tf_1.tar
	batch_size =  2: latency = 2.747 s, FPS = 0.723 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_4/densenet-121-tf_1.tar
	batch_size =  4: latency = 6.259 s, FPS = 0.639 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_8/densenet-121-tf_1.tar
	batch_size =  8: latency = 12.399 s, FPS = 0.645 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_16/densenet-121-tf_1.tar
	batch_size = 16: latency = 24.764 s, FPS = 0.646 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_1/densenet-121-tf_2.tar
	batch_size =  1: latency = 1.208 s, FPS = 0.819 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_2/densenet-121-tf_2.tar
	batch_size =  2: latency = 2.711 s, FPS = 0.734 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_4/densenet-121-tf_2.tar
	batch_size =  4: latency = 6.233 s, FPS = 0.642 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_8/densenet-121-tf_2.tar
	batch_size =  8: latency = 12.298 s, FPS = 0.649 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_16/densenet-121-tf_2.tar
	batch_size = 16: latency = 24.551 s, FPS = 0.652 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_1/densenet-121-tf_3.tar
	batch_size =  1: latency = 1.164 s, FPS = 0.856 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_2/densenet-121-tf_3.tar
	batch_size =  2: latency = 2.641 s, FPS = 0.758 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_4/densenet-121-tf_3.tar
	batch_size =  4: latency = 6.011 s, FPS = 0.665 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_8/densenet-121-tf_3.tar
	batch_size =  8: latency = 11.730 s, FPS = 0.682 frames/s
Load model: ../public/densenet-121-tf/tvm_riscv/bs_16/densenet-121-tf_3.tar
	batch_size = 16: latency = 23.530 s, FPS = 0.681 frames/s
Searching parameters for MobileNet-v3-large...
Number of available images: 64
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_1/mobilenet_v3_large_224_1.0_float_0.tar
	batch_size =  1: latency = 0.160 s, FPS = 6.210 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_2/mobilenet_v3_large_224_1.0_float_0.tar
	batch_size =  2: latency = 0.315 s, FPS = 6.300 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_4/mobilenet_v3_large_224_1.0_float_0.tar
	batch_size =  4: latency = 0.672 s, FPS = 5.899 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_8/mobilenet_v3_large_224_1.0_float_0.tar
	batch_size =  8: latency = 1.309 s, FPS = 6.037 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_16/mobilenet_v3_large_224_1.0_float_0.tar
	batch_size = 16: latency = 2.573 s, FPS = 6.145 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_1/mobilenet_v3_large_224_1.0_float_1.tar
	batch_size =  1: latency = 0.125 s, FPS = 7.990 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_2/mobilenet_v3_large_224_1.0_float_1.tar
	batch_size =  2: latency = 0.249 s, FPS = 7.977 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_4/mobilenet_v3_large_224_1.0_float_1.tar
	batch_size =  4: latency = 0.529 s, FPS = 7.520 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_8/mobilenet_v3_large_224_1.0_float_1.tar
	batch_size =  8: latency = 1.039 s, FPS = 7.612 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_16/mobilenet_v3_large_224_1.0_float_1.tar
	batch_size = 16: latency = 2.058 s, FPS = 7.685 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_1/mobilenet_v3_large_224_1.0_float_2.tar
	batch_size =  1: latency = 0.124 s, FPS = 7.968 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_2/mobilenet_v3_large_224_1.0_float_2.tar
	batch_size =  2: latency = 0.246 s, FPS = 8.046 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_4/mobilenet_v3_large_224_1.0_float_2.tar
	batch_size =  4: latency = 0.531 s, FPS = 7.423 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_8/mobilenet_v3_large_224_1.0_float_2.tar
	batch_size =  8: latency = 1.039 s, FPS = 7.637 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_16/mobilenet_v3_large_224_1.0_float_2.tar
	batch_size = 16: latency = 2.058 s, FPS = 7.701 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_1/mobilenet_v3_large_224_1.0_float_3.tar
	batch_size =  1: latency = 0.101 s, FPS = 9.780 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_2/mobilenet_v3_large_224_1.0_float_3.tar
	batch_size =  2: latency = 0.216 s, FPS = 9.213 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_4/mobilenet_v3_large_224_1.0_float_3.tar
	batch_size =  4: latency = 0.475 s, FPS = 8.354 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_8/mobilenet_v3_large_224_1.0_float_3.tar
	batch_size =  8: latency = 0.934 s, FPS = 8.491 frames/s
Load model: ../public/mobilenet-v3-large-1.0-224-tf/tvm_riscv/bs_16/mobilenet_v3_large_224_1.0_float_3.tar
	batch_size = 16: latency = 1.861 s, FPS = 8.528 frames/s

Для наглядности полученных результатов построим на одной диаграмме графики зависимости FPS от размера пачки входных данных для каждого фиксированного уровня оптимизации модели.

In [16]:
colors = ['teal', 'skyblue', 'steelblue', 'maroon']
markers = ['o', 'd', 's', 'v']
linestyles = ['solid', 'dotted', 'dashed', 'dashdot']
labels = ['opt_level=0', 'opt_level=1', 'opt_level=2', 'opt_level=3']

def draw_tvm_fps(tvm_results, title):
    fig, axs = plt.subplots()
    plt.grid(True)
    line_idx = 0
    for opt_level, bs_results in tvm_results.items():
        x = list(bs_results.keys())
        y = list(bs_results.values())
        axs.plot(x, y, marker=markers[line_idx], color=colors[line_idx],
                 linestyle=linestyles[line_idx], label=labels[line_idx])
        line_idx += 1
        if opt_level != 1:
            for i, j in zip(x, y):
                if i == 1:
                    axs.annotate(f'{j:.2f}', xy=(i,j), ha='right', va='center', fontsize=10)
                elif i != 2:
                    axs.annotate(f'{j:.2f}', xy=(i,j+0.002), ha='center', va='bottom', fontsize=10)
    axs.set_xlabel('Batch size', fontsize=12, fontweight="bold")
    axs.set_ylabel('FPS', fontsize=12, fontweight="bold")
    axs.set_title(title, fontsize=12, fontweight="bold")
    axs.set_xlim(-1, 17)
    plt.legend(loc='upper right', ncols=1, fontsize=11)
    plt.show()

draw_tvm_fps(densenet121_tvm_fps, 'DenseNet-121, Apache TVM')
draw_tvm_fps(mnetv3large_tvm_fps, 'MobileNet-v3-large, Apache TVM')
No description has been provided for this image
No description has been provided for this image

Выводы из построенных графиков: лучшие показатели производительности вывода независимо от модели получены с использованием оптимизированной модели с opt_level=3.

10.4. Определение оптимальных параметров вывода с использованием TensorFlow Lite¶

Для TensorFlow Lite необходимо сравнивать производительность вывода при разных размерах входной пачки данных и разном количестве потоков, исполняемых параллельно, и подбирать оптимальные значения параметров.

In [17]:
# функция запуска тестов производительности для разных размеров входных пачек данных
# при разном количестве потоков
def tflite_perf_tests(model_tflite, batch_sizes, num_threads, iter_num, files, images):
    # словарь "количество потоков" - словарь "размер пачки" - "латентность"
    tflite_latency = dict()
    # словарь "количество потоков" - словарь "размер пачки" - "FPS"
    tflite_fps = dict()
    # цикл по количеству потоков
    for nthreads in num_threads:
        print(f'Number of threads: {nthreads}')
        # словарь "размер пачки" - "латентность"
        tflite_nt_latency = dict()
        # словарь "размер пачки" - "FPS"
        tflite_nt_fps = dict()
        # цикл по размерам пачек данных
        for batch_size in batch_sizes:
            input_shape = [batch_size, 224, 224, 3]
            # подготовка модели (загрузка и масштабирование входа)
            model_interpreter = tflite.Interpreter(model_path=model_tflite,
                                                   num_threads=nthreads)
            input_details = model_interpreter.get_input_details()
            model_interpreter.resize_tensor_input(input_details[0]['index'], input_shape)
            model_interpreter.allocate_tensors()
            # запуск теста производительности для фиксированного размера пачки данных
            _, latency, fps = inference_tflite_perf(iter_num, batch_size, files, images,
                                                    model_interpreter)
            # сохранение результатов производительности в словари
            tflite_nt_latency[batch_size] = latency
            tflite_nt_fps[batch_size] = fps
            # вывод показателей производительности
            print(f'\tbatch_size = {batch_size:2}: latency = {latency:.3f} s, '
                  f'FPS = {fps:.3f} frames/s')
        tflite_latency[nthreads] = tflite_nt_latency
        tflite_fps[nthreads] = tflite_nt_fps
    return tflite_latency, tflite_fps


print('Searching parameters for DenseNet-121...')
# Параметры предобработки изображения для DenseNet-121
mean = (123.68, 116.78, 103.94)
scale = (58.395, 57.12, 57.375)
bgr_to_rgb = True
# Загрузка и предварительная обработка изображений
model_tflite = '../public/densenet-121-tf/densenet-121.tflite'
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
# Запуск экспериментов для DenseNet-121
densenet121_tflite_latency, densenet121_tflite_fps = tflite_perf_tests(
    model_tflite, batch_sizes, num_threads,iter_num, files, images)


print('Searching parameters for MobileNet-v3-large...')
# Параметры предобработки изображения для MobileNet-v3-large
mean = (0, 0, 0)
scale = (1, 1, 1)
bgr_to_rgb = True
# Загрузка и предварительная обработка изображений
model_tflite = '../public/mobilenet-v3-large-1.0-224-tf/mobilenet_v3_large_224_1.0_float.tflite'
files, images = prepare_input(images_dir, input_shape[1:3], 
                              mean, scale, bgr_to_rgb)
# Запуск экспериментов для MobileNet-v3-large
mnetv3large_tflite_latency, mnetv3large_tflite_fps = tflite_perf_tests(
    model_tflite, batch_sizes, num_threads, iter_num, files, images)
Searching parameters for DenseNet-121...
Number of available images: 64
Number of threads: 1
	batch_size =  1: latency = 1.611 s, FPS = 0.621 frames/s
	batch_size =  2: latency = 3.222 s, FPS = 0.620 frames/s
	batch_size =  4: latency = 6.405 s, FPS = 0.624 frames/s
	batch_size =  8: latency = 12.840 s, FPS = 0.622 frames/s
	batch_size = 16: latency = 25.717 s, FPS = 0.621 frames/s
Number of threads: 2
	batch_size =  1: latency = 0.876 s, FPS = 1.140 frames/s
	batch_size =  2: latency = 1.754 s, FPS = 1.140 frames/s
	batch_size =  4: latency = 3.494 s, FPS = 1.144 frames/s
	batch_size =  8: latency = 6.993 s, FPS = 1.143 frames/s
	batch_size = 16: latency = 13.983 s, FPS = 1.143 frames/s
Number of threads: 4
	batch_size =  1: latency = 0.511 s, FPS = 1.953 frames/s
	batch_size =  2: latency = 1.012 s, FPS = 1.969 frames/s
	batch_size =  4: latency = 2.010 s, FPS = 1.982 frames/s
	batch_size =  8: latency = 4.030 s, FPS = 1.977 frames/s
	batch_size = 16: latency = 8.045 s, FPS = 1.987 frames/s
Searching parameters for MobileNet-v3-large...
Number of available images: 64
Number of threads: 1
	batch_size =  1: latency = 0.152 s, FPS = 6.560 frames/s
	batch_size =  2: latency = 0.299 s, FPS = 6.684 frames/s
	batch_size =  4: latency = 0.594 s, FPS = 6.733 frames/s
	batch_size =  8: latency = 1.179 s, FPS = 6.781 frames/s
	batch_size = 16: latency = 2.423 s, FPS = 6.580 frames/s
Number of threads: 2
	batch_size =  1: latency = 0.084 s, FPS = 11.910 frames/s
	batch_size =  2: latency = 0.160 s, FPS = 12.490 frames/s
	batch_size =  4: latency = 0.318 s, FPS = 12.566 frames/s
	batch_size =  8: latency = 0.632 s, FPS = 12.650 frames/s
	batch_size = 16: latency = 1.313 s, FPS = 12.157 frames/s
Number of threads: 4
	batch_size =  1: latency = 0.052 s, FPS = 19.050 frames/s
	batch_size =  2: latency = 0.091 s, FPS = 21.806 frames/s
	batch_size =  4: latency = 0.176 s, FPS = 22.678 frames/s
	batch_size =  8: latency = 0.349 s, FPS = 22.803 frames/s
	batch_size = 16: latency = 0.723 s, FPS = 21.915 frames/s

Для наглядности полученных результатов построим на одной диаграмме графики зависимости FPS от размера входной пачки данных при разных значениях числа потоков.

In [18]:
colors = ['teal', 'skyblue', 'steelblue', 'maroon']
markers = ['o', 'd', 's', 'v']
linestyles = ['solid', 'dotted', 'dashed', 'dashdot']
labels = ['nthreads=1', 'nthreads=2', 'nthreads=4', 'nthreads=8']

def draw_tflite_fps(tflite_fps, title):
    fig, axs = plt.subplots()
    plt.grid(True)
    line_idx = 0
    ymax = 0
    for nthreads, bs_fps in tflite_fps.items():
        x = list(bs_fps.keys())
        y = list(bs_fps.values())
        ymax = max(ymax, max(y))
        axs.plot(x, y, marker=markers[line_idx], color=colors[line_idx],
                 linestyle=linestyles[line_idx], label=labels[line_idx])
        line_idx += 1
        for i, j in zip(x, y):
            if i != 1:
                if nthreads == 1:
                    axs.annotate(f'{j:.2f}', xy=(i,j-0.02), ha='left', va='top', fontsize=10)
                else:
                    axs.annotate(f'{j:.2f}', xy=(i,j+0.02), ha='left', va='bottom', fontsize=10)
    axs.set_xlabel('Batch size', fontsize=12, fontweight="bold")
    axs.set_ylabel('FPS', fontsize=12, fontweight="bold")
    axs.set_title(title, fontsize=12, fontweight="bold")
    axs.set_xlim(0, 18)
    axs.set_ylim(0, int(ymax) + 5)
    plt.legend(loc='upper left', ncols=2, fontsize=11)
    plt.show()


draw_tflite_fps(densenet121_tflite_fps, 'DenseNet-121, TensorFlow Lite')
draw_tflite_fps(mnetv3large_tflite_fps, 'MobileNet-v3-large, TensorFlow Lite')
No description has been provided for this image
No description has been provided for this image

Вывода из построенных графиков: лучшие показатели производительности для обеих рассматриваемых моделей получены при числе потоков, равном 4.

10.5. Сравнение показателей производительности¶

In [19]:
def draw_best_lines(best_tvm_fps, tvm_params, best_tflite_fps, tflite_params, title):
    fig, axs = plt.subplots()
    plt.grid(True)
    
    axs.plot(best_tvm_fps.keys(), best_tvm_fps.values(), marker='o', color='teal',
             linestyle='solid', label=f'Apache TVM ({tvm_params})')
    for i, j in zip(best_tvm_fps.keys(), best_tvm_fps.values()):
        axs.annotate(f'{j:.2f}', xy=(i,j+0.01), ha='center', va='bottom', fontsize=10)
    ymax_id = max(best_tvm_fps, key=best_tvm_fps.get)
    ymax = best_tvm_fps[ymax_id]

    axs.plot(best_tflite_fps.keys(), best_tflite_fps.values(), marker='d', color='maroon',
             linestyle='dashed', label=f'TensorFlow Lite ({tflite_params})')
    for i, j in zip(best_tflite_fps.keys(), best_tflite_fps.values()):
        axs.annotate(f'{j:.2f}', xy=(i,j+0.01), ha='center', va='bottom', fontsize=10)
    ymax_id = max(best_tflite_fps, key=best_tflite_fps.get)
    ymax = max(ymax, best_tflite_fps[ymax_id])

    axs.set_xlabel('Batch size', fontsize=12, fontweight="bold")
    axs.set_ylabel('FPS', fontsize=12, fontweight="bold")
    axs.set_title(title, fontsize=12, fontweight="bold")
    axs.set_xlim(0, 18)
    step = int(ymax) / 10
    if step >= 10:
        ymax += 150
    else:
        ymax += 5
    axs.set_ylim(0, ymax+step)
    plt.legend(loc='upper left', ncols=1, fontsize=11)
    plt.show()

def calc_average_diff(tvm_fps, tflite_fps, model_name):
    diff = 0
    for batch_size in batch_sizes:
        diff += tflite_fps[batch_size] / tvm_fps[batch_size]
    print(f'Average relative difference for {model_name}: {diff / len(batch_sizes):.2f}') 


# Лучшие показатели FPS для модели DenseNet-121
# лучшая кривая зависимости FPS от размера пачки данных при opt_level=3
best_tvm_fps = densenet121_tvm_fps[3]
# лучшая кривая зависимости FPS от размера пачки данных при nthreads=4
best_tflite_fps = densenet121_tflite_fps[4]
# отображение лучших результатов
draw_best_lines(best_tvm_fps, 'opt_level=3', best_tflite_fps, 'nthreads=4', 'DenseNet-121')
calc_average_diff(best_tvm_fps, best_tflite_fps, 'DenseNet-121')


# Лучшие показатели FPS для модели MobileNet-v3-large
# лучшая кривая зависимости FPS от размера пачки данных при opt_level=3
best_tvm_fps = mnetv3large_tvm_fps[3]
# лучшая кривая зависимости FPS от размера пачки данных при nthreads=4
best_tflite_fps = mnetv3large_tflite_fps[4]
# отображение лучших результатов
draw_best_lines(best_tvm_fps, 'opt_level=3', best_tflite_fps, 'nthreads=4', 'MobileNet-v3-large')
calc_average_diff(best_tvm_fps, best_tflite_fps, 'MobileNet-v3-large')
No description has been provided for this image
Average relative difference for DenseNet-121: 2.74
No description has been provided for this image
Average relative difference for MobileNet-v3-large: 2.46

Выводы: Apache TVM отстает от TensorFlow Lite при выводе модели DenseNet-121 в среднем в 2.74 раза, при выводе MobileNet-v3-large -- в среднем в 2.46 раза.

Заключение¶

Время вывода глубоких нейронных сетей определяется архитектурой моделей и вычислительной сложностью преобразований, которые выполняются на слоях сетей. Фреймворки, используемые для реализации вывода, внутри могут задействовать сторонние библиотеки с оптимизированными ядрами, соответствующими базовым преобразованиям на слоях сетей. Библиотеки в свою очередь используют векторные инструкции. Например, в TensorFlow Lite интегрирована библиотека XNNPACK, которая позволяет ускорить вычисления. Об этом свидетельствуют полученные результаты экспериментов. Также следует отметить, что на производительность вывода существенно влияет формат хранения данных.