0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

PyTorch常用代碼段合集資料分享

新機(jī)器視覺(jué) ? 來(lái)源:小白學(xué)視覺(jué) ? 2023-12-18 11:27 ? 次閱讀

本文是PyTorch常用代碼段合集,涵蓋基本配置、張量處理、模型定義與操作、數(shù)據(jù)處理、模型訓(xùn)練與測(cè)試等5個(gè)方面,還給出了多個(gè)值得注意的Tips,內(nèi)容非常全面。

PyTorch最好的資料是官方文檔。本文是PyTorch常用代碼段,在參考資料[1](張皓:PyTorch Cookbook)的基礎(chǔ)上做了一些修補(bǔ),方便使用時(shí)查閱。

基本配置

導(dǎo)入包和版本查詢

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

可復(fù)現(xiàn)性

硬件設(shè)備(CPU、GPU)不同時(shí),完全的可復(fù)現(xiàn)性無(wú)法保證,即使隨機(jī)種子相同。但是,在同一個(gè)設(shè)備上,應(yīng)該保證可復(fù)現(xiàn)性。具體做法是,在程序開始的時(shí)候固定torch的隨機(jī)種子,同時(shí)也把numpy的隨機(jī)種子固定。

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

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

顯卡設(shè)置

如果只需要一張顯卡

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

如果需要指定多張顯卡,比如0,1號(hào)顯卡。

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

也可以在命令行運(yùn)行代碼時(shí)設(shè)置顯卡:

CUDA_VISIBLE_DEVICES=0,1pythontrain.py

清除顯存

torch.cuda.empty_cache()

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

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

張量(Tensor)處理

張量的數(shù)據(jù)類型

PyTorch有9種CPU張量類型和9種GPU張量類型。

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

張量基本信息

tensor=torch.randn(3,4,5)print(tensor.type())#數(shù)據(jù)類型print(tensor.size())#張量的shape,是個(gè)元組print(tensor.dim())#維度的數(shù)量

命名張量

張量命名是一個(gè)非常有用的方法,這樣可以方便地使用維度的名字來(lái)做索引或其他操作,大大提高了可讀性、易用性,防止出錯(cuò)。

#在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)
#也可以這么設(shè)置
tensor=torch.rand(3,4,1,2,names=('C','N','H','W'))
#使用align_to可以對(duì)維度方便地排序
tensor=tensor.align_to('N','C','H','W')

數(shù)據(jù)類型轉(zhuǎn)換

#設(shè)置默認(rèn)類型,pytorch中的FloatTensor遠(yuǎn)遠(yuǎn)快于DoubleTensor
torch.set_default_tensor_type(torch.FloatTensor)

#類型轉(zhuǎn)換
tensor=tensor.cuda()
tensor=tensor.cpu()
tensor=tensor.float()
tensor=tensor.long()

torch.Tensor與np.ndarray轉(zhuǎn)換

除了CharTensor,其他所有CPU上的張量都支持轉(zhuǎn)換為numpy格式然后再轉(zhuǎn)換回來(lái)。

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

Torch.tensor與PIL.Image轉(zhuǎn)換

#pytorch中的張量默認(rèn)采用[N,C,H,W]的順序,并且數(shù)據(jù)范圍在[0,1],需要進(jìn)行轉(zhuǎn)置和規(guī)范化
#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的轉(zhuǎn)換

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

從只包含一個(gè)元素的張量中提取值

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

張量形變

#在將卷積層輸入全連接層的情況下通常需要對(duì)張量做形變處理,
#相比torch.view,torch.reshape可以自動(dòng)處理輸入張量不連續(xù)的情況

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

打亂順序

tensor=tensor[torch.randperm(tensor.size(0))]#打亂第一個(gè)維度

水平翻轉(zhuǎn)

#pytorch不支持tensor[::-1]這樣的負(fù)步長(zhǎng)操作,水平翻轉(zhuǎn)可以通過(guò)張量索引實(shí)現(xiàn)
#假設(shè)張量的維度為[N,D,H,W].

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

復(fù)制張量

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

張量拼接

'''
注意torch.cat和torch.stack的區(qū)別在于torch.cat沿著給定的維度拼接,
而torch.stack會(huì)新增一維。例如當(dāng)參數(shù)是3個(gè)10x5的張量,torch.cat的結(jié)果是30x5的張量,
而torch.stack的結(jié)果是3x10x5的張量。
'''
tensor=torch.cat(list_of_tensors,dim=0)
tensor=torch.stack(list_of_tensors,dim=0)

