深度學(xué)習(xí)與圖像識(shí)別34 神經(jīng)網(wǎng)絡(luò)基礎(chǔ)15 ?誤差反向傳播

前面介紹了神經(jīng)網(wǎng)絡(luò)斯议,并通過(guò)數(shù)值微分計(jì)算了神經(jīng)網(wǎng)絡(luò)的權(quán)重參數(shù)以及偏置量(bias)鞍爱。

雖然數(shù)值微分實(shí)現(xiàn)起來(lái)比較容易敲才,但是在計(jì)算上花費(fèi)的時(shí)間卻比較多樟遣。

下面重點(diǎn)介紹一個(gè)高效計(jì)算權(quán)重以及偏置量的梯度方法——誤差反向傳播法喘批。

要點(diǎn)具體如下。

·激活函數(shù)層的實(shí)現(xiàn)。

·Affine層的實(shí)現(xiàn)。

·Softmax層的實(shí)現(xiàn)低矮。

·整體實(shí)現(xiàn)。

·正則化懲罰被冒。

激活函數(shù)層的實(shí)現(xiàn) 通過(guò)計(jì)算圖來(lái)理解誤差反向傳播法這個(gè)思想是參考了CS231n(斯坦福大學(xué)的深度學(xué)習(xí)課程)军掂,計(jì)算圖被定義為有向圖,其中昨悼,節(jié)點(diǎn)對(duì)應(yīng)于數(shù)學(xué)運(yùn)算蝗锥,計(jì)算圖是表達(dá)和評(píng)估數(shù)學(xué)表達(dá)式的一種方式。

例如率触, 我們可以繪制上述數(shù)學(xué)等式的計(jì)算圖具有一個(gè)加法節(jié)點(diǎn)终议,這個(gè)節(jié)點(diǎn)有兩個(gè)輸入變量x和y,以及一個(gè)輸出q葱蝗。

下面我們?cè)賮?lái)列舉一個(gè)示例穴张,稍微復(fù)雜一些,等式的計(jì)算圖两曼,(x+y)*z的計(jì)算圖ReLU反向傳播實(shí)現(xiàn) 現(xiàn)在陆馁,我們利用計(jì)算圖的思路來(lái)實(shí)現(xiàn)ReLU激活函數(shù)的反向傳播,首先我們回顧一下激活函數(shù)ReLU的前向傳播:

如果前向傳播時(shí)的輸入x大于0合愈,則將這個(gè)x原封不動(dòng)地傳給下一層;如果輸入的x小于0击狮,則將0傳給下一層佛析。具體表達(dá)方程式如下:

通過(guò)上述方程式,我們可以求出y關(guān)于x的導(dǎo)數(shù)彪蓬,其中寸莫,dout為上一層傳過(guò)來(lái)的導(dǎo)數(shù):

ReLU前向傳播利用Python實(shí)現(xiàn)的代碼如下:

class Relu:

? ?def __init__(self):

? ? ? ?self.x = None

? ?def forward(self,x):

? ? ? ?self.x = np.maximum(0,x)

? ? ? ?out = self.x

? ? ? ?return out

? ?def backward(self,dout):

? ? ? ?dx = dout

dx[self.x <=0] = 0

? ? ? ?return dx

Sigmoid反向傳播實(shí)現(xiàn) 接下來(lái),我們來(lái)實(shí)現(xiàn)Sigmoid函數(shù)的反向傳播档冬,Sigmoid函數(shù)公式如下所示:

如果使用計(jì)算圖來(lái)表示的話膘茎,Sigmoid計(jì)算圖 現(xiàn)在,從右向左依次解說(shuō)如下酷誓。 對(duì)于第一個(gè)步驟y=1/1+exp(-x)披坏,可以設(shè)置為x=1+exp(-x),那么盐数,又因?yàn)榘舴鳎宰詈蟆?

對(duì)于第二個(gè)步驟1+exp(-x),進(jìn)行反向傳播時(shí)帚屉,會(huì)將上游的值-y2乘以本階段的導(dǎo)數(shù)谜诫,對(duì)于1+exp(-x)求導(dǎo)得到的導(dǎo)數(shù)為-exp(-x),因?yàn)閑-x的導(dǎo)數(shù)為-e-x攻旦。

所以第二步的導(dǎo)數(shù)為-y2*(-e-x)=y2*(e-x)喻旷。 第三個(gè)步驟的加法運(yùn)算不會(huì)改變導(dǎo)數(shù)值,接著-x的導(dǎo)數(shù)為-1牢屋,所以對(duì)于這個(gè)階段需要將y2*(e-x)*-1且预,最后乘法運(yùn)算還需要乘以-1。

