資源 | 博士生開源深度學習C++庫DLL:快速構建卷積受限玻爾茲曼機

 2017-10-08 13:12:00.0

原標題:資源 | 博士生開源深度學習C++庫DLL:快速構建卷積受限玻爾茲曼機

選自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. intmain(int/*argc*/,char*/*argv*/[]){

  5. // Load the dataset

  6. autodataset =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. autonet =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. return0;

  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. Networkwith3layers

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

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

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

  5. Totalparameters:519500

  6. Dataset

  7. Training:In-MemoryDataGenerator

  8. Size:60000

  9. Batches:600

  10. AugmentedSize:60000

  11. Testing:In-MemoryDataGenerator

  12. Size:10000

  13. Batches:100

  14. AugmentedSize:10000

  15. Trainthe network with"Stochastic Gradient Descent"

  16. Updater:NADAM

  17. Loss:CATEGORICAL_CROSS_ENTROPY

  18. EarlyStop:Goal(error)

  19. Withparameters:

  20. epochs=50

  21. batch_size=100

  22. learning_rate=0.002

  23. beta1=0.9

  24. beta2=0.999

  25. Epoch0/50-Classificationerror:0.03248Loss:0.11162Time3187ms

  26. Epoch1/50-Classificationerror:0.02737Loss:0.08670Time3063ms

  27. Epoch2/50-Classificationerror:0.01517Loss:0.04954Time3540ms

  28. Epoch3/50-Classificationerror:0.01022Loss:0.03284Time2954ms

  29. Epoch4/50-Classificationerror:0.00625Loss:0.02122Time2936ms

  30. Epoch5/50-Classificationerror:0.00797Loss:0.02463Time2729ms

  31. Epoch6/50-Classificationerror:0.00668Loss:0.02066Time2921ms

  32. Epoch7/50-Classificationerror:0.00953Loss:0.02710Time2894ms

  33. Epoch8/50-Classificationerror:0.00565Loss:0.01666Time2703ms

  34. Epoch9/50-Classificationerror:0.00562Loss:0.01644Time2759ms

  35. Epoch10/50-Classificationerror:0.00595Loss:0.01789Time2572ms

  36. Epoch11/50-Classificationerror:0.00555Loss:0.01734Time2586ms

  37. Epoch12/50-Classificationerror:0.00505Loss:0.01446Time2575ms

  38. Epoch13/50-Classificationerror:0.00600Loss:0.01727Time2644ms

  39. Epoch14/50-Classificationerror:0.00327Loss:0.00898Time2636ms

  40. Epoch15/50-Classificationerror:0.00392Loss:0.01180Time2660ms

  41. Epoch16/50-Classificationerror:0.00403Loss:0.01231Time2587ms

  42. Epoch17/50-Classificationerror:0.00445Loss:0.01307Time2566ms

  43. Epoch18/50-Classificationerror:0.00297Loss:0.00831Time2857ms

  44. Epoch19/50-Classificationerror:0.00335Loss:0.01001Time2931ms

  45. Epoch20/50-Classificationerror:0.00378Loss:0.01081Time2772ms

  46. Epoch21/50-Classificationerror:0.00332Loss:0.00950Time2964ms

  47. Epoch22/50-Classificationerror:0.00400Loss:0.01210Time2773ms

  48. Epoch23/50-Classificationerror:0.00393Loss:0.01081Time2721ms

  49. Epoch24/50-Classificationerror:0.00415Loss:0.01218Time2595ms

  50. Epoch25/50-Classificationerror:0.00347Loss:0.00947Time2604ms

  51. Epoch26/50-Classificationerror:0.00535Loss:0.01544Time3005ms

  52. Epoch27/50-Classificationerror:0.00272Loss:0.00828Time2716ms

  53. Epoch28/50-Classificationerror:0.00422Loss:0.01211Time2614ms

  54. Epoch29/50-Classificationerror:0.00417Loss:0.01148Time2701ms

  55. Epoch30/50-Classificationerror:0.00498Loss:0.01439Time2561ms

  56. Epoch31/50-Classificationerror:0.00385Loss:0.01085Time2704ms

  57. Epoch32/50-Classificationerror:0.00305Loss:0.00879Time2618ms

  58. Epoch33/50-Classificationerror:0.00343Loss:0.00889Time2843ms

  59. Epoch34/50-Classificationerror:0.00292Loss:0.00833Time2887ms

  60. Epoch35/50-Classificationerror:0.00327Loss:0.00895Time2644ms

  61. Epoch36/50-Classificationerror:0.00203Loss:0.00623Time2658ms

  62. Epoch37/50-Classificationerror:0.00233Loss:0.00676Time2685ms

  63. Epoch38/50-Classificationerror:0.00298Loss:0.00818Time2948ms

  64. Epoch39/50-Classificationerror:0.00410Loss:0.01195Time2778ms

  65. Epoch40/50-Classificationerror:0.00173Loss:0.00495Time2843ms

  66. Epoch41/50-Classificationerror:0.00232Loss:0.00709Time2743ms

  67. Epoch42/50-Classificationerror:0.00292Loss:0.00861Time2873ms

  68. Epoch43/50-Classificationerror:0.00483Loss:0.01365Time2887ms

  69. Epoch44/50-Classificationerror:0.00240Loss:0.00694Time2918ms

  70. Epoch45/50-Classificationerror:0.00247Loss:0.00734Time2885ms

  71. Epoch46/50-Classificationerror:0.00278Loss:0.00725Time2785ms

  72. Epoch47/50-Classificationerror:0.00262Loss:0.00687Time2842ms

  73. Epoch48/50-Classificationerror:0.00352Loss:0.01002Time2665ms

  74. Epoch49/50-Classificationerror:0.00232Loss:0.00668Time2747ms

  75. Restorethe best (error)weights from epoch 40

  76. Trainingtook 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. intmain(int/*argc*/,char*/*argv*/[]){

  9. // Load the dataset

  10. autodataset =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. autonet =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. return0;

  34. }

比起之前的例子來看並沒有太多變化。這個網絡起始於一個卷積層,然後是一個池化層,然後又是一個卷積層和池化層,最後是兩個全連接層。另一個區別是我們將輸入除以 255((dll::scale_pre<255>{}))而不是歸一化。最後,我們只訓練了 25 個 epoch。

一旦進行編譯和運行,結果將是如下所示的樣子:

  1. Networkwith6layers

  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. Totalparameters:21100

  9. Dataset

  10. Training:In-MemoryDataGenerator

  11. Size:60000

  12. Batches:600

  13. AugmentedSize:60000

  14. Testing:In-MemoryDataGenerator

  15. Size:10000

  16. Batches:100

  17. AugmentedSize:10000

  18. Trainthe network with"Stochastic Gradient Descent"

  19. Updater:NADAM

  20. Loss:CATEGORICAL_CROSS_ENTROPY

  21. EarlyStop:Goal(error)

  22. Withparameters:

  23. epochs=25

  24. batch_size=100

  25. learning_rate=0.002

  26. beta1=0.9

  27. beta2=0.999

  28. Epoch0/25-Classificationerror:0.09392Loss:0.31740Time7298ms

  29. Epoch1/25-Classificationerror:0.07005Loss:0.23473Time7298ms

  30. Epoch2/25-Classificationerror:0.06915Loss:0.22532Time7364ms

  31. Epoch3/25-Classificationerror:0.04750Loss:0.15286Time7787ms

  32. Epoch4/25-Classificationerror:0.04082Loss:0.13191Time7377ms

  33. Epoch5/25-Classificationerror:0.03258Loss:0.10283Time7334ms

  34. Epoch6/25-Classificationerror:0.03032Loss:0.09791Time7304ms

  35. Epoch7/25-Classificationerror:0.02727Loss:0.08453Time7345ms

  36. Epoch8/25-Classificationerror:0.02410Loss:0.07641Time7443ms

  37. Epoch9/25-Classificationerror:0.02448Loss:0.07612Time7747ms

  38. Epoch10/25-Classificationerror:0.02023Loss:0.06370Time7626ms

  39. Epoch11/25-Classificationerror:0.01920Loss:0.06194Time7364ms

  40. Epoch12/25-Classificationerror:0.01810Loss:0.05851Time7391ms

  41. Epoch13/25-Classificationerror:0.01575Loss:0.05074Time7316ms

  42. Epoch14/25-Classificationerror:0.01542Loss:0.04826Time7365ms

  43. Epoch15/25-Classificationerror:0.01392Loss:0.04574Time7634ms

  44. Epoch16/25-Classificationerror:0.01287Loss:0.04061Time7367ms

  45. Epoch17/25-Classificationerror:0.01167Loss:0.03779Time7381ms

  46. Epoch18/25-Classificationerror:0.01202Loss:0.03715Time7495ms

  47. Epoch19/25-Classificationerror:0.00967Loss:0.03268Time7359ms

  48. Epoch20/25-Classificationerror:0.00955Loss:0.03012Time7344ms

  49. Epoch21/25-Classificationerror:0.00853Loss:0.02809Time7314ms

  50. Epoch22/25-Classificationerror:0.00832Loss:0.02834Time7329ms

  51. Epoch23/25-Classificationerror:0.00807Loss:0.02603Time7336ms

  52. Epoch24/25-Classificationerror:0.00682Loss:0.02327Time7335ms

  53. Trainingtook 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

本文爲機器之心編譯,轉載請聯繫本公衆號獲得授權。

責任編輯:

文章來源:機器之心