0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PyTorch常用代码段合集资料分享

新机器视觉 来源:小白学视觉 2023-12-18 11:27 次阅读

本文是PyTorch常用代码段合集,涵盖基本配置、张量处理、模型定义与操作、数据处理、模型训练与测试等5个方面,还给出了多个值得注意的Tips,内容非常全面。

PyTorch最好的资料是官方文档。本文是PyTorch常用代码段,在参考资料[1](张皓:PyTorch Cookbook)的基础上做了一些修补,方便使用时查阅。

基本配置

导入包和版本查询

importtorch
importtorch.nnasnn
importtorchvision
print(torch.__version__)
print(torch.version.cuda)
print(torch.backends.cudnn.version())
print(torch.cuda.get_device_name(0))

可复现性

硬件设备(CPUGPU)不同时,完全的可复现性无法保证,即使随机种子相同。但是,在同一个设备上,应该保证可复现性。具体做法是,在程序开始的时候固定torch的随机种子,同时也把numpy的随机种子固定。

np.random.seed(0)
torch.manual_seed(0)
torch.cuda.manual_seed_all(0)

torch.backends.cudnn.deterministic=True
torch.backends.cudnn.benchmark=False

显卡设置

如果只需要一张显卡

#Deviceconfiguration
device=torch.device('cuda'iftorch.cuda.is_available()else'cpu')

如果需要指定多张显卡,比如0,1号显卡。

importosos.environ['CUDA_VISIBLE_DEVICES']='0,1'

也可以在命令行运行代码时设置显卡:

CUDA_VISIBLE_DEVICES=0,1pythontrain.py

清除显存

torch.cuda.empty_cache()

也可以使用在命令行重置GPU的指令

nvidia-smi--gpu-reset-i[gpu_id]

张量(Tensor)处理

张量的数据类型

PyTorch有9种CPU张量类型和9种GPU张量类型。

a301479a-9cb0-11ee-8b88-92fbcf53809c.png

张量基本信息

tensor=torch.randn(3,4,5)print(tensor.type())#数据类型print(tensor.size())#张量的shape,是个元组print(tensor.dim())#维度的数量

命名张量

张量命名是一个非常有用的方法,这样可以方便地使用维度的名字来做索引或其他操作,大大提高了可读性、易用性,防止出错。

#在PyTorch1.3之前,需要使用注释
#Tensor[N,C,H,W]
images=torch.randn(32,3,56,56)
images.sum(dim=1)
images.select(dim=1,index=0)

#PyTorch1.3之后
NCHW=[‘N’,‘C’,‘H’,‘W’]
images=torch.randn(32,3,56,56,names=NCHW)
images.sum('C')
images.select('C',index=0)
#也可以这么设置
tensor=torch.rand(3,4,1,2,names=('C','N','H','W'))
#使用align_to可以对维度方便地排序
tensor=tensor.align_to('N','C','H','W')

数据类型转换

#设置默认类型,pytorch中的FloatTensor远远快于DoubleTensor
torch.set_default_tensor_type(torch.FloatTensor)

#类型转换
tensor=tensor.cuda()
tensor=tensor.cpu()
tensor=tensor.float()
tensor=tensor.long()

torch.Tensor与np.ndarray转换

除了CharTensor,其他所有CPU上的张量都支持转换为numpy格式然后再转换回来。

ndarray=tensor.cpu().numpy()
tensor=torch.from_numpy(ndarray).float()
tensor=torch.from_numpy(ndarray.copy()).float()#Ifndarrayhasnegativestride.

Torch.tensor与PIL.Image转换

#pytorch中的张量默认采用[N,C,H,W]的顺序,并且数据范围在[0,1],需要进行转置和规范化
#torch.Tensor->PIL.Image
image=PIL.Image.fromarray(torch.clamp(tensor*255,min=0,max=255).byte().permute(1,2,0).cpu().numpy())
image=torchvision.transforms.functional.to_pil_image(tensor)#Equivalentlyway

#PIL.Image->torch.Tensor
path=r'./figure.jpg'
tensor=torch.from_numpy(np.asarray(PIL.Image.open(path))).permute(2,0,1).float()/255
tensor=torchvision.transforms.functional.to_tensor(PIL.Image.open(path))#Equivalentlyway

np.ndarray与PIL.Image的转换

image=PIL.Image.fromarray(ndarray.astype(np.uint8))
ndarray=np.asarray(PIL.Image.open(path))

从只包含一个元素的张量中提取值

value=torch.rand(1).item()

张量形变

#在将卷积层输入全连接层的情况下通常需要对张量做形变处理,
#相比torch.view,torch.reshape可以自动处理输入张量不连续的情况

