當前位置:
首頁 > 最新 > 手把手教你用Keras進行多標籤分類

手把手教你用Keras進行多標籤分類

作者:Adrian Rosebrock

翻譯:程思衍

校對:付宇帥

本文約7000字,建議閱讀10+分鐘。

本文將通過拆解SmallVGGNet的架構及代碼實例來講解如何運用Keras進行多標籤分類。

本文的靈感來源於我收到的一封來自PyImageSearch的讀者Switaj的郵件。

他寫到:

你好,Adrian,感謝PyImageSearch,感謝你每周都分享你的知識。我正在構建一個時尚圖像的搜索引擎,我需要你的幫助。使用我的app,用戶可以上傳一張他們喜歡的服飾的圖片(如襯衫、裙子、褲子、鞋類),我的系統將會返回相似的物品並附上購買鏈接。

問題是我需要訓練一個分類器來將物品分到不同的類別中:

服飾類別:襯衫、裙子、褲子、鞋類等

顏色:紅、藍、黑等

質地:棉、羊毛、絲、麻等

我已經為這三個不同的類別訓練了三個不同的卷積神經網路(下文將稱為CNN),效果非常好。

是否有辦法讓這三個CNN合併為一個CNN呢?或者至少訓練一個神經網路來完成三項分類任務?

我不想在if / else代碼的級聯中單獨應用它們,這些代碼使用不同的網路,具體取決於先前分類的輸出。

謝謝你的幫助

Switaj提出了一個美妙的問題:

Keras深度神經網路是否有可能返回多個預測?

如果可以,它是如何完成的?


本文將分為4個部分。

在第一部分,我將討論我們的多標籤分類數據集(以及如何快速構建屬於你自己的數據集)。

之後我們將簡要討論SmallerVGGNet,它是我們將要實現的一個用於多標籤分類的Keras神經網路結構。

緊接著我們將構建SmallerVGGNet並應用我們的多標籤分類數據集來訓練他。

最後,我們將基於樣例圖片測試我們的神經網路,並討論何時使用多標籤分類問題最為合適,包括您需要注意的一些注意事項。

我們的多標籤分類數據集

圖片1:一份多類別深度學習數據集的組合。我們將使用Keras來訓練一個多標籤分類器來預測衣服的顏色以及類別。

我們將使用的數據集以用於今天的Keras多標籤分類教程旨在模仿本文先前提到的Switaj』s的問題(儘管我們基於本文對它進行了簡化)。

我們的數據集由2167張圖片組成,它們來自6個不同的種類,包括:

黑色牛仔褲(344張圖片)

藍色裙子(386張圖片)

藍色牛仔褲(356張圖片)

藍色襯衫(369張圖片)

紅色裙子(380張圖片)

紅色襯衫(332張圖片)

我們的卷積神經網路的目標是同時預測顏色和服飾類別。

我構建該數據集通過遵循我之前發布過的博文:

How to (quickly) build a deep learning image dataset

下載圖片以及手動為該6個類別剔除不相關圖片的整個過程將耗費大概30分鐘。

當你在嘗試構建你自己的深度學習數據集時,請確保你遵循了上述教程鏈接——它將幫助你快速啟動構建你自己的數據集。


請直接訪問本文的「下載」處以獲得源代碼及文件。一旦你解壓縮了zip文件,你將會看到如下的目錄結構:

在該zip文件的根目錄下,你會看到6個文件及3個文件夾。

我們將用到的重要文件(基於它們本文出現的大致順序)包括:

search_bing_api.py:此腳本使我們能夠快速構建深度學習圖像數據集。你不需要運行這段腳本因為圖片數據集已經囊括在zip文件中。我附上這段腳本僅為保證(代碼的)完整性。

train.py:一旦我們擁有了數據,我們將應用train.py訓練我們的分類器。

fashion.model:我們的train.py腳本將會將我們的Keras模型保存到磁碟中。我們將在之後的classify.py腳本中用到它。

mlb.pickle:一個由train.py創建的scikit-learn MultiLabelBinarizer pickle文件——該文件以順序數據結構存儲了各類別名稱。

plot.png:訓練腳本會生成一個名為plot.png的圖片文件。如果你在你自己的數據集上訓練,你便需要查看這張圖片以獲得正確率/風險函數損失及過擬合情況。

classify.py:為了測試我們的分類器,我寫了classify.py。在你將模型部署於其他地方(如一個iphone的深度學習app或是樹莓派深度學習項目)之前,你應該始終在本地測試你的分類器。

本項目中的三個文件夾為:

