随着研究论文在全球范围内的涌现,深度学习架构的发展势头日益强劲。这些论文不仅包含了大量的信息,而且为新的深度学习架构的诞生提供了新的思路,但它们往往难以解析。为了理解这些论文,可能需要多次阅读,甚至阅读其他相关的论文。其中,Inception网络就是这些架构之一。
Inception网络是CNN图像分类器发展中的一个重要里程碑。在这种架构之前,大多数流行的CNN或分类器只是通过不断堆叠卷积层来获得更好的性能。而Inception网络则经过了精心设计,非常深且复杂。它使用了许多不同的技术来提升其性能,无论是在速度还是准确性方面。
Inception网络(ResNet)是由Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, 和 Andrew Rabinovich 在他们的论文《Going deeper with convolutions》中于2014年提出的一个著名的深度学习模型。随后,Sergey Ioffe, Christian Szegedy, Jonathon Shlens, Vincent Vanhouck, 和 Zbigniew Wojna 在他们的论文《Rethinking the Inception Architecture for Computer Vision》中于2015年提出了Inception网络的不同版本。Inception模型被归类为流行且广泛使用的深度学习模型之一。
提出一些通用的设计原则和优化技术,对于有效地扩展卷积网络是有益的。在网络架构的早期阶段,避免表示瓶颈。如果网络拥有更多的不同过滤器,它将学习得更快,因为这意味着更多的不同特征图。在低维嵌入上进行空间聚合,即维度降低,不会对表示能力造成太大损失。通过平衡宽度和深度,可以实现网络的最优性能。
Inception模块(原始):近似一个最优的局部稀疏结构,处理不同尺度的视觉/空间信息,然后聚合。这在计算上有些乐观,特别是5×5卷积特别昂贵。
Inception模块(维度降低):维度降低是必要的,并且是动机化的(网络内的网络)。通过1×1卷积实现,可以认为是在深度上学习池化,而不是在高度/宽度上的最大/平均池化。
使用降维的Inception模块,构建了一个深度神经网络架构(Inception v1)。架构如下所示:Inception网络线性堆叠了9个这样的Inception模块。它有22层深(如果包括池化层则是27层)。在最后一个Inception模块的末尾,它使用全局平均池化。
class conv_block(nn.Module):
def __init__(self, in_channels, out_channels, **kwargs):
super(conv_block, self).__init__()
self.relu = nn.ReLU()
self.conv = nn.Conv2d(in_channels, out_channels, **kwargs)
self.batchnorm = nn.BatchNorm2d(out_channels)
def forward(self, x):
return self.relu(self.batchnorm(self.conv(x)))
对于降维和修正线性激活,使用128个过滤器的1×1卷积。修正线性激活与具有1024个单元的全连接层。使用dropout层丢弃70%的输出。使用softmax损失和线性层作为分类器。
以下是Inception网络的流行版本:
Inception架构多次使用具有不同过滤器的CNN块,如1×1、3×3、5×5等,因此让创建一个CNN块的类,它接受输入通道和输出通道以及batchnorm2d和ReLU激活。
class Inception_block(nn.Module):
def __init__(self, in_channels, out_1x1, red_3x3, out_3x3, red_5x5, out_5x5, out_1x1pool):
super(Inception_block, self).__init__()
self.branch1 = conv_block(in_channels, out_1x1, kernel_size=(1, 1))
# ... 省略部分代码 ...
def forward(self, x):
return torch.cat([self.branch1(x), self.branch2(x), self.branch3(x), self.branch4(x)], 1)
让以下面的图像作为参考开始构建网络。
class GoogLeNet(nn.Module):
def __init__(self, aux_logits=True, num_classes=1000):
super(GoogLeNet, self).__init__()
assert aux_logits == True or aux_logits == False
self.aux_logits = aux_logits
# ... 省略部分代码 ...
然后定义一个输出层的类,该类具有dropout=0.7,如论文中所述,以及一个带有softmax的线性层,用于输出n_classes。
class InceptionAux(nn.Module):
def __init__(self, in_channels, num_classes):
super(InceptionAux, self).__init__()
self.relu = nn.ReLU()
self.dropout = nn.Dropout(p=0.7)
self.pool = nn.AvgPool2d(kernel_size=5, stride=3)
self.conv = conv_block(in_channels, 128, kernel_size=1)
self.fc1 = nn.Linear(2048, 1024)
self.fc2 = nn.Linear(1024, num_classes)
def forward(self, x):
x = self.pool(x)
x = self.conv(x)
x = x.reshape(x.shape[0], -1)
x = self.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return x
然后,程序应该按照以下方式对齐:
if __name__ == "__main__":
# N = 3 (Mini batch size)
x = torch.randn(3, 3, 224, 224)
model = GoogLeNet(aux_logits=True, num_classes=1000)
print(model(x)[2].shape)