Pytorch 微调(Fine-tuning)
Pytorch 微調(diào)(Fine-tuning)
0. 環(huán)境介紹
環(huán)境使用 Kaggle 里免費(fèi)建立的 Notebook
教程使用李沐老師的 動(dòng)手學(xué)深度學(xué)習(xí) 網(wǎng)站和 視頻講解
小技巧:當(dāng)遇到函數(shù)看不懂的時(shí)候可以按 Shift+Tab 查看函數(shù)詳解。
1. 微調(diào)
1.1 遷移學(xué)習(xí)(Transfer Learning)
假如我們想識(shí)別圖片中不同類型的椅子,然后向用戶推薦購買鏈接。 一種可能的方法是首先識(shí)別 100 把普通椅子,為每把椅子拍攝 1000 張不同角度的圖像,然后在收集的圖像數(shù)據(jù)集上訓(xùn)練一個(gè)分類模型。 共 100×1000=105100 \times 1000 = 10^5100×1000=105 張圖片,數(shù)量不到 ImageNet 的十分之一。適合 ImageNet 的復(fù)雜模型可能會(huì)在這個(gè)椅子圖片上過擬合。此外,由于訓(xùn)練樣本數(shù)量有限,訓(xùn)練模型的準(zhǔn)確性可能無法滿足實(shí)際要求。
一個(gè)解決方案就是收集更多數(shù)據(jù)標(biāo)記數(shù)據(jù)需要更多的時(shí)間和金錢成本。
 另外一個(gè)解決策略就是遷移學(xué)習(xí),遷移學(xué)習(xí)可以從源數(shù)據(jù)集學(xué)到的知識(shí)遷移到目標(biāo)數(shù)據(jù)集。例如,盡管ImageNet數(shù)據(jù)集中的大多數(shù)圖像與椅子無關(guān),但在此數(shù)據(jù)集上訓(xùn)練的模型可能會(huì)提取更通用的圖像特征,這有助于識(shí)別邊緣、紋理、形狀和對(duì)象組合。 這些類似的特征也可能有效地識(shí)別椅子。
1.2 微調(diào)
一個(gè)神經(jīng)網(wǎng)絡(luò)一般可以分為兩塊:
- 特征提取將原始像素變成容易線性分割的特征(一般是卷積層)
 - 線性分類器來做分類(一般是MLP 和 Softmax)
 
遷移學(xué)習(xí)中的常見技巧:微調(diào)。
 微調(diào)通過使用在大數(shù)據(jù)上得到的預(yù)訓(xùn)練好的模型來初始化模型權(quán)重,從而提升精度。這就要求預(yù)訓(xùn)練模型質(zhì)量要有保證。微調(diào)通常速度更快、精度更高。
微調(diào)步驟:
1.3 加入微調(diào)訓(xùn)練
是一個(gè)目標(biāo)數(shù)據(jù)集上的正常訓(xùn)練任務(wù),但是要使用更強(qiáng)的正則化:
- 使用更小的學(xué)習(xí)率
 - 是用更少的數(shù)據(jù)迭代
 
源數(shù)據(jù)集遠(yuǎn)復(fù)雜于目標(biāo)數(shù)據(jù),通常微調(diào)的效果更好。
1.4 重用分類器權(quán)重
- 源數(shù)據(jù)集可能也有目標(biāo)數(shù)據(jù)中的部分標(biāo)號(hào)
 - 可以使用預(yù)訓(xùn)練好模型分類器中對(duì)應(yīng)標(biāo)號(hào)對(duì)應(yīng)的向量來做初始化
 
1.5 固定一些層
神經(jīng)網(wǎng)絡(luò)通常學(xué)習(xí)有層次的特征表示
- 低層次的特征更加通用
 - 高層次的特征則更跟數(shù)據(jù)集相關(guān)
 
所以可以固定底部的一些層的參數(shù),使其不參與更新(更強(qiáng)的正則)
2. 代碼
2.1 導(dǎo)入數(shù)據(jù)
使用的熱狗數(shù)據(jù)集來源于網(wǎng)絡(luò)。 該數(shù)據(jù)集包含 1400 張熱狗的 “正類” 圖像,以及包含盡可能多的其他食物的 “負(fù)類” 圖像。 含著兩個(gè)類別的 1000 張圖片用于訓(xùn)練,其余的則用于測試。
解壓下載的數(shù)據(jù)集,我們獲得了兩個(gè)文件夾hotdog/train和hotdog/test。 這兩個(gè)文件夾都有hotdog(有熱狗)和not-hotdog(無熱狗)兩個(gè)子文件夾, 子文件夾內(nèi)都包含相應(yīng)類的圖像。
!pip install -U d2l %matplotlib inline import os import torch import torchvision from torch import nn from d2l import torch as d2ld2l.DATA_HUB['hotdog'] = (d2l.DATA_URL + 'hotdog.zip','fba480ffa8aa7e0febbb511d181409f899b9baa5')data_dir = d2l.download_extract('hotdog')創(chuàng)建兩個(gè)實(shí)例來分別讀取訓(xùn)練和測試數(shù)據(jù)集中的所有圖像文件:
train_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train')) test_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'))顯示前 8 個(gè)正類樣本圖片和最后 8 張負(fù)類樣本圖片:
hotdogs = [train_imgs[i][0] for i in range(8)] not_hotdogs = [train_imgs[-i - 1][0] for i in range(8)] d2l.show_images(hotdogs + not_hotdogs, 2, 8, scale=1.4);
 對(duì)于 RGB(紅、綠和藍(lán))顏色通道,我們分別標(biāo)準(zhǔn)化每個(gè)通道(這個(gè)是在 ImageNet 上做的操作,直接 Copy 過來),訓(xùn)練集隨機(jī)裁剪后 resize 到 224×224224 \times 224224×224,然后進(jìn)行隨機(jī)的水平翻轉(zhuǎn);測試集 resize 到 256×256256 \times 256256×256 后 取中間的 224×224224 \times 224224×224 像素的圖像:
