资源| 博士生开源深度学习C++库DLL:快速构建卷积受限玻尔兹曼机

机器之心 2017-10-08 13:21 阅读:72
摘要:选自baptiste-wicht机器之心编译参与:刘晓坤、蒋思源BaptisteWicht公布了自己编写的深度学习库DLL1.0,可以通过C++接口使用。文中通过几个例子介绍了DLL调用全连接网络、D

选自baptiste-wicht

机器之心编译

参与:刘晓坤、蒋思源



Baptiste Wicht公布了自己编写的深度学习库DLL1.0,可以通过C++接口使用。文中通过几个例子介绍了DLL调用全连接网络、DNN的能力,并通过实验和其它流行框架如TensorFlow、Keras、Torch和Caffe作了综合性能比较。



很高兴公布深度学习库 Deep Learning Library(DLL)1.0 的第一个版本。DLL 是一个神经网络库,致力于提供快速和易用的使用体验。



项目地址:https://github.com/wichtounet/dll



我从四年前为完成 Ph.D. 论文而开始搭建这个库。我需要一个好用的库来训练和使用受限玻尔兹曼机(RBMs),而当时并没有这样的条件。因此,我决定自己编写。现在它能很完美的支持 RBM 和卷积 RBM(CRBM)模型。RBMs(或深度信念网络,DBNs)的堆栈可以用对比分歧(Contrastive Divergence)预训练,然后用 mini-batch 梯度下降或共轭梯度法进行微调,或者直接作为特征提取器。经过多年发展,该库已经扩展到可以处理人工神经网络(ANNs)和卷积神经网络(CNNs)了。其中网络还可以训练常规的自动编码器。还能使用多种高级层比如 Dropout 或 Batch 正则化,以及自适应学习率技术比如 Adadelta 和 Adam。这个库还能集成支持这几个数据集:MNIST,CIFAR-10 和 ImageNet。



这个库可以通过 C++接口使用,完全的仅有标头档(fully header-only),需要 C++14 编译器,即至少需要 clang3.9 或 GCC6.3。



调用案例



我们先看看下面这个使用库的例子:



 
  1. # include "dll/neural/dense_layer.hpp"

  2. # include "dll/network.hpp"

  3. # include "dll/datasets.hpp"

  4. int main ( int /*argc*/ , char * /*argv*/ []) {

  5. // Load the dataset

  6. auto dataset = dll :: make_mnist_dataset ( dll :: batch_size < 100 >{}, dll :: normalize_pre {});

  7. // Build the network

  8. using network_t = dll :: dyn_network_desc <

  9. dll :: network_layers <

  10. dll :: dense_layer < 28 * 28 , 500 >,

  11. dll :: dense_layer < 500 , 250 >,

  12. dll :: dense_layer < 250 , 10 , dll :: softmax >

  13. >

  14. , dll :: updater < dll :: updater_type :: NADAM > // Nesterov Adam (NADAM)

  15. , dll :: batch_size < 100 > // The mini-batch size

  16. , dll :: shuffle // Shuffle before each epoch

  17. >:: network_t ;

  18. auto net = std :: make_unique < network_t >();

  19. // Train the network for performance sake

  20. net -> fine_tune ( dataset . train (), 50 );

  21. // Test the network on test set

  22. net -> evaluate ( dataset . test ());

  23. return 0 ;

  24. }



这个例子是在 MNIST 数据集上训练并测试的简单 3 层全连接神经网络。



首先,对于头文件(include),你需要包含你将使用的层,在这个例子中只有密集层(dense layer)。然后,你需要包含 network.hpp,这是每一个网络的基本头文件。而且最后的标头就是数据集支持。



在 main 函数中,首先需要加载全部 MNIST 数据集,然后给出两个选项(该函数有一系列可选的参数)。在这里,我们设置批量大小,并指示每一个样本都必须归一化为平均值为 0,方差为 1。



