zoukankan      html  css  js  c++  java
  • C++调用pytorch,LibTorch在win10下的vs配置和cmake的配置

    最近有个项目需要使用C++调用训练好的模型。刚好pytorch1.0版本的发布,加入了对C++的支持,准备试一试pytorch对C++的支持怎么样。这里是官方文档和教程。

    https://pytorch.org/docs/master/jit.html​pytorch.orghttps://pytorch.org/tutorials/advanced/cpp_export.html​pytorch.org

    总的来说,现在可以用python版的pytorch快速实现和训练,使用相应的API导出模型供C++版的pytorch读取,给C++版本相应输入会生成和python版本一样的预测结果。

    开发环境

    • VS2015(VS2017亲测也能通过)
    • win10
    • cmake>=3.0

    转换模型

    pytorch的C++版本用的是Torch Script,官方给了两种将pytorch模型转成Torch Script的方法。

    第一种方法,Tracing:

    这种方法比较简单,不需要添加代码到模型中。只需要传一个输入给torch.jit.trace函数,让它输出一次,然后save。

    import Image
    import torch
    import torchvision.models as models
    from torchvision import transforms as transform
    
    model_resnet = models.resnet50()
    #model_resnet.load_state_dict(torch.load("resnet_Epoch_4_Top1_99.75845336914062.pkl"))
    model_resnet.eval()
    
    image = Image.open("your image path").convert('RGB')
    
    transforms = transform.Compose([
            transform.Resize((224,224)),
            transform.ToTensor(),
            transform.Normalize(mean=[0.5]*3, std=[0.5]*3)
    ])
    
    input = centre_crop_val(image)
    input = input.unsqueeze(0)
    
    traced_script_module_resnet = torch.jit.trace(model_resnet, input)
    
    output = traced_script_module_resnet(input)
    #print(output)
    traced_script_module_resnet.save("model_resnet_jit.pt")

    使用什么做输出都无所谓,为了方便比较python版和C++版是否输出一样,建议使用一个样本来测试下,不然给对方使用的时候发现结果不一样就尴尬了(逃。需要和训练的size以及channel保持一致,同时要保证用于测试的样本和用于训练的样本的transform要一致,不然输出也不一样 。使用torch.rand或者torch.ones也是可行的,不会影响已经训练好的模型权重。

    #使用torch.rand
    input = torch.ones(1, 3, 224, 224)
    traced_script_module_resnet = torch.jit.trace(model_resnet, input)
    
    output = traced_script_module_resnet(input)
    #print(output)
    traced_script_module_resnet.save("model_resnet_jit.pt")

    第二种方法,Annotation:

    第二种适合有控制流的模型,比如你的forward方法中有if/else语句,可能就需要使用这种方法。比如用官方的例子做展示:

    import torch
    
    class MyModule(torch.nn.Module):
        def __init__(self, N, M):
            super(MyModule, self).__init__()
            self.weight = torch.nn.Parameter(torch.rand(N, M))
    
        def forward(self, input):
            if input.sum() > 0:
              output = self.weight.mv(input)
            else:
              output = self.weight + input
            return output

    对于这种模型,可以在forward方法前加一个修饰器@torch.jit.script_method。

    import torch
    
    class MyModule(torch.jit.ScriptModule):
        def __init__(self, N, M):
            super(MyModule, self).__init__()
            self.weight = torch.nn.Parameter(torch.rand(N, M))
    
        @torch.jit.script_method
        def forward(self, input):
            if bool(input.sum() > 0):
              output = self.weight.mv(input)
            else:
              output = self.weight + input
            return output
    
    my_script_module = MyModule(2, 3)
    my_script_module.save("your_model.pt")

    不管哪种方法得到的model.pt(也就是Torch Script),就可以使用C++调用它了。

    准备工作

    确定有>=3.0版本的cmake和比较高的vs版本。cmake下载

    pytorch官网下载对应的LibTorch。有GPU版CP官网下载对应的LibTorch。有GPU版CPU版、有DEBUG和RELEASE版。

    然后解压。

    有include有lib,跟其他库结构差不多。

    VS配置

    官方和其他很多都是用的cmake,其实vs也能用。新建一个空项目,然后和VS配置opencv一样,把LibTorch的include和lib添加到“包含目录”和“库目录”中就行,还需要在链接器中加入:

    torch.lib
    c10.lib
    caffe2.lib

    一般来说3个就足够,以防万一可以把所有lib都加上:

    c10.lib
    caffe2.lib
    caffe2_detectron_ops.lib
    caffe2_module_test_dynamic.lib
    clog.lib
    cpuinfo.lib
    foxi_dummy.lib
    foxi_loader.lib
    libprotobuf.lib
    libprotobuf-lite.lib
    libprotoc.lib
    onnx.lib
    onnx_proto.lib
    onnxifi_dummy.lib
    onnxifi_loader.lib
    torch.lib

    还有两个地方需要修改:
    第一项:属性->C/C++ ->常规->SDL检查->否。
    第二项:属性->C/C++ ->语言->符号模式->否。

    编写C++代码

    新建一个example.cpp,选几张测试的图片,用opencv读入然后转成tensor。训练网络的时候Tensor的shape是N x C x H x W,所以还需要把opencv转成的tensor(H x W x C)用permute转换一下,然后unsqueeze添加一维变成N x C x H x W。同时要保证测试样本和训练样本有一样的transform。

    #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 = "your image folder path";
    
    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("your model path");
    
        assert(module != nullptr);
        std::cout << "ok
    ";
    
        //输入图像
        auto image = cv::imread(image_path +"/"+ "your image name",cv::ImreadModes::IMREAD_IMREAD_COLOR);
        cv::Mat image_transfomed;
        cv::resize(image, image_transfomed, cv::Size(70, 70));
    
        // 转换为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);
    
        // 网络前向计算
        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 << '
    ';
    
    	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 << "
    ";
    
    	system("pause");
    }
    

    对同样的测试图片,我的python版模型输出为:

    同样的模型,同样的测试图片,C++版的输出:

    给出的top3结果都是一样的。

    CMAKE方法

    大家都是在linux下的cmake配置,在windows下编写好CMakeLists.txt,一样可以在windows下cmake,需要根据自己LibTorch和Opencv的路径以及cpp的名字进行修改。我的cpp文件名是example.cpp,设置的为RELEASE模式。

    cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
    project(torchlib-example)
    
    SET(CMAKE_BUILE_TYPE RELEASE)
    
    INCLUDE_DIRECTORIES(
    E:/libtorch/include
    E:/opencv3/opencv/build/include
    E:/opencv3/opencv/build/include/opencv
    E:/opencv3/opencv/build/include/opencv2
    )
    
    SET(TORCH_LIBRARIES E:/libtorch/lib)
    SET(OpenCV_LIBS E:/opencv3/opencv/build/x64/vc14/lib)
    
    LINK_DIRECTORIES(
    ${TORCH_LIBRARIES}
    ${OpenCV_LIBS}
    )
    
    add_executable(torchlib-example example.cpp)
    
    target_link_libraries(torchlib-example
    c10.lib
    caffe2.lib
    caffe2_detectron_ops.lib
    caffe2_module_test_dynamic.lib
    clog.lib
    cpuinfo.lib
    foxi_dummy.lib
    foxi_loader.lib
    libprotobuf.lib
    libprotobuf-lite.lib
    libprotoc.lib
    onnx.lib
    onnx_proto.lib
    onnxifi_dummy.lib
    onnxifi_loader.lib
    torch.lib
    opencv_world344.lib
    )
    
    set_property(TARGET torchlib-example PROPERTY CXX_STANDARD 11)

    然后再CMakeLists.txt和example.cpp目录下新建一个build文件夹,然后打开cmake-gui.exe, 填好两个目录。

    然后选择generator,一定要选择64位。

    点击Configure,没问题的话再点击Configure,然后再点击Generate就成功了。

    然后打开build文件夹中的vcxporj文件,设置项目为启动项目,并更改为release的x64模式,生成解决方案。

    最后,如果提示缺少dll,可以把LibTorch的lib文件夹加入环境变量,也可以把lib文件夹的dll全拷到cpp目录下。

  • 相关阅读:
    EntityFramework6.X 之Relationship
    EntityFramework6.X 之Inheritance Stategy
    EntityFramework6.X 之 Fulent
    EntityFramework6.X之DataAnnotations
    EntityFramework6.X 之 Database Initialization
    EntityFramework6.X 之DbContex
    EntityFramework6.X 之LocalDB&ConnectionString
    EntityFramework6.X之概述
    C提高_day03_玩转多级指针
    C提高_day03_二级指针内存示意图(没有比这重要的了)
  • 原文地址:https://www.cnblogs.com/leoking01/p/14111380.html
Copyright © 2011-2022 走看看