最近在学习B站一个up主的视频,很棒。故决定在学习过程中进行笔记整理和总结。(无它,自用自勉)
给出收藏的博主笔记及up主笔记,以便自己日后查找翻阅。博主链接-AlexNet
(内里给出几篇参考博客可读)
AlexNet与LeNet的架构非常相似,但也存在显著差异。其比LeNet5要深得多,由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层;AlexNet采用ReLU而非Sigmoid作为激活函数。由于早期的GPU显存有限,所以原版AlexNet采用了双数据流设计,使每个GPU只负责存储和计算模型的一半参数。同时,AlexNet通过dropout控制全连接层的模型复杂度,而LeNet只使用了权重衰减。
使用Dropout的方式在网络正向传播过程中随机失活一部分神经元,以减少过拟合
可以发现,除 Conv1 外,AlexNet 的其余卷积层都是在改变特征矩阵的深度,而池化层则只改变(减小)其尺寸。
注意:原作者实验时用了两块GPU并行计算,上下两组图的结构是一样的。
kernels = 48 * 2 = 96 组卷积核(输出深度)、kernel_size = 11
padding = [1, 2] ()、stride = 4
经 Conv1 卷积后的输出层尺寸为:
kernel_size = 3、 padding = 0、 stride = 2
经 Maxpool1 后的输出层尺寸为:
kernels = 128 * 2 = 256 组卷积核 、 kernel_size = 5
padding = [2, 2] 、 stride = 1
经 Conv2 卷积后的输出层尺寸为:
kernel_size = 3 、padding = 0 、stride = 2
经 Maxpool2 后的输出层尺寸为:
kernels = 192* 2 = 384 组卷积核、kernel_size = 3
padding = [1, 1] 、 stride = 1
经 Conv3 卷积后的输出层尺寸为:
kernels = 192* 2 = 384 组卷积核 、 kernel_size = 3
padding = [1, 1] 、 stride = 1
经 Conv4 卷积后的输出层尺寸为:
kernels = 128* 2 = 256 组卷积核 、 kernel_size = 3
padding = [1, 1] 、 stride = 1
经 Conv5 卷积后的输出层尺寸为:
kernel_size = 3 、 padding = 0 、 stride = 2
经 Maxpool3 后的输出层尺寸为:
Maxpool3 → (6*6*256) → FC1 → 2048 → FC2 → 2048 → FC3 → 1000(可根据数据集的类别数进行修改)
为了加快训练,代码只使用了一半的网络参数,相当于只用了原论文中网络结构的下半部分
import torch.nn as nn
import torch
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
# 用nn.Sequential()将网络打包成一个模块,精简代码
self.features = nn.Sequential( # 卷积层-用于提取图像特征
# 参数卷积核个数(深度)只取一半
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2), # input[3, 224, 224] output[48, 55, 55]
nn.ReLU(inplace=True), # 函数详解附链接
# inplace为True,将会改变输入的数据 ,否则不会改变原输入,只会产生新的输出。
# 省去了反复申请与释放内存的时间,直接代替原来的值。类似C语言中址传递
nn.MaxPool2d(kernel_size=3, stride=2), # output[48, 27, 27]
nn.Conv2d(48, 128, kernel_size=5, padding=2), # output[128, 27, 27]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 13, 13]
nn.Conv2d(128, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # output[192, 13, 13]
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # output[128, 13, 13]
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2), # output[128, 6, 6]
)
self.classifier = nn.Sequential( # 全连接层-用于对图像分类
nn.Dropout(p=0.5), # Dropout 随机失活神经元,比例默认为0.5
nn.Linear(128 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes),
)
if init_weights:
self._initialize_weights()
# 前向传播过程
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, start_dim=1) # (从channel这个维度开始)展平后再传入全连接层
x = self.classifier(x)
return x
# 初始化网络权重参数,实际上 pytorch 在构建网络时会自动初始化权重
def _initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d): # 若是卷积层
nn.init.kaiming_normal_(m.weight, mode='fan_out', # 用(何)kaiming_normal_法初始化权重
nonlinearity='relu')
if m.bias is not None:
nn.init.constant_(m.bias, 0) # 初始化偏重为0
elif isinstance(m, nn.Linear): # 若是全连接层
nn.init.normal_(m.weight, 0, 0.01) # 正态分布初始化
nn.init.constant_(m.bias, 0) # 初始化偏重为0
PyTorch------nn.ReLU(inplace = True)详解