dataset:該文件夾包含了我們的圖片數據集。每個類別擁有它自己的子文件夾。我們這樣做以保證(1)我們的數據在結構上工整有序(2)在給定圖片路徑後能更容易地提取類別標籤名稱。

pyimagesearch:這是裝有我們的Keras神經網路的模塊。由於這是一個模塊,它包含了固定格式的__init__.py。另外一個文件smallervggnet.py,它包含組裝神經網路本身的代碼。

examples:該文件夾包含了7個樣例圖片。我們將基於keras,應用classify.py對每一個樣例圖片執行多標籤分類。

如果這些(文件)看起來太多了,別擔心!我們將會按照我所提出的順序逐個回顧。

圖片2:被我稱為「SmallerVGGNet」的類VGGNet神經網路,我們將用它基於Keras訓練一個多標籤深度學習分類器

本教程中我所用到的CNN架構是SmallerVGGNet,一個簡化版本的VGGNet。VGGNet最先由Simonyan和Zisserman在他們2014年的論文:Very Deep Convolutional Networks for Large Scale Image Recognition中提出。

為了完整性,我們將在本教程中實現SmallerVGGNet;然而,我將有關架構/代碼的長篇大論轉移至我之前的一篇文章——如果你有任何關於架構的問題或是想要了解更多細節,請參閱參閱該文章。如果你希望設計你自己的模型,你會希望有一本我的書:Deep Learning for Computer Vision with Python。

請確保你通過文末的「下載」處獲得了源代碼和樣例圖片。獲得之後,打開在pyimagesearch模塊下的smallervggnet.py文件, 繼續:

從第二至第十行代碼,我們引入了Keras模塊並於此開始建立我們的SmallerVGGNet類:

我們的SmallerVGGNet類從第12行開始被定義,隨後我們在第14行定義構建函數,用於組裝卷積神經網路。

該構建方法規定需要4個參數——width,height,depth以及classes;width指定一張輸入圖片的通道(channels)數量,classes是種類(並不是他們所屬的標籤)數量(整數)。我們將在我們的訓練腳本中應用這些參數來舉例說明輸入規格為96×96×3的模型。

可選參數finalAct(默認值為「softmax」)將會在神經網路底部被應用。將這個值由softmax改為sigmoid將允許我們基於Keras執行多標籤分類。

請記住這個行為與我們在之前文章中實現的SmallerVGGNet不同——我們在這裡加入是為了控制執行簡單二分類或者是多類分類。

之後,我們輸入build的程序主體,初始化模型(第17行)並在第18行至第19行將架構默認值設置為「channels_last」(方便切換為支持「channels_first」,實現於第23-25行)。

讓我們構建第一個CONV ==> RELU ==> POOL模塊:

我們的CONV層擁有32個卷積核大小為3×3的濾波器以及RELU(Rectified Linear Unit)激活函數。我們在這之後使用批標準化,最大池化,以及25%的遺忘率(Dropout)。

Dropout是一個隨機切斷當前神經網路層節點與下一神經網路層節點間鏈接的過程。這個隨機斷開的過程自然地幫助神經網路降低了過擬合的可能性,得益於沒有任何一個節點會被分配以預測某個特定的類別、對象、邊緣或是角落。

緊接著我們有兩組(CONV ==> RELU)*2 ==> POOL模塊:

請注意本模塊中過濾器、卷積核以及池化大小的變化,這些變化將會共同運作從而逐漸減少空間大小但提升深度(depth)。

這些模塊之後是我們唯一的FC ==> RELU層:

全連接層被放置在神經網路的最末端(在第57-64行由Dense聲明)。

第65行對於我們的多標籤分類非常重要——finalAct指明我們使用的是針對於單標籤分類的「softmax」激活函數,還是針對於今天我們提出的多標籤分類的sigmoid激活函數。請參考本腳本smallervggnet.py的第14行以及train.py的第95行。


既然我們已經實現了SmallerVGGNet,接下來讓我們創建train.py,我們用於訓練多標籤Keras神經網路的腳本。

我強烈建議你重溫一下先前的博文,今天的train.py腳本便是基於該文章。實際上你可能會想要在屏幕上並行查看它們以觀測它們之間區別並閱讀關於代碼的詳細解釋。今天回顧將簡潔明了。

打開train.py並插入下述代碼:

在第2至第19行,我們導入了該腳本所需要的包和模塊。第三行指定了一個matplotlib後端,基於此我們可以在後台保存我們所繪製的圖片。

