C++版PyTorch之MNIST实战,从入门到?

喜欢就关注我们吧!

输出Hello World是学习任何一门语言的标志性入门过程,对于深度学习而言,成功的训练一个三层的神经网络区分出MNIST数据集就类似于输出了Hello World。在上一期《厌倦了Python训练?C++版本PyTorch尝试一下》中我们给大家普及了如何在C++中使用PyTorch,今天我们将带领大家用C++构建一个三层的神经网络并区分MNIST手写字符数据。

MNIST数据集简介

尽管MNIST数据集已经非常常见,但是为了保证这篇教程的完整性,我们还是简要介绍一下MNIST数据集。

MNIST数据集由60000(训练集)+10000(测试集)张手写字符组成,每张图片的大小为28×28,数据集可以在http://yann.lecun.com/exdb/mnist/下载,MNIST数据集可视化的结果为:

网络结构

由于区分MNIST数据是一个非常简单的任务,因此在深度学习蓬勃发展之前,一个三层的神经网络就已经可以很好的解决这个问题。与厌倦了Python训练?C++版本PyTorch尝试一下中的两层神经网络类似,我们同样使用一个结构体和一个forward函数来定义这个三层的神经网络

struct Net : torch::nn::Module {
  Net() {
    // Construct and register two Linear submodules.
    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));
  }


  // Implement the Net's algorithm.
  torch::Tensor forward(torch::Tensor x) {
    // Use one of many tensor manipulation functions.
    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;
  }


  // Use one of many "standard library" modules.
  torch::nn::Linear fc1{nullptr}, fc2{nullptr}, fc3{nullptr};
};

这个神经网络的三层都是线性层,即该层的激活函数为

其中w为权重,b为偏移值。维度的变化为784->64->32->10,784是将28×28每个像素作为一个维度,最后的10是因为有10类。

在forward函数中,每经过一个线性层,就会进行一次relu运算,同时还加上dropout防止过拟合。最后一层是log_softmax函数,将10类的概率值全部归一化到[0, 1],并且保证十个数值之和为1以构建一个完整的概率分布。有关于log_softmax的相关知识,如果有不懂的同学可以参考Pytorch中交叉熵Loss趣解,其中有详细的数学推导。

主函数定义

与Python不同,主函数是C++执行的入口。C++与Python在训练过程中的代码非常类似。我们把几个重要的步骤用加粗的方式强调出来,供大家学习总结。

首先,实例化网络

// Create a Net
auto net = std::make_shared<Net>();

接下来,定义数据接口(dataloader)

// Create a multi-threaded data loader for the MNIST dataset.
auto data_loader = torch::data::make_data_loader(
    torch::data::datasets::MNIST("./data").map(
        torch::data::transforms::Stack<>()),
    /*batch_size=*/64);

这里面我们不需要下载MNIST数据,与Python中类似,作为一个非常通用的数据集,MNIST数据集有自己对应的接口。这里我们定义batch_size为64,即每次向网络输入64张28×28的图片。

定义优化器,这里我们使用SGD优化器,初始的学习率定为0.1,

// Instantiate an SGD optimization algorithm to update our Net's parameters.
torch::optim::SGD optimizer(net->parameters(), /*lr=*/0.01);

定义数据集循环(epoch),批次循环(iteration),正向传播,反向传播,参数更新,保存权重等,这里的代码与Python非常类似,

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) {
    // Reset gradients.
    optimizer.zero_grad();
    // Execute the model on the input data.
    torch::Tensor prediction = net->forward(batch.data);
    // Compute a loss value to judge the prediction of our model.
    torch::Tensor loss = torch::nll_loss(prediction, batch.target);
    // Compute gradients of the loss w.r.t. the parameters of our model.
    loss.backward();
    // Update the parameters based on the calculated gradients.
    optimizer.step();
    // Output the loss and checkpoint every 100 batches.
    if (++batch_index % 100 == 0) {
      std::cout << "Epoch: " << epoch << " | Batch: " << batch_index
                << " | Loss: " << loss.item<float>() << std::endl;
      // Serialize your model periodically as a checkpoint.
      torch::save(net, "net.pt");
    }
  }
}

