神經網絡識別手寫優化(一)

手寫識別優化

前言

之前實現的神經網絡還有很多可以優化的地方。本文主要正對其進行優化。

初始化

在訓練神經網絡之前參數初始化。這裏的參數就是w,b。
w,b的初始化。
權重w初始化基於正態分佈模型(高斯分佈):
以0爲均值,1爲方差然後除以當前神經元的個數的算數平方根。
偏執b初始化基於高斯模型:
以0爲均值,1爲方差。
公式爲:

爲什麼?
首先觀察一下高斯分佈的函數圖像
此處輸入圖片的描述

符合高斯分佈的數據也就是說,數據的大部分點在中間“山峯”的區域。高斯分佈就是說兩端的很少,中間的很多。這跟人類社會的2 8法則一項。頂尖的人很少,智障也很少。大家都是中庸的。包括看客和我。

在這裏w和b 會對神經元的輸出產生影響。w稱爲權重,我們也可以理解爲神經元的重要性(因爲w越大輸出越大,w越小輸出越小)。
因爲我們在沒有訓練模型之前最好是對每個神經元都不“偏心”。那麼我們乾脆就認爲大家都差不多,差不多的那就是中庸的。那就符合高斯分佈好啦。有因爲權值不能過大,大了收斂很慢。而且難算,那麼幹脆除一個定值(當前層的節點個數)好了。

import random
import numpy as np
class Network(object):
    def __init__(self, sizes):
        """
        基本參數初始化
        :param sizes: 
        """
        # 網絡層數
        self.num_layers = len(sizes)
        # 網絡每層神經元個數
        self.sizes = sizes
        #調用權重初始化函數
        self.default_weight_initializer()

    def default_weight_initializer(self):
        """
        權重和偏置的初始化
        :return: 
        """
        # 初始化每層的偏置
        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:])]

正則化

正則化的目的就是爲了提高模型的泛化能力,一定基礎上防止過擬合。

L1正則化

在前面的博文中我們定義了LOSS函數,也就是COST函數。
損失函數
這個應該很熟悉了,就是每個神經元真實標籤值和預測值之間的gap。(這裏只是討論待定係數,還是忽略激勵函數的存在)。
我們姑且把這個損失函數成爲C0
加了正則項之後損失函數變形:
正則化後的損失函數
我們很容易看出損失函數其實就是多了後面一項。
image.png
其中的amda不是學習率是我們手動設置的一個參數。
還是老規矩,我們對這個COST求偏w
image.png
其中sgn(x)是一個 函數。也稱做符號函數sign函數。就是改變輸入的符號。正號變成負號,負號變成正號。
函數表達式爲:
image.png

函數圖像爲:
image.png

那我們的權重更新也發生了變化。
image.png
式子中的前面兩項已經很熟悉了,就是梯度下降。關鍵是後面加了一個東西。這個作用是什麼樣子的呢?

還記得一元凸函數的樣子
image.png

我們最終目的就是要讓cost滾到谷底。

第一種情況:
當w是個負數的時候,並且COST在山谷的左邊的時候!w應該往右邊滾動。
由梯度下降的知識可以知道前面兩項就是可以讓其向右邊挪動。
因爲第二項偏導數會變成一個負數,正數減去一個負數自然會增大!!!
我們看後面那一項:
因爲w是一個負數,sgn(w)變成了-1。
那麼這裏就是減去一個負數。相當於w往下滾的越快了。

這是怎麼回事,那麼會不會滾來滾去導致無法收斂呢?
別急當w在這個點的時候。
image.png
w也會向右邊滾動(同理),但是我們現在sgn(x)就是一個正數了。那麼現在往右邊挪就不那麼順暢了,這個第三項那個正數會狠狠的拉它一把。讓它滾向右邊的步伐縮小。

還不明白?看第二種情況!

第二種情況:
當w在山谷的右邊的時候,應該往左邊滾動。
由梯度下降的知識就知道,因爲偏導數是個正數自然會向左邊挪動。這裏的sgn(x)也是個正數。
那麼w會加速往左邊滾。

總結一下這個sgn(x)就是爲了讓w爲負數的時候迅速增大,w爲正數的時候迅速見效。讓w收斂的同時儘可能的等於0。

以前梯度下降很單純往谷底走就好了