所以最終求得的導(dǎo)數(shù)為y2*exp(-x)伟阔,進(jìn)行一下整理得到的輸出為y(1-y)辣之,最后乘以上一層的求導(dǎo)結(jié)果,就會(huì)作為本階段Sigmoid函數(shù)的求導(dǎo)結(jié)果了皱炉,最后將這個(gè)結(jié)果傳給下一層(一般來(lái)說(shuō)應(yīng)該是Affine層)怀估。

對(duì)于Python實(shí)現(xiàn)來(lái)說(shuō),具體實(shí)現(xiàn)代碼如下合搅。 ?

class _sigmoid:

? ?def __init__(self):

? ? ? ?self.out = None

def forward(self,x):

? ? ? ?out = 1/ (1+np.exp(-x))

? ? ? ?self.out = out

? ? ? ?return out

? ?def backward(self,dout):

? ? ? ?dx = dout *self.out*(1-self.out)

? ? ? ?return dx

Affine層的實(shí)現(xiàn) Affine的英文翻譯是神經(jīng)網(wǎng)絡(luò)中的一個(gè)全連接層多搀。

仿射(Affine)的意思是前面一層中的每一個(gè)神經(jīng)元都連接到當(dāng)前層中的每一個(gè)神經(jīng)元。在許多方面灾部,這是神經(jīng)網(wǎng)絡(luò)的“標(biāo)準(zhǔn)”層

仿射層通常被加在卷積神經(jīng)網(wǎng)絡(luò)或循環(huán)神經(jīng)網(wǎng)絡(luò)中作為最終預(yù)測(cè)前的輸出的頂層康铭。

仿射層的一般形式為y=f(W*x+b),其中赌髓,x是層輸入从藤,w是參數(shù),b是一個(gè)偏置量锁蠕,f是一個(gè)非線性激活函數(shù)夷野。

對(duì)X(矩陣)的求導(dǎo),可以參看如下公式(此處省略推導(dǎo)過(guò)程)需要注意的是荣倾,X和形狀相同悯搔,W和的形狀相同): ? ?之前列舉的示例是對(duì)于一個(gè)X,如果是多個(gè)X舌仍,那么其形狀將從原來(lái)的(2妒貌,)變?yōu)椋∟,2)铸豁。

如果加上偏置量的話灌曙,偏置量會(huì)被加到各個(gè)X·W中去,比如N=3(數(shù)據(jù)為3個(gè)的時(shí)候)推姻,偏置量會(huì)被分別加到這3個(gè)數(shù)據(jù)中去平匈,因此偏置量,f是一個(gè)非線性激活函數(shù)。

對(duì)X(矩陣)的求導(dǎo)增炭,可以參看如下公式(此處省略推導(dǎo)過(guò)程)需要注意的是忍燥,X和形狀相同,W和的形狀相同): ? ?之前列舉的示例是對(duì)于一個(gè)X隙姿,如果是多個(gè)X梅垄,那么其形狀將從原來(lái)的(2,)變?yōu)椋∟输玷,2)队丝。

如果加上偏置量的話,偏置量會(huì)被加到各個(gè)X·W中去欲鹏,比如N=3(數(shù)據(jù)為3個(gè)的時(shí)候)机久,偏置量會(huì)被分別加到這3個(gè)數(shù)據(jù)中去,因此偏置量的反向傳播會(huì)對(duì)這三個(gè)數(shù)據(jù)的導(dǎo)數(shù)按照第0軸的方向上的元素進(jìn)行求和赔嚎。

其中每一個(gè)偏置量的求導(dǎo)公式都可以表示為: ? ?對(duì)于上述所講的內(nèi)容膘盖,其Python實(shí)現(xiàn)代碼如下:

class Affine:

? ?def __init__(self,W,b):

? ? ? ?self.W = W

? ? ? ?self.b = b

? ? ? ?self.x = None

? ? ? ?self.dW = None

? ? ? ?self.db = None

? ?def forward(self,x):

? ? ? ?self.x = x

? ? ? ?out = np.dot(x,self.W) + self.b

? ? ? ?return out

? ?def backward(self,dout):

? ? ? ?dx = np.dot(dout,self.W.T)

? ? ? ?self.dW = np.dot(self.x.T,dout)

? ? ? ?self.db = np.sum(dout,axis=0)