之后是很重要的部分,即网络的声明。在 DLL 中,一个网络是一个类型(type)。类型有两个性质,层(layers,包含在 dll::network_layers 中),和选项(options,一系列参数选项),跟随层之后。在这个例子中,我们声明了三个层,第一层有 500 个隐藏单元,第二层有 250 个,而最后一层有 10 个。每一层都有一系列参数选项。最后一层使用 Softmax 激活函数,而不是默认的 Sigmoid 函数。网络本身有 3 个选项。我们将使用 Nesterov Adam(NAdam)优化器,批量大小为 100(必须等于前面数据集提取时声明的批量大小),而在每一个 epoch 之前数据集将被重组(shuffled)。



然后,我们将简单地使用 std::make_unique 命令创建该网络,在训练集上训练 50 个 epoch,并在测试集上测试。



以下是该网络的代码:



 
  1. Network with 3 layers

  2. Dense ( dyn ): 784 -> SIGMOID -> 500

  3. Dense ( dyn ): 500 -> SIGMOID -> 250

  4. Dense ( dyn ): 250 -> SOFTMAX -> 10

  5. Total parameters : 519500

  6. Dataset

  7. Training : In - Memory Data Generator

  8. Size : 60000

  9. Batches : 600

  10. Augmented Size : 60000

  11. Testing : In - Memory Data Generator

  12. Size : 10000

  13. Batches : 100

  14. Augmented Size : 10000

  15. Train the network with "Stochastic Gradient Descent"

  16. Updater : NADAM

  17. Loss : CATEGORICAL_CROSS_ENTROPY

  18. Early Stop : Goal ( error )

  19. With parameters :

  20. epochs = 50

  21. batch_size = 100

  22. learning_rate = 0.002

  23. beta1 = 0.9

  24. beta2 = 0.999

  25. Epoch 0 / 50 - Classification error : 0.03248 Loss : 0.11162 Time 3187ms

  26. Epoch 1 / 50 - Classification error : 0.02737 Loss : 0.08670 Time 3063ms

  27. Epoch 2 / 50 - Classification error : 0.01517 Loss : 0.04954 Time 3540ms

  28. Epoch 3 / 50 - Classification error : 0.01022 Loss : 0.03284 Time 2954ms

  29. Epoch 4 / 50 - Classification error : 0.00625 Loss : 0.02122 Time 2936ms

  30. Epoch 5 / 50 - Classification error : 0.00797 Loss : 0.02463 Time 2729ms

  31. Epoch 6 / 50 - Classification error : 0.00668 Loss : 0.02066 Time 2921ms

  32. Epoch 7 / 50 - Classification error : 0.00953 Loss : 0.02710 Time 2894ms

  33. Epoch 8 / 50 - Classification error : 0.00565 Loss : 0.01666 Time 2703ms

  34. Epoch 9 / 50 - Classification error : 0.00562 Loss : 0.01644 Time 2759ms

  35. Epoch 10 / 50 - Classification error : 0.00595 Loss : 0.01789 Time 2572ms

  36. Epoch 11 / 50 - Classification error : 0.00555 Loss : 0.01734 Time 2586ms

  37. Epoch 12 / 50 - Classification error : 0.00505 Loss : 0.01446 Time 2575ms

  38. Epoch 13 / 50 - Classification error : 0.00600 Loss : 0.01727 Time 2644ms

  39. Epoch 14 / 50 - Classification error : 0.00327 Loss : 0.00898 Time 2636ms

  40. Epoch 15 / 50 - Classification error : 0.00392 Loss : 0.01180 Time 2660ms

  41. Epoch 16 / 50 - Classification error : 0.00403 Loss : 0.01231 Time 2587ms

  42. Epoch 17 / 50 - Classification error : 0.00445 Loss : 0.01307 Time 2566ms

  43. Epoch 18 / 50 - Classification error : 0.00297 Loss : 0.00831 Time 2857ms

  44. Epoch 19 / 50 - Classification error : 0.00335 Loss : 0.01001 Time 2931ms

  45. Epoch 20 / 50 - Classification error : 0.00378 Loss : 0.01081 Time 2772ms

  46. Epoch 21 / 50 - Classification error : 0.00332 Loss : 0.00950 Time 2964ms

  47. Epoch 22 / 50 - Classification error : 0.00400 Loss : 0.01210 Time 2773ms

  48. Epoch 23 / 50 - Classification error : 0.00393 Loss : 0.01081 Time 2721ms

  49. Epoch 24 / 50 - Classification error : 0.00415 Loss : 0.01218 Time 2595ms

  50. Epoch 25 / 50 - Classification error : 0.00347 Loss : 0.00947 Time 2604ms

  51. Epoch 26 / 50 - Classification error : 0.00535 Loss : 0.01544 Time 3005ms

  52. Epoch 27 / 50 - Classification error : 0.00272 Loss : 0.00828 Time 2716ms

  53. Epoch 28 / 50 - Classification error : 0.00422 Loss : 0.01211 Time 2614ms

  54. Epoch 29 / 50 - Classification error : 0.00417 Loss : 0.01148 Time 2701ms

  55. Epoch 30 / 50 - Classification error : 0.00498 Loss : 0.01439 Time 2561ms

  56. Epoch 31 / 50 - Classification error : 0.00385 Loss : 0.01085 Time 2704ms

  57. Epoch 32 / 50 - Classification error : 0.00305 Loss : 0.00879 Time 2618ms

  58. Epoch 33 / 50 - Classification error : 0.00343 Loss : 0.00889 Time 2843ms

  59. Epoch 34 / 50 - Classification error : 0.00292 Loss : 0.00833 Time 2887ms

  60. Epoch 35 / 50 - Classification error : 0.00327 Loss : 0.00895 Time 2644ms

  61. Epoch 36 / 50 - Classification error : 0.00203 Loss : 0.00623 Time 2658ms

  62. Epoch 37 / 50 - Classification error : 0.00233 Loss : 0.00676 Time 2685ms

  63. Epoch 38 / 50 - Classification error : 0.00298 Loss : 0.00818 Time 2948ms

  64. Epoch 39 / 50 - Classification error : 0.00410 Loss : 0.01195 Time 2778ms

  65. Epoch 40 / 50 - Classification error : 0.00173 Loss : 0.00495 Time 2843ms

  66. Epoch 41 / 50 - Classification error : 0.00232 Loss : 0.00709 Time 2743ms

  67. Epoch 42 / 50 - Classification error : 0.00292 Loss : 0.00861 Time 2873ms

  68. Epoch 43 / 50 - Classification error : 0.00483 Loss : 0.01365 Time 2887ms

  69. Epoch 44 / 50 - Classification error : 0.00240 Loss : 0.00694 Time 2918ms

  70. Epoch 45 / 50 - Classification error : 0.00247 Loss : 0.00734 Time 2885ms

  71. Epoch 46 / 50 - Classification error : 0.00278 Loss : 0.00725 Time 2785ms

  72. Epoch 47 / 50 - Classification error : 0.00262 Loss : 0.00687 Time 2842ms

  73. Epoch 48 / 50 - Classification error : 0.00352 Loss : 0.01002 Time 2665ms

  74. Epoch 49 / 50 - Classification error : 0.00232 Loss : 0.00668 Time 2747ms

  75. Restore the best ( error ) weights from epoch 40

  76. Training took 142s

  77. error : 0.02040

  78. loss : 0.08889

