1. 程式人生 > >使用python實現深度神經網路 5

使用python實現深度神經網路 5

深度學習

一、實驗介紹

1.1 實驗內容

上次實驗我們使用淺層神經網路達到了0.9的驗證集準確率,本次實驗我們將增加神經網路的深度,進行真正的深度學習

1.2 實驗知識點

  • 梯度消失問題
  • 交叉熵損失函式

1.3 實驗環境

  • python 2.7
  • numpy 1.12.1
  • scipy 0.19.0

二、實驗步驟

2.1 增加網路的深度

要進行深度學習很簡單,向神經網路裡多增加幾個網路層就可以了,只需對上次實驗中的shallow.py稍加修改,我們就可以得到一個深度神經網路,修改後的檔案我們命令為deep.py

# encoding=utf-8
from layers import *


def
main():
datalayer1 = Data('train.npy', 1024) # 用於訓練,batch_size設定為1024 datalayer2 = Data('validate.npy', 10000) # 用於驗證,所以設定batch_size為10000,一次性計算所有的樣例 inner_layers = [] inner_layers.append(FullyConnect(17 * 17, 20)) inner_layers.append(Sigmoid()) inner_layers.append(FullyConnect(20, 26
)) # 增加一個隱層 inner_layers.append(Sigmoid()) losslayer = QuadraticLoss() accuracy = Accuracy() for layer in inner_layers: layer.lr = 1000.0 # 為所有中間層設定學習速率 epochs = 20 for i in range(epochs): print 'epochs:', i losssum = 0 iters = 0 while True
: data, pos = datalayer1.forward() # 從資料層取出資料 x, label = data for layer in inner_layers: # 前向計算 x = layer.forward(x) loss = losslayer.forward(x, label) # 呼叫損失層forward函式計算損失函式值 losssum += loss iters += 1 d = losslayer.backward() # 呼叫損失層backward函式曾計算將要反向傳播的梯度 for layer in inner_layers[::-1]: # 反向傳播 d = layer.backward(d) if pos == 0: # 一個epoch完成後進行準確率測試 data, _ = datalayer2.forward() x, label = data for layer in inner_layers: x = layer.forward(x) accu = accuracy.forward(x, label) # 呼叫準確率層forward()函式求出準確率 print 'loss:', losssum / iters print 'accuracy:', accu break if __name__ == '__main__': main()

我們只做了非常簡單的修改,將原本的一層FullyConnect層改變成了兩層,同時第一個FullyConnect層的輸出有20個節點,這與第二個FullyConnect層的輸入20個節點相匹配。這裡中間網路層的節點數20也是一個需要我們手工設定的超引數。

2.2 深度學習的困難--梯度消失問題

我們期待深度神經網路能帶來更好的效能,可是使用python deep.py執行上面的程式碼,你會發現我們的模型效能似乎不升反降了!上次實驗我們的模型第一個epoch結束後就能達到0.4左右的準確率,可這次訓練了好幾個epoch之後準確率都還不到0.1, 20個epoch結束後,大概只有0.8左右的準確率。  

不過其實如果你增大epochs(比如增大到100,但是這樣比較耗時間),你會發現其實到最後,深度神經網路的準確率還是超過了淺層神經網路,大概能到0.97。最終準確率的提升驗證了我們之前提到的深度神經網路具有更強的表達能力。  

可是為什麼一開始我們在驗證集上的準確率增長的那麼慢呢?難道說,我們的模型,“學習速率”變慢了?  

對,確實是學習速率變慢了,你可以增大超引數lr試試(比如增大到5000),準確率增長緩慢的問題會得到緩解,但和之前比起來還是慢了很多,為什麼會這樣呢?
要解釋這個問題,我們再看看第一次實驗中,梯度下降演算法裡給出的引數更新公式:

此處輸入圖片的描述

我們說超引數alpha(對應程式碼裡的lr)控制學習速率,但同時也要注意到此處輸入圖片的描述也會影響每次引數更新的“步子”大小。由於我們這裡的lr沒有變,那麼一定是此處輸入圖片的描述變小了。  

