天天看點

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼

在訓練深度學習模型時,有時候我們沒有海量的訓練樣本,隻有少數的訓練樣本(比如幾百個圖檔),幾百個訓練樣本顯然對于深度學習遠遠不夠。這時候,我們可以使用别人預訓練好的網絡模型權重,在此基礎上進行訓練,這就引入了一個概念——遷移學習(Transfer Learning)。

什麼是遷移學習

遷移學習(Transfer Learning,TL)對于人類來說,就是掌握舉一反三的學習能力。比如我們學會騎自行車後,學騎機車就很簡單了;在學會打羽毛球之後,再學打網球也就沒那麼難了。對于計算機而言,所謂遷移學習,就是能讓現有的模型算法稍加調整即可應用于一個新的領域和功能的一項技術

如何進行遷移學習

  • 首先需要選擇一個預訓練好的模型,需要注意的是該模型的訓練過程最好與我們要進行訓練的任務相似。比如我們要訓練一個Cat,dog圖像分類的模型,最好應該選擇一個圖像分類的預訓練模型。
  • 針對實際任務,對網絡結構進行調整。比如找到了一個預訓練好的AlexNet(1000類别), 但是我們實際的任務的2分類,是以需要把最後一層的全連接配接輸出改為2.
In practice, very few people train an entire Convolutional Network from scratch (with random initialization), because it is relatively rare to have a dataset of sufficient size. Instead, it is common to pretrain a ConvNet on a very large dataset (e.g. ImageNet, which contains 1.2 million images with 1000 categories), and then use the ConvNet either as an initialization or a fixed feature extractor for the task of interest.

作者:俠之大者_7d3f

連結:https://www.jianshu.com/p/d04c17368922

來源:簡書

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

ResNet-18

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼

ResNet全名Residual Network殘差網絡。Kaiming He 的《Deep Residual Learning for Image Recognition》獲得了CVPR最佳論文。它在保證網絡精度的前提下,将網絡的深度達到了152層,後來又進一步加到1000的深度。論文的開篇先是說明了深度網絡的好處:特征等級随着網絡的加深而變高,網絡的表達能力也會大大提高。是以論文中提出了一個問題:是否可以通過疊加網絡層數來獲得一個更好的網絡呢?作者經過實驗發現,單純的把網絡疊起來的深層網絡的效果反而不如合适層數的較淺的網絡效果。是以何恺明等人在普通平原網絡的基礎上增加了一個shortcut, 構成一個residual block。此時拟合目标就變為F(x),F(x)就是殘差:

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼

資料集準備

  • 訓練集
  • 驗證集

訓練集,驗證集 分别包含2個子檔案夾,這是一個2分類問題。分類對象:螞蟻,蜜蜂

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼
Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼
Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼
  • 代碼

    因為訓練一個2分類的模型,資料集加載直接使用pytorch提供的API——

    ImageFolder

    最友善。原始圖像為jpg格式,在制作資料集時候進行了變換transforms。 加入對GPU的支援,首先判斷

    torch.cuda.is_available()

    ,然後決定使用GPU or CPU
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.utils.data import DataLoader
import torchvision
from torchvision.transforms import transforms
from torchvision import models
from torchvision.models import ResNet
import numpy as np
import matplotlib.pyplot as plt
import os
import utils


data_dir = './data/hymenoptera_data'

train_dataset = torchvision.datasets.ImageFolder(root=os.path.join(data_dir, 'train'),
                                                 transform=transforms.Compose(
                                                     [
                                                         transforms.RandomResizedCrop(224),
                                                         transforms.RandomHorizontalFlip(),
                                                         transforms.ToTensor(),
                                                         transforms.Normalize(
                                                             mean=(0.485, 0.456, 0.406),
                                                             std=(0.229, 0.224, 0.225))
                                                     ]))

val_dataset = torchvision.datasets.ImageFolder(root=os.path.join(data_dir, 'val'),
                                               transform=transforms.Compose(
                                                     [
                                                         transforms.RandomResizedCrop(224),
                                                         transforms.RandomHorizontalFlip(),
                                                         transforms.ToTensor(),
                                                         transforms.Normalize(
                                                             mean=(0.485, 0.456, 0.406),
                                                             std=(0.229, 0.224, 0.225))
                                                     ]))

