Различный вывод от Libtorch C ++ и pytorch

Я использую одну и ту же трассированную модель в pytorch и libtorch, но получаю разные результаты.

Код Python:

import cv2
import numpy as np 
import torch
import torchvision
from torchvision import transforms as trans


# device for pytorch
device = torch.device('cuda:0')

torch.set_default_tensor_type('torch.cuda.FloatTensor')

model = torch.jit.load("traced_facelearner_model_new.pt")
model.eval()

# read the example image used for tracing
image=cv2.imread("videos/example.jpg")

test_transform = trans.Compose([
        trans.ToTensor(),
        trans.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
    ])       

resized_image = cv2.resize(image, (112, 112))

tens = test_transform(resized_image).to(device).unsqueeze(0)
output = model(tens)
print(output)

Код C ++:

#include <iostream>
#include <algorithm> 
#include <opencv2/opencv.hpp>
#include <torch/script.h>


int main()
{
    try
    {
        torch::jit::script::Module model = torch::jit::load("traced_facelearner_model_new.pt");
        model.to(torch::kCUDA);
        model.eval();

        cv::Mat visibleFrame = cv::imread("example.jpg");

        cv::resize(visibleFrame, visibleFrame, cv::Size(112, 112));
        at::Tensor tensor_image = torch::from_blob(visibleFrame.data, { 1, visibleFrame.rows, 
                                                    visibleFrame.cols, 3 }, at::kByte);
        tensor_image = tensor_image.permute({ 0, 3, 1, 2 });
        tensor_image = tensor_image.to(at::kFloat);

        tensor_image[0][0] = tensor_image[0][0].sub(0.5).div(0.5);
        tensor_image[0][1] = tensor_image[0][1].sub(0.5).div(0.5);
        tensor_image[0][2] = tensor_image[0][2].sub(0.5).div(0.5);

        tensor_image = tensor_image.to(torch::kCUDA);
        std::vector<torch::jit::IValue> input;
        input.emplace_back(tensor_image);
        // Execute the model and turn its output into a tensor.
        auto output = model.forward(input).toTensor();
        output = output.to(torch::kCPU);
        std::cout << "Embds: " << output << std::endl;

        std::cout << "Done!\n";
    }
    catch (std::exception e)
    {
        std::cout << "exception" << e.what() << std::endl;
    }
}

Модель дает выходной тензор размера (1x512), как показано ниже.

Вывод Python

tensor([[-1.6270e+00, -7.8417e-02, -3.4403e-01, -1.5171e+00, -1.3259e+00,

-1.1877e+00, -2.0234e-01, -1.0677e+00, 8.8365e-01, 7.2514e-01,

2.3642e+00, -1.4473e+00, -1.6696e+00, -1.2191e+00, 6.7770e-01,

...

-7.1650e-01, 1.7661e-01]], device=‘cuda:0’,
grad_fn=)

Вывод C ++

Embds: Columns 1 to 8 -84.6285 -14.7203 17.7419 47.0915 31.8170 57.6813 3.6089 -38.0543


Columns 9 to 16 3.3444 -95.5730 90.3788 -10.8355 2.8831 -14.3861 0.8706 -60.7844

...

Columns 505 to 512 36.8830 -31.1061 51.6818 8.2866 1.7214 -2.9263 -37.4330 48.5854

[ CPUFloatType{1,512} ]

С использованием

  • Pytorch 1.6.0
  • Либторч 1.6.0
  • Визуальная студия 2019
  • Windows 10
  • Cuda 10.1

person Arki99    schedule 20.08.2020    source источник
comment
Вы понимаете свой (довольно длинный) код намного лучше, чем мы. Если вы хотите, чтобы мы помогли вам, было бы лучше высказать свое мнение о проблеме. Как вы думаете, почему этот код не дает правильного вывода? И что на самом деле должен делать этот код?   -  person Jan Schultke    schedule 20.08.2020
comment
И код C ++, и код Python, по сути, делают одно и то же, то есть загружается модель CNN и ей передаются входные данные. Как уже упоминалось, на выходе получается тензор размера (1x512). Проблема в том, что значения в этом выходном тензоре, заданном моделью, отличаются в C ++ и python. Я не уверен, почему это происходит, хотя входное изображение, шаги предварительной обработки и модель одинаковы в обоих.   -  person Arki99    schedule 20.08.2020
comment
вам просто нужно масштабировать один раз tensor_image.sub_(0.5).div_(0.5); также попробуйте разжать свой тензор после того, как вы создали тензор (удалите 1 из load_from_blob и просто используйте соответствующие строки и столбцы), также вам не нужно IValue, просто используйте model.forward({tensor_image})   -  person Rika    schedule 20.08.2020
comment
кстати, до этого вам нужно изменить масштаб вашего тензора на 255. а затем выполнить нормализацию   -  person Rika    schedule 20.08.2020


Ответы (1)


перед окончательной нормализацией вам нужно масштабировать ввод до диапазона 0-1, а затем продолжить нормализацию, которую вы делаете. преобразовать в число с плавающей запятой, а затем разделить на 255, чтобы добраться до цели. Вот фрагмент, который я написал, могут быть некоторые синтаксические ошибки, которые должны быть видны.
Попробуйте следующее:

#include <iostream>
#include <algorithm> 
#include <opencv2/opencv.hpp>
#include <torch/script.h>


int main()
{
    try
    {
        torch::jit::script::Module model = torch::jit::load("traced_facelearner_model_new.pt");
        model.to(torch::kCUDA);
        
        cv::Mat visibleFrame = cv::imread("example.jpg");

        cv::resize(visibleFrame, visibleFrame, cv::Size(112, 112));
        at::Tensor tensor_image = torch::from_blob(visibleFrame.data, {  visibleFrame.rows, 
                                                    visibleFrame.cols, 3 }, at::kByte);
        
        tensor_image = tensor_image.to(at::kFloat).div(255).unsqueeze(0);
        tensor_image = tensor_image.permute({ 0, 3, 1, 2 });
        ensor_image.sub_(0.5).div_(0.5);

        tensor_image = tensor_image.to(torch::kCUDA);
        // Execute the model and turn its output into a tensor.
        auto output = model.forward({tensor_image}).toTensor();
        output = output.cpu();
        std::cout << "Embds: " << output << std::endl;

        std::cout << "Done!\n";
    }
    catch (std::exception e)
    {
        std::cout << "exception" << e.what() << std::endl;
    }
}

У меня нет доступа к системе, чтобы запустить это, поэтому, если вы столкнетесь с какими-либо комментариями ниже.

person Rika    schedule 20.08.2020
comment
Благодаря тонну! это сработало. Но можете ли вы мне сказать, зачем масштабировать тензор на 255 перед его нормализацией? - person Arki99; 21.08.2020
comment
@ Arki99, это значение по умолчанию для ToTensor pytorchs. когда вы выполняете ToTensor() в преобразовании Pytorch, он просто изменяет масштаб входного изображения до диапазона 0-1. поэтому, чтобы добиться такого же поведения, вам нужно сделать то же самое в libtorch. - person Rika; 21.08.2020
comment
понятно. Большое спасибо. - person Arki99; 21.08.2020