加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_丽江站长网 (http://www.0888zz.com/)- 科技、建站、数据工具、云上网络、机器学习!
当前位置: 首页 > 运营中心 > 建站资源 > 经验 > 正文

PyTorch最佳实践,怎样才能写出一手风格优美的代码

发布时间:2019-05-07 10:55:08 所属栏目:经验 来源:机器之心编译
导读:副标题#e# 虽然这是一个非官方的 PyTorch 指南,但本文总结了一年多使用 PyTorch 框架的经验,尤其是用它开发深度学习相关工作的最优解决方案。请注意,我们分享的经验大多是从研究和实践角度出发的。 这是一个开发的项目,欢迎其它读者改进该文档: https:

即使 PyTorch 已经具有了大量标准损失函数,你有时也可能需要创建自己的损失函数。为了做到这一点,你需要创建一个独立的「losses.py」文件,并且通过扩展「nn.Module」创建你的自定义损失函数:

  1. class CustomLoss(torch.nn.Module): 
  2.  
  3.     def __init__(self): 
  4.         super(CustomLoss,self).__init__() 
  5.  
  6.     def forward(self,x,y): 
  7.         loss = torch.mean((x - y)**2) 
  8.         return loss 

5. 训练模型的最佳代码结构

对于训练的最佳代码结构,我们需要使用以下两种模式:

  • 使用 prefetch_generator 中的 BackgroundGenerator 来加载下一个批量数据
  • 使用 tqdm 监控训练过程,并展示计算效率,这能帮助我们找到数据加载流程中的瓶颈
  1. # import statements 
  2. import torch 
  3. import torch.nn as nn 
  4. from torch.utils import data 
  5. ... 
  6.  
  7. # set flags / seeds 
  8. torch.backends.cudnn.benchmark = True 
  9. np.random.seed(1) 
  10. torch.manual_seed(1) 
  11. torch.cuda.manual_seed(1) 
  12. ... 
  13.  
  14. # Start with main code 
  15. if __name__ == '__main__': 
  16.     # argparse for additional flags for experiment 
  17.     parser = argparse.ArgumentParser(description="Train a network for ...") 
  18.     ... 
  19.     opt = parser.parse_args()  
  20.  
  21.     # add code for datasets (we always use train and validation/ test set) 
  22.     data_transforms = transforms.Compose([ 
  23.         transforms.Resize((opt.img_size, opt.img_size)), 
  24.         transforms.RandomHorizontalFlip(), 
  25.         transforms.ToTensor(), 
  26.         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) 
  27.     ]) 
  28.  
  29.     train_dataset = datasets.ImageFolder( 
  30.         root=os.path.join(opt.path_to_data, "train"), 
  31.         transform=data_transforms) 
  32.     train_data_loader = data.DataLoader(train_dataset, ...) 
  33.  
  34.     test_dataset = datasets.ImageFolder( 
  35.         root=os.path.join(opt.path_to_data, "test"), 
  36.         transform=data_transforms) 
  37.     test_data_loader = data.DataLoader(test_dataset ...) 
  38.     ... 
  39.  
  40.     # instantiate network (which has been imported from *networks.py*) 
  41.     net = MyNetwork(...) 
  42.     ... 
  43.  
  44.     # create losses (criterion in pytorch) 
  45.     criterion_L1 = torch.nn.L1Loss() 
  46.     ... 
  47.  
  48.     # if running on GPU and we want to use cuda move model there 
  49.     use_cuda = torch.cuda.is_available() 
  50.     if use_cuda: 
  51.         netnet = net.cuda() 
  52.         ... 
  53.  
  54.     # create optimizers 
  55.     optim = torch.optim.Adam(net.parameters(), lr=opt.lr) 
  56.     ... 
  57.  
  58.     # load checkpoint if needed/ wanted 
  59.     start_n_iter = 0 
  60.     start_epoch = 0 
  61.     if opt.resume: 
  62.         ckpt = load_checkpoint(opt.path_to_checkpoint) # custom method for loading last checkpoint 
  63.         net.load_state_dict(ckpt['net']) 
  64.         start_epoch = ckpt['epoch'] 
  65.         start_n_iter = ckpt['n_iter'] 
  66.         optim.load_state_dict(ckpt['optim']) 
  67.         print("last checkpoint restored") 
  68.         ... 
  69.  
  70.     # if we want to run experiment on multiple GPUs we move the models there 
  71.     net = torch.nn.DataParallel(net) 
  72.     ... 
  73.  
  74.     # typically we use tensorboardX to keep track of experiments 
  75.     writer = SummaryWriter(...) 
  76.  
  77.     # now we start the main loop 
  78.     n_iter = start_n_iter 
  79.     for epoch in range(start_epoch, opt.epochs): 
  80.         # set models to train mode 
  81.         net.train() 
  82.         ... 
  83.  
  84.         # use prefetch_generator and tqdm for iterating through data 
  85.         pbar = tqdm(enumerate(BackgroundGenerator(train_data_loader, ...)), 
  86.                     total=len(train_data_loader)) 
  87.         start_time = time.time() 
  88.  
  89.         # for loop going through dataset 
  90.         for i, data in pbar: 
  91.             # data preparation 
  92.             img, label = data 
  93.             if use_cuda: 
  94.                 imgimg = img.cuda() 
  95.                 labellabel = label.cuda() 
  96.             ... 
  97.  
  98.             # It's very good practice to keep track of preparation time and computation time using tqdm to find any issues in your dataloader 
  99.             prepare_time = start_time-time.time() 
  100.  
  101.             # forward and backward pass 
  102.             optim.zero_grad() 
  103.             ... 
  104.             loss.backward() 
  105.             optim.step() 
  106.             ... 
  107.  
  108.             # udpate tensorboardX 
  109.             writer.add_scalar(..., n_iter) 
  110.             ... 
  111.  
  112.             # compute computation time and *compute_efficiency* 
  113.             process_time = start_time-time.time()-prepare_time 
  114.             pbar.set_description("Compute efficiency: {:.2f}, epoch: {}/{}:".format( 
  115.                 process_time/(process_time+prepare_time), epoch, opt.epochs)) 
  116.             start_time = time.time() 
  117.  
  118.         # maybe do a test pass every x epochs 
  119.         if epoch % x == x-1: 
  120.             # bring models to evaluation mode 
  121.             net.eval() 
  122.             ... 
  123.             #do some tests 
  124.             pbar = tqdm(enumerate(BackgroundGenerator(test_data_loader, ...)), 
  125.                     total=len(test_data_loader))  
  126.             for i, data in pbar: 
  127.                 ... 
  128.  
  129.             # save checkpoint if needed 
  130.             ... 

