pytorch例子学习——TRANSFER LEARNING TUTORIAL

参考:https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html

 

以下是两种主要的迁移学习场景

  • 微调convnet : 与随机初始化不同,我们使用一个预训练的网络初始化网络,就像在imagenet 1000 dataset上训练的网络一样。其余的训练看起来和往常一样。
  • 将ConvNet作为固定的特征提取器 : 在这里,我们将冻结所有网络的权重,除了最后的全连接层。最后一个完全连接的层被替换为一个具有随机权重的新层,并且只训练这个层。

 

一开始先导入需要的包:

# 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()   # 交互模式

 

1)下载数据

将会使用torchvision和torch.utils.data包去下载数据

今天我们 要解决的问题是建立一个模型去分类蚂蚁和蜜蜂。对于蚂蚁和蜜蜂,我们分别有大约120张训练图片。对于每个类有75张验证图像。通常,如果从零开始训练,这是一个非常小的数据集。由于使用了迁移学习,我们应该能够很好地概括。

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

 ⚠️从 here下载数据集,并将其从当前目录中抽取出来

# 为训练对数据进行扩充和规范化,其实就是通过剪切,翻转等方法来扩充数据集的大小
# 验证只进行规范化
data_transforms = {
    'train': transforms.Compose([ #将多个transform组合起来使用
        transforms.RandomResizedCrop(224), #将给定的图片随机切,然后再resize成给定的size=224大小
        transforms.RandomHorizontalFlip(), #图片有一半概率进行翻转,另一半概率不翻转
        transforms.ToTensor(), #将图片的像素值[0,255]转成取值范围为[0,1]的tensor
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) #两个参数分别代表均值mean和方差std,有三个值是对应三类像素image=[R,G,B],Normalized_image=(image-mean)/std
    ]),
    'val': transforms.Compose([
        transforms.Resize(256), #改变大小
        transforms.CenterCrop(224), #进行中心切割,得到给定的size,切出来的图形形状是正方形的
        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

#如果有CUDA则使用它,否则使用CPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

 

2)可视化一些图像

让我们可视化一些训练图像,以便理解数据扩充。

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)  # 暂停一下,以便更新绘图


# 获得一批训练数据
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])

运行到这里的时候就会显示如下图所示的图像:

 

3)训练模型

现在,让我们编写一个通用函数来训练模型。在这里,我们将举例说明:

  • 调度学习比率
  • 保存最佳模型

在下面,变量scheduler是一个来自torch.optim.lr_scheduler的LR调度对象,

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)

        # 每个周期都有一个训练和验证阶段
        for phase in ['train', 'val']:
            if phase == 'train':
                scheduler.step()
                model.train()  # 设置模型为训练模式
            else:
                model.eval()   # 设置模型为评估模式

            running_loss = 0.0
            running_corrects = 0

            #迭代数据
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)

                # zero the parameter gradients
                optimizer.zero_grad()

                # forward
                # 如果只是在训练要追踪历史
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)

                    # backward + optimize ,仅在训练阶段
                    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))

            # 深度拷贝模型
            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))

    # 下载最好的模型权重
    model.load_state_dict(best_model_wts)
    return model

 

4)可视化模型预测

泛型函数,显示对一些图像的预测

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)

 

5)

1》微调convent

下载预训练模型并重新设置最后的全连接层

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()

# 观察到所有参数都被优化
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)

# 每7个周期,LR衰减0.1倍
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)

 

2》训练和评估

CPU可能会使用15-25分钟。如果使用的是GPU,将花费少于一分钟

model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,
                       num_epochs=25)

⚠️使用的python2.7,如果使用的是python3则会报错:

(deeplearning) userdeMBP:neural transfer user$ python neural_style_tutorial.py 
2019-03-13 19:30:06.194 python[4926:206321] -[NSApplication _setup:]: unrecognized selector sent to instance 0x7f8eb4895080
2019-03-13 19:30:06.199 python[4926:206321] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSApplication _setup:]: unrecognized selector sent to instance 0x7f8eb4895080'

 

终端运行:(我是在CPU上运行的)

