閱讀560 返回首頁    go 阿裏雲 go 技術社區[雲棲]


驚!C++竟然還能有這種操作——高性能深度學習庫(DLL)

更多深度文章,請關注雲計算頻道:https://yq.aliyun.com/cloud 


54d9b2c9ec6061f924ff9be54aeeedc79afd0fb3

作為一個深度學習的老司機,你是不是以為隻有Python才能夠玩轉深度學習?如果是這樣的話,那麼本文作者可能就要教你怎麼“做人”了。畢竟大牛的世界我們不懂!

第一個版本C++的深度學習庫(DLL1.0發布了!DLL是一個關注速度和易用性的神經網絡庫。大約4年前,作者就開始編寫這個庫。為了獲得博士學位,作者需要一個很好的庫來訓練和使用受限製的玻爾茲曼機器(RBMs),因為當時還沒有很好的庫來完成這項工作。所以我決定自己寫一個庫(大牛就是任性!)。它現在可以支持RBM和卷積RBMCRBM)模型,也可以使用對比發散預先訓練RBM(或深層信念網絡(DBN))的堆棧,然後通過微型批量梯度下降或共軛梯度進行微調或用作特征提取器。近年來,該庫已經可以處理人造神經網絡(ANN)和卷積神經網絡(CNN)。該網絡還可以訓練常規的自動編碼器,還提供了幾個高級層,如Dropout或批次規範化(BN),以及自適應學習率技術,如AdadeltaAdam。該庫還集成了對幾個數據集的支持:MNISTCIFAR-10ImageNet

該庫是使用C ++接口,它需要一個C ++ 14編譯器。

在這篇文章中,我將介紹一些關於使用該庫的例子,並提供有關庫表現和項目路線圖的一些信息。

我們來看看一個使用這個庫的例子:

#include "dll/neural/dense_layer.hpp"
#include "dll/network.hpp"
#include "dll/datasets.hpp"
int main(int /*argc*/, char* /*argv*/ []) {
    // Load the dataset
    auto dataset = dll::make_mnist_dataset(dll::batch_size<100>{}, dll::normalize_pre{});
    // Build the network
    using network_t = dll::dyn_network_desc<
        dll::network_layers<
            dll::dense_layer<28 * 28, 500>,
            dll::dense_layer<500, 250>,
            dll::dense_layer<250, 10, dll::softmax>
        >
        , dll::updater<dll::updater_type::NADAM>     // Nesterov Adam (NADAM)
        , dll::batch_size<100>                       // The mini-batch size
        , dll::shuffle                               // Shuffle before each epoch
    >::network_t;
    auto net = std::make_unique<network_t>();
    // Train the network for performance sake
    net->fine_tune(dataset.train(), 50);
    // Test the network on test set
    net->evaluate(dataset.test());
    return 0;
}

這是在MNIST數據集上訓練和測試一個簡單的三層全連接神經網絡。

首先,在頭文件中,你需要列出你正在使用的層,這裏隻有密集層;然後,你需要包括network.hpp這是每個網絡的基本包括;最後一個頭文件是數據集。

main函數中,首先我們需要完整的加載MNIST數據集並傳遞兩個選項。在這裏,我們要設置批量大小,並指示每個樣本應歸一化為具有零均值和單位方差。

之後是最重要的一部分,網絡的聲明。在DLL中,網絡是一種類(Type)。這個類有兩個部分:網絡層(一個dll::network_layers)和選項(一個可選的選項列表,在網絡層後)。在上述代碼中,我們有三層,第一層有500個隱藏單元,第二層有250個隱藏單元,最後一層有10個。每層可以采用可選的選項列表。第三層使用softmax激活函數而不是默認的S形函數。我們正在使用Nesterov AdamNAdam)更新器,批量大小為100(必須與數據集批量大小相同),數據集在每個時期之前被混洗。

最後,我們簡單地創建網絡std::make_unique,然後在訓練集上訓練50次,最後在測試集上進行測試。

如果你編譯並運行這個程序,你應該看到這樣的:

Network with 3 layers
    Dense(dyn): 784 -> SIGMOID -> 500
    Dense(dyn): 500 -> SIGMOID -> 250
    Dense(dyn): 250 -> SOFTMAX -> 10
Total parameters: 519500
Dataset
Training: In-Memory Data Generator
              Size: 60000
           Batches: 600
    Augmented Size: 60000
Testing: In-Memory Data Generator
              Size: 10000
           Batches: 100
    Augmented Size: 10000
Train the network with "Stochastic Gradient Descent"
    Updater: NADAM
       Loss: CATEGORICAL_CROSS_ENTROPY
 Early Stop: Goal(error)
With parameters:
          epochs=50
      batch_size=100
   learning_rate=0.002
           beta1=0.9
           beta2=0.999
