关于第一次深度学习项目的总结

这是第一次从头开始操作一个深度学习项目,从数据预处理,model设计,搭建,训练,调参,基本上整个流程都走了一遍

半年之前我将计算机视觉几个任务的主要模型都熟悉了,像分类任务的vgg, alexnet, 目标检测的YOLO系列,SSD,RCNN,语义分割的Unet,FCN。在此之后,我要求具备深度网络的设计和实现能力。在复现了全卷积网络unet和yolo3之后,我想可以进入一个新的领域检验我所学习到的东西。

本次检验做的是单目自监督深度估计。其目标在于通过单张图像,输出图像中每个像素点与相机的距离。传统的深度估计方法,如运动恢复结构SFM和立体视觉匹配,都是建立在多视点的特征对应上的,并且预测的深度图是稀疏的,但是好处在于估计的深度是绝对深度。从单张图像进行深度估计是个ill pose problem,简单来说,距离的范围在任意尺度上都是可行的

关于model的实现:

在复现unet和yolo3的过程中,我了解到基于pytorch的model由几个基本的模块组成:

Data.py 用于数据的分批次读取,预处理,数据增强等

net.py 用于核心网络的搭建

Train.py 用于训练过程的可视化以及训练结果的保存

Utils.py 用于编写以上程序需要的各种工具函数

Test.py 用于对程序进行各种测试,单张照片,帧率,视频等

Get_metric.py 用于计算各种评价指标

而其中的核心就是前4个,根据实际情况他们的结构可能会变得很复杂,尤其是具有复合结构的网络,比如说深度估计model,他可能会将net.py拆分成4个模块然后将他们组合起来。并且这四个模块都是可以更换的。除此之外,可能还会设计另外的结构用于特殊得目的,这些结构也要模块化,组合到backbone上。

net.py

unet是encoder-decoder结构,具有恢复feature map分辨率的作用。现在来看,它更大程度上提供了一种范式,如果项目有端到端的这种需要,可以首先考虑这种网络结构。一般encoder都是模块化的,可以有多种选择,vgg, resnet,densenet, mobilenet. 这些固定的backbone可以在modelzoo里面调用,也可以自己编辑。之所以这样做,是因为大多数论文都证明采用迁移学习可以极大地提升精度和训练的进度。以上作为backbone的结构都有在imagenet分类数据集上的预训练权重。

yolo: 他就是一种backbone->neck->head的结构,同样的,backbone都是可以更换的。neck和head根据实际需要进行设计。

实现特定功能的module: 他和neck, head, decoder一样,本身就是模块化的网络,设计好module的输入输出,tensor在内部的流动。这些module有点像蛋白质,生物体内部存在各种各样的功能蛋白,比如说抗病虫害,抗盐碱,耐寒,耐热,发光等等。像我见过的,基于空洞卷积,基于可变卷积,基于条纹卷积,基于门控机制,基于注意力机制等等,太多可以发挥的空间

网络的融合。

data.py

在学习理论的时候,我没有想到标注和数据是同时制作的,并且需要同时送到GPU上。比如说yolo, 网络输出的是一个四维张量,标注也需要制作成一个张量,一个三维张量。这样在计算损失的时候,通过直接相减,可以得到计算损失需要的元素。

这一块是我的弱势,目前我还没有实操过数据增强。它的结构是很固定的,目前只需要重写两个继承自父类Datasets的方法即可。这一块我原先并不在意,但是在实际训练过程中发现还是不得不说。对于图像任务,通常至少可以做最值归一化。之前在某篇论文中看过,特征分布会在前向过程中衰减,这就会导致反向传播的梯度更小。因此,还可以对特征做标准化,是特征落到均值为0,方差为1的正态分布上。

数据增强,我得去做一遍。像目标检测任务,对图像做了增强之后,增强之后的图像的标注一般是不能直接使用的,比如说翻转,那么标注也需要做特定的处理。如果是语义分割,那就将图像和标注做同样的变换即可。

train.py

网络结构化除了上述的好处之外,他还丰富了我们的训练策略。在学习yolo的时候,我们知道B哥采用了冻结训练的策略。在具体实现上,就是只将解冻网络的权重送到优化器中,简单说,就是我们可以控制在任何阶段训练任何部分的网络。其实,不对网络结构化也可以实现这一点,但毫无疑问,结构化之后可以更方便地做到。

参数管理,当参数多起来的时候,通常使用parse.args进行管理。yolo的参数很少,B哥,直接将他们写在开头的位置。

面向对象思想。当你有一个复合网络的时候,train.py的体量就逐渐变得庞大起来,冗余起来。参数很多,定义的网络很多。同时还需要很多全局变量。这时就应该考虑将train作为一个对象,定义一个train类来进行管理。我也是第一次做自监督的项目,他需要利用网络输出的结果来制作标签,而制作标签是使用硬编码来实现的,这进一步加重了train.py冗余的程度。

训练的可视化。我模仿B哥的可视化策略,使用tqdm进行训练过程的可视化,同时实时画出loss的变化。可视化确实很重要,可以方便我们监控训练进程

网络管理。因为该任务的需要,inputs, net, outputs的数量都是可观的,我最初使用列表来管理,发现真的是力不从心。而且后面还加了多尺度机制,更是加重了匹配数据的难度。因此采用源码中使用字典管理的策略。

加载权重。网络结构化的另一个好处是可以更方便地加载预训练权重。比如说encoder直接采用resnet的话,那么直接加载权重就可以了。但如果是自己来搭建的,哪怕是同样的结构,因为key发生了变化,那么就不能直接加载权重。

目前我根据不同的情况,采用3种加载权重的方式(其实都是基于状态字典的特性):基于key, 基于value.size, 人工对齐。specificly, 还是看网络的设计参数,encoder-decorder结构的网络,encoder部分以及backbone-neck-head结构的网络的backbone部分,在采用其他网络的时候,往往是从头往后采用连续的一部分,或者是全部采用,不会从中间开始采用,没有这个必要。

utils.py

这个module就可以用来减轻train.py的负担。将相关的各种功能函数,类都放到这个部分。工具模块目前来看不需要做什么管理,他也不需要什么全局变量,调用工具模块的函数时,已经将所有相关实参传进来了。

你可能感兴趣的:(深度学习,目标检测,人工智能)