Normalize 第一個(gè)參數(shù)為三個(gè)通道的均值,第二個(gè)參數(shù)為標(biāo)準(zhǔn)差。
2.2 定義和初始化模型
使用在 ImageNet 數(shù)據(jù)集上預(yù)訓(xùn)練的 ResNet-18 作為源模型,其中 pretrained=True 表示自動(dòng)下載預(yù)訓(xùn)練的模型參數(shù):
pretrained_net = torchvision.models.resnet18(pretrained=True)預(yù)訓(xùn)練的源模型實(shí)例包含許多特征層和一個(gè)輸出層 fc。 此劃分的主要目的是促進(jìn)對(duì)除輸出層以外所有層的模型參數(shù)進(jìn)行微調(diào)。 下面給出了源模型的成員變量 fc:
pretrained_net.fc
 可以看到源模型是針對(duì) ImageNet 數(shù)據(jù)集的千分類任務(wù)的,所以針對(duì)目標(biāo)數(shù)據(jù)要進(jìn)行修改,以下代碼將千分類變成 2 分類:
2.3 微調(diào)模型
如果為預(yù)訓(xùn)練模型,輸出層學(xué)習(xí)率調(diào)整為十倍:
# 如果param_group=True,輸出層中的模型參數(shù)將使用十倍的學(xué)習(xí)率 def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5,param_group=True):train_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'), transform=train_augs),batch_size=batch_size, shuffle=True)test_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'), transform=test_augs),batch_size=batch_size)devices = d2l.try_all_gpus()loss = nn.CrossEntropyLoss(reduction="none")if param_group:# 非最后一層全拿出來params_1x = [param for name, param in net.named_parameters()if name not in ["fc.weight", "fc.bias"]]trainer = torch.optim.SGD([{'params': params_1x},## 最后一層用 10 倍的學(xué)習(xí)率{'params': net.fc.parameters(),'lr': learning_rate * 10}],lr=learning_rate, weight_decay=0.001)else:trainer = torch.optim.SGD(net.parameters(), lr=learning_rate,weight_decay=0.001)d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,devices)2.4 訓(xùn)練
使用預(yù)訓(xùn)練模型:
train_fine_tuning(finetune_net, 5e-5)
 可以發(fā)現(xiàn)剛開始精度就很高了,測試精度比訓(xùn)練精度還高。
為了進(jìn)行比較,所有模型參數(shù)都進(jìn)行初始化:
scratch_net = torchvision.models.resnet18() scratch_net.fc = nn.Linear(scratch_net.fc.in_features, 2) train_fine_tuning(scratch_net, 5e-4, param_group=False)
 可以看到效果沒有微調(diào)模型好。
2.5 凍結(jié)全連接層之前參數(shù)
finetune_net2 = torchvision.models.resnet18(pretrained=True) finetune_net2.fc = nn.Linear(finetune_net2.fc.in_features, 2) nn.init.xavier_uniform_(finetune_net2.fc.weight)for name, param in finetune_net2.named_parameters():if 'fc' not in name:param.requires_grad = Falsetrain_fine_tuning(finetune_net2, 5e-5)2.6 使用 ImageNet 中自帶與目標(biāo)數(shù)據(jù)相關(guān)的權(quán)重進(jìn)行初始化
微調(diào)模型,輸出層兩個(gè)神經(jīng)元,把第一個(gè)神經(jīng)元(下標(biāo)為 0)權(quán)重初始化為 ImageNet 中相關(guān)數(shù)據(jù)權(quán)重(如果想問為什么是第一個(gè)而不是第二個(gè)?因?yàn)闊峁酚?xùn)練數(shù)據(jù)集中正例的標(biāo)簽值為 0,所以初始化第一個(gè)(下標(biāo)為 0)神經(jīng)元的權(quán)重參數(shù)):
finetune_net3 = torchvision.models.resnet18(pretrained=True) finetune_net3.fc = nn.Linear(finetune_net3.fc.in_features, 2) nn.init.xavier_uniform_(finetune_net3.fc.weight)# 因?yàn)槲覀兪菬峁贩诸惾蝿?wù), ImageNet 里面第 934 個(gè)類別也是熱狗, 可以直接拿出來作為目標(biāo)模型的初始化 weight = finetune_net3.fc.weight hotdog_w = torch.split(weight.data, 1, dim=0)[934] print(hotdog_w.shape)# 輸出層兩個(gè)神經(jīng)元, 把第一個(gè)神經(jīng)元(下標(biāo)為 0)權(quán)重初始化為 ImageNet 中相關(guān)數(shù)據(jù)權(quán)重 finetune_net3.fc.weight[0].data = hotdog_wtrain_fine_tuning(finetune_net3, 5e-5)
 
總結(jié)
以上是生活随笔為你收集整理的Pytorch 微调(Fine-tuning)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【Axure教程】鼠标右键显示菜单
 - 下一篇: 用数据分析看泰坦尼克号