為什麼變小了呢?你可以檢查我們反向傳播梯度的程式碼,嘗試找出是哪個環節使得梯度變小。實際上,這裡的主要問題出在Sigmoid層。Sigmoid層是這樣反向傳遞梯度的:

    def backward(self, d):
        sig = self.sigmoid(self.x)
        self.dx = d * sig * (1 - sig)
        return self.dx  # 反向傳遞梯度

對於從之前的網路層反向傳遞來的梯度,Sigmoid層會乘上其對於輸入x的導數並再次反向傳遞。讓我們看看sigmoid函式的導函式是什麼樣的:

此處輸入圖片的描述

我們看到,當輸入為0時,sigmoid函式的導函式最大值為0.25,當輸入資料的絕對值增大時,導函式值迅速減小,馬上就會接近於0。  

這便是導致我們的深度神經網路學習的更慢的“元凶”,由於我們這裡多了一個Sigmoid層,梯度值經過Sigmoid層時,會減小很多,我們的梯度就像是“消失了”(vanishing gradient),導致我們的模型學習的非常慢。

實際上,梯度消失問題(或者說比這個問題更廣泛的“梯度不穩定問題”),是導致難以訓練深度神經網路的重要原因之一。  

2.3 拯救消失的梯度--交叉熵損失函式

解決梯度消失問題(vanishing gradient problem)的方法有很多,可以選擇使用別的啟用層替換掉Sigmoid層,但我們不打算介紹其他種類的啟用層。在這裡我們打算使用一種間接的,曲線救國的方式去解決梯度消失問題--使用交叉熵損失函式(cross-entropy loss)替換掉平方損失函式。

2.3.1 什麼是交叉熵

交叉熵這個概念來源於資訊理論,我們這裡不打算深究交叉熵的深層含義,如果有興趣你可以自己查閱相關資料。交叉熵損失函式公式如下:

此處輸入圖片的描述

公式的形式有些複雜,其中h(theta,x)代表我們的神經網路對輸入資料x對應輸出的預測,y代表輸入資料的預期正確輸出值,同時這裡的log是以自然數e為底的對數。我們可以舉幾個例子去感受這個損失函式的性質,當y=1而h=0時,損失函式值為正無窮,這是合理的,因為我們的模型此時的預測是完全錯誤的。當y=1且h=1時,損失函式值為0, 即模型的預測是正確時,損失函式值為0。你可以多列舉幾組資料測試一下,感受一下交叉熵損失函式的特性。

2.3.2 對交叉熵損失函式求導

損失函式層計算損失函式對於該層輸入的梯度,並反向傳遞回去,所以這裡我們要對交叉熵損失函式求導。

交叉熵公式比較複雜,你可以自己嘗試對它進行求導,求得的導函式為:

此處輸入圖片的描述

這個導函式似乎有些複雜,但其實有一個非常巧妙有趣的巧合在這裡,觀察導函式的分母,這裡的h(theta,x)就是從上一個Sigmoid層前向計算傳過來的輸出,而我們說Sigmoid層之所以會導致梯度消失,是因為它反向傳遞梯度的過程中將梯度值乘以了一個非常小的數:sigmoid(x)*(1-sigmoid(x)),而sigmoid(x)*(1-sigmoid(x))不就是h(thera,x)*(1-h(theta,x))嗎?所以損失函式層反向傳遞時先除以h(thera,x)*(1-h(theta,x)),而Sigmoid層再乘以sigmoid(x)*(1-sigmoid(x)),兩者剛好相互抵消,於是我們的梯度就不會變小!!

這真是一個美妙的巧合,我在這裡不得不感嘆數學實在太美妙了。

2.3.3 編寫交叉熵損失函式層

只要我們把之前的平方損失函式層替換成交叉熵損失函式層,應該就可以解決學習速率變低的問題。我們可以根據上面的公式編寫出交叉熵損失函式層:

class CrossEntropyLoss:
    def __init__(self):
        pass

    def forward(self, x, label):
        self.x = x
        self.label = np.zeros_like(x)
        for a, b in zip(self.label, label):
            a[b] = 1.0
        self.loss = np.nan_to_num(-self.label * np.log(x) - ((1 - self.label) * np.log(1 - x)))  # np.nan_to_num()避免log(0)得到負無窮的情況
        self.loss = np.sum(self.loss) / x.shape[0]
        return self.loss

    def backward(self):
        self.dx = (self.x - self.label) / self.x / (1 - self.x)  # 分母會與Sigmoid層中的對應部分抵消
        return self.dx

其實整體與平方損失函式差不多,注意由於log(0)得到的負無窮屬於nan,所以這裡呼叫np.nan_to_num()避免這種情況。

CrossEntropyLoss已經包含在了上次實驗的layers.py檔案中。  

2.3.4 再次嘗試深度神經網路

讓我們把平方損失函式替換成交叉熵損失函式,再試一試,建立deep2.py檔案,修改程式碼如下:

# encoding=utf-8
from layers import *


def main():
    datalayer1 = Data('train.npy', 1024)  # 用於訓練,batch_size設定為1024
    datalayer2 = Data('validate.npy', 10000)  # 用於驗證,所以設定batch_size為10000,一次性計算所有的樣例
    inner_layers = []
    inner_layers.append(FullyConnect(17 * 17, 20))
    inner_layers.append(Sigmoid())
    inner_layers.append(FullyConnect(20, 26))
    inner_layers.append(Sigmoid())
    losslayer = CrossEntropyLoss()
    accuracy = Accuracy()

    for layer in inner_layers:
        layer.lr = 1.0  # 為所有中間層設定學習速率

    epochs = 20
    for i in range(epochs):
        print 'epochs:', i
        losssum = 0
        iters = 0
        while True:
            data, pos = datalayer1.forward()  # 從資料層取出資料
            x, label = data
            for layer in inner_layers:  # 前向計算
                x = layer.forward(x)

            loss = losslayer.forward(x, label)  # 呼叫損失層forward函式計算損失函式值
            losssum += loss
            iters += 1
            d = losslayer.backward()  # 呼叫損失層backward函式曾計算將要反向傳播的梯度

            for layer in inner_layers[::-1]:  # 反向傳播
                d = layer.backward(d)

            if pos == 0:  # 一個epoch完成後進行準確率測試
                data, _ = datalayer2.forward()
                x, label = data
                for layer in inner_layers:
                    x = layer.forward(x)
                accu = accuracy.forward(x, label)  # 呼叫準確率層forward()函式求出準確率
                print 'loss:', losssum / iters
                print 'accuracy:', accu
                break


if __name__ == '__main__':
    main()

在上次實驗下載的程式碼包中已經包括了這段程式碼(在deep2.py中)。
這裡只做了兩處修改,一處是把QuadraticLoss替換成了CrossEntropyLoss,另一處是我們把學習速率調低到了1.0,因為這裡我們的梯度不會像原來那樣“消失”了,所以學習速率可以適當調低一點。

執行python deep2.py, 準確率在第一個epoch結束時就可以達到0.5左右,同時20個epoch結束後準確率可以達到0.98左右,實際上如果你多訓練幾個epoch,準確率可以超過0.99,就我自己測試而言,最高能到0.992,也就是說,10000張圖片裡面,只有80張圖片的分類出現了錯誤。如果說,最開始0.9的準確率已經可以在一些實際場合運用的話,那0.992的準確率已經差不多達到人類的分類水準了,因為實際上驗證圖片中會有一些字母對於人來說都很難區分(比如一些"I"和“J”)。
此處輸入圖片的描述

當我第一次做出這個結果的時候,我非常非常的興奮,深度學習的強大力量深深震撼了我。可以預見未來幾年,深度學習的運用將會改變很多產業的格局,很多原本需要人來完成的事可以由機器替代我們去完成。甚至也許在有生之年,我們能夠看到真正意義上的人工智慧的出現(雖然到時候的智慧演算法不一定是深度學習)。所以朋友們,抓住這個機會吧!

