前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【他山之石】c++接口libtorch介绍& vscode+cmake实践

【他山之石】c++接口libtorch介绍& vscode+cmake实践

作者头像
马上科普尚尚
发布2020-12-15 14:31:55
5.3K0
发布2020-12-15 14:31:55
举报
文章被收录于专栏:人工智能前沿讲习

“他山之石,可以攻玉”,站在巨人的肩膀才能看得更高,走得更远。在科研的道路上,更需借助东风才能更快前行。为此,我们特别搜集整理了一些实用的代码链接,数据集,软件,编程技巧等,开辟“他山之石”专栏,助你乘风破浪,一路奋勇向前,敬请关注。

作者:知乎—皮特潘

地址:https://www.zhihu.com/people/wu-er-dong

01

前言

libtorch是pytorch推出的C++接口版本,支持CPU端和GPU端的部署和训练。主要是为了满足一些工业场景主体代码是C++实现的。libtorch用于部署官方不会提供太多诸如模型推理时间、模型大小等方面的优化,主要还是为了c++移植。我的理解是:深度学习炼丹是用python,这个毋庸置疑。优化后的模型或者固定的训练流程,如果有需要,可以在c++的libtorch上再实现一遍。本文介绍libtorch的安装和环境搭建,我的环境是ubuntu18.04。

02

安装

直接下载下来解压就好,不需要安装。

03

生成测试用pt

这个非常简单,直接利用python端的接口,将训练好的模型保存成jit.trace的形式。优点是不管python端还是c++端,都不需要重新构建模型代码,移植非常方便。缺点是有些模型的算子无法保存成功,当然这也是部署的“通病”,深度学习部署很大一部分工作就是在模型转换上,另外一大部分的工作就是环境搭建上,苦笑!

代码语言:javascript
复制
def save(net, input, save_path):
    net.eval()
    traced_script_module = torch.jit.trace(net, input)
    traced_script_module.save(save_path)

def load(model_path):
    return torch.jit.load(model_path)


04




可以用测试部署

非常简单,可以参照如下代码,性能和时间和python端基本无差别。

代码语言:javascript
复制
int main()
{
    torch::DeviceType device_type;
    if (torch::cuda::is_available()) {
        device_type = torch::kCUDA;
    }
    else {
        device_type = torch::kCPU;
    }
    torch::Device device(device_type);


    std::string model_pb = "unet.pt";
    auto module = torch::jit::load(model_pb); // 加载模型
    module.to(at::kCUDA);

    cv::Mat image = cv::imread("1.jpg");
    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
    cv::resize(image, image, cv::Size(320, 160));
    image.convertTo(image, CV_32F, 1.0 / 255.0);

    // convert to tensort
    torch::Tensor img_tensor = ToTensor(image).to(at::kCUDA);

    torch::TensorOptions option(torch::kFloat32);
    torch::Tensor img_tensor = torch::from_blob(image.data, { 1, image.rows, image.cols, 3 }, option);
    img_tensor = img_tensor.permute({ 0, 3, 1, 2 });
    img_tensor = img_tensor.toType(torch::kFloat);
    img_tensor = img_tensor.div(255);
   torch::Tensor output = module.forward({ img_tensor }).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;
}


05




可用于训练

稍微复杂的是,需要自己利用c++的接口进行构建模型,并且实现优化器等操作,

代码语言:javascript
复制
#include <torch/torch.h>struct Net : torch::nn::Module {
    Net() {
        conv1 = register_module("conv1", torch::nn::Conv2d(torch::nn::Conv2dOptions(1, 6, /*kernel_size*/{ 5,5 }).padding(/*28->32*/{ 2,2 })));
        conv2 = register_module("conv2", torch::nn::Conv2d(torch::nn::Conv2dOptions(6, 16, /*kernel_size*/{ 5,5 })));
        conv3 = register_module("conv3", torch::nn::Conv2d(torch::nn::Conv2dOptions(16, 120, /*kernel_size*/{ 5,5 })));
        fc1 = register_module("fc1", torch::nn::Linear(torch::nn::LinearOptions(120, 84)));
        fc2 = register_module("fc2", torch::nn::Linear(torch::nn::LinearOptions(84, 10)));
    }
