My Octopress Blog

A blogging framework for hackers.

SSD(2) Bounding-Box Regression

| Comments

以往我們對神經網路的概念都只是侷限在於輸入一張圖片然後對輸出做分類的動作,就像圖左邊的概念圖一樣,假設輸入的這張圖片是貓,那麼就會根據設定給出對應的學習輸出,在學術上我們稱作label,那麼未來在給定一張網路未曾見過的圖片時,若第一顆神經元的輸出數字很大,代表網路判定這張圖片有很高的機率為貓,這就是傳統的分類機器學習,學術上稱做classification。隨著研究的不斷發展,科學家們發現網路不單單只能做分類,還能做到更進階的物件定位,即在圖上把對應的物件以一個矩形框出對應的位置,學術上稱作localization,那麼學習的輸出應該給定什麼呢?很簡單,定義一個矩形就只要四個參數就可以決定了,分別是物件座標中心(X,Y)、物件的寬(W)、物件的高(H),但是一張圖的物件數量是未知的,若我們很確定未來預測的每張圖片都只有兩個物件,那麼輸出就只要設定成8顆神經元即可

但實際情況可不是這樣,每張圖片的物件數量不定,怎麼解決這個問題呢?科學家想出一個很棒的辦法,即利用特定設計的default box來做回歸學習,學術上也稱作anchor,假設anchor的位置和物件真實的位置(ground truth)很接近,那麼就會把學習偵測此物件的工作交由給此anchor學習,如此的動作在學術上稱做Bounding-Box regression

既然要把某個真實物件的座標(ground truth)交由特定的anchor學習,聰明的科學家們就設定出一套定義兩者之間的轉換公式,讓網路學習並定出相對定的label,在學術上稱做encode,而對於已經學習好的網路,拿到輸出並解析出在圖上真正位置稱為decode

encode

論文上的encode 公式定義如下

底下是各個參數對應名稱:

$cx$:代表物體中心的x座標

$g$:代表ground truth box

$d$:代表defaut box

$i$:是default box的索引

$j$:是ground truth box的索引

$\hat g^{cx}_j$:代表ground truth box中心點的x座標和defaut box中心點x座標相減並除以defaut box的寬

$\hat g^{cy}_j$:代表ground truth box中心點的y座標和defaut box中心點y座標相減並除以defaut box的高

$\hat g^{w}_j$:將ground truth box的寬除以defaut box的寬並取log

$\hat g^{h}_j$:將ground truth box的高除以defaut box的高並取log

$m$:是個集合,由四個元素組成,分別是cx,cy,w,h

$l^m_i$:代表由網路預測出第i個defaut box的值

別被複雜的公式嚇跑了,其實白話文來說就是:

$\hat g^{cx}_j=$ (ground truth中心點座標X-anchor中心點座標X)/anchor的寬

$\hat g^{cy}_j=$ (ground truth中心點座標Y-anchor中心點座標Y)/anchor的高

$\hat g^{w}_j=$ log(ground truth的寬/anchor的寬)

$\hat g^{h}_j=$ log(ground truth的高/anchor的高)

因此,encode後的數字就是網路要學習的label,我們希望網路學習後的結果能夠和這label越接近越好

而上述的圖片只是示意圖,代表只有一個anchor在學習,實際上使用會從各層抽出特徵圖出來,然後往後接出各層的anchor,因此會有數千個不等的anchor在學習,取決於網路的設置,像下圖所示的,每個點有3個anchor在學習,總共有25個點,因此這層的anchor總數就是75個

而實際上訓練的時候還會把encode後的值再乘上某個參數,按照y、x、w、h的參數順序分別是[10.0, 10.0, 5.0, 5.0],底下為encode完整程式碼

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
# https://github.com/tensorflow/models/blob/master/research/object_detection/box_coders/faster_rcnn_box_coder.py
self._scale_factors = [10.0, 10.0, 5.0, 5.0]
def _encode(self, boxes, anchors):
    """Encode a box collection with respect to anchor collection.
    Args:
      boxes: BoxList holding N boxes to be encoded.
      anchors: BoxList of anchors.
    Returns:
      a tensor representing N anchor-encoded boxes of the format
      [ty, tx, th, tw].
    """
    # Convert anchors to the center coordinate representation.
    ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes()
    ycenter, xcenter, h, w = boxes.get_center_coordinates_and_sizes()
    # Avoid NaN in division and log below.
    ha += EPSILON
    wa += EPSILON
    h += EPSILON
    w += EPSILON

    tx = (xcenter - xcenter_a) / wa
    ty = (ycenter - ycenter_a) / ha
    tw = tf.log(w / wa)
    th = tf.log(h / ha)
    # Scales location targets as used in paper for joint training.
    if self._scale_factors:
      ty *= self._scale_factors[0]
      tx *= self._scale_factors[1]
      th *= self._scale_factors[2]
      tw *= self._scale_factors[3]
    return tf.transpose(tf.stack([ty, tx, th, tw]))

decode

decode的公式即把公式回推,白話文解釋為

ground truth中心點座標X = (anchor的寬 X 網路輸出中心點座標X)+anchor中心點座標X

ground truth中心點座標Y = (anchor的高 X 網路輸出中心點座標Y)+anchor中心點座標Y