2.4 尾聲--更多的深度學習

本課程到這裡就基本結束了。回想一下所有的實驗,我們學習了深度學習領域的一些基本知識,編寫出了一些神經網路層,用這些層組成了一個淺層神經網路並進一步將其改進為深度神經網路(我們的程式碼只包含模型訓練和驗證過程,你可以新增程式碼,將訓練好的網路結構和引數儲存下來,用到實際的專案中,比如識別圖片驗證碼之類的)。

雖然我們最終達到了0.992的準確率,但實際上,我們的神經網路還是比較簡陋(too simple),深度學習領域有很多其它類別的網路(比如卷積神經網路CNN, 迴圈神經網路RNN)在處理特定問題時有更強大的能力。我們的系列後續課程會進行介紹。

三、實驗總結

作為最後一次實驗,我們實現了一個真正意義上的深度神經網路,你以後可以自豪的說,你是做過“深度學習”的人了。  

本次實驗,我們學習了:

  1. Sigmoid層的導數很小是梯度消失問題的重要原因
  2. 交叉熵損失函式配合sigmoid啟用函式可以避免梯度消失問題
  3. 損失函式如何選擇也可以被視為一個超引數
  4. 深度學習領域還有其它種類的網路結構

四、課後作業

  1. [選做]深度學習中有很多種類的啟用函式,請你查詢相關資料瞭解。
  2. [選做]深度學習中有很多種類的損失函式,請你查詢相關資料瞭解。
  3. [選做]請你新增將訓練好的網路引數儲存下來並運用於實際的圖片字母識別的程式碼。

相關推薦

使用python實現深度神經網路 5

深度學習 一、實驗介紹 1.1 實驗內容 上次實驗我們使用淺層神經網路達到了0.9的驗證集準確率,本次實驗我們將增加神經網路的深度,進行真正的深度學習。 1.2 實驗知識點 梯度消失問題交叉熵損失函式 1.3 實驗環境 python 2.7numpy 1.12.1

使用python實現深度神經網路 1

深度學習基本概念 一、實驗介紹 1.1 實驗內容 深度學習並沒有你想象的那麼難,本課程將會一邊講解深度學習中的基本理論,一邊通過動手使用python實現一個簡單的深度神經網路去驗證這些理論,讓你從原理上真正入門深度學習。 本次實驗將會帶大家學習深度學習中的一些最基本的概念,本次實驗很重要,理解這

使用python實現深度神經網路 3

快速計算梯度的魔法--反向傳播演算法一、實驗介紹1.1 實驗內容第一次實驗最後我們說了,我們已經學習了深度學習中的模型model(神經網路)、衡量模型效能的損失函式和使損失函式減小的學習演算法learn(梯度下降演算法),還了解了訓練資料data的一些概念。但是還沒有解決梯度下降演算法中如何求損失函式梯度的問

深度學習】Python實現簡單神經網路

Python簡單神經網路 環境介紹 定義神經網路的框架 初始化 建立網路節點和連結 簡單均勻分佈隨機初始權重 正態分佈初始權重 編寫查詢函式 階段性測試 編寫訓練函式

如何用70行程式碼實現深度神經網路演算法

現在所有人都在談深度學習,保持學習精神是需要的,架構師永遠都要對核心技術和關鍵演算法保持關注和敏感,必要時要動手寫一寫掌握下來,先不用關心什麼時候用到,用不用是政治問題,會不會寫是技術問題,就像軍人不關心打不打的問題,而要關心如何打贏的問題。 一、程式設計師如何學習

如何用Python深度神經網路識別影象?

來源:王樹義科學網部落格概要:只需要10幾行Python程式碼,你就能自己構建機器視覺模型,對圖

如何用Python深度神經網路發現即將流失的客戶?

想不想了解如何用Python快速搭建深度神經網路,完成資料分類任務?本文一步步為你展示這一過程,

