残差网络原理(Residual Network,简称ResNet):
残差网络的实例:ResNet-50
ResNet-50 是一个经典的残差网络,包含 50 层。它的结构如下:
初始卷积层:
输入图像经过一个 7x7 卷积层,步幅为 2,输出通道数为 64。
接着是批归一化和 ReLU 激活函数。
然后是一个 3x3 最大池化层。
残差块堆叠:
ResNet-50 包含 4 个阶段,每个阶段由多个残差块组成。
每个阶段的第一个残差块会通过 1x1 卷积调整输入和输出的通道数。
每个阶段的残差块数量分别为 3、4、6、3。
全局平均池化:
在最后一个残差块之后,使用全局平均池化将特征图降为 1x1。
全连接层:
最后是一个全连接层,输出分类结果。
残差网络的示范:
import torch
import torch.nn as nn
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_channels)
# 如果输入和输出的通道数不一致,需要通过 1x1 卷积调整
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels)
)
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out += self.shortcut(residual) # 残差连接
out = self.relu(out)
return out
# 示例:构建一个简单的残差网络
class SimpleResNet(nn.Module):
def __init__(self):
super(SimpleResNet, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
# 堆叠残差块
self.layer1 = self._make_layer(64, 64, 3)
self.layer2 = self._make_layer(64, 128, 4, stride=2)
self.layer3 = self._make_layer(128, 256, 6, stride=2)
self.layer4 = self._make_layer(256, 512, 3, stride=2)
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512, 10) # 假设分类任务有 10 个类别
def _make_layer(self, in_channels, out_channels, blocks, stride=1):
layers = []
layers.append(ResidualBlock(in_channels, out_channels, stride))
for _ in range(1, blocks):
layers.append(ResidualBlock(out_channels, out_channels))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
# 示例:使用 SimpleResNet
model = SimpleResNet()
input_tensor = torch.randn(1, 3, 224, 224) # 输入图像大小为 224x224
output = model(input_tensor)
print(output.shape) # 输出形状为 (1, 10)
批处归一化处理原理(BatchNorm):
批归一化的作用
加速训练:
通过归一化每一层的输入,减少内部协变量偏移(Internal Covariate Shift),使得网络更容易训练。
允许使用更高的学习率,从而加快收敛速度。
提高模型稳定性:
减少对参数初始化的敏感性。
缓解梯度消失和梯度爆炸问题。
正则化效果:
批归一化在训练时引入了噪声(由于小批量的统计特性),可以起到一定的正则化作用,减少过拟合。
批归一化的实现:
在 PyTorch 中,批归一化可以通过
torch.nn.BatchNorm2d
(用于卷积层)或torch.nn.BatchNorm1d
(用于全连接层)来实现。1. 二维批归一化(BatchNorm2d)
用于卷积神经网络的卷积层,输入形状为
(batch_size, channels, height, width)
。2. 一维批归一化(BatchNorm1d)
用于全连接层或一维卷积层,输入形状为
(batch_size, channels)
或(batch_size, channels, length)
。
批归一化的参数
可学习参数:
γγ(缩放)和 ββ(平移)是批归一化的可学习参数。
它们通过反向传播进行优化。
不可学习参数:
移动平均均值(running_mean)和移动平均方差(running_var)是批归一化的不可学习参数。
它们在训练时更新,但在推理时固定。
pytorch下的示范:
import torch
import torch.nn as nn
class SimpleCNNWithBatchNorm(nn.Module):
def __init__(self):
super(SimpleCNNWithBatchNorm, self).__init__()
# 卷积层 1
self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(16) # 对out_channels=16进行批归一化,通道数为 16
self.relu = nn.ReLU(inplace=True)
self.pool = nn.MaxPool2d(kernel_size=2, stride=2)
# 卷积层 2
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(32) # 对out_channels=32进行批归一化,通道数为 32
# 全连接层
self.fc = nn.Linear(32 * 8 * 8, 10) # 假设输入图像大小为 32x32,输出 10 个类别
def forward(self, x):
# 卷积层 1
x = self.conv1(x)
x = self.bn1(x) # 批归一化
x = self.relu(x)
x = self.pool(x)
# 卷积层 2
x = self.conv2(x)
x = self.bn2(x) # 批归一化
x = self.relu(x)
x = self.pool(x)
# 展平
x = x.view(x.size(0), -1)
# 全连接层
x = self.fc(x)
return x
# 示例:使用 SimpleCNNWithBatchNorm
model = SimpleCNNWithBatchNorm()
input_tensor = torch.randn(1, 3, 32, 32) # 输入图像大小为 32x32,3 个通道
output = model(input_tensor)
print(output.shape) # 输出形状为 (1, 10)