將整數(shù)標(biāo)簽轉(zhuǎn)為one-hot編碼

#pytorch的標(biāo)記默認(rèn)從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

判斷兩個(gè)張量相等

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

張量擴(kuò)展

#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

計(jì)算兩組數(shù)據(jù)之間的兩兩歐式距離

利用廣播機(jī)制

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

模型定義和操作

一個(gè)簡(jiǎn)單兩層卷積網(wǎng)絡(luò)的示例

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

卷積層的計(jì)算和展示可以用這個(gè)網(wǎng)站輔助。

雙線性匯合(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)

當(dāng)使用 torch.nn.DataParallel 將代碼運(yùn)行在多張 GPU 卡上時(shí),PyTorch 的 BN 層默認(rèn)操作是各卡上數(shù)據(jù)獨(dú)立地計(jì)算均值和標(biāo)準(zhǔn)差,同步 BN 使用所有卡上的數(shù)據(jù)一起計(jì)算 BN 層的均值和標(biāo)準(zhǔn)差,緩解了當(dāng)批量大?。╞atch size)比較小時(shí)對(duì)均值和標(biāo)準(zhǔn)差估計(jì)不準(zhǔn)的情況,是在目標(biāo)檢測(cè)等任務(wù)中一個(gè)有效的提升性能的技巧。

sync_bn=torch.nn.SyncBatchNorm(num_features,

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

將已有網(wǎng)絡(luò)的所有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 滑動(dòng)平均

如果要實(shí)現(xiàn)類似 BN 滑動(dòng)平均的操作,在 forward 函數(shù)中要使用原地(inplace)操作給滑動(dòng)平均賦值。

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)

計(jì)算模型整體參數(shù)量

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

查看網(wǎng)絡(luò)中的參數(shù)

可以通過(guò)model.state_dict()或者model.named_parameters()函數(shù)查看現(xiàn)在的全部可訓(xùn)練參數(shù)(包括通過(guò)繼承得到的父類中的參數(shù))

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

模型權(quán)重初始化

注意 model.modules() 和 model.children() 的區(qū)別:model.modules() 會(huì)迭代地遍歷模型的所有子層,而 model.children() 只會(huì)遍歷模型下的一層。

#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()會(huì)返回模型中所有模塊的迭代器,它能夠訪問(wèn)到最內(nèi)層,比如self.layer1.conv1這個(gè)模塊,還有一個(gè)與它們相對(duì)應(yīng)的是name_children()屬性以及named_modules(),這兩個(gè)不僅會(huì)返回模塊的迭代器,還會(huì)返回網(wǎng)絡(luò)層的名字。

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

部分層使用預(yù)訓(xùn)練模型

注意如果保存的模型是 torch.nn.DataParallel,則當(dāng)前的模型也需要是

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

將在 GPU 保存的模型加載到 CPU

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

導(dǎo)入另一個(gè)模型的相同部分到新的模型

模型導(dǎo)入?yún)?shù)時(shí),如果兩個(gè)模型結(jié)構(gòu)不一致,則直接導(dǎo)入?yún)?shù)會(huì)報(bào)錯(cuò)。用下面方法可以把另一個(gè)模型的相同的部分導(dǎo)入到新的模型中。

#model_new代表新的模型
#model_saved代表其他模型,比如用torch.load導(dǎo)入的已保存的模型
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)

數(shù)據(jù)處理

計(jì)算數(shù)據(jù)集的均值和標(biāo)準(zhǔn)差

importos
importcv2
importnumpyasnp
fromtorch.utils.dataimportDataset
fromPILimportImage


defcompute_mean_and_std(dataset):
#輸入PyTorch的dataset,輸出均值和標(biāo)準(zhǔn)差
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

得到視頻數(shù)據(jù)基本信息

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

常用訓(xùn)練和驗(yàn)證數(shù)據(jù)預(yù)處理

其中 ToTensor 操作會(huì)將 PIL.Image 或形狀為 H×W×D,數(shù)值范圍為 [0, 255] 的 np.ndarray 轉(zhuǎn)換為形狀為 D×H×W,數(shù)值范圍為 [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)),
])

