《PyTorch深度学习实践》11. 卷积神经网络(高级篇)

卷积神经网络(高级篇)

1*1的卷积核

下图是两种网络的构造方式,图二相比于图一多了一个1*1的卷积核层,在长宽不变的情况下减小了通道数,把运算次数减少了一个数量级。

image-20200924112727978

GoogleNet(简化版)

下图为GoogleNet中的Inception模块:

image-20200924210616539

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class InceptionA(nn.Module):
def __init__(self, in_channels):
self.branch1x1 = nn.Conv2d(in_channels, 16, kernel_size=1)

self.branch5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch5x5_2 = nn.Conv2d(16, 24, kernel_size=5, padding=2)

self.branch3x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch3x3_2 = nn.Conv2d(16, 24, kernel_size=3, padding=1)
self.branch3x3_3 = nn.Conv2d(24, 24, kernel_size=3, padding=1)

self.branch_pool = nn.Conv2d(in_channels, 24, kernel_size=1)

def forward(self, x):
branch1x1 = self.branch1x1(x)

branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)

branch3x3 = self.branch3x3_1(x)
branch3x3 = self.branch3x3_2(branch3x3)
branch3x3 = self.branch3x3_3(branch3x3)

branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
branch_pool = self.branch_pool(branch_pool)

outputs = [branch1x1, branch5x5, branch3x3, branch_pool] # 把各个输出根据channel拼起来,共88个channel
return torch.cat(outputs, dim=1)

GoogleNet的一个简化实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(88, 20, kernel_size=5) # 88=24+24+24+16

self.incep1 = InceptionA(in_channels=10)
self.incep2 = InceptionA(in_channels=20)

self.mp = nn.MaxPool2d(2)
self.fc = nn.Linear(1408, 10)

def forward(self, x):
in_size = x.size(0)
x = F.relu(self.mp(self.conv1(x)))
x = self.incep1(x)
x = F.relu(self.mp(self.conv2(x)))
x = self.incep2(x)
x = x.view(in_size, -1)
x = self.fc(x)
return x

梯度消失与Residual Net

随着网络层数的加深,可能会出现梯度消失的问题,更新的时候,接近输入的层参数更新会非常缓慢。对于这种情况的一种解决方案是采用residual net,即在做relu激活前,先把现有的函数输出与原始输入相加(要求长宽和通道数一致),这样得到$H(x)=F(x)+x$的形式,好处是当求导时可以得到这样的形式:$\frac{\partial{H(x)}}{\partial{x}}=\frac{\partial{F(x)}}{\partial{x}}+1$,最小值为一个接近1的数而非很小的小数,相乘时就不会产生梯度消失的问题了。

image-20200925153126823

1
2
3
4
5
6
7
8
9
10
11
12
13
class ResidualBlock(nn.Module):
def __init__(self, channels):
super(ResidualBlock, self).__init__()
self.channels = channels
self.conv1 = nn.Conv2d(channels, channels,
kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(channels, channels,
kernel_size=3, padding=1)

def forward(self, x):
y = F.relu(self.conv1(x))
y = self.conv2(y)
return F.relu(x + y) # 注意是先求和后激活

总体网络构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 16, kernel_size=5)
self.conv2 = nn.Conv2d(16, 32, kernel_size=5)
self.mp = nn.MaxPool2d(2)

self.rblock1 = ResidualBlock(16)
self.rblock2 = ResidualBlock(32)

self.fc = nn.Linear(512, 10)

def forward(self, x):
in_size = x.size(0)
x = self.mp(F.relu(self.conv1(x)))
x = self.rblock1(x)
x = self.mp(F.relu(self.conv2(x)))
x = self.rblock2(x)
x = x.view(in_size, -1)
x = self.fc(x)
return x

在Colab上运行

课程来源:《PyTorch深度学习实践》完结合集