当前位置:   article > 正文

torch学习 (三十四):迁移学习之微调_迁移学习 微调 csdn

迁移学习 微调 csdn

引入

  场景
  从图像中识别出不同种类的椅子,然后将购买链接推荐给用户。
  方案1: 找出100种常见的椅子,为每种椅子拍摄1000张不同角度的图像,并训练模型。
  缺点: 数据集看似庞大,样本类型和数据集仍不足;适用于更大规模数据集的模型,可能会在该数据集上过拟合。
  方案2: 收集更多的数据。
  缺点: 收集和标注的成本巨大。
  方案3: 应用迁移学习 (transfer learning):将从源数据集学到的知识迁移到目标数据集。例如,虽然ImageNet数据集的图像大多和椅子无关,但是该数据集上训练的模型可以抽取较通用的图像特征,从而帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅子也可能同样有效。

1 微调

  微调 (fine tuning)是迁移学习的一种常用技术,分为以下四步,具体如下图:


  1)在源数据集上训练一个神经网络模型,即源模型
  2)创建一个新的神经网络模型,即目标模型:它复制了源模型上除输出层外的所有模型设计及其参数;
  3)为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数;
  4)在目标数据集上训练目标模型。我们将从新训练输出层,其余层的参数则是微调获得。
  该方案的假设如下:
  1)源模型参数包含了源数据集上学到的知识,并能够适用于目标数据集;
  2)源模型的输出层与源数据集的标签紧密相关,因此在目标模型中不予采用。
  优点:
  当目标数据集远小于源数据集时,微调有助于提升模型的泛化能力。

2 热狗识别

  本例基于一个小数据集对在ImageNet数据集上训练好的ResNet模型进行微调。该小数据集包含数千张包含热狗和不包含热狗的图像的图像。最终将判断一个图像中是否包含热狗。

2.1 数据集载入

  数据集的说明和载入可以参照:
  https://blog.csdn.net/weixin_44575152/article/details/118901491

2.2 数据集预处理

  训练: 从图像中随机裁剪出随机大小和随机高宽比的一块区域,并将该区域缩放为 224 × 224 224 \times 224 224×224
  测试: 将图像的高和宽缩放为 256 × 256 256 \times 256 256×256,并裁剪出 224 × 224 224 \times 224 224×224的中心区域。
  RGB标准化: 每个通道的数值减去当前通道所有数值的平均值,再除以该通道所有数值的标准差作为输出。

import torch
from torch import nn, optim
from torch.utils.data import DataLoader
from torchvision import transforms, models
from torchvision.datasets import ImageFolder


def get_format_image(save_home="D:/Data/Image/hotdog/"):
    """
    图像载入
    """
    # 训练集和测试集
    normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    aug_tr = transforms.Compose([
        transforms.RandomResizedCrop(size=224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        normalize,
    ])
    aug_te = transforms.Compose([
        transforms.Resize(size=256),
        transforms.CenterCrop(size=224),
        transforms.ToTensor(),
        normalize,
    ])
    image_tr = DataLoader(ImageFolder(save_home + "train", transform=aug_tr), batch_size=128, shuffle=True)
    image_te = DataLoader(ImageFolder(save_home + "train", transform=aug_te), batch_size=128)

    return image_tr, image_te
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2.3 定义和初始化模型

  对于预训练的模型,可以设置较小的学习率进行微调;而新加入的用于适应输出的全连接层,学习率则设置为较大值:

def get_model(lr=0.01):
    """获取预训练模型"""
    net_pretrained = models.resnet18(pretrained=True)
    # fc是一个全连接层,通过设置可以适应指定数据集,例如二分类
    net_pretrained.fc = nn.Linear(512, 2)
    params_output = list(map(id, net_pretrained.fc.parameters()))
    params_feature = filter(lambda p: id(p) not in params_output, net_pretrained.parameters())
    # 这里fc层是从头学,所以其学习率设置的较大,而预训练层的则相对较小
    optimizer = optim.SGD([
        {"params": params_feature},
        {"params": net_pretrained.fc.parameters(),
         "lr": lr * 10}],
        lr=lr, weight_decay=0.001,
    )
    return net_pretrained, optimizer
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.4 微调模型

def train(train_iter, test_iter, net, loss, optimizer, num_epochs):
    import time
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    net = net.to(device)
    print("training on ", device)
    batch_count = 0
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = evaluate_accuracy(test_iter, net)
        print("epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec"
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))


def evaluate_accuracy(data_iter, net, device=None):
    if device is None and isinstance(net, torch.nn.Module):
        # 如果没指定device就使用net的device
        device = list(net.parameters())[0].device
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            if isinstance(net, torch.nn.Module):
                net.eval()  # 评估模式, 这会关闭dropout
                acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
                net.train()  # 改回训练模式
            else:  # 自定义的模型, 3.13节之后不会用到, 不考虑GPU
                if ('is_training' in net.__code__.co_varnames):  # 如果有is_training这个参数
                    # 将is_training设置成False
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n


def train_fine_tuning():
    """模型微调测试"""
    net, optimizer = get_model()
    image_tr, image_te = get_format_image()
    loss = torch.nn.CrossEntropyLoss()
    train(image_tr, image_te, net, loss, optimizer, num_epochs=5)


if __name__ == '__main__':
    train_fine_tuning()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

致谢

  感谢李沐、Aston Zhang等老师的这本《动手学深度学习》一书,为鄙人学习深度学习提供了很大的帮助。本文一系列关于深度学习的博客均无侵权之意,只为记录自己的深度学习历程。
  项目Github地址:https://github.com/ShusenTang/Dive-into-DL-PyTorch

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/865688
推荐阅读
相关标签
  

闽ICP备14008679号