Neural Networks and Deep Learning習題解答--改進神經網絡的學習方法

神經網絡與深度學習習題解答

改進神經網絡的學習方法

最近在看 Michael Nielsen的Neural Networks and Deep Learning,這本書是以網頁的形式放在網上,非常合適入門,附上傳送地址:http://neuralnetworksanddeeplearning.com/chap1.html#learning_with_gradient_descent

國內哈工大社會計算與信息檢索研究中心將這本書翻譯成中文版放在網絡上,地址爲:https://hit-scir.gitbooks.io/neural-networks-and-deep-learning-zh_cn/content/chap1/c1s5.html

該章詳細介紹交叉熵正則化等一系列可以對神經網絡做改進的技巧和方法。下面給出該章習題

交叉熵部分

  1. 證明σ′(z)=σ(z)(1−σ(z))。
  2. 交叉熵帶來的一個問題就是很難記住表達式中y和a的位置。我們很容易記不清正確的表達式是−[ylna+(1−y)ln(1−a)]還是−[alny+(1−a)ln(1−y)]。當y=0或1時,如果使用了第二個表達式會發生什麼呢?這個問題會發生在第一個表達式上嗎?請說明你的理由。

  3. 在本節開始討論單個神經元時,我曾聲稱如果所有的訓練數據都有σ(z)≈y,那麼交叉熵會變得非常小。這個假設依賴於y非0即1。這對於分類問題是正確的,但是對於其他問題(比如迴歸問題)y的取值可能在0和1之間。證明當所有的訓練數據σ(z)=y時,交叉熵仍然是最小化的。
  4. 我們已經詳盡地討論了當我們使用平方代價來訓練的神經網絡時,會產生輸出神經元飽和、學習速率下降的問題。另一個會妨礙學習的因素是等式(61)中xj項。因爲該項的存在,當輸入xj接近於0時,對應的權重wj會學習得很慢。解釋一下,爲什麼我們不能通過選擇一個好的代價函數來消除xj項。

1. 高中導數知識

 圖(1)

2. 這裏注意y=0或者1,而激活值a一般只可能接近0或1。對於−[alny+(1−a)ln(1−y)],式子本身就不成立,lny在y=0處並沒有定義,及時按照無限接近這樣的思想來處理,lny在y=0處也趨近於無窮大,而a是一個比較小的定值,使得和的第一項趨近於無窮大,而和的第二項的ln(1-y)在y=0的時候爲0,因而代價函數無窮大,這顯然不是理想的結果。用−[ylna+(1−y)ln(1−a)]公式就沒有這樣的結果,因爲y不是0就是1,整個代價函數也在0~1範圍內並接近0。

3. 這個證明儼然一個導數的高考題啊,證明也比較簡單。

圖(2)
4.代價函數對權重的偏導和代價函數對偏置的偏導分別如下:

$$\frac{\partial C}{\partial w_j} = \frac{1}{n} \sum_x x_j(\sigma(z)-y)

$$\frac{\partial C}{\partial b} = \frac{1}{n} \sum_x (\sigma(z)-y)

假設可以通過選擇一個合適的代價函數來消除 xj 項,此時對於同一個輸出, $$\frac{\partial C}{\partial w_j_{1}}$$\frac{\partial C}{\partial w_j_{2}}...$$\frac{\partial C}{\partial w_j_{n}}的計算結果均一致,甚至$$\frac{\partial C}{\partial b}的變化率也和權重變化率一致,此時整個網絡的所有參數將保持一致的變化率和變化趨勢,這顯然不符合神經網絡更新權重的理想結果,也難以達到理想的目標。所以不可通過一個合適好的代價函數來消除 xj 項。

softmax

網上關於softmax有一個很好的圖來表示。帶有softmax層的神經網絡,每個神經元的輸出均與所有的帶權輸入有關。

 圖 (*)

