前言
这段时间观看b站up主 刘二大人 的关于深度学习的讲解,让我获益匪浅。让我了解到什么是盗梦空间(Inception),如何将结构嵌入到模型中,下面是视频地址:
https://www.bilibili.com/video/BV1Y7411d7Ys?p=11&share_source=copy_web
感兴趣的朋友,也可将Inception(盗梦空间)结构放入之前mnist数据识别的代码中进行尝试,因为维度改变,全连接层参数相应的也要进行改变。链接如下:
https://blog.youkuaiyun.com/qq_42962681/article/details/116270125?spm=1001.2014.3001.5501
以下是本篇文章正文内容,可供参考,如有不足,恳请大家指正,共同学习。
一、Inception(盗梦空间)是什么?
当你构建卷积层时,你要决定卷积核的大小,如何判别哪种卷积核效果更好?是1x1合适,还是3x3合适,还是5x5合适?要不要添加pooling层?
由于这些参数都是超参数,需要进行手动设置,在实验中逐个尝试的话无疑是一件费时费力的工程,这时通过Inception(盗梦空间)模块可很好的解决相应问题。
使用多个不同的卷积核,同时对输入图片进行处理(保证输出后的图像尺寸不变,只更改其维度),最后对处理的数据进行拼接。
二、使用步骤
1.空间结构
结构如图所示(示例):
图中5x5卷积和3x3卷积,都对图像进行了相应的填充,因此输入图像尺度和输出图像尺度不变。
2.结构模型
代码如下(示例):
代码中的数据集我使用的是常用的MNIST数据集,单个图像样本大小为(1, 28,28)。
盗梦空间代码示意图
import torch
import torch.nn as nn
from torch.utils.data import DataLoader # 我们要加载数据集的
from torchvision import transforms # 数据的原始处理
from torchvision import datasets # pytorch十分贴心的为我们直接准备了这个数据集
import torch.nn.functional as F # 激活函数
import torch.optim as optim
#盗梦空间Inception
#图像输入网络中同时经过4种不同的卷积进行处理,这里保证分别处理后的,w,h都相同,以便后方拼接
#[b, c, w, h],这里b=64直接套用了上文提到的mnist数据集中batch_size的大小,可自由设置。
class InceptionA(torch.nn.Module):
def __init__(self,in_channels):
super(InceptionA,self).__init__()
#[64,1,28,28]-[64,16,28,28] 通过卷积核为一的卷积,图像尺寸不变
self.branch1x1 = nn.Conv2d(in_channels,16,kernel_size=1)
self.branch5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
# [64,16,28,28]-[64,24,28,28] 通过卷积核为5的卷积,因为w,h分别填充了2,所以图像尺寸不变
self.branch5x5_2 = nn.Conv2d(16,24, kernel_size=5,padding=2)
self.branch3x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
# [64,16,28,28]-[64,24,28,28] 通过卷积核为3的卷积,因为填充了1,所以图像尺寸不变
self.branch3x3_2 = nn.Conv2d(16, 24, kernel_size=3, padding=1)
# [64,24,28,28]-[64,24,28,28] 通过卷积核为3的卷积,因为填充了1,所以图像尺寸不变
self.branch3x3_3 = nn.Conv2d(24, 24, kernel_size=3, padding=1)
#[64,1,28,28]-[64,24,28,28]
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]
#[b,c,h,w] dim=1是维度方向拼接 ,这里返回的维度是上述几个维度相加(16+24+24+24=88)
return torch.cat(outputs, dim=1)
模块示意图
通过盗梦空间的图片,只更改其的通道数量,而不更改图像的尺寸大小,因此在维度方向进行拼接,上图拼接后的通道数量为24+16+24+24=88,对应了下面第二层卷积层的输入维度。
将盗梦空间结构嵌入到我们的网络中
# 接下来我们看一下模型是怎么做的
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
# 定义了我们第一个要用到的卷积层,因为图片输入通道为1,第一个参数就是
# 输出的通道为10,kernel_size是卷积核的大小,这里定义的是5x5的
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
# 看懂了上面的定义,下面这个你肯定也能看懂
self.conv2 = torch.nn.Conv2d(88, 20, kernel_size=5)
#调用盗梦空间
self.incep1 = InceptionA(in_channels=10)
self.incep2 = InceptionA(in_channels=20)
# 再定义一个池化层
self.mp = torch.nn.MaxPool2d(2)
# 最后是我们做分类用的线性层
self.fc = torch.nn.Linear(1408, 10)
# 下面就是计算的过程
def forward(self, x):
# Flatten data from (n, 1, 28, 28)
batch_size = x.size(0) # 这里面的0是x大小第1个参数,自动获取batch大小
#(64, 1, 28, 28)经过10组kernel=5的卷积 ((64, 10, 24, 24)),通过最大池化(64, 10, 12, 12)
x = F.relu(self.mp(self.conv1(x)))
# 输入到盗梦空间(64, 10, 12, 12),尺寸不变,维度相加(64, 88, 12, 12)
x = self.incep1(x)
# (64, 88, 12, 12)经过20组kernel=5的卷积 ((64, 20, 8, 8)),通过最大池化(64, 20, 4, 4)
x = F.relu((self.mp(self.conv2(x))))
#输入到盗梦空间(64, 20, 4, 4),尺寸不变,维度相加(64, 88, 4, 4) ioriw
x = self.incep2(x)
# 为了给我们最后一个全连接的线性层用
# 我们要把一个二维的图片(实际上这里已经是处理过的)64X88x4x4张量变成一维的
x = x.view(batch_size, -1) # flatten
#64X88x4x4改变为64x1408
# 经过线性层,确定他是0~9每一个数的概率
#64x1408改变为64x10 因为手写数据集10个分类
x = self.fc(x)
return x
总结
学无止境,越走下去,发现自己不懂的越多,深感渺小,还需继续努力。