我在Kaggle數海獅

NOAA Fisheries Steller Sea Lion Population Count

阿留申群島的海獅族群數量近年來大量縮減,NOAA科學家希望找到能快速計算海獅數量的方法,以利有效監控海獅族群的變化。

在這篇文章中我們將看到深度學習菜鳥outrunner,天真的拿Kaggle競賽"試刀"並取得冠軍的過程。所有成果應歸功於開源工具的普及與DL的強大威力。

這是一場【好吃、新奇、又好玩】的競賽

好吃:

  • 有獎金

新奇:

  • Kaggle少數沒有套路的競賽
  • 參賽者使用了多種不同的方法
  • Top隊伍間的分數差距不小

好玩:

  • 將深度學習的方法應用在生態領域,饒富意義
  • 對象是可愛的海獅使競賽過程充滿趣味
    • 參賽者:?逼逼,這幾隻海獅沒有標記?
    • 主辦方:?那是海豹?

競賽簡介、數據、與目標

競賽簡介:

  • 主辦單位:NOAA 美國國家海洋暨大氣總署
  • 競賽時間:2017/3/28 ~ 2017/6/27
  • 參賽隊伍:385 隊
  • 總獎金 :$25,000 (美金)
    • 第一名 $12,000
    • 第二名 $8,000
    • 第三名 $5,000

數據:

  • 照片尺寸:5760 x 3840 px 不等
  • 訓練集每張圖有0~1284隻海獅
  • 訓練集900+張圖
  • 測試集18000+張圖
  • 總共96GB

數據就是像這樣的航拍圖,資料量龐大,考驗硬體資源與處理技巧。

海獅分類用五種顏色做標記:

訓練集同一張圖提供標記與未標記的兩張圖,我使用Radu Stoicescu的blob detection kernel來取得標記海獅的類別與座標。

目標:

  • 分公母大小五類計算海獅數量
  • RMSE低者獲勝

LB Score是計算每種類別的Root Mean Squared Error (RMSE)後再取平均。而非平均每張圖的RMSE。前者在這場競賽會比後者來得大。在初步了解競賽內容後,我估計這場競賽最終的結果不會太好,RMSE低於15應該就能取得不錯的名次。

初步分析與構想

一開始的想法也是偵測與計數,但評估使用物件偵測的方法將會面臨重大挑戰。需額外花費大量時間做bbox不說,圖片中的海獅都不大,常常擠成一團。幼仔像石頭,其餘海獅又只靠大小做分類。況且我才剛在上一場辨識魚種競賽中學過SSD...(完全沒有勝算)

此外,在觀察一些圖片後,發現海獅分布有一定的規律性:

  • 幼仔必伴隨著母海獅,且海獅間有一定的距離
  • 青少年與母海獅會擠成一團
  • 大型的公海獅零星分布

因此我認為模型除了學習偵測海獅之外,也要能學會這些分布特徵

模型

做完上述評估後,我決定把困難的部分全部交給CNN處理。建一個可以輸入圖片並輸出海獅數量的模型:

就像是做multi-label的pattern recognition,並把labels換算成數量。(簡言之就是查表)

至於模型到底學到了什麼,說真的我不知道,這就是DL的精髓啊。

不過或許你會想問:

  • 為什麼是300x300?
    • 觀察訓練集的海獅密度,patch中的海獅不能太多也不能太少,一開始選了300。
    • 後來用200試了一下結果沒有比較好。
    • 300在大幅縮圖後遇到了些困難,patch中的海獅太多了,影響模型收斂與表現。
  • 為什麼用VGG16?
    • 前面提到我才剛看完SSD,且初學者的範例多用VGG,自然先試比較熟的網路。
    • 試過Inception V3,結果沒有比較好。(也可能是不熟或偷懶造成)
  • FC-1k夠不夠?為什麼不多幾層?
    • 直覺認為夠了,且VGG接FC太肥大。
    • 後來試過3k、4k,一樣沒得到什麼好處。
  • 為什麼用SGD?
    • 理由同VGG16。
    • 有試試其他方法,結果沒比較好。
  • 為什麼用Mean Squared Loss?
    • 常用。
    • 不過我懷疑青少年海獅減少的程度與這點也有關係,如果有機會我想試試MAE。
  • 為什麼這樣可行?
    • 在我看了物件偵測方法連"座標"都能輸出之後,就深信DNN沒有什麼不行的。