ground truth的寬 = e^(網路輸出的寬) X anchor的寬

ground truth的高 = e^(網路輸出的高) X anchor的高

由於encode的時候有乘上固定參數,因此decode的時候就必須要把參數除回來,因此,完整decode程式碼如下:

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
# https://github.com/tensorflow/models/blob/master/research/object_detection/box_coders/faster_rcnn_box_coder.py
self._scale_factors = [10.0, 10.0, 5.0, 5.0]
def _decode(self, rel_codes, anchors):
    """Decode relative codes to boxes.
    Args:
      rel_codes: a tensor representing N anchor-encoded boxes.
      anchors: BoxList of anchors.
    Returns:
      boxes: BoxList holding N bounding boxes.
    """
    ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes()

    ty, tx, th, tw = tf.unstack(tf.transpose(rel_codes))
    if self._scale_factors:
      ty /= self._scale_factors[0]
      tx /= self._scale_factors[1]
      th /= self._scale_factors[2]
      tw /= self._scale_factors[3]
    w = tf.exp(tw) * wa
    h = tf.exp(th) * ha
    ycenter = ty * ha + ycenter_a
    xcenter = tx * wa + xcenter_a
    ymin = ycenter - h / 2.
    xmin = xcenter - w / 2.
    ymax = ycenter + h / 2.
    xmax = xcenter + w / 2.
    return box_list.BoxList(tf.transpose(tf.stack([ymin, xmin, ymax, xmax])))

decode 完成後會在圖上出現大大小小的bounding box以及對應的分類分數,總數根據設計而定,但實際應用的時候不可能把全部的box都畫出來,我們會把分數較高的排在前面,分數低的排在後面,然後設定一個閥值,只把分數大於閥值的box畫出來,因此最後網路呈現的結果就會類似如下:

最後再經由一個演算法,學術上稱做NMS(Non-Maximum Suppression),就可以把一些重複性大的box濾掉,因此最後呈現的就是類似下面這張圖。這整個過程就稱為object detection,各位常看到的人臉定位就是object detection的其中一個應用

參考資料: https://mingming97.github.io/2018/10/21/SSD/

SSD(1):Single Shot Multibox Detector

| Comments

剛開始什麼都不太懂,直接就把論文載下來看,看完腦袋滿是問號,因為論文中很多概念都是繼承其他論文的研究,所以很多細節概念並沒有講得特別清晰,後來看到一個SSD教學的投影片,雖然全部內容都是俄文,但投影片的圖就足夠讓我們清楚的了解SSD的整個大架構原理,因此很多圖都是截自這個影片的。底下有附上這投影片的連結,有興趣的朋友可以點擊觀看

SSD: Single Shot MultiBox Detector (How it works)

abstract:

SSD是當今最快的object detection算法,兼具了YOLO的速度與FasterRCNN的準度,本篇論文的最大特點是改進了YOLO只用最後一層來檢測目標而SSD運用多層feature map來做檢測,既兼顧大目標物體檢測亦提升小目標物體檢測的精度。本論文將YOLO在PASCAL VOC 2007 test的檢測精度由63.4%提升到74.3%,速度也從45FPS進展到59FPS(論文中闡述與圖中不同),此外,如果採用512*512的大圖片訓練的話,精度甚至可高達81.6%。

Main:

下面圖解說明YOLO與SSD的不同點: YOLO 是以最後一層全連接層來做box regression的動作,而SSD是結合各層的卷積網路來做box regression YOLO:

SSD: 本文以VGG當作基礎網路做了相關實驗,實際上SSD可以接在任何現有的網路後面,如下圖所示的,它是很多Detector組成的卷積網路

底下附上程式原始碼方便比較,原始碼來源如下: Tensorflow-SSD code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Original VGG-16 blocks.
net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
end_points['block1'] = net
net = slim.max_pool2d(net, [2, 2], scope='pool1')
# Block 2.
net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
end_points['block2'] = net
net = slim.max_pool2d(net, [2, 2], scope='pool2')
# Block 3.
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
end_points['block3'] = net
net = slim.max_pool2d(net, [2, 2], scope='pool3')
# Block 4.
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
end_points['block4'] = net
net = slim.max_pool2d(net, [2, 2], scope='pool4')
# Block 5.
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
end_points['block5'] = net
net = slim.max_pool2d(net, [3, 3], 1, scope='pool5')#max pool

下圖將第四個Detector拆開來,以便了解內部構造,如圖所示,輸入是個5X5X256的卷積特徵圖,而輸出是兩個卷積層,一個輸出形狀為5X5X12,用來預測每個box的四個座標,而每個位置可以自由地設定要幾個default box,像這例子就代表每個位置有三個不同比例的default box,因此輸出設計成12(3X4),另一個輸出形狀為5X5X63,代表每個default box需要21個分類,因此輸出設計成63(CX3),在這裡C為21(其中一類為背景類,因此在這裡的分類有20種)。

每個feature map分成25個位置當作中心點擺3個default box,因此總共有75個default box出現在這層

box預測如中間的紅色小框框所示,深度為4,每一層各存不同的資訊,分別是中心點的座標x,y,以及其寬和高w,h,右邊的小紅框框也是同樣的道理,分別是20種類別再加上一個背景類別

