如何在torch7上增加一個新的層?

我看到一篇google的深度學習論文,講的spatial transformer network,github上有相關的torch7代碼,可是我沒用過torch7,請問如何在torch7 中將這個新定義的層寫進去?


這幾天正好整理了一下,寫在專欄中,https://zhuanlan.zhihu.com/p/21550685,這裡再複製一下。

Torch本身提供了許多常用的層(模塊),但有時需要自己寫一些系統沒有提供的層,怎麼寫呢,官方提供了一些說明,Torch | Developer Documentation,一般分為兩種情況:

  • 所有運算均能通過Tensor自帶的操作來完成,這樣只要寫一個lua文件就行,然後直接require就可以使用了,非常方便。
  • 使用Tensor操作無法完成或者效率太低時,就需要使用C和Cuda來實現核心演算法,然後用lua來調用,這個稍微複雜一些。

無論哪一種情況,一個關鍵的文件是新模塊的lua文件(NewLayer.lua),其一般模板如下:

local NewLayer, Parent = torch.class("nn.NewLayer", "nn.Module")

function NewLayer:__init()
Parent.__init(self)
end

function NewLayer:updateOutput(input)
end

function NewLayer:updateGradInput(input, gradOutput)
end

function NewLayer:accGradParameters(input, gradOutput)
end

第一行定義了新的層類,並繼承自nn.Module(順帶提一下,一般的層繼承自nn.Module,損失層繼承自
nn.Criterion。lua語言本身沒有直接提供面向對象編程功能,但可以通過閉包、元表等方式實現,而torch本身提供了面向對象的功能,通過
torch.class來實現的)。定義的新層一般有四個主要的成員函數:

  • __init(),初始化操作,如我們常用
    nn.Linear(3, 4),表示輸入3個元素,輸出4個元素,其初始化函數形式為 function
    Linear:__init(inputSize, outputSize, bias),其中bias表示有無偏置項,默認為無。
  • updateOutput(input),前向forward調用這個函數,計算Y=F(X)X為input,輸出為Y
  • updateGradInput(input, gradOutput),反向backward調用這個和下面的函數,該函數已知input X,gradOutput frac{partial E}{ partial Y},輸出損失對輸入的偏導 gradInput frac{partial E}{ partial X},其中 frac{partial E}{ partial X} = frac{partial E}{ partial Y} 	imes frac{partial Y}{ partial X}

  • accGradParameters(input, gradOutput),該函數已知input X,gradOutput frac{partial E}{ partial Y},輸出損失對參數的偏導 gradWeight和gradBias,其中 frac{partial E}{ partial W} = frac{partial E}{ partial Y} 	imes frac{partial Y}{ partial W}frac{partial E}{ partial B} = frac{partial E}{ partial Y} 	imes frac{partial Y}{ partial B}。對於有些沒有需要學習參數的層(如nn.ReLU,nn.Dropout等),不需要寫這個函數。

以上函數的具體實現,若能通過Tensor自帶的計算完成,則直接一個lua文件即可,因為Tensor的運算自動支持cpu和gpu,故該層也能直接支持cpu和gpu了。

但若無法直接用Tensor的計算來實現時,就需要自己寫C和Cuda的代碼了,並且寫完後需要編譯安裝。需要添加和修改的文件如下:



  • torch/extra/nn/NewLayer.lua,這個就是上面提到的通用lua模板了,不過既然要編譯安裝,放在nn目錄下會省去很多麻煩。代
    碼里主要調用後面C和Cuda的實現函數,其調用形式大致如下,省略號表示其他參數,根據層的不同而不同。其中lua的數據通過cdata()轉為
    C/Cuda的指針的形式,然後調用C/Cuda的實現來完成計算。

input.THNN.NewLayer_updateOutput(
input:cdata(),
self.output:cdata(),
...
)
input.THNN.NewLayer_updateGradInput(
input:cdata(),
gradOutput:cdata(),
self.gradInput:cdata(),
...
)
input.THNN.NewLayer_accGradParameters(
input:cdata(),
gradOutput:cdata(),
self.gradInput:cdata(),
...
)

  • 新建 torch/extra/nn/lib/THNN/generic/NewLayer.c,添加C語言實現代碼,需實現的函數定義在THNN.h中。
  • 新建 torch/extra/cunn/lib/THCUNN/NewLayer.cu,添加Cuda實現代碼,需實現的函數定義在THCUNN.h中。
  • 修改 torch/extra/nn/init.lua,添加 require("nn.NewLayer"),將新模塊添加到nn的初始化文件中。

  • 改 torch/extra/nn/lib/THNN/init.c,添加以下兩行。THGenerateFloatTypes.h
    文件細看會發現很有意思的東西,由於C語言沒有C++中的模板(Template),而這裡既要支持 float 類型又要支持 double
    類型,怎麼辦呢,torch中大量使用了宏來實現了類似Template的功能。A quick tour of Torch internals,這個帖子就分析了torch的一些細節,下面的評論中還有關於為何不用C++的一些討論。