train_dataloader = DataLoader(dataset=train_dataset, batch_size=4, shuffle=4)
val_dataloader = DataLoader(dataset=val_dataset, batch_size=4, shuffle=4)

# 類别名稱
class_names = train_dataset.classes
print('class_names:{}'.format(class_names))

# 訓練裝置  CPU/GPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('trian_device:{}'.format(device.type))

# 随機顯示一個batch
plt.figure()
utils.imshow(next(iter(train_dataloader)))
plt.show()
           

擷取預訓練模型

torchvision.models
           

torchvision

中包含了一些常見的預訓練模型

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼

AlexNet, VGG, SqueezeNet, Resnet,Inception, DenseNet

此次實驗采用ResNet18網絡模型。

torchvision.models

中包含

resnet18

,首先會執行個體化一個ResNet網絡, 然後

model.load_dict()

加載預訓練好的模型。

def resnet18(pretrained=False, **kwargs):
    """Constructs a ResNet-18 model.

    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
    """
    model = ResNet(BasicBlock, [2, 2, 2, 2], **kwargs)
    if pretrained:
        model.load_state_dict(model_zoo.load_url(model_urls['resnet18']))
    return model
           

torchvision 預設将模型儲存在/home/.torch/models路徑。

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼

預訓練模型檔案:

Pytorch學習筆記(11)———遷移學習Transfer Learning完整代碼
  • 代碼

    加載預訓練模型。需要注意的地方:修改ResNet最後一個全連接配接層的輸出個數,二分類問題需要将輸出個數改為2。

# -------------------------模型選擇,優化方法, 學習率政策----------------------
model = models.resnet18(pretrained=True)

# 全連接配接層的輸入通道in_channels個數
num_fc_in = model.fc.in_features

# 改變全連接配接層,2分類問題,out_features = 2
model.fc = nn.Linear(num_fc_in, 2)

# 模型遷移到CPU/GPU
model = model.to(device)

# 定義損失函數
loss_fc = nn.CrossEntropyLoss()

# 選擇優化方法
optimizer = optim.SGD(model.parameters(), lr=0.0001, momentum=0.9)

# 學習率調整政策
# 每7個epoch調整一次
exp_lr_scheduler = lr_scheduler.StepLR(optimizer=optimizer, step_size=10, gamma=0.5)  # step_size

           

訓練,測試網絡

Epoch: 訓練50個epoch

注意地方: 訓練時候,需要調用

model.train()

将模型設定為訓練模式。測試時候,調用

model.eval()

将模型設定為測試模型,否則訓練和測試結果不正确。

# ----------------訓練過程-----------------
num_epochs = 50

for epoch in range(num_epochs):

    running_loss = 0.0
    exp_lr_scheduler.step()

    for i, sample_batch in enumerate(train_dataloader):
        inputs = sample_batch[0]
        labels = sample_batch[1]

        model.train()

        # GPU/CPU
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        # foward
        outputs = model(inputs)

        # loss
        loss = loss_fc(outputs, labels)

        # loss求導,反向
        loss.backward()

        # 優化
        optimizer.step()

        #
        running_loss += loss.item()

        # 測試
        if i % 20 == 19:
            correct = 0
            total = 0
            model.eval()
            for images_test, labels_test in val_dataloader:
                images_test = images_test.to(device)
                labels_test = labels_test.to(device)

                outputs_test = model(images_test)
                _, prediction = torch.max(outputs_test, 1)
                correct += (torch.sum((prediction == labels_test))).item()
               # print(prediction, labels_test, correct)
                total += labels_test.size(0)
            print('[{}, {}] running_loss = {:.5f} accurcay = {:.5f}'.format(epoch + 1, i + 1, running_loss / 20,
                                                                        correct / total))
            running_loss = 0.0

        # if i % 10 == 9:
        #     print('[{}, {}] loss={:.5f}'.format(epoch+1, i+1, running_loss / 10))
        #     running_loss = 0.0

print('training finish !')
torch.save(model.state_dict(), './model/model_2.pth')
           

