Pytorch ----- 卷积神经网络 CNN --进阶部分(GoogLeNet、inception、Residual ) 附代码解读+实现~~学习笔记

基础部分在另一篇文章

之前学的卷积神经网络或者其他的 都是线性的,前一层的输出是下一层的输入。像下面这样串起来的。
在这里插入图片描述

而实际情况远比这复杂的多。
下图的网络结构叫 GoogLeNet。
蓝色 卷积层, 红色 池化层, 黄色,softmax ,绿色 是拼接层。
在这里插入图片描述
可以看到图中有很多相同的块,可以封装到一起(叫Inception)到时候调用就行了,不用像上一节一样一层一层写。

inception 内部结构:
注意图是从下到上走的流程。
会自动测试每条路径上的参数的合适情况,赋予不同的权重。
在这里插入图片描述
蓝色的那个块,显然就是在测试卷积核的大小。
Concatenate : 拼接全部路径上的张量 ,变成一个张量。
Average Pooling :均值池化。
黄色的块 1*1的卷积核,主要用于改变通道数量(可大幅降低运算量)。
整个过程中 W和H 不变 ,batch不变,通道可以变。

代码解读:

将上面的图反转过来看,就是从左到右执行。
括号里面的数字表示输出的通道数。
在这里插入图片描述

首先 最上层,第一个分支。
粉色框的 Average polling:
在这里插入图片描述
第一句 一般放在init里,定义了一个卷积对象。
avg.polling对象不用self定义出来,直接调用函数就行。
Pytorch里调用函数, F.avg.pool2d() 。实现粉色框。
然后将pool好的数据给了一开始定义的卷积对象中,得到第一个分支的输出。

第二层,第二个分支,只有一个 1*1的卷积核。
定义好直接放进去算结果就行了。
在这里插入图片描述

在这里插入图片描述

第三层,第三个分支。
在这里插入图片描述
显然是两个卷积层,所以定义两个卷积层,然后放进去X到第一个卷积层里算,得到结果放到第二个里面去算。

padding 的设置,用之前的方法 ,要求输入和输出的通道一样,则padding = 卷积核大小 //2 5 // 2 = 2
在这里插入图片描述
最后一层分支。

在这里插入图片描述

同样定义三个卷积层,分别写上输入和输出的通道。
显然最后面少一行 branch33 = self.baranch33_3(branch3*3)
在这里插入图片描述

最后吧上面四个分支得到的块拼接起来:
由Concatenate负责拼接
在这里插入图片描述
其中 barch1x1…这几个就是上面四个分支算出来的结果。
dim =1 : 张量参数 [b,c,w,h] 沿着 c 拼接。
在这里插入图片描述

上面的代码整合:
在这里插入图片描述

模型类的设计:
在这里插入图片描述
看到前面两个卷积层,第一个卷积层的输出通道和第二个卷积层的输入通道没匹配上。
在这里插入图片描述
这是因为其中inception的问题。在这里插入图片描述
在上面inception的设计中可以发现,四个分支输出通道总和为88个通道,所以第二个卷积层接受的输入值是88通道。
在这里插入图片描述
这里的1408是进入最后一层线性层的输入。10是最后线性层的输出,对应0-9十个数嘛。

可以在进入线性层之前进行测试,看一下当前的x张量的结构, 使用 x.shape输出一下得到结果:torch.Size([64, 88, 4, 4])

第一个是batch 为:64。 所以后面8844(CWH)就是 要输入给线性层的值,一张图像包含1408个元素。

梯度消失: 由于做的是梯度下降-反向传播。在最后更新权重的时候, w = w -α*g , 链式法则求导,吧一连串的 的梯度进行相乘,如果每一个梯度都小于1,则相乘无限趋于0,那么权重则得不到什么更新,

解决梯度消失问题:
下面目的网络结构叫 Residual 网络。
算出结果 H(x)先不做激活,先加一个X再激活。
这里 算出来的结果 h(x) 和 x必须张量维度一样,就是 通道 高度 宽度都一样 ,才能相加。
在这里插入图片描述

Residual 代码:
在这里插入图片描述

