跟李沐学AI随记-12-数据增广与微调

109 阅读4分钟

图像增广

图像增广在对训练图像进行一系列的随机变化之后,生成相似但不同的训练样本,从而扩大了训练集的规模。 此外,应用图像增广的原因是,随机改变训练样本可以减少模型对某些属性的依赖,从而提高模型的泛化能力。 例如,我们可以以不同的方式裁剪图像,使感兴趣的对象出现在不同的位置,减少模型对于对象出现位置的依赖。 我们还可以调整亮度、颜色等因素来降低模型对颜色的敏感度。

  • 翻转、裁剪
  • 颜色空间:亮度、对比度、饱和度和色调
  • 加噪声、锐化、平滑....

微调

微调包括以下四个步骤。

  1. 在源数据集(例如ImageNet数据集)上预训练神经网络模型,即源模型
  2. 创建一个新的神经网络模型,即目标模型。这将复制源模型上的所有模型设计及其参数(输出层除外)。我们假定这些模型参数包含从源数据集中学到的知识,这些知识也将适用于目标数据集。我们还假设源模型的输出层与源数据集的标签密切相关;因此不在目标模型中使用该层。
  3. 向目标模型添加输出层,其输出数是目标数据集中的类别数。然后随机初始化该层的模型参数。
  4. 在目标数据集(如椅子数据集)上训练目标模型。输出层将从头开始进行训练,而所有其他层的参数将根据源模型的参数进行微调。

个人理解:神经网络模型在大数据集提取到共性,学习到一种特殊的特征提取方法,再应用到自己需要使用的数据集中、只需略微改变参数即可,让预训练模型跑一遍自己的网络。 image.png 当目标数据集比源数据集小得多时,微调有助于提高模型的泛化能力。

神经网络=特征提取+softmax回归

神经网络通常学习有层次的特征表示

  • 低层次的特征更加通用
  • 高层次的特征和数据集更为相关
  • 可以固定底部一些层的参数不参与更新----视作更强的正则,复杂度降低。
# 热狗识别
# 2024.4.2
import os
import torch
import torchvision
from torch import nn
from d2l import torch as d2l

d2l.DATA_HUB['hotdog'] = (d2l.DATA_URL + 'hotdog.zip',
                         'fba480ffa8aa7e0febbb511d181409f899b9baa5')

data_dir = d2l.download_extract('hotdog')
train_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train'))
test_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test'))
hotdogs = [train_imgs[i][0] for i in range(8)]
not_hotdogs = [train_imgs[-i - 1][0] for i in range(8)]
d2l.show_images(hotdogs + not_hotdogs, 2, 8, scale=1.4)

# 数据增广
# 使用RGB通道的均值和标准差,以标准化每个通道,这些参数是经验数据(ImageNet中使用的)
normalize = torchvision.transforms.Normalize(
    [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

# 1、对输入的图像进行随机裁剪,并将其调整为指定的大小(在这里是224x224像素)---用于数据增强
# 2、将输入的图像随机水平翻转,以增加数据的多样性。
# 3、将PIL图像或numpy数组转换为PyTorch张量
# 通过将这些转换操作组合在一起,torchvision.transforms.Compose创建了一个数据预处理管道,该管道可以在训练过程中对输入的图像进行多个操作,
# 包括随机裁剪、随机水平翻转、转换为张量和归一化处理。这些操作有助于提高模型的泛化能力,并使模型更好地适应不同的输入数据。
train_augs = torchvision.transforms.Compose([
    torchvision.transforms.RandomResizedCrop(224),
    torchvision.transforms.RandomHorizontalFlip(),
    torchvision.transforms.ToTensor(),
    normalize])

test_augs = torchvision.transforms.Compose([
    torchvision.transforms.Resize([256, 256]),
    torchvision.transforms.CenterCrop(224),
    torchvision.transforms.ToTensor(),
    normalize])


pretrained_net = torchvision.models.resnet18(pretrained=True)
print('----全连接层:',pretrained_net.fc)

# 下面是一个copy的模型。只对最后一层进行更改,设置成一个两分类模型,并对权重进行随机初始化
finetune_net = torchvision.models.resnet18(pretrained=True)
finetune_net.fc = nn.Linear(finetune_net.fc.in_features, 2)
nn.init.xavier_uniform_(finetune_net.fc.weight)

# 如果param_group=True,输出层中的模型参数将使用十倍的学习率
def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5,
                      param_group=True):
    train_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(
        os.path.join(data_dir, 'train'), transform=train_augs),
        batch_size=batch_size, shuffle=True)
    test_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder(
        os.path.join(data_dir, 'test'), transform=test_augs),
        batch_size=batch_size)
    devices = d2l.try_all_gpus()
    loss = nn.CrossEntropyLoss(reduction="none")
    if param_group:
        # 读取当前层,若是最后一层,把它的学习率放大,希望他能改变的更多一点,而其他层的提取权重已经差不多了不用更改的那么大
        params_1x = [param for name, param in net.named_parameters()
             if name not in ["fc.weight", "fc.bias"]]
        trainer = torch.optim.SGD([{'params': params_1x},
                                   {'params': net.fc.parameters(),
                                    'lr': learning_rate * 10}],
                                lr=learning_rate, weight_decay=0.001)
    else:
        trainer = torch.optim.SGD(net.parameters(), lr=learning_rate,
                                  weight_decay=0.001)
    d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs,
                   devices)

train_fine_tuning(finetune_net, 5e-5)