首先正如代码中所示,是网络和数据集的展示,然后是网络的训练过程的每一个 epoch 的信息,最后是评估的结果。在大约 2 分半的时间内就能训练一个可以识别 MNIST 数字的网络,而错误率是 2.04%,这个结果不错,但还能继续优化。



简单介绍一下如何编译。可以直接使用 sudo make install_headers 命令下载 dll 库到你计算机 checked-out dll 文件夹上,然后使用一下命令对文件进行简单的编译:

 

clang++ -std=c++14 file.cpp

或者,如果需要将 dll 复制到本地的 dll 目录中,你需要具体说明头文件的文件夹:

 

clang++ -std=c++14 -Idll/include -Idll/etl/lib/include -dll/Ietl/include/ -Idll/mnist/include/ -Idll/cifar-10/include/ file.cpp

以下几个编译选项可以帮助你提升性能:



  • -DETL_PARALLEL:允许并行计算

  • -DETL_VECTORIZE_FULL:允许算法的完全向量化

  • -DETL_BLAS_MODE:将使该库 know about 一个 BLAS 库(比如 MKL),你必须为 BLAS 库添加一个头文件选项和连接选项作为可选项。

  • -DETL_CUBLAS_MODE: 使该库知道 NVIDIA cublas 是可用的,必须添加合适的选项(头文件目录和连接库)

  • -DETL_CUDNN_MODE:使该库知道 NVIDIA cudnn 是可用的,必须添加合适的选项(头文件目录和连接库)

  • -DETL_EGBLAS_MODE:使该库知道你安装了 etl-gpu-blas,必须添加合适的选项(头文件目录和连接库)