tensor=torch.rand(2,3,4)
shape=(6,4)
tensor=torch.reshape(tensor,shape)

打乱顺序

tensor=tensor[torch.randperm(tensor.size(0))]#打乱第一个维度

水平翻转

#pytorch不支持tensor[::-1]这样的负步长操作,水平翻转可以通过张量索引实现
#假设张量的维度为[N,D,H,W].

tensor=tensor[:,:,:,torch.arange(tensor.size(3)-1,-1,-1).long()]

复制张量

#Operation|New/Sharedmemory|Stillincomputationgraph|
tensor.clone()#|New|Yes|
tensor.detach()#|Shared|No|
tensor.detach.clone()()#|New|No|

张量拼接

'''
注意torch.cat和torch.stack的区别在于torch.cat沿着给定的维度拼接,
而torch.stack会新增一维。例如当参数是3个10x5的张量,torch.cat的结果是30x5的张量,
而torch.stack的结果是3x10x5的张量。
'''
tensor=torch.cat(list_of_tensors,dim=0)
tensor=torch.stack(list_of_tensors,dim=0)

将整数标签转为one-hot编码

#pytorch的标记默认从0开始
tensor=torch.tensor([0,2,1,3])
N=tensor.size(0)
num_classes=4
one_hot=torch.zeros(N,num_classes).long()
one_hot.scatter_(dim=1,index=torch.unsqueeze(tensor,dim=1),src=torch.ones(N,num_classes).long())

得到非零元素

torch.nonzero(tensor)#indexofnon-zeroelements
torch.nonzero(tensor==0)#indexofzeroelements
torch.nonzero(tensor).size(0)#numberofnon-zeroelements
torch.nonzero(tensor==0).size(0)#numberofzeroelements

判断两个张量相等

torch.allclose(tensor1,tensor2)#floattensor
torch.equal(tensor1,tensor2)#inttensor

张量扩展

#Expandtensorofshape64*512toshape64*512*7*7.
tensor=torch.rand(64,512)
torch.reshape(tensor,(64,512,1,1)).expand(64,512,7,7)

矩阵乘法

#Matrixmultiplcation:(m*n)*(n*p)*->(m*p).
result=torch.mm(tensor1,tensor2)

#Batchmatrixmultiplication:(b*m*n)*(b*n*p)->(b*m*p)
result=torch.bmm(tensor1,tensor2)

#Element-wisemultiplication.
result=tensor1*tensor2

计算两组数据之间的两两欧式距离

利用广播机制

dist=torch.sqrt(torch.sum((X1[:,None,:]-X2)**2,dim=2))

模型定义和操作

一个简单两层卷积网络的示例

#convolutionalneuralnetwork(2convolutionallayers)

classConvNet(nn.Module):
def__init__(self,num_classes=10):
super(ConvNet,self).__init__()
self.layer1=nn.Sequential(
nn.Conv2d(1,16,kernel_size=5,stride=1,padding=2),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2,stride=2))
self.layer2=nn.Sequential(
nn.Conv2d(16,32,kernel_size=5,stride=1,padding=2),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2,stride=2))
self.fc=nn.Linear(7*7*32,num_classes)

defforward(self,x):
out=self.layer1(x)
out=self.layer2(out)
out=out.reshape(out.size(0),-1)
out=self.fc(out)
returnout

model=ConvNet(num_classes).to(device)

卷积层的计算和展示可以用这个网站辅助。

双线性汇合(bilinear pooling)

X=torch.reshape(N,D,H*W)#AssumeXhasshapeN*D*H*W
X=torch.bmm(X,torch.transpose(X,1,2))/(H*W)#Bilinearpooling
assertX.size()==(N,D,D)
X=torch.reshape(X,(N,D*D))
X=torch.sign(X)*torch.sqrt(torch.abs(X)+1e-5)#Signed-sqrtnormalization
X=torch.nn.functional.normalize(X)#L2normalization

多卡同步 BN(Batch normalization)

当使用 torch.nn.DataParallel 将代码运行在多张 GPU 卡上时,PyTorch 的 BN 层默认操作是各卡上数据独立地计算均值和标准差,同步 BN 使用所有卡上的数据一起计算 BN 层的均值和标准差,缓解了当批量大小(batch size)比较小时对均值和标准差估计不准的情况,是在目标检测等任务中一个有效的提升性能的技巧。

sync_bn=torch.nn.SyncBatchNorm(num_features,

eps=1e-05,
momentum=0.1,
affine=True,
track_running_stats=True)

将已有网络的所有BN层改为同步BN层

defconvertBNtoSyncBN(module,process_group=None):