模型訓(xùn)練和測(cè)試

分類模型訓(xùn)練代碼

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

分類模型測(cè)試代碼

#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

標(biāo)簽平滑(label smoothing)

寫一個(gè)label_smoothing.py的文件,然后在訓(xùn)練代碼里引用,用LSR代替交叉熵?fù)p失即可。label_smoothing.py內(nèi)容如下:

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

或者直接在訓(xùn)練文件里做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訓(xùn)練

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

不對(duì)偏置項(xiàng)進(jìn)行權(quán)重衰減(weight decay)

pytorch里的weight decay相當(dāng)于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)

得到當(dāng)前學(xué)習(xí)率

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

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

另一種方法,在一個(gè)batch訓(xùn)練代碼里,當(dāng)前的lr是optimizer.param_groups[0]['lr']

學(xué)習(xí)率衰減

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

優(yōu)化器鏈?zhǔn)礁?/p>

從1.4版本開始,torch.optim.lr_scheduler 支持鏈?zhǔn)礁拢╟haining),即用戶可以定義兩個(gè) schedulers,并交替在訓(xùn)練中使用。

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

模型訓(xùn)練可視化

PyTorch可以使用tensorboard來(lái)可視化訓(xùn)練過(guò)程。

安裝和運(yùn)行TensorBoard。

pipinstalltensorboard
tensorboard--logdir=runs

使用SummaryWriter類來(lái)收集和可視化相應(yīng)的數(shù)據(jù),放了方便查看,可以使用不同的文件夾,比如'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)

保存與加載斷點(diǎn)

注意為了能夠恢復(fù)訓(xùn)練,我們需要同時(shí)保存模型和優(yōu)化器的狀態(tài),以及當(dāng)前的訓(xùn)練輪數(shù)。

start_epoch=0
#Loadcheckpoint.
ifresume:#resume為參數(shù),第一次訓(xùn)練時(shí)設(shè)為0,中斷再訓(xùn)練時(shí)設(shè)為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 預(yù)訓(xùn)練模型某層的卷積特征

#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 預(yù)訓(xùn)練模型多層的卷積特征

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

微調(diào)全連接層

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)

以較大學(xué)習(xí)率微調(diào)全連接層,較小學(xué)習(xí)率微調(diào)卷積層

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)

其他注意事項(xiàng)

不要使用太大的線性層。因?yàn)閚n.Linear(m,n)使用的是的內(nèi)存,線性層太大很容易超出現(xiàn)有顯存。

不要在太長(zhǎng)的序列上使用RNN。因?yàn)镽NN反向傳播使用的是BPTT算法,其需要的內(nèi)存和輸入序列的長(zhǎng)度呈線性關(guān)系。

model(x) 前用 model.train() 和 model.eval() 切換網(wǎng)絡(luò)狀態(tài)。

不需要計(jì)算梯度的代碼塊用 with torch.no_grad() 包含起來(lái)。

model.eval() 和 torch.no_grad() 的區(qū)別在于,model.eval() 是將網(wǎng)絡(luò)切換為測(cè)試狀態(tài),例如 BN 和dropout在訓(xùn)練和測(cè)試階段使用不同的計(jì)算方法。torch.no_grad() 是關(guān)閉 PyTorch 張量的自動(dòng)求導(dǎo)機(jī)制,以減少存儲(chǔ)使用和加速計(jì)算,得到的結(jié)果無(wú)法進(jìn)行 loss.backward()。

model.zero_grad()會(huì)把整個(gè)模型的參數(shù)的梯度都?xì)w零, 而optimizer.zero_grad()只會(huì)把傳入其中的參數(shù)的梯度歸零.

torch.nn.CrossEntropyLoss 的輸入不需要經(jīng)過(guò) Softmax。torch.nn.CrossEntropyLoss 等價(jià)于 torch.nn.functional.log_softmax + torch.nn.NLLLoss。

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

torch.utils.data.DataLoader 中盡量設(shè)置 pin_memory=True,對(duì)特別小的數(shù)據(jù)集如 MNIST 設(shè)置 pin_memory=False 反而更快一些。num_workers 的設(shè)置需要在實(shí)驗(yàn)中找到最快的取值。