(deeplearning2) userdeMBP:transfer learning user$ python transfer_learning_tutorial.py 
Downloading: "https://download.pytorch.org/models/resnet18-5c106cde.pth" to /Users/user/.torch/models/resnet18-5c106cde.pth
100.0%
Epoch 0/24
----------
train Loss: 0.5703 Acc: 0.7049
val Loss: 0.2565 Acc: 0.9020

Epoch 1/24
----------
train Loss: 0.6009 Acc: 0.7951
val Loss: 0.3604 Acc: 0.8627

Epoch 2/24
----------
train Loss: 0.5190 Acc: 0.7869
val Loss: 0.4421 Acc: 0.8105

Epoch 3/24
----------
train Loss: 0.5072 Acc: 0.7910
val Loss: 0.4037 Acc: 0.8431

Epoch 4/24
----------
train Loss: 0.6503 Acc: 0.7869
val Loss: 0.2961 Acc: 0.9085

Epoch 5/24
----------
train Loss: 0.3840 Acc: 0.8730
val Loss: 0.2648 Acc: 0.8954

Epoch 6/24
----------
train Loss: 0.6137 Acc: 0.7705
val Loss: 0.6852 Acc: 0.8170

Epoch 7/24
----------
train Loss: 0.4130 Acc: 0.8279
val Loss: 0.3730 Acc: 0.8954

Epoch 8/24
----------
train Loss: 0.3953 Acc: 0.8648
val Loss: 0.3300 Acc: 0.9216

Epoch 9/24
----------
train Loss: 0.2753 Acc: 0.8934
val Loss: 0.2949 Acc: 0.9020

Epoch 10/24
----------
train Loss: 0.3192 Acc: 0.8648
val Loss: 0.2984 Acc: 0.9020

Epoch 11/24
----------
train Loss: 0.2942 Acc: 0.8852
val Loss: 0.2624 Acc: 0.9150

Epoch 12/24
----------
train Loss: 0.2738 Acc: 0.8811
val Loss: 0.2855 Acc: 0.9020

Epoch 13/24
----------
train Loss: 0.2697 Acc: 0.8648
val Loss: 0.2675 Acc: 0.9020

Epoch 14/24
----------
train Loss: 0.2534 Acc: 0.9016
val Loss: 0.2780 Acc: 0.9085

Epoch 15/24
----------
train Loss: 0.2514 Acc: 0.8811
val Loss: 0.2873 Acc: 0.9020

Epoch 16/24
----------
train Loss: 0.2430 Acc: 0.9098
val Loss: 0.2901 Acc: 0.9085

Epoch 17/24
----------
train Loss: 0.2970 Acc: 0.8402
val Loss: 0.2570 Acc: 0.9150

Epoch 18/24
----------
train Loss: 0.2779 Acc: 0.8934
val Loss: 0.2792 Acc: 0.9085

Epoch 19/24
----------

train Loss: 0.2271 Acc: 0.9262
val Loss: 0.2655 Acc: 0.9150

Epoch 20/24
----------
train Loss: 0.2741 Acc: 0.8975
val Loss: 0.2726 Acc: 0.9085

Epoch 21/24
----------
train Loss: 0.3221 Acc: 0.8320
val Loss: 0.2738 Acc: 0.9150

Epoch 22/24
----------
train Loss: 0.2228 Acc: 0.9139
val Loss: 0.2712 Acc: 0.9020

Epoch 23/24
----------
train Loss: 0.2881 Acc: 0.8975
val Loss: 0.2565 Acc: 0.9150

Epoch 24/24
----------
train Loss: 0.3219 Acc: 0.8648
val Loss: 0.2669 Acc: 0.9150

Training complete in 16m 22s
Best val Acc: 0.921569

实现可视化:

visualize_model(model_ft)

第一次返回图像为:

 

6)

1》将ConvNet作为固定的特征提取器

这里我们需要将除了最后一层的所有网络冻结。我们需要设置requires_grad == False去冻结参数以便梯度在backward()中不会被计算

我们可以从here读取更详细的内容

model_conv = torchvision.models.resnet18(pretrained=True)
for param in model_conv.parameters():
    param.requires_grad = False

# 最新构造函数的参数默认设置requires_grad=True
num_ftrs = model_conv.fc.in_features
model_conv.fc = nn.Linear(num_ftrs, 2)

model_conv = model_conv.to(device)