每一層的default box皆不同,將所有box集合起來就可以得到7308個default box

Batchnorm2conv

| Comments

融合batchnorm 至conv中

最近在將模型轉換成嵌入式裝置可以使用的模型時,發現很多人都會將batchnorm層溶入到卷積層中做運算,剛開始看程式都看不太懂,後來查詢了一些資料再自己推導一次,發現是很簡單的數學運算,覺得很有趣,特地記錄下來學習學習~~

公式運算:

原本的卷積核公式為

原本的batch_norm的公式為:

如今要把batchnorm與卷積核融合成為同一個卷積核,也就是說要計算出一個新的權重w’與b’,如下列公式所示

再來就是公式推導了,首先將Z取代為wx+b然後代入

之後移項推一推就可以得出下列式子

對照一下可以發現出新的w’與新的b’對照如下:

程式碼實現(caffe)

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
## load weights
mean = bn[0].data
var = bn[1].data
scalef = bn[2].data

# process scale
scales = scale[0].data
shift = scale[1].data

if scalef != 0:
    scalef = 1. / scalef
mean = mean * scalef
var = var * scalef

## process variance
rstd = 1. / np.sqrt(var + 1e-5)
                
## caculate conv   
rstd1 = rstd.reshape((channels,1,1,1))
scales1 = scales.reshape((channels,1,1,1))
wt = wt * rstd1 * scales1

## caculate bias
bias = (bias - mean) * rstd * scales + shift


## set nobn weights and bias
nobn.params[key][0].data[...] = wt
nobn.params[key][1].data[...] = bias

參考資料:

https://github.com/chuanqi305/MobileNet-SSD/blob/master/merge_bn.py https://www.itread01.com/content/1556626925.html https://stackoverflow.com/questions/49536856/tensorflow-how-to-merge-batchnorm-into-convolution-for-faster-inference https://ithelp.ithome.com.tw/articles/10225171

tags: learning report

類神經網路筆記

| Comments

一.前言

小編看了很多的書籍介紹類神經的,發現這本講的最淺顯易懂而且還附有程式碼,我認為電腦科學與其他物理學、電磁學…等最不同的點在於他是可以看出效果的,是感覺的到的,就算不太懂他的數學原理,反覆地去看程式碼再回來對照公式很快就可以更深入的了解,但是類似電磁學那種理論,即使你經由公式算出了電場是多少,電荷是多少,我依然沒有什麼感覺因為他看不到也摸不到,但電腦科學就不同了,你可以經由寫程式透過軟體去驗證你的理論正不正確,下面就帶各位一步一步地進入類神經的科學理論以及用個實際的例子說明這理論要如何使用。

類神經網路與模糊控制理論入門與應用(附範例程式光碟片)

二.類神經生物模型

當今最神秘的器官不外乎就是我們人類的大腦,就算在21世紀的今日我們還是對大腦還是有許多不解的地方,因此許多科學家就想解開大腦的奧秘,當中一些電腦科學家就想把腦神經的運作原理透過數學的方式推導出一套可行的演算法出來,因此第一代類神經就這樣產生了。

  • 下圖為一個典型的類神經圖解,人的腦神經系統分為下面幾個構造:

圖 1‑1

圖1中繪有兩個神經細胞,每個神經細胞主要由:(1)神經細胞核(Soma)、(2)神經軸(Axon)、(3)神經樹(Dendrites)、(4)神經節(Synapses)等四部分構成。底下,我們依序介紹此四部份。

  • 神經細胞核(Soma)

它是神經細胞的中心體,它的作用,目前並沒有完全徹底的了解,大概是將神經樹收集到的信號,在此作加總後再作一次非線性轉換,再由神經軸將信號傳送到其它的神經細胞中。

  • 神經軸(Axon)

連接在神經細胞核上,用來傳送由神經細胞核產生的信號至其它的神經細胞中。

  • 神經樹(Dendrites)

神經樹分為兩種:輸入神經樹及輸出神經樹。在圖1中左邊接到神經核的神經樹是用來接收其他神經細胞傳來的信號,稱為輸入神經樹。而在圖1右側接到神經軸的神經樹是用來傳送信號至其它神經細胞,稱為輸出神經樹。所以我們可以說:神經樹是神經細胞呈樹枝狀的輸出入機構。

  • 神經節(Synapse)

輸入神經樹和輸出神經樹相連接的點稱為神經節,如圖1中以小圓圈框起來的接點即是。每個神經細胞大約有1000個神經節。神經節是神經網路上的記憶體,它表示兩個神經細胞間的聯結強度,我們將此聯結強度以一個數值來表示,並稱之為加權值(weight)。

一般言之,當神經網路在進行學習時,外界刺激神經細胞所產生的電流會去改變神經節上的加權值,在學習過程中,外界刺激所產生的電流反覆在神經網路上流動,神經節上的加權值也反覆地改變,最後會慢慢的趨向穩定,此時即表示學習已告完成。若神經網路是處於認知或辨識的過程中,由外界刺激所產生的電流,在進入神經網路後,會與貯存在神經節上的加權值作簡單的運算處理,若處理後的信號為可辨識的信號,則外界事物便是神經網路可認知或辨識的的事物了。

