https://blog.csdn.net/tostq/article/details/51788093
上一節(jié)我們介紹了卷積神經(jīng)網(wǎng)絡(luò)的前向傳播過程,這一節(jié)我們重點(diǎn)介紹反向傳播過程揍堕,反向傳播過程反映神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)訓(xùn)練過程郑象。
誤差反向傳播方法是神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)的基礎(chǔ)精耐,網(wǎng)絡(luò)上已經(jīng)有許多相關(guān)的內(nèi)容了,不過關(guān)于卷積網(wǎng)絡(luò)的誤差反向傳遞的公式推導(dǎo)卻比較少,而且也不是很清晰桃熄,本文將會(huì)詳細(xì)推導(dǎo)這個(gè)過程,雖然內(nèi)容很復(fù)雜型奥,但卻值得學(xué)習(xí).
首先我們需要知道的是誤差反向傳播的學(xué)習(xí)方法瞳收,實(shí)際是梯度下降法求最小誤差的權(quán)重過程。當(dāng)然我們的目的是求誤差能量關(guān)于參數(shù)(權(quán)重)的導(dǎo)數(shù).
梯度下降法更新權(quán)重公式如下所示:
這里表示權(quán)重厢汹,表示誤差能量螟深,表示第輪更新迭代,表示學(xué)習(xí)參數(shù)烫葬,表示輸出界弧,表示局域梯度凡蜻。
而另一方面誤差能量關(guān)于參數(shù)(權(quán)重)的導(dǎo)數(shù)同當(dāng)前層輸入是相關(guān)的,所以我們需要一個(gè)更好地將當(dāng)前層誤差傳遞給下一層的量垢箕,因?yàn)檫@個(gè)同當(dāng)前層的輸出無關(guān)划栓,其只是反映了當(dāng)前層的固定結(jié)構(gòu),所以我們可以將這個(gè)固有性質(zhì)反向傳遞給下一層条获,其定義為:
接下來我們分層分析整個(gè)網(wǎng)絡(luò)的反向傳播過程忠荞。在本文的卷積神經(jīng)網(wǎng)絡(luò)中主要有以下四種情況:
一、輸出層(單層神經(jīng)網(wǎng)絡(luò)層)
(1)誤差能量定義為實(shí)際輸出與理想輸出的誤差
這里的是理想預(yù)期輸出帅掘,指實(shí)際輸出委煤,指輸出位,本文的網(wǎng)絡(luò)輸出為10位修档,所以碧绞。
(2)誤差能量關(guān)于參數(shù)(權(quán)重)的導(dǎo)數(shù)。
這一層是比較簡(jiǎn)單的
由于本文是采用系數(shù)的激活函數(shù)吱窝,所以其導(dǎo)數(shù)可以求出為:
其局域梯度表示為:
二讥邻、后接輸出層的采樣層S4
后接輸出層的采樣層向多層感知器的隱藏神經(jīng)元的反向傳播是類似的。
由于這一層沒有權(quán)重癣诱,所以不需要進(jìn)行權(quán)重更新计维,但是我們也需要將誤差能量傳遞給下一層,所以需要計(jì)算局域梯度撕予,其定義如下鲫惶,這里j指輸出圖像中的像素序號(hào),S4層共有個(gè)輸出像素实抡,所以欠母。
另外輸出層的局域梯度也已經(jīng)計(jì)算過了:
由于采樣層沒有激活函數(shù),所以的導(dǎo)數(shù)為1吆寨,則最終可以得到
通過上式赏淌,我們就可以算出由輸出層傳遞到層的局域梯度值∽那澹可以看出傳遞到采樣層各輸出像素j的局域梯度值六水,實(shí)際是相當(dāng)于與其相連的下層輸出的局域梯度值乘上相連權(quán)重的總和。
三辣卒、后接采樣層的卷積層C1掷贾、C3
前面為了方便計(jì)算,S4層和O5層的輸出都被展開成了一維荣茫,所以像素都是以i和j作為標(biāo)號(hào)的想帅,到了C3層往前,我們以像素的坐標(biāo)來標(biāo)號(hào)港准,表示第張輸出模板的位置的像素。局域梯度值定義為:
傳遞到該像素的誤差能量等于所有與其相連的像素誤差能量和浅缸,這里的指的采樣鄰域內(nèi)的所有像素
因?yàn)楸疚牟捎玫氖瞧骄鵓ooling方法轨帜,S4的輸出就是該像素鄰域內(nèi)的所有像素的平均值,這里的S指鄰域內(nèi)的所有像素的總數(shù)疗杉,本文采用的是的采樣塊阵谚,所以S=4。
(1)因此由S4傳遞到C3層的局域梯度值為:
接下來我們依據(jù)局域梯度值烟具,來計(jì)算C3層的權(quán)重更新值。
(2)C3層的權(quán)重更新值奠蹬。
C3層共有個(gè)的模板朝聋,我們首先定義,表示模板的標(biāo)號(hào)囤躁,,表示模板中參數(shù)的位置
(3)C1層的權(quán)重更新公式和局域梯度值
同理冀痕,我們也可以得到C1層的權(quán)重更新公式,這里的狸演,言蛇,而是指輸入圖像
四、后接卷積層的采樣層S2
這里的為當(dāng)前S2層的輸出圖像序號(hào)(n=1 ~ 6)宵距,為當(dāng)前C3層的輸出圖像序號(hào)(m=1~12)腊尚。
因此第n塊圖像的局域梯度值為
五、誤差反向傳播過程的代碼展示
void cnnbp(CNN* cnn,float* outputData) // 網(wǎng)絡(luò)的后向傳播
{
int i,j,c,r; // 將誤差保存到網(wǎng)絡(luò)中
for(i=0;i<cnn->O5->outputNum;i++)
cnn->e[i]=cnn->O5->y[i]-outputData[i];
/*從后向前反向計(jì)算*/
// 輸出層O5
for(i=0;i<cnn->O5->outputNum;i++)
cnn->O5->d[i]=cnn->e[i]*sigma_derivation(cnn->O5->y[i]);
// S4層满哪,傳遞到S4層的誤差
// 這里沒有激活函數(shù)
nSize outSize={cnn->S4->inputWidth/cnn->S4->mapSize,cnn->S4->inputHeight/cnn->S4->mapSize};
for(i=0;i<cnn->S4->outChannels;i++)
for(r=0;r<outSize.r;r++)
for(c=0;c<outSize.c;c++)
for(j=0;j<cnn->O5->outputNum;j++){
int wInt=i*outSize.c*outSize.r+r*outSize.c+c;
cnn->S4->d[i][r][c]=cnn->S4->d[i][r][c]+cnn->O5->d[j]*cnn->O5->wData[j][wInt];
}
// C3層
// 由S4層傳遞的各反向誤差,這里只是在S4的梯度上擴(kuò)充一倍
int mapdata=cnn->S4->mapSize;
nSize S4dSize={cnn->S4->inputWidth/cnn->S4->mapSize,cnn->S4->inputHeight/cnn->S4->mapSize};
// 這里的Pooling是求平均婿斥,所以反向傳遞到下一神經(jīng)元的誤差梯度沒有變化
for(i=0;i<cnn->C3->outChannels;i++){
float** C3e=UpSample(cnn->S4->d[i],S4dSize,cnn->S4->mapSize,cnn->S4->mapSize);
for(r=0;r<cnn->S4->inputHeight;r++)
for(c=0;c<cnn->S4->inputWidth;c++)
cnn->C3->d[i][r][c]=C3e[r][c]*sigma_derivation(cnn->C3->y[i][r][c])/(float)(cnn->S4->mapSize*cnn->S4->mapSize);
for(r=0;r<cnn->S4->inputHeight;r++)
free(C3e[r]);
free(C3e);
}
// S2層,S2層沒有激活函數(shù)哨鸭,這里只有卷積層有激活函數(shù)部分
// 由卷積層傳遞給采樣層的誤差梯度民宿,這里卷積層共有6*12個(gè)卷積模板
outSize.c=cnn->C3->inputWidth;
outSize.r=cnn->C3->inputHeight;
nSize inSize={cnn->S4->inputWidth,cnn->S4->inputHeight};
nSize mapSize={cnn->C3->mapSize,cnn->C3->mapSize};
for(i=0;i<cnn->S2->outChannels;i++){
for(j=0;j<cnn->C3->outChannels;j++){
float** corr=correlation(cnn->C3->mapData[i][j],mapSize,cnn->C3->d[j],inSize,full);
addmat(cnn->S2->d[i],cnn->S2->d[i],outSize,corr,outSize);
for(r=0;r<outSize.r;r++)
free(corr[r]);
free(corr);
}
/*
for(r=0;r<cnn->C3->inputHeight;r++)
for(c=0;c<cnn->C3->inputWidth;c++)
// 這里本來用于采樣的激活
*/
}
// C1層,卷積層
mapdata=cnn->S2->mapSize;
nSize S2dSize={cnn->S2->inputWidth/cnn->S2->mapSize,cnn->S2->inputHeight/cnn->S2->mapSize};
// 這里的Pooling是求平均像鸡,所以反向傳遞到下一神經(jīng)元的誤差梯度沒有變化
for(i=0;i<cnn->C1->outChannels;i++){
float** C1e=UpSample(cnn->S2->d[i],S2dSize,cnn->S2->mapSize,cnn->S2->mapSize);
for(r=0;r<cnn->S2->inputHeight;r++)
for(c=0;c<cnn->S2->inputWidth;c++)
cnn->C1->d[i][r][c]=C1e[r][c]*sigma_derivation(cnn->C1->y[i][r][c])/(float)(cnn->S2->mapSize*cnn->S2->mapSize);
for(r=0;r<cnn->S2->inputHeight;r++)
free(C1e[r]);
free(C1e);
}
}