練習有如下:

  1. 構造例子說明在使用 sigmoid 輸出層的網絡中,輸出激活值aLj的和並不一定爲1
  2. softmax 的單調性 - 證明如果j=k,那麼∂aLj/∂zLk是正的,如果j≠k,則是負的。如果我們增加zjL能夠保證增加相應的輸出激活值ajL,同時會減少其它所有的輸出激活值,我們已經通過滑塊清楚地看到這個結論,但是現在需要一個嚴格的證明。
  3. softmax 的非局部性 - sigmoid 層的一個好處是輸出aLj是其對應輸入的一個函數aLj=σ(zLj)。解釋一下爲什麼對於 softmax 層並不是這樣的情況:任何一個輸出激活值aLj依賴於所有的輸入。
  4. 反轉 softmax 層 - 假設我們有一個帶有 softmax 輸出層的神經網絡,同時已知激活值ajL。證明對應的帶權輸入的形式爲zLj=lnaLj+C,其中常數C是不依賴於j的。
  5. 推導等式(81)和(82),等式見下圖。
  6. 「softmax」這個名字來源於哪裏?假設我們改變一下 softmax 函數,使得輸出激活值式(83),其中c是一個正常數。注意c=1對應到標準的 softmax 函數。但是如果我們使用不同的c,我們會得到不同的函數,儘管如此,最後得到的結果也和 softmax 很相似。證明改變c以後,也會像通常的 softmax 函數一樣形成一個概率分佈。假設我們允許c非常大,比如c→∞,那麼輸出激活值ajL的極限是什麼?
  7. 證明表達式(84)。

1. 這樣的例子很多,下面給出一個簡單的例子。我們也可以這麼假設,假設某種情況下滿足激活值的和爲1,那麼此時調整其中一個神經元的偏置,該神經元的激活值就會改變,輸出層激活值的和顯然不爲1了。

圖(3)

2. 按部就班求導就行。這裏注意指數函數本身大於0的性質。

圖(4)

3. 見上文的圖(*),顯然softmax層的每個神經元的輸出公式裏包含了所有的帶權輸入。

4. 證明如下,因爲已知激活值ajL,所以等式右邊 ln 的那個式子是比依賴於 j 的常數,即爲題目中所述的C。

圖(5)

5. 在很多資料中,神經網絡中的log和ln似乎沒有區分,計算均作爲ln計算,求導都是倒數。該題求解見下圖。其中參考了

https://blog.csdn.net/u014313009/article/details/51045303。這裏表達式C寫成累加和的形式是因爲yi代表真實值,如果只預測一個結果,那個yi只有一種情況爲1,所有表達式簡寫爲 C = -ykln(ak),其中k是正確輸出的結果。一般yk=1,那麼就寫成 C= -lna,即書中的形式。是不是感覺這裏與交叉熵非常相似,書中明明說的是對log似然,或者說對數似然,但是這裏卻和交叉熵公式一致,我也有這樣的困惑。查閱資料後,得知對數似然代價函數在多分類時可以化簡爲交叉熵代價函數的形式。來源爲https://juejin.im/post/5b38971be51d4558b10aad26#heading-11

圖(6)

6. softmax在有的譯本中被翻譯爲柔性最大值,這裏的柔性和變化率有關。圖(4)已經給出了激活函數對於帶權輸入的偏導數,這也被直接用於圖(6)中代價函數關於權重和偏置的變化率的證明中。對於式(83),因爲多了c,該式求導後將要多出係數c,當c趨近於無窮,權重和偏置的變化率將非常大,c=1的時候變化率最小,可以認爲使得函數變化變得柔和很多。

7. 這個證明和圖(6)中的證明方法和過程均一致,emm就是一樣的。不過這裏還是寫下

 本章到該部分爲止講了交叉熵和softmax的原理和一些公式原理的推導和證明。這裏注意兩個組合,sigmoid激活函數和交叉熵代價函數的組合,以及softmax和log-likelihood (對數似然)代價函數的組合,兩個組合都能有效解決學習速度下降的問題。關於二分類中交叉熵函數和log似然函數的總結,其他博客給出了比較好的解釋

注意到不管是交叉熵損失函數與 log 似然損失函數,交叉熵損失函數用於二分類問題, log 似然損失函數用於多分類,但是對於某一個樣本只屬於一個類別,只有一個標籤。如果用 one-hot 編碼樣本的標籤那麼,對於標籤向量只有一個分量的值爲 1 其餘的值都爲 0。

所以不管是交叉熵損失函數與 log 似然損失函數,都可以化簡爲:

$$C = -ln(a_j)

其中, a_j 是向量  y 中取值爲 1 對應的第 j 個分量的值。在這裏本質上是一樣的。作者建議採用 Kears 中的命名方法,對於二分類的交叉熵損失函數稱之爲 “二分類交叉熵損失函數(binary_crossentropy)” ,對於多分類的交叉熵損失函數稱之爲 “多類別交叉熵損失函數(categorical_crossentropy)”。
來源鏈接:https://juejin.im/post/5b38971be51d4558b10aad26

權重初始化

  1. 驗證z = sum(wx)+b標準差爲sqrt(3/2)。下面兩點可能會有幫助:(a)獨立隨機變量和的方差,是每個獨立隨機變量方差的和;(b)方差是標準差的平方。

