赞
踩
1.关于pytorch里面的单机多卡训练与模型保存的问题:
若想利用pytorch进行单机多卡训练深度学习模型,一般我们或采用
os.environ['CUDA_DEVICE_ORDER']='PCI_BUS_ID'
os.envirion['CUDA_VISIBLE_DEVICES']='1,2,3,4'
device_ids=[1,2,3,4]
model=nn.DataParallel(model,device_ids=device_ids)
model.cuda()
保存模型参数有两种方式:
方式一:torch.save(model.module.state_dict(),path=model_path)
方式二:torch.save(model.state_dict(),path=model_path)
当我们采用方式二进行模型参数保存时,在模型文件的state_dict里面,key值会多一个module,而采用方式一保存模型参数,模型文件的state_dict里面不会有module。
加载预训练模型参数:
case1:单卡加载方式一保存的模型参数
model.load_state_dict(torch.load(path=model_path),map_location='cpu')
case2:单卡加载方式二保存的模型参数
因为方式二保存的并行训练的模型里面的key会多一个module字段(因为nn.DataParallel本质上也是一个nn.module),所以我们采用单卡加载并行训练的模型参数的时候,需要去除key中的module字段
from collections import OrderedDict
state_dict = torch.load(model_path)
new_state_dict=OrderedDict()
for k,v in state_dict.items():
new_name = k.replace('module','.')
new_state_dict[new_name]=v
model.load_state_dict(new_sate_dict)
case3:多卡加载方式一保存的模型参数
new_model.load_state_dict(torch.load(model_path))
new_model=nn.DataParallel()
new_model.cuda()
case4:多卡加载方式二保存的模型参数
new_model.cuda()
new_model.DataParallel()
new_model.load_state_dict(torch.load(model_path))
综上所述,如果预训练模型参数里面的key没有module字段(单卡训练的预训练模型或者采用方式一保存的模型),则可以直接采用model.load_state_dict(torch.load(modelpath));如果预训练模型参数里面的key包含module字段,则需要去除module之后再加载模型参数或者先把模型进行nn.DataParallel()之后直接采用model.load_state_dict(torch.load(model_path))
Tips:
判断采用什么方式加载预训练模型的参数其实只需判断model.load_state_dict(torch.load(pretrained_model_path))中model和pretrained_model中的key值是否相同,要么都key值都经过DataParallel包装过进而key中都有module字段,要么两者key中都不包含module字段
2.pytorch里面CrossEntrophy()与二分类label之间的关系
在做二分类任务时,损失函数一般采用criterion=nn.BCEWithLogitsLoss(),但是也可以采用nn.CrossEntrophyLoss();如果最后输出层是一个神经元,则采用nn.BCEWithLogitsLoss,如果最后输出层是两个神经元,则采用nn.CrossEntrophyLoss
当使用nn.BCEWithLogitsLoss()的时候,需要注意维度匹配的问题,此时我们的label一般来说维度为[batch_size],而对于网络输出来说维度为[batch_size,1],此时我们需要将label的维度进行以下处理以匹配label与output:
label = label.unsqueeze(1)
label = label.float()#因为网络的输出一般是float类型,所以两者的数据类型也要匹配
当时用nn.CrossEntrophyLoss()的时候,我们的数据的label其实代表的是class index,也就是说如果label为1,则代表的是第2类,如果label为0,则代表的是第1类,下面我们看一下nn.CrossEntrophyLoss的具体实现:
在pytorch中CrossEntrophyLoss()=log_softmax+NLLLoss(),先后进行的操作依次是softmax+log+NLLLoss()
其中NLLLoss()=-output[label_index],也就是输出tensor在label对应的class index取值的负数。
下面说一下在二分类时候BCEWithLogitsLoss()和CrossEntrophyLoss()之间的联系与区别
相同之处:
两者都是交叉熵损失函数,前者为二值交叉熵
label=0的时候loss只用计算后一项;label=1的时候,loss只用计算前一项;
后者是通用形式的交叉熵
不同之处:
1.前者直接拟合label值,比如对于正样本,前者直接迫使网络输出接近1的数值
后者用于拟合正负类之间的概率分布,比如对于正样本,后者迫使网络输出[a,b]接近[0,1]分布,其中a是网络判定为负样本的概率,b是网络判定为正样本的概率
2.输出后处理:前者需要设定阈值来确定样本究竟是正样本还是负样本,后者只需进行argmax选出概率最大值对应的类别(其实后者本质上相当于将前者的阈值设为了0.5)
整体来看,对于二分类问题两者本质是一样的,只是CrossEntrophyLoss对输出多做了一步softmax处理
3.利用pytorch进行模型finetune的完整步骤
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
from torchvision import transforms
#加载预训练模型
##定义网络结构
model=model()
##加载预训练权重
model.load_state_dict(torch.load(model_path),map_location='cpu')
model.cuda()
model=nn.DataParallel()
#加载数据
tranform_list=[
transforms.RandomResizedCrop(224),
transforms.RndomHorizontalFlip(),
transforms.ToTensor()
]
transform=transforms.compose(tranform_list)
##创建一个dataset对象
dataset = ImageFolder(root=pic_path,transform=transform)
##创建一个Dataloader对象(循环遍历DataLoader对象)
train_loader = Dataloader(dataset,batch_size=64,shuffle=True,numworkers=32)
#定义损失函数
criterion=nn.CrossEntrophyLoss()
#定义优化器
optimizer=optim.SGD(model.parameters(),lr=0.0001,momentum=0.9)
#循环访问训练数据
for epoch in range(start_epoch,end_epoch):
model.train()
for i,data in enumerate(train_dataloader):
input_batch,label_batch = data
input_batch,label_batch = input_batch.cuda(),label_batch.cuda()
output_batch = model(input_batch)
loss = criterion(output_batch,label_batch)#计算损失
optimizer.zero_grad()#在进行优化之前,需要把loss关于weight的导数置为0
loss.backward()#反向传播,计算每一个参数的梯度
optimizer.step()#优化参数
需要注意的是ImageFolder的参数root:图片数据所在的路径,其路径结构应该是:
可以利用print(dataset.class_to_idx)了解文件夹与label之间的映射关系
dataset[0][0]#第一张图片的图片数据
dataset[0][1]#第一张图片的label
需要确认的是criterion和optimizer是否需要放到gpu上?
解答:输入数据和模型model需要迁到gpu上,而loss是不需要迁到gpu的,network layer本身是包含parameters的,所以需要采用.cuda()递归的将数据迁到gpu,而loss layer本身不含有paramter,所以不需要迁至gpu
使用Pytorch构建网络时,损失函数需要迁移到GPU上吗?www.zhihu.com代码参考链接:
Pytorch tutorials 实战教程(1)--训练自己的数据集(代码详解)blog.csdn.net参考:
1.https://blog.csdn.net/weixin_40123108/article/details/85099449
2.https://www.jianshu.com/p/8ea7fba72673
4.pytoch中的变学习率学习方法
pytorch中可以通过torch.optim.lr_scheduler设置不同的变学习率学习方式,主要由六种变学习率方式:
(1)torch.optim.lr_scheduler.StepLR:等间隔学习率调整方式,也就是每隔固定的迭代次数(epoch或者iteration)进行学习率衰减
(2)torch.optim.lr_scheduler.MultiStepLR:间隔按需学习率调整方式
(3)torch.optim.lr_scheduler.ExponentialLR:指数型衰减变学习率调整方式
(4)torch.optim.lr_scheduler.CosineAnnelingLR:余弦退火型学习率调整方式
(5)torch.optim.lr_scheduler.ReduceLROnPlateau:自适应型学习率调整方式,可以在验证集或者训练集的指标在一定时间内不再下降的时候对学习率进行调整
(6)torch.optim.lr_scheduler.LambdaLR:自定义学习率调整,可以自己定义学习率调整的方式
关于(1))(2)(3)(5)(6),好多博客讲解的已经很清楚了,而且参数也很好理解,这里就不多做赘述;对CosineAnnelingLR的一些认识可以参考:
梯度下降学习率的设定策略 | 卢明冬的博客lumingdong.cn另外说一句,这些变学习率调整方式一般是与SGD一起使用,对于一些自适应学习率的优化器例如Adam,合在一起使用的效果不明显,具体使用方式:
#定义一个SGD优化器
optimizer = torch.optim.SGD(model.parameters(),lr = 0.001,momentum=0.9)
#给SGD优化器绑定一个学习率衰减的sheduler
scheduler = torh.nn.optim.lr_scheduler.ConsineAnneling(optimizer,**kargs)
#在训练过程中应用变学习率学习方式
for epoch in range(total_epochs):
for i,data in enumerate(train_dataloader):
inputs,label = data
output = model(input)
loss = criterion(output,label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
scheduler.step()
注:上述代码只是为了说明使用lr_scheduler的使用方法,对于一些将cpu上的tensor转为cuda类型的tensor没有细究,另外中间有可能会涉及一些维度不匹配的问题,可以利用tensor.unsqueeze()来解决;这里主要想说的是,一般情况下我们是以epoch为最小粒度对lr进行调整的,所以scheduler.step()一般处于一个完整训练周期的外循环位置。
注:上述主要是自己平常使用pytorch中的一些个人理解和总结,如果有不对之处恳请指出;希望上述总结可以帮助你解决一些使用pytorch的一些疑问。
未完待续......
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。