'''RecursivelyreplaceallBNlayerstoSyncBNlayer.

Args:
module[torch.nn.Module].Network
'''
ifisinstance(module,torch.nn.modules.batchnorm._BatchNorm):
sync_bn=torch.nn.SyncBatchNorm(module.num_features,module.eps,module.momentum,
module.affine,module.track_running_stats,process_group)
sync_bn.running_mean=module.running_mean
sync_bn.running_var=module.running_var
ifmodule.affine:
sync_bn.weight=module.weight.clone().detach()
sync_bn.bias=module.bias.clone().detach()
returnsync_bn
else:
forname,child_moduleinmodule.named_children():
setattr(module,name)=convert_syncbn_model(child_module,process_group=process_group))
returnmodule

类似 BN 滑动平均

如果要实现类似 BN 滑动平均的操作,在 forward 函数中要使用原地(inplace)操作给滑动平均赋值。

classBN(torch.nn.Module)

def__init__(self):
...
self.register_buffer('running_mean',torch.zeros(num_features))

defforward(self,X):
...
self.running_mean+=momentum*(current-self.running_mean)

计算模型整体参数量

num_parameters=sum(torch.numel(parameter)forparameterinmodel.parameters())

查看网络中的参数

可以通过model.state_dict()或者model.named_parameters()函数查看现在的全部可训练参数(包括通过继承得到的父类中的参数)

params=list(model.named_parameters())
(name,param)=params[28]
print(name)
print(param.grad)
print('-------------------------------------------------')
(name2,param2)=params[29]
print(name2)
print(param2.grad)
print('----------------------------------------------------')
(name1,param1)=params[30]
print(name1)
print(param1.grad)

模型可视化(使用pytorchviz)

szagoruyko/pytorchvizgithub.com

类似 Keras 的 model.summary() 输出模型信息,使用pytorch-summary

sksq96/pytorch-summarygithub.com

模型权重初始化

注意 model.modules() 和 model.children() 的区别:model.modules() 会迭代地遍历模型的所有子层,而 model.children() 只会遍历模型下的一层。

#Commonpractiseforinitialization.
forlayerinmodel.modules():
ifisinstance(layer,torch.nn.Conv2d):
torch.nn.init.kaiming_normal_(layer.weight,mode='fan_out',
nonlinearity='relu')
iflayer.biasisnotNone:
torch.nn.init.constant_(layer.bias,val=0.0)
elifisinstance(layer,torch.nn.BatchNorm2d):
torch.nn.init.constant_(layer.weight,val=1.0)
torch.nn.init.constant_(layer.bias,val=0.0)
elifisinstance(layer,torch.nn.Linear):
torch.nn.init.xavier_normal_(layer.weight)
iflayer.biasisnotNone:
torch.nn.init.constant_(layer.bias,val=0.0)

#Initializationwithgiventensor.
layer.weight=torch.nn.Parameter(tensor)

提取模型中的某一层

modules()会返回模型中所有模块的迭代器,它能够访问到最内层,比如self.layer1.conv1这个模块,还有一个与它们相对应的是name_children()属性以及named_modules(),这两个不仅会返回模块的迭代器,还会返回网络层的名字。

