無人駕駛汽車系統入門(九)——神經網絡基礎

無人駕駛汽車系統入門(九)——神經網絡基礎

在上一節中,我們介紹了機器學習的相關基礎,尤其是知道了監督學習的基本構成因素:數據,模型,策略和算法。在本節,我們具體學習一種監督學習算法——神經網絡。現代深度學習模型其本質均爲人工神經網絡,所以在進一步探索深度學習在無人駕駛中的應用之前,我們先了解一下神經網絡的理論基礎和代碼實現。

創作不易,轉載請註明出處:http://blog.csdn.net/adamshan/article/details/79004784

既然說到神經網絡和深度學習,那麼我們就先來理一理我們前面介紹的各種概念的關係和範疇,如下圖所示,是這些概念的定義範疇:

這裏寫圖片描述

人工智能是一個很大的定義,機器學習是人們在探索人工智能的過程中的一種思路(並不一定是一條通往終極人工智能的路,所以人工智能絕對不等於機器學習!),神經網絡是機器學習中的一種監督學習算法,而深度學習則是將神經網絡的層數增多,使用大量數據來建立的一種表示學習算法(關於表示學習,我們會在深度學習一節詳細論述)。所以我們就先從神經網絡入手,再進一步學習深度學習。

我們以上一節介紹的監督學習的幾個因素(模型,策略和算法)來逐一介紹神經網絡算法中的這幾個因素,任務我們還是沿用上一節介紹的手寫數字識別任務,當然,數據就是MNIST數據了:

神經網絡入門

基本結構

神經網絡的模型當然就是神經網絡了,它的一個基本結構如下圖所示:

這裏寫圖片描述

這是一個形象化的表示,它看起來就像我們人類的神經網絡(這也是這個模型名稱的由來),其中的每一個小圈圈,就像我們人類的一個神經元,一條條邊就像神經元之間的突觸(人的神經元通過突觸相連接),我們取出神經網絡中的一個神經元,它的結構如下圖的右側,這個人造的神經元我們稱之爲 感知機(Perceptron),下圖的左側則是人類的一個神經細胞:

這裏寫圖片描述

由上圖可知,在神經網絡中,一個神經元能夠接受多個輸入,我們用向量 x=(x0,x1,...,xn) 來表示輸入的數值,在神經元的輸入邊上,輸入會乘以一個權重向量 w=w0,w1,...,wn , 在神經元中,這些乘以權重的輸入被求和,並且加上了一個很小的偏置 b ,所以神經元的值就變成了:

h=iwixi+b

這裏的 wb 就是神經網絡需要訓練的參數。在神經元的輸出端, h 被輸入到一個 激活函數(activation function) f 中,即:

output=f(h)=f(iwixi+b)

這裏的激活函數通常是非線性函數,包括 sigmoid 函數, tanh 函數, ReLU ()等等,下圖總結了目前常用的激活函數的圖像,表達式以及導數:

這裏寫圖片描述

那麼我們的神經網絡呢實際上就是由這麼一些神經元通過連接組合而成。前一個神經元的尾部(輸出)連接到後一個神經元的一個輸入,這樣便形成了一個層次狀的機構,那麼在整個神經網絡的第一層,我們稱之爲 輸入層(input layer),它的輸入數值便是我們的數據,最後一層我們稱之爲 輸出層(output layer),中間的若干層被稱爲 隱含層(hidden layer) 。這就是一個神經網絡的基本結構, 們討論模型時強調我們的模型應當具有一定的容量,這個容量表現在能夠擬合我們要解決的任務的函數。也就是說我們的模型應該具有擬合我們想要的函數的能力。神經網絡的強大之處就在於,通過改變神經網絡的結構和參數規模,神經網絡能夠擬合任意函數! ,換句話說,神經網絡擁有無限容量。

前向傳播

在瞭解神經網絡的無限容量以後,開始使用它來擬合我們要解決的機器學習任務——手寫字識別,在這個問題中,手寫字的圖片均爲 28×28 ,所以輸入的向量X長度爲 784 ,手寫字一共有10個類別, 分別是0到9,假設我們使用如下圖的神經網絡來處理這個任務:

這裏寫圖片描述

那麼輸出層的每一個神經元的輸出對應一個類別,所以輸出層是10個神經元,實際上,我們只需要4個輸出就能表示0-9了,但是在實踐中使用對應類別數的輸出能夠表現出更好的性能,這種單點激活的編碼方式我們稱爲one-hot編碼,即對應類別的位置激活爲1,其他位置爲0。這種編碼方式在類別數很少的情況下是一種非常有效的方式。

神經網絡在訓練的過程中輸出的是各個點的“得分”,這個數值,被稱爲 logit ,我們希望得到的不是得分而是輸出層各個神經元被激活的概率,所以我們在神經網絡的輸出層在添加一個 Softmax 層,它的作用就是對輸出層每個神經元的輸出值計算一個概率,概率和爲1,輸出的值越大的點概率也就越大。

Softmax函數能將一個含任意實數的K維的向量 z 的“壓縮”到另一個K維實向量 σ(z) 中,使得每一個元素的範圍都在 (0,1) 之間,並且所有元素的和爲1。計算公式如下:

σ(z)j=ezjk=1Kezk  j=1,,K.

爲了更加直觀,我們使用Python實現一個SoftMax函數:

import numpy as np

def softmax(x):
    """Compute softmax values for each sets of scores in x."""
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum()

scores = [3.0, 1.0, 0.2]
print(softmax(scores))

得到的輸出結果爲:

[ 0.8360188   0.11314284  0.05083836]

有個結果的概率之後,我們計算損失函數,對於具體的任務,要定義相應的損失函數,對於識別和分類這類多分類任務而言,最常用的損失函數就是 交叉熵(Cross Entropy) , 它的表達式如下:

L(θ)=1nn[ylna+(1y)ln(1a)]

其中 y 是真實的標籤,a 則是神經元的輸出。這樣的損失函數具有兩個很好的性質:

  • 非負性:這樣我們就可以最小化損失函數了
  • 真實值與模型的輸出值接近時損失函數的值也趨向於0

那麼在求得損失以後,也就得到了神經網絡的輸出結果(對於手寫字識別而言,就是識別的結果)以及輸出結果與真實值之間的“距離”。我們把這麼一個過程稱爲一次 前向傳播(forward propagation) ,顯然,前向傳播能夠輸出正確的分類的前提是神經網絡已經具有了合適的參數,那麼如何調整合適的參數呢?神經網絡通過 反向傳播(back propagation) 算法讓來自損失函數的信息通過網絡向後流動,從而計算梯度信息。反向傳播這個術語通常被人誤解爲神經網絡的整個學習算法, 實際上它只是一種計算梯度的方法 ,而 隨機梯度下降(stochastic gradient descent,SGD) 纔是使用梯度進行學習的算法,通過隨機梯度下降算法,深度網絡能夠自行完成參數的調整,最小化損失函數,即最小化經驗風險,從未使得模型的變現近似於我們要處理的任務的模式。

隨機梯度下降

隨機梯度下降是幾乎所有的深度學習模型都採用的一種學習算法,在深度學習中,好的模型往往需要大量的數據,大量的數據帶來的計算量也是巨大的,根據前一節我們知道,機器學習算法中的損失函數是通過計算每個樣本的損失函數的和來求得的,那麼訓練數據的損失就可以寫作:

L(θ)=1ni=1nL(xi, yi, θ)

那麼對與這個相加的函數,梯度下降就需要計算:

θL(θ)=1ni=1nθL(xi, yi, θ)

即使用整個樣本集去求解梯度,對於大數據集合(數以百萬,千萬計的樣本),這種做法是很沒有效率的。

在隨機梯度下降中,梯度被認爲是一個期望,那麼期望就可以通過小批量的樣本來近似,因此,在執行梯度下降的過程中,我們不使用整個樣本集來計算梯度,而是每次都隨機取一個 小批量(minibatch) 的樣本,這個小批量的樣本數往往在一千以內(通常,我們習慣使用2的指數次最爲小批量的樣本個數,比如說64,128,256),假設小批量眼本數爲 m , 那麼每次執行梯度下降,我們只需要將隨機抽取的 m 個樣本放入神經網絡,經過前向傳播,求解了損失,然後基於損失函數,反向求解這 m 個樣本的梯度,這 m 個樣本的梯度就可以看作整個樣本集的梯度的估計:

g=1mi=1mθL(xi, yi, θ)

然後,再使用這個梯度的估計量來更新參數:

θθϵg

其中, ϵ 是學習率。

隨機梯度下降是在大規模數據上訓練大型線性模型的主要方法。在深度學習興起之前,學習非線性模型的主要方法是結合核技巧的線性模型。當數據集的樣本數巨大時,這類方法的計算量是不能接受的。在學術界,深度學習從 2006 年開始收到關注的原因是,在數以萬計樣本的中等規模數據集上,深度學習在新樣本上比當時很多熱門算法泛化得更好。不久後,深度學習在工業界受到了更多的關注,因爲其提供了一種訓練大數據集上的非線性模型的可擴展方式。

使用keras實現神經網絡

下面我們使用Keras快速實現一個三層的神經網絡,網絡的結構如上圖所示,(784,15,10) ,我們使用交叉熵作爲損失函數,使用隨機梯度下降作爲模型的優化算法對模型進行訓練。在訓練完成以後,我們將這個模型保存成”model.json”文件,然後使用我們自己的手寫字來驗證一下模型的準確度。我們在jupyter notebook中逐步完成模型的訓練,調整和驗證。

數據準備

我們需要下載MNIST數據集到本地,然後將數據集讀取到內存,並且切分爲訓練集和測試集。

引入我們需要的庫,並且定義超參數:

from __future__ import print_function

import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import SGD
from matplotlib import pyplot as plt

batch_size = 128
num_classes = 10
epochs = 20

下載MNIST數據集,讀取數據集,並打印數據集的大小:

# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

print(x_train.shape, x_test.shape)
print(y_train.shape, y_test.shape)

結果:

(60000, 28, 28) (10000, 28, 28)
(60000,) (10000,)

