前面幾節(jié)我們學(xué)習(xí)了深度學(xué)習(xí)模型的基本原理與實(shí)現(xiàn),下面我們繼續(xù)學(xué)習(xí)深度學(xué)習(xí)計(jì)算的各個(gè)組成部分,為后續(xù)學(xué)習(xí)復(fù)雜模型打下基礎(chǔ)。
在學(xué)習(xí)模型構(gòu)造之前,我們先了解一下什么是計(jì)算圖。
計(jì)算圖 (Computational Graph)
計(jì)算圖是用來描述運(yùn)算的有向無環(huán)圖,有兩個(gè)主要元素:
節(jié)點(diǎn) (Node) :節(jié)點(diǎn)表示數(shù)據(jù),如向量、矩陣、張量。
邊 (Edge):邊表示運(yùn)算,如加減乘除卷積等。
用計(jì)算圖表示:y=(x1+w)*(x2+w),如下圖所示:
計(jì)算圖
其中,x1, x2, w, y 分別為節(jié)點(diǎn),+ ,* 為節(jié)點(diǎn)之間的操作,即邊。故y=a*b,其中a=x1+w, b=x2+w。
計(jì)算圖與梯度求導(dǎo)
求上圖y對(duì)w的導(dǎo)數(shù),根據(jù)復(fù)合函數(shù)的求導(dǎo)法則,推理如下:
對(duì)應(yīng)到計(jì)算圖中,就是根節(jié)點(diǎn)y到葉子節(jié)點(diǎn)w有兩條路徑y(tǒng)->a->w和y->b->w。根節(jié)點(diǎn)依次對(duì)每條路徑的子節(jié)點(diǎn)求導(dǎo),直到葉子節(jié)點(diǎn)w,最后把每條路徑的導(dǎo)數(shù)相加即可,其實(shí)就是我們前面講過的反向傳播求偏導(dǎo)。
總結(jié)為y對(duì)w求導(dǎo),就是從計(jì)算圖中找到所有y到w的路徑,然后把各個(gè)路徑的導(dǎo)數(shù)進(jìn)行求和。
代碼演示如下:
PyTorch動(dòng)態(tài)計(jì)算圖演示
計(jì)算圖又分為靜態(tài)計(jì)算圖 (Static Computational Graph)和動(dòng)態(tài)計(jì)算圖 (Dynamic Computational Graph):
動(dòng)態(tài)圖就是運(yùn)算和搭建同時(shí)進(jìn)行,也就是可以先計(jì)算前面的節(jié)點(diǎn)的值,再根據(jù)這些值搭建后面的計(jì)算圖。
靜態(tài)圖是先搭建圖,然后再輸入數(shù)據(jù)進(jìn)行運(yùn)算,是先定義后運(yùn)行的方式,之后再次運(yùn)行的時(shí)候就不再需要重新構(gòu)建計(jì)算圖,所以速度會(huì)比動(dòng)態(tài)圖更快,但是不靈活。
PyTorch因其動(dòng)態(tài)圖的特色吸引了很多使用者,而Tensorflow早期是用的靜態(tài)圖導(dǎo)致開發(fā)很不友好,后來也改成了動(dòng)態(tài)圖模式。
tensorflow靜態(tài)計(jì)算圖演示
動(dòng)態(tài)圖和靜態(tài)圖對(duì)比
模型構(gòu)造
PyTorch是基于動(dòng)態(tài)圖的模型搭建方式,我們可以隨機(jī)的在網(wǎng)絡(luò)中添加或者刪除網(wǎng)絡(luò)層。PyTorch為我們提供了非常方便的nn工具箱,我們搭建模型只需要定義一個(gè)繼承自nn.module的類并實(shí)現(xiàn)其init和forward方法就可。
其中nn.Module類是nn模塊中提供的一個(gè)模型構(gòu)造類,是所有神經(jīng)網(wǎng)絡(luò)模塊的基礎(chǔ)類。我們需要繼承它來定義我們的自己的網(wǎng)絡(luò)結(jié)構(gòu)。init方法中動(dòng)態(tài)綁定成員變量,定義我們的網(wǎng)絡(luò)結(jié)構(gòu)和相關(guān)參數(shù);forword方法中決定數(shù)據(jù)流經(jīng)這些成員變量的順序,即網(wǎng)絡(luò)的前向傳播方式。
PyTorch中nn工具箱的結(jié)構(gòu)示意圖
一般來說,我們構(gòu)建網(wǎng)絡(luò)模型時(shí)用到的卷積層、全連接層、Dropout層等含有可學(xué)習(xí)參數(shù)的層都是繼承nn.Module,而激活函數(shù)、池化層等函數(shù)類型的層繼承于nn.functional。
1.繼承Module類來構(gòu)造模型
下面定義的MLP 類中無須定義反向傳播函數(shù)。系統(tǒng)將通過自動(dòng)求梯度而自動(dòng)生成反向傳播所需的backward 函數(shù)。
我們可以實(shí)例化 MLP 類得到模型變量 net 。下的代碼初始化 net 并傳輸數(shù)據(jù) X 做次前向計(jì)算。其中, net(X) 會(huì)調(diào)用 MLP 繼承 Module 類的 __call__ 函數(shù),這個(gè)函數(shù)將調(diào)用 MLP 類定義的forward 函數(shù)來完成前向計(jì)算。
繼承Module類的基礎(chǔ)模型
這并沒有將 Module 類命名為 Layer (層)或者 Model (模型)之類的名字,這是因?yàn)樵擃愂且粋€(gè)可供自由組建的部件。它的子類既可以是一個(gè)層(如PyTorch提供的 Linear 類),又可以是個(gè)模型(如這定義的 MLP 類),或者是模型的一個(gè)部分。我們下面來展示它的靈活性。
2.Module的子類
PyTorch還提供了許多繼承自Module的類,如:Sequential、ModuleList和ModuleDict等。
2.1Sequential類
當(dāng)模型的前向計(jì)算為簡(jiǎn)單的串聯(lián)各個(gè)網(wǎng)絡(luò)層的時(shí)候,可以通過Sequential類以更加簡(jiǎn)單的方式來定義模型。Sequential可以接收一個(gè)子模塊的有序字典(OrderedDict)或者一系列的子模塊作為參數(shù)來逐一的添加Module的子類的實(shí)例。在前向傳播計(jì)算的時(shí)候,可以將這些實(shí)例按照添加的順序逐一計(jì)算,向前傳播。這里實(shí)現(xiàn)一個(gè)MySequential類,其機(jī)制和Sequential類似。
舉例如下:
Sequential類模型
2.2 ModuleList類
ModuleList類接收一個(gè)子模塊的列表作為輸入,也可以類似List那樣進(jìn)行append和extend操作。類似于我們建立一個(gè)list,list內(nèi)部中的每一個(gè)元素代表一個(gè)網(wǎng)絡(luò)層。
舉例如下:
ModuleList類模型
ModuleList不同于一般的Python的list,加入到ModuleList里面的所有模塊的參數(shù)會(huì)被自動(dòng)添加到整個(gè)網(wǎng)絡(luò)中。
2.3 ModuleDict類
ModuleDict類接收一個(gè)子模塊的字典作為輸入,然后按照類似于字典的形式進(jìn)行添加訪問操作,舉例如下:
ModuleDict類模型
ModuleDict和ModuleList類似的是,ModuleDict實(shí)例僅僅是存放了一些模塊的字典,并沒有定義forward函數(shù),前向傳播的方式需要我們自己定義。同樣,ModuleDict也與Python的Dict有所不同,ModuleDict里的所有模塊的參數(shù)會(huì)被自動(dòng)添加到整個(gè)網(wǎng)絡(luò)結(jié)構(gòu)的內(nèi)部。
3.構(gòu)造復(fù)雜的模型
上面介紹的Sequential使用簡(jiǎn)單,但靈活性不足。通常我們還是自定義類,繼承nn.Module,去完成更復(fù)雜的模型定義和控制。下面的我們嘗試構(gòu)建一個(gè)復(fù)雜點(diǎn)的網(wǎng)絡(luò)來總結(jié)上面的內(nèi)容,該網(wǎng)絡(luò)中包含不被迭代的參數(shù),即常數(shù)參數(shù),還多次調(diào)用相同的層。
復(fù)雜模型構(gòu)建
總結(jié)
PyTorch是基于動(dòng)態(tài)圖的模型搭建方式。
Module類是PyTorch中所有神經(jīng)網(wǎng)絡(luò)模塊的基類,也是個(gè)可供自由構(gòu)建的模塊。它的子類既可以是個(gè)層(如PyTorch提供的 Linear 類),又可以是一個(gè)模型(如這里是定義的 MLP 類),或者是模型的一個(gè)部分。
Sequential、ModuleList、ModuleDict類都繼承自Module類。
Sequential內(nèi)的模塊需要按照順序排列,要保證相鄰層的輸入輸出大小相匹配,內(nèi)部forward功能已經(jīng)實(shí)現(xiàn)。與Sequential不同,ModuleList和ModuleDict并沒有定義一個(gè)完整的網(wǎng)絡(luò),它們只是將不同的模塊存放在一起,這些模塊之間沒有聯(lián)系也沒有順序(所以不用保證相鄰層的輸入輸出維度匹配),需要自己定義forward函數(shù)。
雖然Sequential等類可以使模型構(gòu)造更加簡(jiǎn)單,但直接繼承Module類可以極大地拓展模型構(gòu)造的靈活性。