完整代碼

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.utils.data import DataLoader
import torchvision
from torchvision.transforms import transforms
from torchvision import models
from torchvision.models import ResNet
import numpy as np
import matplotlib.pyplot as plt
import os
import utils


data_dir = './data/hymenoptera_data'

train_dataset = torchvision.datasets.ImageFolder(root=os.path.join(data_dir, 'train'),
                                                 transform=transforms.Compose(
                                                     [
                                                         transforms.RandomResizedCrop(224),
                                                         transforms.RandomHorizontalFlip(),
                                                         transforms.ToTensor(),
                                                         transforms.Normalize(
                                                             mean=(0.485, 0.456, 0.406),
                                                             std=(0.229, 0.224, 0.225))
                                                     ]))

val_dataset = torchvision.datasets.ImageFolder(root=os.path.join(data_dir, 'val'),
                                               transform=transforms.Compose(
                                                     [
                                                         transforms.RandomResizedCrop(224),
                                                         transforms.RandomHorizontalFlip(),
                                                         transforms.ToTensor(),
                                                         transforms.Normalize(
                                                             mean=(0.485, 0.456, 0.406),
                                                             std=(0.229, 0.224, 0.225))
                                                     ]))

train_dataloader = DataLoader(dataset=train_dataset, batch_size=4, shuffle=4)
val_dataloader = DataLoader(dataset=val_dataset, batch_size=4, shuffle=4)

# 類别名稱
class_names = train_dataset.classes
print('class_names:{}'.format(class_names))

# 訓練裝置  CPU/GPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('trian_device:{}'.format(device.type))

# 随機顯示一個batch
#plt.figure()
#utils.imshow(next(iter(train_dataloader)))
#plt.show()

# -------------------------模型選擇,優化方法, 學習率政策----------------------
model = models.resnet18(pretrained=True)

# 全連接配接層的輸入通道in_channels個數
num_fc_in = model.fc.in_features

# 改變全連接配接層,2分類問題,out_features = 2
model.fc = nn.Linear(num_fc_in, 2)

# 模型遷移到CPU/GPU
model = model.to(device)

# 定義損失函數
loss_fc = nn.CrossEntropyLoss()

# 選擇優化方法
optimizer = optim.SGD(model.parameters(), lr=0.0001, momentum=0.9)

# 學習率調整政策
# 每7個epoch調整一次
exp_lr_scheduler = lr_scheduler.StepLR(optimizer=optimizer, step_size=10, gamma=0.5)  # step_size


# ----------------訓練過程-----------------
num_epochs = 50

for epoch in range(num_epochs):

    running_loss = 0.0
    exp_lr_scheduler.step()

    for i, sample_batch in enumerate(train_dataloader):
        inputs = sample_batch[0]
        labels = sample_batch[1]

        model.train()

        # GPU/CPU
        inputs = inputs.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()

        # foward
        outputs = model(inputs)

        # loss
        loss = loss_fc(outputs, labels)

        # loss求導,反向
        loss.backward()

        # 優化
        optimizer.step()

        #
        running_loss += loss.item()

        # 測試
        if i % 20 == 19:
            correct = 0
            total = 0
            model.eval()
            for images_test, labels_test in val_dataloader:
                images_test = images_test.to(device)
                labels_test = labels_test.to(device)

                outputs_test = model(images_test)
                _, prediction = torch.max(outputs_test, 1)
                correct += (torch.sum((prediction == labels_test))).item()
               # print(prediction, labels_test, correct)
                total += labels_test.size(0)
            print('[{}, {}] running_loss = {:.5f} accurcay = {:.5f}'.format(epoch + 1, i + 1, running_loss / 20,
                                                                        correct / total))
            running_loss = 0.0

        # if i % 10 == 9:
        #     print('[{}, {}] loss={:.5f}'.format(epoch+1, i+1, running_loss / 10))
        #     running_loss = 0.0

print('training finish !')
torch.save(model.state_dict(), './model/model_2.pth')


           

連結:https://www.jianshu.com/p/d04c17368922

連結:https://blog.csdn.net/sunqiande88/article/details/80100891

繼續閱讀