// forward 方法实现
    torch::Tensor forward(torch::Tensor x) {
        x = conv1->forward(x);//6@28x28
        x = torch::max_pool2d(x, { 2,2 }, { 2,2 });//6@14x14
        x = conv2->forward(x);//16@10x10
        x = torch::max_pool2d(x, { 2,2 }, { 2,2 });//16@10x10

        x = conv3->forward(x);//120@1x1
        x = x.view({ x.size(0),-1 });//-1 表示自动推理计算出该值
        x = fc1->forward(x);//120->84
        x = fc2->forward(x);//84->10
        x = torch::log_softmax(x,/*dim=*/1);

        return x;
    }

    torch::nn::Conv2d conv1{ nullptr };
    torch::nn::Conv2d conv2{ nullptr };
    torch::nn::Conv2d conv3{ nullptr };
    torch::nn::Linear fc1{ nullptr };
    torch::nn::Linear fc2{ nullptr };
};

int main() {
// 构建模型
    auto net = std::make_shared<Net>();
// 构建dataset
    auto train_dataset = torch::data::datasets::MNIST("E:\\data").map(
        torch::data::transforms::Stack<>());
// dataloader
    auto data_loader = torch::data::make_data_loader(train_dataset,/*batch_size=*/64);

    const size_t test_dataset_size = train_dataset.size().value();
// 优化器
    torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);

    for (size_t epoch = 1; epoch <= 100; ++epoch) {
        size_t batch_index = 0;
        int32_t correct = 0;
        int len = 0;

        for (auto& batch : *data_loader) {
            optimizer.zero_grad(); // 优化器梯度清除
            torch::Tensor prediction = net->forward(batch.data); //网络前向
// 计算loss
            torch::Tensor loss = torch::nll_loss(prediction, batch.target);
            auto pred = prediction.argmax(1);
            correct += pred.eq(batch.target).sum().template item<int64_t>();
            loss.backward();//反向传播
            optimizer.step();//优化器迭代
            len += torch::size(batch.data, 0);
            if (++batch_index % 100 == 0) {
                std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
                    << " | Loss: " << loss.item<float>() << " acc:"<<static_cast<double>(correct) / len <<std::endl;
                // Serialize your model periodically as a checkpoint.
                torch::save(net, "net.pt");//保存权重
            }
        }
    }
}


06




常用api

新建tensor

代码语言:javascript
复制
torch::Tensor x = torch::tensor({ {1,2,3,4},{2,3,4,5} }); 
torch::Tensor x = torch::rand({ 1,3, 224,224 });

cuda操作

代码语言:javascript
复制
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);

module.to(at::kCUDA);
torch::Tensor img_tensor = ToTensor(image).to(at::kCUDA);

转换类型

代码语言:javascript
复制
img_tensor = img_tensor.toType(torch::kFloat);

从图像获取

代码语言:javascript
复制
cv::Mat img = cv::imread(path);
cv::resize(img, img, size);
img.convertTo(img, CV_32F, 1.0 / 255.0);
torch::TensorOptions option(torch::kFloat32);
auto img_tensor = torch::from_blob(img.data, { 1,img.rows,img.cols,img.channels() }, option);// opencv H x W x C  torch C x H x W
img_tensor = img_tensor.permute({ 0,3,1,2 });// 调整 opencv 矩阵

模型前向

代码语言:javascript
复制
代码语言:javascript
复制
torch::Tensor output = module.forward({ img_tensor }).toTensor();
代码语言:javascript
复制
模型定义

参考上文

07

CMAKE 举例

libtorch 直接下载下来,不需要加入环境变量。

main.cpp

代码语言:javascript
复制
#include <torch/torch.h>#include <iostream>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);
    torch::Tensor tensor = torch::eye(3);
    tensor = tensor.to(at::kCUDA);
    // std:cout<<tensor<<std::endl;
    std::cout<<"hello torch"<<std::endl;
    std::cout<<tensor<<std::endl;
    return 0;
}

cMakeLists.txt

代码语言:javascript
复制
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)project(libtorch_test)find_package(Torch REQUIRED)add_executable(MAIN main.cpp)target_link_libraries(MAIN "${TORCH_LIBRARIES}")set_property(TARGET MAIN PROPERTY CXX_STANDARD 14)
代码语言:javascript
复制
编译测试

这里需要把libtorch的路径传进来,或者在cmakelist.txt加入下面几句

代码语言:javascript
复制
find_package(Torch REQUIRED
            NO_MODULE
            PATHS /home/Downloads/libtorch
            NO_DEFAULT_PATH)

终端执行

代码语言:javascript
复制
cmake -DCMAKE_PREFIX_PATH=/Downloads/libtorch ..
cmake --build . --config Release

测试

代码语言:javascript
复制
./MAIN

输出结果

成功了!

本文目的在于学术交流,并不代表本公众号赞同其观点或对其内容真实性负责,版权归原作者所有,如有侵权请告知删除。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人工智能前沿讲习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档