運用 cuDNN 深度神經網路函式庫加速機器學習

作者 Crowd Favorite

機器學習 (ML) 源自於人工智慧 (AI) ,已有幾十年歷史,一直以來致力於實現崇高的目標,設計一台電腦可以做任何人類會做的事情。雖然要達到這個目標仍有很長的路要走,但目前已開發了很多有用的工具,並成功解決了許多相關的難題。現今全球最大的金融公司、網路公司和一流的研究機構都在眾多領域中廣泛使用機器學習,包括網路搜尋、詐騙偵測、遊戲、人臉偵測、影像標籤、人腦圖譜、檢查處理和電腦伺服器狀態監控等應用。美國郵局將機器學習應用於手寫辨識,以及 IARPA 和 DARPA 等從事各種應用研究的政府機構現在也集資投入新一代機器學習的開發工作。

目前已有很多建置機器學習系統的演算法和程序。現今機器學習最熱門的範疇乃屬於深層神經網路 (DNNs) ,而運用 GPU 更讓深層神經網路之應用得以大幅提升,並讓它成為訓練大型、複雜的深層神經網路型機器學習系統的首選平台。在這個領域的先驅有 Geoffrey Hinton 、 Yann LeCun 、 Yoshua Bengio 和 Andrew Ng 等頂尖人物;他們在過去三十年的成就激發了學術界的研發風潮,其中卡內基美隆大學、紐約大學、牛津大學、史丹佛大學、加州大學柏克萊分校、蒙特利爾大學和多倫多大學等都在這個範疇投入眾多資料。而最近幾年更有很多企業也開始積極投資這項科技,而以 GPU 加速深度學習應用的知名企業包括有 Adobe 、百度、 Nuance 和 Yandex 。

由於深層神經網路的重要性同時在業界和學術界日益提升,而 GPU 的角色也更被重視,因此 NVIDIA (輝達) 專為深層神經網路推出了一個名為 cuDNN 的原式函式庫。 cuDNN 函式庫可讓開發人員透過深層神經網路輕鬆取得頂尖的效能,並可提供更多其他的優點。

運用 DNN 的機器學習
機器學習系統可被視為一個不用事前說明但可學習辨識我們感興趣的事物的系統。最典型的例子是垃圾郵件分類系統,可檢視收件者信箱的電子郵件,並將垃圾郵件隔離;另一個例子是推薦產品的系統,可依據你之前的購買項目和評等資訊提供新產品 (書籍、電影等) 建議。

一般建置機器學習系統的方法是先用大量妥善分組並標示的例子來訓練系統。例如,我們可以先讓系統檢視成千上萬張的動物影像 (貓、狗、鳥類等),而這些影像每個都做好分類標示 (例如:「獵犬」、「羅賓鳥」)。系統經過訓練後,我們再將沒有標示的影像部署在該系統中,若是當中有任何一張尚未標示的動物影像,系統都將會迅速無誤地將影像中的動物辨別出來,就好比人類在很多影像中辨識的做法一樣。

雖然有佷多不同的特定機器學習技術,例如迴歸 (Regression) 、支援向量機器  (Support Vector Machines) 和不同種類的群聚演算法—神經網路已成為機器學習研發人員一個功能最強大的工具。神經網路是由很多理想化的神經元組成。一個理想化的神經元的產出是輸入程式加權總數的函數 (經常都是邏輯函數) 。過去的神經網路通常都是層數不多 (僅超出輸入層的一層或兩層) 和完全連結的;意思就是說,每個神經元會從底下一層的神經元接收輸入程式。現在,最高執行效能的神經網路是深層的,而且常常層數多達10層,而且有越來越多層的趨勢 。多於一層的神經網路可以學習辨識輸入元素中高度複雜的非線性特徵。此外,最新型的深層神經網路通常會有一些並非完全連結的層數。圖1展示了一個假設的臉部辨識深層神經網路架構。

完全連結層的另一種選擇是卷積層 (Convolutional Layer)。在卷積層中的神經元只會連結到下一層小範圍的神經元。這個範圍的神經元一般有 5×5 區格,或者可能為7×7 或11×11,而這個區格的大小稱為過濾器尺寸。因此卷積層可以在其輸入元素執行旋積。這種連結模式就好像人類大腦感知區會做的事,例如視網膜神經節細胞或初級視覺皮質區內的細胞。

在深層神經網路的卷積層中,過濾器與這一層的每個神經元同樣重要。一般而言,卷積層中會有很多「次層」,而每個次層都有一個不同的過濾器;在一個卷積層中會有多達數百個不同的過濾器。一個深層神經網路的卷積層本身可同時執行數百個不同的旋積,而這些旋積的計算結果會用到下一層。包含卷積層的深層神經網路稱為卷積神經網路 (CNNs) 。

