ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

计算机视觉的迁移学习演示

2022-04-04 14:02:54  阅读:207  来源: 互联网

标签:Acc Loss 演示 val Epoch train 24 视觉 迁移


本文讲述了如何使用迁移学习来对图片分类任务训练一个卷积神经网络。关于更多的迁移学习可以查看cs231n notes

关于这些笔记:

实际上,人们通常不会从头训练一整个卷积神经网络(从随机初始化权重开始),因为通常并没有足够大的数据集。相反,更常见的做法是在一个非常大的数据集上(例如ImageNet数据集,有着1000个类别的120万的图片)训练一个卷积网络,然后将这个网络作为想要的任务的初始化权重或者固定特征提取器(fixed feature extractor)。

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

  • 微调卷积网络(finetuning the convnet):我们从一个预训练的网络模型开始我们的训练任务,而不是随机初始化权重的方式。
  • 将卷积网络作为固定特征提取器(ConvNet as fixed feature extractor):在这里,我们将冻结(freeze,固定权重)除了网络最后一层的全连接层的所有网络部分。将最后一层的全连接层替换为以随机权重初始化的层,然后只对新的一层进行训练。
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 torch.backends.cudnn as cudnn
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy

cudnn.benchmark = True
plt.ion()   # interactive mode
<matplotlib.pyplot._IonContext at 0x7f590ec129d0>

加载数据

我们将使用torchvision和torch.utils.data包来加载数据集。

本文要解决的任务是训练一个分类模型,此模型用来分类蚂蚁和蜜蜂。对于这两个类别,我们大约有120张训练图像;75个验证图像。相对于从头开始训练,通常来说这是一个可以泛化(generalize)的非常小的数据集。所以我们将使用迁移学习,我们应该能够很合理的泛化模型。

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

从此连接下载数据集,并且解压到当前目录。

# 对于训练集,使用数据增强和规范化
# 对于验证集,只使用规范化
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 = "./hymenoptera_data/"
images_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ["train", "val"]}
dataloaders = {x: torch.utils.data.DataLoader(images_datasets[x], batch_size=4, shuffle=True, num_workers=4) for x in ["train", "val"]}
dataset_sizes = {x: len(images_datasets[x]) for x in ['train', 'val']}
class_names = images_datasets["train"].classes
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

绘制部分图像

为了搞清楚数据增强做了什么事情,我们可以绘制出几张训练图像。

def imshow(inp, title=None):
    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"]))
# 对于一个批量,产生一个网格
out = torchvision.utils.make_grid(inputs)
imshow(out, title=[class_names[x] for x in classes])

image

训练模型

现在,让我们编写一个训练模型的通用函数。在这里,我们将阐述:

  • scheduling the learning rate
  • saving the best model

在接下来的部分,schedulertorch.optim.lr_scheduler的一个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(f'Epoch {epoch}/{num_epochs - 1}')
        print('-' * 10)

        # Each epoch has a training and validation phase
        for phase in ['train', 'val']:
            if phase == 'train':
                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)
            if phase == 'train':
                scheduler.step()

            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects.double() / dataset_sizes[phase]

            print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')

            # 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(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')
    print(f'Best val Acc: {best_acc:4f}')

    # load best model weights
    model.load_state_dict(best_model_wts)
    return model

绘制模型预测

对于一少部分的图片,显示预测的通用函数。

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(f'predicted: {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)

微调卷积网络

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

model_ft = models.resnet18(pretrained=True)
num_ftrs = model_ft.fc.in_features
# Here the size of each output sample is set to 2.
# Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).
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)

训练并验证

在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.6200 Acc: 0.7049
val Loss: 0.1974 Acc: 0.9281

Epoch 1/24
----------
train Loss: 0.3805 Acc: 0.8238
val Loss: 0.2273 Acc: 0.9085

Epoch 2/24
----------
train Loss: 0.6180 Acc: 0.7541
val Loss: 0.3575 Acc: 0.8301

Epoch 3/24
----------
train Loss: 0.6691 Acc: 0.7213
val Loss: 0.7067 Acc: 0.7320

Epoch 4/24
----------
train Loss: 0.5769 Acc: 0.7787
val Loss: 0.3549 Acc: 0.8758

Epoch 5/24
----------
train Loss: 0.5283 Acc: 0.8074
val Loss: 0.4467 Acc: 0.8693

Epoch 6/24
----------
train Loss: 0.5416 Acc: 0.8115
val Loss: 0.4135 Acc: 0.8758

Epoch 7/24
----------
train Loss: 0.5169 Acc: 0.8320
val Loss: 0.2594 Acc: 0.9150

Epoch 8/24
----------
train Loss: 0.3956 Acc: 0.8443
val Loss: 0.2633 Acc: 0.8889

Epoch 9/24
----------
train Loss: 0.3106 Acc: 0.8811
val Loss: 0.2324 Acc: 0.9150

Epoch 10/24
----------
train Loss: 0.3582 Acc: 0.8361
val Loss: 0.2172 Acc: 0.9150

Epoch 11/24
----------
train Loss: 0.3057 Acc: 0.8811
val Loss: 0.2832 Acc: 0.9085

Epoch 12/24
----------
train Loss: 0.2628 Acc: 0.8893
val Loss: 0.2257 Acc: 0.9216

Epoch 13/24
----------
train Loss: 0.2785 Acc: 0.8730
val Loss: 0.2305 Acc: 0.9281

Epoch 14/24
----------
train Loss: 0.2999 Acc: 0.8648
val Loss: 0.2338 Acc: 0.9281

Epoch 15/24
----------
train Loss: 0.1689 Acc: 0.9426
val Loss: 0.2332 Acc: 0.9216

