PyTorch系列 | 快速入门迁移学习
點擊上方“算法猿的成長”,選擇“加為星標”
第一時間關注 AI 和 Python 知識
圖片來源:Pexels,作者:Arthur Ogleznev
2019 年第 68 篇文章,總第 92 篇文章
本文大約?6800?字,建議收藏閱讀
原題?| TRANSFER LEARNING TUTORIAL
作者?| Sasank Chilamkurthy
原文?| https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html#transfer-learning-tutorial
譯者?| kbsc13("算法猿的成長"公眾號作者)
聲明?| 翻譯是出于交流學習的目的,歡迎轉載,但請保留本文出于,請勿用作商業或者非法用途
簡介
本次教程主要介紹如何用深度學習實現遷移學習。更多更詳細的遷移學習知識可以查看 cs231n 課程--https://cs231n.github.io/transfer-learning/
實際應用中,很少人會從頭開始,通過隨機初始化來訓練一個卷積神經網絡,因為擁有足夠數量的數據集太少了。通常,大家都會選擇一個在比較大的數據集(比如 ImageNet 數據集,1000個類別總共120萬張圖片)上訓練好的預訓練模型,然后對卷積神經網絡進行初始化,或者用于提取特征。
遷移學習的兩大主要應用場景:
微調網絡:采用預訓練模型對網絡進行初始化,而不是隨機初始化,這種做法可以更快收斂,同時也能取得更好的效果。
作為一個特征提取器:這種應用會將除了最后的全連接層外的網絡層都固定權重參數,然后最后的全連接層會根據數據集類別數量進行修改輸出,并隨機初始化其權值,然后訓練該層。
本文的教程,代碼中需要導入的模型如下所示:
#?License:?BSD #?Author:?Sasank?Chilamkurthyfrom?__future__?import?print_function,?divisionimport?torch import?torch.nn?as?nn import?torch.optim?as?optim from?torch.optim?import?lr_scheduler import?numpy?as?np import?torchvision from?torchvision?import?datasets,?models,?transforms import?matplotlib.pyplot?as?plt import?time import?os import?copyplt.ion()???#?interactive?mode加載數據
加載數據這部分將采用?torchvision?和?torch.utils.data?兩個模塊。
本次教程的目標是訓練一個二分類模型,類別是螞蟻和蜜蜂,因此數據集中分別包含了 120 張螞蟻和蜜蜂的訓練圖片,然后每個類別還包含 75 張圖片作為驗證集。也就是說這個數據集總管只有 390 張圖片,不到一千張圖片,是一個非常小的數據集,如果從頭訓練模型,很難獲得很好的泛化能力。因此,本文將對這個數據集采用遷移學習的方法來得到更好的泛化能力。
獲取本文數據集和代碼,可以在公眾號后臺回復“pytorch遷移學習”獲取。
加載數據的代碼如下所示:
#?數據增強方法,訓練集會實現隨機裁剪和水平翻轉,然后進行歸一化 #?驗證集僅僅是裁剪和歸一化,并不會做數據增強 data_transforms?=?{'train':?transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485,?0.456,?0.406],?[0.229,?0.224,?0.225])]),'val':?transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485,?0.456,?0.406],?[0.229,?0.224,?0.225])]), } #?數據集所在文件夾 data_dir?=?'data/hymenoptera_data' image_datasets?=?{x:?datasets.ImageFolder(os.path.join(data_dir,?x),data_transforms[x])for?x?in?['train',?'val']} dataloaders?=?{x:?torch.utils.data.DataLoader(image_datasets[x],?batch_size=4,shuffle=True,?num_workers=4)for?x?in?['train',?'val']} dataset_sizes?=?{x:?len(image_datasets[x])?for?x?in?['train',?'val']} class_names?=?image_datasets['train'].classesdevice?=?torch.device("cuda:0"?if?torch.cuda.is_available()?else?"cpu")可視化圖片
首先可視化一些訓練圖片,以便于更好理解數據增強。代碼如下所示:
#?圖片展示的函數 def?imshow(inp,?title=None):"""Imshow?for?Tensor."""#?逆轉操作,從?tensor?變回?numpy?數組需要轉換通道位置inp?=?inp.numpy().transpose((1,?2,?0))mean?=?np.array([0.485,?0.456,?0.406])std?=?np.array([0.229,?0.224,?0.225])#?從歸一化后變回原始圖片inp?=?std?*?inp?+?meaninp?=?np.clip(inp,?0,?1)plt.imshow(inp)if?title?is?not?None:plt.title(title)plt.pause(0.001)??#?pause?a?bit?so?that?plots?are?updated#?獲取一個?batch?的訓練數據 inputs,?classes?=?next(iter(dataloaders['train']))#?Make?a?grid?from?batch out?=?torchvision.utils.make_grid(inputs)imshow(out,?title=[class_names[x]?for?x?in?classes])展示的圖片如下所示:
訓練模型
加載數據后,就是開始進行訓練模型,這里會介紹以下兩個內容:
制定學習率的策略
保存最佳模型
在下面的代碼中,參數?scheduler?是采用?torch.optim.lr_scheduler?初始化的 LR 策略對象:
#?訓練模型的函數 def?train_model(model,?criterion,?optimizer,?scheduler,?num_epochs=25):since?=?time.time()best_model_wts?=?copy.deepcopy(model.state_dict())best_acc?=?0.0for?epoch?in?range(num_epochs):print('Epoch?{}/{}'.format(epoch,?num_epochs?-?1))print('-'?*?10)#?每個?epoch?都分為訓練階段和驗證階段for?phase?in?['train',?'val']:#?注意訓練和驗證階段,需要分別對?model?的設置if?phase?==?'train':scheduler.step()model.train()??#?Set?model?to?training?modeelse:model.eval()???#?Set?model?to?evaluate?moderunning_loss?=?0.0running_corrects?=?0#?Iterate?over?data.for?inputs,?labels?in?dataloaders[phase]:inputs?=?inputs.to(device)labels?=?labels.to(device)#?清空參數的梯度optimizer.zero_grad()#?只有訓練階段才追蹤歷史with?torch.set_grad_enabled(phase?==?'train'):outputs?=?model(inputs)_,?preds?=?torch.max(outputs,?1)loss?=?criterion(outputs,?labels)#?訓練階段才進行反向傳播和參數的更新if?phase?==?'train':loss.backward()optimizer.step()#?記錄?loss?和?準確率running_loss?+=?loss.item()?*?inputs.size(0)running_corrects?+=?torch.sum(preds?==?labels.data)epoch_loss?=?running_loss?/?dataset_sizes[phase]epoch_acc?=?running_corrects.double()?/?dataset_sizes[phase]print('{}?Loss:?{:.4f}?Acc:?{:.4f}'.format(phase,?epoch_loss,?epoch_acc))#?deep?copy?the?modelif?phase?==?'val'?and?epoch_acc?>?best_acc:best_acc?=?epoch_accbest_model_wts?=?copy.deepcopy(model.state_dict())print()time_elapsed?=?time.time()?-?sinceprint('Training?complete?in?{:.0f}m?{:.0f}s'.format(time_elapsed?//?60,?time_elapsed?%?60))print('Best?val?Acc:?{:4f}'.format(best_acc))#?載入最好的模型參數model.load_state_dict(best_model_wts)return?model上述函數實現了模型的訓練,在一個 epoch 中分為訓練和驗證階段,訓練階段自然需要前向計算加反向傳播,并更新網絡層的參數,但驗證階段只需要前向計算,然后記錄 loss 和驗證集上的準確率即可。
此外就是需要設置保存模型的條件,這里是當每次驗證集的準確率都高于之前最好的準確率時,保存模型。
可視化模型的預測結果
下面定義了一個可視化模型預測結果的函數,用于展示圖片和模型對該圖片的預測類別信息:
#?可視化模型預測結果,即展示圖片和模型對該圖片的預測類別信息,默認展示?6?張圖片 def?visualize_model(model,?num_images=6):was_training?=?model.trainingmodel.eval()images_so_far?=?0fig?=?plt.figure()with?torch.no_grad():for?i,?(inputs,?labels)?in?enumerate(dataloaders['val']):inputs?=?inputs.to(device)labels?=?labels.to(device)outputs?=?model(inputs)_,?preds?=?torch.max(outputs,?1)for?j?in?range(inputs.size()[0]):images_so_far?+=?1ax?=?plt.subplot(num_images//2,?2,?images_so_far)ax.axis('off')ax.set_title('predicted:?{}'.format(class_names[preds[j]]))imshow(inputs.cpu().data[j])if?images_so_far?==?num_images:model.train(mode=was_training)returnmodel.train(mode=was_training)微調網絡
這部分就是本次遷移學習的核心內容,前面都是正常的加載數據、定義訓練過程的代碼,這里介紹的就是如何進行微調網絡,代碼如下所示:
#?加載?resnet18?網絡模型,并且設置加載預訓練模型 model_ft?=?models.resnet18(pretrained=True) num_ftrs?=?model_ft.fc.in_features #?修改輸出層的輸出數量,本次采用的數據集類別為?2 model_ft.fc?=?nn.Linear(num_ftrs,?2)model_ft?=?model_ft.to(device)criterion?=?nn.CrossEntropyLoss()#?對所有網絡層參數進行更新 optimizer_ft?=?optim.SGD(model_ft.parameters(),?lr=0.001,?momentum=0.9)#?學習率策略,每?7?個?epochs?乘以?0.1 exp_lr_scheduler?=?lr_scheduler.StepLR(optimizer_ft,?step_size=7,?gamma=0.1)這一步因為是設置加載預訓練模型,所以運行后會下載預訓練的?resnet18?網絡模型文件
訓練和驗證
接下來開始正式訓練網絡模型了,代碼如下所示,如果采用 cpu,大約需要 15-25 分鐘的過程,而如果采用 gpu,那么速度就很快了,基本一分鐘左右就訓練完成了。
model_ft?=?train_model(model_ft,?criterion,?optimizer_ft,?exp_lr_scheduler,num_epochs=25)訓練結果:
可視化模型預測結果:
visualize_model(model_ft)可視化結果如下:
用于特征提取器
剛剛是用于微調網絡,即將預訓練模型用于初始化網絡層的參數,接下來介紹遷移學習的第二種用法,作為特征提取器,也就是固定預訓練模型部分的網絡層的權值參數,這部分的實現代碼,如下所示,其中需要將卷積層部分的參數固定,即設置?requires_grad==False?,這樣在反向傳播過程就不會計算它們的梯度,更多內存可以查看 https://pytorch.org/docs/notes/autograd.html#excluding-subgraphs-from-backward
model_conv?=?torchvision.models.resnet18(pretrained=True) #?固定卷積層的權重參數 for?param?in?model_conv.parameters():param.requires_grad?=?False#?新的網絡層的參數默認?requires_grad=True num_ftrs?=?model_conv.fc.in_features model_conv.fc?=?nn.Linear(num_ftrs,?2)model_conv?=?model_conv.to(device)criterion?=?nn.CrossEntropyLoss()#?只對輸出層的參數進行更新 optimizer_conv?=?optim.SGD(model_conv.fc.parameters(),?lr=0.001,?momentum=0.9)#?學習率策略,每?7?個?epochs?乘以?0.1 exp_lr_scheduler?=?lr_scheduler.StepLR(optimizer_conv,?step_size=7,?gamma=0.1)再次進行訓練:
model_conv?=?train_model(model_conv,?criterion,?optimizer_conv,exp_lr_scheduler,?num_epochs=25)訓練結果:
可視化網絡的預測結果
visualize_model(model_conv)plt.ioff() plt.show()輸出結果:
小結
本文教程簡單介紹了遷移學習的內容,通過訓練一個二分類模型,簡單介紹遷移學習的兩個用法,微調網絡和用于固定的特征提取器。
本文代碼地址:
https://github.com/ccc013/DeepLearning_Notes/blob/master/Pytorch/pytorch_transfer_learning_example.ipynb
獲取本文的代碼和數據集方法:
關注公眾號“算法猿的成長”
公眾號會話界面回復“pytorch遷移學習"
歡迎關注我的微信公眾號--算法猿的成長,或者掃描下方的二維碼,大家一起交流,學習和進步!
如果覺得不錯,在看、轉發就是對小編的一個支持!
總結
以上是生活随笔為你收集整理的PyTorch系列 | 快速入门迁移学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unity摄影机depth模式_[蛮牛教
- 下一篇: 解决vbox挂载VBoxGuestAdd