三.類神經人工神經元模型

了解了生物神經細胞模型後,我們將介紹如何以人工神經元來模仿生物神經細胞。

讓我們再把相關的知識作個簡單的陳述,請參考圖1,當神經細胞透過輸入神經樹由其它神經細胞輸入脈波訊號後,經過神經細胞核的處理,其處理大約是將收集到的訊號作加總,再作一次非線性轉換後,產生一個新的脈波信號,如果這個訊號夠強,則新的脈波信號會由神經軸傳送到輸出神經樹,再透過神經節將此訊號傳給其它神經細胞。值得注意的是:當訊號經過神經節後,由於神經節加權值的影響,其訊號大小值會改變。經由上述的說明,我們提出人工神經元的模型如圖2所示。

圖 2‑1

在圖2中,每一個人工神經元皆有多個輸入χ1, χ2, …, χn及一個輸出y,輸入值與輸出值的關係式,一般可用輸入值的加權乘積和的函數來表示,即

其中

wi  = 模仿生物神經細胞的神經節加權值。

θ  = 模仿生物神經細胞的細胞核偏權值(bias),即輸入訊號的加權乘積和必須要大

於偏權值後,才能被傳輸至其它人工神經元中。

f(θ) = 模仿生物神經細胞的細胞核非線性轉換函數。

t = 時間。

n = 人工神經元輸入數目。

常用的非線性轉換函數

Sigmoid

圖 3‑1

Tanh

圖 3‑2

ReLU

圖 3‑3

Leaky ReLU

圖 3‑4

四.單層感知機(perceptron)

此種類神經網路是經由F.Rosenblatt在1957年所提出,為最簡單形式的前饋神經網路,是一種二元線性分類器,當初他之所以會提出此種網路,是希望以此網路來模仿動物的大腦及視覺系統。雖然最初被認為有著良好的發展潛能,但感知機最終被證明不能處理諸多的模式識別問題。1969年,人工智慧支父Marvin Minsky和Seymour Papert在《Perceptrons》書中,仔細分析了以感知機為代表的單層神經網絡系統的功能及局限,證明感知機不能解決簡單的XOR等線性不可分問題。

圖 4‑1

單層感知機的數學模型如圖4-1所示,其中ai表示網路的輸入信號,Wi表示網路的加權值,θ 為網路的偏權值,而其輸出方程式為:

因此轉換函數為Step function

圖 4‑2

單層感知機早期應用在印刷字體的識別,如果輸入樣本是線性可分的,那麼單層感知機便可以成功地做分類,如以下例子。底下我們實現一個AND邏輯閘的單層感知機,我們將網路加權值設為 W1=1,W2=1,θ=1.5。

圖 4‑3

我們簡單的套用真質表來檢視這組參數,首先測試第一組X1=0,X2=0,代入公式後呈現

X1W1 + X2W2 − θ = −1.5 < 0

Fstep(−1.5)=0

繼續測試第二組 X1=1,X2=1,代入公式後呈現

X1W1 + X2W2 − θ = 0.5 > 0

Fstep(0.5)=1

各位可以繼續的測試其他兩樣,做出來也都是同樣符合真質表的描述,如果以X軸當資料X1,Y軸當作資料X2畫圖出來的話,可以發現我們所設的參數所描述的就是一條直線方程式,所以只要是畫在平面圖上且資料呈現線性可分特性,用感知機就能學出來。

圖 4‑4

讓我們再來看一個例子,我們來找可以使得感知機可以解決Xor邏輯閘的參數。

根據第一組資料,我們可以推導出式子如下,因此θ > 0

X1 = 0, X2 = 0 → X1W1 + X2W2 = 0 < θ ( 3-4‑1)

再根據第二組資料,推出W2 > θ

X1 = 0, X2 = 1 → X1W1 + X2W2 = W2 > θ ( 4‑2)

繼續第三組資料,推出W1 > θ

X1 = 1, X2 = 0 → X1W1 + X2W2 = W1 > θ ( 4‑3)

最後一組推出 W1 + W2 < θ

X1 = 1, X2 = 1 → X1W1 + X2W2 = W1 + W2 < θ ( 4‑4)

由( 3-4‑2) ( 4‑2) ( 4‑3) 我們得知 W1 + W2 > θ ,但是( 4‑4)卻推出W1 + W2 < θ ,因此我們無法找到一組解使得單層感知機解決 Xor 問題,所以從此第一代類神經走入第一個寒冬中,因為它連Xor這麼簡單的問題都解決不了。

圖 4‑5 圖 4‑6

五.加入隱藏層的感知機

雖然單層感知機無法解決Xor問題,但是有科學家後續發現加入一個隱藏單元就可以順利排除這困難,原因是加入一個隱藏單元及等於是增加了一維變數,它會形成類似如圖4-5的橢圓曲線來做分類。一個可行的網路架構如圖5-1所示,它的輸出方程式為

我們仿造剛剛的運算,繼續將第一組數據帶入驗證測試,第一科神經元為

X1 = 0, X2 = 0 → X1W11 + X2W21 − θ1 = −1.5 < 0 → y′ = 0

X1W12 + X2W22 − 2y′ − θ2 = −0.5 < 0 → y = 0

