Развертывание сети семантической сегментации (U-Net) с TensorRT (без поддержки повышающей дискретизации)

Я пытаюсь развернуть обученную U-Net с TensorRT. Модель была обучена с использованием Keras (с Tensorflow в качестве бэкэнда). Код очень похож на этот: https://github.com/zhixuhao/unet/blob/master/model.py

Когда я преобразовал модель в формат UFF, используя такой код:

import uff
import os
uff_fname = os.path.join("./models/", "model_" + idx + ".uff")
uff_model = uff.from_tensorflow_frozen_model(
    frozen_file = os.path.join('./models', trt_fname), output_nodes = output_names, 
    output_filename = uff_fname
)

Я получу следующее предупреждение:

Warning: No conversion function registered for layer: ResizeNearestNeighbor yet.
Converting up_sampling2d_32_12/ResizeNearestNeighbor as custom op: ResizeNearestNeighbor
Warning: No conversion function registered for layer: DataFormatVecPermute yet.
Converting up_sampling2d_32_12/Shape-0-0-VecPermuteNCHWToNHWC-LayoutOptimizer as custom op: DataFormatVecPermute

Я попытался избежать этого, заменив слой повышающей дискретизации с повышающей дискретизацией (билинейная интерполяция) и транспонируя свертку. Но конвертер выдаст мне подобные ошибки. Я проверил https://docs.nvidia.com/deeplearning/sdk/tensorrt-support-matrix/index.html, и казалось, что все эти операции еще не поддерживаются.

Мне интересно, есть ли какое-либо решение этой проблемы? Есть ли другой формат/фреймворк, который TensorRT любит и поддерживает повышающую дискретизацию? Или его можно заменить другими поддерживаемыми операциями?

Я также видел где-то, что можно добавить настраиваемые операции, чтобы заменить те, которые не поддерживаются для TensorRT. Хотя я не совсем уверен, каким будет рабочий процесс. Также было бы очень полезно, если бы кто-то мог указать на пример пользовательских слоев.

Заранее спасибо!

Ответ 1

Предупреждения заключаются в том, что эти операции еще не поддерживаются TensorRT, как вы уже упоминали. К сожалению, нет простого способа исправить это. Вы должны либо изменить график (даже после обучения), чтобы использовать только операцию, поддерживаемую комбинацией; или напишите эти операции самостоятельно как пользовательский слой.

Однако в C++ есть лучший способ выполнить логический вывод на других устройствах. Вы можете использовать TensorFlow вместе с TensorRT вместе. TensorRT проанализирует график на предмет поддерживаемых операций и преобразует их в узлы TensorRT, а оставшаяся часть графика будет обрабатываться TensorFlow как обычно. Больше информации здесь. Это решение намного быстрее, чем переписывание операций самостоятельно. Единственная сложная часть - это построить TensorFlow из исходных tensorflow_cc на целевом устройстве и сгенерировать динамическую библиотеку tensorflow_cc. В последнее время появилось много руководств и поддержка портов TensorFlow для различных архитектур, например, ARM.

Ответ 2

Эй, я сделал что-то похожее, я бы сказал, что лучший способ решить эту проблему - экспортировать вашу модель в .onnx с такой же, как эта, если вы проверите матрицу поддержки для onnx поддерживается upsample: enter image description here

Затем вы можете использовать https://github.com/onnx/onnx-tensorrt для преобразования onnx-модели в tenorrt, я хочу преобразовать сеть, которую я обучил Pytorch, и которая имела повышенный уровень. Репозиторий для onnx-tensorrt немного более активен, и если вы откроете вкладку pr, вы можете проверить, что другие люди пишут пользовательские слои и оттуда разветвляются.

Ответ 3

Обновление от 28.09.2009

Nvidia выпустила TensorRT 6.0.1 около двух недель назад и добавила новый API под названием "IResizeLayer". Этот уровень поддерживает "Ближайшую" интерполяцию и, таким образом, может использоваться для реализации повышающей дискретизации. Больше не нужно использовать пользовательские слои/плагины!

Оригинальный ответ:

Спасибо за все ответы и предложения, размещенные здесь!

В итоге мы напрямую внедрили сеть в API TensorRT C++ и загрузили весовые коэффициенты из файла модели .h5. У нас пока нет времени профилировать и полировать решение, но, похоже, вывод работает в соответствии с тестовыми изображениями, которые мы ввели.

Вот рабочий процесс, который мы приняли:

Шаг 1: закодируйте слой повышенной дискретизации.

В нашей модели U-Net все уровни повышающей дискретизации имеют коэффициент масштабирования (2, 2), и все они используют интерполяцию ResizeNearestNeighbor. По существу, значение пикселя в точке (x, y) в исходном тензоре будет равно четырем пикселям: (2x, 2y), (2x + 1, 2y), (2x, 2y + 1) и (2x + 1, 2y + 1) ) в новом тензоре. Это может быть легко закодировано в функцию ядра CUDA.

После того как мы получили ядро с повышающей дискретизацией, нам нужно обернуть его TensorRT API, в частности, класс IPluginV2Ext. Справочник разработчика содержит некоторые описания того, какие функции необходимо реализовать. Я бы сказал, что enqueue() - самая важная функция, потому что там выполняется ядро CUDA.

Есть также примеры в папке TensorRT Samples. Для моей версии эти ресурсы полезны:

Шаг 2. Кодирование остальной части сети с помощью TensorRT API

Остальная часть сети должна быть довольно простой. Просто найдите функцию вызова addxxxLayer из определения сети TensorRT.

Имейте в виду одну вещь: в зависимости от того, какую версию TRT вы используете, способ добавления отступов может отличаться. Я думаю, что новейшая версия (5.1.5) позволяет разработчикам добавлять параметры в addConvolution(), чтобы можно было выбрать правильный режим заполнения.

Моя модель была обучена с использованием Keras, режим заполнения по умолчанию состоит в том, что правый и нижний получают больше заполнения, если общее количество заполнения не является четным. Проверьте эту ссылку Qaru для получения подробной информации. В 5.1.5 есть режим mode, который представляет эту схему заполнения.

Если вы используете более старую версию (5.1.2.2), вам нужно будет добавить заполнение как отдельный слой перед слоем свертки, который имеет два параметра: предварительное заполнение и последующее заполнение.

Кроме того, все вещи NCHW в TensorRT

Полезный пример:

  • TensorRT-5.1.2.2/образцы/sampleMNISTAP

Шаг 3. Загрузите веса

TensorRT требует весов в формате [out_c, in_c, filter_h, filter_w], который упоминается в архивированной документации. Вес Keras имеет формат [filter_h, filter_w, c_in, c_out].

Мы получили файл чистого веса, вызвав model.save_weights('weight.h5') в Python. Затем мы можем считать веса в массив Numpy с помощью h5py, выполнить транспонирование и сохранить транспонированные веса в новый файл. Мы также выяснили имя группы и набора данных, используя h5py. Эта информация использовалась при загрузке весов в код C++ с использованием HDF5 C++ API.

Мы сравнивали выходной слой за слоем между кодом C++ и кодом Python. Для нашей сети U-Net все карты активации одинаковы, возможно, до третьего блока (после 2 пулов). После этого между значениями пикселей есть небольшая разница. Абсолютная ошибка в процентах составляет 10 ^ -8, поэтому мы не думаем, что это плохо. Мы все еще находимся в процессе доработки реализации C++.

Еще раз спасибо за все предложения и ответы, которые мы получили в этом посте. Надеюсь, что наше решение может быть полезным!