本系列是博主刚开始接触深度学习时写的一些笔记,写的很早了一直没有上传,趁着假期上传一下,作为分享,希望能帮助到你。
目录
一、LeNet
LeNet是早期最成功的神经网络,先使用卷积层来学习图片空间信息,然后使用全连接层来转换到类别空间。
从上图可以看出,LeNet网络:(1)输入是一个28×28的图像;(2)卷积层1:卷积核大小为5×5,kernel_size=6,输出是28×28;(3)polling层1:卷积核大小为2×2,kernel_size=6,输出是14×14;(4)卷积层2:卷积核大小为5×5,kernel_size=16,输出是10×10;(5)polling层2:卷积核大小为2×2,kernel_size=16,输出是5×5;(6)卷积层3:卷积核大小为5×5,kernel_size=120,输出是1×1;(7)全连接层1;(8)输出层;
LeNet是一种在手写体字符数据识别任务上效果好的卷积神经网络,MNIST数据集为手写体图片数据集,该数据集中包含60 000个训练数据,10000个测试数据,图像大小28×28,分为10类。
对代码中x = x.view(x.size(0),-1)的理解:这句代码一般在调用分类器之前,分类器是一个简单的nn.Linear()结构,输入输出都是维度为1 的值,x = x.view(x.size(0),-1)是为了将前面多维度的tensor展平成1维。View()函数的功能和reshape类似,用来转换size大小。x=x.view(batchsize,-1)中batchsize指转换后有几行,而-1指在不告诉函数有多少列的情况下,根据原tensor数据和batchsize自动分配列数。
代码实现:
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv = nn.Sequential(
nn.Conv2d(1, 6, 5),
nn.Sigmoid(),
nn.MaxPool2d(2, 2),
nn.Conv2d(6, 16, 5),
nn.Sigmoid(),
nn.MaxPool2d(2, 2)
)
self.fc = nn.Sequential(
nn.Linear(16*4*4, 120),
nn.Sigmoid(),
nn.Linear(120, 84),
nn.Sigmoid(),
nn.Linear(84, 10)
)
def forward(self, img):
feature = self.conv(img)
output = self.fc(feature.view(img.shape[0], -1))
return output
二、AlexNet
(图片来自互联网)
AlexNet是Alex Krizhevsky等在2012年ImageNet比赛上首次提出的,最终该网络夺得冠军。AlexNet是一种简单且高效的卷积神经网络,整个网络模型由5个卷积层、3个全连接层以及相应的池化层构成,最终由softmax函数完成分类。下表为AlexNet网络模型的参数信息。
参数设置 | 输出 | |
Input | kernel-size=11×11, stride=4 | 227×227×3 |
Conv1 | kernel-size=3×3, stride=2 | 55×55×96 |
Polling1 | kernel-size=5×5, stride=2 | 27×27×96 |
Conv2 | kernel-size=5×5, stride=2 | 27×27×256 |
Polling2 | kernel-size=3×3, stride=2 | 13×13×256 |
Conv3 | kernel-size=3×3, stride=2 | 13×13×384 |
Conv4 | kernel-size=3×3 | 13×13×384 |
Conv5 | kernel-size=3×3 | 13×13×256 |
Polling3 | kernel-size=3×3, stride=2 | 6×6×256 |
Fc6 | / | 9216 |
Fc7 | / | 4096 |
Fc8 | / | 4096 |
Output | / | 1000 |
代码实现:
class AlexNet(nn.Module):
def __init__(self, num_classes: int = 1000) -> None:
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=3, stride=2),
)
self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 6 * 6, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Linear(4096, num_classes),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.features(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
三、GoogleNet
GoogleNet是google公司推出深度神经网络模型,该网络在2014年的ImageNet竞赛中夺得了冠军。网络主体是由Inception模块拼接而成。
(1)关于Inception模块
整个inception结构就是由多个这样的incepion模块串联起来的。inception结构主要由有两个贡献:1、使用1×1的卷积来进行升降维;2、在多个尺寸上同时进行卷积再聚合。
1:1×1卷积模块(黄色):在相同尺寸的感受野中叠加更多的卷积,能提取到更丰富的特征;降低了计算复杂度。
2:在直觉感受上在多个尺度上同时进行卷积,能提取到不同尺度的特征,特征更为丰富也意味着最后分类判断时更加准确;利用稀疏矩阵分解成密集矩阵计算的原理来加快收敛速度;
(2)GoogleNet网络详解
a、输入:224×224×3(进行了零均值化预处理操作,即图像中的每个像素减去均值)
b、第一模块:卷积层、最大池化层
卷积核:7×7,stride=2,padding=3,通道数=64,
Output=(224+2*3-1*(7-1)-1)/2 +1=112
输出:112×112×64 卷积后进行Relu操作
池化层:3×3,stride=2 output=(112-1*(3-1)-1)/2 +1=56
输出:56×56×64
c、第二模块:2个卷积层,最大池化层
64个1×1的卷积核将输入的特征图(56×56×64)变为56×56×64,然后Relu()
卷积核:3×3,stride=1,padding=1,通道数192,
Output=(56+1*2-1*(3-1)-1)/1 +1=56
输出:56×56×192,然后Relu()
最大池化层:3×3,stride=2,通道数192, output=(56-1*(3-1)-1)/2 +1=28
输出:28×28×192
d、第三模块 inception 3a层
1)使用64个1×1的卷积核 输出:28×28×64 然后Relu()
2)96个1×1的卷积核 输出:28×28×96 然后Relu()
128个3×3的卷积核 输出28×28×128
3)16个1×1的卷积核 输出:28×28×16 然后Relu()
32个5×5的卷积核 输出:28×28×32
4)3×3的最大池化层,28×28×192
32个1×1的卷积核 输出:28×28×32
将上面4个结果进行连接,对输出结果的第三维进行并联,64+128+32+32=256
最终的输出:28×28×256
e、第三模块 inception 3b
1)128个1×1的卷积核 输出:28×28×128,然后Relu()
2)128个1×1的卷积核 输出:28×28×128,然后Relu()
192个3×3的卷积核 输出:28×28×192
3)32个1×1的卷积核 输出:28×28×32,然后Relu()
96个5×5的卷积核 输出:28×28×96
4)3×3的最大池化层 28×28×256
64个1×1的卷积核 输出28×28×64
将上面4个结果进行连接,对这四部分输出结果的第三维并联,128+192+86+64=480
最终的输出是:28×28×480
f、第四模块 inception 4a、4b、4c、4e
g、第五模块 inception 5a、5b
h、输出层
GoogleNet采用的是全局平均池化,得到的是高和宽均为1的卷积层,然后添加丢弃概率为40%的Dropout,输出层激活函数采用的是softmax。
I、激活函数
Relu()函数
j、辅助分类器
神经网络的中间层也具有很强的识别能力,为了利用中间层抽象的特征,在某些中间层中添加含有多层的分类器。GoogleNet中共增加了两个辅助的softmax分支:一是为了避免梯度消失,用于向前传导梯度。二是将中间某一层输出用作分类,起到模型融合作用。
(图片来自互联网)
代码实现:
class GoogLeNet(nn.Module):
__constants__ = ['aux_logits', 'transform_input']
def __init__(
self,
num_classes: int = 1000,
aux_logits: bool = True,
transform_input: bool = False,
init_weights: Optional[bool] = None,
blocks: Optional[List[Callable[..., nn.Module]]] = None
) -> None:
super(GoogLeNet, self).__init__()
if blocks is None:
blocks = [BasicConv2d, Inception, InceptionAux]
if init_weights is None:
warnings.warn('The default weight initialization of GoogleNet will be changed in future releases of '
'torchvision. If you wish to keep the old behavior (which leads to long initialization times'
' due to scipy/scipy#11299), please set init_weights=True.', FutureWarning)
init_weights = True
assert len(blocks) == 3
conv_block = blocks[0]
inception_block = blocks[1]
inception_aux_block = blocks[2]
self.aux_logits = aux_logits
self.transform_input = transform_input
self.conv1 = conv_block(3, 64, kernel_size=7, stride=2, padding=3)
self.maxpool1 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.conv2 = conv_block(64, 64, kernel_size=1)
self.conv3 = conv_block(64, 192, kernel_size=3, padding=1)
self.maxpool2 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception3a = inception_block(192, 64, 96, 128, 16, 32, 32)
self.inception3b = inception_block(256, 128, 128, 192, 32, 96, 64)
self.maxpool3 = nn.MaxPool2d(3, stride=2, ceil_mode=True)
self.inception4a = inception_block(480, 192, 96, 208, 16, 48, 64)
self.inception4b = inception_block(512, 160, 112, 224, 24, 64, 64)
self.inception4c = inception_block(512, 128, 128, 256, 24, 64, 64)
self.inception4d = inception_block(512, 112, 144, 288, 32, 64, 64)
self.inception4e = inception_block(528, 256, 160, 320, 32, 128, 128)
self.maxpool4 = nn.MaxPool2d(2, stride=2, ceil_mode=True)
self.inception5a = inception_block(832, 256, 160, 320, 32, 128, 128)
self.inception5b = inception_block(832, 384, 192, 384, 48, 128, 128)
if aux_logits:
self.aux1 = inception_aux_block(512, num_classes)
self.aux2 = inception_aux_block(528, num_classes)
else:
self.aux1 = None # type: ignore[assignment]
self.aux2 = None # type: ignore[assignment]
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.dropout = nn.Dropout(0.2)
self.fc = nn.Linear(1024, num_classes)
if init_weights:
self._initialize_weights()
def _initialize_weights(self) -> None:
for m in self.modules():
if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
torch.nn.init.trunc_normal_(m.weight, mean=0.0, std=0.01, a=-2, b=2)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def _transform_input(self, x: Tensor) -> Tensor:
if self.transform_input:
x_ch0 = torch.unsqueeze(x[:, 0], 1) * (0.229 / 0.5) + (0.485 - 0.5) / 0.5
x_ch1 = torch.unsqueeze(x[:, 1], 1) * (0.224 / 0.5) + (0.456 - 0.5) / 0.5
x_ch2 = torch.unsqueeze(x[:, 2], 1) * (0.225 / 0.5) + (0.406 - 0.5) / 0.5
x = torch.cat((x_ch0, x_ch1, x_ch2), 1)
return x
def _forward(self, x: Tensor) -> Tuple[Tensor, Optional[Tensor], Optional[Tensor]]:
# N x 3 x 224 x 224
x = self.conv1(x)
# N x 64 x 112 x 112
x = self.maxpool1(x)
# N x 64 x 56 x 56
x = self.conv2(x)
# N x 64 x 56 x 56
x = self.conv3(x)
# N x 192 x 56 x 56
x = self.maxpool2(x)
# N x 192 x 28 x 28
x = self.inception3a(x)
# N x 256 x 28 x 28
x = self.inception3b(x)
# N x 480 x 28 x 28
x = self.maxpool3(x)
# N x 480 x 14 x 14
x = self.inception4a(x)
# N x 512 x 14 x 14
aux1: Optional[Tensor] = None
if self.aux1 is not None:
if self.training:
aux1 = self.aux1(x)
x = self.inception4b(x)
# N x 512 x 14 x 14
x = self.inception4c(x)
# N x 512 x 14 x 14
x = self.inception4d(x)
# N x 528 x 14 x 14
aux2: Optional[Tensor] = None
if self.aux2 is not None:
if self.training:
aux2 = self.aux2(x)
x = self.inception4e(x)
# N x 832 x 14 x 14
x = self.maxpool4(x)
# N x 832 x 7 x 7
x = self.inception5a(x)
# N x 832 x 7 x 7
x = self.inception5b(x)
# N x 1024 x 7 x 7
x = self.avgpool(x)
# N x 1024 x 1 x 1
x = torch.flatten(x, 1)
# N x 1024
x = self.dropout(x)
x = self.fc(x)
# N x 1000 (num_classes)
return x, aux2, aux1
@torch.jit.unused
def eager_outputs(self, x: Tensor, aux2: Tensor, aux1: Optional[Tensor]) -> GoogLeNetOutputs:
if self.training and self.aux_logits:
return _GoogLeNetOutputs(x, aux2, aux1)
else:
return x # type: ignore[return-value]
def forward(self, x: Tensor) -> GoogLeNetOutputs:
x = self._transform_input(x)
x, aux1, aux2 = self._forward(x)
aux_defined = self.training and self.aux_logits
if torch.jit.is_scripting():
if not aux_defined:
warnings.warn("Scripted GoogleNet always returns GoogleNetOutputs Tuple")
return GoogLeNetOutputs(x, aux2, aux1)
else:
return self.eager_outputs(x, aux2, aux1)
总结
以上就是今天要讲的内容,本文仅仅简单介绍了LeNet、AlexNet、GoogleNet模型的基本网络构成,代码均来自Pytorch官网,图片来自互联网,侵权删。如有错误,请及时指出,我们一起进步!