用 del 及時(shí)刪除不用的中間變量,節(jié)約 GPU 存儲(chǔ)。使用 inplace 操作可節(jié)約 GPU 存儲(chǔ),如:

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

減少 CPU 和 GPU 之間的數(shù)據(jù)傳輸。例如如果你想知道一個(gè) epoch 中每個(gè) mini-batch 的 loss 和準(zhǔn)確率,先將它們累積在 GPU 中等一個(gè) epoch 結(jié)束之后一起傳輸回 CPU 會(huì)比每個(gè) mini-batch 都進(jìn)行一次 GPU 到 CPU 的傳輸更快。

使用半精度浮點(diǎn)數(shù) half() 會(huì)有一定的速度提升,具體效率依賴于 GPU 型號(hào)。需要小心數(shù)值精度過(guò)低帶來(lái)的穩(wěn)定性問(wèn)題。

時(shí)常使用 assert tensor.size() == (N, D, H, W) 作為調(diào)試手段,確保張量維度和你設(shè)想中一致。

除了標(biāo)記 y 外,盡量少使用一維張量,使用 n*1 的二維張量代替,可以避免一些意想不到的一維張量計(jì)算結(jié)果。

統(tǒng)計(jì)代碼各部分耗時(shí):

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

...print(profile)#或者在命令行運(yùn)行python-mtorch.utils.bottleneckmain.py

使用TorchSnooper來(lái)調(diào)試PyTorch代碼,程序在執(zhí)行的時(shí)候,就會(huì)自動(dòng) print 出來(lái)每一行的執(zhí)行結(jié)果的 tensor 的形狀、數(shù)據(jù)類型、設(shè)備、是否需要梯度的信息。

#pipinstalltorchsnooper
importtorchsnooper#對(duì)于函數(shù),使用修飾器@torchsnooper.snoop()

#如果不是函數(shù),使用 with 語(yǔ)句來(lái)激活 TorchSnooper,把訓(xùn)練的那個(gè)循環(huán)裝進(jìn) with 語(yǔ)句中去。
withtorchsnooper.snoop():
原本的代碼

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

模型可解釋性,使用captum庫(kù):https://captum.ai/captum.ai

審核編輯:黃飛

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10805

    瀏覽量

    210850
  • gpu
    gpu
    +關(guān)注

    關(guān)注

    28

    文章

    4673

    瀏覽量

    128594
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4284

    瀏覽量

    62325
  • pytorch
    +關(guān)注

    關(guān)注

    2

    文章

    802

    瀏覽量

    13115

原文標(biāo)題:PyTorch高頻代碼段集錦!