訓練

將訓練集切成300x300的小圖(patch)進行訓練,訓練標記來自使用前面提到取得座標的方法計算對應patch中的五種海獅數量。

由於大部分patch為背景,因此只隨機選擇3倍於出現海獅patch的數量做訓練。此外,為了應付圖片尺寸的變異,也對訓練集做多種比例[1, 0.9, 0.81, 0.73, 0.66]的縮圖。

訓練patch數量與配置:

  • 1x Positive (lion in patch) + 3x Background
  • 57K / 230K patches without / with scale augmentation
  • 95% patches for training, 5% for validation

至於這些參數的原因?哈,你們懂的。

數據增強:(訓練時隨機處理)

  • 旋轉(90度)、水平、垂直翻轉。
  • 亮度、對比、飽和變化。

另外有試過顏色變異。顏色是海獅的重要特徵,但模型也因此誤判了棕色水草、岩石、漂流木等。加入顏色變異後,誤判變少了,但海獅偵測也變差,總體成績下降,因此最後未採用。

訓練使用一張1080,省吃儉用開兩個process,也就是一個只用4GB的ram,免得訓練時啥都不能做。前後約花了5~10天才把模型訓練好。

這裡有個簡易版的kernel提供參考。

數據分析、修正與驗證

我用海獅數量代表patch亮度來粗估整體表現:

模型跑完,開心提交,想說LB Score起碼有15吧。結果...20,只好來看看發生什麼事:

(這裡的首次提交是指模型訓練完之後的提交)

首先,為了取得可參考的分類數量,將訓練集數量乘0.75,取得跟測試集一致的RMS(29)。比對後發現除了RMS偏低(代表總體偵測數量偏低)之外,最大的問題在於青少年。此外,事後發現公海獅數量偏高也出自於相同的問題。

事後回想,如果有經驗夠敏銳,此時就該想到是圖片尺度的問題。好吧,我有一點點懷疑,但當時覺得主辦方會這樣搞的機會不大。我想很多參賽者到最後都沒處理這個問題。想到柯南說過:?除去掉所有不可能的因素,留下來的東西,不論你多麼不願意去相信,但他就是真相!?

調整數據

為了快速驗證猜想,我增加了青少年的數量後再次提交,結果成績真的進步了,接著增加母海獅,成績反而退步。最好的結果來自增加青少年與幼仔,並減少母海獅。此時Public Score來到18,大幅進步也代表這些結果不是出於隨機。

實驗顯示成績不如預期並非完全因為整體數量偏低,一定有其他重要因素導致母海獅(相對)偏多,青少年偏少。此外,驗證集的好表現讓我相信模型本身沒有問題,此時開始懷疑測試集。

偷看考卷

束手無策之下只好拿測試集出來看看啦。看了幾張後赫然發現,測試集的海獅好像都比較大。因為真的大很多(有些有兩倍大)所以不難看出來。這下好啦,之前的數量偏差都有了根據,不管是偏多的公海獅或消失的青少年都是因為海獅"變大"了。趕緊縮圖試試,結果成績躍昇,Public Score一路來到13.7,此時第二名的Score還在17以上。

開心之餘也面臨了新的問題:

  • 要縮多少?
  • 每張尺度都不一樣怎麼辦?
  • 事後調整為什麼會變好?

