NiN网络详解

37 篇文章 1 订阅
27 篇文章 1 订阅

NiN网络详解

简介

NiN全称为Network in Network,是一种用于图像分类的深度卷积神经网络。它由Min Lin, Qiang Chen和Shuicheng Yan在2013年提出。相比于传统的卷积神经网络,NiN网络采用了一种称为“1x1卷积”的技术,可以大幅度减少网络参数,提高模型的效率和泛化能力。

1x1卷积

1x1卷积是指卷积核大小为1x1的卷积操作。在传统的卷积神经网络中,卷积核的大小通常为3x3或5x5,这使得网络参数数量非常庞大,容易造成过拟合。而1x1卷积可以将通道数进行压缩或扩张,从而减少网络参数。

NiN网络结构

NiN网络的结构如下图所示:

输入
卷积层1
卷积层2
卷积层3
池化层1
随机失活层1
卷积层4
卷积层5
卷积层6
池化层2
随机失活层2
卷积层7
卷积层8
卷积层9
池化层3
随机失活层3
全连接层1
ReLU激活函数
随机失活层4
全连接层2
Softmax输出

NiN网络由卷积层、池化层、全连接层和随机失活层构成。其中,卷积层采用1x1卷积和3x3卷积,池化层采用最大池化,全连接层采用ReLU激活函数。随机失活层可以有效地防止过拟合。

NiN网络实现

我们使用PyTorch实现NiN网络,并使用CIFAR-10数据集进行训练和测试。

导入库

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

数据预处理

transform_train = transforms.Compose([
    transforms.RandomCrop(32, padding=4),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

transform_test = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128,
                                          shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, batch_size=100,
                                         shuffle=False, num_workers=2)

定义NiN网络

class NiN(nn.Module):
    def __init__(self):
        super(NiN, self).__init__()
        self.conv1 = nn.Conv2d(3, 192, kernel_size=5, padding=2)
        self.conv2 = nn.Conv2d(192, 160, kernel_size=1)
        self.conv3 = nn.Conv2d(160, 96, kernel_size=1)
        self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.dropout1 = nn.Dropout2d(p=0.5)
        self.conv4 = nn.Conv2d(96, 192, kernel_size=5, padding=2)
        self.conv5 = nn.Conv2d(192, 192, kernel_size=1)
        self.conv6 = nn.Conv2d(192, 192, kernel_size=1)
        self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.dropout2 = nn.Dropout2d(p=0.5)
        self.conv7 = nn.Conv2d(192, 192, kernel_size=3, padding=1)
        self.conv8 = nn.Conv2d(192, 192, kernel_size=1)
        self.conv9 = nn.Conv2d(192, 10, kernel_size=1)
        self.pool3 = nn.AvgPool2d(kernel_size=8, stride=1)
        
    def forward(self, x):
        x = self.conv1(x)
        x = nn.functional.relu(x)
        x = self.conv2(x)
        x = nn.functional.relu(x)
        x = self.conv3(x)
        x = nn.functional.relu(x)
        x = self.pool1(x)
        x = self.dropout1(x)
        x = self.conv4(x)
        x = nn.functional.relu(x)
        x = self.conv5(x)
        x = nn.functional.relu(x)
        x = self.conv6(x)
        x = nn.functional.relu(x)
        x = self.pool2(x)
        x = self.dropout2(x)
        x = self.conv7(x)
        x = nn.functional.relu(x)
        x = self.conv8(x)
        x = nn.functional.relu(x)
        x = self.conv9(x)
        x = self.pool3(x)
        x = x.view(-1, 10)
        return x

net = NiN()

定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4)

训练网络

for epoch in range(100):
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        inputs, labels = data
        optimizer.zero_grad()
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        if i % 100 == 99:
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 100))
            running_loss = 0.0

测试网络

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

首先,假设我们有一个输入张量 X X X,其形状为 C i n × H × W C_{in}\times H\times W Cin×H×W,其中 C i n C_{in} Cin表示输入通道数, H H H W W W分别表示输入的高度和宽度。我们希望对输入进行卷积操作,得到一个输出张量 Y Y Y,其形状为 C o u t × H × W C_{out}\times H\times W Cout×H×W,其中 C o u t C_{out} Cout表示输出通道数。

传统的卷积操作是使用一个大小为 C o u t × C i n × k × k C_{out}\times C_{in}\times k\times k Cout×Cin×k×k的卷积核对输入进行卷积操作,其中 k k k表示卷积核的大小。但是,NiN引入了1x1卷积,可以使用一个大小为 1 × 1 × C i n × C o u t 1\times 1\times C_{in}\times C_{out} 1×1×Cin×Cout的卷积核来代替传统的卷积操作。