卷積神經網路近來在機器學習演算法比賽中扮演重要角色,主要負責辨識手寫功能、偵測行人影像和語音辨識等感知任務。此外,卷積神經網路除了擁有優異的效能外,它也可以擴充處理大量的資料集,例如一個影像的所有像素。神經網路也可以用相當簡單的方法即可與現有的架構一併使用,而這種組合通常會有不錯的屬性,因而也是它們如此受歡迎和普及的原因。


為深層神經網路設計的GPU
然而,深層神經網路和卷積神經網路需要大量的運算作業,尤其是在訓練階段。神經網路的訓練是藉由呈現輸入網路的資料,並讓因計算結果而活化的神經元流到整個網路而到輸出層,而結果在這裡會跟正確的答案作對比。在輸出層的每個單位都會計算出一個錯誤,而這個錯誤會往下「傳回」網路,進而用少量數據調整每個連結權值 (Connection Weight) 。因此在訓練時輸入的資料會「向前傳送」而產生輸出,同時「往回傳送」的資料會將錯誤的資訊散播到整個網路。當部署完成後,只會使用向前傳送。

最高階的深層神經網路和卷積神經網路可以從數百萬甚至十億的參數通過反向傳送作調整。另外,深層神經網路需要大量的訓練資料以達到高準確度,亦即是數十萬到數百萬的輸入範例要同時會執行向前和往回傳送。由於神經網路是由大量相同的神經元組成,因此他們本質上是高度平行的。這種平行的特質與 GPU 不謀而合,也會比用 CPU 運算的訓練速度大為提升,如圖2所示。相較於英特爾 Ivy Bridge CPU ,用 NVIDIA Tesla K40 GPU 訓練「Reference ImageNet」DNN 模型時,從我們用 cuDNN 和知名的神經網路軟體 CAFFE 作的效能評測中,我們取得10倍以上的速度提升。

當我們看到ImageNet大型視覺辨識競賽 (ILSVRC) 的結果時 (如圖3所示), GPU 和深層神經網路的連結清晰可見。在2012年前,沒有參賽隊伍使用 GPU 加速深層神經網路,而獲勝隊伍的錯誤率一般是每年降低10%或以下(圖3中的黃線)。在2012年由 Geoff Hinton 和 Alex Krizhevsky 帶領的多倫多大學參賽隊伍率先使用GPU加速深層神經網路,而且他們以大幅領先贏得競賽。自此之後,使用 GPU 加速深層神經網路的隊伍數量大為增加 (圖3中的綠色柱狀塊) ,而這些深層神經網路乃繼續展示傑出的效能。雖然今年競賽的資料仍在彙整,但目前已顯示至少有九成 ImageNet 大型視覺辨識競賽的參賽隊伍使用 GPU 。

有關cuDNN
NVIDIA cuDNN 是一個為深層神經網路設計的 GPU 加速原式函式庫,提供經過調校的常式建置方法,這些常式則常用於 DNN 應用,例如:

  • 旋積
  • Pooling
  • Softmax
  • 神經元動作包括:
    • Sigmoid
    • Rectified Linear (ReLU)
    • Hyperbolic Tangent (TANH)

當然所有這些函數支援一般向前和往回傳送功能。 cuDNN 的旋積常式主要力求效能上的優勢,並在大量減少記憶體需求的情況下優於相同常式的最快速 GEMM 建置方法。

cuDNN 擁有客製化的資料格式,針對其所有常式的輸入和輸出所用的 4D 張量支援不同尺寸的先後排序、 Striding 和 Subregion 功能。這種靈活性可在任何神經網路內進行簡單的整合,並可避免 GEMM 旋積有時需要的輸入/輸出調換步驟。

cuDNN具有執行緒安全的功能,並提供情境式API,可輕鬆進行多執行緒存取和提供與CUDA的互通性。舉例來說,這可讓開發人員在使用多個主執行緒和多重GPU時能夠明確監控函式庫的設定,另外確保一個特定GPU裝置可用於特定的主執行緒。

cuDNN 可讓深層神經網路開發人員透過簡易的方法取得頂尖的效能,並可專注於設計他們的應用和解答機器學習的問題,而無需額外編寫客製化的程式碼。 cuDNN 可在  Windows 或 Linux OSes 環境執行,並可與全部 NVIDIA GPU,從 Tegra K1 等低功耗嵌入式 GPU 到 Tesla K40 等高階伺服器 GPU 並用。當開發人員使用 cuDNN 時,他們可以放心得到現有和未來 NVIDIA GPU 的高效能,而且可從未來全新的 GPU 功能和特性受惠。

