pytorch framework learning


Pytorch Learning

Date : 2020/3/20 - 2020/3/27 (Week 5)

这一周,我们学习并分析在机器学习任务中使用 pytorch 必用的 module (torch.nn) 的源代码实现。

./torch/nn 源代码分析

torch/nn目录结构以及init.py

# __init__.py:
from .modules import *
from .parameter import Parameter
from .parallel import DataParallel
from . import init
from . import utils

我们在使用中往往使用的语句:

import torch.nn as nn

执行时,默认导入./torch/nn/modules, ./torch/nn/parameter,./torch/nn/parallel, torch/nn/init.py,torch/nn/utils 这些文件的内容。(递归地,又引入了modules.__init__,parallel.__init__的内容 )

这样,我们不需要其他额外的 import 就可以使用我们常用的 nn.Module 来定义我们的模型了。

对于backends, functional.py, _functions 需要在代码前重新Import。例如我们常用的

import torch.nn.functional as F

就是导入了functional.py, 同样地,backends_functionsfunctional.py 实现各种函数时所用到的。

torch/nn/parameter.py 源代码分析

  • 我们首先比较好分析的入手,torch/nn/modules/module.py 的先决, torch/nn/parameter.py
  • torch/nn/parameter.py 本内容就一个 Parameter 类定义,下分析其代码实现:
class Parameter(torch.Tensor):
    r"""A kind of Tensor that is to be considered a module parameter.

    Parameters are :class:`~torch.Tensor` subclasses, that have a
    very special property when used with :class:`Module` s - when they're
    assigned as Module attributes they are automatically added to the list of
    its parameters, and will appear e.g. in :meth:`~Module.parameters` iterator.
    Assigning a Tensor doesn't have such effect. This is because one might
    want to cache some temporary state, like last hidden state of the RNN, in
    the model. If there was no such class as :class:`Parameter`, these
    temporaries would get registered too.

    Arguments:
        data (Tensor): parameter tensor.
        requires_grad (bool, optional): if the parameter requires gradient. See
            :ref:`excluding-subgraphs` for more details. Default: `True`
    """

    def __new__(cls, data=None, requires_grad=True):
        if data is None:
            data = torch.Tensor()
        return torch.Tensor._make_subclass(cls, data, requires_grad)

    def __deepcopy__(self, memo):
        if id(self) in memo:
            return memo[id(self)]
        else:
            result = type(self)(self.data.clone(memory_format=torch.preserve_format), self.requires_grad)
            memo[id(self)] = result
            return result

    def __repr__(self):
        return 'Parameter containing:\n' + super(Parameter, self).__repr__()

    def __reduce_ex__(self, proto):
        # See Note [Don't serialize hooks]
        return (
            torch._utils._rebuild_parameter,
            (self.data, self.requires_grad, OrderedDict())
        )
  • Parameter 类是 torch.Tensor 类的子类,区别于 torch.Tensor 当其被指定为 Module 属性的时候,他们会自动加入 Module.parameters 迭代器。

    • __new__ : 创建对象(类准备将自身实例化时调用)在__init__ 方法前调用。

    • __deepcopy__ : 一旦复制出来了,就是独立的,原对象的改变不影响deepcopy 的对象值。

    • __repr__ : 方向在 print 时调用,在其中又调用父类的 __repr__ 方法。

    • 实例属性和类属性:Python 是动态语言,可以在 __init__ 动态的添加实例属性。而和编译语言一样,Python 的类属性需要直接定义在类中,举例来说:

      class Car(object):
          country = u'中国'
          def __init__(self,owner=None):
              self.owner = owner
              self.country = "china"
      

      Car 类中含有 country 这一类属性和 ownercountry 两个实例属性。

torch/nn/init.py 源代码分析

这一部分在实际的使用中非常常用,经常用在 module 子类的__init__ 方法中,定义模型参数的初始化方法。常见的初始化方法包括kaiming_normal_, xavier_uniform_,normal_ 等,这些在论文中提出的初始化方法能够起到一定控制 gradient 流的作用,从而加速学习效率,十分重要,下举一例说明平时我们如何使用 torch.nn.init 。该例来自 CS231n assignment2。

class MyConvNet(nn.Module):
    def __init__(self, input_channel, num_class):
        super().__init__()
        self.conv_1 = nn.Conv2d(input_channel, 32, kernel_size=(3, 3), padding=1)
        nn.init.kaiming_normal_(self.conv_1.weight)
        self.bn_1 = nn.BatchNorm2d(32)
        self.pooling_1 = nn.MaxPool2d((2, 2), stride = 2)

        self.conv_2 = nn.Conv2d(32, 32, kernel_size=(3, 3))
        nn.init.kaiming_normal_(self.conv_2.weight)
        self.bn_2 = nn.BatchNorm2d(32)
        self.pooling_2 = nn.MaxPool2d((2, 2), stride = 2)

        self.fc_1 = nn.Linear(32 * 7 * 7, 128)
        nn.init.kaiming_uniform_(self.fc_1.weight)
        self.bn_3 = nn.BatchNorm1d(128)

        self.fc_2 = nn.Linear(128, 10)
        nn.init.kaiming_normal_(self.fc_2.weight)


    def forward(self, x):
        conv_1_out = F.relu(self.bn_1(self.conv_1(x)))
        pool_1_out = self.pooling_1(conv_1_out)
        assert pool_1_out.shape == (x.shape[0], 32, 16, 16) , "pool_1_out shape wrong"
        conv_2_out = F.relu(self.bn_2(self.conv_2(pool_1_out)))
        pool_2_out = self.pooling_2(conv_2_out)
        assert pool_2_out.shape == (x.shape[0], 32, 7, 7) , "pool_2_out shape wrong"
        pool_2_out_flatten = flatten(pool_2_out)
        assert pool_2_out_flatten.shape == (x.shape[0], 32 * 7 * 7), "flatten shape wrong"
        fc_1_out = F.relu(self.bn_3(self.fc_1(pool_2_out_flatten)))
        assert fc_1_out.shape == (x.shape[0], 128)
        scores = self.fc_2(fc_1_out)
        return scores

这一部分主要包括以下几个部分:

  1. no_grad_* funcitons (_no_grad_uniform_, _no_grad_normal_, _no_grad_trunc_normal_, _no_grad_fill_, _no_grad_zero_)

    • _no_grad_uniform_ : 返回被 with torch.no_grad() 环境包裹的tensor.uniform_(a, b), 均匀分布
    • _no_grad_normal_ : 返回被 with torch.no_grad() 环境包裹的 tensor.normal_(mean, std), 正太分布
    • _no_grad_trunc_normal_:返回被 with torch.no_grad() 环境包裹的截断正太分布[a, b] 区间内近似服从正态分布。
    • _no_grad_fill_:返回被 with torch.no_grad() 环境包裹的 tensor.fill_(val) 填充函数
    • _no_grad_zero_:返回被 with torch.no_grad() 环境包裹的 tensor.zero_() 函数
  2. 使用 no_grad_* funcitons 完成的 built-in 函数(uniform_,normal_,trunc_normal_,constant_,ones_, zeros

    • 这五个函数分别对应_no_grad_uniform_, _no_grad_normal_, _no_grad_trunc_normal_, _no_grad_fill_, _no_grad_zero_
  3. 常见的初始化方法 kaiming_normal_, kaiming_uniform_, xavier_normal_, xavier_uniform_, eye_, dirac_, orthogonal_ 等。

    Note: 这里不是按照原顺序排列的方法。而是按照使用的频繁程度排序。

  4. _make_deprecate :对于使用 old_name 的情况给出 warnings :建议使用后面加下划线的方法,将old_name 函数重定向到 new_name 方法。

Summary

  • 今天我们大致上分析了torch.nn 的调用结构,以及torch.nn.parameters,和torch.nn.init 三个部分。明白了在import torch.nn 的时候完成了工作,清晰了module的参数parameter本质也是torch.tensor, 了解了torch.nn.init中常见的初始化方法,了解到如果不使用加下划线的初始化方法带来的warnings原理。
  • 之后会继续分析torch.nn文件中其余部分。

文章作者: Jason Yuan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jason Yuan !
 上一篇
Relation_Extraction One Relation_Extraction One
Global Textual Relation Embedding for Relational UnderstandingCore Idea全局的统计信息比局部的统计信息更加鲁棒。 在传统的关系抽取训练中,句子与句子之间是独立的,基于单句
2020-03-26
下一篇 
快手笔试简记 快手笔试简记
快手2020/3/20 算法类笔试 A4道编程题:感觉都是数学题啊。 除了第二题明显是dp以外其余题目都不按套路来😓,就ac了前两个,下次再来。 最后一个问球被椭圆截得表面积,输入椭球(x-a)^2/d+(y-b)^2/e+(z-c)^2
2020-03-22
  目录