下面,我们来推导1x1卷积的计算过程。假设我们使用一个大小为 1 × 1 × C i n × C o u t 1\times 1\times C_{in}\times C_{out} 1×1×Cin×Cout的卷积核 K K K,对输入张量 X X X进行卷积操作,得到输出张量 Y Y Y。则1x1卷积的计算公式为:

Y i , j , l = ∑ c = 1 C i n X i , j , c K c , l Y_{i,j,l}=\sum_{c=1}^{C_{in}}X_{i,j,c}K_{c,l} Yi,j,l=c=1CinXi,j,cKc,l

其中, i i i j j j分别表示输出张量 Y Y Y的高度和宽度, l l l表示输出张量 Y Y Y的通道数。

上述公式可以用矩阵乘法的形式表示,即:

Y i , j = X i , j W Y_{i,j}=X_{i,j}W Yi,j=Xi,jW

其中, X i , j X_{i,j} Xi,j表示输入张量 X X X ( i , j ) (i,j) (i,j)位置上的特征向量, W W W表示大小为 C i n × C o u t C_{in}\times C_{out} Cin×Cout的卷积核 K K K Y i , j Y_{i,j} Yi,j表示输出张量 Y Y Y ( i , j ) (i,j) (i,j)位置上的特征向量。

这样,我们就可以使用NiN中的1x1卷积对输入进行卷积操作,得到输出张量。
我们首先定义一个输入图片大小为 3 × 32 × 32 3\times 32\times 32 3×32×32,输出类别数为 10 的 NiN 网络。该网络的结构如下:

Input: 3x32x32
Convolution: 192x5x5
Convolution: 160x1x1
Convolution: 96x1x1
Max Pooling: 3x3
Dropout: p=0.5
Convolution: 192x5x5
Convolution: 192x1x1
Convolution: 192x1x1
Max Pooling: 3x3
Dropout: p=0.5
Convolution: 192x3x3
Convolution: 192x1x1
Convolution: 10x1x1
Global Average Pooling
Output: 10

接下来,我们使用 PyTorch 来实现该网络。代码如下:

import torch
import torch.nn as nn
import torch.nn.functional as F

class NiN(nn.Module):
    def __init__(self):
        super(NiN, self).__init__()
        self.conv1 = nn.Conv2d(3, 192, kernel_size=5, padding=2)
        self.conv2 = nn.Conv2d(192, 160, kernel_size=1)
        self.conv3 = nn.Conv2d(160, 96, kernel_size=1)
        self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.dropout1 = nn.Dropout(p=0.5)
        self.conv4 = nn.Conv2d(96, 192, kernel_size=5, padding=2)
        self.conv5 = nn.Conv2d(192, 192, kernel_size=1)
        self.conv6 = nn.Conv2d(192, 192, kernel_size=1)
        self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.dropout2 = nn.Dropout(p=0.5)
        self.conv7 = nn.Conv2d(192, 192, kernel_size=3, padding=1)
        self.conv8 = nn.Conv2d(192, 192, kernel_size=1)
        self.conv9 = nn.Conv2d(192, 10, kernel_size=1)
        self.pool3 = nn.AdaptiveAvgPool2d(output_size=1)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.relu(self.conv3(x))
        x = self.pool1(x)
        x = self.dropout1(x)
        x = F.relu(self.conv4(x))
        x = F.relu(self.conv5(x))
        x = F.relu(self.conv6(x))
        x = self.pool2(x)
        x = self.dropout2(x)
        x = F.relu(self.conv7(x))
        x = F.relu(self.conv8(x))
        x = self.conv9(x)
        x = self.pool3(x)
        x = x.view(x.size(0), -1)
        return x

接下来,我们使用一个随机生成的输入,来计算该网络的输出。代码如下:

net = NiN()
x = torch.randn(1, 3, 32, 32)
y = net(x)
print(y)

运行该代码,输出如下:

tensor([[-0.0495,  0.0198, -0.0152,  0.0597, -0.0159, -0.0469,  0.0025, -0.0185,
         -0.0051, -0.0109]], grad_fn=<ViewBackward>)

可以看到,该网络的输出是一个大小为 1 × 10 1\times 10 1×10 的张量,表示该输入图片在每个类别上的得分。

总结

NiN网络采用了1x1卷积和随机失活层等技术,可以大幅度减少网络参数,提高模型的效率和泛化能力。在图像分类等任务中,NiN网络表现出了很好的性能。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值