大半個(gè)月前,fast.ai在博客上宣布fastai 1.0版發(fā)布,之后很快在GitHub上發(fā)布了1.0.5測(cè)試版,半個(gè)月前發(fā)布1.0.6正式版。由于剛發(fā)布不久,網(wǎng)上關(guān)于fastai 1.0的教程極少,因此,我們編寫了這篇入門教程,以一個(gè)簡單的圖像分類問題(異形與鐵血戰(zhàn)士)為例,帶你領(lǐng)略fastai這一高層抽象框架驚人的簡潔性。
注意,fastai最近的更新節(jié)奏很瘋狂:
好在按照語義化版本的規(guī)矩,動(dòng)的都是修訂號(hào),所以這篇教程在這些版本上應(yīng)該都適用。不過,以防萬一,給出我們使用的版本號(hào)供參考:
fastai 1.0.15
pytorch-nightly-cpu 1.0.0.dev20181014
安裝
建議通過conda安裝。如果用的是最近的Nvidia顯卡,可以安裝GPU版本:
conda install -c pytorch pytorch-nightly cuda92
conda install -c fastai torchvision-nightly
conda install -c fastai fastai
顯卡不給力的話,可以安裝CPU版本:
conda install -c pytorch pytorch-nightly-cpu
conda install -c fastai torchvision-nightly-cpu
conda install -c fastai fastai
不用擔(dān)心,在遷移學(xué)習(xí)的加持下,本教程中的模型,即使是性能較低的機(jī)器,不到一小時(shí)也可訓(xùn)練完畢。當(dāng)然,如果使用GPU版,就更快了,幾分鐘搞定。
當(dāng)然也可以通過pip安裝,甚至直接從源代碼編譯,詳見官方倉庫的README。
注意,不管是GPU版本,還是CPU版本,都需要python 3.6以上,如果是GPU版本,還需要正確配置顯卡驅(qū)動(dòng)。
安裝好之后,我們可以通過以下語句引入fastai:
import fastai
from fastai import *
from fastai.vision import *
嚴(yán)格來說最后兩行并不必要,不過,加載模塊中的所有定義,可以使代碼看起來更簡潔,所以我們這里就加上了。
圖像分類示例
MNIST
深度學(xué)習(xí)入門的經(jīng)典例子是MNIST手寫數(shù)字分類,實(shí)際上fastai的官方文檔開篇就是MNIST的例子:
path = untar_data(URLs.MNIST_SAMPLE)
data = ImageDataBunch.from_folder(path)
learn = create_cnn(data, models.resnet18, metrics=accuracy)
learn.fit(1)
只有四行!事實(shí)上,其中還有兩行是加載數(shù)據(jù),然后最后一行是訓(xùn)練,真正搭建模型只用一行!如果你接觸過其他深度學(xué)習(xí)框架,也許立刻就會(huì)意識(shí)到fastai恐怖的簡潔性。反正我第一次看到的反應(yīng)就是:“我靠!這也行???”
不過MNIST分類實(shí)在是太過經(jīng)典,幾乎每篇入門教程都用,說不定有些人已經(jīng)看吐了。而且,黑白手寫數(shù)字分類,在當(dāng)前背景下,太過古老,體現(xiàn)不了近年來深度學(xué)習(xí)在計(jì)算機(jī)視覺領(lǐng)域的突飛猛進(jìn)。所以,我們還是換一個(gè)酷炫一點(diǎn)的例子吧。
異形大戰(zhàn)鐵血戰(zhàn)士
換個(gè)什么例子呢?最近上映了一部新的《鐵血戰(zhàn)士》,我們不如做個(gè)鐵血戰(zhàn)士和異形的分類器吧(以前還有部《異形大戰(zhàn)鐵血戰(zhàn)士》,不妨假想一下,鐵血戰(zhàn)士的HUD依靠神經(jīng)網(wǎng)絡(luò)區(qū)分?jǐn)澄遥?/p>
圖片來源: frolic.media
數(shù)據(jù)集
要做這樣一個(gè)分類器,首先需要數(shù)據(jù)集。這很簡單,網(wǎng)絡(luò)上鐵血戰(zhàn)士和異形的圖片太多了。不過,在自己動(dòng)手搜集圖片之前,我們先檢查下有沒有人做過類似的工作。
這里安利下Google的數(shù)據(jù)集搜索,找數(shù)據(jù)集很方便:https://toolbox.google.com/datasetsearch/
用“alien predator”一搜,還真有,第一個(gè)結(jié)果就是Kaggle上的Alien vs. Predator images:
這些圖像是通過Google圖像搜索搜集的JPEG縮略圖(約250×250像素),訓(xùn)練集、驗(yàn)證集的每個(gè)分類各有347、100張圖像樣本。
從Kaggle下載數(shù)據(jù)后,我們將validation文件夾改名為valid,得到以下的目錄結(jié)構(gòu):
|-- train
|-- alien
|-- predator
|-- valid
|-- alien
|-- predator
這一目錄結(jié)構(gòu)符合fastai組織數(shù)據(jù)的慣例。
數(shù)據(jù)預(yù)處理
之前MNIST的例子中,我們是這樣加載數(shù)據(jù)的:
path = untar_data(URLs.MNIST_SAMPLE)
data = ImageDataBunch.from_folder(path)
我們直接使用URLs.MNIST_SAMPLE,fastai會(huì)自動(dòng)下載數(shù)據(jù)集并解壓,這是因?yàn)镸NIST是fastai的自帶數(shù)據(jù)集。fastai自帶了MNIST、CIFAR10、Wikitext-103等常見數(shù)據(jù)集,詳見fastai官網(wǎng):https://course.fast.ai/datasets
而我們要使用的是非自帶數(shù)據(jù)集,所以只需像之前提到的那樣,在相關(guān)路徑準(zhǔn)備好數(shù)據(jù),然后直接調(diào)用ImageDataBunch.from_folder加載即可。
不過,上面的MNIST例子中,出于簡單性考慮,沒有進(jìn)行預(yù)處理。這是因?yàn)镸NIST圖像本身比較齊整,而且MNIST非常簡單,所以不做預(yù)處理也行。我們的異形和鐵血戰(zhàn)士圖片則需要做一些預(yù)處理。
首先,大多數(shù)卷積神經(jīng)網(wǎng)絡(luò)的輸入層形狀都是28、32、64、96、224、384、512之類的數(shù)字。而數(shù)據(jù)集中的圖片邊長約為250像素,這就需要縮放或者裁切一下。
其次,絕大多數(shù)圖像分類任務(wù),都需要做下數(shù)據(jù)增強(qiáng)。一方面增加些樣本,以充分利用數(shù)量有限的樣本;另一方面,也是更重要的一方面,通過平移、旋轉(zhuǎn)、縮放、翻轉(zhuǎn)等手段,迫使模型學(xué)習(xí)圖像更具概括性的特征,緩解過擬合問題。
如果你平時(shí)積累了一些不依賴框架的圖像增強(qiáng)函數(shù)(比如,基于numpy和scipy定義),那圖像增強(qiáng)不算太麻煩。不過,你也許好奇,fastai有沒有內(nèi)置圖像增強(qiáng)的功能?
有的。實(shí)際上,上面說了一大堆,體現(xiàn)到代碼就是一句話:
data = ImageDataBunch.from_folder('data', ds_tfms=get_transforms(), size=224)
前面MNIST的例子中,我們看到,fastai只需一個(gè)語句就可以完成加載數(shù)據(jù)集的任務(wù),這已經(jīng)足夠簡潔了?,F(xiàn)在我們加上預(yù)處理,還是一個(gè)語句,只不過多了兩個(gè)參數(shù)!
現(xiàn)在我們回過頭來,再看看from_folder這個(gè)方法,它根據(jù)路徑參數(shù)獲取數(shù)據(jù)集目錄,然后根據(jù)目錄結(jié)構(gòu)區(qū)分訓(xùn)練集、驗(yàn)證集、分類集,根據(jù)目錄名稱獲取樣本的分類標(biāo)簽。這種API的設(shè)計(jì)極為簡潔,避免了很多冗余的“模板代碼”。類似地,fastai的ImageDataBunch類還有from_csv、from_df等方法,從CSV文件或DataFrame加載數(shù)據(jù)。
size參數(shù)指定了形狀,ds_tfms指定了預(yù)處理邏輯,兩個(gè)參數(shù)完成預(yù)處理工作。get_transforms()則是fastai的內(nèi)置方法,提供了適用于大多數(shù)計(jì)算機(jī)視覺任務(wù)的默認(rèn)數(shù)據(jù)增強(qiáng)方案:
以0.5的概率隨機(jī)水平翻轉(zhuǎn)
以0.75的概率在-10與10度之間旋轉(zhuǎn)
以0.75的概率在1與1.1倍之間隨機(jī)放大
以0.75的概率隨機(jī)改變亮度和對(duì)比度
以0.75的概率進(jìn)行隨機(jī)對(duì)稱扭曲
get_transforms()充分體現(xiàn)了fastai的高層抽象程度。fastai的使用者考慮的是我需要應(yīng)用常見的數(shù)據(jù)增強(qiáng)方案,而不是具體要對(duì)圖像進(jìn)行哪些處理。
在設(shè)計(jì)模型之前,我們先簡單地檢查下數(shù)據(jù)加載是否成功:
data.show_batch(rows=3, figsize=(6,6))
看起來沒問題。
順便提下,如果使用GPU版本,建議再傳入一個(gè)bs參數(shù),指定下batch大小,比如bs=32,以便充分利用GPU的并行特性,加速之后的訓(xùn)練。
化神經(jīng)網(wǎng)絡(luò)為平常
之前提到,MNIST例子中的核心語句(指定網(wǎng)絡(luò)架構(gòu))其實(shí)只有一行:
learn = create_cnn(data, models.resnet18, metrics=accuracy)
其實(shí)我們這個(gè)異形、鐵血戰(zhàn)士的模型架構(gòu)也只需一行:
learn = create_cnn(data, models.resnet50, metrics=accuracy)
幾乎和MNIST一模一樣,只是把模型換成了表達(dá)力更強(qiáng)、更復(fù)雜的ResNet-50網(wǎng)絡(luò),畢竟,異形和鐵血戰(zhàn)士圖像要比黑白手寫數(shù)字復(fù)雜不少。
正好,提供異形、鐵血戰(zhàn)士數(shù)據(jù)集的Kaggle頁面還提供了分類器的Keras實(shí)現(xiàn)和PyTorch實(shí)現(xiàn)。我們不妨把網(wǎng)絡(luò)架構(gòu)部分的代碼抽出來對(duì)比一下。
首先,是以API簡潔著稱的Keras:
conv_base = ResNet50(
include_top=False,
weights='imagenet')
for layer in conv_base.layers:
layer.trainable = False
x = conv_base.output
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
predictions = layers.Dense(2, activation='softmax')(x)
model = Model(conv_base.input, predictions)
optimizer = keras.optimizers.Adam()
model.compile(loss='sparse_categorical_crossentropy',
optimizer=optimizer,
metrics=['accuracy'])
然后,是以易用性著稱的PyTorch:
device = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")
model = models.resnet50(pretrained=True).to(device)
for param in model.parameters():
param.requires_grad = False
model.fc = nn.Sequential(
nn.Linear(2048, 128),
nn.ReLU(inplace=True),
nn.Linear(128, 2)).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.fc.parameters())
對(duì)比一下,很明顯,論簡潔程度,PyTorch并不比一直打廣告說自己簡潔的Keras差。然后,雖然寫法有點(diǎn)不一樣,但這兩個(gè)框架的基本思路都是差不多的。首先指定模型的凍結(jié)部分,然后指定后續(xù)層,最后指定損失函數(shù)、優(yōu)化算法、指標(biāo)。
然后我們?cè)倏匆槐閒astai:
learn = create_cnn(data, models.resnet50, metrics=accuracy)
你大概能體會(huì)文章開頭提到的驚呼“這樣也行?。俊钡男那榱税???雌饋砦覀冎皇侵付四P头N類和指標(biāo),很多東西都沒有呀。
實(shí)際上,如果你運(yùn)行過開頭提到的MNIST的代碼,就會(huì)發(fā)現(xiàn)一個(gè)epoch就達(dá)到了98%以上的精確度。很明顯,用了遷移學(xué)習(xí),否則不會(huì)學(xué)得這么快。和Keras、PyTorch需要明確指出繼承權(quán)重、預(yù)訓(xùn)練不同,fastai里遷移學(xué)習(xí)是默認(rèn)配置。同理,后續(xù)層的層數(shù)、形狀、激活函數(shù),損失函數(shù),優(yōu)化算法,都不需要明確指定,fastai可以根據(jù)數(shù)據(jù)的形狀、模型種類、指標(biāo),自動(dòng)搞定這些。
fastai的口號(hào)是“makeing neural nets uncool again”(化神經(jīng)網(wǎng)絡(luò)為平常),真是名不虛傳。
訓(xùn)練和解讀
定下模型后,只需調(diào)用fit就可以開始訓(xùn)練了,就像MNIST例子中寫的那樣。
不過,這次我們打算轉(zhuǎn)用fit_one_cycle方法。fit_one_cycle使用的是一種周期性學(xué)習(xí)率,從較小的學(xué)習(xí)率開始學(xué)習(xí),緩慢提高至較高的學(xué)習(xí)率,然后再慢慢下降,周而復(fù)始,每個(gè)周期的長度略微縮短,在訓(xùn)練的最后部分,允許學(xué)習(xí)率比之前的最小值降得更低。這不僅可以加速訓(xùn)練,還有助于防止模型落入損失平面的陡峭區(qū)域,使模型更傾向于尋找更平坦的極小值,從而緩解過擬合現(xiàn)象。
圖片來源:sgugger.github.io
這種學(xué)習(xí)率規(guī)劃方案是Lesile Smith等最近一年剛提出的。fit_one_cycle體現(xiàn)了fastai的一個(gè)知名的特性:讓最新的研究成果易于使用。
先跑個(gè)epoch看看效果:
learn.fit_one_cycle(1)
結(jié)果:
epoch train_loss valid_loss accuracy
1 0.400788 0.520693 0.835106
嗯,看起來相當(dāng)不錯(cuò)。一般而言,先跑一個(gè)epoch是個(gè)好習(xí)慣,可以快速檢查是否在編碼時(shí)犯了一些低級(jí)錯(cuò)誤(比如形狀弄反之類的)。但是fastai這么簡潔,這么自動(dòng)化,犯低級(jí)錯(cuò)誤的幾率相應(yīng)也低了不少,也讓先跑一個(gè)epoch體現(xiàn)價(jià)值的機(jī)會(huì)少了很多。;-)
再多跑幾個(gè)epoch看看:
learn.fit_one_cycle(3)
結(jié)果:
epoch train_loss valid_loss accuracy
1 0.221689 0.364424 0.888298
2 0.164495 0.209541 0.909574
3 0.132979 0.181689 0.930851
有興趣的讀者可以試著多跑幾個(gè)epoch,表現(xiàn)應(yīng)該還能提升一點(diǎn)。不過,我對(duì)這個(gè)精確度已經(jīng)很滿意了。我們可以對(duì)比下Kaggle上的PyTorch實(shí)現(xiàn)跑3個(gè)epoch的表現(xiàn):
Epoch1/3
----------
train loss: 0.5227, acc: 0.7205
validation loss: 0.3510, acc: 0.8400
Epoch2/3
----------
train loss: 0.3042, acc: 0.8818
validation loss: 0.2759, acc: 0.8800
Epoch3/3
----------
train loss: 0.2181, acc: 0.9135
validation loss: 0.2405, acc: 0.8950
fastai的表現(xiàn)與之相當(dāng),但是,相比PyTorch實(shí)現(xiàn)需要進(jìn)行的編碼(已經(jīng)很簡潔了),我們的fastai實(shí)現(xiàn)可以說是毫不費(fèi)力。
當(dāng)然,也不能光看精確度——有的時(shí)候這會(huì)形成偏差。讓我們看下混淆矩陣吧。
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
看起來很不錯(cuò)嘛!
再查看下最讓模型頭疼的樣本:
interp.plot_top_losses(9, figsize=(10,10))
我們看到,這個(gè)模型還是有一定的可解釋性的。我們可以猜想,人臉、亮度過暗、畫風(fēng)獨(dú)特、頭部在畫幅外或較小,都可能干擾分類器的判斷。如果我們想進(jìn)一步提升模型的表現(xiàn)和概括能力,可以根據(jù)我們的猜想再收集一些樣本,然后做一些針對(duì)性的試驗(yàn)加以驗(yàn)證。查明問題后,再采取相應(yīng)措施加以改進(jìn)。
靈活性
本文的目的是展示fastai API的簡潔性和高層抽象性。不過,我們最后需要指出的一點(diǎn)是,fastai并沒有因此放棄靈活性和可定制性。
比如,之前為了符合fastai數(shù)據(jù)集目錄結(jié)構(gòu)的慣例,我們?cè)谙螺d數(shù)據(jù)集后將validation重命名為valid。實(shí)際上,不進(jìn)行重命名也完全可以,只需在調(diào)用ImageDataBunch的from_folder方法時(shí),額外將validation傳入對(duì)應(yīng)的參數(shù)即可。
再比如,如果我們的數(shù)據(jù)集目錄中,子目錄名作為標(biāo)簽,但沒有按訓(xùn)練集、驗(yàn)證集、測(cè)試集分開,需要隨機(jī)按比例劃分。另外,我們還想手動(dòng)指定數(shù)據(jù)增強(qiáng)操作列表。那么可以這樣加載數(shù)據(jù)集:
# 手動(dòng)指定數(shù)據(jù)增強(qiáng)操作
tfms = [rotate(degrees=(-20,20)), symmetric_warp(magnitude=(-0.3,0.3))]
data = (ImageFileList.from_folder(path)
.label_from_folder() # 根據(jù)子目錄決定標(biāo)簽
.random_split_by_pct(0.1) # 隨機(jī)分割10%為驗(yàn)證集
.datasets(ImageClassificationDataset) # 轉(zhuǎn)換為圖像分類數(shù)據(jù)集
.transform(tfms, size=224) # 數(shù)據(jù)增強(qiáng)
.databunch()) # 最終轉(zhuǎn)換
我們上面明確羅列了數(shù)據(jù)增強(qiáng)操作。如果我們只是需要對(duì)默認(rèn)方案進(jìn)行微調(diào)的話,那么get_transforms方法其實(shí)有一大堆參數(shù)可供調(diào)整,比如get_transforms(flip_vert=True, max_rotate=20)意味著我們同時(shí)進(jìn)行上下翻轉(zhuǎn)(默認(rèn)只進(jìn)行水平翻轉(zhuǎn)),并且增加了旋轉(zhuǎn)的角度范圍(默認(rèn)為-10到10)。
-
圖像分類
+關(guān)注
關(guān)注
0文章
96瀏覽量
12166 -
數(shù)據(jù)集
+關(guān)注
關(guān)注
4文章
1224瀏覽量
25445
原文標(biāo)題:fastai 1.0入門教程:如何訓(xùn)練簡單圖像分類器
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
基于多通道分類合成的SAR圖像分類研究
使用MXNetFashionMNIST數(shù)據(jù)集分類簡潔實(shí)現(xiàn)
一種基于圖像平移的目標(biāo)檢測(cè)框架
課程預(yù)告丨12月15日官方直播帶你領(lǐng)略ArkUI的聲明式開發(fā)范式之美
旋轉(zhuǎn)圖像的合成與識(shí)別仿真

PowerVR框架:PVRApi Vulkan和OpenGL ES抽象層

基于雙稀疏正則的圖像集距離學(xué)習(xí)框架DSRID
基于顯著性檢測(cè)的圖像分類算法
電機(jī)控制硬件抽象層(HAL)
簡單好上手的圖像分類教程!

人工智能引發(fā)的圖像分類算法

OpenHarmony工作委員會(huì)PMC委員萬承臻帶你領(lǐng)略OpenHarmony3.1從內(nèi)核到框架

電機(jī)控制硬件抽象層(HAL)用戶指南

評(píng)論