Qt — это мощная среда для создания кроссплатформенных приложений с графическим интерфейсом. Для сценариев производственного развертывания вам необходимо интегрировать модели, разработанные в среде машинного обучения PyTorch, в ваш C++ QT. Чтобы загрузить модель PyTorch в C++, вам необходимо преобразовать модель в формат сценария torch. Вы можете найти подробные инструкции по преобразованию моделей Pytorch в скрипт torch здесь.

Скачать библиотеку libtorch

Перейдите на официальный сайт PyTorch ссылка и выберите соответствующую версию libtorch в зависимости от установленной ОС и версии Cuda.

Окна

https://download.pytorch.org/libtorch/cu111/libtorch-win-shared-with-deps-1.8.1%2Bcu111.zip

линукс

Download here (Pre-cxx11 ABI):
https://download.pytorch.org/libtorch/cu111/libtorch-shared-with-deps-1.8.1%2Bcu111.zip

Download here (cxx11 ABI):
https://download.pytorch.org/libtorch/cu111/libtorch-cxx11-abi-shared-with-deps-1.8.1%2Bcu111.zip

Добавьте libtorch в проект qt

Это самое важное, но и более хлопотное. Во-первых, откройте файл проекта QT «.pro» и добавьте каталог заголовочных файлов libtorch (include) и каталог файлов библиотек (lib). Вам нужно добавить компилятор и флаги компоновки на основе платформы ОС, иначе libtorch не будет связываться с torch_cuda и в итоге «torch::cuda::is_available()» вернет 0. Libtorch рекомендует использовать CMAKE в качестве инструмента сборки, но вы можете добиться того же с помощью QMAKE для QT, но следует быть осторожным с флагами.

CONFIG += c++11
#Include the headers
INCLUDEPATH += $$PYTORCH_DIR/include/torch/csrc/api/include \
               $$quote($$CUDA_DIR/include)
#compiler and linker flags
unix {
QMAKE_LFLAGS += -Wl,--no-as-needed
}
win32 {
QMAKE_LFLAGS += -INCLUDE:?warp_size@cuda@at@@YAHXZ
QMAKE_LFLAGS += -INCLUDE:?searchsorted_cuda@native@at@@YA?AVTensor@2@AEBV32@0_N1@Z
QMAKE_LFLAGS += /machine:x64
}
# linking with libs
win32 {
LIBS += $$quote($$PYTORCH_DIR\lib\*.lib)
LIBS +=  -L$$quote($$PYTORCH_DIR\lib)
LIBS += $$quote($$CUDA_DIR\lib\x64\*.lib)
LIBS += -L$$quote($$CUDA_DIR\v11.1\bin)
}
unix {
LIBS += -L$$PYTORCH_DIR/lib  -ltorch_cpu -ltorch  -lc10 -ltorch_cuda  -lc10_cuda -lcaffe2_observers  \
-lcaffe2_nvrtc -lcaffe2_detectron_ops_gpu  \
-lnvrtc-builtins -lprocess_group_agent -lshm  \
-ltensorpipe_agent -ltorch -ltorch_cuda_cpp -ltorch_cuda_cu -ltorch_global_deps
}

Создайте приложение QT с помощью LibTorch

Следующим шагом является сборка исходного кода проекта QT, и вы получите ошибки, связанные с ошибкой компиляции конфликтов слотов, если ваше приложение основано на виджетах QT. Это связано с конфликтами ключевых слов slots между библиотеками QT и libtorch, и вы можете избежать этого, добавив файлы заголовков libtorch в начало исходного кода, как показано ниже.

#undef slots
#include "torch/torch.h"
#include "torch/jit.h"
#include "torch/nn.h"
#include "torch/script.h"
#define slots Q_SLOTS
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QList>

Проверьте Libtorch, загруженный с возможностью Cuda, и его конфигурацию.

qDebug() << "Cuda Device Count" << torch::cuda::device_count();
qDebug() << "cudnn_is_available" << torch::cuda::cudnn_is_available();
qDebug() << "cuda::is_available" << torch::cuda::is_available();
qDebug() << "cuda::show_config" << torch::show_config().c_str();

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

Cuda Device Count 1
cudnn_is_available true
cuda::is_available true
cuda::show_config PyTorch built with:
 - C++ Version: 199711
  - MSVC 192829913
  - Intel(R) Math Kernel Library Version 2020.0.2 Product Build 20200624 for Intel(R) 64 architecture applications
  - Intel(R) MKL-DNN v1.7.0 (Git Hash 7aed236906b1f7a05c0917e5257a1af05e9ff683)
  - OpenMP 2019
  - CPU capability usage: AVX2
  - CUDA Runtime 11.1
  - NVCC architecture flags: -gencode;arch=compute_37,code=sm_37;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_61,code=sm_61;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86;-gencode;arch=compute_37,code=compute_37
  - CuDNN 8.0.5
  - Magma 2.5.4
  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=11.1, CUDNN_VERSION=8.0.5, CXX_COMPILER=C:/w/b/windows/tmp_bin/sccache-cl.exe, CXX_FLAGS=/DWIN32 /D_WINDOWS /GR /EHsc /w /bigobj -DUSE_PTHREADPOOL -openmp:experimental -DNDEBUG -DUSE_FBGEMM -DUSE_XNNPACK, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_VERSION=1.8.1, USE_CUDA=ON, USE_CUDNN=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=OFF, USE_NNPACK=OFF, USE_OPENMP=ON,

Примечание. Необходимо создать приложение QT с помощью MSVC, поскольку libtorch не поддерживает MinGW в Windows.

Загрузите модель torchscript и запустите вывод

Загрузите модель скрипта факела в модуль факела библиотеки.

torch::jit::script::Module aLibTorchModule = torch::jit::load("torch script model path");

Выберите устройство GPU, если оно доступно, в противном случае выберите устройство CPU.

torch::DeviceType aDeviceType;
if (torch::cuda::is_available())
{
aDeviceType= torch::kCUDA;
}
else 
{
aDeviceType= torch::kCPU;
}
torch::Device aTorchDevice = torch::Device(aDeviceType);

Переместите загруженный модуль на выбранное устройство, в нашем случае это GPU

aLibTorchModule.to(aTorchDevice);

Теперь вы можете выполнить вывод в модуле libtorch, минуя правильный ввод в модель. Lib torch предоставляет функцию преобразования во входной тензор, если у вас есть открытый объект изображения cv::Mat.

auto input_tensor = torch::from_blob(
data, {1, Height, Width, 3}); // here data should opencv CV::MAT

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

input_tensor = input_tensor.permute({0, 3, 1, 2});
std::vector<double> norm_mean = {0.485, 0.456, 0.406};
std::vector<double> norm_std = {0.229, 0.224, 0.225};
input_tensor = torch::data::transforms::Normalize<>(norm_mean, norm_std)(input_tensor);

Наконец, выполните вывод в модуле libtorch, где модель загружается в начале. Метод Libtorch Forward возвращает результаты в виде тензорных объектов, и вы можете получать результаты и манипулировать ими в соответствии с вашими потребностями. Убедитесь, что вы должны переместить свой входной тензор в GPU перед обработкой вывода, если ваша модель загружена в GPU, иначе вы получите ошибку.

input_tensor = input_tensor.to(aTorchDevice);
torch::Tensor out_tensor = aLibTorchModule.forward({input_tensor}).toTensor();