關於尺度的問題,曾經想過用物件偵測(一開始就摒棄的方法)的結果來估計圖片尺度。但評估之後無法確定是否會得到更好的結果且需投入資源太大而作罷。這個方法看到幾位參賽者有實做,但我不確定他們是否有做過與單一尺度縮圖最佳成績的比較。

在此同時,我做了訓練集的尺度增強,結果成績僅進步約1分。我想是因為原始訓練集的尺度就有一定的變異性。失望之餘,也讓我懷疑所謂的尺度不變性的特徵是否存在。

之後,我做了尺度與數量關係的實驗:(來自某張含有壅擠海獅群的測試圖的預測結果)

幾個發現:

  1. 圖片越小母海獅越少、青少年越多
  2. 圖片縮到一個程度海獅數量開始下降
  3. 在一個範圍內母海獅與青少年總數為定值

第一點符合預期。

第二點,我認為是超出訓練集的尺度範圍(反之亦然),這也讓我最終選擇了相對其他參賽者較少的縮圖比例,並搭配事後補償來增加青少年數量。

第三點,雖然顯而易見(當時沒這欄),但我花了很長的時間才領悟出來。在此之前,母海獅與青少年是分開調整的,這會導致在某些場景中(例如母海獅+幼仔的組合)不該減少的母海獅被誤減了,改進這點又讓分數進步1分。

最終,我還是想知道為什麼會變好。

我分析了patch中青少年數量與母海獅比例的分布情況:

  • 表中數值 = 青少年 / (青少年+母海獅)
  • r0.xx = 縮圖比例
  • *1.x = 青少年、母海獅調整比例 (可參考下面流程圖)

可以看出青少年數量越多,所佔的比例也越大。但未經調整的測試集的結果與訓練集差很多。調整後得到的比例越一致,分數也越好。

整個過程可以說是用結果找原因,也符合我對大部分DL文章的感想。(注意,這說法不代表他們的因果關係是正確的)

數海獅流程:

其他工法

像是ensemble或TTA(test time augmentation)我都有試過,基本上幫助有限。冠軍提交有對原圖做四個角度翻轉的TTA,印象中跟不做差了0.1分左右。測試集predict一次就要七小時,四倍就超過一天了。

競賽歷程

簡單來說就是一路大幅領先到終場

四月中參賽,模型邊弄邊學,學習如何讓模型收斂,一直到五月初才訓練好。五月中,討論區出現圖片尺度相關的討論,許多人的成績也開始大幅進步,我想就是縮圖與否的關係吧。我猜主辦方會這樣搞,也是因為他們遇到了尺度的問題,想藉由競賽找出好的解決方法。當然最好的方法就是在圖片中記錄拍攝資訊,例如高度、視角等。

最終第四名的隊伍在比賽結束前(排名第二)改了隊名,也許預言了他們對最終成績的想法。

提交選擇策略

由於領先幅度充裕,我選擇了一個簡單且成績也不錯的單一模型, 方便得獎後繳交作業。另一個我選擇相對保守的做法,確保如果private跟public尺度差異大的時候(如果真的發生這事,文章又要多打幾百字...)能讓損失降到最低。此外我也對分數變異做了模擬。比賽結束前,Konstantin Lopuhin(2nd)也在討論區回答關於LB shake的提問,他認為最終變化不會超過0.7分,他也是這場競賽討論貢獻度最高的參賽者。我的看法跟他差不多,當然前提是private/public是隨機分配。

後期的提交大多是為了準備報告與自我突破。最後我弄了個Public LB破11的單模提交,也是本次Private最好的成績,可惜沒能突破10。

總結

用簡單的方法贏得比賽:

  • 模型含海獅分布特徵
  • 發現並處理測試集尺度不匹配的問題
  • 事後調整海獅數量

我想我總是喜歡把事情做得簡單。最後附上網路上看到最符合我心境的評論:

「While everyone tried object detection/segmentation, winner is simple VGG16 regressor that directly outputs sea lion counts from raw images.」 (hardmaru@twitter)

別忘了海豹

