1、GoogleNet网络结构:(常用基础网络架构,经常拿来做主架构,在进行修改完成自己的需求)

蓝色块:卷积

红色块:池化

黄色:Softmax

绿色:拼接层

构造神经网络时候有些超参数比较难选,例如卷积核应该是3x3还是5x5…。GoogleNet就是把这些这些卷积都用,把结果放在一起,那个卷积核好用,相对于的权重就比较大,其他路线的权重不重要就很小。提供了几种候选,挑选一个最优的。

神经网络 迭代期 批 神经网络inception_pytorch

上图中画框中的是 Inception Module。除了图中的 Inception Module,还有其他种类。例如:

神经网络 迭代期 批 神经网络inception_cnn_02

黄色块是 1x1的卷积核(卷积核的通道取决于输入通道(16)),可以增加或者降低通道数。 (1x1大小的卷积核作用如下描述)

2、卷积神经网络的最大困境就是运算量太大。如下图第一个卷积(192通道—>32通道,卷积核大小5x5),具有进行120422400次计算;通过1x1的卷积核的作用改变通道数量(192—>16),-同通过卷积核大小5x5改变通道(16—>32),虽然模型看上去复杂了,但是实际上运算量(12433648)只有之前的十分之一。就可以继续尝试其他卷积的权重。(有时候称1x1的卷积是神经网络中的神经网络)。1x1的卷积核也可以改变通道数。

神经网络 迭代期 批 神经网络inception_神经网络 迭代期 批_03

3、细化网络结构,最后将结构块拼接在一起,如下图:

神经网络 迭代期 批 神经网络inception_cnn_04

神经网络 迭代期 批 神经网络inception_神经网络 迭代期 批_05

4、上图网络结构中的各个支路的代码为:

import

# 分支1:卷积层构造(输入通道:in_channels,输出通道:24,卷积核大小:1)
self.branch_pool = nn.Conv2d(in_channels, 24, kernel_size=1)
# 分支1:均值池化
branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
# 分支1:对均值池化层得到的层进行卷积
branch_pool = self.branch_pool(branch_pool)

# 分支2:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
self.branch_1x1 = nn.Conv2d(in_channels, 16, kernel_size=1)
branch_1x1 = self.branch_1x1(x)

# 分支3:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
self.branch_5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
# 分支3:卷积层构造(输入通道:16,输出通道:24,卷积核大小:5)
self.branch_5x5_2 = nn.Conv2d(16, 24, kernel_size=5, padding=2)

branch_5x5 = self.branch_5x5_1(x)
branch_5x5 = self.branch_5x5_2(branch_5x5)

# 分支4:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
self.branch_3x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch_3x3_2 = nn.Conv2d(16, 24, kernel_size=3, padding=1)
self.branch_3x3_3 = nn.Conv2d(24, 24, kernel_size=3, padding=1)

branch_3x3 = self.branch_3x3_1(x)
branch_3x3 = self.branch_3x3_2(branch_3x3)
branch_3x3 = self.branch_3x3_3(branch_3x3)

# 将上面4个分支的结果拼接在一起
output = [branch_pool, branch_1x1, branch_5x5, branch_3x3]
return torch.cat(output, dim=1)  # 沿着维度1(dim=1)将output拼接起来(维度:0,1,2,3)

5、将上面代码整合到上一篇博文(八)中,完整代码:

############## GoogleNet网络架构中的 inception module 模块为本次网络架构
import torch
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

batch_size = 64
transform = transforms.Compose([
    transforms.ToTensor(),      # 将PIL格式图像转换成Tensor矩阵向量(维度28x28转换成1x28x28,1:为RGB通道)【 [0...255]--->[0,1] 】
    transforms.Normalize((0.1307, ), (0.3081, ))   # 均一化处理(均值、标准差)
])
# 训练集数据
train_dataset = datasets.MNIST(root='../dataset/mnist/',
                               train=True,
                               download=True,
                               transform=transform)
# 加载训练集数据
train_loader = DataLoader(train_dataset,
                          shuffle=True,
                          batch_size=batch_size)
