介紹如何在 Linux 中安裝與使用 fastai 深度學習函式庫,使用 ResNet 預訓練模型辨識貓的圖片。
若要在 Miniconda 環境中安裝 fastai,可以執行:
# miniconda 環境安裝 fastai conda install -c fastchan fastai
若要在 Anaconda 環境中安裝 fastai,則可以執行:
# Anaconda 環境安裝 fastai conda install -c fastchan fastai anaconda
若要以 pip
安裝 fastai,則必須先安裝好 PyTorch 之後,再執行:
# pip 環境安裝 fastai
pip install fastai
在使用 fastai 函式庫進行深度學習的程式中,都會包含以下四個部分:
DataLoaders
。Learner
。fit()
方法。這樣的程式架構適用於各種不同類型的應用程式與資料集,雖然影用類型差異很大,但是程式碼的架構都類似。
以 untar_data()
函數下載 Oxford-IIIT Pet 資料集,解壓縮後取得影像路徑:
from fastai.vision.all import * # 下載 Oxford-IIIT Pet 資料集 path = untar_data(URLs.PETS)/'images'
由於在 Oxford-IIIT Pet 資料集中,貓類別的圖片檔案名稱都是以大寫字母開頭,而狗類別的圖片檔案名稱則是以小寫字母開頭,因此這裡我們自行定義一個產生標註影像用的函數,依據檔案名稱開頭大小寫來決定影像是否為貓類別,此函數將用於隨後的 ImageDataLoaders
:
# 依據檔案名稱第一個字母大小寫產生標註 def is_cat(x): return x[0].isupper()
DataLoader
在 fastai 中有非常多的類別可以用來處理不同的資料與問題,這裡我們使用 ImageDataLoaders
建立一個 DataLoader
來載入影像資料,並以 from_name_func()
方式搭配 is_cat()
來產生影像標註:
# 建立 DataLoader dls = ImageDataLoaders.from_name_func( path, get_image_files(path), valid_pct=0.2, seed=42, label_func=is_cat, item_tfms=Resize(224))
此處的影像轉換(transform)是以 item_tfms
參數指定以 Resize(224)
將每張影像轉為邊長為 224
的正方形圖片,另外也可以改用 batch_tfms
參數以 GPU 批次進行轉換,轉換速度會更快。
224
的正方形圖片,只是過去大家的習慣而已,某些舊的模型會要求圖片必須符合這樣的尺寸,但一般的情況下我們也可以採用其他尺圖片,較大的圖片會有較高的準確率,但也會耗費較多的計算資源,反之小圖片則準確率低、計算資源相對也較節省。而這裡的 valid_pct=0.2
代表保留 20% 的資料作為驗證用的資料,不要放入訓練過程,僅用於驗證模型的準確率,這些 20% 的資料稱為驗證資料集(validation set),而用於訓練的 80% 資料則稱為訓練資料集(training set)。
在預設的情況下,fastai 會以隨機的方式挑選 20% 的資料作為驗證資料集,而 seed=42
這個參數則是用來指定亂數種子,也就是說若指定相同的亂數種子,就可以產生相同的驗證資料集,當我們更改模型參數並重新訓練時,就可以用相同的驗證資料集來評估模型的準確率,方便比較模型的差異。
由於模型是根據訓練資料集所訓練出來的,模型本身就含有大量的訓練資料集資訊,因此在估算模型的準確率時,fastai 只會以驗證資料集來驗證模型,並不會使用到訓練資料集的資料,這樣才能估算出模型對於新資料的預測效果。
Learner
接著建立一個 CNN 的 Learner
,指定 CNN 網路模型、DataLoader、損失函數:
# 建立 Learner(指定模型、DataLoader、損失函數)
learn = cnn_learner(dls, resnet34, metrics=error_rate)
這裡我們採用 ResNet 的模型,而 34
是代表模型的層數,除了 34
之外,還有 18
、50
、101
與 152
可以選擇,層數越高則需要的訓練時間越久、也越有可能 overfitting,當資料量較大的時候才會較準確。
metrics
是代表使用驗證資料集進行模型驗證時,所採用的衡量基準,在每一個 epoch 結束時都會計算出這個 metric 值做為參考,以這個例子所採用的 error_rate
來說就是計算分類錯誤的比例,而另一個常見的 accuracy
則是分類正確的比例,也就是 1.0 - error_rate
。fastai 中提供了很多種 metric 可供使用者選用,詳細說明可參考 fastai 的網頁。
cnn_learner()
中有一個 pretrained
參數,可以用來指定是否採用預訓練的模型,當參數設定為 True
的時候,fastai 會將最後一層移除,再添加一層或多層隨機的權重,通常使用預訓練模型可以有效提高模型的準確率,減少訓練時間。將預訓練模型用於不同的應用上就是所謂的遷移學習(transfer learning)。
最後使用 fine_tune()
進行模型的訓練,並指定要進行幾個 epoch 的訓練:
# 訓練模型 learn.fine_tune(1)
事實上 Learner
也有一個 fit()
函數,而這裡採用 fine_tune()
來訓練模型的原因是因為我們採用了預訓練模模型,fine_tune()
預設會先以一個 epoch 訓練部分的預訓練模型參數,讓添加的網路層能夠正確運作,再接著以指定數量的 epoch 訓練整個模型。
訓練完成之後,我們就可以使用這個模型進行預測了。我們使用 Wikimedia Commons 的一張貓的圖片作為示範,先下載這張圖片:
# 下載圖片
wget https://commons.wikimedia.org/wiki/File:A-Cat.jpg
接著使用 fastai 的 PILImage
載入圖片之後,以剛剛訓練好的模型進行預測:
# 載入圖片 img = PILImage.create("A-Cat.jpg") # 使用模型預測 is_cat,_,probs = learn.predict(img) print(f"是否為貓:{is_cat}.") print(f"是貓的機率:{probs[1].item():.6f}")
是否為貓:True. 是貓的機率:1.000000
以下是一個使用 fastai 搭配 CamVid 資料集訓練影像分割模型的範例程式,雖然應用不同,但整個程式架構也都很類似:
from fastai.vision.all import * # 下載 CamVid 資料 path = untar_data(URLs.CAMVID_TINY) # 建立 DataLoader dls = SegmentationDataLoaders.from_label_func( path, bs=8, fnames = get_image_files(path/"images"), label_func = lambda o: path/'labels'/f'{o.stem}_P{o.suffix}', codes = np.loadtxt(path/'codes.txt', dtype=str) ) # 建立 Learner learn = unet_learner(dls, resnet34) # 訓練模型 learn.fine_tune(8)
模型訓練完成之後,觀看幾筆模型預測的結果:
# 觀看 3 筆預測結果 learn.show_results(max_n=3, figsize=(7,8))
以下是使用 fastai 搭配 IMDb Large Movie Review Dataset 訓練自然語言處理模型的範例程式:
from fastai.text.all import * # 下載 IMDb Large Movie Review Dataset,建立 DataLoader dls = TextDataLoaders.from_folder(untar_data(URLs.IMDB), valid='test') # 建立 Learner learn = text_classifier_learner(dls, AWD_LSTM, drop_mult=0.5, metrics=accuracy) # 訓練模型 learn.fine_tune(4, 1e-2)
模型訓練完成之後,可以使用此模型來辨識句子是屬於正面還是反面:
# 辨識句子屬於正面或反面 learn.predict("I really liked that movie!")
('pos', tensor(1), tensor([0.0041, 0.9959]))
這裡的結果中,第一個 'pos'
代表預測結果為正面(positive),而第二個 tensor(1)
則是代表 'pos'
的索引,而最後一個 tensor([0.0041, 0.9959])
則代表負面與正面的機率分別為 0.41% 與 99.59%。
以下是拿 Adult Data Set 資料集,以 fastai 訓練模型並預測一個人是否為高收入族群的範例:
from fastai.tabular.all import * # 下載 Adult Data Set path = untar_data(URLs.ADULT_SAMPLE) # 建立 DataLoader dls = TabularDataLoaders.from_csv(path/'adult.csv', path=path, y_names="salary", cat_names = ['workclass', 'education', 'marital-status', 'occupation', 'relationship', 'race'], cont_names = ['age', 'fnlwgt', 'education-num'], procs = [Categorify, FillMissing, Normalize]) # 建立 Learner learn = tabular_learner(dls, metrics=accuracy)
這裡在使用 TabularDataLoaders
建立 DataLoader
的時候,必須區分連續型(continuous)與類別型(categorical)的變數。
在這個例子中並沒有預訓練模型,所以我們改用 fit_one_cycle()
來進行模型訓練,從頭開始訓練模型時,時常都會使用這種方式。
# 訓練模型 learn.fit_one_cycle(3)
訓練完成之後,觀看幾筆預測結果:
# 觀看幾筆預測結果
learn.show_results()
以下是拿 MovieLens 資料集,以 fastai 訓練模型並預測人對於電影喜好程度的範例,也就是一個推薦系統:
from fastai.collab import * # 下載 MovieLens 資料集 path = untar_data(URLs.ML_SAMPLE) # 建立 DataLoader dls = CollabDataLoaders.from_csv(path/'ratings.csv') # 建立 Learner learn = collab_learner(dls, y_range=(0.5,5.5)) # 訓練模型 learn.fine_tune(10) # 觀看幾筆預測結果 learn.show_results()
由於這個例子所輸出的預測值屬於連續型的分數,所以在建立 Learner
要加上 y_range
參數指定輸出值的範圍。
雖然這個例子也沒有使用預訓練模型,但是仍然可以使用 fine_tune()
來訓練模型,有時候我們可以直接嘗試使用分別 fine_tune()
與 fit_one_cycle()
來訓練模型,選擇結果比較好的來使用。
若要查詢 fastai 中各種函數的使用說明,除了可以從 fastai 官方網站查詢之外,也可以使用 doc()
函數來查詢,例如若要查詢 learn.predict()
函數的使用方式,則可執行:
# 查詢函數使用方法
doc(learn.predict)
fastai 中內建了許多常用的資料集,包含影像分類、自然語言處理、影像分割、聲音分類、醫學影像等各類的資料集,詳細的資料集說明可以參考 fastai 的網頁。