【Pytorch】复杂神经网络(GoogleNet,Inception)
将7x7分解成两个一维的卷积(1x7,7x1),3x3也是一样(1x3,3x1),这样的好处,既可以加速计算,又可以将1个卷积拆成2个卷积,使得网络深度进一步增加,增加了网络的非线性(每增加一层都要进行ReLU)。3x3的卷积核的参数为9,5x5的卷积核参数为25,所以理论上使用3x3的卷积核更好,那么我们能不能找一种特殊的卷积方式,在不改变表达方式的情况下,分解卷积核呢?的作用是减少输入数据的维
一、怎么提升神经网络的性能
要想提升网络的性能,要么增加网络的深度(网络层次的数量),要么提高网络的宽度(神经元的数量)。但是一旦增加网络的深度和宽度就会遇到一些问题。
(1)增加神经元和网络的深度会增加相关的超参数数量,会增加对训练数据集的要求。
(2)计算复杂度很大
(3)梯度弥散问题
所以我们最好的办法就是增加网络深度和宽度的同时,减少超参数的数量。最简单的办法就是将全连接变为稀疏连接。但这种办法因为机器硬件的限制,不能减少计算的时间。
所以我们需要通过聚类的方式,将稀疏矩阵变为较为密集的矩阵提高性能。
因此GoogleNet提出的Inception网络结构就是一种稀疏性,高计算性能的网络结构。
二、Inception V1
最初的Inception结构:
思路就是“稀疏”+”稠密保证既增加了神经元的数量,又减少了运算的时间。
其中1X1的卷积核的作用是减少输入数据的维度。即降低了输入数据的通道数(宽度和高度不变),压缩之后再通过3X3或者5X5的卷积核调整通道数复原。
如输入数据为(1922828)通过1X1的卷积核可以得到(162828)在通过5X5卷积核可以得到(322828)通过计算,可以在这种情况下的运算速度大概是十分之一。
下图是完整的结构:
三、Inception V2
3x3的卷积核的参数为9,5x5的卷积核参数为25,所以理论上使用3x3的卷积核更好,那么我们能不能找一种特殊的卷积方式,在不改变表达方式的情况下,分解卷积核呢?
答案是可以的,经过很多论文和实验证明。可以拆分卷积核。
如下图,一个nxn的卷积核可以拆成1xn的卷积核后接nx1的卷积核。
四、Inception V3
Inception V3一个最重要的改进是分解。将7x7分解成两个一维的卷积(1x7,7x1),3x3也是一样(1x3,3x1),这样的好处,既可以加速计算,又可以将1个卷积拆成2个卷积,使得网络深度进一步增加,增加了网络的非线性(每增加一层都要进行ReLU)。
五、Inception V4
Inception V4研究了Inception模块与残差连接的结合。
六、简单Inception网络的实现
(1)导入包
import torch
from torch import nn
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
import matplotlib.pyplot as plt
(2)准备数据
batch_size = 64
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])
train_dataset = datasets.MNIST(root='dataset/mnist',
train=True,
download=True,
transform=transform)
train_loader = DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_dataset = datasets.MNIST(root='dataset/mnist',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
(3)构建模型
我们要对如下的模型进行建模。
首先,如模型图,我们需要一个平均池化层,4个卷积1x1卷积层,1个5x5卷积层,两个3x3卷积层。这将模型分为四条路线。
所以在__init__方法中,我们将建立:
branch1x1:输入通道待定,输出通道为16,卷积核大小为1。
branch5x5:输入通道待定,输出通道为16,卷积核大小为1。输入通道为16,输出通道为24,卷积核为5,为了保证数据最后能拼接,故步长为2
branch3x3:同理根据图来建立。
branch_pool:一个池化层。
前馈中,将输入分别输入4个层,再将结果通过torch.cat方法拼接在一起,dim=1表示通过通道进行拼接。
class InceptionA(nn.Module):
def __init__(self, in_channels):
super(InceptionA, self).__init__()
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]
return torch.cat(outputs, dim=1)
这样一个inception层就构建完毕了,再结合其他层,我们就能建立一个复杂一点的神经网络。下面对整个神经网络进行建模。
(1)因为MINIST数据是(batch,1,28,28) 的大小,所以现进行一个卷积->池化->激活层,输出为(batch,10,12,12)。
(2)再将数据输入一个inception层(输入通道参数为10),输出结果为(batch,88,8,8)。
(3)再进行一个卷积->池化->激活层,出入为(batch,20,4,4)。
(4)再进行一个inception层(输入通道参数为20),输出结果为(batch,88,4,4)。
(5)将数据进行展开成(batch,1,1408)通过最后一个线性层。
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)
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
其他训练和测试代码与简单神经网络没有差别。
最后可以看到正确率最高达到98.98%比简单神经网络的正确率要高。
更多推荐
所有评论(0)