Introduction
在Torch NN tutorial 1 : NN.Module & NN.Linear中,介紹了構成 neural network 的最基本的單位,也就是 nn.Module
,並介紹了 nn.Linear
可以進行一次線性運算,但通常一個 neural network 是經由多個線性和非線性的運算構成的。要把多個 Module 串接起來,就需要用到 nn.Contaner
。
例如,有個 neural network 的輸入為 x ,輸出為 z ,中間經過了一次線性運算與一個sigmoid function ,如下:
假設 為 2 維的向量,而 為 3 維向量, 分別為 weight 和 bias ,此兩參數皆以隨機值進行初始化, 是 經過 sigmoid 非線性運算的輸出結果。
使用 torch 實作此運算:
首先,載入 nn
套件:
1
|
|
建立以上兩個運算所需的Module,分別為 nn.Linear
和 nn.Sigmoid
,並將它們分別命名為 l1
和 s1
,如下:
1 2 |
|
其中, nn.Sigmoid
為進行 sigmoid 運算所需的 module 。
如果要進行運算,方法如下:
假設 x 為一個二維向量 ,先輸入 l1
,進行 forward propagation ,將運算結果傳給 y
,如下:
1 2 3 |
|
執行完後會印出 l1
的輸出值,也就是 y
的值,是個三維的向量,
y 的值會與 l1.weight
和 l1.bias
的初始值值關, y
值如下:
1 2 3 4 |
|
再來將 y
輸入到 s1
中, 進行 forward propagation,將運算結果傳給 z
,如下:
1 2 |
|
執行完後會印出 z
值,此值是由 y
的值經過 sigmoid function 的運算所得出來的, z
值如下:
1 2 3 4 |
|
以上過程中,給定了 x
值,經過每一個 module 都要呼叫一次 forward
,最後才能取得 z
的值,
如果一個 network 是由很多個 module 所組成的,這樣要呼叫很多次 forward
,會很麻煩。
而 nn.Container
則是一個容器,它可以把多個 module 放進去,並自動處理這些 module 輸入與輸出值的傳遞。 nn.Container
也是個抽象個類別,泛指能夠把 module 放進去的容器。
其中一種 container 可將前一個 module 的輸出,傳給下一個 module 的輸入,這種 contanier 為 nn.Sequential()
。
例如,如果要將 l1
的輸出,傳入 s1
,則可以用 nn.Sequential
來達成。建立一個命名為 n1
的 sequential container ,將 l1
和 s1
串起來,方法如下:
1 2 3 |
|
其中,add
是將 module 加入 container 的 function。
其實, nn.Container
也是從 nn.Module
繼承而來的,所以它也有 forward
,可以進行 forward propagation ,只要呼叫了 nn.Container
的 forward
,它就會自動將裡面的 module 進行 forward ,並輸出結果。
如此給定 x
之後,只要進行一次 forward 即可取得 z
的結果,如下:
1 2 3 |
|
執行完後,會印出 z
的結果,如下:
1 2 3 4 |
|
nn.Container & nn.Sequential
這邊要介紹 nn.Container
和 nn.Sequential
的程式碼。
nn.Container
程式碼:https://github.com/torch/nn/blob/master/Container.lua
nn.Sequential
程式碼: https://github.com/torch/nn/blob/master/Sequential.lua
首先,介紹 nn.Container
,先看 init()
的部分:
1 2 3 4 5 6 |
|
第1行處,看到 nn.Container
是從 nn.Module
繼承而來的,因此它具有 nn.Module
的 variable 及 function 。
第5行中,container 的變量 modules
,它是一個 lua table ,是用來存放加到 container 中的 module 。
至於,要如何將 module 加到 container 中?可以用 add
加入。 add
的程式碼如下:
1 2 3 4 |
|
在第2行可看到, add
可以把新的 module 加到 modules
中。
1 2 3 |
|
除了加進去之外,也可以用 get
取得 modules
中的某個元素,或是用 size
取得 modules
的大小,程式碼如下:
1 2 3 4 5 6 7 |
|
再來是實作的部分,首先我們建立一個 sequential 的 container ,命名為 n2
,並以 size
取得它所含的 module 個數,如下:
1 2 |
|
剛建立時, 由於 n2
的 modules
是空的,所以 size
是 0。執行結果如下:
1
|
|
再來將 l1
和 s1
依序加入,再看看 size
的變化:
1 2 3 |
|
此時已經加入了兩個 module 進去了,所以 size
會是 2 ,執行結果如下:
1
|
|
可以用 get
取得加進去的 module ,例如,想取得第一個加進去的,作法如下:
1
|
|
第一個加進去的為 nn.Linear
,執行結果如下:
1 2 3 4 5 6 7 8 9 10 |
|
再來看到 nn.Sequential()
的程式碼。
1
|
|
第一行顯示 nn.Sequential
繼承了 nn.Container
。它的 init
也與 nn.Container
共用,沒有再另外實作。
再來看 add
的程式碼。
1 2 3 4 5 6 7 8 |
|
在 nn.Sequential
中, add
除了會將 module 加到 modules
之外,它的 output
會是最後一個加進去的 module 的 output
。
以下實作,將 n2
的 output
和它裡面的兩個 module 的 output
都印出來看看:
1 2 3 |
|
結果如下, n2
的 output 是第二個 module 的 output :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
再來看到 updateOutput
的部分:
1 2 3 4 5 6 7 8 |
|
以上可知,在第3行的 for 迴圈中, modules
中的 module
會依序進行 updateOutput
,並將其 output
傳遞到下個 module
的 input
,而 nn.Sequential
的 output
會是最後一個 module
的 output
假設輸入 n2
的 x
為 ,則進行 forward propagation ,將輸出結果存到 z
,實作如下 :
1 2 3 |
|
透過 updateOutput 中的 for 迴圈,它會依序跑完內部所有的 module,並得出最後結果,結果如下:
1 2 3 4 |
|
如果想看 n2
中有哪些 module ,以及它們的順序,也可以直接用 print
的方式,作法如下:
1
|
|
結果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
以上結果可分成兩部分來看,第一部分是 nn.Sequential
裡面每個 module 的順序,從 input 到 output 之間,依次進行了哪些運算。
第二部份是詳細印出 nn.Sequential
中,有哪些成員 ,它有從 nn.Module
繼承而來的 gradInput
即 output
,也有從 nn.Container
繼承而來的 modules
。而 modules
的部分會詳細列出裡面每個 module 中有哪些 variable。
這部分的程式碼於 tostring
函式中,程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Materials
本次教學的完整程式碼於此:
https://github.com/ckmarkoh/torch_nn_tutorials/blob/master/2_nn_container_and_sequential.ipynb