我將假定你已經安裝了Keras,scikit-learn,matplotlib,imutils以及OpenCV。

如果這是你的深度學習首秀,你有兩個選擇來確保你擁有正確的庫和包:

已配置好的環境(你將在5分鐘內準備就緒並執行代碼,訓練今天的這個神經網路的花費將少於一杯星巴克咖啡的價格)。

建立你自己的環境(需要時間,耐性以及持久性)。

我更喜歡在雲端預先配置好的環境,你能夠在雲上啟動、上傳文件、訓練+獲取數據以及在幾分鐘之內終止程序。我推薦的兩個預先配置好的環境:

使用Python預配置的Amazon AWS深度學習AMI

用於深度學習的Microsoft數據科學虛擬機(DSVM)

如果你堅持要建立你自己的環境(而且你有時間來調試及問題修復),我建議你遵循下列博文中的一個:

使用Python配置Ubuntu進行深度學習(僅限CPU)

使用Python(GPU和CPU)設置Ubuntu 16.04 + CUDA + GPU進行深度學習

使用Python,TensorFlow和Keras進行深度學習的macOS

既然你的環境已經準備就緒,而且你已經導入了相關包,那麼讓我們解析命令行參數:

命令行參數之於腳本猶如參數之於函數——如果你不理解這個類比,你需要參閱命令行參數。

我們今天將會處理4個命令行參數:

--dataset:輸入的數據集路徑。

--model:輸出的Keras序列模型路徑。

--labelbin:輸出的多標籤二值化對象路徑。

--plot:輸出的訓練損失及正確率圖像路徑。

如果你需要關於這些參數的結束,請務必參閱之前的博文。

讓我們進一步討論一些在我們訓練過程中起到至關重要的作用的參數:

在第35-38行的這些參數定義了:

我們的神經網路將會訓練75輪(epoch),通過反向傳播不斷提升模型表現從而學習數據背後的模式。

我們設置初始學習率為1e-3(Adam優化器的默認值)。

Batch size是32。如果你擁有GPU,你應該根據你的GPU能力調整這個值,但我發現設置batch size為32能使這個項目執行的非常好。

如之前所言,我們的圖片大小是96×96並包含3個通道。

之前的博文提供了更多細節。

緊接著,接下來的兩個代碼模塊用於載入及預處理我們的訓練數據:

在這裡我們獲取imagePaths並將它們的順序隨機打亂,隨後初始化data和labels數組。

然後我們將循環遍歷imagePaths,預處理圖像數據並解析多類標籤。

譯者註:該代碼的行號出現問題

首先我們將每張圖片載入至內存。其次,我們在第54和第55行代碼執行預處理(深度學習流水線中的重要一環)。我們將image添加在data的末尾。

第60和第61行針對我們的多標籤分類問題將圖片路徑切分為多個標籤。在第60的代碼執行之後,一個擁有2個元素的數組被創建,隨後在第61行中被添加至labels數據中。如下是一個在終端中經過分解的例子,你能從中了解多標籤分詞的過程:

如你所見,labels數組是一個「包含數組的數組」——labels中的每個元素都是一個包含兩個元素的數組。每個數組對應兩個標籤這種架構是基於輸入圖片的文件路徑構建的。

我們仍未完成預處理:

我們的data數據由利用Numpy數組存儲的圖片組成。在每一行代碼中,我們將Python數組轉換為Numpy數組並將像素值縮放於範圍 [0,1] 之中。

我們也將標籤轉換為Numpy數組。

隨後,然我們將標籤二值化——下述模塊對於本周的多類分類概念十分重要:

為了能夠針對多類分類將標籤二值化,我們需要運用scikit-learn庫中的MultiLabelBinarizer類。你不能在多類分類問題上用標準的LabelBinarizer類。

第72和第73行代碼將人可讀的標籤轉換為包含各類對應編碼的向量,該向量根據某類是否在圖片中出現來決定對應類的具體值。

這裡是一個展現MultiLabelBinarizer如何將(「red」,「dress」)元組轉換為一個有6個類別的向量的例子:

One-hot編碼將分類標籤由一個整數轉換為一個向量。同樣的概念可以應用在第16和第17行代碼上,除非這是一個two-hot編碼。

請注意在Python命令行(為了不與train.py中的代碼塊混淆)中的第17行,有兩個分類標籤是「hot」(在數組中用一個「1」表示),表明這各個標籤的出現。在本例中,「dress」和「red」在數組中是「hot」(第14至第17行)。其他所有標籤的值為「0」。

我們將數據分為訓練集和測試集並初始化數據增強器。

