• 动手学pytorch-迁移学习


    迁移学习

    1.基本概念

    2.Fine-tuning

    3.Fixed

    1.基本概念

    假设我们想从图像中识别出不同种类的椅子,然后将购买链接推荐给用户。一种可能的方法是先找出100种常见的椅子,为每种椅子拍摄1,000张不同角度的图像,然后在收集到的图像数据集上训练一个分类模型。这个椅子数据集虽然可能比Fashion-MNIST数据集要庞大,但样本数仍然不及ImageNet数据集中样本数的十分之一。这可能会导致适用于ImageNet数据集的复杂模型在这个椅子数据集上过拟合。同时,因为数据量有限,最终训练得到的模型的精度也可能达不到实用的要求。

    为了应对上述问题,一个显而易见的解决办法是收集更多的数据。然而,收集和标注数据会花费大量的时间和资金。例如,为了收集ImageNet数据集,研究人员花费了数百万美元的研究经费。虽然目前的数据采集成本已降低了不少,但其成本仍然不可忽略。

    另外一种解决办法是应用迁移学习(transfer learning),将从源数据集学到的知识迁移到目标数据集上。例如,虽然ImageNet数据集的图像大多跟椅子无关,但在该数据集上训练的模型可以抽取较通用的图像特征,从而能够帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅子也可能同样有效。

    2.Fine-tuning

    • 第一步:在新数据集上,替换预训练ConvNet顶层的分类器并retrain该分类器;
    • 第二步:以较小的学习率继续反向传播来微调预训练网络的权重;
    • 两种做法:微调ConvNet的所有层,或者保持some earlier layers fixed (due to overfitting concerns) ,只微调some higher-level portion of the network;
    from torchvision import models
    pretrained_net = models.resnet18(pretrained=True)
    
    # 打印最后两层网络
    print([*pretrained_net.named_children()][-2:])
    # [('avgpool', AdaptiveAvgPool2d(output_size=(1, 1))), 
    #  ('fc', Linear(in_features=512, out_features=1000, bias=True))]
    
    # 改变全连接层
    pretrained_net.fc = nn.Linear(512, 2)
    print(pretrained_net.fc)
    # Linear(in_features=512, out_features=2, bias=True)
    

    此时,pretrained_netfc层就被随机初始化了,但是其他层依然保存着预训练得到的参数。由于是在很大的ImageNet数据集上预训练的,所以参数已经足够好,因此一般只需使用较小的学习率来微调这些参数,而fc中的随机初始化参数一般需要更大的学习率从头训练。PyTorch可以方便的对模型的不同部分设置不同的学习参数,在下面代码中将fc的学习率设为已经预训练过的部分的10倍。

    output_params = list(map(id, pretrained_net.fc.parameters()))
    feature_params = filter(lambda p: id(p) not in output_params, pretrained_net.parameters())
    
    lr = 0.01
    optimizer = optim.SGD([{'params': feature_params},
                           {'params': pretrained_net.fc.parameters(), 'lr': lr * 10}],
                           lr=lr, weight_decay=0.001)
    

    3.Fixed

    利用在大数据集(如ImageNet)上预训练过的ConvNet(如AlexNet,VGGNet),移除最后几层(一般是最后分类器),将剩下的ConvNet作为应用于新数据集的固定不变的特征提取器,输出特征称为CNN codes,提取出所有CNN codes之后,再基于新数据集训练一个线性分类器。

    # 将模型参数设置为不进行梯度更新
    for name, layer in pretrained_net.named_children():
        requires_grad = False
        if name == 'fc':
            requires_grad = True
        for param in layer.parameters():
                param.requires_grad = requires_grad
                
    # 查看设置结果
    for name, layer in pretrained_net.named_children():
        for param in layer.parameters():
            print(f'{name}	requires grad = {param.requires_grad}' )
            break
    #conv1	requires grad = False
    #bn1	requires grad = False
    #layer1	requires grad = False
    #layer2	requires grad = False
    #layer3	requires grad = False
    #layer4	requires grad = False
    #fc	requires grad = True
    
    lr = 0.01
    optimizer = optim.SGD(pretrained_net.fc.parameters(), lr=lr, weight_decay=0.001)
    
  • 相关阅读:
    C++入门经典-例4.9-输出不同生命周期的变量值
    C++入门经典-例4.8-同名的全局变量和局部变量
    C++入门经典-例4.7-变量的作用域
    C++入门经典-例4.6-使用重载函数
    C++入门经典-例4.5-利用循环求n的阶乘
    C++入门经典-例4.4-循环嵌套之求n的阶乘
    C++入门经典-例4.3-函数的递归调用之汉诺塔问题
    C++入门经典-例4.2-调用默认参数的函数
    C++入门经典-例4.1-声明、定义和使用函数
    C++入门经典-例3.25-使用循环输出闰年
  • 原文地址:https://www.cnblogs.com/54hys/p/12340859.html
Copyright © 2020-2023  润新知