如何利用Python深度神經網路鎖定即將流失的客戶?業績過十萬!

煩惱作為一名資料分析師,你來到這家跨國銀行工作已經半年了。今天上午,老闆把你叫到辦公室,面色凝重。你心裡直打鼓,以為自己捅了什麼簍子。幸好老闆的話讓你很快打消了顧慮。客戶主要分佈在法國、德國和西班牙。你手裡掌握的資訊,包括他們的年齡、性別、信用、辦卡資訊等。客戶是否已流失的資

Python深度神經網路識別影象

進化的作用,讓人類對影象的處理非常高效。這裡,我給你展示一張照片。如果我這樣問你:你能否分辨出圖片中哪個是貓,哪個是狗?你可能立即會覺得自己遭受到了莫大的侮辱。並且大聲質問我:你覺得我智商有問題嗎?!息怒。換一個問法:你能否把自己分辨貓狗圖片的方法,描述成嚴格的規則,教給計算

python實現LSTM神經網路模型

''' 用tensorflow實現遞迴迴圈網路(LSTM) ''' from __future__ import print_function import tensorflow as tf from tensorflow.contrib import r

如何用Python深度神經網路尋找近似圖片?

給你10萬張圖片,讓你從中找出與某張圖片最為近似的10張,你會怎麼做?不要輕言放棄,也不用一張張

java程式碼實現深度神經網路演算法

對於現在流行的深度學習,保持學習精神是必要的——程式設計師尤其是架構師永遠都要對核心技術和關鍵演算法保持關注和敏感,必要時要動手寫一寫掌握下來,先不用關心什麼時候用到——用不用是政治問題,會不會寫是技術問題,就像軍人不關心打不打的問題,而要關心如何打贏的問題。 程式設計師

機器學習實戰——python實現SOM神經網路聚類演算法

演算法基礎 SOM網路結構 輸入層:假設一個輸入樣本為X=[x1,x2,x3,…,xn],是一個n維向量,則輸入層神經元個數為n個。 輸出層(競爭層):通常輸出層的神經元以矩陣方式排列在二維空間中,每個神經元都有一個權值向量。 假設輸出層有m個神經元,則有m

C++從零實現深度神經網路之六——實戰手寫數字識別(sigmoid和tanh)

本文由@星沉閣冰不語出品,轉載請註明作者和出處。之前的五篇部落格講述的內容應該覆蓋瞭如何編寫神經網路的大部分內容,在經過之前的一系列努力之後,終於可以開始實戰了。試試寫出來的神經網路怎麼樣吧。一、資料準

python實現人工神經網路的一個例子

人工神經網路已經有無數的開源框架,比如tensorflow,caffe等,可以直接用。但最近需要做一個小樣例,把基本思想講一講,因

用純Python實現迴圈神經網路RNN向前傳播過程(吳恩達DeepLearning.ai作業)

Google TensorFlow程式設計師點讚的文章!   前言 目錄:     - 向量表示以及它的維度     - rnn cell     - rnn 向前傳播 &nbs

【電腦科學】【2016】【含部分原始碼】深度神經網路及其實現

本文為捷克布拉格查理大學(作者:Bc. Ján Vojt)的碩士論文,共104頁。 深度神經網路是一種有效且通用的模型,能夠完成各種各樣的任務。本文主要研究了三種不同型別的深度神經網路——多層感知器、卷積神經網路和深度置信網路。所有討論的網路模型都是在並行硬體上實現的,並且針對網路

Python基於K-均值、RLS演算法實現RBF神經網路神經網路與機器學習 第五章 計算機實驗)

1、生成資料集 class moon_data_class(object): def __init__(self,N,d,r,w): self.N=N self.w=w self.d=d self.r=r

深度學習基礎5深度神經網路的優化與調參(2)

     轉載請註明出處。謝謝。      本博文根據 coursera 吳恩達 Improving Deep Neural Networks: Hyperparameter tuning, Regularizati