Advertisement

Pytorch官方教程(四)—Transfer Learning Tutorial

阅读量:

2019 April 04 pytorch

Pytorch官方教程(四)—Transfer Learning Tutorial

迁移学习主要的两种应用场景:

  • 经过预训练的模型:我们采用经过预训练的模型来进行网络初始化,默认情况下不会进行随机初始化操作。具体而言,在基于Imagenet 1000的数据集上进行特定阶段的学习后(即经过特定优化后),剩余的部分则通过常规方式进行后续训练。
    • 将卷积网络用作固定不变的特征提取器:通过固定权值不变的方式冻结除最后一层全连接层之外的所有网络参数权重。随后将最后一层全连接层替换为一个具有随机初始权重的新层,并对该新层进行参数优化。
复制代码
 # License: BSD

    
 # Author: Sasank Chilamkurthy
    
  
    
 from __future__ import print_function, division
    
  
    
 import torch
    
 import torch.nn as nn
    
 import torch.optim as optim
    
 from torch.optim import lr_scheduler
    
 import numpy as np
    
 import torchvision
    
 from torchvision import datasets, models, transforms
    
 import matplotlib.pyplot as plt
    
 import time
    
 import os
    
 import copy
    
  
    
 plt.ion()   # interactive mode

Load Data

使用 torchvision 和 torch.utils.data 加载数据。

构建一种用于分类蚂蚁与蜜蜂的模型,在训练阶段中为每一类提供了120张图片;对应的验证集则包含每类75张图片。通常情况下, 若从零开始进行训练, 这样的数据量对于模型来说是极为有限的.通过采用迁移学习的方法, 可以显著提升模型的泛化性能.

这个数据集是imagenet的一个非常小的子集。

复制代码
 # Data augmentation and normalization for training

    
 # Just normalization for validation
    
 data_transforms = {
    
     'train': transforms.Compose([
    
     transforms.RandomResizedCrop(224),
    
     transforms.RandomHorizontalFlip(),
    
     transforms.ToTensor(),
    
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    
     ]),
    
     'val': transforms.Compose([
    
     transforms.Resize(256),
    
     transforms.CenterCrop(224),
    
     transforms.ToTensor(),
    
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    
     ]),
    
 }
    
  
    
 data_dir = 'data/hymenoptera_data'
    
 image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
    
                                       data_transforms[x])
    
               for x in ['train', 'val']}
    
 dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
    
                                          shuffle=True, num_workers=4)
    
           for x in ['train', 'val']}
    
 dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
    
 class_names = image_datasets['train'].classes
    
  
    
 device = torch.device("cuda:2" if torch.cuda.is_available() else "cpu")

Visualize a few images

可视化部分训练集的图片以便理解数据增强。

复制代码
 def imshow(inp, title=None):

    
     """Imshow for Tensor."""
    
     inp = inp.numpy().transpose((1, 2, 0))
    
     mean = np.array([0.485, 0.456, 0.406])
    
     std = np.array([0.229, 0.224, 0.225])
    
     inp = std * inp + mean
    
     inp = np.clip(inp, 0, 1)
    
     plt.imshow(inp)
    
     if title is not None:
    
     plt.title(title)
    
     plt.pause(0.001)  # pause a bit so that plots are updated
    
  
    
  
    
 # Get a batch of training data
    
 inputs, classes = next(iter(dataloaders['train']))
    
  
    
 # Make a grid from batch
    
 out = torchvision.utils.make_grid(inputs)
    
  
    
 imshow(out, title=[class_names[x] for x in classes])
12.png

Training the model

定义模型训练的通用函数。需要声明:

  • 学习率的调度
  • 保存最好的模型

在代码中,默认使用的参数名为 scheduler 的源自 torch.optim.lr_scheduler 学习率调度器。