这里面 用于创建卷积层的 Conv2d里输入通道和输出通道都是一致的。
后面算的时候就是,第一层先卷积然后激活,放到第二层去卷积但是不激活,让第二层的卷积结果和输入的X相加然后再激活(参考上面的流程图)。

整体结构:
在这里插入图片描述

GoogLeNet 网络 完整代码

import torch
from torch import optim, nn
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F

# 数据准备
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(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)


# inception 模块类
class InceptionA(torch.nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()
        # 第一个分支
        self.branch1_pool = nn.Conv2d(in_channels, 24, kernel_size=1)
        # 第二个分支
        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.branch1x3_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch1x3_2 = nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch1x3_3 = nn.Conv2d(24, 24, kernel_size=3, padding=1)

    def forward(self, x):
        # 处理分支一
        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
        branch_pool = self.branch1_pool(branch_pool)
        # 处理分支二
        branch1x1 = self.branch1x1(x)
        # 处理分支三
        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)
        # 处理分支四
        branch3x3 = self.branch1x3_1(x)
        branch3x3 = self.branch1x3_2(branch3x3)
        branch3x3 = self.branch1x3_3(branch3x3)
        output = [branch1x1, branch5x5, branch3x3, branch_pool]  # 输出通道共88个
        return torch.cat(output, dim=1)


# 训练模型类

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)  # 2*2的最大池化层

        self.fc = nn.Linear(1408, 10)  # 全连接线性层

    def forward(self, x):
        in_size = x.size(0)
        # 卷积池化激活 inception
        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


model = Net()



# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)


def train(epoch):
    runing_loss = 0.0
    for i, data in enumerate(train_loader):
        x, y = data
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    # 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000

    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 10000))


def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。
            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数
            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch + 1, correct / total * 100))


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


得到结果:
这是第 1轮训练,当前损失值 0.044191轮测试结束,当前正确率:96 %
..
..
..
..
..
这是第 10轮训练,当前损失值 0.0030810轮测试结束,当前正确率:98 %


Residual 网络 完整代码:

import torch
from torch import optim, nn
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F

# 数据准备
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(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)


# Residual 网络
class ResidualBlock(nn.Module):
    # 该网络的全程通道数一样
    def __init__(self, channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels
        self.conv1 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(channels, channels, kernel_size=3, padding=1)

    def forward(self, x):
        y = F.relu(self.conv1(x))
        y = self.conv2(y)
        return F.relu(x + y)


# 训练模型类

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=5)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=5)

        # self.incep1 = ResidualBlock(in_channels=10)
        # self.incep2 = ResidualBlock(in_channels=10)

        self.rblock1 = ResidualBlock(16)
        self.rblock2 = ResidualBlock(32)

        self.mp = nn.MaxPool2d(2)  # 2*2的最大池化层

        self.fc = nn.Linear(512, 10)  # 全连接线性层

    def forward(self, x):
        in_size = x.size(0)
        # 卷积池化激活 inception
        x = F.relu(self.mp(self.conv1(x)))
        x = self.rblock1(x)
        x = F.relu(self.mp(self.conv2(x)))
        x = self.rblock2(x)
        # 变换张量为线性向量,然后进全连接层
        x = x.view(in_size, -1)
        x = self.fc(x)
        return x


model = Net()

# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)


def train(epoch):
    runing_loss = 0.0
    for i, data in enumerate(train_loader):
        x, y = data
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    # 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000

    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 10000))


def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。
            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数
            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch + 1, correct / total * 100))


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


得到结果
这是第 1轮训练,当前损失值 0.024681轮测试结束,当前正确率:97 %
.
.
.
这是第 6轮训练,当前损失值 0.003276轮测试结束,当前正确率:99 %
这是第 7轮训练,当前损失值 0.002927轮测试结束,当前正确率:98 %
.
.
.
这是第 9轮训练,当前损失值 0.002289轮测试结束,当前正确率:99 %
这是第 10轮训练,当前损失值 0.0020610轮测试结束,当前正确率:98 %

可以看到结果中间有几次的准确率达到了99% ,所以不是训练越多越好,可能过拟合。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值