转移学习的计算机视觉教程

转移学习的计算机视觉教程

引用这些注释,

实际上,很少有人从头开始训练整个卷积网络(使用随机初始化),因为拥有足够大小的数据集相对很少。 相反,通常在非常大的数据集上对 ConvNet 进行预训练(例如 ImageNet,其中包含 120 万个具有 1000 个类别的图像),然后将 ConvNet 用作初始化或固定特征提取器以完成感兴趣的任务。

这两个主要的转移学习方案如下所示:

  • 对卷积网络进行微调:代替随机初始化,我们使用经过预训练的网络初始化网络,例如在 imagenet 1000 数据集上进行训练的网络。 其余的训练照常进行。

  • 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() # interactive mode

载入资料

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

我们今天要解决的问题是训练一个模型来对蚂蚁蜜蜂进行分类。 我们为蚂蚁和蜜蜂提供了大约 120 张训练图像。 每个类别有 75 个验证图像。 通常,如果从头开始训练的话,这是一个很小的数据集。 由于我们正在使用迁移学习,因此我们应该能够很好地概括。

该数据集是 imagenet 的很小一部分。

Note

从的下载数据,并将其提取到当前目录。

# 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:0" if torch.cuda.is_available() else "cpu")

可视化一些图像

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

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

训练模型

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

  • 安排学习率
  • 保存最佳模型

以下,参数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)

        # 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('{} 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

可视化模型预测

通用功能可显示一些图像的预测

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)

微调 convnet

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

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 上,此过程不到一分钟。

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

出:

Epoch 0/24
----------
train Loss: 0.5582 Acc: 0.6967
val Loss: 0.1987 Acc: 0.9216

Epoch 1/24
----------
train Loss: 0.4663 Acc: 0.8238
val Loss: 0.2519 Acc: 0.8889

Epoch 2/24
----------
train Loss: 0.5978 Acc: 0.7623
val Loss: 1.2933 Acc: 0.6601

Epoch 3/24
----------
train Loss: 0.4471 Acc: 0.8320
val Loss: 0.2576 Acc: 0.8954

Epoch 4/24
----------
train Loss: 0.3654 Acc: 0.8115
val Loss: 0.2977 Acc: 0.9150

Epoch 5/24
----------
train Loss: 0.4404 Acc: 0.8197
val Loss: 0.3330 Acc: 0.8627

Epoch 6/24
----------
train Loss: 0.6416 Acc: 0.7623
val Loss: 0.3174 Acc: 0.8693

Epoch 7/24
----------
train Loss: 0.4058 Acc: 0.8361
val Loss: 0.2551 Acc: 0.9085

Epoch 8/24
----------
train Loss: 0.2294 Acc: 0.9098
val Loss: 0.2603 Acc: 0.9085

Epoch 9/24
----------
train Loss: 0.2805 Acc: 0.8730
val Loss: 0.2765 Acc: 0.8954

Epoch 10/24
----------
train Loss: 0.3139 Acc: 0.8525
val Loss: 0.2639 Acc: 0.9020

Epoch 11/24
----------
train Loss: 0.3198 Acc: 0.8648
val Loss: 0.2458 Acc: 0.9020

Epoch 12/24
----------
train Loss: 0.2947 Acc: 0.8811
val Loss: 0.2835 Acc: 0.8889

Epoch 13/24
----------
train Loss: 0.3097 Acc: 0.8730
val Loss: 0.2542 Acc: 0.9085

Epoch 14/24
----------
train Loss: 0.1849 Acc: 0.9303
val Loss: 0.2710 Acc: 0.9085

Epoch 15/24
----------
train Loss: 0.2764 Acc: 0.8934
val Loss: 0.2522 Acc: 0.9085

Epoch 16/24
----------
train Loss: 0.2214 Acc: 0.9098
val Loss: 0.2620 Acc: 0.9085

Epoch 17/24
----------
train Loss: 0.2949 Acc: 0.8525
val Loss: 0.2600 Acc: 0.9085

Epoch 18/24
----------
train Loss: 0.2237 Acc: 0.9139
val Loss: 0.2666 Acc: 0.9020

Epoch 19/24
----------
train Loss: 0.2456 Acc: 0.8852
val Loss: 0.2521 Acc: 0.9150

Epoch 20/24
----------
train Loss: 0.2351 Acc: 0.8852
val Loss: 0.2781 Acc: 0.9085

Epoch 21/24
----------
train Loss: 0.2654 Acc: 0.8730
val Loss: 0.2560 Acc: 0.9085

Epoch 22/24
----------
train Loss: 0.1955 Acc: 0.9262
val Loss: 0.2605 Acc: 0.9020

Epoch 23/24
----------
train Loss: 0.2285 Acc: 0.8893
val Loss: 0.2650 Acc: 0.9085

Epoch 24/24
----------
train Loss: 0.2360 Acc: 0.9221
val Loss: 0.2690 Acc: 0.8954