可以看出,這個數據集的訓練集有6萬個樣本,測試集有1萬個樣本,每個樣本是一張 28×28 的圖像,我們用pyplot把這些圖片展示一部分:

def show_samples(samples, labels):
    """
    display 16 samples and labels
    """
    plt.figure(figsize=(12, 12))
    for i in range(len(samples)):
        plt.subplot(4, 4, i+1)
        plt.imshow(samples[i], cmap='gray')
        plt.title(labels[i])
    plt.show()

show_samples(x_train[:16], y_train[:16])

這裏寫圖片描述

如上圖所示,展示了MNIST數據集的訓練集的前16個樣本圖片和標籤,說明我們的數據讀取正確,由於我們的神經網絡接受的是784這樣一個維度的輸入,所以我們在這要把樣本的形狀做一下調整,同時我們對標籤進行一個 one-hot 編碼:

x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
# 將樣本歸一化
x_train /= 255
x_test /= 255

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print(x_train.shape, x_test.shape)
print(y_train.shape, y_test.shape)
(60000, 784) (10000, 784)
(60000, 10) (10000, 10)

接下來我們構造神經網絡,我們先僅使用15個隱含層神經源看一下訓練的效果:

model = Sequential()
model.add(Dense(15, activation='relu', input_shape=(784,)))
model.add(Dense(num_classes, activation='softmax'))

model.summary()

model.compile(loss='categorical_crossentropy',
              optimizer=SGD(lr=0.01),
              metrics=['accuracy'])

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_data=(x_test, y_test))
### print the keys contained in the history object
print(history.history.keys())

畫出訓練過程的損失情況:

def plot_training(history):
    ### plot the training and validation loss for each epoch
    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('model mean squared error loss')
    plt.ylabel('mean squared error loss')
    plt.xlabel('epoch')
    plt.legend(['training set', 'validation set'], loc='upper right')
    plt.show()

plot_training(history=history)

這裏寫圖片描述

我們發現訓練集和驗證集的損失都是呈現一個下降的趨勢,並且大約在20個epoch之後區域穩定。

我們使用測試集來驗證一下模型識別的精度:

score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

結果:

Test loss: 0.2586659453
Test accuracy: 0.9249

發現我們的模型在測試集上的識別進度爲92% ,看起來很高,但是我們還是不滿意,我們想改進我們的模型來提高識別的精度,其實,傳統的BP神經網絡就到此爲止了,我們使用當前大熱的深度神經網絡的思維來改變一下代碼,讀者可能會對後面的代碼產生困惑,但是不用擔心,相關的理論知識我會在後面的博客中在和大家一起詳細討論。

3層網絡的一點小變動——深度前饋神經網絡:

首先我們把我們原來的網絡的層數進一步加深,變成2個隱含層,同時,我們將每一個隱含層的神經元數量擴大到512個,在每一個隱含層的後面,我們使用一種叫做 Dropout 的正則化的技術,最後,我們使用一種SGD的變體——RMSprop算法作爲模型的學習算法,這樣,我們的第一個深度神經網絡就構造好了,來看看效果吧~

from keras.layers import Dropout
from keras.optimizers import RMSprop

model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))

model.summary()

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

history = model.fit(x_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_data=(x_test, y_test))

### print the keys contained in the history object
print(history.history.keys())
plot_training(history=history)
model.save('model.json')

score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

網絡的結構和參數數量:

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
dense_3 (Dense)              (None, 512)               401920
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0
_________________________________________________________________
dense_4 (Dense)              (None, 512)               262656
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0
_________________________________________________________________
dense_5 (Dense)              (None, 10)                5130
=================================================================
Total params: 669,706
Trainable params: 669,706
Non-trainable params: 0

訓練過程中的損失的變化:

這裏寫圖片描述

測試集的識別精度:

Test loss: 0.117383948493
Test accuracy: 0.9824

我們的”深度”網絡取得了 98% 的分類精度,比我們之前的3層網絡確實好了很多,那麼我們抽取測試集的16張圖片來看看模型識別的效果吧:

import numpy as np
result = model.predict(x_test[:16])
result = np.argmax(result, 1)
print('predict: ', result)
true = np.argmax(y_test[:16], 1)
print('true: ', true)
predict:  [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5]
true:  [7 2 1 0 4 1 4 9 5 9 0 6 9 0 1 5]

看看實際的圖片:

fig2 = plt.figure(figsize=(12, 12))
for i in range(16):
    plt.subplot(4, 4, i+1)
    plt.imshow(x_test[i].reshape((28, 28)), cmap='gray')
    plt.title('predict:'+str(result[i])+' true:'+str(true[i]))
plt.show()

這裏寫圖片描述

小結

至此,神經網絡的基礎部分我們就已經瞭解完了,下面我們會進一步瞭解深度學習的一些理論和實踐,在接下來,我們將學習深度學習在無人駕駛的感知模塊的應用,以及單純基於深度學習的端到端無人駕駛技術。我們還會進一步探索強化學習+深度神經網絡,學習應用於無人駕駛的強化學習理論和技術。

完整代碼見鏈接:http://download.csdn.net/download/adamshan/10194745

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