如果想要得到最佳的 CPU 性能,需要用到前面 3 个选项。如果想要得到最佳的 GPU 性能,需要用到后面 3 个选项。由于有些算法并不是完全在 GPU 上计算的,最好使用所有的选项。



接下来我们重复上述实验,但这次使用的是一个包含两个卷积层和两个池化层的卷积神经网络:



 
  1. # include "dll/neural/conv_layer.hpp"

  2. # include "dll/neural/dense_layer.hpp"

  3. # include "dll/pooling/mp_layer.hpp"

  4. # include "dll/network.hpp"

  5. # include "dll/datasets.hpp"

  6. # include "mnist/mnist_reader.hpp"

  7. # include "mnist/mnist_utils.hpp"

  8. int main ( int /*argc*/ , char * /*argv*/ []) {

  9. // Load the dataset

  10. auto dataset = dll :: make_mnist_dataset ( dll :: batch_size < 100 >{}, dll :: scale_pre < 255 >{});

  11. // Build the network

  12. using network_t = dll :: dyn_network_desc <

  13. dll :: network_layers <

  14. dll :: conv_layer < 1 , 28 , 28 , 8 , 5 , 5 >,

  15. dll :: mp_2d_layer < 8 , 24 , 24 , 2 , 2 >,

  16. dll :: conv_layer < 8 , 12 , 12 , 8 , 5 , 5 >,

  17. dll :: mp_2d_layer < 8 , 8 , 8 , 2 , 2 >,

  18. dll :: dense_layer < 8 * 4 * 4 , 150 >,

  19. dll :: dense_layer < 150 , 10 , dll :: softmax >

  20. >

  21. , dll :: updater < dll :: updater_type :: NADAM > // Momentum

  22. , dll :: batch_size < 100 > // The mini-batch size

  23. , dll :: shuffle // Shuffle the dataset before each epoch

  24. >:: network_t ;

  25. auto net = std :: make_unique < network_t >();

  26. // Display the network and dataset

  27. net -> display ();

  28. dataset . display ();

  29. // Train the network

  30. net -> fine_tune ( dataset . train (), 25 );

  31. // Test the network on test set

  32. net -> evaluate ( dataset . test ());

  33. return 0 ;

  34. }



比起之前的例子来看并没有太多变化。这个网络起始于一个卷积层,然后是一个池化层,然后又是一个卷积层和池化层,最后是两个全连接层。另一个区别是我们将输入除以 255((dll::scale_pre<255>{}))而不是归一化。最后,我们只训练了 25 个 epoch。

