【機器學習基礎】神經網絡/深度學習基礎


神經網絡是深度學習的基礎,上節提到由LR能夠聯系到神經網絡,本節就對神經網絡和BP算法進行一個回顧和總結。


1.由LR到神經網絡

  前面在邏輯回歸的文章末尾提到,當樣本是線性不可分時,需要對樣本數據進行轉換,轉換過后在進行分類,那么轉換的這個步驟就成為特征的提取的過程,結構如圖所示:

  如上圖所示,圖中的結構每進行一次轉換的結構,就稱為一個神經元,還可以有如下這樣的結構:

  同樣,一個紅色的框起來的部分稱之為神經元,神經元不同的連接方式,會產生不同的模型,模型的參數都包含在神經元的內部。

  值得一提的是,在前面LR中說,當數據線性不可分時,需要我們自己去找特征轉換的方程,使得樣本變成線性可分的,然后再使用LR進行分類;

  然而在神經網絡中,不需要我們去找轉換的方程,參數包含在網絡中,一起進行訓練,但這時需要我們自己去設計網絡的結構,來找出合適的模型(參數),從而得到好的結果。

2.全連接神經網絡

網絡/模型結構

  按照機器學習的三步走理論,首先我們需要確定模型,就是模型長什么樣子,這里介紹一種全連接神經網絡。

  上面說到神經網絡神經元之間的連接方式,決定了神經網絡各種各樣的模型和結構,下面說一種最常見的神經網絡結構——全連接神經網絡:

  顧名思義,全連接神經網絡就是每個神經元都相互連接,首先通過一個例子看一下一個結構的傳播過程:

  左邊為輸入(1,-1),每個箭頭指向為權重參數w,綠色的方框為偏差b,首先進行線性相加,然后經過sigmoid方程,得到輸出后,將該輸出作為下一次傳播的輸入,繼續向前:

  那么上面的這個過程,利用向量的形式表示為:

  將第一節的圖中方框的神經元“neuron”用“○”代替,那么全連接神經網絡的結構如下:

  上面就是一個較為完整全連接神經網絡結構,最左側為輸入,稱之為輸入層,最右側為輸出,稱之為輸出層,在輸入出與輸出層之間的結構稱之為隱藏層;

  值得注意的是,在神經網絡中,左側的靠近輸入層的稱之為“后”,右側靠近輸出層的結構稱之為“前”。因此上面那個例子的傳播方式也稱之為前向傳播

  那么上面的示例的前向傳播過程,我們用向量的形式來表示,這里只看第一層的過程:

  第一層中的四個權重,利用向量的形式表示為:

   然后再經過sigmoid函數:

  可以看到每一個神經元其實就是一個LR單元,總的來說,神經網絡前向傳播的向量形式即為:

  每一層的輸出即是下一層的輸入,直到最后的輸出層。上面就是神經網絡正向傳播的過程。

  在多分類問題中,通常最后一層的輸出層為用於多分類的Softmax函數。

3.模型的訓練以及BP算法

  網絡的結構需要我們初始給定,即網絡的層數、每個網絡所含有的神經元個數,確定了網絡模型的結構也就確定了參數的數量,那么接下來就是找出最好的一組參數,也就是模型的訓練。

  根據在LR中我們計算損失的方式,在神經網絡中,同樣我們期望真實值與預測值越接近越好,因此在此同樣采用交叉熵作為損失函數,不同的是,在LR中交叉熵的推導來自於最大似然估計的推導,而這里直接使用交叉熵公式,期望樣本的真實分布與預測的分布越接近越好,即:

                 

  這里假設多分類的類別為10類,那么需要計算每一個維度之間的交叉熵,然后加和得到一個樣本的交叉熵,對於多個樣本,將所有樣本再次相加即為交叉熵損失函數:

  然后就是利用梯度下降進行求解,其梯度為:

  這種前向傳播的梯度下降訓練方式跟之前的一致,但是,當網絡過於復雜時,參數的數量也過於龐大,這樣可能目標損失函數過於復雜,直接求導難度較大,因此為更有效地計算梯度,通常采用BP反向傳播算法。

