在实际项目中,对于一个神经网络我们一般不会完全从零开始训练一个神经络,而是采用一些预训练模型作为我们网络的初始参数,甚至直接拿过来作为主干网络,然后经过fine-tuning即可完成对我们网络的训练。

而对网络的fine-tuning大致分为三种:

第一种:预训练模型的参数比较适合我们的数据集,我们只需要对新添加的网络层进行训练即可;这时候可以通过pytorch将预训练模型的梯度冻结,训练过程中不在向预训练模型传递梯度信息,梯度只在新添加的网络层有效。

此处以ResNet50网络为例:

pre_model = ResNet.resnet50(True, './pretrained_model/')
'''
首先,我们必须冻结预训练过的层,因此在训练期间它们不会进行反向传播。
然后,我们重新定义最后的全连接层,即使用我们的图像来训练的图层。
'''
for param in pre_model.parameters():
    param.requires_grad = False     # 冻结预训练模型的各层,不对原模型进行反向传播训练

第二种:预训练模型的数据集和我们的数据集比较相近,并且我们数据集的规模也不算小,此时我们一般会给预训练模型和我们新添加的网络层分配不同大小的学习率(预训练模型的学习率会相对较小)。这样我们就能让预训练模型更适合我们的数据集。

pytorch实现不同学习率的分配:

'''
    首先,我们期望预训练模型的学习率是新添加网络层学习率的十分之一。
    具体的,筛选出新添加网络层的参数和预训练模型的参数分别为其配置学习率
    '''
    # 获得新添加网络层的参数
    new_layers_param = list(map(id, pre_model.new_layers.parameters()))
    # 获得预训练模型的参数
    base_param = filter(lambda p: id(p) not in new_layers_param , pre_model.parameters())

    # 定义优化器和损失函数
    optimizer = torch.optim.Adam([
        {'params': base_param, 'lr': LR * 0.1},
        {'params': pre_model.new_layers.parameters()}
    ], lr=LR)

第三种:当我们的数据集和预训练模型的数据集差别较大并且我们的数据集规模较大的时候,我们可以将预训练模型的参数当做我们模型的初始化参数,然后进行端到端的训练即可