一旦进行编译和运行,结果将是如下所示的样子:



 
  1. Network with 6 layers

  2. Conv ( dyn ): 1x28x28 -> ( 8x5x5 ) -> SIGMOID -> 8x24x24

  3. MP ( 2d ): 8x24x24 -> ( 2x2 ) -> 8x12x12

  4. Conv ( dyn ): 8x12x12 -> ( 8x5x5 ) -> SIGMOID -> 8x8x8

  5. MP ( 2d ): 8x8x8 -> ( 2x2 ) -> 8x4x4

  6. Dense ( dyn ): 128 -> SIGMOID -> 150

  7. Dense ( dyn ): 150 -> SOFTMAX -> 10

  8. Total parameters : 21100

  9. Dataset

  10. Training : In - Memory Data Generator

  11. Size : 60000

  12. Batches : 600

  13. Augmented Size : 60000

  14. Testing : In - Memory Data Generator

  15. Size : 10000

  16. Batches : 100

  17. Augmented Size : 10000

  18. Train the network with "Stochastic Gradient Descent"

  19. Updater : NADAM

  20. Loss : CATEGORICAL_CROSS_ENTROPY

  21. Early Stop : Goal ( error )

  22. With parameters :

  23. epochs = 25

  24. batch_size = 100

  25. learning_rate = 0.002

  26. beta1 = 0.9

  27. beta2 = 0.999

  28. Epoch 0 / 25 - Classification error : 0.09392 Loss : 0.31740 Time 7298ms

  29. Epoch 1 / 25 - Classification error : 0.07005 Loss : 0.23473 Time 7298ms

  30. Epoch 2 / 25 - Classification error : 0.06915 Loss : 0.22532 Time 7364ms

  31. Epoch 3 / 25 - Classification error : 0.04750 Loss : 0.15286 Time 7787ms

  32. Epoch 4 / 25 - Classification error : 0.04082 Loss : 0.13191 Time 7377ms

  33. Epoch 5 / 25 - Classification error : 0.03258 Loss : 0.10283 Time 7334ms

  34. Epoch 6 / 25 - Classification error : 0.03032 Loss : 0.09791 Time 7304ms

  35. Epoch 7 / 25 - Classification error : 0.02727 Loss : 0.08453 Time 7345ms

  36. Epoch 8 / 25 - Classification error : 0.02410 Loss : 0.07641 Time 7443ms

  37. Epoch 9 / 25 - Classification error : 0.02448 Loss : 0.07612 Time 7747ms

  38. Epoch 10 / 25 - Classification error : 0.02023 Loss : 0.06370 Time 7626ms

  39. Epoch 11 / 25 - Classification error : 0.01920 Loss : 0.06194 Time 7364ms

  40. Epoch 12 / 25 - Classification error : 0.01810 Loss : 0.05851 Time 7391ms

  41. Epoch 13 / 25 - Classification error : 0.01575 Loss : 0.05074 Time 7316ms

  42. Epoch 14 / 25 - Classification error : 0.01542 Loss : 0.04826 Time 7365ms

  43. Epoch 15 / 25 - Classification error : 0.01392 Loss : 0.04574 Time 7634ms

  44. Epoch 16 / 25 - Classification error : 0.01287 Loss : 0.04061 Time 7367ms

  45. Epoch 17 / 25 - Classification error : 0.01167 Loss : 0.03779 Time 7381ms

  46. Epoch 18 / 25 - Classification error : 0.01202 Loss : 0.03715 Time 7495ms

  47. Epoch 19 / 25 - Classification error : 0.00967 Loss : 0.03268 Time 7359ms

  48. Epoch 20 / 25 - Classification error : 0.00955 Loss : 0.03012 Time 7344ms

  49. Epoch 21 / 25 - Classification error : 0.00853 Loss : 0.02809 Time 7314ms

  50. Epoch 22 / 25 - Classification error : 0.00832 Loss : 0.02834 Time 7329ms

  51. Epoch 23 / 25 - Classification error : 0.00807 Loss : 0.02603 Time 7336ms

  52. Epoch 24 / 25 - Classification error : 0.00682 Loss : 0.02327 Time 7335ms

  53. Training took 186s

  54. error : 0.01520

  55. loss : 0.05183



这个网络比之前的稍微要好一些,在 3 分钟的时间里达到了 1.52% 的错误率。如果你感兴趣的话,可以在 Github 中找到更多的例子。



性能



如果你看过我最新的博客,那么你可能已经看过以下部分信息,但我仍然想在这里强调一下。我在 DLL 库的性能表现上做了大量工作。我决定将 DLL 的性能和流行的框架如 TensorFlow、Keras、Torch 和 Caffe 做个对比。我也试过 DeepLearning4J,但出现了很多问题使我不得不先放弃它。如果有人对其中的结果有兴趣我也可以在某个网站发布。所有的框架都以默认选项安装,并且都可以使用 MKL。



第一个实验是在 MNIST 数据集上训练一个 3 层网络:



对于 CPU,DLL 训练这个网络是最快的。比 TensorFlow 和 Keras 快大约 35%,比 Torch 快 4 倍,比 Caffe 快 5 倍。而对于 GPU,Caffe 是最快的,紧接着是 Keras,TensorFlow 和 DLL,而 Torch 是最慢的。



