別做空想家!學好PyTorch,你的物件識別專案穩了

人工智慧頻道發表於2018-11-30

Keras是一個很棒的庫,它提供了一個簡單的API來構建神經網路,但最近對PyTorch的興奮感最終讓我對探索這個庫產生了興趣。雖然我是一個"盲目追隨炒作"的人,但是研究人員的採用和fast.ai的推崇使我確信在這個深度學習的新入口中必定有新的東西值得我去探尋。

由於學習新技術的最佳方法是使用它來解決問題,所以我學習PyTorch的工作始於一個簡單的專案:使用預先訓練的卷積神經網路進行物件識別任務。在本文中,我們將看到如何使用PyTorch來實現這一目標,並在此過程中學習一些關於庫和遷移學習的重要概念。

別做空想家!學好PyTorch,你的物件識別專案穩了


雖然PyTorch可能不適合所有人,但在這一點上,很難說出哪個深度學習庫會脫穎而出,而能夠快速學習和使用不同的工具對於成為資料科學家來說至關重要。

該專案的完整程式碼在GitHub上以Jupyter Notebook的形式提供(https://github.com/WillKoehrsen/pytorch_challenge/blob/master/Transfer%20Learning%20in%20PyTorch.ipynb)。這個專案源於我參加Udacity PyTorch獎學金挑戰(https://www.udacity.com/facebook-pytorch-scholarship)。

別做空想家!學好PyTorch,你的物件識別專案穩了

從受過訓練的網路預測

遷移學習法

我們的任務是訓練可以識別影像中物體的卷積神經網路(CNN)。我們將使用Caltech 101資料集(http://www.vision.caltech.edu/Image_Datasets/Caltech101/),該資料集包含101個類別的影像。大多數類別只有50個影像,這些影像通常不足以讓神經網路學會高精度。因此,我們將使用預先構建和預先訓練的模型來應用遷移學習,而不是從頭開始構建和訓練CNN。

遷移學習的基本前提很簡單:採用在大型資料集上訓練的模型,並將其轉移到較小的資料集上。對於使用CNN的物件識別,我們凍結網路的早期卷積層,並且僅訓練進行預測的最後幾層。這個想法是卷積層提取適用於影像的一般,低階特徵(例如邊緣、圖案、漸變)後面的圖層識別影像中的特定特徵,如眼睛或車輪。

因此,我們可以使用在大規模資料集(通常是Imagenet)中訓練不相關類別的網路,並將其應用於我們自己的問題中,因為影像之間共享通用的低階特徵。Caltech 101資料集中的影像與Imagenet資料集中的影像非常相似,模型在Imagenet上學習的知識應該很容易轉移到此任務中。(http://www.image-net.org/)

別做空想家!學好PyTorch,你的物件識別專案穩了


遷移學習背後的理念

以下是物體識別的遷移學習的概要:

  1. 載入在大型資料集上訓練的預訓練CNN模型

  2. 凍結模型的下卷積層中的引數(權重)

  3. 新增具有多層可訓練引數的自定義分類器以進行建模

  4. 訓練可用於任務的訓練資料的分類器層

  5. 根據需要微調超引數並解凍更多層

事實證明,這種方法適用於廣泛的領域。這是一個很好的工具,通常是面對新的影像識別問題時應該嘗試的第一種方法。

資料設定

對於所有資料科學問題,正確格式化資料將決定專案的成功或失敗。幸運的是,Caltech 101資料集影像清晰,並以正確的格式儲存。如果我們正確設定資料目錄,PyTorch可以很容易地將正確的標籤與每個類關聯起來。我將資料分為訓練,驗證和測試集,分別為50%,25%,25%,然後按如下方式構建目錄:

別做空想家!學好PyTorch,你的物件識別專案穩了


按類別劃分的訓練影像數量(我可以互換地使用術語類別和類別):

別做空想家!學好PyTorch,你的物件識別專案穩了

按類別劃分的訓練影像數量

我們希望模型在具有更多示例的類上做得更好,因為它可以更好地學習將特性對映到標籤。為了處理有限數量的訓練樣例,我們將在訓練期間使用資料增加。

作為另一項資料探索,我們還可以檢視大小分佈。

別做空想家!學好PyTorch,你的物件識別專案穩了

按類別分佈平均影像大小(以畫素為單位)

Imagenet模型需要224 x 224的輸入大小,因此其中一個預處理步驟將是調整影像大小。預處理也是我們為訓練資料實施資料增強的地方。

資料增強

資料增強的想法是通過對影像應用隨機變換來人為地增加模型看到的訓練影像的數量。例如,我們可以隨機旋轉或裁剪影像或水平翻轉它們。我們希望我們的模型能夠區分物件,而不管方向如何,資料增強也可以使模型對輸入資料的轉換不變。

無論大象朝哪個方向走,大象仍然是大象!

別做空想家!學好PyTorch,你的物件識別專案穩了

訓練資料的影像變換

通常僅在訓練期間進行增強(儘管在fast.ai庫中可以進行測試時間增加)。每個時期 - 通過所有訓練影像的一次迭代 - 對每個訓練影像應用不同的隨機變換。這意味著如果我們迭代資料20次,我們的模型將看到每個影像的20個略有不同的版本。整體結果應該是一個模型,它可以學習物件本身,而不是如何呈現它們或影像中的工件。

影像預處理

這是處理影像資料最重要的一步。在影像預處理期間,我們同時為網路準備影像並將資料增強應用於訓練集。每個模型都有不同的輸入要求,但如果我們讀完Imagenet所需的內容,我們就會發現我們的影像需要為224x224並標準化為一個範圍。

要在PyTorch中處理影像,我們使用遷移,即應用於陣列的簡單操作。驗證(和測試)遷移如下:

  • 調整

  • 中心裁剪為224 x 224

  • 遷移為張量

  • 用均值和標準差標準化

通過這些遷移的最終結果是可以進入我們網路的張量。訓練變換是相似的,但增加了隨機增強。

首先,我們定義訓練和驗證轉換:

別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


然後,我們建立資料集和資料閱讀器。ImageFolder建立資料集,PyTorch將自動將影像與正確的標籤關聯,前提是我們的目錄設定如上述。然後將資料集傳遞給DataLoader,這是一個產生批量影像和標籤的迭代器。

別做空想家!學好PyTorch,你的物件識別專案穩了


我們可以使用以下方法檢視DataLoader的迭代行為:

別做空想家!學好PyTorch,你的物件識別專案穩了


批處理的形狀是(batch_size,color_channels,height,width)。在訓練、驗證和最終測試期間,我們將遍歷DataLoaders,一次通過包含一個時期的完整資料集。每個時期,訓練DataLoader將對影像應用稍微不同的隨機變換以進行訓練資料增強。

用於影像識別的預訓練模型

隨著我們的資料的成形,我們接下來將注意力轉向模型。為此,我們將使用預先訓練的卷積神經網路。PyTorch有許多模型已經在Imagenet的1000個類中訓練了數百萬個影像。完整的模型列表可以在這裡看到(https://pytorch.org/docs/stable/torchvision/models.html)。這些模型在Imagenet上的效能如下所示:

別做空想家!學好PyTorch,你的物件識別專案穩了

PyTorch中的預訓練模型和Imagenet上的效能

對於此實現,我們將使用VGG-16。雖然它沒有記錄最低的錯誤,但我發現它適用於任務,並且比其他模型訓練得更快。使用預訓練模型的過程已經建立:

  1. 從在大型資料集上訓練的網路載入預訓練的權重

  2. 凍結較低(卷積)圖層中的所有權重:根據新任務與原始資料集的相似性調整要凍結的圖層

  3. 用自定義分類器替換網路的上層:輸出數必須設定為等於類的數量

  4. 僅為任務訓練自定義分類器層,從而優化較小資料集的模型

在PyTorch中載入預先訓練的模型很簡單:

別做空想家!學好PyTorch,你的物件識別專案穩了


這個模型有超過1.3億個引數,但我們只訓練最後幾個完全連線的層。首先,我們凍結所有模型的權重:

別做空想家!學好PyTorch,你的物件識別專案穩了


然後,我們使用以下圖層新增我們自己的自定義分類器:

  • 與ReLU啟用完全連線,shape =(n_inputs,256)

  • Dropout有40%的可能性下降

  • 與log softmax輸出完全連線,shape =(256,n_classes)

別做空想家!學好PyTorch,你的物件識別專案穩了


將額外圖層新增到模型時,預設情況下將它們設定為可訓練(require_grad = True)。對於VGG-16,我們只改變最後一個原始的全連線層。卷積層和前5個完全連線層中的所有權重都是不可訓練的。

別做空想家!學好PyTorch,你的物件識別專案穩了


網路的最終輸出是我們資料集中100個類中每個類的對數概率。 該模型共有1.35億個引數,其中只有100多萬個將被訓練。

別做空想家!學好PyTorch,你的物件識別專案穩了


將模型移動到GPU(s)

PyTorch的最佳方面之一是可以輕鬆地將模型的不同部分移動到一個或多個gpus(https://pytorch.org/docs/stable/notes/cuda.html),以便你可以充分利用你的硬體。由於我使用2 gpus進行訓練,我首先將模型移動到cuda,然後建立一個分佈在gpus上的DataParallel模型:

別做空想家!學好PyTorch,你的物件識別專案穩了


(這個筆記本應該在一個gpu上執行,以便在合理的時間內完成。對CPU的加速可以輕鬆達到10倍或更多。)

訓練損失和優化

訓練損失(預測和真值之間的誤差或差異)是負對數似然(NLL:https://ljvmiranda921.github.io/notebook/2017/08/13/softmax-and-the-negative-log-likelihood/)。(PyTorch中的NLL損失需要對數概率,因此我們從模型的最後一層傳遞原始輸出。)PyTorch使用自動微分,這意味著張量不僅跟蹤它們的值,而且還跟蹤每個操作(乘法,加法,啟用等)。這意味著我們可以針對任何先前張量計算網路中任何張量的梯度。

這在實踐中意味著損失不僅跟蹤誤差,而且跟蹤模型中每個權重和偏差對誤差的貢獻。在我們計算損失後,我們可以找到相對於每個模型引數的損失梯度,這個過程稱為反向傳播。一旦我們獲得了梯度,我們就會使用它們來更新引數和優化器。

優化器是Adam(https://machinelearningmastery.com/adam-optimization-algorithm-for-deep-learning/),梯度下降的有效變體,通常不需要手動調整學習速率。在訓練期間,優化器使用損失的梯度來嘗試通過調整引數來減少模型輸出的誤差("優化")。只會優化我們在自定義分類器中新增的引數。

損失和優化器初始化如下:

別做空想家!學好PyTorch,你的物件識別專案穩了


通過預先訓練的模型,自定義分類器,損失,優化器以及最重要的資料,我們已準備好進行訓練。

訓練

PyTorch中的模型訓練比Keras中的實際操作多一些,因為我們必須自己進行反向傳播和引數更新步驟。主迴圈迭代多個時期,並且在每個時期迭代通過DataLoader。 DataLoader生成一批我們通過模型的資料和目標。在每個訓練批次之後,我們計算損失,相對於模型引數反向傳播損失的梯度,然後用優化器更新引數。

我建議你檢視筆記本上的完整訓練詳細資訊(https://github.com/WillKoehrsen/pytorch_challenge/blob/master/Transfer%20Learning%20in%20PyTorch.ipynb),但基本的虛擬碼如下:

別做空想家!學好PyTorch,你的物件識別專案穩了


我們可以繼續迭代資料,直到達到給定數量的時期。然而,這種方法的一個問題是,我們的模型最終將過度擬合訓練資料。為了防止這種情況,我們使用驗證資料並早期停止。

早期停止

早期停止(https://en.wikipedia.org/wiki/Early_stopping)意味著當驗證損失在許多時期沒有減少時停止訓練。在我們繼續訓練時,訓練損失只會減少,但驗證損失最終會達到最低限度並達到穩定水平或開始增加。理想情況下,當驗證損失最小時,我們希望停止訓練,希望此模型能夠最好地推廣到測試資料。當使用早期停止時,驗證損失減少的每個時期,我們儲存引數,以便我們以後可以檢索具有最佳驗證效能的那些。

我們通過在每個訓練時期結束時迭代驗證DataLoader來實現早期停止。我們計算驗證損失並將其與最低驗證損失進行比較。如果到目前為止損失最小,我們儲存模型。如果在一定數量的時期內損失沒有改善,我們停止訓練並返回已儲存到磁碟的最佳模型。

同樣,完整的程式碼在筆記本中,但虛擬碼是:

別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


要了解早期停止的好處,我們可以檢視顯示訓練和驗證損失和準確性的訓練曲線:

別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了

負對數似然和準確性訓練曲線

正如預期的那樣,隨著進一步的訓練,訓練損失只會繼續減少。另一方面,驗證損失達到最低和穩定的狀態。在某一時期,進一步訓練是沒有回報的(甚至是負回報)。我們的模型將僅開始記憶訓練資料,並且無法推廣到測試資料。

如果沒有早期停止,我們的模型將訓練超過必要的時間並且將過度訓練資料。

我們從訓練曲線中可以看到的另一點是我們的模型並沒有過度擬合。總是存在一些過度擬合,但是在第一個可訓練的完全連線層之後的退出可以防止訓練和驗證損失過多。

做出預測:推論

在筆記本中我處理了一些無聊但必要的儲存和載入PyTorch模型的細節,但在這裡我們將移動到最佳部分:對新影像進行預測。我們知道我們的模型在訓練甚至驗證資料方面做得很好,但最終的測試是它如何在一個前所未見的保持測試集上的執行。我們儲存了25%的資料,以確定我們的模型是否可以推廣到新資料。

使用訓練過的模型進行預測非常簡單。我們使用與訓練和驗證相同的語法:

別做空想家!學好PyTorch,你的物件識別專案穩了


我們概率的形狀是(batch_size,n_classes),因為我們有每個類的概率。我們可以通過找出每個示例的最高概率來找到準確性,並將它們與標籤進行比較:

別做空想家!學好PyTorch,你的物件識別專案穩了


在診斷用於物件識別的網路時(https://www.coursera.org/lecture/machine-learning/model-selection-and-train-validation-test-sets-QGKbr),檢視測試集的整體效能和單個預測會很有幫助。

模型結果

以下是模型的兩個預測:

別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了

這些都很簡單,所以我很高興模型沒有問題!

我們不僅僅想關注正確的預測,我們還將很快就會看到一些錯誤的輸出。現在讓我們評估整個測試集的效能。為此,我們希望迭代測試DataLoader並計算每個示例的損失和準確性。

用於物件識別的卷積神經網路通常根據topk精度(https://stats.stackexchange.com/questions/95391/what-is-the-definition-of-top-n-accuracy)來測量。這是指真實的類是否屬於k最可能預測的類中。例如,前5個準確度是5個最高概率預測中正確等級的百分比。你可以從PyTorch張量中獲取topk最可能的概率和類,如下所示:

別做空想家!學好PyTorch,你的物件識別專案穩了


在整個測試集上評估模型,我們計算指標:

別做空想家!學好PyTorch,你的物件識別專案穩了


這些與驗證資料中接近90%的top1精度相比是有利的。總的來說,我們得出結論,我們的預訓練模型能夠成功地將其知識從Imagenet轉移到我們較小的資料集。

模型調查

儘管該模型表現良好,但仍有可能採取一些步驟可以使其變得更好。通常,弄清楚如何改進模型的最佳方法是調查其錯誤(注意:這也是一種有效的自我改進方法。)

我們的模型不太適合識別鱷魚,所以我們來看看這個類別的一些測試預測:

別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


別做空想家!學好PyTorch,你的物件識別專案穩了


考慮到鱷魚和鱷魚頭之間的微妙區別,以及第二張影像的難度,我會說我們的模型在這些預測中並非完全不合理。影像識別的最終目標是超越人類的能力,我們的模型幾乎已經接近了!

最後,我們希望模型在具有更多影像的類別上表現更好,因此我們可以檢視給定類別中的準確度圖表與該類別中的訓練影像數量:

別做空想家!學好PyTorch,你的物件識別專案穩了


在訓練影像的數量和前一個測試精度之間似乎存在正相關關係。這表明更多的訓練資料增加是有所幫助的,或者我們應該對測試時間進行增加。我們還可以嘗試不同的預訓練模型,或者構建另一個自定義分類器。目前,深度學習仍然是一個經驗領域,這意味著經常需要實驗!

結論

雖然有更容易使用的深度學習庫,但PyTorch的優點是速度快,對模型架構/訓練的各個方面的控制好,能使張量自動區分的反向傳播,以及由於PyTorch圖的動態特性而易於除錯的程式碼。對於生產程式碼或你自己的專案,我不確定使用PyTorch而不是具有更溫和學習曲線的庫(例如Keras)還存在令人信服的論據,但知道如何使用不同選項會很有幫助。

通過這個專案,我們能夠看到使用PyTorch的基礎知識以及遷移學習的概念,這是一種有效的物件識別方法。我們可以使用已在大型資料集上進行過訓練的現有體系結構,然後根據我們的任務調整它們,而不是從頭開始訓練模型。這無疑減少了訓練的時間並且通常導致更好的整體效能。這個專案的成果是對遷移學習和PyTorch一些知識的應用,我們可以構建它來構建更復雜的應用程式。

我們確實生活在一個令人難以置信的深度學習時代,任何人都可以利用輕鬆可用的資源建立深度學習模型!現在是時候,通過構建自己的專案來更好的利用這些資源了。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31545819/viewspace-2222242/,如需轉載,請註明出處,否則將追究法律責任。

相關文章