遷移學習
標題1.什么是遷移學習
遷移學習(Transfer Learning)是一種機器學習方法,就是把為任務 A 開發 的模型作為初始點,重新使用在為任務 B 開發模型的過程中。遷移學習是通過 從已學習的相關任務中轉移知識來改進學習的新任務,雖然大多數機器學習算 法都是為了解決單個任務而設計的,但是促進遷移學習的算法的開發是機器學 習社區持續關注的話題。 遷移學習對人類來說很常見,例如,我們可能會發現 學習識別蘋果可能有助于識別梨,或者學習彈奏電子琴可能有助于學習鋼琴。
找到目標問題的相似性,遷移學習任務就是從相似性出發,將舊領域 (domain)學習過的模型應用在新領域上
標題2.遷移學習的步驟
1、選擇預訓練的模型和適當的層
通常,我們會選擇在大規模圖像數據集(如ImageNet)上預訓練的模型,如VGG、ResNet等。然后,根據新數據集的特點,選擇需要微調的模型層。對于低級特征的任務(如邊緣檢測),最好使用淺層模型的層,而對于高級特征的任務(如分類),則應選擇更深層次的模型。
2、凍結預訓練模型的參數
保持預訓練模型的權重不變,只訓練新增加的層或者微調一些層,避免因為在數據集中過擬合導致預訓練模型過度擬合。
3、在新數據集上訓練新增加的層
在凍結預訓練模型的參數情況下,訓練新增加的層。這樣,可以使新模型適應新的任務,從而獲得更高的性能。
4、微調預訓練模型的層
在新層上進行訓練后,可以解凍一些已經訓練過的層,并且將它們作為微調的目標。這樣做可以提高模型在新數據集上的性能。
5、評估和測試
在訓練完成之后,使用測試集對模型進行評估。如果模型的性能仍然不夠好,可以嘗試調整超參數或者更改微調層。
標題3.遷移學習實例
該實例使用的模型是ResNet-18殘差神經網絡模型
###1. 導入必要的庫
在import torch
import torchvision.models as models
from torch import nn
from torch.utils.data import Dataset,DataLoader
from torchvision import transforms
from PIL import Image
import numpy as np
這里導入了后續代碼會用到的庫,具體如下:
torch:PyTorch 深度學習框架的核心庫。
torchvision.models:包含了預訓練的模型,這里會用到 ResNet-18。
torch.nn:用于構建神經網絡的模塊。
torch.utils.data.Dataset 和 torch.utils.data.DataLoader:用于自定義數據集和加載數據。
torchvision.transforms:用于圖像的預處理。
PIL.Image:用于讀取圖像。
numpy:用于數值計算。
###2. 加載預訓練模型并修改全連接層
resnet_model= models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
for param in resnet_model.parameters():print(param)param.requires_grad=False
in_features=resnet_model.fc.in_features
resnet_model.fc=nn.Linear(in_features,20)
params_to_update=[]
for param in resnet_model.parameters():if param.requires_grad==True:params_to_update.append(param)
加載預訓練的 ResNet-18 模型。
把模型中所有參數的 requires_grad 設置為 False,也就是凍結這些參數,使其在訓練時不更新。
獲取原模型全連接層的輸入特征數,然后將全連接層替換為一個新的全連接層,輸出維度為 20。
收集所有 requires_grad 為 True 的參數,這些參數會在訓練時更新。
###3. 定義圖像預處理變換
data_transforms = {'train':transforms.Compose([transforms.Resize([300,300]),transforms.RandomRotation(45),transforms.CenterCrop(224),transforms.RandomHorizontalFlip(p=0.5),transforms.RandomVerticalFlip(p=0.5),transforms.RandomGrayscale(p=0.1),transforms.ToTensor(),transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])]),'valid':transforms.Compose([transforms.Resize([224,224]),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
}
定義了兩個圖像預處理的組合變換,分別用于訓練集和驗證集。
訓練集的變換包含了數據增強操作,像隨機旋轉、水平翻轉、垂直翻轉等。
驗證集的變換只包含了調整大小、轉換為張量和標準化操作。
4. 自定義數據集類
class food_dataset(Dataset):def __init__(self,file_path,transform=None):self.file_path = file_pathself.imgs = []self.labels = []self.transform = transformwith open(self.file_path) as f:samples = [x.strip().split(' ') for x in f.readlines()]for img_path,label in samples:self.imgs.append(img_path)self.labels.append(label)def __len__(self):return len(self.imgs)def __getitem__(self, idx):image = Image.open(self.imgs[idx])if self.transform:image = self.transform(image)label = self.labels[idx]label = torch.from_numpy(np.array(label,dtype=np.int64))return image,label
自定義了一個 food_dataset 類,繼承自 torch.utils.data.Dataset。 init 方法:解析包含圖像路徑和標簽的文本文件,把圖像路徑和標簽分別存到 self.imgs 和 self.labels 中。
len 方法:返回數據集的大小。
getitem 方法:根據索引讀取圖像,對圖像進行預處理,將標簽轉換為張量,然后返回圖像和標簽。
5. 創建數據集和數據加載器
training_data = food_dataset(file_path='./trainbig.txt',transform=data_transforms['train'])
test_data = food_dataset(file_path='./testbig.txt',transform=data_transforms['valid'])
train_dataloader = DataLoader(training_data,batch_size=64,shuffle=True)
test_dataloader = DataLoader(test_data,batch_size=64,shuffle=True)
創建訓練集和測試集的數據集對象。
創建訓練集和測試集的數據加載器,設置批量大小為 64,并且打亂數據
###6. 配置訓練設備、損失函數、優化器和學習率調度器
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
print(f"Using {device} device")
model=resnet_model.to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params_to_update,lr=0.001)
scheduler=torch.optim.lr_scheduler.StepLR(optimizer,step_size=5,gamma=0.5)
選擇合適的訓練設備(GPU 或 CPU)。
把模型移動到所選設備上。
定義交叉熵損失函數。
定義 Adam 優化器,只對之前收集的需要更新的參數進行優化。
定義學習率調度器,每 5 個 epoch 將學習率乘以 0.5。
###7. 定義訓練和測試函數
def train(dataloader,model,loss_fn,optimizer):model.train()batch_size_num = 1for X,y in dataloader:X,y = X.to(device),y.to(device)pred = model.forward(X)loss = loss_fn(pred,y)optimizer.zero_grad()loss.backward()optimizer.step()def test(dataloader, model,loss_fn):global best_accsize = len(dataloader.dataset)num_batches =len(dataloader)model.eval()test_loss,correct =0,0with torch.no_grad():for X, y in dataloader:X,y = X.to(device),y.to(device)pred = model.forward(X)test_loss+=loss_fn(pred,y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= num_batchescorrect /= sizeprint(f"Test result:\n Accuracy:{(100 * correct)}%, Avg loss: {test_loss}")acc_s.append(correct)loss_s.append(test_loss)if correct>best_acc:best_acc=correct
train 函數:將模型設置為訓練模式,遍歷訓練數據加載器,計算損失,反向傳播并更新模型參數。
test 函數:將模型設置為評估模式,遍歷測試數據加載器,計算測試集的準確率和平均損失,記錄最佳準確率。
8. 訓練模型并保存
epochs = 20
acc_s = []
loss_s =[]
for t in range(epochs):print(f"Epoch {t + 1}\n-----------")train(train_dataloader, model,loss_fn, optimizer)scheduler.step()test(test_dataloader,model,loss_fn)
print('最優訓練結果為:',best_acc)
torch.save(model.state_dict(), 'food_classification_model.pt')
訓練模型 20 個 epoch。
每個 epoch 結束后,更新學習率并進行測試。
打印最優訓練結果。
保存模型的參數到 food_classification_model.pt 文件中。