本文简单介绍了,Pytorch框架下的一款深度学习网络框架的配置。
class ResidualBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1):
super(ResidualBlock, self).__init__()
self.conv1 = nn.Conv1d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
self.norm1 = nn.BatchNorm1d(out_channels)
self.relu = nn.LeakyReLU()
self.conv2 = nn.Conv1d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
self.norm2 = nn.BatchNorm1d(out_channels)
self.shortcut = nn.Sequential()
if stride != 1 or in_channels != out_channels:
self.shortcut = nn.Sequential(
nn.Conv1d(in_channels, out_channels, kernel_size=1, stride=stride),
nn.BatchNorm1d(out_channels)
)
__init__ 方法
super(ResidualBlock, self).__init__():调用父类 nn.Module 的构造函数,确保正确初始化。
self.conv1 = nn.Conv1d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1):定义第一个一维卷积层。in_channels 是输入的通道数,out_channels 是输出的通道数,kernel_size=3 表示卷积核的大小为 3,stride 是步长,padding=1 表示在输入的两侧各填充 1 个单位,以保持输出的长度与输入相同(当 stride=1 时)。
self.norm1 = nn.BatchNorm1d(out_channels):定义第一个批量归一化层,用于加速训练和提高模型的稳定性,对 out_channels 个通道进行归一化操作。
self.relu = nn.LeakyReLU():定义一个 LeakyReLU 激活函数,用于引入非线性。
self.conv2 = nn.Conv1d(out_channels, out_channels, kernel_size=3, stride=1, padding=1):定义第二个一维卷积层,输入和输出的通道数相同,卷积核大小为 3,步长为 1,填充为 1。
self.norm2 = nn.BatchNorm1d(out_channels):定义第二个批量归一化层。
self.shortcut = nn.Sequential():初始化一个空的序列模块,用于残差连接。
if stride != 1 or in_channels != out_channels::如果步长不等于 1 或者输入通道数不等于输出通道数,需要对输入进行调整,使其能够与卷积层的输出相加。
self.shortcut = nn.Sequential(nn.Conv1d(in_channels, out_channels, kernel_size=1, stride=stride), nn.BatchNorm1d(out_channels)):定义一个 1x1 卷积层和一个批量归一化层,用于调整输入的通道数和长度。
def forward(self, x):
out = self.relu(self.norm1(self.conv1(x)))
out = self.norm2(self.conv2(out))
out += self.shortcut(x)
out = self.relu(out)
return out
forward 方法
out = self.relu(self.norm1(self.conv1(x))):输入 x 首先经过第一个卷积层 self.conv1,然后经过批量归一化层 self.norm1,最后通过 LeakyReLU 激活函数。
out = self.norm2(self.conv2(out)):上一步的输出 out 经过第二个卷积层 self.conv2,然后经过批量归一化层 self.norm2。
out += self.shortcut(x):将上一步的输出 out 与经过 self.shortcut 处理后的输入 x 相加,实现残差连接。
out = self.relu(out):将相加后的结果通过 LeakyReLU 激活函数。
return out:返回最终的输出。
class CNNModule(nn.Module):
def __init__(self, in_channels, num_classes):
super(CNNModule, self).__init__()
self.conv1 = nn.Conv1d(in_channels, 32, kernel_size=3, stride=1, padding=1)
self.norm1 = nn.BatchNorm1d(32)
self.relu1 = nn.LeakyReLU()
self.pool1 = nn.MaxPool1d(kernel_size=2, stride=1, padding=0)
self.res_block1 = ResidualBlock(32, 64, stride=1)
self.pool2 = nn.AvgPool1d(kernel_size=2, stride=1, padding=0)
self.res_block2 = ResidualBlock(64, 128, stride=1)
self.pool3 = nn.MaxPool1d(kernel_size=2, stride=1, padding=0)
self.conv2 = nn.Conv1d(128, 256, kernel_size=3, stride=1, padding=1)
self.norm2 = nn.BatchNorm1d(256)
self.relu2 = nn.LeakyReLU()
self.pool4 = nn.AvgPool1d(kernel_size=2, stride=1, padding=0)
self.dropout = nn.Dropout(0.5)
self.fc = nn.Linear(256, num_classes)
__init__ 方法
super(CNNModule, self).__init__():调用父类 nn.Module 的构造函数,确保正确初始化。
self.conv1 = nn.Conv1d(in_channels, 32, kernel_size=3, stride=1, padding=1):定义第一个一维卷积层,输入通道数为 in_channels,输出通道数为 32,卷积核大小为 3,步长为 1,填充为 1。
self.norm1 = nn.BatchNorm1d(32):定义第一个批量归一化层,对 32 个通道进行归一化操作。
self.relu1 = nn.LeakyReLU():定义第一个 LeakyReLU 激活函数。
self.pool1 = nn.MaxPool1d(kernel_size=2, stride=1, padding=0):定义第一个最大池化层,池化核大小为 2,步长为 1,不进行填充。
self.res_block1 = ResidualBlock(32, 64, stride=1):定义第一个残差块,输入通道数为 32,输出通道数为 64,步长为 1。
self.pool2 = nn.AvgPool1d(kernel_size=2, stride=1, padding=0):定义第二个平均池化层,池化核大小为 2,步长为 1,不进行填充。
self.res_block2 = ResidualBlock(64, 128, stride=1):定义第二个残差块,输入通道数为 64,输出通道数为 128,步长为 1。
self.pool3 = nn.MaxPool1d(kernel_size=2, stride=1, padding=0):定义第三个最大池化层,池化核大小为 2,步长为 1,不进行填充。
self.conv2 = nn.Conv1d(128, 256, kernel_size=3, stride=1, padding=1):定义第二个一维卷积层,输入通道数为 128,输出通道数为 256,卷积核大小为 3,步长为 1,填充为 1。
self.norm2 = nn.BatchNorm1d(256):定义第二个批量归一化层,对 256 个通道进行归一化操作。
self.relu2 = nn.LeakyReLU():定义第二个 LeakyReLU 激活函数。
self.pool4 = nn.AvgPool1d(kernel_size=2, stride=1, padding=0):定义第四个平均池化层,池化核大小为 2,步长为 1,不进行填充。
self.dropout = nn.Dropout(0.5):定义一个 Dropout 层,用于防止过拟合,丢弃率为 0.5。
self.fc = nn.Linear(256, num_classes):定义一个全连接层,输入维度为 256,输出维度为 num_classes,用于分类任务。
def forward(self, x):
print(f"Input shape: {x.shape}")
x = self.conv1(x)
print(f"After conv1 shape: {x.shape}")
x = self.norm1(x)
x = self.relu1(x)
x = self.pool1(x)
print(f"After pool1 shape: {x.shape}")
x = self.res_block1(x)
print(f"After res_block1 shape: {x.shape}")
x = self.pool2(x)
print(f"After pool2 shape: {x.shape}")
x = self.res_block2(x)
print(f"After res_block2 shape: {x.shape}")
x = self.pool3(x)
print(f"After pool3 shape: {x.shape}")
x = self.conv2(x)
print(f"After conv2 shape: {x.shape}")
x = self.norm2(x)
x = self.relu2(x)
x = self.pool4(x)
print(f"After pool4 shape: {x.shape}")
x = self.dropout(x)
x = torch.mean(x, dim=2)
x = self.fc(x)
return x
forward 方法
print(f"Input shape: {x.shape}"):打印输入的形状。
x = self.conv1(x):输入 x 经过第一个卷积层 self.conv1。
print(f"After conv1 shape: {x.shape}"):打印经过第一个卷积层后的形状。
x = self.norm1(x):上一步的输出经过第一个批量归一化层 self.norm1。
x = self.relu1(x):经过第一个 LeakyReLU 激活函数。
x = self.pool1(x):经过第一个最大池化层 self.pool1。
print(f"After pool1 shape: {x.shape}"):打印经过第一个最大池化层后的形状。
x = self.res_block1(x):经过第一个残差块 self.res_block1。
print(f"After res_block1 shape: {x.shape}"):打印经过第一个残差块后的形状。
x = self.pool2(x):经过第二个平均池化层 self.pool2。
print(f"After pool2 shape: {x.shape}"):打印经过第二个平均池化层后的形状。
x = self.res_block2(x):经过第二个残差块 self.res_block2。
print(f"After res_block2 shape: {x.shape}"):打印经过第二个残差块后的形状。
x = self.pool3(x):经过第三个最大池化层 self.pool3。
print(f"After pool3 shape: {x.shape}"):打印经过第三个最大池化层后的形状。
x = self.conv2(x):经过第二个卷积层 self.conv2。
print(f"After conv2 shape: {x.shape}"):打印经过第二个卷积层后的形状。
x = self.norm2(x):经过第二个批量归一化层 self.norm2。
x = self.relu2(x):经过第二个 LeakyReLU 激活函数。
x = self.pool4(x):经过第四个平均池化层 self.pool4。
print(f"After pool4 shape: {x.shape}"):打印经过第四个平均池化层后的形状。
x = self.dropout(x):经过 Dropout 层。
x = torch.mean(x, dim=2):对第三个维度(长度维度)求均值,将输出的长度维度压缩为 1。
x = self.fc(x):经过全连接层 self.fc,得到最终的输出。
return x:返回最终的输出。
print(f"Input shape: {x.shape}")
x = self.conv1(x)
print(f"After conv1 shape: {x.shape}")
x = self.norm1(x)
x = self.relu1(x)
x = self.pool1(x)
print(f"After pool1 shape: {x.shape}")
输入 x 会与卷积核进行卷积操作。self.conv1 是一个一维卷积层,其参数为 nn.Conv1d(in_channels, 32, kernel_size=3, stride=1, padding=1)。
这意味着它会把输入的 in_channels 个通道转换为 32 个通道,卷积核大小为 3,步长为 1,并且在输入的两侧各填充 1 个单位。
输出形状为 (batch_size, 32, length)(因为 stride = 1 且 padding = 1,所以长度不变)。
对卷积层的输出进行批量归一化操作,即对每个通道的数据进行归一化,使其均值接近 0,方差接近 1。
这样做可以加速模型的收敛速度,提高模型的稳定性。
使用 LeakyReLU 激活函数对批量归一化后的输出进行非线性变换。LeakyReLU 可以避免在输入为负数时梯度消失的问题。
self.pool1 是一个最大池化层,参数为 nn.MaxPool1d(kernel_size=2, stride=1, padding=0)。
它会在每个长度为 2 的窗口中选取最大值作为输出。由于步长为 1,输出长度会减少 1,输出形状为 (batch_size, 32, length - 1)。
x = self.res_block1(x)
print(f"After res_block1 shape: {x.shape}")
x = self.pool2(x)
print(f"After pool2 shape: {x.shape}")
残差块由两个卷积层、两个批量归一化层和一个残差连接组成。
输入 x 首先经过第一个卷积层 self.conv1(在 ResidualBlock 类中),然后进行批量归一化和激活函数操作。
接着经过第二个卷积层 self.conv2 和批量归一化层。
最后将输入 x 经过 self.shortcut 处理后与卷积层的输出相加,再经过激活函数得到最终输出。
self.res_block1 的参数为 ResidualBlock(32, 64, stride=1),所以输出通道数变为 64,输出形状为 (batch_size, 64, length - 1)。
self.pool2 是一个平均池化层,参数为 nn.AvgPool1d(kernel_size=2, stride=1, padding=0)。
它会在每个长度为 2 的窗口中计算平均值作为输出。输出长度会减少 1,输出形状为 (batch_size, 64, length - 2)。
x = self.res_block2(x)
print(f"After res_block2 shape: {x.shape}")
x = self.pool3(x)
print(f"After pool3 shape: {x.shape}")
与第一个残差块类似,self.res_block2 的参数为 ResidualBlock(64, 128, stride=1)。
输入 x 经过一系列卷积、批量归一化和残差连接操作后,输出通道数变为 128,输出形状为 (batch_size, 128, length - 2)。
同样使用最大池化操作,输出长度会减少 1,输出形状为 (batch_size, 128, length - 3)。
x = self.conv2(x)
print(f"After conv2 shape: {x.shape}")
x = self.norm2(x)
x = self.relu2(x)
x = self.pool4(x)
print(f"After pool4 shape: {x.shape}")
self.conv2 是一个一维卷积层,参数为 nn.Conv1d(128, 256, kernel_size=3, stride=1, padding=1)。
它将输入的 128 个通道转换为 256 个通道,输出形状为 (batch_size, 256, length - 3)。
对卷积层的输出进行批量归一化操作。
使用 LeakyReLU 激活函数进行非线性变换。
输出长度会减少 1,输出形状为 (batch_size, 256, length - 4)。
x = self.dropout(x)
x = torch.mean(x, dim=2)
self.dropout 是一个 Dropout 层,参数为 nn.Dropout(0.5)。
它会以 0.5 的概率随机丢弃一些神经元的输出,以防止过拟合。
对第三个维度(长度维度)求均值,将输出的长度维度压缩为 1。输出形状变为 (batch_size, 256)。
x = self.fc(x)
self.fc 是一个全连接层,参数为 nn.Linear(256, num_classes)。
它将输入的 256 维向量映射到 num_classes 维的输出,用于分类任务。输出形状为 (batch_size, num_classes)。
最终输出 x 是一个形状为 (batch_size, num_classes) 的二维张量,其中每个样本对应 num_classes 个类别的得分。在后续的训练或推理过程中,可以使用这些得分进行分类决策,例如通过 softmax 函数将得分转换为概率分布。