#取模型中的前两层
new_model=nn.Sequential(*list(model.children())[:2]
#如果希望提取出模型中的所有卷积层,可以像下面这样操作:
forlayerinmodel.named_modules():
ifisinstance(layer[1],nn.Conv2d):
conv_model.add_module(layer[0],layer[1])

部分层使用预训练模型

注意如果保存的模型是 torch.nn.DataParallel,则当前的模型也需要是

model.load_state_dict(torch.load('model.pth'),strict=False)

将在 GPU 保存的模型加载到 CPU

model.load_state_dict(torch.load('model.pth',map_location='cpu'))

导入另一个模型的相同部分到新的模型

模型导入参数时,如果两个模型结构不一致,则直接导入参数会报错。用下面方法可以把另一个模型的相同的部分导入到新的模型中。

#model_new代表新的模型
#model_saved代表其他模型,比如用torch.load导入的已保存的模型
model_new_dict=model_new.state_dict()
model_common_dict={k:vfork,vinmodel_saved.items()ifkinmodel_new_dict.keys()}
model_new_dict.update(model_common_dict)
model_new.load_state_dict(model_new_dict)

数据处理

计算数据集的均值和标准差

importos
importcv2
importnumpyasnp
fromtorch.utils.dataimportDataset
fromPILimportImage


defcompute_mean_and_std(dataset):
#输入PyTorch的dataset,输出均值和标准差
mean_r=0
mean_g=0
mean_b=0

forimg,_indataset:
img=np.asarray(img)#changePILImagetonumpyarray
mean_b+=np.mean(img[:,:,0])
mean_g+=np.mean(img[:,:,1])
mean_r+=np.mean(img[:,:,2])

mean_b/=len(dataset)
mean_g/=len(dataset)
mean_r/=len(dataset)

diff_r=0
diff_g=0
diff_b=0

N=0

forimg,_indataset:
img=np.asarray(img)

diff_b+=np.sum(np.power(img[:,:,0]-mean_b,2))
diff_g+=np.sum(np.power(img[:,:,1]-mean_g,2))
diff_r+=np.sum(np.power(img[:,:,2]-mean_r,2))

N+=np.prod(img[:,:,0].shape)

std_b=np.sqrt(diff_b/N)
std_g=np.sqrt(diff_g/N)
std_r=np.sqrt(diff_r/N)

mean=(mean_b.item()/255.0,mean_g.item()/255.0,mean_r.item()/255.0)
std=(std_b.item()/255.0,std_g.item()/255.0,std_r.item()/255.0)
returnmean,std

得到视频数据基本信息

importcv2
video=cv2.VideoCapture(mp4_path)
height=int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
width=int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
num_frames=int(video.get(cv2.CAP_PROP_FRAME_COUNT))
fps=int(video.get(cv2.CAP_PROP_FPS))
video.release()

TSN 每段(segment)采样一帧视频

K=self._num_segments
ifis_train:
ifnum_frames>K:
#Randomindexforeachsegment.
frame_indices=torch.randint(
high=num_frames//K,size=(K,),dtype=torch.long)
frame_indices+=num_frames//K*torch.arange(K)
else:
frame_indices=torch.randint(
high=num_frames,size=(K-num_frames,),dtype=torch.long)
frame_indices=torch.sort(torch.cat((
torch.arange(num_frames),frame_indices)))[0]
else:
ifnum_frames>K:
#Middleindexforeachsegment.
frame_indices=num_frames/K//2
frame_indices+=num_frames//K*torch.arange(K)
else:
frame_indices=torch.sort(torch.cat((
torch.arange(num_frames),torch.arange(K-num_frames))))[0]
assertframe_indices.size()==(K,)
return[frame_indices[i]foriinrange(K)]

常用训练和验证数据预处理

其中 ToTensor 操作会将 PIL.Image 或形状为 H×W×D,数值范围为 [0, 255] 的 np.ndarray 转换为形状为 D×H×W,数值范围为 [0.0, 1.0] 的 torch.Tensor。

train_transform=torchvision.transforms.Compose([
torchvision.transforms.RandomResizedCrop(size=224,
scale=(0.08,1.0)),
torchvision.transforms.RandomHorizontalFlip(),
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(mean=(0.485,0.456,0.406),
std=(0.229,0.224,0.225)),
])
val_transform=torchvision.transforms.Compose([
torchvision.transforms.Resize(256),
torchvision.transforms.CenterCrop(224),
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(mean=(0.485,0.456,0.406),
std=(0.229,0.224,0.225)),
])

模型训练和测试

分类模型训练代码

#Lossandoptimizer
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=learning_rate)

#Trainthemodel
total_step=len(train_loader)
forepochinrange(num_epochs):
fori,(images,labels)inenumerate(train_loader):
images=images.to(device)
labels=labels.to(device)

#Forwardpass
outputs=model(images)
loss=criterion(outputs,labels)

#Backwardandoptimizer
optimizer.zero_grad()
loss.backward()
optimizer.step()

if(i+1)%100==0:
print('Epoch:[{}/{}],Step:[{}/{}],Loss:{}'
.format(epoch+1,num_epochs,i+1,total_step,loss.item()))

分类模型测试代码

#Testthemodel
model.eval()#evalmode(batchnormusesmovingmean/variance
#insteadofmini-batchmean/variance)
withtorch.no_grad():
correct=0
total=0
forimages,labelsintest_loader:
images=images.to(device)
labels=labels.to(device)
outputs=model(images)
_,predicted=torch.max(outputs.data,1)
total+=labels.size(0)
correct+=(predicted==labels).sum().item()

print('Testaccuracyofthemodelonthe10000testimages:{}%'
.format(100*correct/total))

自定义loss

继承torch.nn.Module类写自己的loss。

classMyLoss(torch.nn.Moudle):
def__init__(self):
super(MyLoss,self).__init__()

defforward(self,x,y):
loss=torch.mean((x-y)**2)
returnloss

标签平滑(label smoothing)

写一个label_smoothing.py的文件,然后在训练代码里引用,用LSR代替交叉熵损失即可。label_smoothing.py内容如下:

importtorch
importtorch.nnasnn


classLSR(nn.Module):

def__init__(self,e=0.1,reduction='mean'):
super().__init__()

self.log_softmax=nn.LogSoftmax(dim=1)
self.e=e
self.reduction=reduction

def_one_hot(self,labels,classes,value=1):
"""
Convertlabelstoonehotvectors

Args:
labels:torchtensorinformat[label1,label2,label3,...]
classes:int,numberofclasses
value:labelvalueinonehotvector,defaultto1

Returns:
returnonehotformatlabelsinshape[batchsize,classes]
"""

one_hot=torch.zeros(labels.size(0),classes)

#labelsandvalue_addedsizemustmatch
labels=labels.view(labels.size(0),-1)
value_added=torch.Tensor(labels.size(0),1).fill_(value)

value_added=value_added.to(labels.device)
one_hot=one_hot.to(labels.device)

one_hot.scatter_add_(1,labels,value_added)

returnone_hot

def_smooth_label(self,target,length,smooth_factor):
"""converttargetstoone-hotformat,andsmooth
them.
Args:
target:targetinformwith[label1,label2,label_batchsize]
length:lengthofone-hotformat(numberofclasses)
smooth_factor:smoothfactorforlabelsmooth

Returns:
smoothedlabelsinonehotformat
"""
one_hot=self._one_hot(target,length,value=1-smooth_factor)
one_hot+=smooth_factor/(length-1)

returnone_hot.to(target.device)

defforward(self,x,target):

ifx.size(0)!=target.size(0):
raiseValueError('Expectedinputbatchsize({})tomatchtargetbatch_size({})'
.format(x.size(0),target.size(0)))

ifx.dim()< 2:
            raise ValueError('Expected input tensor to have least 2 dimensions(got {})'
                    .format(x.size(0)))

        if x.dim() != 2:
            raise ValueError('Only 2 dimension tensor are implemented, (got {})'
                    .format(x.size()))


        smoothed_target = self._smooth_label(target, x.size(1), self.e)
        x = self.log_softmax(x)
        loss = torch.sum(- x * smoothed_target, dim=1)

        if self.reduction == 'none':
            return loss

        elif self.reduction == 'sum':
            return torch.sum(loss)

        elif self.reduction == 'mean':
            return torch.mean(loss)

        else:
            raise ValueError('unrecognized option, expect reduction to be one of none, mean, sum')

或者直接在训练文件里做label smoothing

forimages,labelsintrain_loader:
images,labels=images.cuda(),labels.cuda()
N=labels.size(0)
#Cisthenumberofclasses.
smoothed_labels=torch.full(size=(N,C),fill_value=0.1/(C-1)).cuda()
smoothed_labels.scatter_(dim=1,index=torch.unsqueeze(labels,dim=1),value=0.9)

score=model(images)
log_prob=torch.nn.functional.log_softmax(score,dim=1)
loss=-torch.sum(log_prob*smoothed_labels)/N
optimizer.zero_grad()
loss.backward()
optimizer.step()

Mixup训练

beta_distribution=torch.distributions.beta.Beta(alpha,alpha)
forimages,labelsintrain_loader:
images,labels=images.cuda(),labels.cuda()

#Mixupimagesandlabels.
lambda_=beta_distribution.sample([]).item()
index=torch.randperm(images.size(0)).cuda()
mixed_images=lambda_*images+(1-lambda_)*images[index,:]
label_a,label_b=labels,labels[index]

#Mixuploss.
scores=model(mixed_images)
loss=(lambda_*loss_function(scores,label_a)
+(1-lambda_)*loss_function(scores,label_b))
optimizer.zero_grad()
loss.backward()
optimizer.step()

L1 正则化

l1_regularization=torch.nn.L1Loss(reduction='sum')
loss=...#Standardcross-entropyloss

forparaminmodel.parameters():
loss+=torch.sum(torch.abs(param))
loss.backward()

不对偏置项进行权重衰减(weight decay)

pytorch里的weight decay相当于l2正则

bias_list=(paramforname,paraminmodel.named_parameters()ifname[-4:]=='bias')
others_list=(paramforname,paraminmodel.named_parameters()ifname[-4:]!='bias')
parameters=[{'parameters':bias_list,'weight_decay':0},
{'parameters':others_list}]
optimizer=torch.optim.SGD(parameters,lr=1e-2,momentum=0.9,weight_decay=1e-4)

梯度裁剪(gradient clipping)

torch.nn.utils.clip_grad_norm_(model.parameters(),max_norm=20)

得到当前学习率

#Ifthereisonegloballearningrate(whichisthecommoncase).
lr=next(iter(optimizer.param_groups))['lr']

#Iftherearemultiplelearningratesfordifferentlayers.
all_lr=[]
forparam_groupinoptimizer.param_groups:
all_lr.append(param_group['lr'])

另一种方法,在一个batch训练代码里,当前的lr是optimizer.param_groups[0]['lr']

学习率衰减

#Reducelearningratewhenvalidationaccuarcyplateau.
scheduler=torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer,mode='max',patience=5,verbose=True)
fortinrange(0,80):
train(...)
val(...)
scheduler.step(val_acc)

