如何在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調用這個函數,計算,為input,輸出為。
- updateGradInput(input, gradOutput),反向backward調用這個和下面的函數,該函數已知input ,gradOutput ,輸出損失對輸入的偏導 gradInput ,其中 。
- accGradParameters(input, gradOutput),該函數已知input ,gradOutput ,輸出損失對參數的偏導 gradWeight和gradBias,其中 ,。對於有些沒有需要學習參數的層(如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深度學習框架 |