深度学习基础——卷积神经网络的基础模块
卷积神经网络(Convolutional Neural Networks,CNN)是深度学习中一种非常重要的神经网络结构,它在图像识别、图像分类、目标检测等领域取得了巨大成功。本文将介绍卷积神经网络的几个基础模块,包括批归一化、全局平均池化、瓶颈结构和沙漏结构。我们将首先对这些基础模块进行概述,然后介绍其概念及公式,并通过Python实现示例代码进行可视化展示。
1. 概述
1.1 批归一化(Batch Normalization)
批归一化是一种用于神经网络中的技术,旨在减少训练过程中的内部协变量偏移,并且可以作为一个正则化项来降低网络的过拟合程度。通过对每个小批量输入进行归一化,使得网络的输入更加稳定,加快收敛速度,同时提高网络的泛化能力。
1.2 全局平均池化(Global Average Pooling)
全局平均池化是一种用于卷积神经网络中的池化操作,与传统的最大池化或平均池化不同,全局平均池化将输入特征图的每个通道进行平均,得到一个数值作为该通道的输出,从而降低了特征图的维度,减少了参数数量。
1.3 瓶颈结构(Bottleneck Structure)
瓶颈结构是一种用于深度残差网络(Residual Networks,ResNet)中的设计,通过采用三层卷积的结构,先减少维度再增加维度,有效地降低了网络的计算量和参数数量,提高了网络的性能和效率。
1.4 沙漏结构(Hourglass Structure)
沙漏结构是一种用于人体姿态估计等任务中的网络结构,采用了递归的编码-解码结构,旨在提高网络的特征提取能力和重建精度,同时减少了网络的参数数量和计算量。
2. 概念及公式介绍
2.1 批归一化
批归一化的计算公式如下:
BN ( x ) = x − μ σ 2 + ϵ ∗ γ + β \text{BN}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} * \gamma + \beta BN(x)=σ2+ϵ x−μ∗γ+β
其中 x x x 是输入特征, μ \mu μ是输入特征的均值, σ \sigma σ 是输入特征的标准差, γ \gamma γ和 β \beta β是可学习的参数, ϵ \epsilon ϵ 是一个很小的数,用于防止分母为零。
2.2 全局平均池化
全局平均池化的计算公式如下:
GAP ( x ) = 1 H × W ∑ i = 1 H ∑ j = 1 W x i , j \text{GAP}(x) = \frac{1}{H \times W} \sum_{i=1}^{H} \sum_{j=1}^{W} x_{i,j} GAP(x)=H×W1i=1∑Hj=1∑Wxi,j
其中 H H H 和 W W W分别是输入特征图的高度和宽度。
2.3 瓶颈结构
瓶颈结构的计算公式如下:
y = F 3 ( F 2 ( F 1 ( x ) ) ) y = F_3(F_2(F_1(x))) y=F3(F2(F1(x)))
其中 F 1 F_1 F1、 F 2 F_2 F2和 F 3 F_3 F3 分别表示三个卷积层,(x) 是输入特征。
2.4 沙漏结构
沙漏结构的计算公式如下:
y = F 2 ( F 1 ( x ) ) + x y = F_2(F_1(x)) + x y=F2(F1(x))+x
其中 (F_1) 和 (F_2) 分别表示编码和解码部分的卷积层,(x) 是输入特征。
3
. 用Python实现示例代码(结果可视化)
import torch
import torch.nn as nn
import torch.nn.functional as F
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, in_planes, planes, stride=1):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion*planes)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += self.shortcut(x)
out = F.relu(out)
return out
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, in_planes, planes, stride=1):
super(Bottleneck, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False)
self.bn3 = nn.BatchNorm2d(self.expansion*planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != self.expansion*planes:
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion*planes)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = F.relu(self.bn2(self.conv2(out)))
out = self.bn3(self.conv3(out))
out += self.shortcut(x)
out = F.relu(out)
return out
class GlobalAveragePooling(nn.Module):
def __init__(self):
super(GlobalAveragePooling, self).__init__()
def forward(self, x):
return F.avg_pool2d(x, x.size()[2:]).view(x.size(0), -1)
class ResNet(nn.Module):
def __init__(self, block, num_blocks, num_classes=10):
super(ResNet, self).__init__()
self.in_planes = 64
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
self.linear = nn.Linear(512*block.expansion, num_classes)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = F.avg_pool2d(out, 4)
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
def ResNet18():
return ResNet(BasicBlock, [2,2,2,2])
def ResNet50():
return ResNet(Bottleneck, [3,4,6,3])
# 计算网络参数量
def count_parameters(model):
return sum(p.numel() for p in model.parameters() if p.requires_grad)
def main():
# 创建ResNet模型
model = ResNet18()
print("ResNet18:")
print(model)
# 计算参数量
parameters = count_parameters(model)
print("Parameters:", parameters)
# 计算FLOPs
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
inputs = torch.randn(1, 3, 32,
32).to(device)
flops = torch.profiler.profile(model, inputs=(inputs,), use_cuda=torch.cuda.is_available())
print("FLOPs:", flops)
if __name__ == "__main__":
main()
4. 总结
本文介绍了卷积神经网络的几个基础模块,包括批归一化、全局平均池化、瓶颈结构和沙漏结构,并通过Python实现了示例代码进行了可视化展示。这些基础模块在深度学习中起着重要的作用,能够提高网络的性能和效率,为各种计算机视觉任务提供了有力的支持。