#Cosineannealinglearningrate.
scheduler=torch.optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max=80)
#Reducelearningrateby10atgivenepochs.
scheduler=torch.optim.lr_scheduler.MultiStepLR(optimizer,milestones=[50,70],gamma=0.1)
fortinrange(0,80):
scheduler.step()
train(...)
val(...)

#Learningratewarmupby10epochs.
scheduler=torch.optim.lr_scheduler.LambdaLR(optimizer,lr_lambda=lambdat:t/10)
fortinrange(0,10):
scheduler.step()
train(...)
val(...)

优化器链式更新

从1.4版本开始,torch.optim.lr_scheduler 支持链式更新(chaining),即用户可以定义两个 schedulers,并交替在训练中使用。

importtorch
fromtorch.optimimportSGD
fromtorch.optim.lr_schedulerimportExponentialLR,StepLR
model=[torch.nn.Parameter(torch.randn(2,2,requires_grad=True))]
optimizer=SGD(model,0.1)
scheduler1=ExponentialLR(optimizer,gamma=0.9)
scheduler2=StepLR(optimizer,step_size=3,gamma=0.1)
forepochinrange(4):
print(epoch,scheduler2.get_last_lr()[0])
optimizer.step()
scheduler1.step()
scheduler2.step()

模型训练可视化