1. 權重初始化目的是爲了讓每一層的輸出z(神經元前的那一項)和該層輸出 x 保持一致的數據分佈。權重初始化的方法是對權重的數據除以輸入權重神經元個數的根號,即通過w=np.random.randn(n)/sqrt(n)來初始化。這個題目的證明需要注意 w 和 x 和 b 三個變量相互獨立。過程見下圖

其他改進神經網絡方法的技術

該節主要介紹了momentum及人工神經元的其他模型。momentum的公式如下,該方法引入了一個稱爲速度(velocity)的概念,梯度的作用是改變速度,而不是直接改變位置,另外,momentum方法引入了一種摩擦力的項,用來逐漸減少速度。如書中所述,這裏需要注意變量之間的對應關係,速度變量 v = v1,v2,v3...其中每一個對應wj變量,我理解這裏的 v 和 w 是維度和大小(shape)是一樣的,即每個Wij都對應一個vij。

  1. 如果我們使⽤ µ > 1 會有什麼問題? • 如果我們使⽤ µ < 0 會有什麼問題?
  2. 增加基於 momentum 的隨機梯度下降到 network2.py 中。
  3. 證明公式 (111)

1. 這個µ類似於物理中的摩擦力,這裏用來約束v的變化,µ=1時,沒有摩擦,速度由▽C決定,速度每次都在疊加,梯度下降的速度將比正常的梯度下降速度快很多;µ=0時,存在很大的摩擦,速度無法疊加,這個時候就等同於通常所說的梯度下降。在實踐中,使用0和1之間的µ值可以爲我們避免過量而又能夠疊加速度的好處。

那麼 µ > 1或者 µ < 0有什麼問題呢,顯然,作爲摩擦力來理解的話,µ > 1就是憑空出現v的疊加,可以理解在完全光滑的平面上一個物體不受外力作用速度卻在增加,這就使得梯度下降幾乎不受控制一直增大,及時▽C反向也不能阻止w向一個方向更新,最後w越來越大,越來越大(或者越來越小)...效果在達到最優之後越過,從而越來越差。當µ < 0時,可以認爲摩擦力太大,大到摩擦力竟然比物體本身的重量還要大,相當於給物體一個額外的向下的壓力,此時,參數更新不是疊加地增加了,是疊加的減少,即梯度下降一開始朝一個方向,但momentum 會使得梯度下降比常規的梯度下降更慢,甚者,在▽C最小(約爲0),即達到最優的時候,權重開始反向改變,始終無法停留在最優處。

2. 根據公式感覺這個代碼還是比較好改的,真正改的時候對於變量的類型和維度還是要考慮一些地方。如下在 update_mini_batch函數的參數更新的地方做了修改。

    def update_mini_batch(self, mini_batch, eta, lmbda, n, momentum):
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

        # 原方法Stochastic Gradient Descent(Mini-Batch SGD)
        # self.weights = [(1-eta*(lmbda/n))*w-(eta/len(mini_batch))*nw
        #                 for w, nw in zip(self.weights, nabla_w)]
        # self.biases = [b-(eta/len(mini_batch))*nb
        #                for b, nb in zip(self.biases, nabla_b)]
        # Momentum-SGD 下降法
        vss = [momentum*vs - (eta/len(mini_batch))*nw
                        for vs, w, nw in zip(self.vs, self.weights, nabla_w)]
        self.weights = [w + vs
                        for vs, w, nw in zip(vss, self.weights, nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb
                       for b, nb in zip(self.biases, nabla_b)]

注意這裏多了 momentum以及self.vs,前者傳遞的是公式(107)的µ,後者存儲的是公式(107)的v。這裏的self.vs和self.weights存儲類型一致,其爲list類型,list的每個元素是一個矩陣,存儲的是每個權重wij的變化速率。而該速率初始化爲0,因而default_weight_initializer函數也加了self.vs的初始化。

    def default_weight_initializer(self):
        # 默認使用新的權重初始化方法進行初始化
        self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]
        self.weights = [np.random.randn(y, x)/np.sqrt(x)
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]
        self.vs = [np.zeros([y, x])
                        for x, y in zip(self.sizes[:-1], self.sizes[1:])]

之後記得在SGD函數和update_mini_batch函數的參數傳遞里加上momentum就行了。另外,我這樣的修改方法沒有考慮到正則化,如果需要還需要另行添加。

3. 公式證明比較簡單

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章