第二組數據為X1 = 0, X2 = 1,第一二科神經元為

X1 = 0, X2 = 1 → X1W11 + X2W21 − θ1 = −0.5 < 0 → y′ = 0

X1W12 + X2W22 − 2y′ − θ2 = 0.5 > 0 → y = 1

第三組

X1 = 1, X2 = 0 → X1W11 + X2W21 − θ1 = −0.5 < 0 → y′ = 0

X1W12 + X2W22 − 2y′ − θ2 = 0.5 > 0 → y = 1

第四組

X1 = 1, X2 = 1 → X1W11 + X2W21 − θ1 = 0.5 > 0 → y′ = 1

X1W1 + X22W22 − 2y′ − θ2 = −0.5 < 0 → y = 0

圖 5‑1

我們可以將上列方程式簡單的用 Matlab 跑圖驗證一下,可以發現產生如圖5-2 的效果,紅色代表輸出為1,藍色代表輸出為0,多加了一顆隱藏神經元就會產生另一條直線來畫分。

圖 5‑2

後續的研究人員重複著類似的實驗,並把神經元做多項的變化,比較加一層以及加兩層神經元的效果圖,可以統計出一張圖表如下(摘自書中),他們發現神經元加越多層或是神經元加越多顆,所能區分的圖形就更複雜、圖形就更佳多樣化。

圖 5‑3

六.倒傳遞神經網路

倒傳遞神經網路(Back-propagation Neural Network) 是一種具有學習能力的多層前授型網路。此網路是由 Rumelhart、Mcclelland在1985年所提出的,當初他們之所以會提出此種網路,是希望提出一種平行分布訊息的處理方法來探索人類認知的微結構。

圖 6‑1

  • 簡單手算版

我找尋許多資料發現大多數講解倒傳遞類神經模型時都是用一堆數學公式,的確,類神經是經由精巧的微積分並運用微積分的連鎖律,一層一層地從後面傳遞誤差回去,因此我們就可以求出神經元權重的改變量,但直接看公式的話會很容易陷入迷思,因此我在網路上找到一份手算倒傳遞網路的資料 [6],我把它翻譯成中文,相信經過這個例子的說明後再去看公式會容易許多。

圖 6‑2

我們先從簡單的模型開始講起,倒傳遞網路的連接方式與perceptron不同,它是採取全部神經元互相連接的方式,但是只有層與層的神經元互相連結,層之間的神經元不做溝通,旁邊的數字代表參數的初始值為了等一下方便計算,這裡的b就是相當於剛剛所講的θ,事實上,大部分類神經書籍文獻都用b表示,只是書中為了與人腦神經元模型相呼應因此改成θ,在這裡我們將b命名為 bias,並將以前的公式改為如下

假如我們這裡選的函數f是sigmoid函數,因此,這裡h1的輸出值就為

用同樣的算法我們可以得出yh2 = 0.596884378

接下來以同樣的方法計算出o1, o2

outo1 = f(h1w5+h2w6+b2) = 0.75

outo2 = f(h1w7+h2w8+b2) = 0.773

這裡要注意的是作者似乎把bias項目簡化了,事實上每科神經元都各自有一個bias,但這裡作者為了計算方便將兩顆神經元的bias都設為一樣。

接下來我們要定義總體誤差為

其中target就是我們所想得到的值,整個演算法運作的目的就是想讓我的輸出越接近target越好,平方是為了消除正負號所帶來的影響,0.5是為了後面微分方便,加總是指,假如輸出神經元有N個,那就是代表要將N科神經元的誤差都加總起來,所以類神經訓練到最後Etotal會越來越小,代表神經元的輸出已經和我的樣本資料幾乎一模一樣了。

這裡輸出神經元有兩個,因此

再來我們要用微積分的連鎖律求出Etotal和w5的關係式,我們想要知道w5該如何調整能使我的Etotal變得更小,因此我們可以求出$\frac{\partial Etotal}{\partial w5}$ ,找出微分關係式後就可以套用底下公式找出調整w5的方向,其中α稱作學習率。

根據微積分連鎖率我們可以得知

其中neto1指的是f函式中裡面的加總式子,也就是說

neto1 = h1 * w5 + h2 * w6 + b2

接下來就是分別算出各項偏微分,然後再將它們組合起來就可以得到$\frac{\partial Etotal}{\partial w5}$

首先算出第一項$\frac{\partial Etotal}{\partial out_{o1}}$,回想起剛剛定義的Etotal為

因此

再來計算第二項$\frac{\partial out_{o1}}{\partial net_{o1}}$,回想起剛剛解釋的neto1,因此

所以

最後一項$\frac{\partial net_{o1}}{\partial w5}$,因為neto1 = h1 * w5 + h2 * w6 + b2,所以

全部組裝再一起可以得到

假設學習率為0.5,因此我們就可以得知

圖6-3為作者所畫的流程圖,這就是為什麼網路稱為倒傳遞神經網路的原因,從最後一層的Error項傳遞誤差到outo1,然後再到neto1,一直層層傳遞到w5。

圖 6‑3

我們可以用同樣的方法算出其他科神經元

w6new = 0.4

w7new = 0.51