PyTorch可以使用tensorboard来可视化训练过程。

安装和运行TensorBoard。

pipinstalltensorboard
tensorboard--logdir=runs

使用SummaryWriter类来收集和可视化相应的数据,放了方便查看,可以使用不同的文件夹,比如'Loss/train'和'Loss/test'。

fromtorch.utils.tensorboardimportSummaryWriter
importnumpyasnp

writer=SummaryWriter()

forn_iterinrange(100):
writer.add_scalar('Loss/train',np.random.random(),n_iter)
writer.add_scalar('Loss/test',np.random.random(),n_iter)
writer.add_scalar('Accuracy/train',np.random.random(),n_iter)
writer.add_scalar('Accuracy/test',np.random.random(),n_iter)

保存与加载断点

注意为了能够恢复训练,我们需要同时保存模型和优化器的状态,以及当前的训练轮数。

start_epoch=0
#Loadcheckpoint.
ifresume:#resume为参数,第一次训练时设为0,中断再训练时设为1
model_path=os.path.join('model','best_checkpoint.pth.tar')
assertos.path.isfile(model_path)
checkpoint=torch.load(model_path)
best_acc=checkpoint['best_acc']
start_epoch=checkpoint['epoch']
model.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
print('Loadcheckpointatepoch{}.'.format(start_epoch))
print('Bestaccuracysofar{}.'.format(best_acc))

#Trainthemodel
forepochinrange(start_epoch,num_epochs):
...

#Testthemodel
...

#savecheckpoint
is_best=current_acc>best_acc
best_acc=max(current_acc,best_acc)
checkpoint={
'best_acc':best_acc,
'epoch':epoch+1,
'model':model.state_dict(),
'optimizer':optimizer.state_dict(),
}
model_path=os.path.join('model','checkpoint.pth.tar')
best_model_path=os.path.join('model','best_checkpoint.pth.tar')
torch.save(checkpoint,model_path)
ifis_best:
shutil.copy(model_path,best_model_path)

提取 ImageNet 预训练模型某层的卷积特征

#VGG-16relu5-3feature.
model=torchvision.models.vgg16(pretrained=True).features[:-1]
#VGG-16pool5feature.
model=torchvision.models.vgg16(pretrained=True).features
#VGG-16fc7feature.
model=torchvision.models.vgg16(pretrained=True)
model.classifier=torch.nn.Sequential(*list(model.classifier.children())[:-3])
#ResNetGAPfeature.
model=torchvision.models.resnet18(pretrained=True)
model=torch.nn.Sequential(collections.OrderedDict(
list(model.named_children())[:-1]))

withtorch.no_grad():
model.eval()
conv_representation=model(image)

提取 ImageNet 预训练模型多层的卷积特征

classFeatureExtractor(torch.nn.Module):
"""Helperclasstoextractseveralconvolutionfeaturesfromthegiven
pre-trainedmodel.

Attributes:
_model,torch.nn.Module.
_layers_to_extract,listorset

Example:
>>>model=torchvision.models.resnet152(pretrained=True)
>>>model=torch.nn.Sequential(collections.OrderedDict(
list(model.named_children())[:-1]))
>>>conv_representation=FeatureExtractor(
pretrained_model=model,
layers_to_extract={'layer1','layer2','layer3','layer4'})(image)
"""
def__init__(self,pretrained_model,layers_to_extract):
torch.nn.Module.__init__(self)
self._model=pretrained_model
self._model.eval()
self._layers_to_extract=set(layers_to_extract)