#include "generic/NewLayer.c"
#include "THGenerateFloatTypes.h"

  • 修改 torch/extra/nn/lib/THNN/generic/THNN.h,添加 NewLayer.c 中的函數聲明,包括下面兩個或者三個函數:

TH_API void THNN_(NewLayer_updateOutput)(
THNNState *state,
THTensor *input,
THTensor *output,
...);

TH_API void THNN_(NewLayer_updateGradInput)(
THNNState *state,
THTensor *input,
THTensor *gradOutput,
THTensor *gradInput,
...);

TH_API void THNN_(NewLayer_accGradParameters)(
THNNState *state,
THTensor *input,
THTensor *gradOutput,
THTensor *gradInput,
...);

  • 修改 torch/extra/cunn/lib/THCUNN/THCUNN.h,添加 NewLayer.cu 中的函數聲明,包括下面兩個或者三個函數:

TH_API void THNN_CudaNewLayer_updateOutput(
THCState *state,
THCudaTensor *input,
THCudaTensor *output,
...);

TH_API void THNN_CudaNewLayer_updateGradInput(
THCState *state,
THCudaTensor *input,
THCudaTensor *gradOutput,
THCudaTensor *gradInput,
...);

TH_API void THNN_CudaNewLayer_accGradParameters(
THCState *state,
THCudaTensor *input,
THCudaTensor *gradOutput,
THCudaTensor *gradInput,
...);

  • 修改 torch/extra/nn/test.lua,添加CPU實現的測試代碼。
  • 修改 torch/extra/cunn/test.lua,添加GPU實現的測試代碼。

以上就是所需要添加和修改的部分,寫的比較簡要,具體實現中可參考torch中原有的模塊,特別是C/Cuda代碼實現中需要模仿和學習現有的代碼中的技巧。

代碼寫完後,就可以編譯安裝了,因為我們將代碼添加在了nn和cunn目錄下,直接重新編譯nn和cunn即可,命令如下:

cd torch/extra/nn/
luarocks make rocks/nn-scm-1.rockspec

cd torch/extra/cunn/
luarocks make rocks/cunn-scm-1.rockspec


謝邀。

Torch7不管是Criterion還是Layer,基本上都是實現一個forward和一個backward就可以了,這點和Caffe很相似。但如果追求效率想自己用C++/CUDA實現Layer(看代碼,似乎很多Torch內置的layer是這麼做的),則可能涉及到LUA-C調用、和Torch庫鏈接(以使用tensor等功能)等等麻煩的事情,個人暫時沒嘗試過。

不過可以確定的是,Torch加上fb.python(一個Facebook Lab維護的LUA-Python橋)可以近乎無縫地調用Cython編譯出來的動態庫,可以省去不少麻煩。需要注意的是fb.python幾乎只支持Ubuntu平台,其它平台的用戶安裝只能自求多福了(Your mileage may vary, by a lot)。


遇到這種層次結構,最好用Layer-Layer的類庫:

TensorLayer: Deep learning and Reinforcement learning library for Researchers and Engineers.

http://tensorlayer.readthedocs.io/en/latest/


參照nn里寫法。

比如你想寫一個新Module叫ABC,那就寫個文件叫ABC.lua,第一行

local ABC, parent = torch.class("nn.ABC", "nn.Module")

如果ABC是Criterion的話則是

local ABC, parent = torch.class("nn.ABC", "nn.Criterion")

然後把必須有的兩個function

ABC:updateOutput

ABC:updateGradInput

給寫了,如果有參數的話還需要

ABC:accGradParameters

基本就行了。

如果需要CUDA的GPU運算的話,記得所有運算都調用tensor里定義的function。

想要深度優化的話就得自己寫CUDA了。


Torch7深度學習教程(一)

Torch7深度學習教程(二)

Torch7深度學習教程(三)

Torch7深度學習教程(四)

Torch7深度學習教程(五)

我寫了一個教程,你可以看下,一起學習,還沒有寫完


推薦閱讀:

萬元深度學習電腦如何配置?
深度學習CNN,用卷積和下採樣,為什麼就有效?全連接的物理意義又是什麼?
caffe 在Ubuntu下如何用已訓練出來的模型測試一張圖片?
有了AWS,不需要自己配GPUs?做深度學習。?
關於這些用於深度學習的機器配置,合理嗎,哪個好?

TAG:機器學習 | 深度學習DeepLearning | Torch深度學習框架 |