# 测试集数据集
test_dataset = datasets.MNIST(root='../dataset/mnist/',
                              train=False,
                              download=True,
                              transform=transform)
# 加载测试集数据集
test_loader = DataLoader(test_dataset,
                         shuffle=False,
                         batch_size=batch_size)

class Inception_A(torch.nn.Module):
    def __init__(self, in_channels):
        super(Inception_A, self).__init__()
        # 分支1:卷积层构造(输入通道:in_channels,输出通道:24,卷积核大小:1)
        self.branch_pool = torch.nn.Conv2d(in_channels, 24, kernel_size=1)

        # 分支2:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
        self.branch_1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)

        # 分支3:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
        self.branch_5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        # 分支3:卷积层构造(输入通道:16,输出通道:24,卷积核大小:5)
        self.branch_5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)

        # 分支4:卷积层构造(输入通道:in_channels,输出通道:16,卷积核大小:1)
        self.branch_3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch_3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch_3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)

    # 前向传播
    def forward(self, x):
        # 分支1:均值池化
        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        # 分支1:对均值池化层得到的层进行卷积
        branch_pool = self.branch_pool(branch_pool)
        # 分支2:卷积
        branch_1x1 = self.branch_1x1(x)
        # 分支3:卷积
        branch_5x5 = self.branch_5x5_1(x)
        branch_5x5 = self.branch_5x5_2(branch_5x5)
        # 分支4:卷积
        branch_3x3 = self.branch_3x3_1(x)
        branch_3x3 = self.branch_3x3_2(branch_3x3)
        branch_3x3 = self.branch_3x3_3(branch_3x3)

        output = [branch_pool, branch_1x1, branch_5x5, branch_3x3]
        return torch.cat(output, dim=1)    # 沿着维度1(dim=1)将output拼接起来(维度:0,1,2,3)

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)  # 1个通道-->10个通道
        self.conv2 = torch.nn.Conv2d(88, 20, kernel_size=5) # 经过上面4个分支得到88层,所以设计88层

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

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


    def forward(self, x):
        in_size = x.size(0)
        x = F.relu(self.mp(self.conv1(x)))  # 首先进行conv1(x)卷积,得到10层通道,然后mp最大值池化,relu激活
        x = self.incep1(x)                  # (即Google-Net神经网络后,得到88层)
        x = F.relu(self.mp(self.conv2(x)))  # conv2(x)卷积将88层-->20层,然后mp最大值池化,relu激活
        x = self.incep2(x)                  # 经过Google-Net神经网络后,又得到88层
        x = x.view(in_size, -1)             # 展开变成向量
        x = self.fc(x)                      # 进行全连接,进行分类
        return x


# 想使用模型,就实例化即可,可以直接调用
model = Net()

# 将模型放到GPU上运行,需要加如下两行代码(训练集、测试集中的输入值、实际值也需要加载到GPU上)
# device = torch.device("cude:0" if torch.cuda.is_available() else "cpu")
# model.to(device)

###################3 构建损失函数、优化器###############################
criterion = torch.nn.CrossEntropyLoss()          # 交叉熵损失
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)   # 参数优化

#####################4 循环训练 #########################
def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        # 准备数据(input:输入,target:实际值)
        inputs, target = data
        # 将输入、实际值加载到GPU
        # inputs, target = inputs.to(device), target.to(device)
        # 梯度清0
        optimizer.zero_grad()
        # 前向传播(经过设计的神经网络)
        outputs = model(inputs)
        # 交叉熵损失函数计算
        loss = criterion(outputs, target)
        # 反向传播
        loss.backward()
        # 参数优化
        optimizer.step()
        # 累计loss
        running_loss += loss.item()
        # 数据集一共有batch_idx个数据,每隔300个打印一次平均损失函数值
        if batch_idx % 300 ==299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0

def test():
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            # 将输入、实际值加载到GPU中
            # inputs, target = inputs.to(device), target.to(device)
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
        print('Accuracy on test set: %d %%' % (100 * correct / total))

if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test()

神经网络 迭代期 批 神经网络inception_pytorch_06