defforward(self,x):
withtorch.no_grad():
conv_representation=[]
forname,layerinself._model.named_children():
x=layer(x)
ifnameinself._layers_to_extract:
conv_representation.append(x)
returnconv_representation

微调全连接层

model=torchvision.models.resnet18(pretrained=True)
forparaminmodel.parameters():
param.requires_grad=False
model.fc=nn.Linear(512,100)#Replacethelastfclayer
optimizer=torch.optim.SGD(model.fc.parameters(),lr=1e-2,momentum=0.9,weight_decay=1e-4)

以较大学习率微调全连接层,较小学习率微调卷积层

model=torchvision.models.resnet18(pretrained=True)
finetuned_parameters=list(map(id,model.fc.parameters()))
conv_parameters=(pforpinmodel.parameters()ifid(p)notinfinetuned_parameters)
parameters=[{'params':conv_parameters,'lr':1e-3},
{'params':model.fc.parameters()}]
optimizer=torch.optim.SGD(parameters,lr=1e-2,momentum=0.9,weight_decay=1e-4)

其他注意事项

不要使用太大的线性层。因为nn.Linear(m,n)使用的是的内存,线性层太大很容易超出现有显存。

不要在太长的序列上使用RNN。因为RNN反向传播使用的是BPTT算法,其需要的内存和输入序列的长度呈线性关系。

model(x) 前用 model.train() 和 model.eval() 切换网络状态。

不需要计算梯度的代码块用 with torch.no_grad() 包含起来。

model.eval() 和 torch.no_grad() 的区别在于,model.eval() 是将网络切换为测试状态,例如 BN 和dropout在训练和测试阶段使用不同的计算方法。torch.no_grad() 是关闭 PyTorch 张量的自动求导机制,以减少存储使用和加速计算,得到的结果无法进行 loss.backward()。

model.zero_grad()会把整个模型的参数的梯度都归零, 而optimizer.zero_grad()只会把传入其中的参数的梯度归零.

torch.nn.CrossEntropyLoss 的输入不需要经过 Softmax。torch.nn.CrossEntropyLoss 等价于 torch.nn.functional.log_softmax + torch.nn.NLLLoss。

loss.backward() 前用 optimizer.zero_grad() 清除累积梯度。

torch.utils.data.DataLoader 中尽量设置 pin_memory=True,对特别小的数据集如 MNIST 设置 pin_memory=False 反而更快一些。num_workers 的设置需要在实验中找到最快的取值。

用 del 及时删除不用的中间变量,节约 GPU 存储。使用 inplace 操作可节约 GPU 存储,如:

x=torch.nn.functional.relu(x,inplace=True)

减少 CPU 和 GPU 之间的数据传输。例如如果你想知道一个 epoch 中每个 mini-batch 的 loss 和准确率,先将它们累积在 GPU 中等一个 epoch 结束之后一起传输回 CPU 会比每个 mini-batch 都进行一次 GPU 到 CPU 的传输更快。

使用半精度浮点数 half() 会有一定的速度提升,具体效率依赖于 GPU 型号。需要小心数值精度过低带来的稳定性问题。

时常使用 assert tensor.size() == (N, D, H, W) 作为调试手段,确保张量维度和你设想中一致。

除了标记 y 外,尽量少使用一维张量,使用 n*1 的二维张量代替,可以避免一些意想不到的一维张量计算结果。

统计代码各部分耗时:

withtorch.autograd.profiler.profile(enabled=True,use_cuda=False)asprofile:

...print(profile)#或者在命令行运行python-mtorch.utils.bottleneckmain.py

使用TorchSnooper来调试PyTorch代码,程序在执行的时候,就会自动 print 出来每一行的执行结果的 tensor 的形状、数据类型、设备、是否需要梯度的信息。

#pipinstalltorchsnooper
importtorchsnooper#对于函数,使用修饰器@torchsnooper.snoop()

#如果不是函数,使用 with 语句来激活 TorchSnooper,把训练的那个循环装进 with 语句中去。
withtorchsnooper.snoop():
原本的代码

https://github.com/zasdfgbnm/TorchSnoopergithub.com

模型可解释性,使用captum库:https://captum.ai/captum.ai

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10695

    浏览量

    209208
  • gpu
    gpu
    +关注

    关注

    27

    文章

    4583

    浏览量

    128119
  • 函数
    +关注

    关注

    3

    文章

    4233

    浏览量

    61953
  • pytorch
    +关注

    关注

    2

    文章

    794

    浏览量

    13002