三、PyTorch 的多 GPU 训练

PyTorch 中有两种使用多 GPU 进行训练的模式。

根据我们的经验,这两种模式都是有效的。然而,第一种方法得到的结果更好、需要的代码更少。由于第二种方法中的 GPU 间的通信更少,似乎具有轻微的性能优势。

1. 对每个网络输入的 batch 进行切分

最常见的一种做法是直接将所有网络的输入切分为不同的批量数据,并分配给各个 GPU。

这样一来,在 1 个 GPU 上运行批量大小为 64 的模型,在 2 个 GPU 上运行时,每个 batch 的大小就变成了 32。这个过程可以使用「nn.DataParallel(model)」包装器自动完成。

2. 将所有网络打包到一个超级网络中,并对输入 batch 进行切分

这种模式不太常用。下面的代码仓库向大家展示了 Nvidia 实现的 pix2pixHD,它有这种方法的实现。

地址:https://github.com/NVIDIA/pix2pixHD

四、PyTorch 中该做和不该做的

1. 在「nn.Module」的「forward」方法中避免使用 Numpy 代码

Numpy 是在 CPU 上运行的,它比 torch 的代码运行得要慢一些。由于 torch 的开发思路与 numpy 相似,所以大多数 Numpy 中的函数已经在 PyTorch 中得到了支持。

2. 将「DataLoader」从主程序的代码中分离

载入数据的工作流程应该独立于你的主训练程序代码。PyTorch 使用「background」进程更加高效地载入数据,而不会干扰到主训练进程。

3. 不要在每一步中都记录结果

通常而言,我们要训练我们的模型好几千步。因此,为了减小计算开销,每隔 n 步对损失和其它的计算结果进行记录就足够了。尤其是,在训练过程中将中间结果保存成图像,这种开销是非常大的。

4. 使用命令行参数

(编辑:应用网_丽江站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读