pytorch进阶章节内容

自定义损失函数

以函数的方式定义

def my_loss(output, target):
    loss = torch.mean((output - target)**2)
    return loss

以类的方式定义

  • Loss函数部分继承自_loss, 部分继承自_WeightedLoss, 而_WeightedLoss继承自_loss, _loss继承自 nn.Module。我们可以将其当作神经网络的一层来对待,同样地,我们的损失函数类就需要继承自nn.Module类
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)  //代表的是reshape的含义
        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自带的张量接口,这样可以省去繁琐的求导步骤;

动态学习调整率

  • 学习率:学习率过小,导致收敛速度很慢,需要花费大量的时间去收敛;学习率过大,可能导致结果在最优解两侧不断震荡,甚至无法收敛
  • 在选定一个学习率之后 ,可能出现准确率震荡或者loss不再下降的情况,这种时候可以选择使用学习率衰减策略(scheduler)进行改善,来提高我们 的精度

pytorch提供的有如下:
# 选择一种优化器
optimizer = torch.optim.Adam(...) 
# 选择上面提到的一种或多种动态调整学习率的方法
scheduler1 = torch.optim.lr_scheduler.... 
scheduler2 = torch.optim.lr_scheduler....
...
schedulern = torch.optim.lr_scheduler....
# 进行训练
for epoch in range(100):
    train(...)
    validate(...)
    optimizer.step()
    # 需要在优化器参数更新之后再动态调整学习率
	scheduler1.step() 
	...
    schedulern.step()
  • 注:我们在使用官方给出的torch.optim.lr_scheduler时,需要将scheduler.step()放在optimizer.step()后面进行使用

自定义scheduler

def adjust_learning_rate(optimizer, epoch):
    lr = args.lr * (0.1 ** (epoch // 30)) //学习率每30轮下降为原来的1/10
    for param_group in optimizer.param_groups:
        param_group['lr'] = lr

定义好了之后,如何使用呢?

def adjust_learning_rate(optimizer,...):
    ...
optimizer = torch.optim.SGD(model.parameters(),lr = args.lr,momentum = 0.9)
for epoch in range(10):
    train(...)
    validate(...)
    adjust_learning_rate(optimizer,epoch)

模型微调

模型微调的引入以及背景

  • 许多开源的参数模型都是基于大,非常大的数据集进行训练的,但是当我们的训练集比较少的时候,用那些开源的模型肯定会导致过拟合
  • 一个解决方法是增加我们本身的数据集,采集更多的数据(图片)等
  • 另外一种解决方法是应用迁移学习,从开源数据集学到的知识迁移到我们自身的数据集上(比如抽取图像特征这种常规操作)
  • 迁移学习的一大应用场景就是模型微调:我们先找到一个同类的别人训练好的模型,把别人现成的训练好了的模型拿过来,换成自己的数据,通过训练调整一下参数

模型微调的步骤

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

1.实例化网络

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()

2.传递pretrained参数

  • 通过True或者False来决定是否使用预训练好的权重,在默认状态下pretrained = False,意味着我们不使用预训练得到的权重,当pretrained = 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代从头开始训练整个模型;
  • .requires_grad = False来冻结一部分层(一部分参数不改变),只训练某些特定层;
    冻结层的函数如下:
def set_parameter_requires_grad(model, feature_extracting):
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False

然后只改变某些特定层的函数,不改变其它层的参数

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=512, out_features=4, bias=True)

半精度训练

  • PyTorch默认的浮点数存储方式用的是torch.float32,但是很多情况下,用16位的也有同样的效果,且可以大大节省空间和时间,使得可以读入更多数据进行训练

如何实现呢?

  • 1:from torch.cuda.amp import autocast
  • 2:用autocast修饰forward
@autocast()   
def forward(self, x):
    ...
    return x
  • 3:在训练过程中,只需在将数据输入模型及其之后的部分放入“with autocast():“即可
for x in train_loader:
	x = x.cuda()
	with autocast():
        output = model(x)
        ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值