成人av在线资源一区,亚洲av日韩av一区,欧美丰满熟妇乱XXXXX图片,狠狠做五月深爱婷婷伊人,桔子av一区二区三区,四虎国产精品永久在线网址,国产尤物精品人妻在线,中文字幕av一区二区三区欲色
    您正在使用IE低版瀏覽器,為了您的雷峰網賬號安全和更好的產品體驗,強烈建議使用更快更安全的瀏覽器
    此為臨時鏈接,僅用于文章預覽,將在時失效
    人工智能開發者 正文
    發私信給AI研習社
    發送

    1

    一文詳解神經網絡 BP 算法原理及 Python 實現

    本文作者: AI研習社 2017-05-26 10:02
    導語:關于反向傳播。

    雷鋒網按:本文作者曾梓華,原文載于作者個人博客,雷鋒網已獲授權。

    最近這段時間系統性的學習了 BP 算法后寫下了這篇學習筆記,因為能力有限,若有明顯錯誤,還請指正。

      什么是梯度下降和鏈式求導法則

    假設我們有一個函數 J(w),如下圖所示。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    梯度下降示意圖

    現在,我們要求當 w 等于什么的時候,J(w) 能夠取到最小值。從圖中我們知道最小值在初始位置的左邊,也就意味著如果想要使 J(w) 最小,w的值需要減小。而初始位置的切線的斜率a > 0(也即該位置對應的導數大于0),w = w – a 就能夠讓 w 的值減小,循環求導更新w直到 J(w) 取得最小值。如果函數J(w)包含多個變量,那么就要分別對不同變量求偏導來更新不同變量的值。

    所謂的鏈式求導法則,就是求復合函數的導數:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    鏈式求導法則

    放個例題,會更加明白一點:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    鏈式求導的例子

      神經網絡的結構

    神經網絡由三部分組成,分別是最左邊的輸入層,隱藏層(實際應用中遠遠不止一層)和最右邊的輸出層。層與層之間用線連接在一起,每條連接線都有一個對應的權重值 w,除了輸入層,一般來說每個神經元還有對應的偏置 b。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    神經網絡的結構圖

    除了輸入層的神經元,每個神經元都會有加權求和得到的輸入值 z 和將 z 通過 Sigmoid 函數(也即是激活函數)非線性轉化后的輸出值 a,他們之間的計算公式如下

    一文詳解神經網絡 BP 算法原理及 Python 實現

    神經元輸出值 a 的計算公式

    其中,公式里面的變量l和j表示的是第 l 層的第 j 個神經元,ij 則表示從第 i 個神經元到第 j 個神經元之間的連線,w 表示的是權重,b 表示的是偏置,后面這些符號的含義大體上與這里描述的相似,所以不會再說明。下面的 Gif 動圖可以更加清楚每個神經元輸入輸出值的計算方式(注意,這里的動圖并沒有加上偏置,但使用中都會加上)

    一文詳解神經網絡 BP 算法原理及 Python 實現

    動圖顯示計算神經元輸出值

    使用激活函數的原因是因為線性模型(無法處理線性不可分的情況)的表達能力不夠,所以這里通常需要加入 Sigmoid 函數來加入非線性因素得到神經元的輸出值。

    關于為什么線性函數模型表達能力不夠,可以點擊這里查看知乎上面的討論。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    sigmoid 函數

    可以看到 Sigmoid 函數的值域為 (0,1) ,若對于多分類任務,輸出層的每個神經元可以表示是該分類的概率。當然還存在其他的激活函數,他們的用途和優缺點也都各異。

      BP 算法執行的流程(前向傳遞和逆向更新)

    在手工設定了神經網絡的層數,每層的神經元的個數,學習率 η(下面會提到)后,BP 算法會先隨機初始化每條連接線權重和偏置,然后對于訓練集中的每個輸入 x 和輸出 y,BP 算法都會先執行前向傳輸得到預測值,然后根據真實值與預測值之間的誤差執行逆向反饋更新神經網絡中每條連接線的權重和每層的偏好。在沒有到達停止條件的情況下重復上述過程。

    其中,停止條件可以是下面這三條

    ● 權重的更新低于某個閾值的時候

    ● 預測的錯誤率低于某個閾值

    ● 達到預設一定的迭代次數

    譬如說,手寫數字識別中,一張手寫數字1的圖片儲存了28*28 = 784個像素點,每個像素點儲存著灰度值(值域為[0,255]),那么就意味著有784個神經元作為輸入層,而輸出層有10個神經元代表數字0~9,每個神經元取值為0~1,代表著這張圖片是這個數字的概率。

    每輸入一張圖片(也就是實例),神經網絡會執行前向傳輸一層一層的計算到輸出層神經元的值,根據哪個輸出神經元的值最大來預測輸入圖片所代表的手寫數字。

    然后根據輸出神經元的值,計算出預測值與真實值之間的誤差,再逆向反饋更新神經網絡中每條連接線的權重和每個神經元的偏好。

    前向傳輸(Feed-Forward)

    從輸入層=>隱藏層=>輸出層,一層一層的計算所有神經元輸出值的過程。

    逆向反饋(Back Propagation)

    因為輸出層的值與真實的值會存在誤差,我們可以用均方誤差來衡量預測值和真實值之間的誤差。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    均方誤差

    逆向反饋的目標就是讓E函數的值盡可能的小,而每個神經元的輸出值是由該點的連接線對應的權重值和該層對應的偏好所決定的,因此,要讓誤差函數達到最小,我們就要調整w和b值, 使得誤差函數的值最小。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    權重和偏置的更新公式

    對目標函數 E 求 w 和 b 的偏導可以得到 w 和 b 的更新量,下面拿求 w 偏導來做推導。

    一文詳解神經網絡 BP 算法原理及 Python 實現

    其中 η 為學習率,取值通常為 0.1 ~ 0.3,可以理解為每次梯度所邁的步伐。注意到 w_hj 的值先影響到第 j 個輸出層神經元的輸入值a,再影響到輸出值y,根據鏈式求導法則有:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    使用鏈式法則展開對權重求偏導

    根據神經元輸出值 a 的定義有:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    對函數 z 求 w 的偏導

    Sigmoid 求導數的式子如下,從式子中可以發現其在計算機中實現也是非常的方便:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    Sigmoid 函數求導

    所以

    一文詳解神經網絡 BP 算法原理及 Python 實現

    則權重 w 的更新量為:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    類似可得 b 的更新量為:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    但這兩個公式只能夠更新輸出層與前一層連接線的權重和輸出層的偏置,原因是因為 δ 值依賴了真實值y這個變量,但是我們只知道輸出層的真實值而不知道每層隱藏層的真實值,導致無法計算每層隱藏層的 δ 值,所以我們希望能夠利用 l+1 層的 δ 值來計算 l 層的 δ 值,而恰恰通過一些列數學轉換后可以做到,這也就是逆向反饋名字的由來,公式如下:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    從式子中我們可以看到,我們只需要知道下一層的權重和神經元輸出層的值就可以計算出上一層的 δ 值,我們只要通過不斷的利用上面這個式子就可以更新隱藏層的全部權重和偏置了。

    在推導之前請先觀察下面這張圖:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    l 和 l+1 層的神經元

    首先我們看到 l 層的第 i 個神經元與 l+1 層的所有神經元都有連接,那么我們可以將 δ 展開成如下的式子:

    一文詳解神經網絡 BP 算法原理及 Python 實現

    也即是說我們可以將 E 看做是 l+1 層所有神經元輸入值的 z 函數,而上面式子的 n 表示的是 l+1 層神經元的數量,再進行化簡后就可以得到上面所說的式子。

    在這里的推導過程只解釋了關鍵的部分,如果要查看更加詳細的推導內容,可以點擊此處下載我在學習過程中參考的一篇 pdf 文檔,里面的推導過程非常詳細。另外也參考了周志華所寫的機器學習中的神經網絡部分的內容和 neural networks and deep learning 的內容。

      Python 源碼解析

    源碼來自于 Michael Nielsen 大神的深度學習在線教程,但他的內容都是英文的,我結合了自己的理解和上面的理論知識對源碼進行了注釋。>>點擊此處查看整理的代碼和數字識別實例<<

    使用 Python 實現的神經網絡的代碼行數并不多,僅包含一個 Network 類,首先來看看該類的構造方法。

    def __init__(self, sizes):

            """

            :param sizes: list類型,儲存每層神經網絡的神經元數目

                          譬如說:sizes = [2, 3, 2] 表示輸入層有兩個神經元、

                          隱藏層有3個神經元以及輸出層有2個神經元

            """

            # 有幾層神經網絡 

            self.num_layers = len(sizes)

            self.sizes = sizes

            # 除去輸入層,隨機產生每層中 y 個神經元的 biase 值(0 - 1)

            self.biases = [np.random.randn(y, 1) for y in sizes[1:]]

            # 隨機產生每條連接線的 weight 值(0 - 1)

            self.weights = [np.random.randn(y, x)

                            for x, y in zip(sizes[:-1], sizes[1:])]

    向前傳輸(FreedForward)的代碼。

        def feedforward(self, a):

            """

            前向傳輸計算每個神經元的值

            :param a: 輸入值

            :return: 計算后每個神經元的值

            """

            for b, w in zip(self.biases, self.weights):

                # 加權求和以及加上 biase

                a = sigmoid(np.dot(w, a)+b)

            return a

    源碼里使用的是隨機梯度下降(Stochastic Gradient Descent,簡稱 SGD),原理與梯度下降相似,不同的是隨機梯度下降算法每次迭代只取數據集中一部分的樣本來更新 w 和 b 的值,速度比梯度下降快,但是,它不一定會收斂到局部極小值,可能會在局部極小值附近徘徊。

        def SGD(self, training_data, epochs, mini_batch_size, eta,

                test_data=None):

            """

            隨機梯度下降

            :param training_data: 輸入的訓練集

            :param epochs: 迭代次數

            :param mini_batch_size: 小樣本數量

            :param eta: 學習率 

            :param test_data: 測試數據集

            """

            if test_data: n_test = len(test_data)

            n = len(training_data)

            for j in xrange(epochs):

                # 攪亂訓練集,讓其排序順序發生變化

                random.shuffle(training_data)

                # 按照小樣本數量劃分訓練集

                mini_batches = [

                    training_data[k:k+mini_batch_size]

                    for k in xrange(0, n, mini_batch_size)]

                for mini_batch in mini_batches:

                    # 根據每個小樣本來更新 w 和 b,代碼在下一段

                    self.update_mini_batch(mini_batch, eta)

                # 輸出測試每輪結束后,神經網絡的準確度

                if test_data:

                    print "Epoch {0}: {1} / {2}".format(

                        j, self.evaluate(test_data), n_test)

                else:

                    print "Epoch {0} complete".format(j)

    根據 backprop 方法得到的偏導數更新 w 和 b 的值。

        def update_mini_batch(self, mini_batch, eta):

            """

            更新 w 和 b 的值

            :param mini_batch: 一部分的樣本

            :param eta: 學習率

            """

            # 根據 biases 和 weights 的行列數創建對應的全部元素值為 0 的空矩陣

            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:

                # 根據樣本中的每一個輸入 x 的其輸出 y,計算 w 和 b 的偏導數

                delta_nabla_b, delta_nabla_w = self.backprop(x, y)

                # 累加儲存偏導值 delta_nabla_b 和 delta_nabla_w 

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

            # 更新根據累加的偏導值更新 w 和 b,這里因為用了小樣本,

            # 所以 eta 要除于小樣本的長度

            self.weights = [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)]

    下面這塊代碼是源碼最核心的部分,也即 BP 算法的實現,包含了前向傳輸和逆向反饋,前向傳輸在 Network 里有單獨一個方法(上面提到的 feedforward 方法),那個方法是用于驗證訓練好的神經網絡的精確度的,在下面有提到該方法。

        def backprop(self, x, y):

            """

            :param x:

            :param y:

            :return:

            """

            nabla_b = [np.zeros(b.shape) for b in self.biases]

            nabla_w = [np.zeros(w.shape) for w in self.weights]

            # 前向傳輸

            activation = x

            # 儲存每層的神經元的值的矩陣,下面循環會 append 每層的神經元的值

            activations = [x] 

            # 儲存每個未經過 sigmoid 計算的神經元的值

            zs = [] 

            for b, w in zip(self.biases, self.weights):

                z = np.dot(w, activation)+b

                zs.append(z)

                activation = sigmoid(z)

                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 xrange(2, self.num_layers):

                # 從倒數第 **l** 層開始更新,**-l** 是 python 中特有的語法表示從倒數第 l 層開始計算

                # 下面這里利用 **l+1** 層的 δ 值來計算 **l** 的 δ 值

                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)

    接下來則是 evaluate 的實現,調用 feedforward 方法計算訓練好的神經網絡的輸出層神經元值(也即預測值),然后比對正確值和預測值得到精確率。

        def evaluate(self, test_data):

            # 獲得預測結果

            test_results = [(np.argmax(self.feedforward(x)), y)

                            for (x, y) in test_data]

            # 返回正確識別的個數

            return sum(int(x == y) for (x, y) in test_results)

    最后,我們可以利用這個源碼來訓練一個手寫數字識別的神經網絡,并輸出評估的結果,代碼如下:

    import mnist_loader

    import network


    training_data, validation_data, test_data = mnist_loader.load_data_wrapper()

    net = network.Network([784, 30, 10])

    net.SGD(training_data, 30, 10, 3.0, test_data = test_data)

    # 輸出結果

    # Epoch 0: 9038 / 10000

    # Epoch 1: 9178 / 10000

    # Epoch 2: 9231 / 10000

    # ...

    # Epoch 27: 9483 / 10000

    # Epoch 28: 9485 / 10000

    # Epoch 29: 9477 / 10000

    可以看到,在經過 30 輪的迭代后,識別手寫神經網絡的精確度在 95% 左右,當然,設置不同的迭代次數,學習率以取樣數對精度都會有影響,如何調參也是一門技術活,這個坑就后期再填吧。

      總結

    神經網絡的優點:

    網絡實質上實現了一個從輸入到輸出的映射功能,而數學理論已證明它具有實現任何復雜非線性映射的功能。這使得它特別適合于求解內部機制復雜的問題。

    網絡能通過學習帶正確答案的實例集自動提取“合理的”求解規則,即具有自學習能力。

    網絡具有一定的推廣、概括能力。

    神經網絡的缺點:

    對初始權重非常敏感,極易收斂于局部極小。

    容易 Over Fitting 和 Over Training。

    如何選擇隱藏層數和神經元個數沒有一個科學的指導流程,有時候感覺就是靠猜。

    應用領域:

    常見的有圖像分類,自動駕駛,自然語言處理等。

      TODO

    但其實想要訓練好一個神經網絡還面臨著很多的坑(譬如下面四條):

    1. 如何選擇超參數的值,譬如說神經網絡的層數和每層的神經元數量以及學習率;

    2. 既然對初始化權重敏感,那該如何避免和修正;

    3. Sigmoid 激活函數在深度神經網絡中會面臨梯度消失問題該如何解決;

    4. 避免 Overfitting 的 L1 和 L2正則化是什么。

      參考

    [1] 周志華 機器學習

    [2] 斯坦福大學機器學習在線課程

    [3] Parallel Distributed Processing (1986, by David E. Rumelhart, James L. McClelland), Chapter 8 Learning Internal Representations by Error Propagation

    [4] How the backpropagation algorithm works

    [5] Backpropagation Algorithm

    [6] 鏈式求導法則,臺灣中華科技大學數位課程,Youtube 視頻,需要翻墻,順便安利一下他們的數學相關的視頻,因為做的都非常淺顯易懂

    雷鋒網(公眾號:雷鋒網)相關閱讀:

    反向傳播算法最全解讀,機器學習進階必看!

    淺談神經網絡訓練方法,Deepmind更青睞正向傳播


    深度學習之神經網絡特訓班

    20年清華大學神經網絡授課導師鄧志東教授,帶你系統學習人工智能之神經網絡理論及應用!

    課程鏈接:http://www.mooc.ai/course/65

    加入AI慕課學院人工智能學習交流QQ群:624413030,與AI同行一起交流成長


    雷峰網版權文章,未經授權禁止轉載。詳情見轉載須知

    一文詳解神經網絡 BP 算法原理及 Python 實現

    分享:
    相關文章

    編輯

    聚焦數據科學,連接 AI 開發者。更多精彩內容,請訪問:yanxishe.com
    當月熱門文章
    最新文章
    請填寫申請人資料
    姓名
    電話
    郵箱
    微信號
    作品鏈接
    個人簡介
    為了您的賬戶安全,請驗證郵箱
    您的郵箱還未驗證,完成可獲20積分喲!
    請驗證您的郵箱
    立即驗證
    完善賬號信息
    您的賬號已經綁定,現在您可以設置密碼以方便用郵箱登錄
    立即設置 以后再說