原文标题:PyTorch高频代码段集锦!

文章出处:【微信号:vision263com,微信公众号:新机器视觉】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Verilog HDL详细资料合集

    合集资料包括:1.Verilog HDL程序设计实例详解2.Verilog HDL经典教程3.Verilog HDL实验练习与语法手册4.Verilog HDL硬件描述语言
    发表于 08-21 10:06

    智能家居合集资料

    基于单片机的智能家居控制系统、智能家居控制系统毕业论文(包含PCB和程序)、基于SPCE061A的智能家居系统的设计、智能家电控制系统毕业设计,智能家居合集资料,从事开发工作可以参考一下。
    发表于 12-03 16:38 80次下载

    工程测试技术的详细中文合集资料(免费下载)

    本文的主要内容是工程测试技术的详细中文合集资料包括了:信号分析处理总结,信号分类与特性,周期信号的频域分析,非周期信号频域分析,典型信号频域分析,测试系统特性,典型测试系统,模拟信号处理
    发表于 06-04 09:00 4次下载
    工程测试技术的详细中文<b class='flag-5'>合集资料</b>(免费下载)

    根据51单片机的陀螺仪飞鼠算法的详细合集资料概述免费下载

    本文档的主要内容详细介绍的是陀螺仪飞鼠算法的详细合集资料概述免费下载
    发表于 07-02 08:00 26次下载

    电路分析基础教程合集资料免费下载

    本文档的主要内容详细介绍的是电路分析基础教程合集资料免费下载主要内容包括了:电路的基本概念和基本定律,电阻电路的一般分析方法,电路定理,动态电路,三相电路,电阻电路习题
    发表于 10-29 08:00 0次下载
    电路分析基础教程<b class='flag-5'>合集资料</b>免费下载

    使用51单片机设计的智能小车程序代码合集资料免费下载

    本文档的主要内容详细介绍的是使用51单片机设计的智能小车程序代码合集资料免费下载主要包括了:1、ZYWIFI0939C 跟随单独程序-PWM调速,2、ZYWIFI0939C红外避障单独程序-PWM
    发表于 11-09 16:06 156次下载

    使用STM32F407进行的综合实验程序合集资料免费下载

    本文档的主要内容详细介绍的是使用STM32F407进行的综合实验程序合集资料免费下载。
    发表于 12-28 08:00 113次下载

    运算放大器的电路合集资料免费下载

    本文档的主要内容详细介绍的是运算放大器的放大电路合集资料免费下载
    发表于 03-25 08:00 15次下载
    运算放大器的电路<b class='flag-5'>合集资料</b>免费下载

    51单片机PWM脉冲宽度调制的程序合集资料免费下载

    本文档的主要内容详细介绍的是51单片机PWM脉冲宽度调制的程序合集资料免费下载。
    发表于 03-28 16:21 38次下载
    51单片机PWM脉冲宽度调制的程序<b class='flag-5'>合集资料</b>免费下载

    Logix5000控制器运动控制指令集资料合集免费下载

    本文档的主要内容详细介绍的是Logix5000控制器运动控制指令集资料合集免费下载。
    发表于 09-27 08:00 11次下载

    VB.NET的常用命名空间和类介绍和VB的完美代码资料合集免费

    本文档的主要内容详细介绍的是VB.NET的常用命名空间和类介绍和VB的完美代码资料合集免费。
    发表于 06-09 08:00 8次下载
    VB.NET的<b class='flag-5'>常用</b>命名空间和类介绍和VB的完美<b class='flag-5'>代码</b>库<b class='flag-5'>资料</b><b class='flag-5'>合集</b>免费

    机器学习实战的源代码资料合集

    本文档机器学习实战的源代码资料合集
    发表于 03-01 09:28 51次下载
    机器学习实战的源<b class='flag-5'>代码</b><b class='flag-5'>资料</b><b class='flag-5'>合集</b>

    常用的电气电路图集资料下载

    电子发烧友网为你提供常用的电气电路图集资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文
    发表于 04-01 08:40 17次下载
    <b class='flag-5'>常用</b>的电气电路图<b class='flag-5'>集资料</b>下载

    如何进行编程可以减少程序的bug?单片机技巧合集资料下载

    电子发烧友网为你提供如何进行编程可以减少程序的bug?单片机技巧合集资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文
    发表于 04-02 08:51 9次下载
    如何进行编程可以减少程序的bug?单片机技巧<b class='flag-5'>合集资料</b>下载

    labview图像采集资料合集

    labview图像采集资料合集
    发表于 12-15 14:59 10次下载