? ? ? ?return dx

Softmaxwithloss層的實(shí)現(xiàn) 假設(shè)網(wǎng)絡(luò)最后一層的輸出為z,經(jīng)過(guò)Softmax后輸出為p尤误,真實(shí)標(biāo)簽為y(one-hot編碼)侠畔,其中,C表示共有C個(gè)類別损晤,那么損失函數(shù)為:

因?yàn)閜是z經(jīng)過(guò)Softmax函數(shù)計(jì)算后的輸出软棺,即p=softmax(z)。其中尤勋, ? ?求導(dǎo)過(guò)程分為i=j和i喘落!=j兩種情況,分別如下:

當(dāng)i=j的時(shí)候最冰,得到的求導(dǎo)解為pj(1-pj)揖盘。 當(dāng)i!=j的時(shí)候锌奴,得到的求導(dǎo)解為-pipj。 最終整理一下可以得到憾股,Loss對(duì)z的求導(dǎo)為: ? ?其Python的實(shí)現(xiàn)代碼具體如下:

class SoftmaxWithLoss:

? ?def __init__(self):

? ? ? ?self.loss = None #損失

? ? ? ?self.p = None # Softmax的輸出

? ? ? ?self.y = None #監(jiān)督數(shù)據(jù)代表真值鹿蜀,one-hot vector

? ?def forward(self,x,y):

? ? ? ?self.y = y

? ? ? ?self.p = softmax(x)

? ? ? ?self.loss = cross_entropy_error(self.p,self.y)

? ? ? ?return self.loss

? ?def backward(self,dout=1):

? ? ? ?batch_size = self.y.shape[0]

dx = (self.p - self.y) / batch_size

? ? ? ?return dx ?

上述代碼實(shí)現(xiàn)是利用了之前實(shí)現(xiàn)的Softmax和cross_entropy_error函數(shù),值得注意的是服球,進(jìn)行反向傳播的時(shí)候茴恰,應(yīng)將需要傳播的值除以批的大小(batch_size)斩熊,并將單個(gè)數(shù)據(jù)的誤差傳遞給前面的層往枣。

基于數(shù)值微分和誤差反向傳播的比較 到目前為止,我們介紹了兩種求梯度的方法:

一種是基于數(shù)值微分的方法,另一種是基于誤差反向傳播的方法分冈,對(duì)于數(shù)值微分來(lái)說(shuō)圾另,它的計(jì)算非常耗費(fèi)時(shí)間,如果讀者對(duì)于誤差反向傳播掌握得非常好的話雕沉,那么根本就沒(méi)有必要使用到數(shù)值微分〖牵現(xiàn)在的問(wèn)題是,我們?yōu)槭裁匆榻B數(shù)值微分呢坡椒?

原因很簡(jiǎn)單扰路,數(shù)值微分的優(yōu)點(diǎn)就在于其實(shí)現(xiàn)起來(lái)非常簡(jiǎn)單,一般情況下倔叼,數(shù)值微分實(shí)現(xiàn)起來(lái)不太容易出錯(cuò)汗唱,而誤差反向傳播法的實(shí)現(xiàn)就非常復(fù)雜,且很容易出錯(cuò)丈攒,所以經(jīng)常會(huì)比較數(shù)值微分和誤差反向傳播的結(jié)果(兩者的結(jié)果應(yīng)該是非常接近的)哩罪,以確認(rèn)我們書寫的反向傳播邏輯是正確的。這樣的操作就稱為梯度確認(rèn)(gradientcheck)肥印。

數(shù)值微分和誤差反向傳播這兩者的比較誤差應(yīng)該是非常小的识椰,實(shí)現(xiàn)代碼具體如下:


from collections import OrderedDict

class TwoLayerNet:

? ?def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):

? ? ? ?#初始化權(quán)重

? ? ? ?self.params = {}

? ? ? ?self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)

? ? ? ?self.params['b1'] = np.zeros(hidden_size)

? ? ? ?self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)

? ? ? ?self.params['b2'] = np.zeros(output_size)

? ? ? ?#生成層

? ? ? ?self.layers = OrderedDict()

? ? ? ?self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])

? ? ? ?self.layers['Relu1'] = Relu()

? ? ? ?self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

? ? ? ?self.layers['Relu2'] = Relu()

? ? ? ?self.lastLayer = SoftmaxWithLoss()

? ?def predict(self, x):

? ? ? ?for layer in self.layers.values():

? ? ? ? ? ?x = layer.forward(x)

? ? ? ?return x