在機器學習實戰中,將數據分為訓練和測試集是一種很常見的做法——我把80%的圖片分配為訓練數據,20%為測試數據。這一過程在第81和82行中由scikit-learn進行處理。

我們的數據增強器對象在第85至第87中被初始化。當你的沒類數據少於1000張圖像時,數據增強是一個最好的實踐也或許是一個「必須」的實踐。

接下來,讓我們建立模型並初始化Adam優化器:

在第92至第95行中,我們構建SmallerVGGNet模型,finalAct=」sigmoid」這個參數指明我們將執行多標籤分類。

隨後,我們將編譯模型並開始訓練(取決於你的硬體,這可能會需要一段時間):

在第105行和第106行中,我們編譯模型並使用二元交叉熵而不是類別交叉熵。

對於多標籤分類問題,這可能看起來有些違背了直覺;然而,目標是將每個輸出標籤視作一個獨立伯努利分布,而且我們需要獨立地懲罰每個輸出節點。

隨後我們啟動運用了數據增強生成器的訓練過程(第110-114行)。

在完成訓練之後我們可以將模型和標籤二值化器儲存至磁碟:

隨後,我們繪製正確率及損失:

訓練和驗證的準確率+損失在第127-137行代碼中繪畫。該圖片在第138行中被保存為一個圖片文件。

在我看來,訓練圖像的繪製就跟模型本身一樣重要。在我們滿意並在博客上與你們分享之前,我通常會執行訓練的幾個迭代周期並查看圖像是否無誤。

在迭代過程中我喜歡講圖片存至硬碟上出於幾個原因:

我在一個無界面的後台伺服器上運行代碼,也並不想依賴於X-forwarding

我不想忘記保存圖片(即使我正在使用X-forwarding或是我正使用一個擁有圖形化界面的機器)。

回想我們在上面將腳本的第三行改變了matplotlib的後端,就是為了幫助我們將圖片儲存至硬碟上。


請不要忘了使用本文底下的「下載」處來下載代碼、數據集和預先訓練好的模型(以防你不想自己訓練模型)。

如果你想要自己訓練模型,請打開終端。在那裡,打開項目路徑並執行如下命令:

如你所見,我們將模型訓練了75個epoch,實現了:

98.57% 訓練集上的多標籤分類正確率

98.42% 測試集上的多標籤分類正確率

訓練圖在圖3中展示:

圖3:我們的Keras深度學習多標籤分類在訓練集和測試集中的正確率/損失。


既然我們的多標籤分類Keras模型已經訓練好了,讓我們將它應用在測試集之外的圖片上。

這段腳本和我之前的博文中的classify.py中的十分相似,請務必小心多標籤的區別。

當你準備好了以後,在項目路徑下創建一個新的名為classify.py的文件並加入如下代碼(或是遵循「下載」處所包含的文件)。

在第2-9行中,我們導入本腳本需要的包。尤其是Keras和OpenCV,我們在這個腳本中將用到它們。

隨後在第12-19行,我們進一步將三個要求的命令行參數分詞。

在這之後,我們載入並預處理輸入圖片:

我們使用與訓練數據相同的同一標準小心預處理圖片。

隨後,讓我們載入模型+多標籤二值化器並將圖片分類:

我們在第34-35行代碼中,從磁碟將模型和多標籤二值化器載入至內存中。

隨後我們分類(經過預處理的)圖片(第40行)並通過如下方式解析出相關性最大的前兩個類的標籤索引:

基於相關概率將數組索引按降序排序

獲取前兩個類標籤的索引,這便是我們的神經網路所作出的最好的兩個預測。

如你需要,你可以修改這段代碼以返回更多的類標籤。我也建議你對概率設置閾值,並且只返回那些置信程度 > N%的標籤。

然後我們將對每一個輸出圖像準備類標籤+相關的置信值。

第44-48行的循環將可能性最大的兩個多標籤預測及相應的置信值繪製在輸出圖片上。

相似地,第51和第52行代碼將所有的預測列印在終端上。這對於調試過程非常有用。

最後,我們在屏幕上顯示輸出圖片(第55行和第56行代碼)。


讓我們用命令行參數將classify.py執行。你不用為了傳遞新圖片經由CNN而修改上述代碼。你只需要按照下述步驟在終端中應用命令行參數。

讓我們來試一張紅色裙子的圖片——請注意在運行過程中被處理的三個命令行參數:

圖片4:這張紅色裙子的照片被我們的Keras多標籤分類深度學習腳本由分類器正確分為「紅色」和「裙子」。

