蘭州大學在讀碩士研究生,主攻無人駕駛,深度學習;蘭大未來計算研究院無人車團隊骨干,在改自己的無人車,參加過很多無人車Hackathon,喜歡極限編程。
在前幾十年,神經網絡并沒有受到人們的重視,直到深度學習的出現,人們利用深度學習解決了不少實際問題(即一些落地性質的商業應用),神經網絡才成為學界和工業界關注的一個焦點。本文以盡可能直白,簡單的方式介紹深度學習中三種典型的神經網絡以及深度學習中的正則化方法。為后面在無人駕駛中的應用做鋪墊。
▌深度學習的能力
近期在學術領域存在這許多批判深度學習的言論(參考Gary Marcus的文章:https://arxiv.org/ftp/arxiv/papers/1801/1801.00631.pdf),深度學習在一些學者看來并不是通往通用人工智能的道路。但是,作為關注行業應用的研究者和工程師,我們并不需要關注深度學習到底是不是通往最終的通用人工智能的道路,我們只需要知道,深度學習到底能不能夠解決我們行業的一些問題(通過傳統的軟件工程很難解決的問題)?答案是能,正是因為深度學習有著這樣的能力,它才成為人工智能研究領域歷史上第一次為各大商業公司追逐的技術(以往的人工智能大多是實驗室產品,從未吸引巨頭的大量資本投入)。商業公司追逐利益,追逐商業化,一項技術能夠為業內大量公司熱捧,說明其已經具備在某些應用行業商業化,產品化的能力,那么我們首先來了解一下深度學習現在已經具備的“產品力”:
圖像識別與分類
傳統的計算機視覺技術在處理圖像識別問題時往往需要人為設計特征,要識別不同的類別,就要設計不同的特征,要識別貓和狗,就要分別為貓和狗設計特征,而這個過程是非常麻煩的,我們以貓和狗為例:
上圖是貓和狗的照片,可以說,光是為貓和狗兩個類別的識別設計特征就需要耗費大量的精力,并且還得是“貓狗專家”才能做這件事情。那么,當要識別的類別上升到1000類的時候呢?傳統的視覺算法的識別精度將會更低。
深度學習取得的第一個重大的突破就在ImageNet的識別挑戰上。
ImageNet是一個擁有1400萬張圖片的巨大數據集,基于ImageNet數據集,ILSVRC(ImageNet Large Scale Visual Recognition Challenge)挑戰賽每年舉辦一次。
自2012年AlexNet在ILSVRC上以遠超第二名的識別率打破當年的記錄以后,深度學習在ImageNet數據集上的識別率在近兩年取得了一個又一個的突破。到2015年,ResNet的top-5識別率正式超過人類:
目標檢測
像素級別的場景分割
視頻字幕生成
游戲和棋牌
語音識別,文本生成,黑白影片自動著色,雅爾塔游戲……
深度學習有著極強的表示能力,能夠處理復雜的任務,自然,我們需要使用深度學習來解決無人駕駛中一直以來的問題(基于深度學習的計算機視覺,基于深度學習的決策,基于強化學習的決策等等)。下面我們就開始深度學習的相關基礎。
▌深度前饋神經網絡——為什么要深?
在第九篇博客的末尾其實我們已經接觸了深度前饋神經網絡,我們使用一個規模很大的深度前饋網絡來解決MNIST手寫字識別問題,我們的這個網絡取得了98%98%的識別率。簡單來說,深度前饋網絡就是早前的三層BP網絡的“加深版”,如圖所示:
其中的層被我們稱為全連接層。那么根據前面的神經網絡的介紹我們知道,即使僅僅使用3層的神經網絡,我們就能夠擬合任意函數了,神經網絡在設計的時候往往遵循奧卡姆剃刀原則,即我們往往使用最簡單的結構來建模,那么為什么要加深網絡的層數呢?
這個問題要從兩個方面來看:一是大數據下的模型訓練效率,一是表示學習。
大數據下的模型訓練效率
有人把深度學習突破的起因歸結于三個要素:
神經網絡理論(一直以來的理論基礎)
大量數據(得益于互聯網的發展)
當代更強的并行計算能力(以GPU計算為代表)
其中的大量數據是深度神經網絡能夠在性能上取得成功的一個重要因素,傳統機器學習算法在數據量增大到一定的數量級以后似乎會陷入一個性能的瓶頸(即使是基于結構風險最小化的支持向量機,其性能也會在數據量到達一定程度以后飽和),但是神經網路似乎是可以不斷擴容的機器學習算法,數據量越大,可以通過增加神經元的個數以及隱含層的層數,訓練更加強大的神經網絡,其變化趨勢大致如下:
但是我們之前的文章也提到,當前已經可以證明,僅僅是簡單的三層神經網絡,通過增加隱含層神經元數量,理論上也可以擬合任意函數。那么我們為什么不直接使用單純的三層網絡結構,只增加隱含層神經元數量來提高模型容量,從而擬合復雜問題呢?
單純的增加單層神經元數量能夠是的模型具有更加強的表示能力,但是,相比于增加層數,每層使用相對少的神經元的策略,前者在實際訓練中訓練成功的難度更大,包含大量隱含層神經元的三層網絡的過擬合問題難以控制,并且要達到相同的性能,深層神經網絡的結構往往要比三層網絡需要的神經元更少。
表示學習
另一種對深度學習前若干層作用的解釋就是表示學習。深度學習=深度表示學習(特征學習),下圖是一個多層卷積網絡在輸入圖像以后,神經網絡隱含層神經元激活的可視化結果:
如圖,神經網絡的前若干層實際上發揮這特征提取和表示建立的作用,區別與傳統機器學習方法的人為設計特征,神經網絡的特征設計是伴隨著神經網絡的訓練而進行的,是一個自動的表示建立過程。從圖中我們還能發現,越靠近輸入端的層(越底層)提取的特征越簡單,層數越高建立的特征越復雜。比如說圖中,第一層提取了“邊緣”特征,第二層則提取了輪廓特征,那么到了第三個隱含層,通過組合簡單的底層特征,綜合出了更加高級的表示,提取的是識別目標的局部特征。通過對特征的逐層抽象化,神經網絡的層數越多,其能夠建立的特征表示也就越豐富。
▌應用于深度神經網絡的正則化技術
當神經網絡的隱含層數和神經元數量增大時,隨之而來的是參數數量的大幅度增大。這是的我們的神經網絡更加容易過擬合,即模型在訓練集上表現好,但是泛化能力差。在機器學習中,許多策略顯式地被設計為減少測試誤差(可能會以增大訓練誤差為代價)。這些策略被統稱為正則化。
下面我們介紹四種常見的正則化技術,它們分別是:
數據集增強(Data Agumentation)
提前終止(Early Stopping)
參數范數懲罰(Parameter Norm Penalties)
Dropout
數據集增強
增強機器學習魯棒性的最直觀的一個策略就是使用更多的數據來訓練模型,即數據集增強。然而,在現實情況下,我們的數據是有限的,所以我們往往通過創建假數據來增加我們的數據集合,而對于某些機器學習任務而言(如圖像分類),創建假數據是非常簡單的,下面我們以MNIST手寫字為例來說明:
上圖中的三個字符是MNIST數據集的訓練集中的三個數字,對于圖像這樣的高維并且包含巨大的可變性的數據,我們可以對數據進行簡單的平移,旋轉,縮放等等來產生新的數據。
def expend_training_data(train_x, train_y): """ Augment training data """ expanded_images = np.zeros([train_x.shape[0] * 5, train_x.shape[1], train_x.shape[2]]) expanded_labels = np.zeros([train_x.shape[0] * 5]) counter = 0 for x, y in zip(train_x, train_y): # register original data expanded_images[counter, :, :] = x expanded_labels[counter] = y counter = counter + 1 # get a value for the background # zero is the expected value, but median() is used to estimate background's value bg_value = np.median(x) # this is regarded as background's value for i in range(4): # rotate the image with random degree angle = np.random.randint(-15, 15, 1) new_img = ndimage.rotate(x, angle, reshape=False, cval=bg_value) # shift the image with random distance shift = np.random.randint(-2, 2, 2) new_img_ = ndimage.shift(new_img, shift, cval=bg_value) # register new training data expanded_images[counter, :, :] = new_img_ expanded_labels[counter] = y counter = counter + 1 return expanded_images, expanded_labelsagument_x, agument_y = expend_training_data(x_train[:3], y_train[:3])
得到5倍于原數據集的新數據集:
后面的四個圖像是我們通過一定的變換得到的,我們并沒有去采集新的數據,通過創造假數據,我們的數據集變成了原來的若干倍,這種處理方法能夠顯著提高神經網絡的泛化能力,即使是具有平移不變性的卷積神經網絡(我們后面會詳述),使用這種簡單處理方式得到的新數據也能大大改善泛化。
提前終止
當訓練參數數量大的神經網絡時,即模型容量大于實際需求時,神經網絡最終總是會過擬合,但是我們總是能夠觀察到,訓練誤差會隨著訓練時間的推移逐漸降低,但是驗證集的誤差卻會先降低后升高,如下圖所示:
那么基于這個現象,我們可以在每次觀察到驗證集誤差有所改善以后保存一份模型的副本,如果誤差惡化,則將 耐心值 +1,當耐心值到達一個事先設定的閾值的時候,終止訓練,返回保存的最后一個副本。這樣,我們能夠得到整個誤差曲線中最低的點的模型。
參數范數懲罰
許多正則化方法會向神經網絡的損失函數L(θ)添加一個懲罰項Ω(w),一次來約束模型的學習能力,形式如下:
其中的θ是包括權重和偏置(w,b)在內的神經網絡的參數,需要注意的是,懲罰項往往只對仿射變換中的權重(即w)進行懲罰,偏置單元b不會被正則化,原因在于:每個權重明確表明兩個變量之間是如何相互作用的。要將權重擬合地很好,需要在各種不同的條件下觀察這些變量。每一個偏置只會控制一個單獨的變量,這也就意味著在保留不被正則化的偏置時,不需要引入過多的方差。 同樣,對偏置參數進行正則化會引入相當程度的欠擬合可能。因此往往只對權重進行懲罰。α是一個需要人為設置的超參數,稱為懲罰系數, 當α為 0 的時候,表示沒有參數懲罰,α越大,則對應的參數的懲罰也就越大。以 L2 正則化為例,我們在損失函數的后面添加了的正則項為:
那么最小化權重的平方會帶來什么結果呢?
神經網絡將傾向于使所有的權重都很小,除非誤差導數過大。
防止擬合錯誤的樣本。
使得模型更加“光滑”,即輸入輸出敏感性更低,輸入的微小變化不會明顯的反映到輸出上。
如果輸入端輸入兩個相同的輸入,網絡的權重分配會傾向于均分權重而不是將所有的權重都分到一個連接上。
L2懲罰一方面降低了權重的學習自由度,削弱了網絡的學習能力,另一方面相對均勻的權重又能使模型光滑化,使模型對輸入的細微變化不敏感,從而增強模型的魯棒性。
Dropout
參數范數懲罰通過改變神經網絡的損失函數來實現正則化,而Dropout則通過改變神經網絡的結構來增強網絡的泛化能力,如圖是一個神經網絡訓練時的結構:??
我們在第一個隱含層后面添加了一個Dropout層,Dropout 就是指隨機地刪除掉網絡中某層的節點,包括該節點的輸入輸出的邊,如下圖所示:
這也等價與以一定的幾率保留節點。在本例中,p即保留節點的幾率,我們設置為50%, 在實踐中,保留概率通常設置在[0.5,1]。那么Dropout為什么有助于防止過擬合呢?簡單的解釋就是,運用了dropout的訓練過程,相當于訓練了很多個只有半數隱層單元的神經網絡(后面簡稱為“半數網絡”),每一個這樣的半數網絡,都可以給出一個分類結果,這些結果有的是正確的,有的是錯誤的。隨著訓練的進行,大部分半數網絡都可以給出正確的分類結果,那么少數的錯誤分類結果就不會對最終結果造成大的影響。
那么等到訓練結束的時候,我們的我們的網絡可以看作是很多個半數網絡的集成模型,到應用網絡的階段,我們就不再使用Dropout,即p=1,網絡的最終輸出結果是所有半數網絡的集成結果,其泛化能力自然就會更好。
▌基于深度前饋神經網絡的交通信號識別
Belgium Traffic Sign Dataset 數據集
我們使用BelgiumTS(Belgium Traffic Sign Dataset)來做一個簡單的識別實例,BelgiumTS是一個交通信號的數據集,包含62中交通信號。
訓練集的下載連接:
http://btsd.ethz.ch/shareddata/BelgiumTSC/BelgiumTSC_Training.zip
測試集的下載鏈接:http://btsd.ethz.ch/shareddata/BelgiumTSC/BelgiumTSC_Testing.zip
此數據集在不采用科學上網的方式時下載速度偏慢。
▌數據的讀取和可視化
下載好數據以后,解壓,使用如下目錄結構存放數據:
data/Training/data/Testing/
該數據集的訓練集和測試集均包含了62個目錄,表示62種交通信號。使用如下函數讀取數據:
def load_data(data_dir): """Loads a data set and returns two lists: images: a list of Numpy arrays, each representing an image. labels: a list of numbers that represent the images labels. """ # Get all subdirectories of data_dir. Each represents a label. directories = [d for d in os.listdir(data_dir) if os.path.isdir(os.path.join(data_dir, d))] # Loop through the label directories and collect the data in # two lists, labels and images. labels = [] images = [] for d in directories: label_dir = os.path.join(data_dir, d) file_names = [os.path.join(label_dir, f) for f in os.listdir(label_dir) if f.endswith(".ppm")] # For each label, load it's images and add them to the images list. # And add the label number (i.e. directory name) to the labels list. for f in file_names: images.append(skimage.data.imread(f)) labels.append(int(d)) return images, labels # Load training and testing datasets.ROOT_PATH = "data"train_data_dir = os.path.join(ROOT_PATH, "Training")test_data_dir = os.path.join(ROOT_PATH, "Testing")images, labels = load_data(train_data_dir)
輸出訓練集的類別數和總的圖片數量:
print("Unique Labels: {0} Total Images: {1}".format(len(set(labels)), len(images)))
Unique Labels: 62Total Images: 4575
我們顯示每一個類別的第一張圖片看看。
def display_images_and_labels(images, labels): """Display the first image of each label.""" unique_labels = set(labels) plt.figure(figsize=(15, 15)) i = 1 for label in unique_labels: # Pick the first image for each label. image = images[labels.index(label)] plt.subplot(8, 8, i) # A grid of 8 rows x 8 columns plt.axis('off') plt.title("Label {0} ({1})".format(label, labels.count(label))) i += 1 _ = plt.imshow(image) plt.show()display_images_and_labels(images, labels)
顯然,數據集的圖片并不是統一的尺寸的,為了訓練神經網絡,我們需要將所有圖片resize到一個相同的尺寸,在本文中,我們將圖片resize到(32,32):
# Resize imagesimages32 = [skimage.transform.resize(image, (32, 32)) for image in images]display_images_and_labels(images32, labels)
輸出resize以后的圖片信息:
for image in images32[:5]: print("shape: {0}, min: {1}, max: {2}".format(image.shape, image.min(), image.max()))
shape: (32, 32, 3), min: 0.0, max: 1.0shape: (32, 32, 3), min: 0.13088235294117614, max: 1.0shape: (32, 32, 3), min: 0.057059972426470276, max: 0.9011967677696078shape: (32, 32, 3), min: 0.023820465686273988, max: 1.0shape: (32, 32, 3), min: 0.023690257352941196, max: 1.0
圖像的取值范圍已經歸一化好了,下面我們使用TensorFlow構造神經網絡來訓練一個深度前饋網絡識別這個交通信號。
數據預處理
我們對數據進行預處理,首先將三通道的RGB轉成灰度圖:
images_a = color.rgb2gray(images_a)display_images_and_labels(images_a, labels)
注意,這里現實的并不是灰度圖,原因在于我們使用了之前的display_images_and_labels函數,只需要在該函數的imshow部分添加cmap='gray'即可顯示灰度圖。
我們使用前面的方法對數據進行擴充(將數據擴充為原來的5倍),我們現實其中的三張圖片:
然后我們對數據進行shuffle,并且把數據切分為訓練集和驗證集,并對標簽進行one-hot編碼:
from sklearn.utils import shuffleindx = np.arange(0, len(labels_a))indx = shuffle(indx)images_a = images_a[indx]labels_a = labels_a[indx]print(images_a.shape, labels_a.shape)train_x, val_x = images_a[:20000], images_a[20000:]train_y, val_y = labels_a[:20000], labels_a[20000:]train_y = keras.utils.to_categorical(train_y, 62)val_y = keras.utils.to_categorical(val_y, 62)print(train_x.shape, train_y.shape)
▌使用Keras構造并訓練深度前饋網絡
我們仍然使用上一篇文章中用到的深度前饋網絡,看看在這類復雜問題中的性能如何:
model = Sequential()model.add(Flatten(input_shape=(32, 32)))model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(62, activation='softmax'))model.summary()model.compile(loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy']) history = model.fit(train_x, train_y, batch_size=128, epochs=20, verbose=1, validation_data=(val_x, val_y)) ### print the keys contained in the history objectprint(history.history.keys())model.save('model.json')
現實訓練loss和驗證loss為:
加載測試數據集,查看精度:
('Test loss:', 0.8060373229994661)('Test accuracy:', 0.7932539684431893)
我們的這個簡單神經網絡在測試集合上取得了79%的精度,我們現實幾個測試樣本的預測結果:
雖然精度不高,但效果似乎還行。。當然,這個例子只是一個入門的網絡,首先,它拋棄了3通道的圖像,所以信息會有一定的損失,其次,全連接網絡的第一步就是把圖像向量化了,我們能不能使用更加深,更加符合圖片二維特征的網絡呢?我們在后面的文章中繼續探討!
完整代碼鏈接:
http://download.csdn.net/download/adamshan/10217607
原文鏈接:
https://blog.csdn.net/adamshan/article/details/79127573
-
無人駕駛
+關注
關注
98文章
4076瀏覽量
120629 -
深度學習
+關注
關注
73文章
5507瀏覽量
121294
原文標題:無人駕駛汽車系統入門:深度前饋網絡,深度學習的正則化,交通信號識別
文章出處:【微信號:AI_Thinker,微信公眾號:人工智能頭條】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論