pytorch C++ model deployment

 First train a simple image classifier. code show as below:

import torch.optim as optim
import torch.nn.functional as F
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.onnx
import torchvision
import torchvision.transforms as transforms
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=0)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=0)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')


# functions to show an image


def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()

print(images.shape)

# show images
imshow(torchvision.utils.make_grid(images))
# print labels
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 12, 3)
        self.conv3 = nn.Conv2d(12, 32, 3)
        self.fc1 = nn.Linear(32 * 4 * 4, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = F.relu(self.conv3(x))
        x = x.view(-1, 32 * 4 * 4)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
net.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

for epoch in range(10):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data[0].to(device), data[1].to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)

        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print(outputs)
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')


# 导出网络到ONNX
dummy_input = torch.randn(1, 3, 32, 32).to(device)
torch.onnx.export(net, dummy_input, "torch.onnx")

# 保存网络位TORCHSCRIPT
dummy_input = torch.randn(1, 3, 32, 32).to(device)
traced_cell = torch.jit.trace(net, dummy_input)
traced_cell.save("tests.pth")

        According to the instructions in the official opencv documentation, the following frameworks can be supported: Caffe, Darknet, Onnx, Tensorflow, Torch, etc. But unfortunately, there is no pytoch that I use, but according to the method in the third reference link, ONNX can be used to save the country by curve. First, save the network and parameters in the corresponding format by using the method shown in the method of saving model 3. Then use the Net cv::dnn::readNetFromONNX ( const String & onnxFile )function provided by opencv to read the saved network. The code is implemented as follows:

//测试opencv加载pytorch模型
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace cv::dnn;
#include <fstream>
#include <iostream>
#include <cstdlib>
using namespace std;


int main()
{
	String modelFile = "./torch.onnx";
	String imageFile = "./dog.jpg";

	dnn::Net net = cv::dnn::readNetFromONNX(modelFile); //读取网络和参数
	
	Mat image = imread(imageFile); // 读取测试图片
	cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
	Mat inputBolb = blobFromImage(image, 0.00390625f, Size(32, 32), Scalar(), false, false); //将图像转化为正确输入格式

	net.setInput(inputBolb); //输入图像

	Mat result = net.forward(); //前向计算

	cout << result << endl;
}


The above code is to simplify the code of the first reference link, and change the model of the input network from torch to ONNX format.
The results of the operation are as follows:

[-0.19793352, -4.0697966, 1.2769811, 2.7011304, 0.22390884, 1.9039617, -0.47333384, -0.15912014, 0.32441139, -2.4327304]

Use LibTorch officially provided by pytorch to load the trained model and network

 Reference link:
windows+VS2019+PyTorchLib configuration Use guide
C++ to call pytorch, LibTorch's vs configuration under win10 and cmake configuration
, load the TORCHSCRIPT model official website link in C++

First, let me explain that there are two ways to save pytroch as TORCHSCRIPT, one is the tracking type, and the other is the scripting type. For details, see the official document . In theory, this method can be saved in two ways. Here we use the tracking method.

First, configure the LibTorch environment according to the method in the first reference link. On this basis, refer to link 2. There are two more places that need to be modified:

  • Properties -> C/C++ -> General -> SDL check -> No.
  • Properties -> C/C++ -> Language -> Symbol Mode -> No.

Then copy and paste the sample code in it to test, but when I personally run ToTensor(image).to(at::kCUDA);this statement, I report an error, prompting that ToTensor() is not defined. The function of this sentence is also very simple, which is to convert ordinary image format into model input. Format, so I modified the conversion code according to the second reference link, the code is as follows:

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

// 有人说调用的顺序有关系,我这好像没啥用~~

int main()
{
    torch::DeviceType device_type;
    if (torch::cuda::is_available()) {
        std::cout << "CUDA available! Predicting on GPU." << std::endl;
        device_type = torch::kCUDA;
    }
    else {
        std::cout << "Predicting on CPU." << std::endl;
        device_type = torch::kCPU;
    }
    torch::Device device(device_type);

    //Init model
    std::string model_pb = "tests.pth";
    auto module = torch::jit::load(model_pb);
    module.to(at::kCUDA);

    auto image = cv::imread("dog.jpg", cv::ImreadModes::IMREAD_COLOR);
    cv::Mat image_transfomed;
    cv::resize(image, image_transfomed, cv::Size(32, 32));

    // convert to tensort
    torch::Tensor tensor_image = torch::from_blob(image_transfomed.data,
        { image_transfomed.rows, image_transfomed.cols,3 }, torch::kByte);
    tensor_image = tensor_image.permute({ 2,0,1 });
    tensor_image = tensor_image.toType(torch::kFloat);
    tensor_image = tensor_image.div(255);
    tensor_image = tensor_image.unsqueeze(0);
    tensor_image = tensor_image.to(at::kCUDA);
    torch::Tensor output = module.forward({ tensor_image }).toTensor();
    auto max_result = output.max(1, true);
    auto max_index = std::get<1>(max_result).item<float>();
    std::cout << output << std::endl;
    //return max_index;
    return 0;

}

The results of the operation are as follows:

CUDA available! Predicting on GPU.
 1.0824 -4.6106  1.0189  2.9937  1.4570  1.4964 -1.3164 -0.7753  0.4567 -3.2543
[ CUDAFloatType{1,10} ]

Original link: https://blog.csdn.net/cai1493105270/article/details/108127290

Guess you like

Origin blog.csdn.net/wzhrsh/article/details/110151945