干貨|深入理解神經網絡(深度神經網絡通俗理解)
本文將介紹用于解決實際問題的深度學習架構的不同模塊。前一章使用PyTorch的低級操作構建了如網絡架構、損失函數和優化器這些模塊。本章將介紹用于解決真實問題的神經網絡的一些重要組件,以及PyTorch如何通過提供大量高級函數來抽象出復雜度。本章還將介紹用于解決真實問題的算法,如回歸、二分類、多類別分類等。
本文將討論如下主題:
詳解神經網絡的不同構成組件;
探究PyTorch中用于構建深度學習架構的高級功能;
應用深度學習解決實際的圖像分類問題。
1 詳解神經網絡的組成部分
上一章已經介紹了訓練深度學習算法需要的幾個步驟。
1.構建數據管道。
2.構建網絡架構。
3.使用損失函數評估架構。
4.使用優化算法優化網絡架構的權重。
上一章中的網絡由使用PyTorch數值運算構建的簡單線性模型組成。盡管使用數值運算為玩具性質的問題搭建神經架構很簡單,但當需要構建解決不同領域的復雜問題時,如計算機視覺和自然語言處理,構建一個架構就迅速變得復雜起來。大多數深度學習框架,如PyTorch、TensorFlow和Apache MXNet,都提供了抽象出很多復雜度的高級功能。這些深度學習框架的高級功能稱為層(layer)。它們接收輸入數據,進行如同在前面一章看到的各種變換,并輸出數據。解決真實問題的深度學習架構通常由1~150個層組成,有時甚至更多。抽象出低層的運算并訓練深度學習算法的過程如圖3.1所示。
圖3.1
1.1 層——神經網絡的基本組成
在本章的剩余部分,我們會見到各種不同類型的層。首先,先了解其中最重要的一種層:線性層,它就是我們前面講過的網絡層結構。線性層應用了線性變換:
{-:-}Y
=Wx
+b
線性層之所以強大,是因為前一章所講的功能都可以寫成單一的代碼行,如下所示。
from torch.nn import Linear
myLayer = Linear(in_features=10,out_features=5,bias=True)
上述代碼中的myLayer層,接受大小為10的張量作為輸入,并在應用線性變換后輸出一個大小為5的張量。下面是一個簡單例子的實現:
可以使用屬性weights和bias訪問層的可訓練參數:
線性層在不同的框架中使用的名稱有所不同,有的稱為dense層,有的稱為全連接層(fully connected layer)。用于解決真實問題的深度學習架構通常包含不止一個層。在PyTorch中,可以用多種方式實現。
一個簡單的方法是把一層的輸出傳入給另一層:
inp = Variable(torch.randn(1,10))
myLayer = Linear(in_features=10,out_features=5,bias=True)
myLayer(inp)
每一層都有自己的學習參數,在多個層的架構中,每層都學習出它本層一定的模式,其后的層將基于前一層學習出的模式構建。把線性層簡單堆疊在一起是有問題的,因為它們不能學習到簡單線性表示以外的新東西。我們通過一個簡單的例子看一下,為什么把線性層堆疊在一起的做法并不合理。
假設有具有如下權重的兩個線性層:
以上包含兩個不同層的架構可以簡單表示為帶有另一不同層的單層。因此,只是堆疊多個線性層并不能幫助我們的算法學習任何新東西。有時,這可能不太容易理解,我們可以用下面的數學公式對架構進行可視化:
{-:-}Y
= 2(3X
1
) -2?Linear layers
{-:-}Y
= 6(X
1
) -1?Linear layers
為解決這一問題,相較于只是專注于線性關系,我們可以使用不同的非線性函數,幫助學習不同的關系。
深度學習中有很多不同的非線性函數。PyTorch以層的形式提供了這些非線性功能,因為可以采用線性層中相同的方式使用它們。
一些流行的非線性函數如下所示:
sigmoid
tanh
ReLU
Leaky ReLU
1.2 非線性激活函數
非線性激活函數是獲取輸入,并對其應用數學變換從而生成輸出的函數。我們在實戰中可能遇到數個非線性操作。下面會講解其中幾個常用的非線性激活函數。
sigmoid激活函數的數學定義很簡單,如下:

