10+个神经网络


最后更新
2022.05.02
一些代码示例不全,浅浅理解
一些基础的概念没有解释,比如attention、LoU、NWS、conv、pooling等,baidu it


Transformer.ViT.DETR

从自然语言处理任务起家,又在图像分类和生成领域大放异彩,所向披靡的 Transformer 会成为下一个神话吗?

注意力机制是一种在现代深度学习模型中无处不在的方法,它有助于提高神经机器翻译应用程序性能的概念。Transformer模型就通过注意力机制来提高训练模型的速度,在特定任务中,Transformer的表现优于Google神经机器翻译模型。
在这里插入图片描述
在这里插入图片描述
模型大致分为Encoder(编码器)和Decoder(解码器)两个部分,分别对应上图中的左右两部分。
其中编码器由N个相同的层堆叠在一起(我们后面的实验取N=6),每一层又有两个子层。
第一个子层是一个Multi-Head Attention(多头的自注意机制),第二个子层是一个简单的Feed Forward(全连接前馈网络)。两个子层都添加了一个残差连接+layer normalization的操作。
模型的解码器同样是堆叠了N个相同的层,不过和编码器中每层的结构稍有不同。对于解码器的每一层,除了编码器中的两个子层Multi-Head Attention和Feed Forward,解码器还包含一个子层Masked Multi-Head Attention,如图中所示每个子层同样也用了residual以及layer normalization。
模型的输入由Input Embedding和Positional Encoding(位置编码)两部分组合而成,模型的输出由Decoder的输出简单的经过softmax得到。

Embedding层的作用是将某种格式的输入数据,例如文本,转变为模型可以处理的向量表示,来描述原始数据所包含的信息。
Embedding层输出的可以理解为当前时间步的特征,如果是文本任务,这里就可以是Word Embedding,如果是其他任务,就可以是任何合理方法所提取的特征。

编码器和解码器两个部分都包含输入,且两部分的输入的结构是相同的,只是推理时的用法不同,编码器只推理一次,而解码器是类似RNN那样循环推理,不断生成预测结果的。
编码器只进行一次并行推理,即获得了对于输入的法语句子所提取的若干特征信息。
而对于解码器,是循环推理,逐个单词生成结果的。最开始,由于什么都还没预测,我们会将编码器提取的特征,以及一个句子起始符传给解码器,解码器预期会输出一个单词I。然后有了预测的第一个单词,我们就将I输入给解码器,会再预测出下一个单词am,再然后我们将I am作为输入喂给解码器,以此类推直到预测出句子终止符完成预测。

Transformer是第一个完全依靠Self-attention而不使用序列对齐的RNN或卷积的方式来计算输入输出表示的转换模型
Self-attention是在单个句子不同位置上做的Attention,并得到序列的一个表示。它能够很好地应用到很多任务中,包括阅读理解、摘要、文本蕴涵,以及独立于任务的句子表示。

Attention:
先引入不同的函数和计算机制,根据Query和某个 Key i ,计算两者的相似性或者相关性,最常见的方法包括:求两者的向量点积、求两者的向量Cosine相似性或者通过再引入额外的神经网络来求值
然后引入类似SoftMax的计算方式对第一阶段的得分进行数值转换,一方面可以进行归一化,将原始计算分值整理成所有元素权重之和为1的概率分布;另一方面也可以通过SoftMax的内在机制更加突出重要元素的权重,进行加权求和即可得到Attention数值

Self-attention:
自注意力机制是注意力机制的变体,其减少了对外部信息的依赖,更擅长捕捉数据或特征的内部相关性。
自注意力机制在文本中的应用,主要是通过计算单词间的互相影响,来解决长距离依赖问题。
自注意力机制的计算过程:
1.将输入单词转化成嵌入向量;
2.根据嵌入向量得到q,k,v三个向量;
3.为每个向量计算一个score:score =q . k ;
4.为了梯度的稳定,Transformer使用了score归一化
5.对score施以softmax激活函数;
6.softmax点乘Value值v,得到加权的每个输入向量的评分v;
7.相加之后得到最终的输出结果z

.

受到 Transformer 强大的表示能力的启发,研究人员提出将 Transformer 扩展到计算机视觉任务。2020年,Dosovitskiy等人提出了一种pure transformer,即Vision Transformer(ViT),当直接应用于图像块的序列时,它在图像分类任务上表现良好。 它们尽可能遵循原始transformer的设计。
ViT将Transformer结构完全替代卷积结构完成分类任务, 并在超大规模数据集上取得了超越CNN的效果
在这里插入图片描述
为了处理2D图像,将图像x∈R H×W×C整形为一系列平坦的2D块x(p)∈R N×(P^2·C)。 (H,W)是原始图像的分辨率,(P,P)是每个图像块的分辨率。 那么N = HW / P^2是该Transformer的有效序列长度。 由于Transformer的所有层都使用恒定的宽度,因此可训练的线性投影将每个矢量化路径映射到模型尺寸D,它们的输出称为patch embeddings。
positional encoding(standard learnable 1D position embeddings):ViT同样需要加入位置编码,位置编码可以理解为一张表,表一共有N行,N的大小和输入序列长度相同,每一行代表一个向量,向量的维度和输入序列embedding的维度相同(768)。注意位置编码的操作是sum,而不是concat
MLP:将维度放大再缩小回去

.

DETR(End-to-End Object Detection with Transformers),使用Transformers进行物体检测和分割
在这里插入图片描述
在这里插入图片描述
DETR是第一个成功地将Transformer作为pipeline中的主要构建块的目标检测框架。它与以前的SOTA方法(高度优化的Faster R-CNN)的性能匹配,具有更简单和更灵活的pipeline。

流程:
CNN用来学习图像的二维表示并提取特征
CNN的输出是扁平化的,并辅以位置编码,以馈入标准Transformer的编码器
Transformer的解码器通过输出嵌入到前馈网络(FNN,就是两个全连接层)来预测类别和包围框(一个分类和一个BBox的回归)

DETR的特点:
使用Transformer得到更简单和灵活的pipeline
在目标检测任务上可以匹配SOTA
并行的更有效的直接输出最终的预测集
统一的目标检测和分割架构
大目标的检测性能显著提高,但小目标检测性能下降
在这里插入图片描述

# DETR
class DETRdemo(nn.Module):
    """
    Demo DETR implementation.

    Demo implementation of DETR in minimal number of lines, with the
    following differences wrt DETR in the paper:
    * learned positional encoding (instead of sine)
    * positional encoding is passed at input (instead of attention)
    * fc bbox predictor (instead of MLP)
    The model achieves ~40 AP on COCO val5k and runs at ~28 FPS on Tesla V100.
    Only batch size 1 supported.
    """
    def __init__(self, num_classes, hidden_dim=256, nheads=8,
                 num_encoder_layers=6, num_decoder_layers=6):
        super().__init__()

        # create ResNet-50 backbone
        self.backbone = resnet50()    #backbone选择的是resnet50
        del self.backbone.fc                 #去掉resnet50的全连接层

        # create conversion layer
        self.conv = nn.Conv2d(2048, hidden_dim, 1)    #1*1卷积进行降维,形成hidden_dim个channel的特征向量

        # create a default PyTorch transformer
        self.transformer = nn.Transformer(
            hidden_dim, nheads, num_encoder_layers, num_decoder_layers)   #transformer模块

        # prediction heads, one extra class for predicting non-empty slots
        # note that in baseline DETR linear_bbox layer is 3-layer MLP
        self.linear_class = nn.Linear(hidden_dim, num_classes + 1)          #分为两个分支,一个分支预测类别(为什么加1呢,因为对与背景,实际上给了一个$的类别)
        self.linear_bbox = nn.Linear(hidden_dim, 4)                                          #预测bbox
        
        # output positional encodings (object queries)
        self.query_pos = nn.Parameter(torch.rand(100, hidden_dim))
        # spatial positional encodings
        # note that in baseline DETR we use sine positional encodings
        self.row_embed = nn.Parameter(torch.rand(50, hidden_dim // 2))
        self.col_embed = nn.Parameter(torch.rand(50, hidden_dim // 2))

    def forward(self, inputs):
        # propagate inputs through ResNet-50 up to avg-pool layer
        x = self.backbone.conv1(inputs)
        x = self.backbone.bn1(x)
        x = self.backbone.relu(x)
        x = self.backbone.maxpool(x)

        x = self.backbone.layer1(x)
        x = self.backbone.layer2(x)
        x = self.backbone.layer3(x)
        x = self.backbone.layer4(x)

        # convert from 2048 to 256 feature planes for the transformer
        h = self.conv(x)

        # construct positional encodings
        H, W = h.shape[-2:]
        pos = torch.cat([
            self.col_embed[:W].unsqueeze(0).repeat(H, 1, 1),
            self.row_embed[:H].unsqueeze(1).repeat(1, W, 1),
        ], dim=-1).flatten(0, 1).unsqueeze(1)

        # propagate through the transformer
        h = self.transformer(pos + 0.1 * h.flatten(2).permute(2, 0, 1),
                             self.query_pos.unsqueeze(1)).transpose(0, 1)
        
        # finally project transformer outputs to class labels and bounding boxes
        return {'pred_logits': self.linear_class(h), 
                'pred_boxes': self.linear_bbox(h).sigmoid()}

SNN

脉冲神经网络(Spiking Neural Network,SNN)脉冲神经网络是源于生物启发的新一代人工神经网络模型,属于深度学习的子集,且具有较强的生物基础支撑,更贴近实际。脉冲神经网络更接近人脑神经网络的工作机理,因此更适合用于揭示智能的本质。
在这里插入图片描述
SNN 使用脉冲——这是一种发生在时间点上的离散事件——而非常见的连续值。每个峰值由代表生物过程的微分方程表示出来,其中最重要的是神经元的膜电位。本质上,一旦神经元达到了某一电位,脉冲就会出现,随后达到电位的神经元会被重置。此外,SNN 通常是稀疏连接的,并会利用特殊的网络拓扑。
在这里插入图片描述
近年来,由于脉冲神经网络的计算效率和学习算法的不断提高,脉冲神经网络也开始广泛应用于目标分类、图像处理、机器人控制等实际问题

脉冲耦合神经网络(PCNN)可以看做是脉冲神经网络(SNN)的一种,而脉冲神经网络(SNN)是更广泛的分类。两者其实无明显差异,都是基于脉冲编码(spike coding)


YOLOV12345XE

YOLOv1是单阶段目标检测方法,不需要像Faster RCNN这种两阶段目标检测方法一样,需要生成先验框。Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测
在这里插入图片描述
YOLO将全图划分为S×S的格子, 每个格子负责对落入其中的目标进行检测,一次性预测所有格子所含目标的边界框、置信度、以及所有类别概率向量
它用多个锚框anchor-box检测对象,再用loU做阈值控制,非极大值抑制,一个对象只取其中最大loU值的那个anchor-box输出,一般YOLO卷积输出的目标向量维度为n * n(网格) * (anchorbox对象检测数)*(向量维度=目标对象数+对象id+边界框参数)

该网络存在以下缺点:
每个网格只对应两个bounding box,当物体的长宽比不常见(也就是训练数据集覆盖不到时),效果较差。
原始图片只划分为7x7的网格,当两个物体靠的很近时,效果比较差。
最终每个网格只对应一个类别,容易出现漏检(物体没有被识别到)。
对于图片中比较小的物体,效果比较差。这其实是所有目标检测算法的通病

YOLOv2在v1的基础上的优化:BN、高分辨率预训练、Dimension Clusters(维度聚类,类kmeans)检测

YOLOv3在v2的基础上的优化:锚框使用Kmeans聚类的方法、多尺度预测 增加对细粒度物体的检测力度、网络结构上采用Darknet-53,在v2的基础上每隔两层增加了一个residual(残差),使用这种办法对于训练很深层的网络可以解决梯度消失或者梯度爆炸的问题
在这里插入图片描述
DBL:代码中的Darknetconv2d_BN_Leaky,是yolo_v3的基本组件。就是卷积+BN+Leaky relu。
resn:n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit。
concat:张量拼接。将darknet中间层和后面的某一层的上采样进行拼接

Yolov3是2018年发明提出的,这成为了目标检测one-stage中非常经典的算法,包含Darknet-53网络结构、anchor锚框、FPN等结构

在这里插入图片描述
Yolov4本质上和Yolov3相差不大,Yolov4和v3相比,多了CSP结构,PAN结构
CBM:Yolov4网络结构中的最小组件,由Conv+Bn+Mish激活函数三者组成。
CBL:由Conv+Bn+Leaky_relu激活函数三者组成。
Res unit:借鉴Resnet网络中的残差结构,让网络可以构建的更深。
CSPX:借鉴CSPNet网络结构,由卷积层和X个Res unint模块Concate组成。
SPP:采用1×1,5×5,9×9,13×13的最大池化的方式,进行多尺度融合。

在yolov3中,学习率一般都是使用衰减学习率,就是每学习多少epoch,学习率减少固定的值。
在yolov4中,学习率的变化使用了一种比较新的方法:学习率余弦退火衰减,学习率会先上升再下降。

YOLOv5在v4的基础上的优化:进行了3种数据增强:缩放 色彩空间调整 Mosaic数据增强、锚点框是基于训练数据集自动学习的-自适应锚点框、中间隐藏层使用的是leakyReLU激活函数,最后的检测层使用的是Sigmoid激活函数 等
在这里插入图片描述

YOLOX 在 YOLO 系列的基础上做了一系列工作,其主要贡献在于:在 YOLOv3 的基础上,引入了「Decoupled Head」,「Data Aug」,「Anchor Free」 和「SimOTA 样本匹配」的方法,构建了一种anchor-free的端到端目标检测框架,并且达到了一流的检测水平
YOLOX-L 模型在视频感知挑战赛(CVPR 2021年自动驾驶研讨会)上获得了第一名
在这里插入图片描述

2022还新出了一个YOLOE,YOLOE在速度和准确性权衡方面优于YOLOv5和YOLOX
设计机制包括:
Anchor free无锚盒机制
可扩展的backbone和neck,由CSPRepResStage(CSPNet+RMNet)构成
使用Varifocal Loss(VFL)和Distribution focal loss(DFL)的头部机制ET-head
动态标签分配算法Task Alignment Learning(TAL)
YOLOE,2022年新版YOLO解读_pogg_的博客-CSDN博客_yolo最新版本

# 示例 yolov5/yolo.py at master · ultralytics/yolov5 · GitHub
# v3代码参考 https://github.com/eriklindernoren/PyTorch-YOLOv3/blob/master
# Yolov3代码实现_为算法工程师的博客-CSDN博客_yolov3代码

SSD

目标检测近年来已经取得了很重要的进展,主流的算法主要分为两个类型
(1)two-stage方法,如R-CNN系算法,其主要思路是先通过启发式方法(selective search)或者CNN网络(RPN)产生一系列稀疏的候选框,然后对这些候选框进行分类与回归,two-stage方法的优势是准确度高
(2)one-stage方法,如Yolo和SSD,其主要思路是均匀地在图片的不同位置进行密集抽样,抽样时可以采用不同尺度和长宽比,然后利用CNN提取特征后直接进行分类与回归,整个过程只需要一步,所以其优势是速度快,但是均匀的密集采样的一个重要缺点是训练比较困难,这主要是因为正样本与负样本(背景)极其不均衡,导致模型准确度稍低

SSD英文全名是Single Shot MultiBox Detector
整个网络由三大部分组成,VGG Backbone、Extra Layers、Multi-box Layers
使用vgg16的部分网络作为基础网络,然后丢弃全连接,改为卷积网络用于多尺度特征提取
在这里插入图片描述
SSD将每次卷积得到的特征图(feature map)都进行检测,即基于特征金字塔(Pyramidal Feature Hierarchy)的检测方式
一次完成目标定位与分类,但是对特征图(feature map)进行卷积来检测目标
引入先验框(Prior Box) 这些特征图层上面的每一个点构造6个不同尺度大小的先验框
将所有特征图上得到的输出结合起来,最后通过NMS得到检测结果。

SSD的损失函数包括两部分的加权:位置损失函数、置信度损失函数
在这里插入图片描述
在这里插入图片描述


RCNN.fastRCNN.fasterRCNN

R-CNN的全称是Region-CNN,它是将CNN引入目标检测的开山之作
传统的目标检测方法大多以图像识别为基础。 一般可以在图片上使用穷举法选出所所有物体可能出现的区域框,对这些区域框提取特征并使用图像识别方法分类,得到所有分类成功的区域后,通过非极大值抑制(Non-maximumsuppression)输出结果。
R-CNN遵循传统目标检测的思路,同样采用提取框,对每个框提取特征、图像分类、 非极大值抑制四个步骤进行目标检测。只不过在提取特征这一步,将传统的特征(如 SIFT、HOG 特征等)换成了深度卷积网络提取的特征
在这里插入图片描述
获取候选区域最直接的方式就是滑窗法了,但是滑窗法需要用一个固定大小的小窗口遍历整张图片,因此其有很多的局限性。
所以一般都是使用一些候选区域(Region Proposal)算法,候选区域算法使用一些图像分割的算法来识别潜在的物体,然后合并一些相似(可能颜色或者纹理相近)的小区域,然后就能获得许多的候选区域,虽然可能这些候选区域和实际物体重合度不是很高,但是只要有一个候选区域符合要求即可,生成候选区域的算法一个重要的要求就是:要求较高的召回率(查准率)。
目前已经有很多成熟的产生候选区域算法了,Selective Search(选择性搜索,通过相似性有选择的合并和分离) 就是其中一个高效的算法。

候选框获取完后就对图像做预处理上CNN提取特征了,然后用SVM对矩形框中的物体做出类别判断
最后我们使用一个线性回归器对候选框进行精修,认为IoU大于0.6的为正样本,否则为负样本

RCNN中提取候选框,提取特征和分类回归是分开的,独立的分类器和回归器需要很多的特征作为训练,消耗大
并且一张图像上有大量的重叠框,所以这些候选框送入神经网络时候,提取特征会有冗余,重复操作
故提出了fast RCNN
在这里插入图片描述
该网络使用整个图片和一组目标proposals作为输入。该网络一开始使用几个卷积层和最大池化层去处理整个图片,然后生成一个卷积特征映射。然后对于每个目标proposal,一个region of interest(RoI)池化层将会从得到的特征映射中抽取固定长度的特征向量。每个特征向量(从每个目标proposal中得到的)被输入一个全连接层(fc)层序列,最后分支成两个同级输出层:
一个是对k个目标类加上一个背景类生成softmax概率估计(用于分类)
另一个是对于k个目标类的4个位置值输出(用于回归,得到目标位置)

在之前两个模型结构的基础上,Faster RCNN已经将特征抽取(feature extraction),proposal提取,bounding box regression(rect refine),classification都整合在了一个网络中,使得综合性能有较大提高,在检测速度方面尤为明显
在这里插入图片描述
faster RCNN的4部分:
Conv layers。作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的conv+relu+pooling层提取image的feature maps。该feature maps被共享用于后续RPN层和全连接层。
Region Proposal Networks。RPN网络用于生成region proposals。该层通过softmax判断anchors属于positive或者negative,再利用bounding box regression修正anchors获得精确的proposals。
Roi Pooling。该层收集输入的feature maps和proposals,综合这些信息后提取proposal feature maps,送入后续全连接层判定目标类别。
Classification。利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。
在这里插入图片描述
在这里插入图片描述


Mask RCNN

Mask-RCNN 是何凯明大神继Faster-RCNN后的又一力作,集成了物体检测和实例分割两大功能,并且在性能上上也超过了Faster-RCNN。
Mask R-CNN是一个实例分割(Instance segmentation)算法,可以用来做目标检测、实例分割、目标关键点检测、人体姿势识别等多种任务
在这里插入图片描述
backbone是一系列的卷积层用于提取图像的feature maps,比如可以是VGG,ResNet等

mask的预测是在ROI之后的,通过FCN(Fully Convolution Network)来进行。这个实现的语义分割而不是实例分割。因为每个ROI只对应一个物体。Mask-RCNN与其他分割框架的不同,是先分类再分割。

对于每一个ROI的mask都有80类,因为coco上的数据集是80个类别,并且这样做的话是为了减弱类别间的竞争,从而得到更加好的结果。
该模型的训练和预测是分开的,不是套用同一个流程。在训练的时候,classifier和mask都是同时进行的;在预测的时候,显示得到classifier的结果,然后再把此结果传入到mask预测中得到mask,有一定的先后顺序。
在这里插入图片描述

# 暂时百度,模块太多,不贴了

UNet

语义分割(Semantic Segmentation)是图像处理和机器视觉一个重要分支。与分类任务不同,语义分割需要判断图像每个像素点的类别,进行精确分割。语义分割目前在自动驾驶、自动抠图、医疗影像等领域有着比较广泛的应用。
Unet可以说是最常用、最简单的一种分割模型了,它简单、高效、易懂、容易构建、可以从小数据集中训练
Unet提出的初衷是为了解决医学图像分割的问题
在这里插入图片描述
UNet采用全卷积神经网络,它的优点是U型结构,这个结构可以使它使用更少的训练图片的同事,且分割的准确度也不会差
这个结构就是先对图片进行卷积和池化,在Unet论文中是池化4次,比方说一开始的图片是224x224的,那么就会变成112x112,56x56,28x28,14x14四个不同尺寸的特征。然后我们对14x14的特征图做上采样或者反卷积,得到28x28的特征图,这个28x28的特征图与之前的28x28的特征图进行通道伤的拼接concat,然后再对拼接之后的特征图做卷积和上采样,得到56x56的特征图,再与之前的56x56的特征拼接,卷积,再上采样,经过四次上采样可以得到一个与输入图像尺寸相同的224x224的预测结果。
其实整体来看,这是一个Encoder-Decoder结构,特征提取,然后上采样
通过反卷积得到的更大的尺寸的特征图的边缘,是缺少信息的,毕竟每一次下采样提炼特征的同时,也必然会损失一些边缘特征,而失去的特征并不能从上采样中找回,因此UNet通过特征的拼接,来实现边缘特征的一个找回。
在这里插入图片描述

import torch
import torch.nn as nn
import torch.nn.functional as F

class double_conv2d_bn(nn.Module):
    def __init__(self,in_channels,out_channels,kernel_size=3,strides=1,padding=1):
        super(double_conv2d_bn,self).__init__()
        self.conv1 = nn.Conv2d(in_channels,out_channels,
                               kernel_size=kernel_size,
                              stride = strides,padding=padding,bias=True)
        self.conv2 = nn.Conv2d(out_channels,out_channels,
                              kernel_size = kernel_size,
                              stride = strides,padding=padding,bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)
    
    def forward(self,x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.relu(self.bn2(self.conv2(out)))
        return out
    
class deconv2d_bn(nn.Module):
    def __init__(self,in_channels,out_channels,kernel_size=2,strides=2):
        super(deconv2d_bn,self).__init__()
        self.conv1 = nn.ConvTranspose2d(in_channels,out_channels,
                                        kernel_size = kernel_size,
                                       stride = strides,bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)
        
    def forward(self,x):
        out = F.relu(self.bn1(self.conv1(x)))
        return out
    
class Unet(nn.Module):
    def __init__(self):
        super(Unet,self).__init__()
        self.layer1_conv = double_conv2d_bn(1,8)
        self.layer2_conv = double_conv2d_bn(8,16)
        self.layer3_conv = double_conv2d_bn(16,32)
        self.layer4_conv = double_conv2d_bn(32,64)
        self.layer5_conv = double_conv2d_bn(64,128)
        self.layer6_conv = double_conv2d_bn(128,64)
        self.layer7_conv = double_conv2d_bn(64,32)
        self.layer8_conv = double_conv2d_bn(32,16)
        self.layer9_conv = double_conv2d_bn(16,8)
        self.layer10_conv = nn.Conv2d(8,1,kernel_size=3,
                                     stride=1,padding=1,bias=True)
        
        self.deconv1 = deconv2d_bn(128,64)
        self.deconv2 = deconv2d_bn(64,32)
        self.deconv3 = deconv2d_bn(32,16)
        self.deconv4 = deconv2d_bn(16,8)
        
        self.sigmoid = nn.Sigmoid()
        
    def forward(self,x):
        conv1 = self.layer1_conv(x)
        pool1 = F.max_pool2d(conv1,2)
        
        conv2 = self.layer2_conv(pool1)
        pool2 = F.max_pool2d(conv2,2)
        
        conv3 = self.layer3_conv(pool2)
        pool3 = F.max_pool2d(conv3,2)
        
        conv4 = self.layer4_conv(pool3)
        pool4 = F.max_pool2d(conv4,2)
        
        conv5 = self.layer5_conv(pool4)
        
        convt1 = self.deconv1(conv5)
        concat1 = torch.cat([convt1,conv4],dim=1)
        conv6 = self.layer6_conv(concat1)
        
        convt2 = self.deconv2(conv6)
        concat2 = torch.cat([convt2,conv3],dim=1)
        conv7 = self.layer7_conv(concat2)
        
        convt3 = self.deconv3(conv7)
        concat3 = torch.cat([convt3,conv2],dim=1)
        conv8 = self.layer8_conv(concat3)
        
        convt4 = self.deconv4(conv8)
        concat4 = torch.cat([convt4,conv1],dim=1)
        conv9 = self.layer9_conv(concat4)
        outp = self.layer10_conv(conv9)
        outp = self.sigmoid(outp)
        return outp
    

model = Unet()
inp = torch.rand(10,1,224,224)
outp = model(inp)
print(outp.shape)

inceptionV123

Inception V1:提出了inception的卷积网络结构
提高网络最简单的方法就是提高网络的深度和宽度,但他会导致参数更多,更容易过拟合等问题
解决这种困难的方法就是,把全连接改成稀疏连接,卷积层也是稀疏连接,但是不对称的稀疏数据数值计算效率低下,因为硬件全是针对密集矩阵优化的,所以,我们要找到卷积网络可以近似的最优局部稀疏结构,产生的结果就是Inception
在这里插入图片描述
采用大小不同的卷积核,意味着感受野的大小不同,就可以得到不同尺度的特征

Inception V2: 主要提出了BN层,提高网络性能(减少梯度消失和爆炸、防止过拟合、代替dropout层、使初始化学习参数更大)
Batch Normalization:白化操作,对图像提取特征之前,都会对图像做白化操作,即对输入数据变换成0均值、单位方差的正态分布。它可以加速网络训练,防止梯度消失
在训练中的每个mini-batch上做正则化。卷积神经网络的输入就是图像,白化操作可以加快收敛,对于深度网络,每个隐层的输出都是下一个隐层的输入,即每个隐层的输入都可以做白化操作。

Inception V3:主要提出了分解卷积,把大卷积因式分解成小卷积和非对称卷积,比如把Inception-v1中55的卷积用2个33的卷积替换,优点在于保持相同感受野的同时减少参数
在这里插入图片描述
第二种方法是不对称分解,就是将nn的卷积核替换成 1n 和 n*1 的卷积核堆叠,计算量又会降低,它的优点在于:节约了大量的参数;增加一层非线性,提高模型的表达能力;可以处理更丰富的空间特征,增加特征的多样性
在这里插入图片描述

代码太多就不粘了

FlowNet

光流,简单来说,就是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法
2015年的iccv一篇论文《FlowNet: Learning Optical Flow with Convolutional Networks》提出了一种能够解决光流计算问题的模型,采用监督学习
在这里插入图片描述
他们的两个神经网络大体的思路就是这样。
首先他们有一个收缩部分,主要由卷积层组成,用于深度的提取两个图片的一些特征。
但是pooling会使图片的分辨率降低,为了提供一个密集的光流预测,他们增加了一个扩大层,能智能的把光流恢复到高像素
他们用back progation 对这整个网络进行训练。

第一种简单的FlowNet-S,它的输入是由相邻的两帧图像在channel维度上concat形成的,channel=6,之后就是传统的一层一层的卷积了,最后还有反卷积的操作,输出就是光流图
在这里插入图片描述
第二种FlowNet-c,网络先独立的提取俩图片的特征,再在高层次中把这两特征混合在一起。
黄色部分就是correlation layer,其实就是两个patch的卷积操作,获得了一个值。因为是数据和数据的卷积,所以这里没有训练参数
在这里插入图片描述
后来ICCV 2017 里又提出了FlowNet2,《FlowNet 2.0: Evolution of Optical Flow Estimation with Deep Networks》
相比传统方法,FlowNet1.0中的光流效果还存在很大差距,并且FlowNet1.0不能很好的处理包含物体小移动(small displacements)的数据或者真实场景数据(real-world data),FlowNet2.0极大的改善了1.0的缺点
它通过网络叠加优化光流效果,并warp输入图片
在这里插入图片描述
作者堆叠了三个网络,第一个为FlowNetC,第二个和第三个为FlowNetS,因为在第二和第三个中还包含:FLow、根据Flow把Image 2 warp到Image 1、warp的Image 1与原先Image 1之间的亮度差值,如果也用FlowNetC网络,则无法处理这么多输入情况。
在这里插入图片描述

#来自https://github.com/NVIDIA/flownet2-pytorch/blob/master/,下列举的只是FlowNetS,其他网络代码示例见此

import torch
import torch.nn as nn
from torch.nn import init

import math
import numpy as np

from .submodules import *
'Parameter count : 38,676,504 '

class FlowNetS(nn.Module):
    def __init__(self, args, input_channels = 12, batchNorm=True):
        super(FlowNetS,self).__init__()

        self.batchNorm = batchNorm
        self.conv1   = conv(self.batchNorm,  input_channels,   64, kernel_size=7, stride=2)
        self.conv2   = conv(self.batchNorm,  64,  128, kernel_size=5, stride=2)
        self.conv3   = conv(self.batchNorm, 128,  256, kernel_size=5, stride=2)
        self.conv3_1 = conv(self.batchNorm, 256,  256)
        self.conv4   = conv(self.batchNorm, 256,  512, stride=2)
        self.conv4_1 = conv(self.batchNorm, 512,  512)
        self.conv5   = conv(self.batchNorm, 512,  512, stride=2)
        self.conv5_1 = conv(self.batchNorm, 512,  512)
        self.conv6   = conv(self.batchNorm, 512, 1024, stride=2)
        self.conv6_1 = conv(self.batchNorm,1024, 1024)

        self.deconv5 = deconv(1024,512)
        self.deconv4 = deconv(1026,256)
        self.deconv3 = deconv(770,128)
        self.deconv2 = deconv(386,64)

        self.predict_flow6 = predict_flow(1024)
        self.predict_flow5 = predict_flow(1026)
        self.predict_flow4 = predict_flow(770)
        self.predict_flow3 = predict_flow(386)
        self.predict_flow2 = predict_flow(194)

        self.upsampled_flow6_to_5 = nn.ConvTranspose2d(2, 2, 4, 2, 1, bias=False)
        self.upsampled_flow5_to_4 = nn.ConvTranspose2d(2, 2, 4, 2, 1, bias=False)
        self.upsampled_flow4_to_3 = nn.ConvTranspose2d(2, 2, 4, 2, 1, bias=False)
        self.upsampled_flow3_to_2 = nn.ConvTranspose2d(2, 2, 4, 2, 1, bias=False)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                if m.bias is not None:
                    init.uniform_(m.bias)
                init.xavier_uniform_(m.weight)

            if isinstance(m, nn.ConvTranspose2d):
                if m.bias is not None:
                    init.uniform_(m.bias)
                init.xavier_uniform_(m.weight)
                # init_deconv_bilinear(m.weight)
        self.upsample1 = nn.Upsample(scale_factor=4, mode='bilinear')

    def forward(self, x):
        out_conv1 = self.conv1(x)

        out_conv2 = self.conv2(out_conv1)
        out_conv3 = self.conv3_1(self.conv3(out_conv2))
        out_conv4 = self.conv4_1(self.conv4(out_conv3))
        out_conv5 = self.conv5_1(self.conv5(out_conv4))
        out_conv6 = self.conv6_1(self.conv6(out_conv5))

        flow6       = self.predict_flow6(out_conv6)
        flow6_up    = self.upsampled_flow6_to_5(flow6)
        out_deconv5 = self.deconv5(out_conv6)
        
        concat5 = torch.cat((out_conv5,out_deconv5,flow6_up),1)
        flow5       = self.predict_flow5(concat5)
        flow5_up    = self.upsampled_flow5_to_4(flow5)
        out_deconv4 = self.deconv4(concat5)
        
        concat4 = torch.cat((out_conv4,out_deconv4,flow5_up),1)
        flow4       = self.predict_flow4(concat4)
        flow4_up    = self.upsampled_flow4_to_3(flow4)
        out_deconv3 = self.deconv3(concat4)
        
        concat3 = torch.cat((out_conv3,out_deconv3,flow4_up),1)
        flow3       = self.predict_flow3(concat3)
        flow3_up    = self.upsampled_flow3_to_2(flow3)
        out_deconv2 = self.deconv2(concat3)

        concat2 = torch.cat((out_conv2,out_deconv2,flow3_up),1)
        flow2 = self.predict_flow2(concat2)

        if self.training:
            return flow2,flow3,flow4,flow5,flow6
        else:
            return flow2,

convLSTM

LSTM在时序数据的处理上能力非常强,但是如果时序数据是图像,则在LSTM的基础上加上卷积操作,对于图像的特征提取会更加有效
卷积长短期记忆网络(Convolutional LSTM), 即将传统LSTM 的fully-connected layer 改成Convolutional layer

ConvLSTM最早由《Convolutional LSTM Network: A Machine Learning Approach for Precipitation Nowcasting》论文提出,目的是用于解决降水预报问题。降水预报问题通常被看做时序上的问题,因此被考虑使用LSTM来解决,但是单纯的LSTM不能通过图片来利用空间上的数据特征,因此空间特征在这个LSTM方法中利用是很不充分的。根据以上的描述,论文提出一种ConvLSTM结构,不仅能够建立类似LSTM时序关系,而且可以拥有类似CNN的空间特征提取能力。而且ConvLSTM不仅可以预测天气,还能够解决其他时空序列的预测问题。比如视频分类,动作识别等。
在这里插入图片描述

import torch
import torch.nn as nn

class ConvLSTMCell(nn.Module):
    #这里面全都是数,衡量后面输入数据的维度/通道尺寸
    def __init__(self, input_dim, hidden_dim, kernel_size, bias):
 
        super(ConvLSTMCell,self).__init__()
 
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        #卷积核为一个数组
        self.kernel_size = kernel_size
        #填充为高和宽分别填充的尺寸
        self.padding_size = kernel_size[0]//2,kernel_size[1]//2
        self.bias = bias
        self.conv = nn.Conv2d(self.input_dim + self.hidden_dim, 
                              4 * self.hidden_dim,#4* 是因为后面输出时要切4片
                              self.kernel_size, 
                              padding=self.padding_size, 
                              bias=self.bias)
    
    
    def forward(self,input_tensor,cur_state):
        
        h_cur,c_cur = cur_state
        combined = torch.cat((input_tensor,h_cur),dim=1)
        combined_conv = self.conv(combined)
        cc_f,cc_i,cc_o,cc_g = torch.split(combined_conv, self.hidden_dim, dim=1)
        
        #torch.sigmoid(),激活函数--
        #nn.functional中的函数仅仅定义了一些具体的基本操作,
        #不能构成PyTorch中的一个layer
        #torch.nn.Sigmoid()(input)等价于torch.sigmoid(input)
        f = torch.sigmoid(cc_f)
        i = torch.sigmoid(cc_i)
        o = torch.sigmoid(cc_o)
        g = torch.tanh(cc_g)
        
        #这里的乘是矩阵对应元素相乘,哈达玛乘积
        c_next = f*c_cur + i*g
        h_next = o*nn.Tanh(c_next)
        
    def  init_hidden(self,batch_size, image_size):
        heigth,weight = image_size
        #返回两个是因为cell的尺寸与h一样
        return(torch.zeros(batch_size, self.hidden_dim, height, width, device=self.conv.weight.device),
               torch.zeros(batch_size, self.hidden_dim, height, width, device=self.conv.weight.device))

class ConvLstm(nn.Module):
    def __init__(self,input_dim,hidden_dim,kernel_size,num_layers,batch_first=False,bias=False,return_all_layers=False):
        super(ConvLstm,self).__init__()
        
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.kernel_size = kernel_size
        self.num_layers = num_layers
        self.batch_first = batch_first
        self.bias = bias
        self.return_all_layers = return_all_layers
        
        #为了储存每一层的参数尺寸
        cell_list = []
        for i in range(0,num_layers):
            #注意这里利用lstm单元得出到了输出h,h再作为下一层的输入,依次得到每一层的数据维度并储存
            cur_input_dim = input_dim if i == 0 else self.hidden_dim[i-1]
            cell_list.append(ConvLSTMCell(input_dim = cur_input_dim,
                                          hidden_dim = self.hidden_dim,
                                          kernel_size = self.kernel_size,
                                          bias = self.bias
                                         ))
        #将上面循环得到的每一层的参数尺寸/维度,储存在self.cell_list中,后面会用到
        #注意这里用了ModuLelist函数,模块化列表
        self.cell_list = nn.ModuleList(cell_list)
     
    #这里forward有两个输入参数,input_tensor 是一个五维数据
    #(t时间步,b输入batch_ize,c输出数据通道数--维度,h,w图像高乘宽)
    #hidden_state=None 默认刚输入hidden_state为空,等着后面给初始化
    def forward(self,input_tensor,hidden_state=None):
        #先调整一下输出数据的排列
        if not self.batch_first:
            input_tensor = input_tensor.permute(1,0,2,3,4)
        #取出图片的数据,供下面初始化使用
        b,_,_,h,w = input_tensor.size()
        #初始化hidd_state,利用后面和lstm单元中的初始化函数
        hidden_state = self._init_hidden(batch_size=b,image_size=(h, w))
        
        #储存输出数据的列表
        layer_output_list = []
        layer_state_list = []
        
        seq_len = input_tensor.size(1)
        
        #初始化输入数据
        cur_layer_input = input_tensor
        
        for layer_idx in range(self.num_layers):
    
            h,c = hidden_state[layer_idx]
            output_inner = []
            
            for t in range(seq_len):
                #每一个时间步都更新 h,c
                #注意这里self.cell_list是一个模块(容器)
                h,c = self.cell_list[layer_idx](input_tensor=cur_layer_input[:,t,:,:,:],cur_state=[h,c])
                #储存输出,注意这里 h 就是此时间步的输出
                output_inner.append(h)
            
            #这一层的输出作为下一次层的输入,
            layer_output = torch.stack(output_inner, dim=1)
            cur_layer_input = layer_output
            
            layer_output_list.append(layer_output)
            #储存每一层的状态h,c
            layer_state_list.append([h,c])
 
        #选择要输出所有数据,还是输出最后一层的数据    
        if not self.return_all_layers:
            layer_output_list = layer_output_list[-1:]
            last_state_list = last_state_list[-1:]
 
        return layer_output_list, last_state_list
        
    def _init_hidden(self, batch_size, image_size):
        init_states = []
        for i in range(self.num_layers):
            init_states.append(self.cell_list[i].init_hidden(batch_size, image_size))
        return init_states


CAE

自编码器(Autoencoder)是一种旨在将它们的输入复制到的输出的神经网络。他们通过将输入压缩成一种隐藏空间表示(latent-space representation),然后这种重构这种表示的输出进行工作。这种网络由两部分组成:
编码器:将输入压缩为潜在空间表示。可以用编码函数h = f(x)表示。
解码器:这部分旨在重构来自隐藏空间表示的输入。可以用解码函数r = g(h)表示。

卷积自编码器CAE:
卷积自编码器是采用卷积层代替全连接层,原理和自编码器一样,对输入的象征进行降采样以提供较小维度潜在表示,并让自编码器学习象征的压缩版本
目的在于学习图像中的局部结构信息
它在输入的所有位置都共享权值,以保证二维空间的局部性

先卷积再反卷积,比较解码的数据与原始的数据的差异进行训练,最后得到比较稳定的参数,待这一层的参数都训练好的时,再进行下一个的训练
为了增加其抗干扰的能力,有时在训练时加入随机噪声进而增加其鲁棒性
基于 卷积自编码器CAE 可以提取图像的空间特征和图像重构
在这里插入图片描述

# ENCODER
conv_1 = tf.layers.conv2d(x_input, 32, (4, 4), strides=(1, 1), padding='valid',
                          kernel_initializer=tf.truncated_normal_initializer)  # => (142, 142, 32)
batch_norm_1 = tf.nn.relu(tf.layers.batch_normalization(conv_1, training=is_training))   # (142, 142, 32)
conv_2 = tf.layers.conv2d(batch_norm_1, 64, (3, 3), strides=(1, 1), padding='valid',
                          kernel_initializer=tf.truncated_normal_initializer)  # =>(140, 140, 64)
batch_norm_2 = tf.nn.relu(tf.layers.batch_normalization(conv_2, training=is_training))  # (140, 140, 64)
pooling_1 = tf.layers.max_pooling2d(batch_norm_2, pool_size=(5, 5), strides=(5, 5))   # =>(28, 28, 64)
conv_3 = tf.layers.conv2d(pooling_1, 128, (3, 3), strides=(1, 1), padding='valid',
                          kernel_initializer=tf.truncated_normal_initializer)   # =>(26, 26, 128)
batch_norm_3 = tf.nn.relu(tf.layers.batch_normalization(conv_3, training=is_training))    # (26, 26, 128)
pooling_2 = tf.layers.max_pooling2d(batch_norm_3, pool_size=(2, 2), strides=(2, 2))   # =>(13, 13, 128)
self.code = pooling_2
# DECODER
conv_trans_1 = tf.layers.conv2d_transpose(pooling_2, 128, kernel_size=(2, 2), strides=(2, 2))  # =>(26, 26, 128)
conv_trans_1 = tf.nn.relu(tf.layers.batch_normalization(conv_trans_1, training=is_training))  # (26, 26, 256)
 
conv_trans_2 = tf.layers.conv2d_transpose(conv_trans_1, 64, kernel_size=(3, 3), strides=(1, 1)) # =>(28, 28, 64)
conv_trans_2 = tf.nn.relu(tf.layers.batch_normalization(conv_trans_2, training=is_training))  # (28, 28, 128)
 
conv_trans_3 = tf.layers.conv2d_transpose(conv_trans_2, 64, kernel_size=(5, 5), strides=(5, 5))  # =>(140, 140, 64)
conv_trans_3 = tf.nn.relu(tf.layers.batch_normalization(conv_trans_3, training=is_training))  # (140, 140, 128)
 
conv_trans_4 = tf.layers.conv2d_transpose(conv_trans_3, 32, kernel_size=(3, 3), strides=(1, 1))  # (142, 142, 32)
conv_trans_4 = tf.nn.relu(tf.layers.batch_normalization(conv_trans_4, axis=3))  # (142, 142, 64)
 
conv_trans_5 = tf.layers.conv2d_transpose(conv_trans_4, 1, kernel_size=(4, 4), strides=(1, 1))  # (145, 145, 32)
conv_trans_5 = tf.nn.relu(tf.layers.batch_normalization(conv_trans_5, axis=3))  # (145, 145, 2)
 
conv_final = tf.layers.conv2d(conv_trans_5, 1, kernel_size=(2, 2), strides=(1, 1), padding='same')  #(145, 145, 1)

ResNet

ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出,斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名。获得COCO数据集中目标检测第一名,图像分割第一名

网络优点
1.超深的网络结构(超过1000层)。
2.提出residual(残差结构)模块。
3.使用Batch Normalization 加速训练(丢弃dropout)

一般人们会觉得网络越深效果越好,但因为很深的网络层数会导致梯度消失或梯度爆炸问题,为了解决这个问题,ResNet在网络中使用 BN(Batch Normalization)层来解决
随着深度网络的训练,参数得到不同程度的更新,这样这些参数会失去0均值和1方差的分布。这样会带来数据的偏移,导致收敛速度下降,并且容易带来过多神经元未被激活,会带来参数的调整过大影响收敛性能
BN方法会把数据分为若干组,针对每一批数据,在网络的每一层输入之前增加归一化处理,使输入的均值为0,标准差为1,目的是将数据限制在统一的分布下,然后作为该神经元的激活值
BN可以看作在各层之间加入了一个新的计算层,对数据分布进行额外的约束,从而增强模型的泛化能力,也同时降低了模型的拟合能力.破坏了之前的特征分布(防止过拟合)

还有为了解决深层网络中的退化问题,可以人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系。ResNet提出了 residual结构(残差结构,远程跳接)来减轻退化问题,这可以防止梯度消失
计算残差(ResNet)块的公式:
a[l+2]=g(z[l+2]+a[l])=g(w[l+2] * a[l+1]+b[l+2]+a[l])=…
a[l+1]=g(w[l+1]*a[l]+b[l+1])
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class ResNet50(object):
    def __init__(self, inputs, num_classes=1000, is_training=True,
                 scope="resnet50"):
        self.inputs =inputs
        self.is_training = is_training
        self.num_classes = num_classes

        with tf.variable_scope(scope):
            # construct the model
            net = conv2d(inputs, 64, 7, 2, scope="conv1") # -> [batch, 112, 112, 64]
            net = tf.nn.relu(batch_norm(net, is_training=self.is_training, scope="bn1"))
            net = max_pool(net, 3, 2, scope="maxpool1")  # -> [batch, 56, 56, 64]
            net = self._block(net, 256, 3, init_stride=1, is_training=self.is_training,
                              scope="block2")           # -> [batch, 56, 56, 256]
            net = self._block(net, 512, 4, is_training=self.is_training, scope="block3")
                                                        # -> [batch, 28, 28, 512]
            net = self._block(net, 1024, 6, is_training=self.is_training, scope="block4")
                                                        # -> [batch, 14, 14, 1024]
            net = self._block(net, 2048, 3, is_training=self.is_training, scope="block5")
                                                        # -> [batch, 7, 7, 2048]
            net = avg_pool(net, 7, scope="avgpool5")    # -> [batch, 1, 1, 2048]
            net = tf.squeeze(net, [1, 2], name="SpatialSqueeze") # -> [batch, 2048]
            self.logits = fc(net, self.num_classes, "fc6")       # -> [batch, num_classes]
            self.predictions = tf.nn.softmax(self.logits)


    def _block(self, x, n_out, n, init_stride=2, is_training=True, scope="block"):
        with tf.variable_scope(scope):
            h_out = n_out // 4
            out = self._bottleneck(x, h_out, n_out, stride=init_stride,
                                   is_training=is_training, scope="bottlencek1")
            for i in range(1, n):
                out = self._bottleneck(out, h_out, n_out, is_training=is_training,
                                       scope=("bottlencek%s" % (i + 1)))
            return out

    def _bottleneck(self, x, h_out, n_out, stride=None, is_training=True, scope="bottleneck"):
        """ A residual bottleneck unit"""
        n_in = x.get_shape()[-1]
        if stride is None:
            stride = 1 if n_in == n_out else 2

        with tf.variable_scope(scope):
            h = conv2d(x, h_out, 1, stride=stride, scope="conv_1")
            h = batch_norm(h, is_training=is_training, scope="bn_1")
            h = tf.nn.relu(h)
            h = conv2d(h, h_out, 3, stride=1, scope="conv_2")
            h = batch_norm(h, is_training=is_training, scope="bn_2")
            h = tf.nn.relu(h)
            h = conv2d(h, n_out, 1, stride=1, scope="conv_3")
            h = batch_norm(h, is_training=is_training, scope="bn_3")

            if n_in != n_out:
                shortcut = conv2d(x, n_out, 1, stride=stride, scope="conv_4")
                shortcut = batch_norm(shortcut, is_training=is_training, scope="bn_4")
            else:
                shortcut = x
            return tf.nn.relu(shortcut + h)

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值