首頁 > 軟體

使用LibTorch進行C++呼叫pytorch模型方式

2022-12-19 14:01:27

前天由於某些原因需要利用C++呼叫PyTorch,於是接觸到了LibTorch,配了兩天最終有了一定的效果,於是記錄一下。

環境

  • PyTorch1.6.0
  • cuda10.2
  • opencv4.4.0
  • VS2017

具體過程

下載LibTorch

PyTorch官網下載LibTorch包,選擇對應的版本,這裡我選擇Stable(1.6.0),Windows,LibTorch,C++/JAVA,10.2,然後我選擇release版本下載,如下圖

下載完後先不用管它,之後再用

用pytorch生成模型檔案

我先建立了一個python檔案,載入resnet50預訓練模型,用來生成模型檔案,程式碼如下

import torch
import torchvision.models as models
from PIL import Image
import numpy as np
from torchvision import transforms

model_resnet = models.resnet50(pretrained=True).cuda()

# model_resnet.load_state_dict(torch.load("resnet_Epoch_4_Top1_99.75845336914062.pkl"))
model_resnet.eval()
# 自己選擇任意一張圖片,並將它的路徑寫在open方法裡,用來讀取影象,我這裡路徑就是‘111.jpg'了
image = Image.open("111.jpg").convert('RGB')

tf = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        # transforms.Normalize(mean=[0.5]*3, std=[0.5]*3)
])

img = tf(image)
img = img.unsqueeze(dim=0)
print(img.shape)
input = torch.rand(1, 3, 224, 224).cuda()

traced_script_module_resnet = torch.jit.trace(model_resnet, input)

output = traced_script_module_resnet(img.cuda())
print(output.shape)
pred = torch.argmax(output, dim=1)
print(pred)
traced_script_module_resnet.save("model_resnet_jit_cuda.pt")

最後可以生成一個model_resnet_jit_cuda.pt檔案,產生的輸出如下所示

第一行是我們讀取影象的shape,我們讀取圖片之後經過各種resize,增加維度,把圖片資料的shape修改成模型接受的格式,可以看到預測的結果是921,之後我們將用到生成的model_resnet_jit_cuda.pt檔案。

VS建立工程並進行環境設定

我在這個python檔案路徑下建立了這個vs工程Project1

建立完成之後我們開啟Project1資料夾,裡面內容如下

現在建立VS工程先告一段落,開始進行工程環境設定。把之前下載的LibTorch,解壓到當前目錄,解壓後會出現一個libtorch的資料夾,資料夾目錄裡的內容為

這裡將我框選的資料夾路徑設定到工程屬性當中,開啟剛才新建的VS工程,選擇專案為relaese的×64版本

然後點選專案->Project1屬性,彈出屬性頁

在屬性頁同樣注意是release的×64平臺,點選VC++目錄,在包含目錄下載入我之前框出來的include資料夾路徑,在庫目錄下載入框出來的lib資料夾路徑,同時,我們也要用到opencv,所以也需要在包含目錄下載入opencv的include資料夾與opencv2資料夾,在庫目錄下載入opencvbuildx64vc14lib,如下圖

然後在屬性頁的連結器->輸入,新增附加依賴項,首先先把opencv的依賴項新增了

opencv_world440.lib,(如果一直用的Debug模式,就新增opencv_world440d.lib),然後將libtorch/lib裡所有字尾為.lib的檔案全新增進來,開啟這個資料夾

全都寫進去,再點選確定,如下圖所示

然後點選連結器->命令列,加上/INCLUDE:?warp_size@cuda@at@@YAHXZ 這一句,加上這一句是因為我們要用cuda版本的,如果是cpu版本可以不加。

最後點選C/C++ ->常規的SDL檢查,設定為

點選C/C++ ->語言的符合模式,設定為

到此我們的設定就全部結束了!最後!複製libtorch/lib資料夾下所有檔案,貼上到工程資料夾Project1/×64/release資料夾裡(點選此處的Project1資料夾可以發現裡面也有一個×64/release,之前我也糾結是放在哪,然後我都試了一下,發現這個裡面是可以不放的)

執行VS2017工程檔案

然後我執行VS工程下一個空的main檔案,沒有報錯,設定大致是沒問題的,最後新增完整程式碼,如下

#include <torch/script.h> // One-stop header.
#include <opencv2/opencv.hpp>
#include <iostream>
#include <memory>

//https://pytorch.org/tutorials/advanced/cpp_export.html

std::string image_path = "../../111.jpg";

int main(int argc, const char* argv[]) {

	// Deserialize the ScriptModule from a file using torch::jit::load().
	//std::shared_ptr<torch::jit::script::Module> module = torch::jit::load("../../model_resnet_jit.pt");
	using torch::jit::script::Module;
	Module module = torch::jit::load("../../model_resnet_jit_cuda.pt");
	module.to(at::kCUDA);

	//assert(module != nullptr);
	//std::cout << "okn";

	//輸入影象
	auto image = cv::imread(image_path, cv::ImreadModes::IMREAD_COLOR);
	cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
	cv::Mat image_transfomed;
	cv::resize(image, image_transfomed, cv::Size(224, 224));

	// 轉換為Tensor
	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);

	// 網路前向計算
	at::Tensor output = module.forward({ tensor_image }).toTensor();
	//std::cout << "output:" << output << std::endl;

	auto prediction = output.argmax(1);
	std::cout << "prediction:" << prediction << std::endl;

	int maxk = 3;
	auto top3 = std::get<1>(output.topk(maxk, 1, true, true));

	std::cout << "top3: " << top3 << 'n';

	std::vector<int> res;
	for (auto i = 0; i < maxk; i++) {
		res.push_back(top3[0][i].item().toInt());
	}
	for (auto i : res) {
		std::cout << i << " ";
	}
	std::cout << "n";

	system("pause");
}

得到最終輸出為921,可以看到和之前的python檔案下輸出一致,這裡還輸出了它的top前三,分別是921,787,490。

注意到,我的這兩個輸出相同的前提條件是:

1、確定載入的是由對應python檔案生成的模型!

2、輸入的圖片是同一張!並且在python下和C++下進行了同樣的轉換,這裡我在python下,將它進行了RGB模型的轉換,resize(224, 224),並且將它的每一個元素值除以255.0,轉換到0~1之間(ToTensor()方法),最後維度轉換為1, 3, 224, 224,在C++中同樣需要將BGR模型轉化為RGB模型,進行影象縮放至224,224,並且將畫素值除以255,將型別轉化為float型別,最後維度同樣轉換為1,3,224,224,再進行網路前向計算。

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com