BP算法原理

  神經網絡的損失函數為L(θ),那么損失函數對參數的導數為:

  首先拿出一個神經元來看:

   根據鏈式求導法則:

   這里可以看到,導數的第一部分,即z對w的導數即為w所對應的輸入x,比如下面這個例子:

  接下來看鏈式求導所得導數的后半部分,假設這一個神經元的輸出為a,那么進一步利用鏈式求導法則:

  導數的前半部分導數即為sigmoid函數的導數σ'(z),然后就是后半部分,a為該層的輸出,同時也是下一層網絡的輸入,與l有關,那么繼續到下一層:

   a作為后面網絡的輸入,影響到下一層網絡的每一個輸出,假設下一層有兩個神經元,那么a經過線性加權,分別得到z'和z'',那么根據鏈式求導法則:

  兩個部分,每一部分的前半部分為輸入a對應的連接的權重w,即:

.

  那么有:

  那么回到第一步的l對z的求導結果:

  如果到這一步就到達了輸出層,那么這里就可以知道l分別對z'和z''的導數了,因為:

  然后就可以求得l對w的導數了。

  如果這一步沒有到達輸出層,那么就繼續進入下一層:

  繼續重復上述步驟即可,直到到達輸出層。

  那么從上面的過程來看,我們在計算l對z的導數,需要一步一步遞歸地向后計算,直到傳播到輸出層,然后求輸出層y對前一層的導數,再一步一步向后(輸入層)傳,最終得到l對w的導數,即梯度,就可以利用梯度下降進行迭代了。

  因此上面的過程就是一個反向傳播的過程,如圖所示:

  根據上面的過程,在前向傳播中求鏈式求導結果中,每一項的第一部分,這一部分較容易直接求導得出的,反向傳播求得每一項的第二部分,這一部分需要不斷地遞歸求得,如圖所示:

 4.利用Keras實現深度學習

  下面就通過一個實例,來實現神經網絡(深度學習),並說明每一步的作用。

  首先數據集來源與MNIST的手寫數字辨識數據集,數據是手寫的0~9的圖片數據,首先導入所需要的的庫,並從sklearn讀取數據集並對數據作處理:

from sklearn.datasets import fetch_openml
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
import numpy as np
from keras import Sequential
from keras.layers import Dense
from keras.layers import Activation
import matplotlib.pyplot as plt
import matplotlib as mpl


data_x, data_y = fetch_openml('mnist_784', version=1, return_X_y=True)
# 將大於0的置為1,只要0和1的圖片數據
data_x[data_x > 0] = 1

data_x = np.mat(data_x)

one_hot = OneHotEncoder()
data_y = one_hot.fit_transform(np.array(data_y).reshape(data_y.shape[0], 1)).toarray()

train_x, test_x, train_y, test_y = train_test_split(data_x, data_y)

  先來看一下數據長什么樣子:

data_x[:10]
####
matrix([[0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        ...,
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.],
        [0., 0., 0., ..., 0., 0., 0.]])

