Write a neural network with cpp (libtorch version)(1)

I thought that installing libTorch would take a lot of trouble, but it was successfully installed step by step according to the official guide. In fact, the main reason is that I am not familiar with how cmake compiles a C++ project. The first is to download libtorch from the official website. The address given below is the libtorch download address I used in my subsequent sharing.

libtorch usage

For the time being, the purpose of libtorch is to load the trained model into the C++ environment and then perform inference or some additional work, because many network implementations are implemented with the python version of torch, that is, pytorch.

wget https://download.pytorch.org/libtorch/nightly/cpu/libtorch-shared-with-deps-latest.zip
unzip libtorch-shared-with-deps-latest.zip
复制代码

The example given on the official website is a project built with CMakeLists. Of course, you can use your handy tool to build the project. In fact, I am also a C++ entry-level developer.

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(example-app)

find_package(Torch REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")

add_executable(example-app main.cpp)
target_link_libraries(example-app "${TORCH_LIBRARIES}")
set_property(TARGET example-app PROPERTY CXX_STANDARD 14)

复制代码

Regarding cmake, an advanced compilation and configuration tool, as individuals gain an in-depth understanding of it, relevant sharing will also be launched.

  • cmake_minimum_required This mainly specifies the minimum supported cmake version
  • add_executable add executable
  • find_package can find torch installation
  • set can set some parameters
  • target_link_libraries
  • set_property set property
#include <iostream>

int main

{
std::cout << "hello world" std::endl;
}
复制代码
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/absolute/path/to/libtorch ..
cmake --build . --config Release
复制代码

Note here that is /absolute/path/to/libtorchan absolute path, then

-D The following parameters are custom parameters, CMAKE_PREFIX_PATH indicates the installation directory to be compiled, you need to replace /absolute/path/to with the directory where you just decompressed libtorch

Download dataset

Do some preparatory work, that is, you need to download the database first, otherwise an error will be reported when running the program. Prepare a piece of python code here

import torch
import torchvision
from torchvision import transforms

if __name__ == "__main__":
    train_dataset = torchvision.datasets.MNIST(root='./data',
                    train=True,
                    transform=transforms.ToTensor(),
                    download=True
                    )
复制代码

After the download is complete, we need to copy train-labels-idx3-ubyte and train-images-idx3-ubyte to data under the build folder, otherwise an error will be reported at runtime.

cmake_minimum_required(VERSION 3.1 FATAL_ERROR)

project(NNet)

set(CMAKE_PREFIX_PATH /home/ubuntu16/Downloads/libtorch)

find_package(Torch REQUIRED)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")

add_executable(main src/main.cpp include/network.h)

target_include_directories(main PRIVATE include)

target_link_libraries(main ${TORCH_LIBRARIES})

set_property(TARGET main PROPERTY CXX_STANDARD 14)
复制代码

当我们配置好如何编译一个项目之后,我们就可以做更多有趣的事,其实也就是将 c++ 作为前端来开发一个神经网络,首先我们来看一看如何在 c++ 中定义和操作一个模块(module),想必大家都有一定开发神经网络经验,可以用 python 在 pytorch 框架下可以开发一个小型神经网络,或者自己有过经验去实现一个经典神经网络,例如 VGG ResNet。

这个与 pytorch 提供 python 的接口类似,因为为 C++ 提供可重复使用,用于搭建神经网络基础设施 module (模块)。所有其他的模块都是从这个基础 module 派生出来的。在 python 中,这个类是torch.nn.Module,而在 C++ 对应的是 torch::nn::Module。模块所封装了前向传播的方法 forward() 外,还包括 parameter (参数)、buffers(缓冲器)和submodules(子模块)。

参数和缓冲区以张量的形式存储状态。参数记录梯度,而缓冲区不记录。参数通常是你的神经网络的可训练权重。缓冲区的例子包括用于批量规范化的平均值和方差。为了重复使用特定的逻辑和状态块,PyTorch的API允许模块嵌套。一个嵌套的模块被称为子模块。

#pragma once

#include <iostream>
#include <torch/torch.h>

struct NetImpl:torch::nn::Module{

    NetImpl(int fc1_dims, int fc2_dims):fc1(fc1_dims,fc1_dims),fc2(fc1_dims,fc2_dims),out(fc2_dims,1){

    register_module("fc1",fc1);
    register_module("fc2",fc2);
    register_module("out",out);

}

torch::Tensor forward(torch::Tensor x){
    x = torch::relu(fc1(x));
    x = torch::relu(fc2(x));
    x = out(x);

    return x;

}

torch::nn::Linear fc1{nullptr}, fc2{nullptr}, out{nullptr};

};

TORCH_MODULE(Net);
复制代码
#include "network.h"

#include <iostream>

#include <torch/torch.h>

\


using namespace std;

using namespace torch;



int main()

{

Net network(50,10);

cout << network << endl;

cout << "hello world" << endl;

}
复制代码

搭建网络

#include <torch/torch.h>
#include <iostream>


// 定义一个简单全连接层组成的网络

struct Net: torch::nn::Module{
  Net(){
    // 构造并且注册网络结构中层
    fc1 = register_module("fc1",torch::nn::Linear(784,64));
    fc2 = register_module("fc2",torch::nn::Linear(64,32));
    fc3 = register_module("fc3",torch::nn::Linear(32,10));
  }
  // 实现前向传播
  torch::Tensor forward(torch::Tensor x){
    x = torch::relu(fc1->forward(x.reshape({x.size(0),784})));
    x = torch::dropout(x, /*p=*/0.5, /*train=*/is_training());
    x = torch::relu(fc2->forward(x));
    x = torch::log_softmax(fc3->forward(x), /*dim=*/1);
    return x;
  }

  torch::nn::Linear fc1{nullptr}, fc2{nullptr}, fc3{nullptr};
};

int main() {
  // 创建一个新的网络
  auto net = std::make_shared<Net>();

  auto data_loader = torch::data::make_data_loader(
      torch::data::datasets::MNIST("./data").map(
          torch::data::transforms::Stack<>()),
      /*batch_size=*/64);

  torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);

  for (size_t epoch = 1; epoch <= 10; ++epoch) {
    size_t batch_index = 0;
    // Iterate the data loader to yield batches from the dataset.
    for (auto& batch : *data_loader) {
      // 重置梯度
      optimizer.zero_grad();
      // 在输入数据上运行模型
      torch::Tensor prediction = net->forward(batch.data);
      // 根据模型预测值与真实值之间差距来计算损失值
      torch::Tensor loss = torch::nll_loss(prediction, batch.target);
      //在反向传播中计算参数的梯度
      loss.backward();

      // 基于计算得到梯度来更新参数
      optimizer.step();
      if (++batch_index % 100 == 0) {
        std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
                  << " | Loss: " << loss.item<float>() << std::endl;
        // 保存模型
        torch::save(net, "net.pt");
      }
    }
  }
}
复制代码

Guess you like

Origin juejin.im/post/7080099847163871239