2021年11月04日21:18:30
简单理解:上图为一个SE block,由SE block块构成的网络叫做SEnet;可以基于原生网络,添加SE block块构成SE-NameNet,如基于AlexNet等添加SE结构,称作SE-AlexNet、SE-ResNet等
SE-block说明:
- 输入X经过卷积操作得到U
- H×W为输入特征图的长和宽,C代表维度
- 对U进行全局平均池化得到1×1×C的向量
- 进入分支,如下图2,分支由2个全连接层构成,分别使用Relu激活函数与Sigmoid激活函数
- r是降维率,取不同的值会使得中间层的神经元个数不同,r取1的话就是输入层、隐含层、输出层的神经元个数相同均为C
- 全连接层输出的结果为C个通道的权重(1×1×C),与进行分支前C个通道H×W特征图(H×W×C)相乘得到SE block的输出
分支:
原文说:SE块能够被集成到标准的网络体系结构中去,如VGGNet,在每次卷积后的非线性激活后添加。
网络设计:
所以,我们构建一个简单的卷积神经网络包含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)