data_y[:10]
####
array([[0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
       [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]])

  X是28*28共784維的稀疏矩陣,Y經過獨熱編碼后每一個數據是一個10維的數據,我們畫一下任意一張來看一下:

def plot_digit(data):
    image = data.reshape(28, 28)
    plt.imshow(image, cmap=mpl.cm.binary, interpolation='nearest')
    plt.axis("off")


one_digit = data_x[10000]
plot_digit(one_digit)

  數據准備好后,就到了建模的階段,利用keras神經網絡框架建模:

  首先是網絡結構,需要我們自己定一個網絡結構,包括網絡層數、每個層數的神經元個數,這里輸入為28*28維,因此輸入層為784維,輸出為10維,輸出層結構10。中間層我們暫定為500,那么網絡結構如圖所示:

  然后就是利用keras對上面網絡進行建模的過程:

  這樣模型就建好了,接下來就是對模型進行編譯,這里與之前的不太一樣,之前直接定義好模型和參數就可以fit了:

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

  可選的optimizer就是之前的梯度下降那一節介紹的參數優化方法,詳見:https://www.cnblogs.com/501731wyb/p/15322391.html

  可選的loss也有很多,可見官方文檔:https://keras.io/zh/losses/

  接下來利用數據進行訓練了:

model.fit(train_x, train_y, batch_size=300, epochs=20)

  這里batch_size就是訓練中采用一批數據進行訓練,選完一批繼續下一批,直到所有數據完成一次,成為1個epoch。

  然后查看訓練結果,以及在測試集上的表現:

score = model.evaluate(test_x, test_y)
print('total loss on testing data', score[0])
print('accuracy on testing data', score[1])


32/17500 [..............................] - ETA: 17s
 1120/17500 [>.............................] - ETA: 1s 
 2272/17500 [==>...........................] - ETA: 0s
 3648/17500 [=====>........................] - ETA: 0s
 5216/17500 [=======>......................] - ETA: 0s
 6720/17500 [==========>...................] - ETA: 0s
 8224/17500 [=============>................] - ETA: 0s
 9888/17500 [===============>..............] - ETA: 0s
11648/17500 [==================>...........] - ETA: 0s
13376/17500 [=====================>........] - ETA: 0s
15072/17500 [========================>.....] - ETA: 0s
16864/17500 [===========================>..] - ETA: 0s
17500/17500 [==============================] - 1s 35us/step
total loss on testing data 0.116816398623446
accuracy on testing data 0.9730285714285715

  可以看到,在訓練集上有99.53左右的精確度,在測試集上有97.3%的准確率,測試數據共有17500張圖片,其中錯誤分類的又472張,我們找出這472張:

error_idx = []
for i in range(len(test_x)):
    predict_array = model.predict(test_x[i])
    true_array = test_y[i]
    predict_result = np.argmax(predict_array)
    true_idx = np.argwhere(true_array == 1)[0][0]
    if true_idx != predict_result:
        error_idx.append(i)

  然后看一下這些分錯的數據,先寫一個批量畫圖的函數:

def plot_digits(instances, image_per_row=10, **options):
    size = 28
    image_per_row = min(len(instances), image_per_row)
    images = [instance.reshape(28, 28) for instance in instances]

    n_rows = (len(instances) - 1)//image_per_row + 1
    row_images = []
    n_empty = n_rows * image_per_row - len(instances)
    images.append(np.zeros((size, size * n_empty)))
    for row in range(n_rows):
        rimages = images[row*image_per_row:(row+1)*image_per_row]
        row_images.append(np.concatenate(rimages, axis=1))

    image = np.concatenate(row_images, axis=0)
    plt.imshow(image, cmap=mpl.cm.binary, **options)
    plt.axis("off")
    plt.figure(figsize=(9, 9))

  然后畫出來一部分數據,看下為啥會分錯:

example_images = []
for idx in error_idx[:30]:
    example_images.append(test_x[idx])

plot_digits(example_images, image_per_row=10)

  從這些圖片上可以看到,這些分錯的數據中一大部分還是很難區分的,比如第二排第一張,肉眼都是比較難區分的。

  這是可能是因為對於特征的提取還是不夠充分,導致錯誤分類,需要進一步調整模型,下一節主要說一下深度學習中的一些優化策略。

  神經網絡到這里初步介紹完畢了,主要介紹了全連接神經網絡和BP算法,並利用keras框架進行了實現,完成了深度學習的"Hello World"。


 

內容主要來源於李宏毅老師的課程,由於看的時間比較久了,這里再回顧一下,事情比較多,總算完結了,下一節主要總結一下常用的損失函數及特性,以及在深度學習中一些模型優化和調整策略。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM