pytorch 从入门到精通

深度学习框架训练模型时的代码主要包含数据读取、网络构建和参数设置三方面

pytoch中Tensor和Variable有什么区别?

Sequential和ModuleList有什么区别?直接调用model(input)和调用model.forword(input)有什么区别?

DataSet要实现哪几个函数?

二元分类为什么不能用MSE做为损失函数?

怎么获取网络的计算量和模型大小?有哪几种模型加载和保存方式?pytorch-summary, flops-counter.pytorch

有哪些提高pytorch 训练速度的trick? 预处理加速albumentations

PyTorch 深度学习:60分钟快速入门 ImageNet training in PyTorch, 训练一个图像分类模型

PyTorch源码解读之torch.utils.data.DataLoader、torchvision.transforms、torchvision.models,

PyTorch实战指南、trick 集锦

详解Pytorch中的网络构造、resnet50pytorch, Autograd

PyTorch分布式训练简明教程: horovod

部署PyTorch模型到终端

pytorch2caffe

首推PytorchToCaffe, 其支持 0.3-1.*版本的转换,注意1.1有些BUG暂不支持;还有就是不支持双线性插值的上采样层,这个在分割模型里用的比较多;还有就是torchvison的版本一定得是0.2,不然自带的alexnet模型转换报错,使用说明见pytorch模型转caffe

原理呢其实很简单,pytorch的model.state_dict().items()里保存了所有层的信息,最朴素的方法就是将其逐个提取出来,再转换为对应的caffe层,pytorch转caffe步骤就是这么弄的,很明显工作量很大,那有没有更取巧的方法呢?当然有,pyTorch-To-Caffe利用了python的trace机制,在回调函数中捕获网络所调用的原子操作,然后将对应的操作使用caffe的python接口进行映射,通过frame.f_code.co_name和frmae.f_locals可以获得网络传递过程中的函数名和参数,但可惜的是完成度不高,作者也没给出源码

f_code: The code object being executed in this frame
    co_name: Function name
    co_varnames: A tuple containing the names of the local variables
f_locals: The dictionary used to look up local variables
f_back: The previous stack frame

pytorch里权重保存也是[out_channels,in_channel,h,w]的形式,和caffe的是一致的,拿到data直接赋值就成.

难道就没有可用的方法了吗?所谓山重水复疑无路,柳暗花明又一村. PytorchToCaffe作为目前完成度最高的一份代码,给我们提供了很好的指南,只是刚拿到这份代码时感到一头雾水,不知道到底是怎么做到的.

模块初始化时会创建Rp类的对象,并用这个对象覆盖pytorch中的层实现,例如卷积层的实现F.conv2d=Rp(F.conv2d,_conv2d)

在工具使用中会调用pytorch网络的forward()方法,此时在调用到F.conv2d层是就会调用刚才覆盖的Rp(F.conv2d,_conv2d)这个对象中的__call__方法,并在此方法中调用_conv2d

_conv2d是工具内部定义的方法,作用是计算pytorch中的conv,将该层的名字以及计算得到的blob加入到之前创建的Translog中,并创建caffe中的conv实现,将pytorch中的相关权重写入caffe层中

恍然大悟,其实就是用自己定义的函数替换pytorch内置的计算,顺便把参数保存下来,不得不说真是高明呀.

反向操作的话见把Caffe的模型转换为Pytorch模型

ResNet模块

如下的左图对应于resnet-18/34使用的基本块,右图是50/101/152所使用的,由于他们都比较深,所以右图相比于左图使用了1x1卷积来降维。

图片描述

  • (a) conv3x3: 没啥好解释的,将原有的pytorch函数固定卷积和尺寸为3重新封装了一次;
  • (b) BasicBlock: 搭建上图左边的模块。

    (1) 每个卷积块后面连接BN层进行归一化;

    (2) 残差连接前的3x3卷积之后只接入BN,不使用ReLU,避免加和之后的特征皆为正,保持特征的多样;

    (3) 跳层连接:两种情况,当模块输入和残差支路(3x3->3x3)的通道数一致时,直接相加;当两者通道不一致时(一般发生在分辨率降低之后,同分辨率一般通道数一致),需要对模块输入特征使用1x1卷积进行升/降维(步长为2,上面说了分辨率会降低),之后同样接BN,不用ReLU。

  • (c)  Bottleneck: 搭建上图右边的模块。

    (1) 使用1x1卷积先降维,再使用3x3卷积进行特征提取,最后再使用1x1卷积把维度升回去;

    (2) 每个卷积块后面连接BN层进行归一化;

    (2) 残差连接前的1x1卷积之后只接入BN,不使用ReLU,避免加和之后的特征皆为正,保持特征的多样性。

    (3) 跳层连接:两种情况,当模块输入和残差支路(1x1->3x3->1x1)的通道数一致时,直接相加;当两者通道不一致时(一般发生在分辨率降低之后,同分辨率一般通道数一致),需要对模块输入特征使用1x1卷积进行升/降维(步长为2,上面说了分辨率会降低),之后同样接BN,不用ReLU。

def conv3x3(in_planes, out_planes, stride=1):
    """3x3 convolution with padding"""
    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
                     padding=1, bias=False)


class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(BasicBlock, self).__init__()
        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out


class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, inplanes, planes, stride=1, downsample=None):
        super(Bottleneck, self).__init__()
        self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
                               padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
        self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1,
                               bias=False)
        self.bn3 = nn.BatchNorm2d(planes * self.expansion,
                                  momentum=BN_MOMENTUM)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu(out)

        out = self.conv3(out)
        out = self.bn3(out)

        if self.downsample is not None:
            residual = self.downsample(x)

        out += residual
        out = self.relu(out)

        return out

 

你可能感兴趣的:(深度学习)