簡單來說,sigmoid函數以實數作為輸入,并以一個0到1之間的數值作為輸出。對于一個極大的負值,它返回的值接近于0,而對于一個極大的正值,它返回的值接近于1。圖3.2所示為sigmoid函數不同的輸出。
圖3.2
sigmoid函數曾一度被不同的架構使用,但由于存在一個主要弊端,因此最近已經不太常用了。當sigmoid函數的輸出值接近于0或1時,sigmoid函數前一層的梯度接近于0,由于前一層的學習參數的梯度接近于0,使得權重不能經常調整,從而產生了無效神經元。
非線性函數tanh將實數值輸出為-1到1之間的值。當tanh的輸出極值接近-1和1時,也面臨梯度飽和的問題。不過,因為tanh的輸出是以0為中心的,所以比sigmoid更受偏愛,如圖3.3所示。
圖3.3
近年來ReLU變得很受歡迎,我們幾乎可以在任意的現代架構中找到ReLU或其某一變體的身影。它的數學公式很簡單:
{-:-}f(x)=max(0,x)
簡單來說,ReLU把所有負值取作0,正值保持不變??梢詫eLU函數進行可視化,如圖3.4所示。
圖3.4
使用ReLU函數的一些好處和弊端如下。
有助于優化器更快地找到正確的權重集合。從技術上講,它使隨機梯度下降收斂得更快。
計算成本低,因為只是判斷了閾值,并未計算任何類似于sigmoid或tangent函數計算的內容。
ReLU有一個缺點,即當一個很大的梯度進行反向傳播時,流經的神經元經常會變得無效,這些神經元稱為無效神經元,可以通過謹慎選擇學習率來控制。我們將在第4章中討論調整學習率的不同方式時,了解如何選擇學習率。
Leaky ReLU嘗試解決一個問題死角,它不再將飽和度置為0,而是設為一個非常小的數值,如0.001。對某些用例,這一激活函數提供了相較于其他激活函數更優異的性能,但它不是連續的。
1.3 PyTorch中的非線性激活函數
PyTorch已為我們實現了大多數常用的非線性激活函數,我們可以像使用任何其他的層那樣使用它們。讓我們快速看一個在PyTorch中使用ReLU激活函數的例子:
在上面這個例子中,輸入是包含兩個正值、兩個負值的張量,對其調用ReLU函數,負值將取為0,正值則保持不變。
現在我們已經了解了構建神經網絡架構的大部分細節,我們來構建一個可用于解決真實問題的深度學習架構。上一章中,我們使用了簡單的方法,因而可以只關注深度學習算法如何工作。后面將不再使用這種方式構建架構,而是使用PyTorch中正常該用的方式構建。
PyTorch中所有網絡都實現為類,創建PyTorch類的子類要調用nn.Module,并實現__init__和forward方法。在init方法中初始化層,這一點已在前一節講過。在forward方法中,把輸入數據傳給init方法中初始化的層,并返回最終的輸出。非線性函數經常被forward函數直接使用,init方法也會使用一些。下面的代碼片段展示了深度學習架構是如何用PyTrorch實現的:
如果你是Python新手,上述代碼可能會比較難懂,但它全部要做的就是繼承一個父類,并實現父類中的兩個方法。在Python中,我們通過將父類的名字作為參數傳入來創建子類。init方法相當于Python中的構造器,super方法用于將子類的參數傳給父類,我們的例子中父類就是nn.Module。
待解決的問題種類將基本決定我們將要使用的層,處理序列化數據問題的模型從線性層開始,一直到長短期記憶(LSTM)層?;谝鉀Q的問題類別,最后一層是確定的。使用機器學習或深度學習算法解決的問題通常有三類,最后一層的情況通常如下。
對于回歸問題,如預測T恤衫的銷售價格,最后使用的是有一個輸出的線性層,輸出值為連續的。
將一張給定的圖片歸類為T恤衫或襯衫,用到的是sigmoid激活函數,因為它的輸出值不是接近1就是接近0,這種問題通常稱為二分類問題。
對于多類別分類問題,如必須把給定的圖片歸類為T恤、牛仔褲、襯衫或連衣裙,網絡最后將使用softmax層。讓我們拋開數學原理來直觀理解softmax的作用。舉例來說,它從前一線性層獲取輸入,并輸出給定數量樣例上的概率。在我們的例子中,將訓練它預測每個圖片類別的4種概率。記住,所有概率相加的總和必然為1。
一旦定義好了網絡架構,還剩下最重要的兩步。一步是評估網絡執行特定的回歸或分類任務時表現的優異程度,另一步是優化權重。
優化器(梯度下降)通常接受一個標量值,因而loss函數應生成一個標量值,并使其在訓練期間最小化。某些用例,如預測道路上障礙物的位置并判斷是否為行人,將需要兩個或更多損失函數。即使在這樣的場景下,我們也需要把損失組合成一個優化器可以最小化的標量。最后一章將詳細討論把多個損失值組合成一個標量的真實例子。
上一章中,我們定義了自己的loss函數。PyTorch提供了經常使用的loss函數的實現。我們看看回歸和分類問題的loss函數。
回歸問題經常使用的loss函數是均方誤差(MSE)。它和前面一章實現的loss函數相同??梢允褂肞yTorch中實現的loss函數,如下所示:
對于分類問題,我們使用交叉熵損失函數。在介紹交叉熵的數學原理之前,先了解下交叉熵損失函數做的事情。它計算用于預測概率的分類網絡的損失值,損失總和應為1,就像softmax層一樣。當預測概率相對正確概率發散時,交叉熵損失增加。例如,如果我們的分類算法對圖3.5為貓的預測概率值為0.1,而實際上這是只熊貓,那么交叉熵損失就會更高。如果預測的結果和真實標簽相近,那么交叉熵損失就會更低。
圖3.5
下面是用Python代碼實現這種場景的例子。
為了在分類問題中使用交叉熵損失,我們真的不需要擔心內部發生的事情——只要記住,預測差時損失值高,預測好時損失值低。PyTorch提供了loss函數的實現,可以按照如下方式使用。
PyTorch包含的其他一些loss函數如表3.1所示。
表3.1
計算出網絡的損失值后,需要優化權重以減少損失,并改善算法準確率。簡單起見,讓我們看看作為黑盒的優化器,它們接受損失函數和所有的學習參數,并微量調整來改善網絡性能。PyTorch提供了深度學習中經常用到的大多數優化器。如果大家想研究這些優化器內部的動作,了解其數學原理,強烈建議瀏覽以下博客:
http://colah.github.io/posts/2015-08-Backprop/
PyTorch提供的一些常用的優化器如下:
ADADELTA
Adagrad
Adam
SparseAdam
Adamax
ASGD
LBFGS
RMSProp
Rprop
SGD
第4章中將介紹更多算法細節,以及一些優勢和折中方案考慮。讓我們看看創建任意optimizer的一些重要步驟:
optimizer = optim.SGD(model.parameters(), lr = 0.01)
在上面的例子中,創建了SGD優化器,它把網絡的所有學習參數作為第一個參數,另外一個參數是學習率,學習率決定了多大比例的變化調整可以作用于學習參數。第4章將深入學習率和動量(momentum)的更多細節,它們是優化器的重要參數。創建了優化器對象后,需要在循環中調用zero_grad()方法,以避免參數把上一次optimizer調用時創建的梯度累加到一起:
再一次調用loss函數的backward方法,計算梯度值(學習參數需要改變的量),然后調用optimizer.step()方法,用于真正改變調整學習參數。
現在已經講述了幫助計算機識別圖像所需要的大多數組件。我們來構建一個可以區分狗和貓的復雜深度學習模型,以將學到的內容用于實踐。
1.4 使用深度學習進行圖像分類
解決任何真實問題的重要一步是獲取數據。Kaggle提供了大量不同數據科學問題的競賽。我們將挑選一個2014年提出的問題,然后使用這個問題測試本章的深度學習算法,并在第5章中進行改進,我們將基于卷積神經網絡(CNN)和一些可以使用的高級技術來改善圖像識別模型的性能。大家可以從https://www.kaggle.com/c/dogs-vs-cats/data下載數據。數據集包含25,000張貓和狗的圖片。在實現算法前,預處理數據,并對訓練、驗證和測試數據集進行劃分是需要執行的重要步驟。數據下載完成后,可以看到對應數據文件夾包含了如圖3.6所示的圖片。
圖3.6
當以圖3.7所示的格式提供數據時,大多數框架能夠更容易地讀取圖片并為它們設置標簽的附注。也就是說每個類別應該有其所包含圖片的獨立文件夾。這里,所有貓的圖片都應位于cat文件夾,所有狗的圖片都應位于dog文件夾。
圖3.7
Python可以很容易地將數據調整成需要的格式。請先快速瀏覽一下代碼,然后,我們將講述重要的部分。
上述代碼所做的處理,就是獲取所有圖片文件,并挑選出2,000張用于創建驗證數據集。它把圖片劃分到了cats和dogs這兩個類別目錄中。創建獨立的驗證集是通用的重要實踐,因為在相同的用于訓練的數據集上測試算法并不合理。為了創建validation數據集,我們創建了一個圖片數量長度范圍內的數字列表,并把圖像無序排列。在創建validation數據集時,我們可使用無序排列的數據來挑選一組圖像。讓我們詳細解釋一下每段代碼。
下面的代碼用于創建文件:
files = glob(os.path.join(path,'*/*.jpg'))
glob方法返回特定路徑的所有文件。當圖片數量巨大時,也可以使用iglob,它返回一個迭代器,而不是將文件名載入到內存中。在我們的例子中,只有25,000個文件名,可以很容易加載到內存里。
可以使用下面的代碼混合排列文件:
shuffle?=?np.random.permutation(no_of_images)
上述代碼返回25,000個0~25,000范圍內的無序排列的數字,可以把其作為選擇圖片子集的索引,用于創建validation數據集。
可以創建驗證代碼,如下所示:
上述代碼創建了validation文件夾,并在train和valid目錄里創建了對應的類別文件夾(cats和dogs)。
可以用下面的代碼對索引進行無序排列:
在上面的代碼中,我們使用無序排列后的索引隨機抽出2000張不同的圖片作為驗證集。同樣地,我們把訓練數據用到的圖片劃分到train目錄。
現在已經得到了需要格式的數據,我們來快速看一下如何把圖片加載成PyTorch張量。
PyTorch的torchvision.datasets包提供了一個名為ImageFolder的工具類,當數據以前面提到的格式呈現時,它可以用于加載圖片以及相應的標簽。通常需要進行下面的預處理步驟。
1.把所有圖片轉換成同等大小。大多數深度學習架構都期望圖片具有相同的尺寸。
2.用數據集的均值和標準差把數據集歸一化。
3.把圖片數據集轉換成PyTorch張量。
PyTorch在transforms模塊中提供了很多工具函數,從而簡化了這些預處理步驟。例如,進行如下3種變換:
調整成256 ×256大小的圖片;
轉換成PyTorch張量;
歸一化數據(第5章將探討如何獲得均值和標準差)。
下面的代碼演示了如何使用ImageFolder類進行變換和加載圖片:
train對象為數據集保留了所有的圖片和相應的標簽。它包含兩個重要屬性:一個給出了類別和相應數據集索引的映射;另一個給出了類別列表。
把加載到張量中的數據可視化往往是一個最佳實踐。為了可視化張量,必須對張量再次變形并將值反歸一化。下面的函數實現了這樣的功能:
現在,可以把張量傳入前面的imshow函數,將張量轉換成圖片:
imshow(train[50][0])
上述代碼生成的輸出如圖3.8所示。
圖3.8
在深度學習或機器學習中把圖片進行批取樣是一個通用實踐,因為當今的圖形處理器(GPU)和CPU都為批量圖片的操作進行了優化。批尺寸根據我們使用的GPU種類而不同。每個GPU都有自己的內存,可能從2GB到12GB不等,有時商業GPU內存會更大。PyTorch提供了DataLoader類,它輸入數據集將返回批圖片。它抽象出了批處理的很多復雜度,如應用變換時的多worker的使用。下面的代碼把前面的train和valid數據集轉換到數據加載器(data loader)中:
DataLoader類提供了很多選項,其中最常使用的選項如下。
shuffle:為true時,每次調用數據加載器時都混合排列圖片。
num_workers:負責并發。使用少于機器內核數量的worker是一個通用的實踐。
對于大多的真實用例,特別是在計算機視覺中,我們很少構建自己的架構。可以使用已有的不同架構快速解決我們的真實問題。在我們的例子中,使用了流行的名為ResNet的深度學習算法,它在2015年贏得了不同競賽的冠軍,如與計算機視覺相關的ImageNet。為了更容易理解,我們假設算法是一些仔細連接在一起的不同的PyTorch層,并不關注算法的內部。在第5章學習卷積神經網絡(CNN)時,我們將看到一些關鍵的ResNet算法的構造塊。PyTorch通過torchvision.models模塊提供的現成應用使得用戶更容易使用這樣的流行算法。因而,對于本例,我們快速看一下如何使用算法,然后再詳解每行代碼:
models.resnet18(pertrained = True)對象創建了算法的實例,實例是PyTorch層的集合。我們打印出model_ft,快速地看一看哪些東西構成了ResNet算法。算法的一小部分看起來如圖3.9所示。這里沒有包含整個算法,因為這很可能會占用幾頁內容。
圖3.9
可以看出,ResNet架構是一個層的集合,包含的層為Conv2d、BatchNorm2d和?MaxPool2d,這些層以一種特有的方式組合在一起。所有這些算法都將接受一個名為pretrained的參數。當pretrained為True時,算法的權重已為特定的ImageNet分類問題微調好。ImageNet預測的類別有1000種,包括汽車、船、魚、貓和狗等。訓練該算法,使其預測1000種ImageNet類別,權重調整到某一點,讓算法得到最高的準確率。我們為用例使用這些保存好并與模型共享的權重。與以隨機權重開始的情況相比,算法以微調好的權重開始時會趨向于工作得更好。因而,我們的用例將從預訓練好的權重開始。
ResNet算法不能直接使用,因為它是用來預測1,000種類別,而對于我們的用例,僅需預測貓和狗這兩種類別。為此,我們拿到ResNet模型的最后一層——linear層,并把輸出特征改成2,如下面的代碼所示:
model_ft.fc?=?nn.Linear(num_ftrs,?2)
如果在基于GPU的機器上運行算法,需要在模型上調用cuda方法,讓算法在GPU上運行。強烈建議在裝備了GPU的機器上運行這些算法;有了GPU后,用很少的錢就可以擴展出一個云實例。下面代碼片段的最后一行告知PyTorch在GPU上運行代碼:
if?is_cuda: ????model_ft?=?model_ft.cuda()
前一節中,我們已經創建了DataLoader實例和算法?,F在訓練模型。為此我們需要loss函數和optimizer:
在上述代碼中,創建了基于CrossEntropyLoss的loss函數和基于SGD的優化器。StepLR函數幫助動態修改學習率。第4章將討論用于調優學習率的不同策略。
下面的train_model函數獲取模型輸入,并通過多輪訓練調優算法的權重降低損失:
上述函數的功能如下。
1.傳入流經模型的圖片并計算損失。
2.在訓練階段反向傳播。在驗證/測試階段,不調整權重。
3.每輪訓練中的損失值跨批次累加。
4.存儲最優模型并打印驗證準確率。
上面的模型在運行25輪后,驗證準確率達到了87%。下面是前面的train_model函數在Dogs vs. Cats數據集上訓練時生成的日志;為了節省篇幅,本書只包含了最后幾輪的結果。
接下來的章節中,我們將學習可以以更快的方式訓練更高準確率模型的高級技術。前面的模型在Titan X GPU上運行了30分鐘的時間,后面將講述有助于更快訓練模型的不同技術。
2 小結
本章通過使用SGD優化器調整層權重,講解了PyTorch中神經網絡的全生命周期——從構成不同類型的層,到加入激活函數、計算交叉熵損失,再到優化網絡性能(即最小化損失)。
本章還介紹了如何應用流行的ResNet架構解決二分類和多類別分類問題。
同時,我們嘗試解決了真實的圖像分類問題,把貓的圖片歸類為cat,把狗的圖片歸類為dog。這些知識可以用于對不同的實體進行分類,如辨別魚的種類,識別狗的品種,劃分植物種子,將***癌歸類成Type1、Type2和Type3型等。
本文摘自《PyTorch深度學習》
譯者:王海玲, 劉江峰
本書在不深入數學細節的條件下,給出了多個先進深度學習架構的直觀解釋,如ResNet、DenseNet、Inception和Seq2Seq等,也講解了如何進行遷移學習,如何使用預計算特征加速遷移學習,以及如何使用詞向量、預訓練的詞向量、LSTM和一維卷積進行文本分類。
閱讀完本書后,讀者將會成為一個熟練的深度學習人才,能夠利用學習到的不同技術解決業務問題。
本文轉載自異步社區
高性能計算 人工智能
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。