复制代码
 def train_model(model, criterion, optimizer, scheduler, num_epochs=25):

    
     since = time.time()
    
  
    
     best_model_wts = copy.deepcopy(model.state_dict())
    
     best_acc = 0.0
    
  
    
     for epoch in range(num_epochs):
    
     print('Epoch {}/{}'.format(epoch, num_epochs - 1))
    
     print('-' * 10)
    
  
    
     # Each epoch has a training and validation phase
    
     for phase in ['train', 'val']:
    
         if phase == 'train':
    
             scheduler.step()
    
             model.train()  # Set model to training mode
    
         else:
    
             model.eval()   # Set model to evaluate mode
    
  
    
         running_loss = 0.0
    
         running_corrects = 0
    
  
    
         # Iterate over data.
    
         for inputs, labels in dataloaders[phase]:
    
             inputs = inputs.to(device)
    
             labels = labels.to(device)
    
  
    
             # zero the parameter gradients
    
             optimizer.zero_grad()
    
  
    
             # forward
    
             # track history if only in train
    
             with torch.set_grad_enabled(phase == 'train'):
    
                 outputs = model(inputs)
    
                 _, preds = torch.max(outputs, 1)
    
                 loss = criterion(outputs, labels)
    
  
    
                 # backward + optimize only if in training phase
    
                 if phase == 'train':
    
                     loss.backward()
    
                     optimizer.step()
    
  
    
             # statistics
    
             running_loss += loss.item() * inputs.size(0)
    
             running_corrects += torch.sum(preds == labels.data)
    
  
    
         epoch_loss = running_loss / dataset_sizes[phase]
    
         epoch_acc = running_corrects.double() / dataset_sizes[phase]
    
  
    
         print('{} Loss: {:.4f} Acc: {:.4f}'.format(
    
             phase, epoch_loss, epoch_acc))
    
  
    
         # deep copy the model
    
         if phase == 'val' and epoch_acc > best_acc:
    
             best_acc = epoch_acc
    
             best_model_wts = copy.deepcopy(model.state_dict())
    
  
    
     print()
    
  
    
     time_elapsed = time.time() - since
    
     print('Training complete in {:.0f}m {:.0f}s'.format(
    
     time_elapsed // 60, time_elapsed % 60))
    
     print('Best val Acc: {:4f}'.format(best_acc))
    
  
    
     # load best model weights
    
     model.load_state_dict(best_model_wts)
    
     return model

Visualizing the model predictions

显示预测的泛化函数

复制代码
 def visualize_model(model, num_images=6):

    
     was_training = model.training
    
     model.eval()
    
     images_so_far = 0
    
     fig = plt.figure()
    
  
    
     with torch.no_grad():
    
     for i, (inputs, labels) in enumerate(dataloaders['val']):
    
         inputs = inputs.to(device)
    
         labels = labels.to(device)
    
  
    
         outputs = model(inputs)
    
         _, preds = torch.max(outputs, 1)
    
  
    
         for j in range(inputs.size()[0]):
    
             images_so_far += 1
    
             ax = plt.subplot(num_images//2, 2, images_so_far)
    
             ax.axis('off')
    
             ax.set_title('predicted: {}'.format(class_names[preds[j]]))
    
             imshow(inputs.cpu().data[j])
    
  
    
             if images_so_far == num_images:
    
                 model.train(mode=was_training)
    
                 return
    
     model.train(mode=was_training)

Finetuning the convnet

加载预训练的模型并重置最后的全连接层。

复制代码
 model_ft = models.resnet18(pretrained=True)

    
 num_ftrs = model_ft.fc.in_features
    
 model_ft.fc = nn.Linear(num_ftrs, 2)
    
  
    
 model_ft = model_ft.to(device)
    
  
    
 criterion = nn.CrossEntropyLoss()
    
  
    
 # Observe that all parameters are being optimized
    
 optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)
    
  
    
 # Decay LR by a factor of 0.1 every 7 epochs
    
 exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)

Train and evaluate

在 CPU 上需 15-25 分钟. 在 GPU 不需要 1 分钟。

复制代码
 model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,

    
                    num_epochs=25)
复制代码
 Epoch 0/24

    
 ----------
    
 train Loss: 0.6469 Acc: 0.6393
    
 val Loss: 0.2514 Acc: 0.9020
    
  
    
 ...
    
  
    
 Epoch 24/24
    
 ----------
    
 train Loss: 0.3563 Acc: 0.8320
    
 val Loss: 0.2286 Acc: 0.9020
    
  
    
 Training complete in 1m 5s
    
 Best val Acc: 0.954248

ConvNet as fixed feature extractor

在除了最后一层之外的所有层上固定参数。将所有可训练的神经网络节点标记为不计算梯度的状态,在反向传播过程中这些固定的参数就不会参与梯度的计算。

复制代码
 model_conv = torchvision.models.resnet18(pretrained=True)

    
 for param in model_conv.parameters():
    
     param.requires_grad = False
    
  
    
 # Parameters of newly constructed modules have requires_grad=True by default
    
 num_ftrs = model_conv.fc.in_features
    
 model_conv.fc = nn.Linear(num_ftrs, 2)
    
  
    
 model_conv = model_conv.to(device)
    
  
    
 criterion = nn.CrossEntropyLoss()
    
  
    
 # Observe that only parameters of final layer are being optimized as
    
 # opposed to before.
    
 optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)
    
  
    
 # Decay LR by a factor of 0.1 every 7 epochs
    
 exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)

Train and evaluate

网络的大部分梯度不需要计算,只需要计算前向传播。

复制代码
 model_conv = train_model(model_conv, criterion, optimizer_conv,

    
                      exp_lr_scheduler, num_epochs=25)
复制代码
 Epoch 0/24

    
 ----------
    
 train Loss: 0.5829 Acc: 0.6967
    
 val Loss: 0.3998 Acc: 0.8039
    
  
    
 ...
    
  
    
 Epoch 24/24
    
 ----------
    
 train Loss: 0.3436 Acc: 0.8320
    
 val Loss: 0.2410 Acc: 0.9020
    
  
    
 Training complete in 0m 53s
    
 Best val Acc: 0.954248
复制代码
 visualize_model(model_conv)

    
  
    
 plt.ioff()
    
 plt.show()
13.png
14.png
15.png
16.png
17.png
18.png

全部评论 (0)

还没有任何评论哟~