Epoch 16/24
----------
train Loss: 0.2660 Acc: 0.9057
val Loss: 0.2629 Acc: 0.9150

Epoch 17/24
----------
train Loss: 0.3769 Acc: 0.8074
val Loss: 0.2362 Acc: 0.9281

Epoch 18/24
----------
train Loss: 0.3248 Acc: 0.8607
val Loss: 0.2255 Acc: 0.9216

Epoch 19/24
----------
train Loss: 0.2859 Acc: 0.8893
val Loss: 0.2545 Acc: 0.9150

Epoch 20/24
----------
train Loss: 0.2435 Acc: 0.8852
val Loss: 0.2538 Acc: 0.9150

Epoch 21/24
----------
train Loss: 0.3553 Acc: 0.8525
val Loss: 0.2828 Acc: 0.9085

Epoch 22/24
----------
train Loss: 0.3092 Acc: 0.8770
val Loss: 0.2246 Acc: 0.9216

Epoch 23/24
----------
train Loss: 0.2671 Acc: 0.8934
val Loss: 0.2437 Acc: 0.9216

Epoch 24/24
----------
train Loss: 0.1983 Acc: 0.9262
val Loss: 0.2293 Acc: 0.9281

Training complete in 8m 22s
Best val Acc: 0.928105
visualize_model(model_ft)

image

将ConvNet作为固定特征提取器

接下来,我们要冻结网络中除最后一层的所有部分。我们需要设置requires_grad=False来冻结网络中的参数,以便于在backward()中梯度不被计算。

你可以从此文档中查看更多。

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)

训练和验证

在CPU上训练,这会耗与之前场景下一半的时间。这是因为对于网络中的大部分参数,已经不需要被计算梯度。但是,前向传播仍然是需要进行的。

model_conv = train_model(model_conv, criterion, optimizer_conv, exp_lr_scheduler, num_epochs=25)
Epoch 0/24
----------
train Loss: 0.6645 Acc: 0.6557
val Loss: 0.2080 Acc: 0.9477

Epoch 1/24
----------
train Loss: 0.4321 Acc: 0.7787
val Loss: 0.2543 Acc: 0.8889

Epoch 2/24
----------
train Loss: 0.4560 Acc: 0.8320
val Loss: 0.3580 Acc: 0.8301

Epoch 3/24
----------
train Loss: 0.4300 Acc: 0.8197
val Loss: 0.1768 Acc: 0.9542

Epoch 4/24
----------
train Loss: 0.4507 Acc: 0.8074
val Loss: 0.1854 Acc: 0.9412

Epoch 5/24
----------
train Loss: 0.3498 Acc: 0.8525
val Loss: 0.1786 Acc: 0.9477

Epoch 6/24
----------
train Loss: 0.4869 Acc: 0.8033
val Loss: 0.3503 Acc: 0.8562

Epoch 7/24
----------
train Loss: 0.3607 Acc: 0.8484
val Loss: 0.1843 Acc: 0.9477

Epoch 8/24
----------
train Loss: 0.3784 Acc: 0.8238
val Loss: 0.1927 Acc: 0.9412

Epoch 9/24
----------
train Loss: 0.2795 Acc: 0.8811
val Loss: 0.1895 Acc: 0.9346

Epoch 10/24
----------
train Loss: 0.3200 Acc: 0.8811
val Loss: 0.1843 Acc: 0.9477

Epoch 11/24
----------
train Loss: 0.3509 Acc: 0.8607
val Loss: 0.1780 Acc: 0.9477

Epoch 12/24
----------
train Loss: 0.3471 Acc: 0.8279
val Loss: 0.1747 Acc: 0.9412

Epoch 13/24
----------
train Loss: 0.3467 Acc: 0.8320
val Loss: 0.2249 Acc: 0.9150

Epoch 14/24
----------
train Loss: 0.3171 Acc: 0.8484
val Loss: 0.1845 Acc: 0.9477

Epoch 15/24
----------
train Loss: 0.3456 Acc: 0.8279
val Loss: 0.1745 Acc: 0.9412

Epoch 16/24
----------
train Loss: 0.3599 Acc: 0.8525
val Loss: 0.1960 Acc: 0.9477

Epoch 17/24
----------
train Loss: 0.2593 Acc: 0.8934
val Loss: 0.1894 Acc: 0.9477

Epoch 18/24
----------
train Loss: 0.3548 Acc: 0.8361
val Loss: 0.1900 Acc: 0.9346

Epoch 19/24
----------
train Loss: 0.3291 Acc: 0.8361
val Loss: 0.1977 Acc: 0.9412

Epoch 20/24
----------
train Loss: 0.2964 Acc: 0.8402
val Loss: 0.1920 Acc: 0.9412

Epoch 21/24
----------
train Loss: 0.3526 Acc: 0.8607
val Loss: 0.2000 Acc: 0.9281

Epoch 22/24
----------
train Loss: 0.3836 Acc: 0.7992
val Loss: 0.1915 Acc: 0.9412

Epoch 23/24
----------
train Loss: 0.3399 Acc: 0.8566
val Loss: 0.1821 Acc: 0.9346

Epoch 24/24
----------
train Loss: 0.3259 Acc: 0.8402
val Loss: 0.1718 Acc: 0.9477

Training complete in 4m 1s
Best val Acc: 0.954248
visualize_model(model_conv)

plt.ioff()
plt.show()

image

下一步阅读

如果你希望阅读更多的迁移学习的应用,可以查看Quantized Transfer Learning for Computer Vision Tutorial

标签:Acc,Loss,演示,val,Epoch,train,24,视觉,迁移
来源: https://www.cnblogs.com/geekfx/p/16098962.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有