WiDS資料馬拉松由女性資料科學工作者與她們的夥伴聯合發起,她們面臨的挑戰是需要建立一個模型,來預測一批衛星影像中存在油棕人工林種植園的情況。
資料集是稱為“行星”的人造衛星新近拍攝的經加註後的衛星影像資料集,該影像資料集具有3米的空間解析度,每幅影像都是基於影像中是否存在油棕種植園進行標記的(0表示沒有油棕種植園,1表示有油棕種植園)。任務是需要訓練一個模型,該模型以衛星影像為輸入,並輸出對油棕種植園的影像預測的機率。競賽建立者為模型開發提供了經過標記後的訓練和測試資料集。
詳情見這裡:https://www.kaggle.com/c/widsdatathon2019
我的隊友(Abundshakur,Halimah,和IfeomaOkoh)和我採用了fast.ai框架來應對這一挑戰。感謝托馬斯·卡貝爾(ThomasCapelle)提供了Kaggle上的入門核心,它給出瞭如何處理這個問題的深刻見解,同時也為fast.ai團隊創造了一門神奇的深度學習課程,從而簡化了許多複雜的深度學習的概念。現在,深度學習的初學者也可以贏得kaggle比賽了。
我們從一個簡單易學的關於深度學習的教程開始吧
目前,無需為理解所有的東西而擔心,文中會伴隨著大量的練習。本教程旨在展示fast.ai對於深度學習的初學者來說的神奇之處,前提是假設讀者會python語言,並且接觸過一些ML(機器學習)。如果你已經具備上述技能,那我們就可以走上正軌了。
文中的所有程式碼都可以在Google Colaboratory(https://colab.research.google.com/notebooks/welcome.ipynb)上獲得,這是一個免費的Jupyter筆記本環境,無需安裝,執行在雲上。可以採用協作的方式編寫和執行程式碼,存檔和共享分析結果,訪問功能強大的計算資源,所有這些都是免費的。點選這裡可以訪問到我們將要使用到的程式碼。
匯入fast.ai和將要用到的其他庫
# This ensures that any edits to libraries you make are reloaded here automatically,
# and also that any charts or images displayed are shown in this notebook.
%reload_ext autoreload
%autoreload 2
%matplotlib inline匯入庫
# Import libraries
from fastai import *
from fastai.vision import *
from fastai.callbacks import CSVLogger, SaveModelCallback
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
獲取競賽資料
為了儘可能地直觀,Abdishakur將競賽資料檔案上傳到dropbox.com,可以在這個競賽頁面上找到競賽資料檔案,而且只有接受競爭規則之後才能訪問到這些資料檔案。
# Get the data from dropbox link
!wget https://www.dropbox.com/s/6kltw0kqynlijxv/widsdatathon2019.zip
# The downloaded competition data is zipped, let us unzip it
!unzip widsdatathon2019.zip
# The training and testing data have already been seperated, Unzip them as well
!unzip train_images.zip
!unzip leaderboard_holdout_data.zip
!unzip leaderboard_test_data.zip
檢視資料
在處理這個問題之前,首先要做的是檢視可用的資料。在知道如何解決問題之前,我們首先需要了解問題和資料是什麼樣子。檢視資料意味著瞭解資料目錄的結構,標籤是什麼,以及一些示例影像是什麼。
# Overview of the labels of the training data;
df = pd.read_csv('data/traininglabels.csv')
df.head()
使用pandas庫讀取資料
處理影像分類資料集和表格資料集的主要區別在於標籤的儲存方式,這裡的標籤指的是影像中的內容。在這個特定的資料集中,標籤儲存在CSV檔案中。
要了解如何計算分數列的詳細資訊,請訪問此連結:https://success.figure-eight.com/hc/en-us/articles/201855939-How-to-Calculate-a-Confidence-Score
利用countplot函式來檢視培訓資料的分佈情況,從圖中可以看到:大約有14,300幅圖片沒有油棕櫚種植園,而只有942幅圖片有油棕櫚種植園,這可以稱為非均衡資料集,關於非均衡資料集這個深度學習問題,我們不打算在此討論;目前,可以從這裡開始起步:
sns.countplot(df.has_oilpalm)
對兩類進行計數
準備資料
所提供的測試資料位於兩個獨立的資料夾中,即排行榜預留資料(leaderboard holdout data)和排行榜測試資料(leaderboard test data)這兩個資料夾。由於競賽要求對兩個資料集提交預測結果,所以我們將二者組合起來,這樣一共得到6534張圖片。
test_imgs = [i for i in test.iterdir()]
hold_imgs = [i for i in lb_test.iterdir()]
combined_test = test_imgs + hold_imgs
len(combined_test)
將排行榜預留資料(leaderboard holdout data )與排行榜測試資料(leaderboard test data)組合起來
採用fast.ai的DataBlock API來構造資料,這是向模型輸送資料集的一種簡易的方法。
src = (ImageList.from_df(df, path, folder='train_images')
.random_split_by_pct(0.2, seed=14)
.label_from_df('has_oilpalm')
.add_test(combined_test))
data = (src.transform(get_transforms(flip_vert=True), size=164)
.databunch()
.normalize(imagenet_stats))
建立一個ImageList來儲存資料
此步驟要點:
利用ImageList的from_df方法來儲存訓練資料,這樣做是因為將有關訓練集的資訊儲存在名字為df的資料幀中,讓它能找到訓練影像所在的路徑和儲存影像的資料夾名稱,train_images.
接下來,使用隨機分割來對訓練集進行分割,留出20%的資料來監控模型在訓練過程中的效能。選擇一顆種子,以確保再次檢查時能得到同樣的結果,我們必須知道什麼在起作用,什麼不起作用。
告訴ImageList在訓練集中的資料的標籤所在地,利用has_oilpalm方法將組合後的資料新增到測試資料中。
最後,對資料進行轉換,使用flip_vert = True翻轉影像有助於模型識別影像。利用imagenet_stats對影像歸一化處理。注意:這是一種轉移學習技術,我要說的是需要保持操作儘可能簡單。
影像預覽
不管有沒有油棕種植園,衛星影像是這樣的:
data.show_batch(2)
顯示2批影像
訓練模型
現在開始訓練模型,採用卷積神經網路骨幹,並使用預先訓練的權重,這個權重從一個已經訓練好的影像分類的resnet模型中直接獲得,無須擔心這種方法的細節。目前為止,我們正在構建一個以衛星影像為輸入並輸出這兩種分類的預測機率模型。
learn = create_cnn(data, models.resnet50, metrics=[accuracy, error_rate],
callback_fns=[ShowGraph, SaveModelCallback])
# View model architecture
learn.model()
卷積神經網路
learn.lr_find()
learn.recorder.plot()
接下來,使用lr_find()找到理想的學習率,並利用recorder.plot().對它視覺化。
找出最優的模型學習率
選擇一個接近坡度最陡之處的學習速率,在這個示例中是1e-2。
learn.fit_one_cycle(5, slice(1e-2))
學習率為1e-2的5個迴圈的訓練模型
利用fit_one_cycle函式對模型訓練5個週期 (對所有資料訓練5個週期)。
注意到顯示出來的結果,如training_loss 和valid_loss沒有?後續,會用它們來監控模型的改進。
在第四個迴圈,得到了最佳的模型。
fast.ai在執行訓練和驗證資料集時,內部自動選取和儲存最優的那個模型。
評估模型
競賽組委會根據預測機率與觀測目標has_oilpalm之間的工作特性曲線下的面積對參賽作品進行評價。透過以下開發者速成班、影片或Kaggle學習論壇的帖子,可以瞭解到更多關於AUC 的諮詢。
開發者速成班:
https://developers.google.com/machine-learning/crash-course/classification/roc-and-auc
影片:
https://www.dataschool.io/roc-curves-and-auc-explained/ Kaggle
學習論壇:
https://www.kaggle.com/learn-forum/5378
預設情況下,Fast.ai沒有提供這個評價標準的指標度量,所以我們將用到Scikit-Learning庫。
from sklearn.metrics import roc_auc_score
def auc_score(y_score,y_true):
return torch.tensor(roc_auc_score(y_true,y_score[:,1]))
probs,val_labels = learn.get_preds(ds_type=DatasetType.Valid)
print('Accuracy',accuracy(probs,val_labels)),
print('Error Rate', error_rate(probs, val_labels))
print('AUC', auc_score(probs,val_labels))
列印出驗證指標
使用預訓練模型和fast.ai的優點是,可以得到一個非常好的預測精度,在這個示例中,在沒有多做其他工作的情況下,獲得了99.44%精確度。
將模型存檔,繪製出預測的混淆矩陣。
learn.save('resnet50-stg1')
利用混淆矩陣檢視結果
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(dpi=120)
繪製混淆矩陣
混淆矩陣是一種圖形化的方法,用來檢視模型準確或不準確預測的影像數量。
從這幅圖中可以看出,模型準確地預測了2863幅沒有油棕人工林的影像,對168幅油棕人工林的影像進行了正確的分類。將10幅含有油棕人工林的影像分類為無油棕人工林影像,並將7幅無油棕人工林影像分類為有油棕人工林影像。
對於一個簡單的模型來說這個結果還不錯。
接下來,找出這個訓練迭代理想的學習率。
learn.lr_find()
learn.recorder.plot()
找出理想的學習率
利用介於1e-6和1e-4之間的一個學習率最大值對模型進行擬合。
learn.fit_one_cycle(7, max_lr=slice(1e-6,1e-4))
學習率在1e-6和1e-4的範圍範圍內,對模型進行7次迴圈訓練
在每個訓練週期後,以圖形的方式觀察訓練指標,從而監測模型的效能。
儲存第二階段的模型訓練結果。
learn.save('resnet50-stg2')
probs,val_labels = learn.get_preds(ds_type=DatasetType.Valid)
print('Accuracy',accuracy(probs,val_labels)),
print('Error Rate', error_rate(probs, val_labels))
print('AUC', auc_score(probs,val_labels))
準確度、誤差率和AUC評分
列印出模型的精度、錯誤率和曲線下面的面積。
你會注意到,此時,模型的準確度從99.44%提高到99.48%,錯誤率從0.0056降低到0.0052,AUC也有改善,從99.82%提高到99.87%。
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(dpi=120)
繪製混淆矩陣
透過與我們繪製的上一個混淆矩陣的比較,可以發現模型做出了更精準的預測。
先前沒有油棕種植園的7張圖片被錯誤分類,現在降到了3張,效能有所提高。
你會注意到在訓練過程中遵循了一個模式,在這個過程中調整了一些引數,這便是所謂的精調,絕大多數深度學習訓練均遵循類似的迭代模式。
影像變換
我們將對資料執行更多的影像變換,透過這些變換對模型進行改進。
關於每種變換的詳細描述,可以在fast.ai的相關文件中找到:https://docs.fast.ai/vision.transform.html
tfms = get_transforms(flip_vert=True, max_lighting=0.1, max_zoom=1.05, max_warp=0.)
data = (src.transform(tfms, size=200)
.databunch().normalize(imagenet_stats))
learn.data = data
data.train_ds[0][0].shape
應用不同的變換改進模型
hting:如果非空 None,則在機率p_lighting下使用由max_light控制的隨機亮度和對比度變化。
max_zoom:如果非1,或是一個比1更小的數,則在機率p_affine下使用max_zoom到1之間的隨機縮放比
max_warp:如果非空None,則使用機率p_affine應用-max_warp和max_warp之間的隨機對稱翹曲。
再次找出最優學習率:
learn.lr_find()
learn.recorder.plot()
找出理想的學習率
迴圈訓練模型5次:
learn.fit_one_cycle(5, 1e-6)
將訓練指標與先前的指標進行比較,模型在0.0169與0.0163之間的迭代稍差,但是不要失望。
將第三階段的訓練模型儲存,並列印出指標。可以注意到,目前,模型的準確度是99.38%,在前一階段是99.48%,AUC評分從99.87%提高到99.91%,達到了比賽評分的標準。
learn.save('resnet50-stg3')
probs,val_labels = learn.get_preds(ds_type=DatasetType.Valid)
print('Accuracy',accuracy(probs,val_labels)),
print('Error Rate', error_rate(probs, val_labels))
print('AUC', auc_score(probs,val_labels))
準確度、誤差率和AUC評分
最終訓練階段
可以注意到,我們從最初影像大小size = 164的影像開始,然後逐漸遞增,最終達到了 size = 256 。這樣做是為了利用FAST.ai的累進影像大小進行分類,即在訓練開始時使用小影像,並隨著訓練的進展逐漸增大大小。這樣,即便模型早期訓練時非常不準確,它也可以快速地看到大量影像並加快進度,在後續的訓練中,可以看到更大的影像,從而瞭解更細粒度的區別。
若要閱讀更多有關此資訊,請訪問此連結:https://www.fast.ai/2018/08/10/fastai-diu-imagenet/
tfms = get_transforms(flip_vert=True, max_lighting=0.1, max_zoom=1.05, max_warp=0.)
data = (src.transform(tfms, size=256)
.databunch().normalize(imagenet_stats))
learn.data = data
data.train_ds[0][0].shape
應用不同的變換來改進模型
將影像大小增加到256
再次找出最佳化後的學習率:
learn.lr_find()
learn.recorder.plot()
learn.fit_one_cycle(5, slice(1e-4))
學習率設定為1e-4,訓練模型5次迴圈
來看一下訓練指標,並與過去的指標進行比較,模型略有改進,從0.0169提高到0.0168。
將最後階段的模型訓練結果儲存,並列印出指標:
learn.save('resnet50-stg4')
probs,val_labels = learn.get_preds(ds_type=DatasetType.Valid)
print('Accuracy',accuracy(probs,val_labels)),
print('Error Rate', error_rate(probs, val_labels))
print('AUC', auc_score(probs,val_labels))
準確度、誤差率和AUC評分
你會注意到,模型的準確度現在是99.44%,比上一階段的99.38%有所提高:
準備競賽提交檔案
現在可以看到模型是如何對資料進行預測的:
p,t = learn.get_preds(ds_type=DatasetType.Test)
p = to_np(p);
p.shape
ids = np.array([f.name for f in (combined_test)]);
ids.shape
sample_sub = Path('data/SampleSubmission.csv')
df_sample = pd.read_csv(sample_sub)
sub = pd.DataFrame(np.stack([ids, p[:,1]], axis=1), columns=df_sample.columns)
sub.to_csv(path/'wids-notebook.csv', index=False)
準備CSV格式的提交檔案
向WiDS資料馬拉松提交檔案
現在可以參加WiDS的競賽,並提交參賽檔案了,請轉到此處(https://www.kaggle.com/c/widsdatathon2019)的競賽頁面,單擊“加入競賽”並接受競賽規則,便可以提交參賽內容,如果你參加,看看你會排名第幾。
我提交了自己的模型的預測之後
獲得的私有和公開分數
免責宣告:按照文章中的說明操作後,不會像我那樣位列第三,為確保過程儘可能簡單,請參看連結(https://www.kaggle.com/c/widsdatathon2019/discussion/82252)中Abdishakur的帖子。
原文標題:
How a team of deep learning newbies came 3rd place in a kaggle contest——Classifying images of oil palm plantations using fast.ai
原文連結:
https://towardsdatascience.com/how-a-team-of-deep-learning-newbies-came-3rd-place-in-a-kaggle-contest-644adcc143c8
譯者簡介
陳之炎,北京交通大學通訊與控制工程專業畢業,獲得工學碩士學位,歷任長城計算機軟體與系統公司工程師,大唐微電子公司工程師,現任北京吾譯超群科技有限公司技術支援。目前從事智慧化翻譯教學系統的運營和維護,在人工智慧深度學習和自然語言處理(NLP)方面積累有一定的經驗。業餘時間喜愛翻譯創作,翻譯作品主要有:IEC-ISO 7816、伊拉克石油工程專案、新財稅主義宣言等等,其中中譯英作品“新財稅主義宣言”在GLOBAL TIMES正式發表。