w8new = 0.56

圖 6‑4

再來我們要將Error繼續傳遞到 w1,因此我們要算出$\frac{\partial Etotal}{\partial w1}$

我們一樣套用連鎖率概念可以得知

其中$\frac{\partial Etotal}{\partial out_{h1}}$     為如下,因為改變outh1對後面每一項的誤差皆有影響,因此必須把它們加總起來

我們開始像剛剛一樣逐個計算偏微分項目,從$\frac{\partial E_{o1}}{\partial out_{h1}}$開始,我們可以套用剛剛已經求得的項目

因為 neto1 = w5 * outh1 + w6 * outh2 + b2 * 1,所以

組合起來就得到

同樣的程序我們可以得到

因此

再來繼續算第二項$\frac{\partial out_{h1}}{\partial net_{h1}}$

∵$out_{h1} = \frac{1}{1 + e^{- net_{h1}}}$         

∴$\frac{\partial out_{h1}}{\partial net_{h1}}=out_{h1}*(1-out_{h1})=0.6\times(1-0.6)=0.24$

最後一項$\frac{\partial\text{ne}t_{h1}}{\partial w1}$

∵neth1 = w1 * i1 + w2 * i2 + b1 * 1

∴$\frac{\partial net_{h1}}{\partial w1} = i1 = 0.05$(也就是前一層的輸出)

全部組裝再一起可以得到

因此

$w1_{\text{new}}$

$=w1_{old} - \alpha\frac{\partial Etotal}{\partial w1}$

$= 0.15 - 0.5*0.0004248 = 0.1497876$

同樣的也可以找出隱藏層的其他weight出來

w2new = 0.19956143

w3new = 0.24975114

w4new = 0.29950229

有了前面的例子作簡介後,接下來我們要代入公式版的backpropagation推導,如果在公式版的有哪個地方看不懂,請反覆去回顧剛剛講的簡介,如此來回推敲多次相信很快就能理解的。

  • 複雜公式版

圖 6‑5

$a_{j}^{l} = f(\sum_{k}^{}{w_{\text{jk}}^{l}a_{k}^{l - 1} + b_{j}^{l})} = f(z_{j}^{l})$ ( 6-2‑1)

首先我們先定義各項參數,$a_{j}^{l}$代表在第l層中,第j科神經元的輸出,$w_{\text{jk}}^{l}$代表在模型中第l層和l-1層中的權重值,$b_{j}^{l}$代表在模型中第l層和l-1層中的偏權值,k代表l-1層的神經元索引值。

$E = \frac{1}{2}\sum_{n}^{}\left\lbrack y_{j} - a_{j}^{L} \right\rbrack^{2}$ ( 6-2‑2)

其中,L代表最後一層,$a_{j}^{L}$代表在輸出層中第j科神經元的輸出,$y_j$是代表第j個輸出的樣本,n代表最後一層神經元個數,我們將輸出層所有神經元誤差都加總起來,並加上平方項目以消除正負號所帶來的影響,最後除以2來當作整個神經網路的總體誤差。

我們首先求得總體誤差對輸出層權重質的微分,根據微積分的交換率我們可以改寫如以下公式( 6-2‑3),我們分別對各個篇微分項做運算得到( 6-2‑4) ( 6-2‑5) ( 6-2‑6)

$\frac{\partial E}{\partial w_{\text{jk}}^{L}} = \frac{\partial E}{\partial a_{j}^{L}}*\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}}\times\frac{\partial z_{j}^{L}}{w_{\text{jk}}^{L}}$ ( 6-2‑3)

$\frac{\partial E}{\partial a_{j}^{L}} = - (y_{j} - a_{j}^{L})$ ( 6-2‑4)

$\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}} = \frac{\partial f\left( z_{j}^{L} \right)}{\partial z_{j}^{L}} = f’(z_{j}^{L})$ ( 6-2‑5)

$\frac{\partial z_{j}^{L}}{\partial w_{jk}^{L}}=\frac{\partial\sum_{k}^{}{w_{\text{jk}}^{L}a_{k}^{L - 1} + b_{j}^{L}}}{\partial w_{jk}^{L}} = a_{k}^{L - 1}$( 6-2‑6)

將( 6-2‑4) ( 6-2‑5) ( 6-2‑6)組裝再一起我們可以得到( 6-2‑8),其中$δ_j^L$稱作在L層第j顆神經元的敏感度,式子如( 6-2‑9)所示。

$\frac{\partial E}{\partial w_{\text{jk}}^{L}} = a_{k}^{L - 1}\left( a_{j}^{L} - y_{j} \right)f^{‘}\left( z_{j}^{L} \right) = a_{k}^{L - 1}\delta_{j}^{L}$ ( 6-2‑8)

$δ_j^L = (a_j^L−y_j)f′(z_j^L)$ ( 6-2‑9)

再來我們求得總體誤差對隱藏層權重值的偏導數,我們一樣可以按照連鎖律將式子拆成如( 6-2‑10)所示,依照剛剛的方法分別運算可以求得式子( 6-2‑11) ( 6-2‑12) ( 6-2‑13)