这里我们定义了10个epoch,每100个iteration会在控制台输出一次训练的情况并保存一次当前的权重。

运行步骤

完整的项目大家可以点击阅读原文或者是后台回复“cppMNIST”得到。首先我们需要编译C++的代码,我们使用的是CMakefile的方式。编译的方法为:

$ cd mnist
$ mkdir build
$ cd build
$ cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..
$ make

其中/path/to/libtorch是libtorch的路径。编译过程中会下载MNIST数据

-- The C compiler identification is GNU 5.5.0
-- The CXX compiler identification is GNU 5.5.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Looking for pthread_create in pthreads
-- Looking for pthread_create in pthreads - not found
-- Looking for pthread_create in pthread
-- Looking for pthread_create in pthread - found
-- Found Threads: TRUE
-- Found CUDA: /usr/local/cuda-10.1 (found version "10.1")
-- Caffe2: CUDA detected: 10.1
-- Caffe2: CUDA nvcc is: /usr/local/cuda-10.1/bin/nvcc
-- Caffe2: CUDA toolkit directory: /usr/local/cuda-10.1
-- Caffe2: Header version is: 10.1
-- Found CUDNN: /usr/local/cuda-10.1/lib64/libcudnn.so
-- Found cuDNN: v7.6.5  (include: /usr/local/cuda-10.1/include, library: /usr/local/cuda-10.1/lib64/libcudnn.so)
-- Automatic GPU detection failed. Building for common architectures.
-- Autodetected CUDA architecture(s): 3.5;5.0;5.2;6.0;6.1;7.0;7.0+PTX;7.5;7.5+PTX
-- Added CUDA NVCC flags for: -gencode;arch=compute_35,code=sm_35;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_52,code=sm_52;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_61,code=sm_61;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_70,code=compute_70;-gencode;arch=compute_75,code=compute_75
-- Found torch: /mnt/libtorch/lib/libtorch.so
-- Downloading MNIST dataset
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz ...
0% |################################################################| 100%
Unzipped /mnt/examples/cpp/mnist/build/data/train-images-idx3-ubyte.gz ...
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz ...
0% |################################################################| 100%
Unzipped /mnt/examples/cpp/mnist/build/data/train-labels-idx1-ubyte.gz ...
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz ...
0% |################################################################| 100%
Unzipped /mnt/examples/cpp/mnist/build/data/t10k-images-idx3-ubyte.gz ...
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz ...
0% |################################################################| 100%
Unzipped /mnt/examples/cpp/mnist/build/data/t10k-labels-idx1-ubyte.gz ...
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/examples/cpp/mnist/build

编译完成后我们会得到一个名为mnist的二进制文件,接下来执行二进制文件,就可以在控制台到看到如下的输出了。

$ ./mnist
Train Epoch: 1 [59584/60000] Loss: 0.4232
Test set: Average loss: 0.1989 | Accuracy: 0.940
Train Epoch: 2 [59584/60000] Loss: 0.1926
Test set: Average loss: 0.1338 | Accuracy: 0.959
Train Epoch: 3 [59584/60000] Loss: 0.1390
Test set: Average loss: 0.0997 | Accuracy: 0.969
Train Epoch: 4 [59584/60000] Loss: 0.1239
Test set: Average loss: 0.0875 | Accuracy: 0.972
...

自此,我们就可以用C++训练一个三层的神经网络对MNIST数据集进行训练,在后面的教程中我们会教大家如何用PyTorch的C++接口训练一个DCGAN,敬请期待。

往期推荐

厌倦了Python训练?C++版本PyTorch尝试一下

2020年代,中国AI创业公司将走向何方

都2020年了,在校学生还值得继续转行搞AI吗

AIZOO,打造中国最大的深度学习和人工智能社区,欢迎关注我们,也欢迎添加下方小助手的微信,邀请您加入我们的千人深度学习爱好者社区。

欢迎扫描下方的二维码添加小助手微信,邀请您加入我们的微信交流群。

群里有多位清北复交、BAT、AI独角兽大牛和众多深度学习er在一起愉快的交流技术,有任何问题,都可以咨询大家,欢迎你的加入哦。

 添加小助手微信,邀您进AIZOO技术交流群

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值