未來,我們將專注於持續提升效能和擴充支援更多的功能。下一版本的 cuDNN 將會針對旋積常式大幅提升效能,並會建置更廣泛的神經元種類,尤其集中於常會在卷積神經網路的神經元。我們也非常期待在同一節點的多款 GPU 中加入分解式運算的支援功能,並朝著在接下來的版本達成這項目標而努力。

容易使用
cuDNN 函式庫的對象乃瞄準深層神經網路架構 (例如: CAFFE 和 Torch) 的開發者,可以直接使用,而且不需要會用 CUDA 才能使用。以下的程式碼例子是要展示為一批輸入的影像和 cuDNN 中旋積過濾器分配儲存空間,以及在卷積層如何前向執行這些影像批次。

對 cudnnSetTensor4dDescriptor ()和 cudnnSetFilterDescriptor ()的指令分別定義了輸入卷積層元素和過濾器參數。而針對 cudnnSetConvolutionDescriptor ()的指令則運用前兩個指命的描述器和 padding 和 striding 參數等一些特定層的資訊,為這一層的旋積描述器進行初始化。而以下的 cudnnGetOutputTensor4dDim ()指令可計算旋積的輸出尺寸。接下來的指令僅會為這一層的輸出進行配置和分配儲存空間,然後 cudnnConvolutionForward ()則會執行經 NVIDIA (輝達) 調校過的旋積。

/* Allocate memory for Filter and ImageBatch, fill with data */
cudaMalloc( &ImageInBatch , … );
cudaMalloc( &Filter , … );

/* Set decriptors */
cudnnSetTensor4dDescriptor(InputDesc, CUDNN_TENSOR_NCHW, 128, 96, 221,221);
cudnnSetFilterDescriptor(FilterDesc, 256, 96, 7, 7 );
cudnnSetConvolutionDescriptor(convDesc, InputDesc, FilterDesc,
       pad_x, pad_y, 2, 2, 1, 1, CUDNN_CONVOLUTION);

/* query output layout */
cudnnGetOutputTensor4dDim(convDesc, CUDNN_CONVOLUTION_FWD, &n_out, &c_out,
       &h_out, &w_out);

/* Set and allocate output tensor descriptor */
cudnnSetTensor4dDescriptor(&OutputDesc, CUDNN_TENSOR_NCHW, n_out, c_out,
       h_out, w_out);
cudaMalloc(&ImageBatchOut, n_out*c_out*h_out*w_out * sizeof(float));

/* launch convolution on GPU */
cudnnConvolutionForward(handle, InputDesc, ImageInBatch, FilterDesc,
       Filter, convDesc, OutputDesc, ImageBatchOut,
       CUDNN_RESULT_NO_ACCUMULATE);

由於 cuDNN 非常簡單易用,因此我們期待很多人會透過神經網路工具套件中選用cuDNN 。在某些使用情況裡,這完全不需要設計程式。

不需任何程式設計
cuDNN 已整合在 CAFFE 神經網路工具套件的開發工具中,未來將會整合到CAFFE 1.0 正式版本中。在 CAFFE 軟體中,深層神經網路是完全由文字型的配置檔案來定義和建置。你可用 CAFFE 定義你的神經網路的每一「層」(有具體說明每層的種類,例如:資料層、卷積層或完全連結層等),以及輸入來源的層數。而類似的配置檔也會用於定義如何為網路的參數進行初始化,以及要用多少次重複作業來進行訓練等。以下是一個稍為簡化的例子,說明如何用一個資料層和兩個卷積層為 CAFFE 神經網路的定義進行配置。

layers {
   name: “MyData”
   type:  DATA
   top: “data”
   top: “label”
}
layers {
   name: “Conv1”
    type:  CONVOLUTION
    bottom: “MyData”
    top: “Conv1”
    convolution_param

   {
       num_output: 96
      kernel_size: 11
      stride: 4
    }
}
layers {
   name: “Conv2”
   type: CONVOLUTION
   bottom: “Conv1”
   top: “Conv2”
   convolution_param
   {
      num_output: 256
      kernel_size: 5
    }
}

馬上體驗cuDNN !
cuDNN 是完全免費的,可適用於學術、研究或商用的用途。只需登記註冊一個CUDA開發者帳號即可使用。一旦你的帳號開始啟用,登入後即可看到一個連至cuDNN下載網頁 (developer.nvidia.com/cuDNN) 的連結。你也可以從其中的使用者指南開始。馬上進入cuDNN吧!