$\frac{\partial E}{\partial w_{\text{ki}}^{L - 1}} = \frac{\partial E}{\partial a_{k}^{L - 1}}\times\frac{\partial a_{k}^{L - 1}}{\partial z_{k}^{L - 1}}\times\frac{\partial z_{k}^{L - 1}}{w_{\text{ki}}^{L - 1}}$ ( 6-2‑10)

$\frac{\partial E}{\partial a_{k}^{L - 1}} = \sum_{j}^{}{\frac{\partial E}{\partial a_{j}^{L}}\times\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}}}\times\frac{\partial z_{j}^{L}}{\partial a_{k}^{L - 1}}$ ( 6-2‑11)

$\frac{\partial a_{k}^{L - 1}}{\partial z_{k}^{L - 1}} = f’(z_{k}^{L - 1})$ ( 6-2‑12)

$\frac{\partial z_{k}^{L - 1}}{w_{\text{ki}}^{L - 1}} = a_{i}^{L - 2}$ ( 6-2‑13)

其中( 6-2‑11-1) 為式子( 6-2‑11)中的子項目,( 6-2‑11-1-1)為( 6-2‑11-1)的子項目,( 6-2‑12) ( 6-2‑13)與( 6-2‑5) ( 6-2‑6)算法一樣只是代號變了。

$\frac{\partial E}{\partial a_{k}^{L - 1}} = \sum_{j}^{}{\frac{\partial E}{\partial a_{j}^{L}}}\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}}\frac{\partial z_{j}^{L}}{\partial a_{k}^{L - 1}}\ = \sum_{j}^{}(a_{j}^{L} - y_{j})f’(z_{j}^{L})w_{\text{jk}}^{L}$ ( 6-2‑11-1)

$\frac{\partial z_{j}^{L}}{\partial a_{k}^{L - 1}} = \frac{\partial\sum_{k}^{}{w_{\text{jk}}^{L}a_{k}^{L - 1} + b_{j}^{L}}}{\partial a_{k}^{L - 1}} = w_{\text{jk}}^{L}$ ( 6-2‑11-1-1)

因此我們可以將其組裝再一起寫做( 6-2‑14),我們可以將後面的項目取代成式子( 6-2‑15),其中$δ_k^{L − 1}$稱做在L-1層中第K個神經元的敏感度。

$\frac{\partial E}{\partial w_{\text{ki}}^{L - 1}}=a_{i}^{L - 2}f’(z_{k}^{L - 1})\sum_{j}^{}{\frac{\partial E}{\partial a_{j}^{L}}\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}}}\frac{\partial z_{j}^{L}}{\partial a_{k}^{L - 1}}$( 6-2‑14)

$\frac{\partial E}{\partial w_{\text{ki}}^{L - 1}} = a_{i}^{L - 2}\delta_{k}^{L - 1}$ ( 6-2‑15)

因此$δ_k^{L − 1}$如公式( 6-2‑16)所示,我們可以將( 6-2‑9)代入公式( 6-2‑16)得到( 6-2‑17),這是類神經中很重要的公式,代表前一層的敏感度是後面一層敏感度與權重的加總,然後再對前一層的神經元做輸出函數的微分,不論類神經模型還是深度學習模型都是靠這行重要的公式反覆的把誤差由後面往前傳做運算以求出總體誤差對權重值的偏導量,以得知權重值的改變方向而使得模型中樣本輸出與最後一層神經元輸出值的差距越來越小,進而使的模型具有擬合任意非線性方程式的意義。

$\delta_{k}^{L - 1} = f’(z_{k}^{L - 1})\sum_{j}^{}{(a_{j}^{L} - y_{j})f’(z_{j}^{L})}w_{jk}^{L}$ ( 6-2‑16)

$= f’\left( z_{k}^{L - 1} \right)\sum_{j}^{}{\delta_{j}^{L}w_{\text{jk}}^{L}}$ ( 6-2‑17)

我們接著繼續求總體誤差對偏權值的偏導項( 6-2‑18),其中前面兩項式子( 6-2‑4) ( 6-2‑5)就已經求過,而最後一項對偏權值的微分項為1,因此可以寫成式子( 6-2‑19),表示總體誤差對偏權值的微分剛好等於該層神經元的敏感度。

$\frac{\partial E}{\partial b_{j}^{L}} = \frac{\partial E}{\partial a_{j}^{L}}\frac{\partial a_{j}^{L}}{\partial z_{j}^{L}}\frac{\partial z_{j}^{L}}{\partial b_{j}^{L}}$ ( 6-2‑18)

$\frac{\partial E}{\partial b_{j}^{L}} = \left( a_{j}^{L} - y_{j} \right)f’\left( z_{j}^{L} \right) = \delta_{j}^{L}$ ( 6-2‑19)

自此,四大公式已全數求出,分別為如下,我將表示最後一層L的代號用任意層l取代,藉以表達出類神經模型中的通用函式:

$\delta_{j}^{l} = \frac{\partial E}{\partial a_{j}^{L}}\ f’\left( z_{j}^{l} \right)$ (Equation 1)

$\delta_{k}^{l - 1} = f’\left( z_{k}^{l - 1} \right)\sum_{j}^{}{\delta_{j}^{l}w_{\text{jk}}^{l}}$ (Equation 2)

