2021年11月04日21:18:30

 

segnet网络pytorch_SEnet

简单理解:上图为一个SE block,由SE block块构成的网络叫做SEnet;可以基于原生网络,添加SE block块构成SE-NameNet,如基于AlexNet等添加SE结构,称作SE-AlexNet、SE-ResNet等

SE-block说明

  1. 输入X经过卷积操作得到U
  2. H×W为输入特征图的长和宽,C代表维度
  3. 对U进行全局平均池化得到1×1×C的向量
  4. 进入分支,如下图2,分支由2个全连接层构成,分别使用Relu激活函数与Sigmoid激活函数
  5. r是降维率,取不同的值会使得中间层的神经元个数不同,r取1的话就是输入层、隐含层、输出层的神经元个数相同均为C
  6. 全连接层输出的结果为C个通道的权重(1×1×C),与进行分支前C个通道H×W特征图(H×W×C)相乘得到SE block的输出

分支

segnet网络pytorch_Pytorch_02

原文说:SE块能够被集成到标准的网络体系结构中去,如VGGNet,在每次卷积后的非线性激活后添加。

segnet网络pytorch_深度学习_03


网络设计

所以,我们构建一个简单的卷积神经网络包含3个【卷积-激活-池化】+2个全连接层,现在将SE blockt添加到原有的【卷积-激活】后,其它均保存不变。

今天简单实现,基于CNN的SE-CNN

一、CNN

"""
Author: yida
Time is: 2021/11/4 19:58 
this Code: 自定义简单的CNN5, 由3个(卷积+激活+池化) + 2个全连接层构成
"""
import os

import torch
import torch.nn as nn

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"


class CNN(nn.Module):
    def __init__(self, n_class):
        super(CNN, self).__init__()
        self.n_class = n_class  # 分类数

        # 卷积 + 激活 + 池化
        self.layer1 = nn.Sequential(
            nn.Conv2d(3, 6, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.layer2 = nn.Sequential(
            nn.Conv2d(6, 12, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        self.layer3 = nn.Sequential(
            nn.Conv2d(12, 24, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )

        # 全连接层
        self.fc = nn.Sequential(
            nn.Linear(1536, 768),
            nn.ReLU(),
            nn.Linear(768, self.n_class),
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x


if __name__ == '__main__':
    # 输入
    inputs = torch.randn(10, 3, 64, 64)
    print("输入维数:", inputs.shape)
    # 初始化网络结构
    model = CNN(n_class=9)  # n_class分类数
    print(model)
    outputs = model(inputs)
    print("输出维数:", outputs.shape)

二、SE-CNN5

"""
Author: yida
Time is: 2021/11/4 19:58 
this Code: 简单的CNN5, 由3个(卷积+激活+池化) + 2个全连接层构成 ; 现对原网络添加SE块, 其它不变构成SE-CNN5
"""
import os

import torch
import torch.nn as nn

os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"


class CNN(nn.Module):
    def __init__(self, n_class):
        super(CNN, self).__init__()
        self.n_class = n_class  # 分类数

        # 卷积 + 激活 + 池化
        self.layer1 = nn.Sequential(
            nn.Conv2d(3, 6, 3, padding=1),
            nn.ReLU(),

            SEblock(channel=6),  # 添加SE模块
            nn.MaxPool2d(2, 2)
        )

        self.layer2 = nn.Sequential(
            nn.Conv2d(6, 12, 3, padding=1),
            nn.ReLU(),

            SEblock(channel=12),  # 添加SE模块
            nn.MaxPool2d(2, 2)
        )

        self.layer3 = nn.Sequential(
            nn.Conv2d(12, 24, 3, padding=1),
            nn.ReLU(),

            SEblock(channel=24),  # 添加SE模块
            nn.MaxPool2d(2, 2)
        )

        # 全连接层
        self.fc = nn.Sequential(
            nn.Linear(1536, 768),
            nn.ReLU(),
            nn.Linear(768, self.n_class),
        )

    def forward(self, x):
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x


class SEblock(nn.Module):
    def __init__(self, channel, r=0.5):  # channel为输入的维度, r为全连接层缩放比例->控制中间层个数
        super(SEblock, self).__init__()
        # 全局均值池化
        self.global_avg_pool = nn.AdaptiveAvgPool2d(1)
        # 全连接层
        self.fc = nn.Sequential(
            nn.Linear(channel, int(channel * r)),  # int(channel * r)取整数
            nn.ReLU(),
            nn.Linear(int(channel * r), channel),
            nn.Sigmoid(),
        )

    def forward(self, x):
        # 对x进行分支计算权重, 进行全局均值池化
        branch = self.global_avg_pool(x)
        branch = branch.view(branch.size(0), -1)

        # 全连接层得到权重
        weight = self.fc(branch)

        # 将维度为b, c的weight, reshape成b, c, 1, 1 与 输入x 相乘
        h, w = weight.shape
        weight = torch.reshape(weight, (h, w, 1, 1))

        # 乘积获得结果
        scale = weight * x
        return scale


if __name__ == '__main__':
    # 输入
    inputs = torch.randn(10, 3, 64, 64)
    print("输入维数:", inputs.shape)
    # 初始化网络结构
    model = CNN(n_class=9)  # n_class分类数
    print(model)
    outputs = model(inputs)
    print("输出维数:", outputs.shape)