Training complete in 1m 7s
Best val Acc: 0.921569

visualize_model(model_ft)

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)

Train and evaluate

与以前的方案相比,在 CPU 上将花费大约一半的时间。 这是可以预期的,因为不需要为大多数网络计算梯度。 但是,确实需要计算正向。

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

Out:

Epoch 0/24
----------
train Loss: 0.5633 Acc: 0.7008
val Loss: 0.2159 Acc: 0.9412

Epoch 1/24
----------
train Loss: 0.4394 Acc: 0.7623
val Loss: 0.2000 Acc: 0.9150

Epoch 2/24
----------
train Loss: 0.5182 Acc: 0.7623
val Loss: 0.1897 Acc: 0.9346

Epoch 3/24
----------
train Loss: 0.3993 Acc: 0.8074
val Loss: 0.3029 Acc: 0.8824

Epoch 4/24
----------
train Loss: 0.4163 Acc: 0.8607
val Loss: 0.2190 Acc: 0.9412

Epoch 5/24
----------
train Loss: 0.4741 Acc: 0.7951
val Loss: 0.1903 Acc: 0.9477

Epoch 6/24
----------
train Loss: 0.4266 Acc: 0.8115
val Loss: 0.2178 Acc: 0.9281

Epoch 7/24
----------
train Loss: 0.3623 Acc: 0.8238
val Loss: 0.2080 Acc: 0.9412

Epoch 8/24
----------
train Loss: 0.3979 Acc: 0.8279
val Loss: 0.1796 Acc: 0.9412

Epoch 9/24
----------
train Loss: 0.3534 Acc: 0.8648
val Loss: 0.2043 Acc: 0.9412

Epoch 10/24
----------
train Loss: 0.3849 Acc: 0.8115
val Loss: 0.2012 Acc: 0.9346

Epoch 11/24
----------
train Loss: 0.3814 Acc: 0.8361
val Loss: 0.2088 Acc: 0.9412

Epoch 12/24
----------
train Loss: 0.3443 Acc: 0.8648
val Loss: 0.1823 Acc: 0.9477

Epoch 13/24
----------
train Loss: 0.2931 Acc: 0.8525
val Loss: 0.1853 Acc: 0.9477

Epoch 14/24
----------
train Loss: 0.2749 Acc: 0.8811
val Loss: 0.2068 Acc: 0.9412

Epoch 15/24
----------
train Loss: 0.3387 Acc: 0.8566
val Loss: 0.2080 Acc: 0.9477

Epoch 16/24
----------
train Loss: 0.2992 Acc: 0.8648
val Loss: 0.2096 Acc: 0.9346

Epoch 17/24
----------
train Loss: 0.3396 Acc: 0.8648
val Loss: 0.1870 Acc: 0.9412

Epoch 18/24
----------
train Loss: 0.3956 Acc: 0.8320
val Loss: 0.1858 Acc: 0.9412

Epoch 19/24
----------
train Loss: 0.3379 Acc: 0.8402
val Loss: 0.1729 Acc: 0.9542

Epoch 20/24
----------
train Loss: 0.2555 Acc: 0.8811
val Loss: 0.2186 Acc: 0.9281

Epoch 21/24
----------
train Loss: 0.3764 Acc: 0.8484
val Loss: 0.1817 Acc: 0.9477

Epoch 22/24
----------
train Loss: 0.2747 Acc: 0.8975
val Loss: 0.2042 Acc: 0.9412

Epoch 23/24
----------
train Loss: 0.3072 Acc: 0.8689
val Loss: 0.1924 Acc: 0.9477

Epoch 24/24
----------
train Loss: 0.3479 Acc: 0.8402
val Loss: 0.1835 Acc: 0.9477

Training complete in 0m 34s
Best val Acc: 0.954248

visualize_model(model_conv)

plt.ioff()
plt.show()
相关推荐
幻风_huanfeng4 分钟前
线性代数中的核心数学知识
人工智能·机器学习
怀旧6668 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
volcanical12 分钟前
LangGPT结构化提示词编写实践
人工智能
weyson43 分钟前
CSharp OpenAI
人工智能·语言模型·chatgpt·openai
RestCloud1 小时前
ETLCloud异常问题分析ai功能
人工智能·ai·数据分析·etl·数据集成工具·数据异常
infiniteWei1 小时前
【Lucene】原理学习路线
学习·搜索引擎·全文检索·lucene
IT古董1 小时前
【机器学习】决定系数(R²:Coefficient of Determination)
人工智能·python·机器学习
follycat1 小时前
[极客大挑战 2019]PHP 1
开发语言·学习·网络安全·php
鲜枣课堂1 小时前
5G-A如何与AI融合发展?华为MBBF2024给出解答
人工智能·5g·华为
武子康2 小时前
大数据-213 数据挖掘 机器学习理论 - KMeans Python 实现 距离计算函数 质心函数 聚类函数
大数据·人工智能·python·机器学习·数据挖掘·scikit-learn·kmeans