Epoch   0/50 - Classification error: 0.03248 Loss: 0.11162 Time 3187ms
Epoch   1/50 - Classification error: 0.02737 Loss: 0.08670 Time 3063ms
Epoch   2/50 - Classification error: 0.01517 Loss: 0.04954 Time 3540ms
Epoch   3/50 - Classification error: 0.01022 Loss: 0.03284 Time 2954ms
•••••••••••••••••••
Epoch  48/50 - Classification error: 0.00352 Loss: 0.01002 Time 2665ms
Epoch  49/50 - Classification error: 0.00232 Loss: 0.00668 Time 2747ms
Restore the best (error) weights from epoch 40
Training took 142s
error: 0.02040
 loss: 0.08889

首先顯示網絡和數據集信息,如代碼中所設定的,然後用數據訓練網絡,按次數劃分。最後,評估結果。在大約2分半左右的時間裏,我們訓練了一個能夠將MNIST數字分類出錯率為2.04%的神經網絡,這已經不錯的,但仍然可以改進。

幾個關於如何編譯的信息:你可以直接sudo make install_headers在計算機上安裝dll庫。然後,你可以使用以下方式簡單地編譯文件:

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

或者,如果將dll複製到本地dll目錄中,這編譯的時候需要指定include文件夾:

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

有幾個編譯選項,以提高性能:

1.-DETL_PARALLEL:允許並行計算。

2.-DETL_VECTORIZE_FULL啟用算法的全矢量化。

3.-DETL_BLAS_MODE讓庫知道一個BLAS庫(例如MKL)。然後,你必須為你選擇的BLAS係列添加包含選項和鏈接選項。

4.-DETL_CUBLAS_MODE讓庫知道NVIDIA cublas在本機上可用。然後必須添加相應的選項(包括目錄和鏈接庫)。

5.-DETL_CUDNN_MODE讓庫知道NVIDIA cudnn在本機上可用。然後必須添加相應的選項(包括目錄和鏈接庫)。

6.-DETL_EGBLAS_MODE讓庫知道你在本機上安裝了etl-gpu-blas。然後必須添加相應的選項(包括目錄和鏈接庫)。

如果你想要最佳的CPU性能,你應該使用前三個選項。如果你想要最好的GPU性能,你隻需啟動第三個。理想情況下,你可以啟用所有選項,因此你將獲得最佳性能,因為GPU尚未完全支持。

我們再次進行相同的實驗,但是使用具有兩個卷積層和兩個池層的卷積神經網絡:

#include "dll/neural/conv_layer.hpp"
#include "dll/neural/dense_layer.hpp"
#include "dll/pooling/mp_layer.hpp"
#include "dll/network.hpp"
#include "dll/datasets.hpp"
#include "mnist/mnist_reader.hpp"
#include "mnist/mnist_utils.hpp"
int main(int /*argc*/, char* /*argv*/ []) {
    // Load the dataset
    auto dataset = dll::make_mnist_dataset(dll::batch_size<100>{}, dll::scale_pre<255>{});
    // Build the network
    using network_t = dll::dyn_network_desc<
        dll::network_layers<
            dll::conv_layer<1, 28, 28, 8, 5, 5>,
            dll::mp_2d_layer<8, 24, 24, 2, 2>,
            dll::conv_layer<8, 12, 12, 8, 5, 5>,
            dll::mp_2d_layer<8, 8, 8, 2, 2>,
            dll::dense_layer<8 * 4 * 4, 150>,
            dll::dense_layer<150, 10, dll::softmax>
        >
        , dll::updater<dll::updater_type::NADAM>     // Momentum
        , dll::batch_size<100>                       // The mini-batch size
        , dll::shuffle                               // Shuffle the dataset before each epoch
    >::network_t;
    auto net = std::make_unique<network_t>();
    // Display the network and dataset
    net->display();
    dataset.display();
    // Train the network
    net->fine_tune(dataset.train(), 25);
    // Test the network on test set
    net->evaluate(dataset.test());
    return 0;
}

與上一個例子相比,沒有什麼大的改變。網絡現在以卷積層開始,隨後是一個池層,然後有是卷積層和一個池層,最後是兩個完全連接的層。另一個區別是我們將輸入縮放為255,而不是歸一化它們。最後,我們訓練25次。

一旦編譯並運行,輸出應該是這樣的:

Network with 6 layers
    Conv(dyn): 1x28x28 -> (8x5x5) -> SIGMOID -> 8x24x24
    MP(2d): 8x24x24 -> (2x2) -> 8x12x12
    Conv(dyn): 8x12x12 -> (8x5x5) -> SIGMOID -> 8x8x8
    MP(2d): 8x8x8 -> (2x2) -> 8x4x4
    Dense(dyn): 128 -> SIGMOID -> 150
    Dense(dyn): 150 -> SOFTMAX -> 10
Total parameters: 21100
Dataset
Training: In-Memory Data Generator
              Size: 60000
           Batches: 600
    Augmented Size: 60000
Testing: In-Memory Data Generator
              Size: 10000
           Batches: 100
    Augmented Size: 10000
Train the network with "Stochastic Gradient Descent"
    Updater: NADAM
       Loss: CATEGORICAL_CROSS_ENTROPY
 Early Stop: Goal(error)
