• [pytorch笔记]04 --进阶训练技巧


    自定义损失函数

    方法

    • 定义成函数
    • 定义为类
    class DiceLoss(nn.Module):
        def __init__(self,weight=None,size_average=True):
            super(DiceLoss,self).__init__()
            
        def forward(self,inputs,targets,smooth=1):
            inputs = F.sigmoid(inputs)       
            inputs = inputs.view(-1)
            targets = targets.view(-1)
            intersection = (inputs * targets).sum()                   
            dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth)  
            return 1 - dice
    
    # 使用方法    
    criterion = DiceLoss()
    loss = criterion(input,targets)
    

    动态调整学习率

    学习率是影响模型收敛速度的重要因素,当学习速率设置过小时,会极大降低收敛速度,增加训练时间;学习速率过大,可能导致模型无法收敛。
    在pytorch中,torch.optim.lr_scheduler封装了一些动态调整学习率的方法。

    学习率变化策略作用
    LambdaLR将每个参数组的学习率设置为初始lr乘以给定函数
    MultiplicativeLR将每个参数组的学习率乘以指定函数中给出的因子
    StepLR根据学习率下降间隔step_size将学习率调整为lr*gamma
    MultiStepLR根据学习率下降间隔step_size将调整每个参数组的学习率
    ExponentialLR按指数函数开始衰减
    CosineAnnealingLR使用余弦退火来调整每个参数组的学习率
    ReduceLROnPlateau一旦学习过程停滞,将会自动调整学习率
    CyclicLR以恒定频率循环使用两边界之间的学习率
    OneCycleLR先由初始学习率提升到某个最大学习率,然后再退火到比初始学习率低得多的最小学习率
    CosineAnnealingWarmRestarts热重启策略详细

    模型微调

    目的:当我们使用预训练模型在自己的目标数据集上时,因为数据样本的不同,可能导致输出结果不理想

    1. 在源数据集(如ImageNet数据集)上预训练一个神经网络模型,即源模型。
    2. 创建一个新的神经网络模型,即目标模型。它复制了源模型上除了输出层外的所有模型设计及其参数。我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适用于目标数据集。我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在目标模型中不予采用。
    3. 为目标模型添加一个输出⼤小为⽬标数据集类别个数的输出层,并随机初始化该层的模型参数。
    4. 在目标数据集上训练目标模型。我们将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。

    常用库:torchvision、timm

    • torchvision
    #实例化网络
    import torchvision.models as models
    resnet18 = models.resnet18()
    # resnet18 = models.resnet18(pretrained=False)  等价于与上面的表达式
    alexnet = models.alexnet()
    vgg16 = models.vgg16()
    squeezenet = models.squeezenet1_0()
    densenet = models.densenet161()
    inception = models.inception_v3()
    googlenet = models.googlenet()
    shufflenet = models.shufflenet_v2_x1_0()
    mobilenet_v2 = models.mobilenet_v2()
    mobilenet_v3_large = models.mobilenet_v3_large()
    mobilenet_v3_small = models.mobilenet_v3_small()
    resnext50_32x4d = models.resnext50_32x4d()
    wide_resnet50_2 = models.wide_resnet50_2()
    mnasnet = models.mnasnet1_0()
    #传递pretrained参数
    #默认情况下为False,为True代表使用预训练好的权重
    import torchvision.models as models
    resnet18 = models.resnet18(pretrained=True)
    alexnet = models.alexnet(pretrained=True)
    squeezenet = models.squeezenet1_0(pretrained=True)
    vgg16 = models.vgg16(pretrained=True)
    densenet = models.densenet161(pretrained=True)
    inception = models.inception_v3(pretrained=True)
    googlenet = models.googlenet(pretrained=True)
    shufflenet = models.shufflenet_v2_x1_0(pretrained=True)
    mobilenet_v2 = models.mobilenet_v2(pretrained=True)
    mobilenet_v3_large = models.mobilenet_v3_large(pretrained=True)
    mobilenet_v3_small = models.mobilenet_v3_small(pretrained=True)
    resnext50_32x4d = models.resnext50_32x4d(pretrained=True)
    wide_resnet50_2 = models.wide_resnet50_2(pretrained=True)
    mnasnet = models.mnasnet1_0(pretrained=True)
    #训练特定层
    #默认情况下.requires_grad=True 
    #False下大街层,使参数不变动
    def set_parameter_requires_grad(model, feature_extracting):
        if feature_extracting:
            for param in model.parameters():
                param.requires_grad = False
    #将分类结果有1000类改为4类
    import torchvision.models as models
    # 冻结参数的梯度
    feature_extract = True
    model = models.resnet18(pretrained=True)
    set_parameter_requires_grad(model, feature_extract)
    # 修改模型
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(in_features=num_ftrs, out_features=4, bias=True)
    
    • timm
    #安装pip install timm
    #提供约592个预训练模型
    #timm.list_models方法查看提供的预训练模型
    import timm
    avail_pretrained_models = timm.list_models(pretrained=True)
    #参数传入模型名称,可以返回所有查询模型系列的所有模型
    len(avail_pretrained_models)
    #查看模型的具体参数
    model = timm.create_model('resnet34',num_classes=10,pretrained=True)#模型的创建
    model.default_cfg
    #使用和修改
    import timm
    import torch
    
    model = timm.create_model('resnet34',pretrained=True)
    x = torch.randn(1,3,224,224)
    output = model(x)
    output.shape
    model = timm.create_model('resnet34',pretrained=True)
    list(dict(model.named_children())['conv1'].parameters())
    #修改模型  1000类结果变为10类
    model = timm.create_model('resnet34',num_classes=10,pretrained=True)
    x = torch.randn(1,3,224,224)
    output = model(x)
    output.shape
    #改变输入通道数
    model = timm.create_model('resnet34',num_classes=10,pretrained=True,in_chans=1)
    x = torch.randn(1,1,224,224)
    output = model(x)
    #保存
    torch.save(model.state_dict(),'./checkpoint/timm_model.pth')
    model.load_state_dict(torch.load('./checkpoint/timm_model.pth'))
    

    半精度训练

    目的:节约算力和显存,pytorch默认未浮点数存储torch.float32,我们保留一半的信息对结果影响较小,故可以使用float16格式

    • 半精度训练设置
    form torch.cuda.amp import autocast
    @autocast()
    def forward(self,x):#模型中的forward函数
    	...
    	return x
    #训练中
    for x in train_loader:
    	x=x.cuda()
    	with autocast():
    	output = model(x)
    	...
    

    注:主要适用于数据本身尺寸较大的情况(3D图像、视频等)

    数据增强

    作用:解决无法获得大量数据,模型过拟合问题
    库包:imgaug

    • 安装
    #conda
    conda config --add channels conda-forge
    conda install imgaug
    #pip -install imgaug either via pypi
    pip install imgaug
    #pip -install the latest version directly from github
    pip install git+https://github.com/aleju/imgaug.git
    
    • 使用
      :该库无图像导入io操作,股使用imageio进行读入,其它库导入需要注意转换格式
    • 单图处理
    import imageio
    import imgaug as ia
    %matplotlib inline
    # 图片的读取
    img = imageio.imread("./Lenna.jpg")
    # 使用Image进行读取
    # img = Image.open("./Lenna.jpg")
    # image = np.array(img)
    # ia.imshow(image)
    # 可视化图片
    ia.imshow(img)
    from imgaug import augmenters as iaa
    # 设置随机数种子
    ia.seed(4)
    # 实例化方法
    rotate = iaa.Affine(rotate=(-4,45))
    img_aug = rotate(image=img)
    ia.imshow(img_aug)
    iaa.Sequential(children=None, # Augmenter集合
                   random_order=False, # 是否对每个batch使用不同顺序的Augmenter list
                   name=None,
                   deterministic=False,
                   random_state=None)#构建数据增强的管道
    # 构建处理序列
    aug_seq = iaa.Sequential([
        iaa.Affine(rotate=(-25,25)),
        iaa.AdditiveGaussianNoise(scale=(10,60)),
        iaa.Crop(percent=(0,0.2))
    ])
    # 对图片进行处理,image不可以省略,也不能写成images
    image_aug = aug_seq(image=img)
    ia.imshow(image_aug)
    
    • 按批次图处理
      将图形数据按照NHWC的形式或者由列表组成HWC的形式对批量图处理
    #以同一种方式处理
    images = [img,img,img,img,]#需要处理的放入一个list中
    images_aug = rotate(images=images)#仿射变换
    ia.imshow(np.hstack(images_aug))
    aug_seq = iaa.Sequential([
        iaa.Affine(rotate=(-25, 25)),
        iaa.AdditiveGaussianNoise(scale=(10, 60)),
        iaa.Crop(percent=(0, 0.2))
    ])#数据增强pipline
    
    # 传入时需要指明是images参数
    images_aug = aug_seq.augment_images(images = images)
    #images_aug = aug_seq(images = images) 
    ia.imshow(np.hstack(images_aug))
    #对同批次图片分部分处理
    iaa.Sometimes(p=0.5,  # 代表划分比例
                  then_list=None,  # Augmenter集合。p概率的图片进行变换的Augmenters。
                  else_list=None,  #1-p概率的图片会被进行变换的Augmenters。注意变换的图片应用的Augmenter只能是then_list或者else_list中的一个。
                  name=None,
                  deterministic=False,
                  random_state=None)
    
    • 对不同大小的图片处理
    # 构建pipline
    seq = iaa.Sequential([
        iaa.CropAndPad(percent=(-0.2, 0.2), pad_mode="edge"),  # crop and pad images
        iaa.AddToHueAndSaturation((-60, 60)),  # change their color
        iaa.ElasticTransformation(alpha=90, sigma=9),  # water-like effect
        iaa.Cutout()  # replace one squared area within the image by a constant intensity value
    ], random_order=True)
    
    # 加载不同大小的图片
    images_different_sizes = [
        imageio.imread("https://upload.wikimedia.org/wikipedia/commons/e/ed/BRACHYLAGUS_IDAHOENSIS.jpg"),
        imageio.imread("https://upload.wikimedia.org/wikipedia/commons/c/c9/Southern_swamp_rabbit_baby.jpg"),
        imageio.imread("https://upload.wikimedia.org/wikipedia/commons/9/9f/Lower_Keys_marsh_rabbit.jpg")
    ]
    
    # 对图片进行增强
    images_aug = seq(images=images_different_sizes)
    
    # 可视化结果
    print("Image 0 (input shape: %s, output shape: %s)" % (images_different_sizes[0].shape, images_aug[0].shape))
    ia.imshow(np.hstack([images_different_sizes[0], images_aug[0]]))
    
    print("Image 1 (input shape: %s, output shape: %s)" % (images_different_sizes[1].shape, images_aug[1].shape))
    ia.imshow(np.hstack([images_different_sizes[1], images_aug[1]]))
    
    print("Image 2 (input shape: %s, output shape: %s)" % (images_different_sizes[2].shape, images_aug[2].shape))
    ia.imshow(np.hstack([images_different_sizes[2], images_aug[2]]))
    

    imgaug在pytorch中的应用

    怎样在pytorch中使用imgaug

    import numpy as np
    from imgaug import augmenters as iaa
    from torch.utils.data import DataLoader, Dataset
    from torchvision import transforms
    
    # 构建pipline
    tfs = transforms.Compose([
        iaa.Sequential([
            iaa.flip.Fliplr(p=0.5),
            iaa.flip.Flipud(p=0.5),
            iaa.GaussianBlur(sigma=(0.0, 0.1)),
            iaa.MultiplyBrightness(mul=(0.65, 1.35)),
        ]).augment_image,
        # 不要忘记了使用ToTensor()
        transforms.ToTensor()
    ])
    
    # 自定义数据集
    class CustomDataset(Dataset):
        def __init__(self, n_images, n_classes, transform=None):
    		# 图片的读取,建议使用imageio
            self.images = np.random.randint(0, 255,
                                            (n_images, 224, 224, 3),
                                            dtype=np.uint8)
            self.targets = np.random.randn(n_images, n_classes)
            self.transform = transform
        def __getitem__(self, item):
            image = self.images[item]
            target = self.targets[item]
    
            if self.transform:
                image = self.transform(image)
    
            return image, target
    
        def __len__(self):
            return len(self.images)
    def worker_init_fn(worker_id):#在linux中保证num_workers>0时对数据的增强是随机的
        imgaug.seed(np.random.get_state()[1][0] + worker_id)
    custom_ds = CustomDataset(n_images=50, n_classes=10, transform=tfs)
    custom_dl = DataLoader(custom_ds, batch_size=64,
                           num_workers=4, pin_memory=True, 
                           worker_init_fn=worker_init_fn)
    

    使用argparse调参

    Argparse
    作用:解析输入的命令行参数再传入模型的超参数中。
    python命令行解析的标准模块,内置于python

    • 使用
    # demo.py
    import argparse
    # 创建ArgumentParser()对象
    parser = argparse.ArgumentParser()
    # 添加参数
    parser.add_argument('-o', '--output', action='store_true', 
        help="shows output")
    # action = `store_true` 会将output参数记录为True
    # type 规定了参数的格式
    # default 规定了默认值
    parser.add_argument('--lr', type=float, default=3e-5, help='select the learning rate, default=1e-3') 
    parser.add_argument('--batch_size', type=int, required=True, help='input batch size')  
    # 使用parse_args()解析函数
    args = parser.parse_args()
    if args.output:
        print("This is some output")
        print(f"learning rate:{args.lr} ")
    #不使用--  将会严格按照参数位置进行解析
    # positional.py
    import argparse
    # 位置参数
    parser = argparse.ArgumentParser()
    parser.add_argument('name')
    parser.add_argument('age')
    args = parser.parse_args()
    print(f'{args.name} is {args.age} years old')
    

    高效使用:使用argparse定义一个config.py 使用时导入

    参考

    损失函数库

  • 相关阅读:
    【项目开发】成长与收获
    C++ 练气期之指针所指何处
    Kmeans特征降维方法
    浏览器Browser截屏截长图使用记录220813
    我眼中的大数据(一)
    R语言使用plot函数可视化数据散点图,使用cex.main参数自定义设置主标题的字体大小
    苹果手机适配Xcode14及iOS 16操作系统
    【Linux】进程概念万字详解(上篇)
    Cilium系列-6-从地址伪装从IPtables切换为eBPF
    持续集成中软件测试的作用
  • 原文地址:https://blog.csdn.net/qq_42947060/article/details/126926541