模型微调------学习笔记
遷移學習(Transfer Learning)
遷移學習是機器學習的分支,提出的初衷是節(jié)省人工標注樣本的時間,讓模型可以通過一個已有的標記數據向未標記數據領域進行遷移從而訓練出適用于該領域的模型,直接對目標域從頭開始學習成本太高,我們故而轉向運用已有的相關知識來輔助盡快地學習新知識。
舉一個例子就能很好的說明問題,我們學習編程的時候會學習什么?語法、特定語言的API、流程處理、面向對象,設計模式和面向對象是不用去學的,因為原理都是一樣的,甚至在學習C#的時候語法都可以少學很多,這就是遷移學習的概念呢,把統(tǒng)一的概念抽象出來,只學習不同的內容。
其實Transfer Learing和Fine tune并沒有嚴格的區(qū)分,只不過后者跟常用于形容遷移學習的后期微調。
模型微調(Fine tuning)
什么是模型微調?
針對于某個任務,自己的訓練數據不多時,那怎么辦?我們可以找到一個同類的別人訓練好的模型,把別人現成的訓練好了的模型(預訓練模型)拿過來,換成自己的數據。調整一下參數,再訓練一遍,這就是微調。PyTorch里面提供的網絡模型都是官方通過Imagenet的數據集與訓練好的數據,如果我們的訓練數據不夠,這些數據是可以作為基礎模型來使用的。在此我個人理解簡言之就是:預訓練模型拿過來用,改一下參數或者結構就叫微調。
為什么要微調?
1、對于數據集本身很小的情況,從頭開始訓練具有幾千萬參數的大型神經網絡是不現實的,因為越大的魔型對數據量的要求越大,過擬合無法避免。這時候如果還想用上大型神經網絡的超強特征提取能力,只能靠微調已經訓練好的模型。
2、可以降低訓練成本;如哦使用到處特征向量的方法進行遷移學習,后期的訓練成本非常低,用CPU都完全無壓力,沒有深度學習機器也可以做。
3、前人花很大精力訓練出來的模型在大概率上會比你自己從零開始搭的要強悍,沒有必要重復造輪子。
怎么微調?
對于不同的領域微調的方法也不一樣,比如語音識別領域一般微調前幾層,圖片識別問題微調后面幾層。對于圖片來說,我們CNN的前幾層學習到的都是低級的特征,比如點、線、面,這些低級的特征對于任何圖片來說都是可以抽象出來的。所以我們將他作為通用數據,只微調這些低級特征組合起來的高級特征即可,例如,這些點、線、面,組成的是圓還是橢圓,還是正方形,這些代表的含義都是我們需要后面訓練出來的。
對于語音來說,每個單詞表達的意思都是一樣的,只不過發(fā)音或者是單詞的拼寫不一樣,比如蘋果,apple,apfel,都表示的是同一個東西,只不過發(fā)音和單詞不一樣,但是他具體代表的含義是一樣的,就是高級特征是相同的,所以我們只要微調低級的特征就可以了。
下面只介紹下計算機視覺反向的微調,摘自https://cs231n.github.io/transfer-learning/
ConvNet as fixed feature extractor:其實這里有兩種做法:
1、使用最后一個fc layer之前的fc layer獲得的特征,學習個線性分類器(比如SVM);
2、重新訓練最后一個fc layer。
Fine tuing the ConvNet:固定前幾層的參數,只對最后幾層進行fine tuing,
對于以上兩種方案有一些微調的小技巧,比如先計算出預訓練模型的卷積層對所有訓練和測試數據的特征向量,然后拋開預訓練模型,只訓練自己定制的簡配版全連接網絡。這個方式的一個好處就是節(jié)省計算資源,每次迭代都不會再去跑全部的數據,而只是跑一下簡配版的全連接
Pretrained models:這個其實和第二種是一個意思,不過比較極端,使用整個pre trained的model作為初始化,然后fine tuning整個網絡而不是某些層,但是這個的計算量是非常大的,就只相當于做了一個初始化。
注意事項
1、新數據集和原始數據集合類似,那么直接可以微調一個最后的FC層或者重新指定一個新的分類器
2、新數據集比較小和原始數據集合差異性比較大,那么可以使用從模型的中部開始訓練,只對最后幾層進行fine tuing
3、新數據集比較小和原始數據集合差異性比較大,如果上面方法還是不行的話,那么最好是重新訓練,只將預訓練的模型作為一個新模型初始化的數據。
4、新數據集的大小一定要與原始數據集相同,比如CNN中輸入的圖片大小一定要相同,才不會報錯
5、如果數據集大小不同的話,可以在最后的fc層之前添加conv或者pool層,使得最后的輸出于fc層一致,但這樣會導致準確度大幅下降,所以不建議這樣做
6、對于不同的層可以設置不同的學習率,一般情況下建議,對于使用的原始數據做初始化的層設置的學習率要小于初始化的學習率,這樣保證對于已經初始化的數據不會扭曲的過快,而使用初始化學習率的新層可以快速的收斂。
微調實例
這里我們使用官方訓練好的resnet50來參加kaggle上的 dog breed狗的種類識別來做一個簡單的微調實例。
import torch, os, torchvision import torch.nn as nn import torch.nn.functional as F import pandas as pd import numpy as np import matplotlib.pyplot as plt from torch.utils.data import DataLoader, Dataset from torchvision import datasets, models, transforms from PIL import Image from sklearn.model_selection import StratifiedShuffleSplit# 定義一個數據集格式化 class DogDataset(Dataset):def __init__(self, labels_df, img_path, transform=None):self.labels_df = labels_dfself.img_path = img_pathself.transform = transformdef __len__(self):return self.labels_df.shape[0]def __getitem__(self, idx):image_name = os.path.join(self.img_path, self.labels_df.id[idx]) + '.jpg'img = Image.open(image_name)label = self.labels_df.label_idx[idx]if self.transform:img = self.transform(img)return img, labeldef train(model, device, train_loader, epoch):model.train()for batch_idx, data in enumerate(train_loader):x, y = datax = x.to(device)y = y.to(device)optimizer.zero_grad()y_hat = model(x)loss = criterion(y_hat, y)loss.backward()optimizer.step()print('Train Epoch: {}\t Loss: {:.6f}'.format(epoch, loss.item()))def test(model, device, test_loader):model.eval()test_loss = 0correct = 0with torch.no_grad():for i, data in enumerate(test_loader):x, y = datax = x.to(device)y = y.to(device)optimizer.zero_grad()y_hat = model(x)test_loss += criterion(y_hat, y).item()predict = y_hat.max(1, keepdim=True)[1] # get the index of the max log-probabilitycorrect += predict.eq(y.view_as(predict)).sum().item()test_loss /= len(test_loader.dataset)print('\n Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(100. * correct / len(val_dataset)))# 固定層的向量導出 def hook(module, input, output):for i in range(input[0].size(0)):in_list.append(input[0][i].cpu().numpy())if __name__ == '__main__':DATA_ROOT = '自己的文件目錄\Dog_Dataset'all_labels_df = pd.read_csv(os.path.join(DATA_ROOT, 'labels.csv'))# all_labels_df.head() 顯示頭部數據 默認前5個breeds = all_labels_df.breed.unique() # breed讀取全部標簽部分 unique剔除重復統(tǒng)計的標簽breed2idx = dict((breed, idx) for idx, breed in enumerate(breeds)) # dict的字典all_labels_df['label_idx'] = [breed2idx[b] for b in all_labels_df.breed]all_labels_df.head()#定義一些超參數IMG_SIZE = 224BATCH_SIZE = 8IMG_MEAN = [0.485, 0.456, 0.406]IMG_STD = [0.229, 0.224, 0.225]CUDA = torch.cuda.is_available()DEVICE = torch.device("cuda" if CUDA else "cpu")#定義數據初始化train_transforms = transforms.Compose([transforms.Resize(IMG_SIZE), # 縮放尺寸transforms.RandomResizedCrop(IMG_SIZE), # 隨機長寬比裁剪transforms.RandomHorizontalFlip(), # 依概率p水平翻轉ttransforms.RandomRotation(30), # 隨機旋轉 選擇30°transforms.ToTensor(),transforms.Normalize(IMG_MEAN, IMG_STD)])val_transforms = transforms.Compose([transforms.Resize(IMG_SIZE),transforms.CenterCrop(IMG_SIZE),transforms.ToTensor(),transforms.Normalize(IMG_MEAN, IMG_STD)])# 分割10%的數據dataset_names = ['train', 'valid']stratified_split = StratifiedShuffleSplit(n_splits=1, test_size=0.1, random_state=42) # 提供分層抽樣功能,確保每個標簽對應的樣本的比例train_split_idx, val_split_idx = next(iter(stratified_split.split(all_labels_df.id, all_labels_df.breed)))train_df = all_labels_df.iloc[train_split_idx].reset_index()val_df = all_labels_df.iloc[val_split_idx].reset_index()image_transforms = {'train': train_transforms, 'valid': val_transforms}train_dataset = DogDataset(train_df, os.path.join(DATA_ROOT, 'train'), transform=image_transforms['train'])val_dataset = DogDataset(val_df, os.path.join(DATA_ROOT, 'train'), transform=image_transforms['valid'])image_dataset = {'train': train_dataset, 'valid': val_dataset}# 使用官方的dataloader載入數據image_dataloader = {x: DataLoader(image_dataset[x], batch_size=BATCH_SIZE, shuffle=True, num_workers=0) for x in dataset_names}dataset_sizes = {x: len(image_dataset[x]) for x in dataset_names}#開始配置網絡,由于ImageNet是識別1000個物體,狗的分類一共只有120,所以需要對模型的最后一層全連接層#進行微調,將輸出從1000改為120 model_ft = models.resnet50(pretrained=True) #自動下載官方的預訓練模型for param in model_ft.parameters():param.requires_grad = Falsenum_fc_ftr = model_ft.fc.in_featuresmodel_ft.fc = nn.Linear(num_fc_ftr, len(breeds))model_ft = model_ft.to(DEVICE)criterion = nn.CrossEntropyLoss()optimizer = torch.optim.Adam([{'params': model_ft.fc.parameters()}], lr=0.001)#訓練查看效果 這里要拼顯卡,實在不行整個服務器。for epoch in range(1, 10):train(model=model_ft, device=DEVICE, train_loader=image_dataloader["train"], epoch=epoch)test(model=model_ft, device=DEVICE, test_loader=image_dataloader["valid"])#但是這里每次訓練都需要將一張圖片在全部網絡中進行計算,而且每次的計算結果都是一樣的。為了提高效率,#我們將這些不更新網絡權值參數層的計算結果保存下來,這樣就可以直接將這些結果輸入到fc層或者以這些結#果建立新的網絡層。#如何將固定層的向量輸出in_list = []model_ft.avgpool.register_forward_hook(hook)with torch.no_gard():for batch_idx, data in enumerate(image_dataloader["train"]):x, y = data;x = x.to(DEVICE)y = y.to(DEVICE)y_hat = model_ft(x)features = np.array(in_list)np.save("features",features)#這樣再訓練時我們就只需將這個數組讀出來,然后就可以直接使用這個數組在輸入到線性層或者sigmoid層就可以了。總結
以上是生活随笔為你收集整理的模型微调------学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 固定资产模块-12月末结账提示制单业务未
- 下一篇: ireport oracle,用irep