在神經(jīng)網(wǎng)絡(luò)中,激活函數(shù)負(fù)責(zé)將來自節(jié)點(diǎn)的加權(quán)輸入轉(zhuǎn)換為該輸入的節(jié)點(diǎn)或輸出的激活。ReLU 是一個分段線性函數(shù),如果輸入為正,它將直接輸出,否則,它將輸出為零。它已經(jīng)成為許多類型神經(jīng)網(wǎng)絡(luò)的默認(rèn)激活函數(shù),因?yàn)槭褂盟哪P透菀子?xùn)練,并且通常能夠獲得更好的性能。在本文中,我們來詳細(xì)介紹一下ReLU,主要分成以下幾個部分:

創(chuàng)新互聯(lián)建站堅(jiān)信:善待客戶,將會成為終身客戶。我們能堅(jiān)持多年,是因?yàn)槲覀円恢笨芍档眯刨嚒N覀儚牟缓鲇瞥踉L客戶,我們用心做好本職工作,不忘初心,方得始終。十余年網(wǎng)站建設(shè)經(jīng)驗(yàn)創(chuàng)新互聯(lián)建站是成都老牌網(wǎng)站營銷服務(wù)商,為您提供網(wǎng)站制作、網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、H5開發(fā)、網(wǎng)站制作、品牌網(wǎng)站制作、小程序設(shè)計(jì)服務(wù),給眾多知名企業(yè)提供過好品質(zhì)的建站服務(wù)。
1、Sigmoid 和 Tanh 激活函數(shù)的局限性
2、ReLU(Rectified Linear Activation Function)
3、如何實(shí)現(xiàn)ReLU
4、ReLU的優(yōu)點(diǎn)
5、使用ReLU的技巧
一個神經(jīng)網(wǎng)絡(luò)由層節(jié)點(diǎn)組成,并學(xué)習(xí)將輸入的樣本映射到輸出。對于給定的節(jié)點(diǎn),將輸入乘以節(jié)點(diǎn)中的權(quán)重,并將其相加。此值稱為節(jié)點(diǎn)的summed activation。然后,經(jīng)過求和的激活通過一個激活函數(shù)轉(zhuǎn)換并定義特定的輸出或節(jié)點(diǎn)的“activation”。
最簡單的激活函數(shù)被稱為線性激活,其中根本沒有應(yīng)用任何轉(zhuǎn)換。 一個僅由線性激活函數(shù)組成的網(wǎng)絡(luò)很容易訓(xùn)練,但不能學(xué)習(xí)復(fù)雜的映射函數(shù)。線性激活函數(shù)仍然用于預(yù)測一個數(shù)量的網(wǎng)絡(luò)的輸出層(例如回歸問題)。
非線性激活函數(shù)是更好的,因?yàn)樗鼈冊试S節(jié)點(diǎn)在數(shù)據(jù)中學(xué)習(xí)更復(fù)雜的結(jié)構(gòu) 。兩個廣泛使用的非線性激活函數(shù)是 sigmoid 函數(shù)和 雙曲正切 激活函數(shù)。
Sigmoid 激活函數(shù) ,也被稱為 Logistic函數(shù)神經(jīng)網(wǎng)絡(luò),傳統(tǒng)上是一個非常受歡迎的神經(jīng)網(wǎng)絡(luò)激活函數(shù)。函數(shù)的輸入被轉(zhuǎn)換成介于0.0和1.0之間的值。大于1.0的輸入被轉(zhuǎn)換為值1.0,同樣,小于0.0的值被折斷為0.0。所有可能的輸入函數(shù)的形狀都是從0到0.5到1.0的 s 形。在很長一段時間里,直到20世紀(jì)90年代早期,這是神經(jīng)網(wǎng)絡(luò)的默認(rèn)激活方式。
雙曲正切函數(shù) ,簡稱 tanh,是一個形狀類似的非線性激活函數(shù),輸出值介于-1.0和1.0之間。在20世紀(jì)90年代后期和21世紀(jì)初期,由于使用 tanh 函數(shù)的模型更容易訓(xùn)練,而且往往具有更好的預(yù)測性能,因此 tanh 函數(shù)比 Sigmoid激活函數(shù)更受青睞。
Sigmoid和 tanh 函數(shù)的一個普遍問題是它們值域飽和了 。這意味著,大值突然變?yōu)?.0,小值突然變?yōu)?-1或0。此外,函數(shù)只對其輸入中間點(diǎn)周圍的變化非常敏感。
無論作為輸入的節(jié)點(diǎn)所提供的求和激活是否包含有用信息,函數(shù)的靈敏度和飽和度都是有限的。一旦達(dá)到飽和狀態(tài),學(xué)習(xí)算法就需要不斷調(diào)整權(quán)值以提高模型的性能。
最后,隨著硬件能力的提高,通過 gpu 的非常深的神經(jīng)網(wǎng)絡(luò)使用Sigmoid 和 tanh 激活函數(shù)不容易訓(xùn)練。在大型網(wǎng)絡(luò)深層使用這些非線性激活函數(shù)不能接收有用的梯度信息。錯誤通過網(wǎng)絡(luò)傳播回來,并用于更新權(quán)重。每增加一層,錯誤數(shù)量就會大大減少。這就是所謂的 消失梯度 問題,它能有效地阻止深層(多層)網(wǎng)絡(luò)的學(xué)習(xí)。
雖然非線性激活函數(shù)的使用允許神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)復(fù)雜的映射函數(shù),但它們有效地阻止了學(xué)習(xí)算法與深度網(wǎng)絡(luò)的工作。在2000年代后期和2010年代初期,通過使用諸如波爾茲曼機(jī)器和分層訓(xùn)練或無監(jiān)督的預(yù)訓(xùn)練等替代網(wǎng)絡(luò)類型,這才找到了解決辦法。
為了訓(xùn)練深層神經(jīng)網(wǎng)絡(luò), 需要一個激活函數(shù)神經(jīng)網(wǎng)絡(luò),它看起來和行為都像一個線性函數(shù),但實(shí)際上是一個非線性函數(shù),允許學(xué)習(xí)數(shù)據(jù)中的復(fù)雜關(guān)系 。該函數(shù)還必須提供更靈敏的激活和輸入,避免飽和。
因此,ReLU出現(xiàn)了, 采用 ReLU 可以是深度學(xué)習(xí)革命中為數(shù)不多的里程碑之一 。ReLU激活函數(shù)是一個簡單的計(jì)算,如果輸入大于0,直接返回作為輸入提供的值;如果輸入是0或更小,返回值0。
我們可以用一個簡單的 if-statement 來描述這個問題,如下所示:
對于大于零的值,這個函數(shù)是線性的,這意味著當(dāng)使用反向傳播訓(xùn)練神經(jīng)網(wǎng)絡(luò)時,它具有很多線性激活函數(shù)的理想特性。然而,它是一個非線性函數(shù),因?yàn)樨?fù)值總是作為零輸出。由于矯正函數(shù)在輸入域的一半是線性的,另一半是非線性的,所以它被稱為 分段線性函數(shù)(piecewise linear function ) 。
我們可以很容易地在 Python 中實(shí)現(xiàn)ReLU激活函數(shù)。
我們希望任何正值都能不變地返回,而0.0或負(fù)值的輸入值將作為0.0返回。
下面是一些修正的線性激活函數(shù)的輸入和輸出的例子:
輸出如下:
我們可以通過繪制一系列的輸入和計(jì)算出的輸出,得到函數(shù)的輸入和輸出之間的關(guān)系。下面的示例生成一系列從 -10到10的整數(shù),并計(jì)算每個輸入的校正線性激活,然后繪制結(jié)果。
運(yùn)行這個例子會創(chuàng)建一個圖,顯示所有負(fù)值和零輸入都突變?yōu)?.0,而正輸出則返回原樣:
ReLU函數(shù)的導(dǎo)數(shù)是斜率。負(fù)值的斜率為0.0,正值的斜率為1.0。
傳統(tǒng)上,神經(jīng)網(wǎng)絡(luò)領(lǐng)域已經(jīng)不能是任何不完全可微的激活函數(shù),而ReLU是一個分段函數(shù)。從技術(shù)上講,當(dāng)輸入為0.0時,我們不能計(jì)算ReLU的導(dǎo)數(shù),但是,我們可以假設(shè)它為0。
tanh 和 sigmoid 激活函數(shù)需要使用指數(shù)計(jì)算, 而ReLU只需要max(),因此他 計(jì)算上更簡單,計(jì)算成本也更低 。
ReLU的一個重要好處是,它能夠輸出一個真正的零值 。這與 tanh 和 sigmoid 激活函數(shù)不同,后者學(xué)習(xí)近似于零輸出,例如一個非常接近于零的值,但不是真正的零值。這意味著負(fù)輸入可以輸出真零值,允許神經(jīng)網(wǎng)絡(luò)中的隱層激活包含一個或多個真零值。這就是所謂的稀疏表示,是一個理想的性質(zhì),在表示學(xué)習(xí),因?yàn)樗梢约铀賹W(xué)習(xí)和簡化模型。
ReLU看起來更像一個線性函數(shù),一般來說,當(dāng)神經(jīng)網(wǎng)絡(luò)的行為是線性或接近線性時,它更容易優(yōu)化 。
這個特性的關(guān)鍵在于,使用這個激活函數(shù)進(jìn)行訓(xùn)練的網(wǎng)絡(luò)幾乎完全避免了梯度消失的問題,因?yàn)樘荻热匀慌c節(jié)點(diǎn)激活成正比。
ReLU的出現(xiàn)使得利用硬件的提升和使用反向傳播成功訓(xùn)練具有非線性激活函數(shù)的深層多層網(wǎng)絡(luò)成為可能 。
很長一段時間,默認(rèn)的激活方式是Sigmoid激活函數(shù)。后來,Tanh成了激活函數(shù)。 對于現(xiàn)代的深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò),默認(rèn)的激活函數(shù)是ReLU激活函數(shù) 。
ReLU 可以用于大多數(shù)類型的神經(jīng)網(wǎng)絡(luò), 它通常作為多層感知機(jī)神經(jīng)網(wǎng)絡(luò)和卷積神經(jīng)網(wǎng)絡(luò)的激活函數(shù) ,并且也得到了許多論文的證實(shí)。傳統(tǒng)上,LSTMs 使用 tanh 激活函數(shù)來激活cell狀態(tài),使用 Sigmoid激活函數(shù)作為node輸出。 而ReLU通常不適合RNN類型網(wǎng)絡(luò)的使用。
偏置是節(jié)點(diǎn)上具有固定值的輸入,這種偏置會影響激活函數(shù)的偏移,傳統(tǒng)的做法是將偏置輸入值設(shè)置為1.0。當(dāng)在網(wǎng)絡(luò)中使用 ReLU 時, 可以將偏差設(shè)置為一個小值,例如0.1 。
在訓(xùn)練神經(jīng)網(wǎng)絡(luò)之前,網(wǎng)絡(luò)的權(quán)值必須初始化為小的隨機(jī)值。當(dāng)在網(wǎng)絡(luò)中使用 ReLU 并將權(quán)重初始化為以零為中心的小型隨機(jī)值時,默認(rèn)情況下,網(wǎng)絡(luò)中一半的單元將輸出零值。有許多啟發(fā)式方法來初始化神經(jīng)網(wǎng)絡(luò)的權(quán)值,但是沒有最佳權(quán)值初始化方案。 何愷明的文章指出Xavier 初始化和其他方案不適合于 ReLU ,對 Xavier 初始化進(jìn)行一個小的修改,使其適合于 ReLU,提出He Weight Initialization,這個方法更適用于ReLU 。
在使用神經(jīng)網(wǎng)絡(luò)之前對輸入數(shù)據(jù)進(jìn)行縮放是一個很好的做法。這可能涉及標(biāo)準(zhǔn)化變量,使其具有零均值和單位方差,或者將每個值歸一化為0到1。如果不對許多問題進(jìn)行數(shù)據(jù)縮放,神經(jīng)網(wǎng)絡(luò)的權(quán)重可能會增大,從而使網(wǎng)絡(luò)不穩(wěn)定并增加泛化誤差。 無論是否在網(wǎng)絡(luò)中使用 ReLU,這種縮放輸入的良好實(shí)踐都適用。
ReLU 的輸出在正域上是無界的。這意味著在某些情況下,輸出可以繼續(xù)增長。因此,使用某種形式的權(quán)重正則化可能是一個比較好的方法,比如 l1或 l2向量范數(shù)。 這對于提高模型的稀疏表示(例如使用 l 1正則化)和降低泛化誤差都是一個很好的方法 。
.
import mathdef sigmoid(x,derivate=False): if derivate: return sigmoid(x)*(1-sigmoid(x)) return 1.0 / (1+math.exp(-x)) def relu(x): if x 0: return x else: return 0sigmoid 求導(dǎo)和其函數(shù)值相關(guān)
在本文中,將探討如何可視化卷積神經(jīng)網(wǎng)絡(luò)(CNN),該網(wǎng)絡(luò)在計(jì)算機(jī)視覺中使用最為廣泛。首先了解CNN模型可視化的重要性,其次介紹可視化的幾種方法,同時以一個用例幫助讀者更好地理解模型可視化這一概念。
正如上文中介紹的癌癥腫瘤診斷案例所看到的,研究人員需要對所設(shè)計(jì)模型的工作原理及其功能掌握清楚,這點(diǎn)至關(guān)重要。一般而言,一名深度學(xué)習(xí)研究者應(yīng)該記住以下幾點(diǎn):
1.1 理解模型是如何工作的
1.2 調(diào)整模型的參數(shù)
1.3 找出模型失敗的原因
1.4 向消費(fèi)者/終端用戶或業(yè)務(wù)主管解釋模型做出的決定
2.可視化CNN模型的方法
根據(jù)其內(nèi)部的工作原理,大體上可以將CNN可視化方法分為以下三類:
初步方法:一種顯示訓(xùn)練模型整體結(jié)構(gòu)的簡單方法
基于激活的方法:對單個或一組神經(jīng)元的激活狀態(tài)進(jìn)行破譯以了解其工作過程
基于梯度的方法:在訓(xùn)練過程中操作前向傳播和后向傳播形成的梯度
下面將具體介紹以上三種方法,所舉例子是使用Keras深度學(xué)習(xí)庫實(shí)現(xiàn),另外本文使用的數(shù)據(jù)集是由“識別數(shù)字”競賽提供。因此,讀者想復(fù)現(xiàn)文中案例時,請確保安裝好Kears以及執(zhí)行了這些步驟。
研究者能做的最簡單的事情就是繪制出模型結(jié)構(gòu)圖,此外還可以標(biāo)注神經(jīng)網(wǎng)絡(luò)中每層的形狀及參數(shù)。在keras中,可以使用如下命令完成模型結(jié)構(gòu)圖的繪制:
model.summary()_________________________________________________________________Layer (type) ? ? ? ? ? ? ? ? Output Shape ? ? ? ? ? ? ?Param # ?
=================================================================conv2d_1 (Conv2D) ? ? ? ? ? ?(None, 26, 26, 32) ? ? ? ?320_________________________________________________________________conv2d_2 (Conv2D) ? ? ? ? ? ?(None, 24, 24, 64) ? ? ? ?18496_________________________________________________________________max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64) ? ? ? ?0_________________________________________________________________dropout_1 (Dropout) ? ? ? ? ?(None, 12, 12, 64) ? ? ? ?0_________________________________________________________________flatten_1 (Flatten) ? ? ? ? ?(None, 9216) ? ? ? ? ? ? ?0_________________________________________________________________dense_1 (Dense) ? ? ? ? ? ? ?(None, 128) ? ? ? ? ? ? ? 1179776_________________________________________________________________dropout_2 (Dropout) ? ? ? ? ?(None, 128) ? ? ? ? ? ? ? 0_________________________________________________________________preds (Dense) ? ? ? ? ? ? ? ?(None, 10) ? ? ? ? ? ? ? ?1290 ? ? ?
=================================================================Total params: 1,199,882Trainable params: 1,199,882Non-trainable params: 0
還可以用一個更富有創(chuàng)造力和表現(xiàn)力的方式呈現(xiàn)模型結(jié)構(gòu)框圖,可以使用keras.utils.vis_utils函數(shù)完成模型體系結(jié)構(gòu)圖的繪制。
另一種方法是繪制訓(xùn)練模型的過濾器,這樣就可以了解這些過濾器的表現(xiàn)形式。例如,第一層的第一個過濾器看起來像:
top_layer = model.layers[0]plt.imshow(top_layer.get_weights()[0][:, :, :, 0].squeeze(), cmap='gray')
一般來說,神經(jīng)網(wǎng)絡(luò)的底層主要是作為邊緣檢測器,當(dāng)層數(shù)變深時,過濾器能夠捕捉更加抽象的概念,比如人臉等。
為了理解神經(jīng)網(wǎng)絡(luò)的工作過程,可以在輸入圖像上應(yīng)用過濾器,然后繪制其卷積后的輸出,這使得我們能夠理解一個過濾器其特定的激活模式是什么。比如,下圖是一個人臉過濾器,當(dāng)輸入圖像是人臉圖像時候,它就會被激活。
from vis.visualization import visualize_activation
from vis.utils import utils
from keras import activations
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (18, 6)
# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx = utils.find_layer_idx(model, 'preds')
# Swap softmax with linear
model.layers[layer_idx].activation = activations.linear
model = utils.apply_modifications(model)
# This is the output node we want to maximize.filter_idx = 0
img = visualize_activation(model, layer_idx, filter_indices=filter_idx)
plt.imshow(img[..., 0])
同理,可以將這個想法應(yīng)用于所有的類別,并檢查它們的模式會是什么樣子。
for output_idx in np.arange(10):
# Lets turn off verbose output this time to avoid clutter and just see the output.
img = visualize_activation(model, layer_idx, filter_indices=output_idx, input_range=(0., 1.))
plt.figure()
plt.title('Networks perception of {}'.format(output_idx))
plt.imshow(img[..., 0])
在圖像分類問題中,可能會遇到目標(biāo)物體被遮擋,有時候只有物體的一小部分可見的情況。基于圖像遮擋的方法是通過一個灰色正方形系統(tǒng)地輸入圖像的不同部分并監(jiān)視分類器的輸出。這些例子清楚地表明模型在場景中定位對象時,若對象被遮擋,其分類正確的概率顯著降低。
為了理解這一概念,可以從數(shù)據(jù)集中隨機(jī)抽取圖像,并嘗試?yán)L制該圖的熱圖(heatmap)。這使得我們直觀地了解圖像的哪些部分對于該模型而言的重要性,以便對實(shí)際類別進(jìn)行明確的區(qū)分。
def iter_occlusion(image, size=8):
# taken from
occlusion = np.full((size * 5, size * 5, 1), [0.5], np.float32)
occlusion_center = np.full((size, size, 1), [0.5], np.float32)
occlusion_padding = size * 2
# print('padding...')
image_padded = np.pad(image, ( \? (occlusion_padding, occlusion_padding), (occlusion_padding, occlusion_padding), (0, 0) \? ), 'constant', constant_values = 0.0)
for y in range(occlusion_padding, image.shape[0] + occlusion_padding, size):
? for x in range(occlusion_padding, image.shape[1] + occlusion_padding, size):
? ? ? tmp = image_padded.copy()
? ? ? tmp[y - occlusion_padding:y + occlusion_center.shape[0] + occlusion_padding, \
? ? ? ? x - occlusion_padding:x + occlusion_center.shape[1] + occlusion_padding] \? ? ? ? ? ? = occlusion
? ? ? tmp[y:y + occlusion_center.shape[0], x:x + occlusion_center.shape[1]] = occlusion_center? ? ? ? ? yield x - occlusion_padding, y - occlusion_padding, \
? ? ? ? tmp[occlusion_padding:tmp.shape[0] - occlusion_padding, occlusion_padding:tmp.shape[1] - occlusion_padding]i = 23 # for exampledata = val_x[i]correct_class = np.argmax(val_y[i])
# input tensor for model.predictinp = data.reshape(1, 28, 28, 1)# image data for matplotlib's imshowimg = data.reshape(28, 28)
# occlusionimg_size = img.shape[0]
occlusion_size = 4print('occluding...')heatmap = np.zeros((img_size, img_size), np.float32)class_pixels = np.zeros((img_size, img_size), np.int16)
from collections import defaultdict
counters = defaultdict(int)for n, (x, y, img_float) in enumerate(iter_occlusion(data, size=occlusion_size)):
X = img_float.reshape(1, 28, 28, 1)
out = model.predict(X)
#print('#{}: {} @ {} (correct class: {})'.format(n, np.argmax(out), np.amax(out), out[0][correct_class]))
#print('x {} - {} | y {} - {}'.format(x, x + occlusion_size, y, y + occlusion_size))
heatmap[y:y + occlusion_size, x:x + occlusion_size] = out[0][correct_class]
class_pixels[y:y + occlusion_size, x:x + occlusion_size] = np.argmax(out)
counters[np.argmax(out)] += 1
正如之前的坦克案例中看到的那樣,怎么才能知道模型側(cè)重于哪部分的預(yù)測呢?為此,可以使用顯著圖解決這個問題。顯著圖首先在這篇文章中被介紹。
使用顯著圖的概念相當(dāng)直接——計(jì)算輸出類別相對于輸入圖像的梯度。這應(yīng)該告訴我們輸出類別值對于輸入圖像像素中的微小變化是怎樣變化的。梯度中的所有正值告訴我們,像素的一個小變化會增加輸出值。因此,將這些梯度可視化可以提供一些直觀的信息,這種方法突出了對輸出貢獻(xiàn)最大的顯著圖像區(qū)域。
class_idx = 0indices = np.where(val_y[:, class_idx] == 1.)[0]
# pick some random input from here.idx = indices[0]
# Lets sanity check the picked image.from matplotlib import pyplot as plt%matplotlib inline
plt.rcParams['figure.figsize'] = (18, 6)plt.imshow(val_x[idx][..., 0])
from vis.visualization import visualize_saliency
from vis.utils import utilsfrom keras import activations# Utility to search for layer index by name.
# Alternatively we can specify this as -1 since it corresponds to the last layer.
layer_idx = utils.find_layer_idx(model, 'preds')
# Swap softmax with linearmodel.layers[layer_idx].activation = activations.linear
model = utils.apply_modifications(model)grads = visualize_saliency(model, layer_idx, filter_indices=class_idx, seed_input=val_x[idx])
# Plot with 'jet' colormap to visualize as a heatmap.plt.imshow(grads, cmap='jet')
# This corresponds to the Dense linear layer.for class_idx in np.arange(10):
indices = np.where(val_y[:, class_idx] == 1.)[0]
idx = indices[0]
f, ax = plt.subplots(1, 4)
ax[0].imshow(val_x[idx][..., 0])
for i, modifier in enumerate([None, 'guided', 'relu']):
? ? grads = visualize_saliency(model, layer_idx, filter_indices=class_idx,
? ? seed_input=val_x[idx], backprop_modifier=modifier)
? ? if modifier is None:
? ? ? ? modifier = 'vanilla'
? ? ax[i+1].set_title(modifier)
? ? ax[i+1].imshow(grads, cmap='jet')
類別激活映射(CAM)或grad-CAM是另外一種可視化模型的方法,這種方法使用的不是梯度的輸出值,而是使用倒數(shù)第二個卷積層的輸出,這樣做是為了利用存儲在倒數(shù)第二層的空間信息。
from vis.visualization import visualize_cam
# This corresponds to the Dense linear layer.for class_idx in np.arange(10):
indices = np.where(val_y[:, class_idx] == 1.)[0]
idx = indices[0]f, ax = plt.subplots(1, 4)
ax[0].imshow(val_x[idx][..., 0])
for i, modifier in enumerate([None, 'guided', 'relu']):
grads = visualize_cam(model, layer_idx, filter_indices=class_idx,
seed_input=val_x[idx], backprop_modifier=modifier)
if modifier is None:
? ? modifier = 'vanilla'
ax[i+1].set_title(modifier)
ax[i+1].imshow(grads, cmap='jet')
本文簡單說明了CNN模型可視化的重要性,以及介紹了一些可視化CNN網(wǎng)絡(luò)模型的方法,希望對讀者有所幫助,使其能夠在后續(xù)深度學(xué)習(xí)應(yīng)用中構(gòu)建更好的模型。 免費(fèi)視頻教程:
在本次,我們將學(xué)習(xí)如何自定義一個torch.autograd.Function,下面是本次的主要內(nèi)容
1. 對Function的直觀理解;
2. Function與Module的差異與應(yīng)用場景;
3. 寫一個簡單的ReLU Function;
在之前的介紹中,我們知道,Pytorch是利用Variable與Function來構(gòu)建計(jì)算圖的。回顧下Variable,Variable就像是計(jì)算圖中的節(jié)點(diǎn),保存計(jì)算結(jié)果(包括前向傳播的激活值,反向傳播的梯度),而Function就像計(jì)算圖中的邊,實(shí)現(xiàn)Variable的計(jì)算,并輸出新的Variable。Function簡單說就是對Variable的運(yùn)算,如加減乘除,relu,pool等。但它不僅僅是簡單的運(yùn)算。與普通Python或者numpy的運(yùn)算不同,F(xiàn)unction是針對計(jì)算圖,需要計(jì)算反向傳播的梯度。因此他不僅需要進(jìn)行該運(yùn)算(forward過程),還需要保留前向傳播的輸入(為計(jì)算梯度),并支持反向傳播計(jì)算梯度。如果有做過公開課cs231的作業(yè),記得里面的每個運(yùn)算都定義了forward,backward,并通過保存cache來進(jìn)行反向傳播。這兩者是類似的。在之前Variable的學(xué)習(xí)中,我們知道進(jìn)行一次運(yùn)算后,輸出的Variable對應(yīng)的creator就是其運(yùn)行的計(jì)算,如y = relu(x), y.creator,就是relu這個Function。我們可以對Function進(jìn)行拓展,使其滿足我們自己的需要,而拓展就需要自定義Function的forward運(yùn)算,以及對應(yīng)的backward運(yùn)算,同時在forward中需要通過保存輸入值用于backward。總結(jié),F(xiàn)unction與Variable構(gòu)成了pytorch的自動求導(dǎo)機(jī)制,它定義的是各個Variable之間的計(jì)算關(guān)系。
2. Function與Module的差異與應(yīng)用場景
Function與Module都可以對pytorch進(jìn)行自定義拓展,使其滿足網(wǎng)絡(luò)的需求,但這兩者還是有十分重要的不同:
1)Function一般只定義一個操作,因?yàn)槠錈o法保存參數(shù),因此適用于激活函數(shù)、pooling等操作;Module是保存了參數(shù),因此適合于定義一層,如線性層,卷積層,也適用于定義一個網(wǎng)絡(luò)。
2)Function需要定義三個方法:__init__, forward, backward(需要自己寫求導(dǎo)公式);Module:只需定義__init__和forward,而backward的計(jì)算由自動求導(dǎo)機(jī)制構(gòu)成。
3)可以不嚴(yán)謹(jǐn)?shù)恼J(rèn)為,Module是由一系列Function組成,因此其在forward的過程中,F(xiàn)unction和Variable組成了計(jì)算圖,在backward時,只需調(diào)用Function的backward就得到結(jié)果,因此Module不需要再定義backward。
4)Module不僅包括了Function,還包括了對應(yīng)的參數(shù),以及其他函數(shù)與變量,這是Function所不具備的
1)首先我們定義一個繼承Function的ReLU類;
2)然后我們來看Variable在進(jìn)行運(yùn)算時,其creator是否是對應(yīng)的Function;
3)最后我們?yōu)榉奖闶褂眠@個ReLU類,將其wrap成一個函數(shù),方便調(diào)用,不必每次顯式都創(chuàng)建一個新對象;
3.1 定義一個ReLU類
3.2 驗(yàn)證Variable與Function的關(guān)系
輸出:
可見,F(xiàn)unction連接了Variable與Variable,并實(shí)現(xiàn)不同計(jì)算。
3.3 Wrap一個ReLU函數(shù)
可以直接把剛才自定義的ReLU類封裝成一個函數(shù),方便直接調(diào)用
輸出:
網(wǎng)站標(biāo)題:relu函數(shù)python,relu函數(shù)公式
文章起源:http://chinadenli.net/article22/dsgiejc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、網(wǎng)站內(nèi)鏈、網(wǎng)站導(dǎo)航、品牌網(wǎng)站設(shè)計(jì)、搜索引擎優(yōu)化、軟件開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)