criterion = nn.CrossEntropyLoss()

# 观察到与之前相比,只有最后一层的参数被优化
optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)

# 每7个周期,LR衰减0.1倍
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)

 

2》训练和评估

在CPU上,与之前的场景相比,这将只花费大约其一半的时间。因为这预期了对于大部分网络梯度不需要计算。当然forward还是需要计算的

model_conv = train_model(model_conv, criterion, optimizer_conv,
                         exp_lr_scheduler, num_epochs=25)

接下来继续训练:(我是在CPU上运行的)

Epoch 0/24
----------
train Loss: 0.6719 Acc: 0.6516
val Loss: 0.2252 Acc: 0.9281

Epoch 1/24
----------
train Loss: 0.6582 Acc: 0.7254
val Loss: 0.4919 Acc: 0.7778

Epoch 2/24
----------
train Loss: 0.5313 Acc: 0.8115
val Loss: 0.2488 Acc: 0.9085

Epoch 3/24
----------
train Loss: 0.5134 Acc: 0.7623
val Loss: 0.1881 Acc: 0.9412

Epoch 4/24
----------
train Loss: 0.3834 Acc: 0.8525
val Loss: 0.2220 Acc: 0.9085

Epoch 5/24
----------
train Loss: 0.5442 Acc: 0.7910
val Loss: 0.2865 Acc: 0.8954

Epoch 6/24
----------
train Loss: 0.6136 Acc: 0.7213
val Loss: 0.2915 Acc: 0.9085

Epoch 7/24
----------
train Loss: 0.3393 Acc: 0.8730
val Loss: 0.1839 Acc: 0.9542

Epoch 8/24
----------
train Loss: 0.3616 Acc: 0.8156
val Loss: 0.1967 Acc: 0.9346

Epoch 9/24
----------
train Loss: 0.3798 Acc: 0.8402
val Loss: 0.1903 Acc: 0.9542

Epoch 10/24
----------
train Loss: 0.3918 Acc: 0.8320
val Loss: 0.1860 Acc: 0.9477

Epoch 11/24
----------
train Loss: 0.3950 Acc: 0.8115
val Loss: 0.1803 Acc: 0.9542

Epoch 12/24
----------
train Loss: 0.3094 Acc: 0.8566
val Loss: 0.1978 Acc: 0.9542

Epoch 13/24
----------
train Loss: 0.2791 Acc: 0.8811
val Loss: 0.1932 Acc: 0.9542

Epoch 14/24
----------
train Loss: 0.3797 Acc: 0.8484
val Loss: 0.2318 Acc: 0.9346

Epoch 15/24
----------
train Loss: 0.3456 Acc: 0.8689
val Loss: 0.1965 Acc: 0.9412

Epoch 16/24
----------
train Loss: 0.4585 Acc: 0.7910
val Loss: 0.2264 Acc: 0.9346

Epoch 17/24
----------
train Loss: 0.3889 Acc: 0.8566
val Loss: 0.1847 Acc: 0.9477

Epoch 18/24
----------
train Loss: 0.3636 Acc: 0.8361
val Loss: 0.2680 Acc: 0.9346

Epoch 19/24
----------
train Loss: 0.2616 Acc: 0.8730
val Loss: 0.1892 Acc: 0.9477

Epoch 20/24
----------
train Loss: 0.3114 Acc: 0.8648
val Loss: 0.2295 Acc: 0.9346

Epoch 21/24
----------
train Loss: 0.3597 Acc: 0.8443
val Loss: 0.1857 Acc: 0.9477

Epoch 22/24
----------
train Loss: 0.3794 Acc: 0.8402
val Loss: 0.1822 Acc: 0.9477

Epoch 23/24
----------
train Loss: 0.3553 Acc: 0.8279
val Loss: 0.1992 Acc: 0.9608

Epoch 24/24
----------
train Loss: 0.3514 Acc: 0.8238
val Loss: 0.2144 Acc: 0.9346

Training complete in 13m 33s
Best val Acc: 0.960784

实现可视化:

visualize_model(model_conv)

plt.ioff()
plt.show()

返回图像为:

 

posted @ 2019-03-16 23:13  慢行厚积  阅读(1025)  评论(0编辑  收藏  举报