1. 使用現有數據集進行分類
圖像數據為Oxford-IIIT Pet Dataset(12類貓和25類狗,共37類),這里僅使用原始圖片集images.tar.gz
數據准備
import numpy as np
from fastai.vision import *
from fastai.metrics import error_rate
path_img = 'data/pets/images'
bs = 64 #batch size
fnames = get_image_files(path_img) #get filenames(absolute path) from path_img
pat = re.compile(r'/([^/]+)_d+.jpg$') #get labels from filenames(e.g., 'american_bulldog' from 'data/pets/images/american_bulldog_20.jpg')
### ImageDataBunch
### 使用正則表達式pat從圖像文件名fnames中提取標簽,並和圖像對應起來
### ds_tfms: 圖像轉換(翻轉、旋轉、裁剪、放大等),用於圖像數據增強(data augmentation)
### size: 最終圖像尺寸, bs: batch size, valid_pct: train/valid split
### normalize: 使用提供的均值和標准差(每個通道對應一個均值和標准差)對圖像數據進行歸一化
np.random.seed(2)
data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(), size=224, bs=bs, valid_pct=0.2).normalize(imagenet_stats)
data.show_batch(rows=3, figsize=(7,6)) #grab a batch and display 3x3 images
模型搭建和訓練
使用Resnet34進行遷移學習,首先通過lr_find確定最大學習率,再通過fit_one_cycle(1-Cycle style)進行訓練
lr_find: 在前面幾次的迭代中將學習率從一個很小的值逐漸增加,選擇損失函數(train loss)處於下降趨勢之中並且距離損失停止下降的拐點有一定距離的點做為模型的最大學習率max_lr
fit_one_cycle: 共分為兩個階段,在第一階段學習率從max_lr/div_factor線性增長到max_lr,momentum線性地從moms[0]降到moms[1];第二階段學習率以余弦形式從max_lr降為0,momentum也同樣按余弦形式從moms[1]增長到moms[0]。第一階段的迭代次數占總迭代次數的比例為pct_start
學習率和momentum: , , , 其中是要更新的參數,G為梯度, 為學習率, 為momentum
### Use Resnet34 to classify images
learn = create_cnn(data, models.resnet34, metrics=error_rate)
print(learn.model) #model summary
learn.lr_find()
learn.recorder.plot() #由左上圖可以看出max_lr可選擇函數fit_one_cycle的默認值0.003
learn.fit_one_cycle(4, max_lr=slice(0.003), div_factor=25.0, moms=(0.95, 0.85), pct_start=0.3) #4 epochs
learn.recorder.plot_lr(show_moms=True) #中上圖(學習率)和右上圖(momentum), x軸表示迭代次數
learn.save('stage-1') #save model
### Unfreeze all the model layers and keep training
learn.unfreeze()
learn.lr_find()
learn.recorder.plot() #左下圖
### 由左下圖可以看出max_lr可選擇1e-6, 但是模型的不同層可以設置不同的學習率加速訓練
### 模型的前面幾層的學習率設置為max_lr, 后面幾層的學習率可以適當增加(例如可以設置成比上一個fit_one_cycle的學習率小一個量級)
### slice(1e-6,1e-4)表示模型每層的學習率由1e-6逐漸增加過渡到1e-4
learn.fit_one_cycle(2, max_lr=slice(1e-6,1e-4), div_factor=25.0, moms=(0.95, 0.85), pct_start=0.3) #2 epochs
learn.recorder.plot_lr(show_moms=True) #中下圖(模型最后一層的學習率)和右下圖(momentum)
可視化
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(figsize=(12,12), dpi=60) #confusion matrix
print(interp.most_confused(min_val=2)) #從大到小列出混淆矩陣中非對角線的最大的幾個元素
2. 從谷歌圖片下載數據並進行分類
獲得圖片鏈接
打開谷歌圖片,輸入想要下載的圖像類別,頁面上出現的圖片即為可下載的圖片
打開JavaScript Console(Windows/Linux:Ctrl+Shift+J, Mac:Cmd+Opt+J),運行下面的命令獲取圖片鏈接
大專欄 使用fastai完成圖像分類 class="nx">urls = Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el=>JSON.parse(el.textContent).ou);
window.open('data:text/csv;charset=utf-8,' + escape(urls.join('n')));
分別搜索teddy bears、 black bears、 grizzly bears, 將下載的保存鏈接的文件分別命名為urls_teddys.txt、 urls_black.txt、 urls_grizzly.txt
下載圖片
import numpy as np
from fastai.vision import *
from fastai.metrics import error_rate
### 建立目錄並下載圖片
path = Path('data/bears')
folders = ['teddys', 'black', 'grizzly']
files = 'urls_teddys.txt', 'urls_black.txt', 'urls_grizzly.txt'
for i,folder in enumerate(folders):
dest = path/folder
dest.mkdir(parents=True, exist_ok=True)
download_images(files[i], dest, max_pics=200)
print(path.ls())
### 刪除不能被打開的圖片
for folder in folders:
verify_images(path/folder, delete=True, max_size=500)
訓練模型
np.random.seed(42)
data = ImageDataBunch.from_folder(path, train=".", valid_pct=0.2, ds_tfms=get_transforms(), size=224, bs=64, num_workers=4).normalize(imagenet_stats)
print(data.classes)
learn = create_cnn(data, models.resnet34, metrics=error_rate)
learn.lr_find()
learn.recorder.plot() #左圖
learn.fit_one_cycle(4)
learn.save('stage-1')
learn.unfreeze()
learn.lr_find()
learn.recorder.plot() #右圖
learn.fit_one_cycle(2, max_lr=slice(3e-5,3e-4)) #若數據量較小,該步不一定有正效果
learn.save('stage-2')
learn.load('stage-1') #選擇stage-1
interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()
根據訓練好的模型去除錯誤圖片
模型預測效果不好不一定是因為模型本身的問題,還可能是由於圖片自身的問題(例如下載了錯誤的圖片,圖片標簽有誤),需要進行檢查和處理
from fastai.widgets import *
### ds: 訓練圖片集, idxs: 具有最大損失的訓練圖片索引
ds, idxs = DatasetFormatter().from_toplosses(learn, n_imgs=200) #選出前200個具有最大損失的訓練圖片
ImageCleaner(ds, idxs, path) #手動處理,處理好的文件被存入path/cleaned.csv(該文件僅包含經過處理后的訓練圖片集,不包含驗證圖片)
可根據具體情況對處理之后的數據重新進行訓練
保存模型並預測
learn.export() #將模型存入learn.path/export.pkl
learn = load_learner(path) #從path中讀取模型
img = open_image(path/'black'/'00000021.jpg') #以訓練集中的一個圖片為例
pred_class,pred_idx,outputs = learn.predict(img) #預測圖片
print(pred_class) #輸出類別
print(outputs) #輸出每個類的概率