Pytorch 微调(Fine-tuning)
Pytorch 微調(diào)(Fine-tuning)
0. 環(huán)境介紹
環(huán)境使用 Kaggle 里免費建立的 Notebook
教程使用李沐老師的 動手學深度學習 網(wǎng)站和 視頻講解
小技巧:當遇到函數(shù)看不懂的時候可以按 Shift+Tab 查看函數(shù)詳解。
1. 微調(diào)
1.1 遷移學習(Transfer Learning)
假如我們想識別圖片中不同類型的椅子,然后向用戶推薦購買鏈接。 一種可能的方法是首先識別 100 把普通椅子,為每把椅子拍攝 1000 張不同角度的圖像,然后在收集的圖像數(shù)據(jù)集上訓(xùn)練一個分類模型。 共 100×1000=105100 \times 1000 = 10^5100×1000=105 張圖片,數(shù)量不到 ImageNet 的十分之一。適合 ImageNet 的復(fù)雜模型可能會在這個椅子圖片上過擬合。此外,由于訓(xùn)練樣本數(shù)量有限,訓(xùn)練模型的準確性可能無法滿足實際要求。
一個解決方案就是收集更多數(shù)據(jù)標記數(shù)據(jù)需要更多的時間和金錢成本。
另外一個解決策略就是遷移學習,遷移學習可以從源數(shù)據(jù)集學到的知識遷移到目標數(shù)據(jù)集。例如,盡管ImageNet數(shù)據(jù)集中的大多數(shù)圖像與椅子無關(guān),但在此數(shù)據(jù)集上訓(xùn)練的模型可能會提取更通用的圖像特征,這有助于識別邊緣、紋理、形狀和對象組合。 這些類似的特征也可能有效地識別椅子。
1.2 微調(diào)
一個神經(jīng)網(wǎng)絡(luò)一般可以分為兩塊:
- 特征提取將原始像素變成容易線性分割的特征(一般是卷積層)
- 線性分類器來做分類(一般是MLP 和 Softmax)
遷移學習中的常見技巧:微調(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)練
是一個目標數(shù)據(jù)集上的正常訓(xùn)練任務(wù),但是要使用更強的正則化:
- 使用更小的學習率
- 是用更少的數(shù)據(jù)迭代
源數(shù)據(jù)集遠復(fù)雜于目標數(shù)據(jù),通常微調(diào)的效果更好。
1.4 重用分類器權(quán)重
- 源數(shù)據(jù)集可能也有目標數(shù)據(jù)中的部分標號
- 可以使用預(yù)訓(xùn)練好模型分類器中對應(yīng)標號對應(yīng)的向量來做初始化
1.5 固定一些層
神經(jīng)網(wǎng)絡(luò)通常學習有層次的特征表示
- 低層次的特征更加通用
- 高層次的特征則更跟數(shù)據(jù)集相關(guān)
所以可以固定底部的一些層的參數(shù),使其不參與更新(更強的正則)
2. 代碼
2.1 導(dǎo)入數(shù)據(jù)
使用的熱狗數(shù)據(jù)集來源于網(wǎng)絡(luò)。 該數(shù)據(jù)集包含 1400 張熱狗的 “正類” 圖像,以及包含盡可能多的其他食物的 “負類” 圖像。 含著兩個類別的 1000 張圖片用于訓(xùn)練,其余的則用于測試。
解壓下載的數(shù)據(jù)集,我們獲得了兩個文件夾hotdog/train和hotdog/test。 這兩個文件夾都有hotdog(有熱狗)和not-hotdog(無熱狗)兩個子文件夾, 子文件夾內(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)建兩個實例來分別讀取訓(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 個正類樣本圖片和最后 8 張負類樣本圖片:
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);
對于 RGB(紅、綠和藍)顏色通道,我們分別標準化每個通道(這個是在 ImageNet 上做的操作,直接 Copy 過來),訓(xùn)練集隨機裁剪后 resize 到 224×224224 \times 224224×224,然后進行隨機的水平翻轉(zhuǎn);測試集 resize 到 256×256256 \times 256256×256 后 取中間的 224×224224 \times 224224×224 像素的圖像:
Normalize 第一個參數(shù)為三個通道的均值,第二個參數(shù)為標準差。
2.2 定義和初始化模型
使用在 ImageNet 數(shù)據(jù)集上預(yù)訓(xùn)練的 ResNet-18 作為源模型,其中 pretrained=True 表示自動下載預(yù)訓(xùn)練的模型參數(shù):
pretrained_net = torchvision.models.resnet18(pretrained=True)預(yù)訓(xùn)練的源模型實例包含許多特征層和一個輸出層 fc。 此劃分的主要目的是促進對除輸出層以外所有層的模型參數(shù)進行微調(diào)。 下面給出了源模型的成員變量 fc:
pretrained_net.fc
可以看到源模型是針對 ImageNet 數(shù)據(jù)集的千分類任務(wù)的,所以針對目標數(shù)據(jù)要進行修改,以下代碼將千分類變成 2 分類:
2.3 微調(diào)模型
如果為預(yù)訓(xùn)練模型,輸出層學習率調(diào)整為十倍:
# 如果param_group=True,輸出層中的模型參數(shù)將使用十倍的學習率 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 倍的學習率{'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)練精度還高。
為了進行比較,所有模型參數(shù)都進行初始化:
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 中自帶與目標數(shù)據(jù)相關(guān)的權(quán)重進行初始化
微調(diào)模型,輸出層兩個神經(jīng)元,把第一個神經(jīng)元(下標為 0)權(quán)重初始化為 ImageNet 中相關(guān)數(shù)據(jù)權(quán)重(如果想問為什么是第一個而不是第二個?因為熱狗訓(xùn)練數(shù)據(jù)集中正例的標簽值為 0,所以初始化第一個(下標為 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)# 因為我們是熱狗分類任務(wù), ImageNet 里面第 934 個類別也是熱狗, 可以直接拿出來作為目標模型的初始化 weight = finetune_net3.fc.weight hotdog_w = torch.split(weight.data, 1, dim=0)[934] print(hotdog_w.shape)# 輸出層兩個神經(jīng)元, 把第一個神經(jīng)元(下標為 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教程】鼠标右键显示菜单
- 下一篇: 用数据分析看泰坦尼克号