快捷搜索: 王者荣耀 脱发

[Pytorch进阶技巧(零)] Pytorch中计算图的建立与运行

前言

如今Pytorch的使用率正在超过tensorflow。虽然pytorch的开发效率比tensorflow高很多,但是使用tensorflow可以对计算图的概率更加清晰。关于tensorflow可以参考我的博文。

不清楚pytorch背后的计算图概念,在进行pytorch编程时可能会遇到:

  1. 随着迭代显存占用不断增大,直至报错;
  2. 报错参数不在同个设备,比如报错cpu里的浮点数无法和cuda:0里的浮点数进行运算;
  3. 自动求导时,程序没有按照原本的设计进行导数更新。
  4. 进行NAS实验时,在多卡训练很多epoch后,想根据训练结果改变网络结构再继续训练咋办?

本文涉及的pytorch API包括: torch.nn torch.register_buffer torch.register_parameter torch.no_grad torch.nn.Module.cuda torch.nn.Module.cpu torch.nn.DataParallel

本文将依次回答如下问题:

  1. pytorch的计算图是怎么建立的;
  2. 如何控制自动求导;
  3. cpu,gpu,多卡分别是如何运行模型。

动态计算图

任何一个深度学习框架都无法离开计算图这个概念,主要的原因就是框架需要将运算过程并行化。如果无法定义运算过程相对固定的计算图,框架也就无法决定如何并行,从而也就无法实现GPU加速的目的。

都在说pytorch是动图,tensorflow是静图。因为Tensorflow的计算图基本在开启Session前已经写死了,也就是说在真正进行计算时,有哪些变量、这些变量怎么运算,已经确定了,所以称为静图。

那么pytorch"动"在哪呢。pytorch在进行计算前,基本确定下来的是有哪些变量参与运算,但是这些变量是如何运算的并不确定。从代码层面看,tensorflow中,变量的运算(加减乘除卷积啥的)在Session run之前就写好了,而且需要写一遍。而pytorch中,每次计算时,都需要调用Module的forward,其实就是又写了一遍变量的运算。

网络模型搭建

不得不佩服pytorch框架设计者,通过torch.nn.Module可以很好地组织网络。与从最小元素阐述的方式不一样,本文直接描述pytorch是如何搭建一个网络模型。

  1. 一般而言,一个pytorch模型是个torch.nn.Module的子类,比如ResNet(torch.nn.Module)。使用的时候需要实例化。查看源码可以发现,torch.nn.Conv2d(torch.nn.Module)其实也是torch.nn.Module的子类。
# 经常看代码的你一定知道,一般这么用是吧
model = ResNet()
pre = model(img)
  1. 继承torch.nn.Module,要求在__init__()里定义变量,在forward()里定义前向传播过程。
  2. (太精髓了)在__init__()里可以实例化其它torch.nn.Module的子类。此时被实列化的Module会调用__init__()初始化其中的参数或者Module。
  1. (啧啧啧)在forward()里定义前向传播,可以调用被实例化的

自动求导

最"大"的torch.nn.Module树遍历到每一个子module的forward函数,就可以确定前向传播的路径。那么沿着这个计算路径反向传播,就可以完成自动求导。当某些前向传播处不希望被纳入求导范围内是,只需用with torch.no_grad():包装即可。

cpu与gpu

在cpu里的model可以进行参数增减与前向传播修改,而放入了cuda与多卡的model不可进行参数的增减。因此,在修改模型前,需要model.cpu()后修改,在通过model.cuda()放入gpu内训练。

经验分享 程序员 微信小程序 职场和发展