Escriba una red neuronal con cpp (versión libtorch)(1)

Pensé que instalar libTorch sería muy complicado, pero se instaló con éxito paso a paso de acuerdo con la guía oficial. De hecho, la razón principal es que no estoy familiarizado con la forma en que cmake compila un proyecto de C++. La primera es descargar libtorch desde el sitio web oficial. La dirección que se proporciona a continuación es la dirección de descarga de libtorch que utilicé en mi uso compartido posterior.

uso de libtorch

Por el momento, el propósito de libtorch es cargar el modelo entrenado en el entorno C++ y luego realizar inferencias o algún trabajo adicional, porque muchas implementaciones de red se implementan con la versión python de torch, es decir, pytorch.

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

El ejemplo dado en el sitio web oficial es un proyecto construido con CMakeLists. Por supuesto, puede usar su práctica herramienta para construir el proyecto. De hecho, también soy un desarrollador básico de C++.

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)

复制代码

Con respecto a cmake, una herramienta avanzada de compilación y configuración, a medida que las personas obtengan una comprensión más profunda de ella, también se lanzará el intercambio relevante.

  • cmake_minimum_required Esto especifica principalmente la versión mínima admitida de cmake
  • add_executable añadir ejecutable
  • find_package puede encontrar la instalación de la antorcha
  • set puede establecer algunos parámetros
  • target_link_libraries
  • set_property establecer propiedad
#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
复制代码

Tenga en cuenta aquí que es /absolute/path/to/libtorchuna ruta absoluta, entonces

-D Los siguientes parámetros son parámetros personalizados, CMAKE_PREFIX_PATH indica el directorio de instalación que se compilará, debe reemplazar /absolute/path/to con el directorio donde acaba de descomprimir libtorch

Descargar conjunto de datos

Realice un trabajo preparatorio, es decir, primero debe descargar la base de datos; de lo contrario, se informará un error al ejecutar el programa. Prepare un fragmento de código python aquí

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
                    )
复制代码

Una vez completada la descarga, debemos copiar train-labels-idx3-ubyte y train-images-idx3-ubyte a los datos en la carpeta de compilación; de lo contrario, se informará un error en tiempo de ejecución.

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");
      }
    }
  }
}
复制代码

Supongo que te gusta

Origin juejin.im/post/7080099847163871239
Recomendado
Clasificación