以下是在同样的任务中使用 CNN 训练的结果:



再一次,对于 CPU,DLL 是最快的,非常明显,比起 TensorFlow 和 Keras 快 4 倍,比 Torch 和 Caffe 快 5 倍。对于 GPU,DLL 和 TensorFlow 以及 Keras 持平,比 Caffe 快 3 倍,比 Torch 快 5 倍。



以下是在 CIFAR-10 上用更大的 CNN 训练的结果:



在更大的 CNN 中,区别没有之前的那么明显,尽管如此,对于 CPU,DLL 仍然是最快的,比 TensorFlow、Keras 和 Torch 快两倍,比 Caffe 快 3 倍。对于 GPU,DLL 比 TensorFlow 和 Keras 稍快,比 Caffe 快 2.7 倍,比 Torch 快 5 倍。



最后一个实验是在 Imagenet 上用 12 层的 CNN 训练。mini-batch 设置为 128。



DLL 无论是在 CPU 还是 GPU 上都比其它所有框架要快。DLL 和 TensorFlow、Keras 的最大的不同主要是由于用 Python 代码读取 ImageNet 的图片的能力很差,而 DLL 中的代码已经优化过。



综上,在所有的实验中,DLL 的 CPU 计算都是最快的。对于 GPU,除了超小型全连接神经网络,DLL 也总是最快的,和 TensorFlow、Keras 并驾齐驱。



如果感兴趣,可以在这里找到实验的代码:https://github.com/wichtounet/frameworks



下一步



我并不知道下一个版本的 DLL 将具体包括哪些函数,但我知道它在以后的发展方向。



我真的希望能使用 DLL 执行文本分类任务。计划第一步将支持文本的嵌入学习,并在嵌入上使用 CNN。我同样计划添加支持合并 CNN 层的能力,从而我们能使用各种大小的滤波器,希望第一步不要花太多时间。第二步希望将循环神经网络(RNN)纳入该框架中。当然首先只会支持简单的 RNN 单元,但后来会添加 LSTM 单元和 GRU 单元的支持。这一部分肯定需要很长的时间,但我真的希望能通过这个理解这些循环神经网络的原理到底是什么。



我关注的下一件事是该神经网络库的文档构建。当然现在使用案例来了解各种函数的用法是比较好的,但还是需要列出可能的神经网络层函数和它们所有的可选参数。我还希望能有更多的实现案例,特别是当添加嵌入和 RNN 支持的时候。



此外,虽然性能一般来说还是不错的,但还有一些地方需要改进。例如目前很多运算(如批量归一化和 Dropout)在 GPU 上是比较低效的,我希望所有运算在 GPU 中都能高效地执行。还有一些运算如批量归一化或 SGD 优化器等在 CPU 上运行比较低效,所以我还需要解决这一些问题。理想的情况是,即使不使用性能库,DLL 也能表现的比较好。



最后,我还希望能提升编译时间。虽然最近的修正已经令 DLL 程序的编译过程更加快速,但我还希望取得更快的速度。



下载 DLL



读者可以在 GitHub 中下载 DLL,如果你们对 1.0 版本比较感兴趣,可以直接查看发布页面(Releases pages)或复制 tag 1.0。下面还有一些分支:



  • master 是永远的开发分支,可能并不是太稳定

  • stable 分支永远指向最新的 tag,并不会经常更新



对于未来的版本,总会有 tag 指向对应的 commits,你可以通过 GitHub 或发布的 tag 访问以前的版本。



对于文档,当前最好的文档说明是目前可用的实现案例。你可以查看测试案例的源代码,其中该软件库每一个函数都得到了使用。如果这一次开发的神经网络库得到较多的关注,后面我们将关注文档的构建。



原文链接:https://baptiste-wicht.com/posts/2017/10/deep-learning-library-10-fast-neural-network-library.html





本文为机器之心编译, 转载请联系本公众号获得授权

✄------------------------------------------------

加入机器之心(全职记者/实习生):hr@jiqizhixin.com

投稿或寻求报道:content@jiqizhixin.com

广告&商务合作:bd@jiqizhixin.com

版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。
阅读量: 72
0