Pytorch学习-01

本次主要学习了三部分

  1. nn.Module部分,通过看知乎上一个源码分析博客习得
  2. pytorch梯度机制,这个主要是看一些博客然后进行总结的
  3. Visdom,Facebook的配合pytorch的可视化工具

nn.Module

方法

  • 自定义时需要实现的方法

    • __init__
    • forward:前向传播计算,返回类型无限制
  • 转换相关

    • to():in-place改Module类型(device、dtype、non_blocking)

      • non_blocking

      • device

      • dtype

    • device转换相关

      • cuda(device=None):Moves all model parameters and buffers to the GPU
      • cpu():Moves all model parameters and buffers to the CPU
    • 类型转换相关:都是进行类型转换的函数

      • type:指定类型转换
      • float
      • double
      • half
  • apply与_apply

    • _apply函数调用children()方法循环遍历一遍全部的子模型,都执行一次传入的函数;然后遍历一遍_parameters字典,也都执行一次传入的函数操作(会自行判断是否需要in-place);对parameters.grad和buffer也执行同样的操作;这个函数一般是用来内部调用的,外部使用apply
    • apply:递归执行传入的函数,常用来进行初始化
  • share_memory:将Module中的Tensor放到共享内存空间中(参数固定)

  • 模型的保存与加载

    • state_dict():拿到parameter和buffer组成的字典
    • load_state_dict()
    • _save_to_state_dict()
    • _load_from_state_dict()
  • 获取内部属性相关方法

    • parameters & named_parameters
    • buffer & named_buffer
    • children & named_children
    • modules & named_modules
  • 训练相关:分别设置为训练模式和评估模式,只对特定的Module有影响:dropout、batchnorm

    • train()
    • eval()
  • 梯度相关

    • requires_grad_():用于设置self.parameters()是否需要record梯度,默认情况下是True

    • requires_grad:是Tensor一个属性,用于说明当前量是否需要在计算中保留对应的梯度信息,若置为False则无法进行反向传播,常用来冻结部分参数

      class RESNET_MF(nn.Module):
          def __init__(self, model, pretrained):
              super(RESNET_MF, self).__init__()
              self.resnet = model(pretrained)
              for p in self.parameters():
                  p.requires_grad = False   #预训练模型加载进来后全部设置为不更新参数,然后再后面加层
              self.f = SpectralNorm(nn.Conv2d(2048, 512, 1))
              self.g = SpectralNorm(nn.Conv2d(2048, 512, 1))
              self.h = SpectralNorm(nn.Conv2d(2048, 2048, 1))
      
    • zero_grad():用于设置self.parameters()gradients为零

  • class属性相关

    常见的属性有_parameters_modules _buffers 还有若干hooks

    • _get_name:返回类名
    • extra_repr:输出模块信息、需要自己实现
    • __repr__:输出子模块的信息
    • __dir__:输出属性的信息或者keys
  • 运算调用细节相关

    • _slow_forward

      封装forward方法

    • __call__

      调用_slow_forward,另外还执行了hook的运算

      def __call__(self, *input, **kwargs):
          for hook in self._forward_pre_hooks.values(): # _forward_pre_hooks
              result = hook(self, input)
              if result is not None:
                  if not isinstance(result, tuple):
                      result = (result,)
                  input = result
          if torch._C._get_tracing_state():  # _slow_forward -> forward
              result = self._slow_forward(*input, **kwargs)
          else:
              result = self.forward(*input, **kwargs)
          for hook in self._forward_hooks.values(): # _forward_hooks
              hook_result = hook(self, input, result)
              if hook_result is not None:
                  result = hook_result
          if len(self._backward_hooks) > 0: # _backward_hooks
              var = result
              while not isinstance(var, torch.Tensor):
                  if isinstance(var, dict):
                      var = next((v for v in var.values() if isinstance(v, torch.Tensor)))
                  else:
                      var = var[0]
              grad_fn = var.grad_fn
              if grad_fn is not None:
                  for hook in self._backward_hooks.values():
                      wrapper = functools.partial(hook, self)
                      functools.update_wrapper(wrapper, hook)
                      grad_fn.register_hook(wrapper) # variable hook
          return result
      
  • 注册相关

    • register_parameter:加入一个新参数到module中
    • register_buffer:有些非网络的参数如batchnorm的mean和var
    • add_module
  • 钩子函数相关

    通常配合isinstance(module, nn.ReLU)model.named_children()在特定位置加入hook

    • register_backward_hook
    • register_forward_pre_hook
    • register_forward_hook
    • _register_state_dict_hook
    • _register_load_state_dict_pre_hook

Pytorch梯度记录机制

torch.Tensor中有一个requires_grad属性,默认为False,决定是否反向传播计算梯度(1.若置为False其之前的都无法计算梯度;2.置为True的之后的中间变量默认都是True)

nn.Module中的参数默认是自动计算梯度的,Module中用requires_grad_方法递归改Tensor的requires_grad属性

一个例子是冻结预训练模型参数

class RESNET_MF(nn.Module):
    def __init__(self, model, pretrained):
        super(RESNET_MF, self).__init__()
        self.resnet = model(pretrained)
        for p in self.parameters():
            p.requires_grad = False   #预训练模型加载进来后全部设置为不更新参数,然后再后面加层
        self.f = SpectralNorm(nn.Conv2d(2048, 512, 1))
        self.g = SpectralNorm(nn.Conv2d(2048, 512, 1))
        self.h = SpectralNorm(nn.Conv2d(2048, 2048, 1))
  • torch.no_grad(): 将下文Tensor、Module的requires_grad都置为空,常用于冻结权值、eval

    with torch.no_grad():
        ...
    
  • optimizer.zero_grad() & model.zero_grad()

    self.parameters()gradients置零,因为在计算图反向传播过程中必须用+=而不是赋值来算梯度

  • torch.Tensor.backward():tensor执行反向传播

    • retain_graph参数

      If False, the graph used to compute the grad will be freed. Note that in nearly all cases setting this option to True is not needed and often can be worked around in a much more efficient way. Defaults to the value of create_graph.

  • optimizer.step() : 参数更新

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

    Performs a single optimization step (parameter update).

  • torch.detach()

    返回一个新的Variable,从当前计算图中分离下来的,但是仍指向原变量的存放位置,不同之处只是requires_grad为false,得到的这个Variable永远不需要计算其梯度,不具有grad。

Visdom

visdom是FB的,所以对pytorch会更友好一些;并且可以通过visdom显示matplotlib的图片

启动

python3 -m visdom.server

文字

vis.text('Hello World', win='text1', append=True)

绘制单条折线图

win:某个窗格id

env:某个大标签栏id 默认为main

vis.line(y, x, win, env, opts={title:"title_name"})

绘制多条折线图

绘制图片

好处是不用转化成numpy数据就可以传到visdom里面

Reference