點擊上方關注,All in AI中國
人類學習的一個基本特徵是我們可以同時學到很多東西。機器學習中的等效思想被稱為多任務學習(MTL),它在實踐中變得越來越有用,特別是對於強化學習和自然語言處理。事實上,即使在標準的單任務情況下,也可以設計額外的輔助任務並將其包含在優化過程中以幫助學習。
本文通過展示如何在圖像分類基準中解決簡單的多任務問題來介紹該領域。重點是TensorFlow(Head API)的一個實驗組件,它通過將神經網絡的共享組件與特定任務組件解耦,幫助設計MTL的自定義估算器。在這個過程中,我們有機會討論TensorFlow核心的其他功能,包括tf.data,tf.image和自定義估算器。
本教程的代碼作為完全包含的Colab筆記本提供,隨時可以測試和實驗!
(https://colab.research.google.com/drive/1NMB9lpi7P-GkkELkMU0h-yHtUq531D_Z)
內容一目瞭然
為了使教程更有趣,我們通過重新實現2014年論文的一部分(通過深度多任務學習進行面部特徵點檢測)來考慮一個現實的用例。問題很簡單:給我們一個面部圖像,我們需要定位一系列特徵點,即圖像上的興趣點(鼻子、左眼、嘴巴......)和標籤,包括人的年齡和性別。每個界標/標籤構成圖像上的單獨任務,並且任務之間明顯相關(即,想預測左眼的位置,需要先知道右邊的位置)。
來自數據集的示例圖像(源)。綠點是地標,每個圖像還與一些其他標籤相關聯,包括年齡和性別。
我們將實現分為三個部分:(i)加載圖像(使用tf.data和tf.image); (ii)從論文中實施卷積網絡(使用TF的自定義估計量); (iii)使用Head API添加MTL邏輯。
第0步 - 加載數據集
下載數據集(http://mmlab.ie.cuhk.edu.hk/projects/TCDCN/data/MTFL.zip)後,快速檢查,發現圖像分為三個不同的文件夾(AFLW,lfw_5590和net_7876)。通過不同的文本文件提供訓練和測試分割,每行對應一個圖像和標籤的路徑:
來自訓練數據集的第一個圖像和標籤。藍色數字是圖像位置(從左上角開始),紅色數字是類別(見下文)。
為簡單起見,我們將使用Pandas加載文本文件並調整Unix標準的路徑URL,例如:對於訓練部分:
由於文本文件不是很大,在這種情況下使用Pandas稍微容易一些,並且提供了一點靈活性。但是,對於較大的文件,更好的選擇是直接使用tf.data對象TextLineDataset。
第1步 - 使用tf.data和Dataset對象
現在有了數據,我們可以使用tf.data加載它以使其估算好!在最簡單的情況下,我們可以通過Pandas的DataFrame進行切片,就可以獲取我們的數據:
以前,將tf.data與Estimators一起使用的一個主要問題是調試數據集相當複雜,必須通過tf.Session對象。但是,從最新版本開始,即使在使用估算器時,也可以通過啟用即時執行來調試數據集。例如,我們可以使用數據集構建8個元素的批次,獲取第一批,並在屏幕上輸出所有內容:
現在是從路徑開始加載圖像的時候了!通常這不是一件容易的事,因為圖像可以有許多不同的擴展、大小,有些可以是黑白,等等。幸運的是,我們可以從TF教程中獲取靈感來構建一個簡單的函數來封裝所有這些邏輯,利用tf.image模塊中的工具:
該函數負責解決大多數解析問題:
- 'channels'參數允許在一行中加載彩色和黑白圖像;
- 我們將所有圖像調整為我們想要的格式(40x40,根據原始文件);
- 在第8行,我們還標準化了我們的標籤,以表示0和1之間的相對位置,而不是絕對的位置(因為我們調整了所有圖像的大小,圖像可能會有不同的形狀)。
我們可以使用其內部的“map”函數將解析函數應用於數據集的每個元素:將它與一些用於測試的額外邏輯放在一起,我們獲得最終的加載函數:
第2步 - 使用自定義估算器構建卷積網絡
下一步,我們想要複製原始論文中的卷積神經網絡(CNN):
CNN的邏輯由兩部分組成:第一部分是整個圖像的通用特徵提取器(在所有任務中共享),而對於每個任務,我們有一個單獨的、較小的模型作用於圖像的最終的特徵嵌入。由於以下原因,我們將這些簡單模型稱為“頭部”。通過梯度下降同時訓練所有頭部。
讓我們從特徵提取部分開始。為此,我們利用tf.layers對象構建我們的主網絡:
目前,我們將專注於單個頭/任務,即估計圖像中的鼻子位置。一種方法是使用自定義估算器,允許將我們自己的模型實現與標準Estimator對象的所有功能相結合。
自定義估算器的一個缺點是它們的代碼往往非常“冗長”,因為我們需要將估算器的整個邏輯(訓練、評估和預測)封裝到一個函數中:
粗略地說,模型函數接收到一個模式參數,我們可以使用它來區分我們期望做什麼類型的操作(例如,訓練)。反過來,模型函數通過另一個自定義對象EstimatorSpec與主Estimator對象交換所有信息:
這不僅使代碼難以閱讀,而且上面的大多數代碼都傾向於“樣板”代碼,這僅取決於我們面臨的具體任務,例如,使用迴歸問題的均方誤差。 Head API是一個實驗性功能,旨在簡化在這種情況下的編寫代碼,這是我們的下一個主題。
步驟3a - 使用Head API重寫我們的自定義估算器
Head API的想法是,一旦指定了幾個關鍵項,就可以自動生成主要預測組件(我們的模型函數):特徵提取部分、損失和我們的優化算法:
從某種意義上說,這與Keras的高級界面類似,但它仍然具有足夠的靈活性來定義一系列更有趣的頭部,我們很快就會看到。
現在,讓我們重寫前面的代碼,這次使用“regression head”:
從意圖和目的來看,這兩個模型是等效的,但後者更具可讀性並且更不容易出錯,因為大多數估計器特定的邏輯現在封裝在頭部內部。我們可以使用估算器的“訓練”界面訓練兩個模型中的任何一個,並開始得到我們的預測:
請不要將Head API(位於tf.contrib中)與tf.contrib.learn.head混淆,後者已棄用。
步驟3b - 多任務學習
我們最終得到了本教程中更有趣的部分:MTL邏輯。請記住,在最簡單的情況下,執行MTL相當於在同一個特徵提取部分的頂部具有“多個頭”,如下所示:
在數學上,我們可以通過最小化任務特定損失的總和來共同優化所有任務。例如,假設我們有迴歸部分的損失L1(每個地標的均方誤差)和分類部分的L2(不同的標記),我們可以通過梯度下降來最小化L = L1 + L2。
在這個(非常冗長的)介紹之後,您可能不會對Head API具有針對這種情況的特定頭部(稱為多頭)感到驚訝。根據我們之前的描述,它允許線性組合源自不同磁頭的多個損耗。在這一點上,我將讓代碼說明一切:
為簡單起見,我只考慮兩個任務:預測鼻子位置和麵部“姿勢”(左側輪廓,左側,前側,右側,右側輪廓)。我們只需要定義兩個單獨的頭(迴歸一個,分類一個),並將它們與multi_head對象組合。現在添加更多頭只是幾行代碼的問題!
為簡潔起見,我們在此處省略了對輸入功能的輕微修改:您可以在Colab筆記本上找到它。
此時的估算器可以使用標準方法進行訓練,我們可以同時獲得兩個預測:
閱讀更多 AI中國 的文章