$\frac{\partial E}{\partial w_{\text{jk}}^{l}} = a_{k}^{l - 1}\delta_{j}^{l}$ (Equation 3)

$\frac{\partial E}{\partial b_{j}^{l}} = \delta_{j}^{l}$ (Equation 4)

常見的表示式如下

七.參考資料

類神經網路與模糊控制理論 入門與應用 王進德 編著

http://neuron.csie.ntust.edu.tw/homework/93/NN/homework2/M9304302/welcome.htm

http://iamtrask.github.io/2015/07/12/basic-python-network/

https://en.wikipedia.org/wiki/Hadamard_product_(matrices)

http://neuralnetworksanddeeplearning.com/chap2.html

https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

http://cs231n.github.io/optimization-2/

在windows系統上用octopress打造自己的部落格

| Comments

一.前言

小編之前一直都是使用匹克幫這種簡單的網站,不用費太多心力,直接剪剪貼貼就可以將圖片以及想要的資訊分享給大家,但使用一段時間後覺得匹克邦的網頁簡潔度、美觀都不如我長期使用的Markdown好用,常常Markdown寫完一篇文章後還要再花費心力整理到匹克幫然後做排版的動作,實在很費功夫,因此決定換個方式架設我的網站,搜尋了一下後發現竟然有可以使用github當成架設網站伺服器的方法,經過幾番摸索後終於架設成功了,底下就紀錄架設順序方便以後複習。

二.安裝步驟

底下介紹安裝的方法,首先要安裝ruby 環境以及gem

a.安裝Ruby環境

到上面的官網連結下載

1.rubyinstaller-2.3.3-x64.exe

2.DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe\

然後點擊安裝rubyinstaller,記得要勾選自動配置環境。

然後點擊安裝Devkit,選擇完解壓縮的位置後開啟命令提示元移動到安裝的資料夾下輸入以下指令

1
2
ruby dk.rb init 
ruby dk.rb install

b.更新gem

安裝完成功後就要輸入以下指令更新gem,可以打gem測試看看有無成功

1
gem update --system

c.用git拿到octopress資料

執行這步驟前先要確定電腦已經安裝了git,安裝完後打開git bash 輸入以下指令:

1
2
3
4
5
6
7
git clone git://github.com/imathis/octopress.git octopress
cd octopress
gem install bundler
bundle install
rake install

# gem uninstall rake(if you want to delete rake)

d.設定github的路徑

這裡會將部落格原始碼連結到您的github帳號底下,沒有github帳好的請去申請一個,之後創造一個名稱為username.github.io的repository(注意:這裡要把username換成您的github帳號名稱)

之後在git bash視窗下輸入下列程式碼,它會直接跳到下一行等待您輸入github的網域位置

1
2
rake setup_github_pages
git@github.com:darren1231/darren1231.github.io.git

e.接下來就可以運用rake指令管理您的部落格了

  • 建立新文章
1
rake new_post["this is the title"]
  • 發佈
1
2
rake generate
rake deploy
  • 預覽 輸入下列程式碼就可以在本地端觀看網頁(在網址列輸入http://127.0.0.1:4000/)
1
rake preview

f.push 到github上

1
2
3
git add .
git commit -m "first commit"
git push origin source

三.增加額外功能

a.加入disqus 討論區

修改 _config.yml 內的 # Disqus Comments ,填上在 Disqus 申請的 short name ,並改成 true。

b.加入數學公式

  • 1.安裝kramdown
1
gem install kramdown
  • 2.修改_config.yml 取代所有rdiscount 關鍵字成kramdown

  • 3.修改Gemfile 把gem ‘ridiscount’改成gem ‘kramdown in Gemfile

  • 4.添加Mathjax配置

在source/_includes/custom/head.html 添加以下代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- mathjax config similar to math.stackexchange -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  jax: ["input/TeX", "output/HTML-CSS"],
  tex2jax: {
    inlineMath: [ ['$', '$'] ],
    displayMath: [ ['$$', '$$']],
    processEscapes: true,
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
  },
  messageStyle: "none",
  "HTML-CSS": { preferredFont: "TeX", availableFonts: ["STIX","TeX"] }
});
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script>
  • 5.修復Mathjax右擊頁面空白bug

修改 sass/base/_theme.scss,並將div 改成 div#main

1
2
3
4
  > div#main {
     background: $sidebar-bg $noise-bg;
     border-bottom: 1px solid $page-border-bottom;
     > div {
  • 6.測試公式

整段LeTex

1
2
3
4
5
6
7
$$
\begin{align}
\mbox{Union: } & A\cup B = \{x\mid x\in A \mbox{ or } x\in B\} \\
\mbox{Concatenation: } & A\circ B  = \{xy\mid x\in A \mbox{ and } y\in B\} \\
\mbox{Star: } & A^\star  = \{x_1x_2\ldots x_k \mid  k\geq 0 \mbox{ and each } x_i\in A\} \\
\end{align}
$$

內嵌 LaTex

1
2
If $a^2=b$ and $b=2$, then the solution must be
either $a=+\sqrt{2}$ or $a=-\sqrt{2}$.

If $a^2=b$ and $b=2$, then the solution must be either $a=+\sqrt{2}$ or $a=-\sqrt{2}$.

四.參考資料