現在 谷底和原點就兩個磁鐵一樣吸引着我們的w
image.png

w會找到一個比較合適的中間值停下來,從而在一定程度上面避免過擬合!!!

以上是一元凸函數而且只有一個參數,我們上升一下維度(盜個圖)
image.png
其中彩色的波浪叫做損失等高線(藉助地理的概念)。就是說在w在一個圈上面產生的cost是一樣的。那麼我們的w應該儘量靠近紫色的圈圈。這個時候損失會最小。
但是這裏加了一個正方形也就是w的絕對值圍城的正放形。同樣的它想要w儘可能的靠近它的圓心。於是就出現了上述的情況。w爲了滿足這兩個條件就在黑點的地方停下來了。正則項成功的限制住了w。讓其一定程度上避免過擬合的發生。

L2正則

與L1正則是一樣的。也是爲了避免過擬合。
看公式
image.png
後面不再是絕對值。而是w的平方。前面的2n純屬爲了求導方便。
我的理解就是
L2正則就是爲了方便計算
其餘的跟L1正則沒有任何區別!!!
那就看看計算
1、求偏導
image.png
2、設置更新函數
image.png

是不是基本一毛一樣!大家可以根據L1正則和梯度下降的法則分析一下過程。其實L2正則也是限制W的。(又偷個圖)
image.png
這一次再是個圓。其餘一樣的!!

代碼部分

說了這麼其實多代碼其實超級簡單,在我們之前實現的代碼基礎上加一行即可,就是是w的更新公式改改!!!

class Network(object):
    def __init__(self, sizes):
        # 網絡層數
        self.num_layers = len(sizes)
        # 網絡每層神經元個數
        self.sizes = sizes

        self.default_weight_initializer()

    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:])]

    # 隨機梯度下降
    def SGD(self, training_data, epochs, mini_batch_size, eta,lmbda=0.0,
            test_data=None):
        if test_data: n_test = len(test_data)
        # 訓練數據總個數
        n = len(training_data)

        # 開始訓練 循環每一個epochs
        for j in xrange(epochs):
            # 洗牌 打亂訓練數據
            random.shuffle(training_data)

            # mini_batch
            mini_batches = [training_data[k:k + mini_batch_size]
                            for k in range(0, n, mini_batch_size)]


            # 訓練mini_batch
            for mini_batch in mini_batches:
                self.update_mini_batch(mini_batch, eta,lmbda,n)

            if test_data:
                print "Epoch {0}: {1} / {2}".format(
                    j, self.evaluate(test_data), n_test)
            print "Epoch {0} complete".format(j)

    # 更新mini_batch
    def update_mini_batch(self, mini_batch, eta,lmbda,n):
        # 保存每層偏倒
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]

        # 訓練每一個mini_batch
        for x, y in mini_batch:
            delta_nable_b, delta_nabla_w = self.update(x, y)

            # 保存一次訓練網絡中每層的偏倒
            nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nable_b)]
            nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]

        # 更新權重和偏置 Wn+1 = wn - eta * nw 我們只改了這裏
        self.weights = [w -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)]


    # 前向傳播
    def update(self, x, y):
        # 保存每層偏倒
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]

        activation = x

        # 保存每一層的激勵值a=sigmoid(z)
        activations = [x]

        # 保存每一層的z=wx+b
        zs = []
        # 前向傳播
        for b, w in zip(self.biases, self.weights):
            # 計算每層的z
            z = np.dot(w, activation) + b

            # 保存每層的z
            zs.append(z)

            # 計算每層的a
            activation = sigmoid(z)

            # 保存每一層的a
            activations.append(activation)

        # 反向更新了
        # 計算最後一層的誤差
        delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])

        # 最後一層權重和偏置的倒數
        nabla_b[-1] = delta
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())

        # 倒數第二層一直到第一層 權重和偏置的倒數
        for l in range(2, self.num_layers):
            z = zs[-l]

            sp = sigmoid_prime(z)

            # 當前層的誤差
            delta = np.dot(self.weights[-l+1].transpose(), delta) * sp

            # 當前層偏置和權重的倒數
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())

        return (nabla_b, nabla_w)

注意!這代碼是不能允許的啊。只是結合之前的做一下優化,看一下修改的地方即可!

發佈了47 篇原創文章 · 獲贊 80 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章