NiN网络详解
简介
NiN全称为Network in Network,是一种用于图像分类的深度卷积神经网络。它由Min Lin, Qiang Chen和Shuicheng Yan在2013年提出。相比于传统的卷积神经网络,NiN网络采用了一种称为“1x1卷积”的技术,可以大幅度减少网络参数,提高模型的效率和泛化能力。
1x1卷积
1x1卷积是指卷积核大小为1x1的卷积操作。在传统的卷积神经网络中,卷积核的大小通常为3x3或5x5,这使得网络参数数量非常庞大,容易造成过拟合。而1x1卷积可以将通道数进行压缩或扩张,从而减少网络参数。
NiN网络结构
NiN网络的结构如下图所示:
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=1∑CinXi,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 网络。该网络的结构如下:
接下来,我们使用 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网络表现出了很好的性能。