? ?# x:輸入數(shù)據(jù), y:監(jiān)督數(shù)據(jù)

? ?def loss(self, x, y):

? ? ? ?p = self.predict(x)

? ? ? ?return self.lastLayer.forward(p, y)

? ?def accuracy(self, x, y):

? ? ? ?p = self.predict(x)

? ? ? ?p = np.argmax(y, axis=1)

? ? ? ?if y.ndim != 1 : y = np.argmax(y, axis=1)

? ? ? ?accuracy = np.sum(p == y) / float(x.shape[0])

? ? ? ?return accuracy

? ?# x:輸入數(shù)據(jù), y:監(jiān)督數(shù)據(jù)

? ?def numerical_gradient(self, x, y):

? ? ? ?loss_W = lambda W: self.loss(x, y)

? ? ? ?grads = {}

? ? ? ?grads['W1'] = numerical_gradient(loss_W, self.params['W1'])

? ? ? ?grads['b1'] = numerical_gradient(loss_W, self.params['b1'])

? ? ? ?grads['W2'] = numerical_gradient(loss_W, self.params['W2'])

? ? ? ?grads['b2'] = numerical_gradient(loss_W, self.params['b2'])

? ? ? ?return grads

? ?def gradient(self, x, y):

? ? ? ?# forward

? ? ? ?self.loss(x, y)

? ? ? ?# backward

? ? ? ?dout = 1

? ? ? ?dout = self.lastLayer.backward(dout)

? ? ? ?layers = list(self.layers.values())

? ? ? ?layers.reverse()

? ? ? ?for layer in layers:

? ? ? ? ? ?dout = layer.backward(dout)

? ? ? ?#設(shè)定

? ? ? ?grads = {}

? ? ? ?grads['W1'], grads['b1'] = self.layers['Affine1'].dW, ?self.layers['Affine1'].db

? ? ? ?grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

? ? ? ?return grads

network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)

x_batch = x_train[:100]

y_batch = y_train[:100]

grad_numerical = network.numerical_gradient(x_batch,y_batch)

grad_backprop = network.gradient(x_batch,y_batch)

for key in grad_numerical.keys():

? ?diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) )

? ?print(key + ":" + str(diff)) ?從以下輸出結(jié)果中,我們可以觀察到深碱,它們兩者的差值并不是很大腹鹉。

W1:5.9329106471124405e-05

b1:1.844024470884823e-09

W2:0.0007755803070111151

b2:9.234723605880401e-08

這里需要補(bǔ)充一點(diǎn)的是,我們?cè)诖a中使用了OrderedDict這個(gè)類敷硅,OrderedDict是有序字典功咒,“有序”是指它可以“記住”我們向這個(gè)類里添加元素的順序,因此神經(jīng)網(wǎng)絡(luò)的前向傳播只需要按照添加元素的順序調(diào)用各層的Forward方法即可完成處理绞蹦,而相對(duì)的誤差反向傳播則只需要按照前向傳播相反的順序調(diào)用各層的backward方法即可力奋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幽七,隨后出現(xiàn)的幾起案子景殷,更是在濱河造成了極大的恐慌,老刑警劉巖澡屡,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猿挚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡驶鹉,警方通過(guò)查閱死者的電腦和手機(jī)绩蜻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)室埋,“玉大人办绝,你說(shuō)我怎么就攤上這事伊约。” “怎么了孕蝉?”我有些...
    開(kāi)封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵屡律,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我昔驱,道長(zhǎng)疹尾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任骤肛,我火速辦了婚禮纳本,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腋颠。我一直安慰自己繁成,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布淑玫。 她就那樣靜靜地躺著巾腕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪絮蒿。 梳的紋絲不亂的頭發(fā)上尊搬,一...
    開(kāi)封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音土涝,去河邊找鬼佛寿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛但壮,可吹牛的內(nèi)容都是我干的冀泻。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蜡饵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼弹渔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起溯祸,我...
    開(kāi)封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肢专,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后焦辅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鸟召,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年氨鹏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片压状。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仆抵,死狀恐怖跟继,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情镣丑,我是刑警寧澤舔糖,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站抖所,受9級(jí)特大地震影響崔涂,放射性物質(zhì)發(fā)生泄漏众羡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一摇庙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遥缕,春花似錦卫袒、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至户秤,卻和暖如春码秉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鸡号。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工转砖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人膜蠢。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓堪藐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親挑围。 傳聞我的和親對(duì)象是個(gè)殘疾皇子礁竞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容