With parameters:
          epochs=25
      batch_size=100
   learning_rate=0.002
           beta1=0.9
           beta2=0.999
Epoch   0/25 - Classification error: 0.09392 Loss: 0.31740 Time 7298ms
Epoch   1/25 - Classification error: 0.07005 Loss: 0.23473 Time 7298ms
••••••
Epoch  24/25 - Classification error: 0.00682 Loss: 0.02327 Time 7335ms
Training took 186s
error: 0.01520
 loss: 0.05183

這個網絡性能比以前的網絡好一點,在大約3分鍾內達到1.52%的錯誤率。

如果你有興趣,可以在Github信息庫中找到更多的例子。

我一直在做該庫性能提升方麵的工作。為了看看該庫和其他流行的框架的差別,我決定比較DLLTensorFlowKerasTorchCaffe的性能。我也嚐試過DeepLearning4J,但是我認為它的表現是相當差的,所以我放棄了。

我做的第一個實驗是在MNIST數據集上訓練一個三層的神經網絡:

35da07c1ae073a168680cfbe1a4be122cec0285e

CPU上,DLL是訓練這個網絡最快的框架,比TensorFlowKeras35%,比Torch4倍,比Caffe5倍。在GPU上,Caffe是最快的框架,其次是KerasTensorFlowDLL

讓我們來看看框架是如何與這些流行框架在一個小的CNN任務:

14df90bc157e3ff3ba82e3ac04acc31d2b710aee

CPU上,DLL是最快的框架,並且優勢非常大,比TensorFlowKeras快四倍,比TorchCaffe快五倍。在GPU上,它與KerasTensorFlow相當,比Caffe3倍。

下一個測試是用較大的CNNCIFAR-10上完成的:

17ef9a6b69d9acf80b4f9d4ff846b5a2d31cdb44

在這個更大的CNN上,差異不如以前那麼令人印象深刻,但是,DLL仍然是CPU中最快的框架。它比TensorFlowKerasTorch還要快兩倍,比Caffe快三倍。在GPU上,DLLKerasTensorFlow稍快,比Caffe2.7倍,比Torch5倍。

最後一次測試是在Imagenet上完成的,具有12CNN。這一次,性能體現主要是在必要的時間訓練了128個圖像。

9c954ad47cc5bb4f274efa58ec9abcc0db5a3ad3

再次,DLLCPUGPU上的所有其他框架更快。DLLTensorFlowKeras之間的巨大差異主要是由於從Python代碼讀取Imagenet圖像的性能差,而代碼在DLL中進行了優化。

總的來說,在所有測試實驗中,DLL總是CPU上最快的框架。在GPU上,除了一個非常小的全連接網絡,它也是比TensorFlowKeras快。

如果你有興趣,可以查找這些實驗的代碼

我不知道下一個版本的DLL將包含什麼,但我知道我將要開發的方向。我想使用DLL來分類文本。第一次打算,我計劃增加對文本嵌入學習的支持,並能夠在嵌入之上使用CNN,希望這不會花太長時間。第二次打算,我希望將支持循環神經網絡(RNNs)納,以及增加對LSTMGRU單元的支持,這可能會需要一些時間。

雖然該框架性能表現已經相當不錯了,但還有一些事情要改善。現在有些操作在GPU上實現時並不高效,例如Batch Normalization and Dropout。我想保證所有的操作都可以在GPU上進行有效的計算。還有一些在CPU上效率不高的情況,例如,批處理標準化目前很差。一些SGD優化器,如Nadam也很慢。

下載DLL

你可以Github下載DLL 。如果你隻對1.0版本感興趣,可以查看 版本頁麵或克隆標簽1.0。有幾個分支:

1.master是前進的發展分支,可能是不穩定的。

2.stable分支總是指向最後一個標簽,這裏沒有開發。

對於文檔,這是關於這個框架迄今為止最好的文檔是可用示例。你還可以查看使用庫的每個功能的測試源。

如果你對此圖書館有任何意見或任何問題,請隨時給予評論。如果你在使用此庫時遇到問題,或者如果你想對該庫有所貢獻,請及時與我聯係我期待你的到來。

本文由北郵@愛可可-愛生活老師推薦,阿裏雲雲棲社區組織翻譯。

文章原標題:《deep-learning-library-1.0-fast-neural-network-library

作者:Baptiste Wicht

現年28歲。我是弗裏堡大學應用科學大學計算機科學博士生,主要從事機器學習。目前,主要從事開發C ++高性能應用程序。

郵箱:baptiste.wicht@gmail.com

Twitter@wichtounet

LinkedIn我的LinkedIn個人資料

譯者:袁虎,審校:

文章為簡譯,更為詳細的內容,請查看原文


最後更新:2017-10-08 11:33:20

  上一篇:go  Linux Shell 基礎--更多的結構化命令
  下一篇:go 樂家網設計師侯現龍:雲棲徑126㎡“減法與自然”