競賽中出現的海豹很像海獅幼仔,我的模型在某些條件下處理的不好,例如在偏紅的圖中沙灘上的海豹群。關於這點最終我只對幼仔與母海獅的比例設下門檻,門檻值使用訓練集中的最大值(約1.7)。也就是如果圖片預測結果為10母30幼,直接修正為10母17幼。此外,訓練集中的海豹可能是導致幼仔預測數量偏低的原因之一。

最後

我很享受這次的競賽過程,希望你們也會喜歡。

歡迎非商業用途轉載、或幫忙翻譯。(主辦方似乎需要一個英文版本QQ)

檢討與補充說明

(最後更新:9/11)

我個人覺得粗暴地修改海獅比例這一點並不是特別好,有點去擬合測試集的感覺,模型的泛化能力可能會降低。 @RogerYu

早在五月初,成績取得大幅領先開始,到比賽結束前約兩個月的時間,我一直在處理這個問題。我覺得可以分幾個層面來看:

初級失誤

在我的解法中,我很貪心的把所有訓練集都切成小片來用,訓練與驗證都在patch level(僅在初期有保留部分完整圖片確認此方法不會過擬合,隨即就全部切碎了)。之後做的全圖驗證只做了shift讓切出來的patch不會與訓練集重疊,但還是過於相似,導致測試集發生的問題在這裡並不明顯,青少年短少現象僅在驗證集的patch有反應,但沒有測試集全圖結果來得強烈。

Fit LB

我承認我有刻意突顯Fit LB這件事。原因...哈,保留一下。

基本上有兩種情況我會做Fit LB:

  1. 測試集資料量遠大於訓練集
  2. 測試集與訓練集不匹配

關於第一點,數海獅在測試集為了防欺騙,我(用了一些方法,但沒用在訓練)估計主辦方做了18~20倍的數據增強,因此有效樣本數跟訓練集差不多。

此次Fit LB是因為第二點。關於這點我想問的是,在這個層級的競賽中,出現測試集與訓練集不匹配的狀況是否正常?是增加競賽難度還是摧毀了競賽本身?

改輸出

當然,這點也被我刻意強調。當預測結果出現偏差,一般的做法有:

  • 調整訓練樣本(比例...)
  • 調整模型(參數)
  • 調整損失函數
  • 調整輸出門檻
  • 任何其他看起來有學問的方法

我覺得在某些條件下,特別是當你理解之間的關係,做這些調整跟直接改輸出是等價的。關於這點,我做了大量的尺度強化與樣本比重的實驗。此外我認為損失函數也是導因之一。最後,改輸出並非只為了擬合測試集,而是我的解法的一個步驟。

比賽結束後,4th、5th都試了這個這顆?撒尿牛丸?:

  • "Applying further postprocessing as suggested by @outrunner, could improve results. Just increasing the number of pups by 20% gives a huge improvement. (12.7/12.5)" ~@4th
  • "Applying the same postprocessing as used by outrunner has improved the public/private score to 13.1/12.2" ~@5th

也就是?分分鐘帶你進入獎金池?的具體實現。

特化、泛化

我認為競賽或解決特定問題就是找特化解。如果題目是要在多種情況下都要表現很好,那也是在這個前提下找特化。例如比百米不會去練馬拉松,比十項就不能只練百米,但也不會去練跳水。

泛化只拿來對付不確定性,例如我提交的第二選擇。我有把握在private與public不匹配的時候會更加有趣(例如private跟train尺度相同,其他做縮圖一倍的領先者就...)。


推薦閱讀:

Zillow Prize競賽系列--(一)競賽簡介
Kaggle 的比賽在 Machine Learning 領域中屬於什麼地位?
2016 CCF大數據與計算智能大賽的開源資料整理
kaggle:Titanic: Machine Learning from Disaster,有什麼比較好的feature可以提取,哪位大神hit 80%了?

TAG:Kaggle | 机器学习 | 深度学习DeepLearning |