文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Verilog HDL詳細(xì)資料合集!

    合集資料包括:1.Verilog HDL程序設(shè)計(jì)實(shí)例詳解2.Verilog HDL經(jīng)典教程3.Verilog HDL實(shí)驗(yàn)練習(xí)與語(yǔ)法手冊(cè)4.Verilog HDL硬件描述語(yǔ)言
    發(fā)表于 08-21 10:06

    智能家居合集資料

    基于單片機(jī)的智能家居控制系統(tǒng)、智能家居控制系統(tǒng)畢業(yè)論文(包含PCB和程序)、基于SPCE061A的智能家居系統(tǒng)的設(shè)計(jì)、智能家電控制系統(tǒng)畢業(yè)設(shè)計(jì),智能家居合集資料,從事開發(fā)工作可以參考一下。
    發(fā)表于 12-03 16:38 ?80次下載

    工程測(cè)試技術(shù)的詳細(xì)中文合集資料(免費(fèi)下載)

    本文的主要內(nèi)容是工程測(cè)試技術(shù)的詳細(xì)中文合集資料包括了:信號(hào)分析處理總結(jié),信號(hào)分類與特性,周期信號(hào)的頻域分析,非周期信號(hào)頻域分析,典型信號(hào)頻域分析,測(cè)試系統(tǒng)特性,典型測(cè)試系統(tǒng),模擬信號(hào)處理
    發(fā)表于 06-04 09:00 ?4次下載
    工程測(cè)試技術(shù)的詳細(xì)中文<b class='flag-5'>合集資料</b>(免費(fèi)下載)

    根據(jù)51單片機(jī)的陀螺儀飛鼠算法的詳細(xì)合集資料概述免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是陀螺儀飛鼠算法的詳細(xì)合集資料概述免費(fèi)下載
    發(fā)表于 07-02 08:00 ?26次下載

    電路分析基礎(chǔ)教程合集資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是電路分析基礎(chǔ)教程合集資料免費(fèi)下載主要內(nèi)容包括了:電路的基本概念和基本定律,電阻電路的一般分析方法,電路定理,動(dòng)態(tài)電路,三相電路,電阻電路習(xí)題
    發(fā)表于 10-29 08:00 ?0次下載
    電路分析基礎(chǔ)教程<b class='flag-5'>合集資料</b>免費(fèi)下載

    使用51單片機(jī)設(shè)計(jì)的智能小車程序代碼合集資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用51單片機(jī)設(shè)計(jì)的智能小車程序代碼合集資料免費(fèi)下載主要包括了:1、ZYWIFI0939C 跟隨單獨(dú)程序-PWM調(diào)速,2、ZYWIFI0939C紅外避障單獨(dú)程序-PWM
    發(fā)表于 11-09 16:06 ?156次下載

    使用STM32F407進(jìn)行的綜合實(shí)驗(yàn)程序合集資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用STM32F407進(jìn)行的綜合實(shí)驗(yàn)程序合集資料免費(fèi)下載。
    發(fā)表于 12-28 08:00 ?121次下載

    運(yùn)算放大器的電路合集資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是運(yùn)算放大器的放大電路合集資料免費(fèi)下載
    發(fā)表于 03-25 08:00 ?15次下載
    運(yùn)算放大器的電路<b class='flag-5'>合集資料</b>免費(fèi)下載

    51單片機(jī)PWM脈沖寬度調(diào)制的程序合集資料免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是51單片機(jī)PWM脈沖寬度調(diào)制的程序合集資料免費(fèi)下載。
    發(fā)表于 03-28 16:21 ?38次下載
    51單片機(jī)PWM脈沖寬度調(diào)制的程序<b class='flag-5'>合集資料</b>免費(fèi)下載

    Logix5000控制器運(yùn)動(dòng)控制指令集資料合集免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是Logix5000控制器運(yùn)動(dòng)控制指令集資料合集免費(fèi)下載。
    發(fā)表于 09-27 08:00 ?11次下載

    VB.NET的常用命名空間和類介紹和VB的完美代碼庫(kù)資料合集免費(fèi)

    本文檔的主要內(nèi)容詳細(xì)介紹的是VB.NET的常用命名空間和類介紹和VB的完美代碼庫(kù)資料合集免費(fèi)。
    發(fā)表于 06-09 08:00 ?8次下載
    VB.NET的<b class='flag-5'>常用</b>命名空間和類介紹和VB的完美<b class='flag-5'>代碼</b>庫(kù)<b class='flag-5'>資料</b><b class='flag-5'>合集</b>免費(fèi)

    機(jī)器學(xué)習(xí)實(shí)戰(zhàn)的源代碼資料合集

    本文檔機(jī)器學(xué)習(xí)實(shí)戰(zhàn)的源代碼資料合集
    發(fā)表于 03-01 09:28 ?51次下載
    機(jī)器學(xué)習(xí)實(shí)戰(zhàn)的源<b class='flag-5'>代碼</b><b class='flag-5'>資料</b><b class='flag-5'>合集</b>

    常用的電氣電路圖集資料下載

    電子發(fā)燒友網(wǎng)為你提供常用的電氣電路圖集資料下載的電子資料下載,更有其他相關(guān)的電路圖、源代碼、課件教程、中文資料、英文
    發(fā)表于 04-01 08:40 ?17次下載
    <b class='flag-5'>常用</b>的電氣電路圖<b class='flag-5'>集資料</b>下載

    如何進(jìn)行編程可以減少程序的bug?單片機(jī)技巧合集資料下載

    電子發(fā)燒友網(wǎng)為你提供如何進(jìn)行編程可以減少程序的bug?單片機(jī)技巧合集資料下載的電子資料下載,更有其他相關(guān)的電路圖、源代碼、課件教程、中文資料、英文
    發(fā)表于 04-02 08:51 ?9次下載
    如何進(jìn)行編程可以減少程序的bug?單片機(jī)技巧<b class='flag-5'>合集資料</b>下載

    labview圖像采集資料合集

    labview圖像采集資料合集
    發(fā)表于 12-15 14:59 ?14次下載