我們成功了!請注意這兩個類(「紅色」和「裙子」)是如何被標註為高置信程度的。

現在讓我們來試一條藍色裙子:

圖片5:「藍色」和「裙子」類標籤在我們的Keras多標籤圖片分類項目的第二次測試中正確給出。

這條藍色裙子對我們的分類器來說並不是什麼難事。我們有了一個好的開端,讓我們來試一張紅色T恤:

圖片6:在100%的置信把握下,我們的深度學習多標籤分類腳本正確分類了這件紅色Polo衫。

紅色Polo衫的結果很棒。

如果是一件藍色襯衫又會是怎樣呢?

圖片7:深度學習+多標籤+Keras分類正確計算出了一件藍色襯衫。

我們的模型非常確信它看到了藍色,但有些許不自信它碰到了一件襯衫。話雖這麼說,這仍然是一個正確的多標籤分類!

讓我們看看我們是否能夠用一條藍色牛仔褲騙過我們多標籤的分類器:

圖片8:該深度學習多標籤分類結果證明了這條牛仔褲可以同時被正確的分類為「藍色」和「牛仔褲」。

讓我們試試黑色牛仔褲:

圖片9:在該Keras深度學習多標籤分類實驗中,「牛仔褲」和「黑色」這兩個標籤都正確了。

我沒法100%保證這是一條牛仔褲(在我看來他們更像是打底褲/緊身牛仔褲),但是我們的多標籤確是能100%保證!

讓我們最後來是一條黑色裙子(example_07.jpg)。當我們的神經網路已經學會如何預測「黑色牛仔褲」、「藍色牛仔褲」、「藍色裙子」和「紅色裙子」時,它是否也能夠被用來分類一條「黑色裙子」?

圖片10:在這裡發生了什麼?我們的多類標籤出錯了。顏色被標註為「黑色」但是比起說這張圖是一張「裙子」的圖片,我們的分類器擁有更高的信心說這張圖是一張「牛仔褲」的圖片。

這是因為我們的神經網路在訓練集中從來沒有看見過這樣的組合。請看底下的「總結」部分以獲得更詳盡的解釋。

噢不——我們的分類器犯了個大錯!我們的分類器報告說該模特身著黑色牛仔褲然而她實際穿著的黑色裙子。

在這裡發生了什麼?

為什麼我們的多類預測出錯了?想要知道原因的話,請檢閱底下的總結。


在今天的博文中,你學會了如何用Keras執行多標籤分類。

應用Keras執行多標籤分類是直觀的,它包含兩個主要的步驟:

在神經網路的最末端將softmax激活函數改為sigmoid激活函數。

將損失函數由分類交叉熵替換為二元交叉熵。

隨後你便可以按平時的方法來訓練該神經網路。

應用上述過程的最終結果是一個多類分類器。

你可以應用你的Keras多類分類器來預測多重標籤,該過程僅需要一次的數據傳遞。

然而,你也需要考慮一些難點:

你需要你想要預測的每種類別組合的訓練數據。

正像是一個神經網路沒法預測出一個它未曾訓練過的類,你的神經網路沒法預測出它未曾見過的多類標籤組合。這個特性是由於神經網路內部神經元的激活函數。

如果你的神經網路同時經過:(1)黑色褲子(2)紅色襯衫的訓練,現在你希望預測「紅色褲子」(你的數據集中沒有「紅色褲子」的圖片),用於檢測「紅色」和「褲子」的神經元將會發生變化,但由於神經網路在之前從沒見過這些數據/激活的組合,一旦他們到達了全連接層,你的輸出預測就很有可能出錯了(例如你可能會見到「紅色」或者是「褲子」,但不太可能會同時見到這兩個)。

再次重申,你的神經網路不能預測出它之前未曾訓練過的數據(而且你也不該期望它能夠預測出)。當你在訓練你自己的多標籤分類Keras神經網路時,請牢記這一點。

我希望你喜歡這篇博文!

https://www.pyimagesearch.com/2018/05/07/multi-label-classification-with-keras/

原文標題:

Multilabel Classification with Keras

譯者簡介

程思衍,本科畢業於北航計算機系,美國南加州大學 Data Informatics 專業在讀碩士。曾進行過問答系統、機器學習預測、高並發等方面的開發,目前主要從事問答領域的學習和研究。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 數據派THU 的精彩文章:

帶你用深度學習虛擬機進行文本遷移學習
機器能否擁有像人類一樣的意識?Science長文綜述解讀

TAG:數據派THU |