pytorch学习-11卷积神经网络(高级篇)

2.线性模型

3.梯度下降算法

4.反向传播(用pytorch算梯度)

5.用pytorch实现线性回归

6.logistic回归

7.处理多维特征的输入

8.加载数据集

9.多分类问题

10.卷积神经网络(基础篇)

11.卷积神经网络(高级篇)_哔哩哔哩_bilibili

11.1 GoogleNet

GoogleNet 其创新的Inception模块而闻名,这种模块允许网络在同一层次中进行不同尺寸的卷积操作,从而捕捉不同尺度的特征。

11.1.2 Inception模块

它的主要思想是在同一层 次中使用不同大小的卷积核 进行特征提取,从而能够捕捉不同尺度的特征。设计神经网络时以模块为单位去组装整个网络结构,在神经网络中多次重复使用该模块,因此将其封装为类,来减少代码量。该模块构成如下图所示:

11.1.3 1*1convolution

1x1卷积是一种在卷积神经网络中使用的特殊类型的卷积操作。它的主要作用是调整 特征图的维度(即通道数 ),同时保持 特征图的空间尺寸(高度和宽度 ),此外,它还能跨通道的特征整合(不同通道的卷积结果最后求和)。例如,数据集为3*3*3经过3*1*1的卷积操作后,输出结果为1*3*3,如下图所示:

1x1卷积的计算量相对较小,因为卷积核的大小为1x1,只需要进行简单的点乘和加法操作。这使得1x1卷积在深层网络中非常有用,可以作为其他卷积操作的降维或升维层,提高整体网络的计算效率。如下图所示:经过1*1卷积后下方操作次数减少许多

11.1.4 Inception模块的实现

课上的Inception模块结构如下:

  • 平均池化路径
    • 3x3平均池化。
    • 1x1卷积降维。
  • 1x1卷积:用于降维。
  • 5x5卷积路径
    • 1x1卷积降维。
    • 5x5卷积提取特征。
  • 3x3卷积路径
    • 1x1卷积降维。
    • 3x3卷积提取特征。
    • 3x3卷积提取特征。

最后再将每条路径所得张量,沿着channel的方向整合成一个张量

代码演示:

复制代码
import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

#1.准备数据
#1.1 定义transform
transform=transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.1307),(0.3081))])

#1.2 下载数据
trainset = datasets.MNIST('../dataset/mnist',
                               train=True,
                               download=True,
                               transform=transform)
#1.3 定义dataloader
train_loader = DataLoader(trainset,batch_size=64,shuffle=True)

#1.4测试集
testset=datasets.MNIST(root='../dataset/mnist',
                       train=False,
                       download=True,
                       transform=transform)
test_loader = DataLoader(testset,batch_size=64,shuffle=False)


#2.定义网络

#2.1 定义inception模块



class Inception(torch.nn.Module):
    def __init__(self,in_channels):
        super(Inception,self).__init__()

        #2.1.1 平均池化路径,所用到的1x1的卷积层
        self.branch_pool = torch.nn.Conv2d(in_channels,24,kernel_size=1)
            #输入通道为in_channels,输出通道为24,卷积核大小为1x1


        #2.1.2 1x1卷积路径,仅用到1x1的卷积层
        self.branch1x1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)
            #输入通道为in_channels,输出通道为16,卷积核大小为1x1

        #2.1.3 5x5卷积路径 ,用到1X1和5x5的卷积层
        self.branch5x5_1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)
            #输入通道为in_channels,输出通道为16,卷积核大小为1x1
        self.branch5x5_2 = torch.nn.Conv2d(16,24,kernel_size=5,padding=2)
            #输入通道为16,输出通道为24,卷积核大小为5x5,padding为2(保证输出尺寸不变)

        #2.1.4 3x3卷积路径,用到1x1、3x3的卷积层、3x3的卷积层
        self.branch3x3_1 = torch.nn.Conv2d(in_channels,16,kernel_size=1)
            #输入通道为in_channels,输出通道为16,卷积核大小为1x1
        self.branch3x3_2 = torch.nn.Conv2d(16,24,kernel_size=3,padding=1)
            #输入通道为16,输出通道为24,卷积核大小为3x3,padding为1(保证输出尺寸不变)
        self.branch3x3_3 = torch.nn.Conv2d(24,24,kernel_size=3,padding=1)
            #输入通道为24,输出通道为24,卷积核大小为3x3,padding为1(保证输出尺寸不变)

    def forward(self,x):
        #2.1.1 平均池化路径,先经过平均池化,再经过1x1卷积
        branch_pool=F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
             #输入为x,池化核大小为3x3,步长为1,padding为1
        branch_pool=self.branch_pool(branch_pool)
            #输入为branch_pool,卷积核大小为1x1,输出通道为24

        #2.1.2 1x1卷积路径,直接经过1x1卷积
        branch1x1=self.branch1x1(x)
            #输入为x,卷积核大小为1x1,输出通道为16

        #2.1.3 5x5卷积路径,先经过1x1卷积,再经过5x5卷积
        branch5x5=self.branch5x5_1(x)
            #输入为x,卷积核大小为1x1,输出通道为16
        branch5x5=self.branch5x5_2(branch5x5)
            #输入为branch5x5,卷积核大小为5x5,输出通道为24,padding为2

        #2.1.4 3x3卷积路径,先经过1x1卷积,再经过3x3卷积,再经过3x3卷积
        branch3x3=self.branch3x3_1(x)
            #输入为x,卷积核大小为1x1,输出通道为16
        branch3x3=self.branch3x3_2(branch3x3)
            #输入为branch3x3,卷积核大小为3x3,输出通道为24,padding为1
        branch3x3=self.branch3x3_3(branch3x3)
            #输入为branch3x3,卷积核大小为3x3,输出通道为24,padding为1

        #2.1.5 将所有路径的输出拼接起来
        outputs=[branch_pool,branch1x1,branch5x5,branch3x3]
        return torch.cat(outputs,dim=1)
            #将outputs中的元素沿着dim=1方向(即通道维度)拼接起来,输出为(batch_size,88,28,28)

#2.2 定义网络结构
class Net(torch.nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.conv1=torch.nn.Conv2d(1,10,kernel_size=5)
        self.conv2=torch.nn.Conv2d(88,20,kernel_size=5)

        self.inception1=Inception(in_channels=10)
        self.inception2=Inception(in_channels=20)

        self.maxpool=torch.nn.MaxPool2d(2)
        self.fc1=torch.nn.Linear(88*4*4,10)

    def forward(self,x):
        #获取batch_size
        in_size=x.size(0)

        x=F.relu(self.maxpool(self.conv1(x)))
        x=self.inception1(x)

        x=F.relu(self.maxpool(self.conv2(x)))
        x=self.inception2(x)

        x=x.view(in_size,-1)
        x=self.fc1(x)
        return x

model=Net()
device = torch.device("cuda: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.训练网络
#4.1 训练函数
def train(epoch):
    running_loss=0.0
    for i,data in enumerate(train_loader,0):
        inputs,labels=data
        inputs,labels=inputs.to(device),labels.to(device)
        optimizer.zero_grad()

        #forward + backward + update
        outputs=model(inputs)
        loss=criterion(outputs,labels)
        loss.backward()
        optimizer.step()
        running_loss+=loss.item()
        if i%300==299:
            print('[%d, %5d] loss: %.3f' %(epoch+1,i+1,running_loss/300))
            running_loss=0.0

#4.2 测试函数
acuracy_list=[]
def Net_test():
    correct=0
    total=0
    with torch.no_grad():
        for data in test_loader:
            inputs,targets=data
            inputs,targets=inputs.to(device),targets.to(device)
            outputs=model(inputs)
            _,predicted=torch.max(outputs.data,1)
            total+=targets.size(0)
            correct+=predicted.eq(targets.data).sum().item()
    print('Accuracy of the network  test : %.2f %%' % (100.0*correct/total))
    acuracy_list.append(100.0*correct/total)


#4.开始训练
for epoch in range(10):
    train(epoch)
    Net_test()

#5.绘制准确率变化图
epochs=list(range(len(acuracy_list)))
plt.plot(epochs,acuracy_list)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Accuracy of the network')
plt.show()

运行结果:

11.2 ResidualNet(ResNet)

11.2.1深度带来的问题

  • 一是vanishing/exploding gradient,导致了训练十分难收敛,这类问题能够通过normalized initialization 和intermediate normalization layers解决;
  • 另一个是被称为degradation的退化现象。对合适的深度模型继续增加层数,模型准确率会下滑(不是overfit造成),training error和test error都会很高,相应的现象在CIFAR-10和ImageNet都有出现。

当梯度小于1时,由于backword()求梯度时遵循链式法则,致使梯度相乘后不断减小最终出现梯度消失,影响所求权重(),使得训练不充分,最终导致准确率下降。下图为深度增加而导致错误率增大

11.2.2 残差结构

普通直连的卷积神经网络和 ResNet 的最大区别在于,ResNet 有很多旁路的支线将输入直接连到后面的层,使得后面的层可以直接学习残差 ,这种结构也被称shortcut connections

传统的卷积层或全连接层在信息传递时,或多或少会存在信息丢失、损耗等问题。ResNet 在某种程度上解决了这个问题,通过直接输入信息 绕道传到输出,保护信息的完整性,整个网络则只需要学习输入、输出差别的那一部分,简化学习目标和难度。注意:实线部分是深度未发生变化的连接,虚线部分是深度发生变化的连接。 对应深度有变化的连接有两种解决方案:

  • 使用 zero-pading 进行提升深度 parameter-free。
  • 使用 1*1的卷积核提升维度 有卷积核的运算时间。

两种方法,使用下面一种方法效果更好,但是运行会更耗时,一般还是更倾向于第一种方案节约运算成本。

11.2.3 残差块

在正常的神经网络中就是一层连一层,如下图所示:

假定某段神经网络的输入是 x,期望输出是 H(x),即 H(x) 是期望的复杂潜在映射,但学习难度大;如果我们直接 把输入 x 传到输出 作为初始结果,通过下图"shortcut connections",那么此时我们需要学习的目标就是 F(x)=H(x)-x,于是 ResNet 相当于将学习目标改变了,不再是学习一个完整的输出,而是最优解 H(X)全等映射 x 的差值,即残差 F(x) = H(x) - x

此时,梯度的计算公式,确保梯度大于等于1,就不会造成梯度消失。

11.2.4 ResNet 结构

Residual Block 实现

值得注意的是,我们需要输入通道和输出通道一致

第一层中先做卷积在做relu(conv1(x)),第二层中做卷积conv2(y),最后返回relu(x+y)

代码演示:

复制代码
import torch
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

#1.准备数据
#1.1 定义transform
transform=transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.1307),(0.3081))])

#1.2 下载数据
trainset = datasets.MNIST('../dataset/mnist',
                               train=True,
                               download=True,
                               transform=transform)
#1.3 定义dataloader
train_loader = DataLoader(trainset,batch_size=64,shuffle=True)

#1.4测试集
testset=datasets.MNIST(root='../dataset/mnist',
                       train=False,
                       download=True,
                       transform=transform)
test_loader = DataLoader(testset,batch_size=64,shuffle=False)


#2.定义网络
#2.1 定义残差块
class ResidualBlock(torch.nn.Module):
    def __init__(self, channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels
        self.conv1 =torch.nn.Conv2d(channels, channels,
        kernel_size=3, padding=1)
        self.conv2 = torch.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)
        # 注:这里的x + y是残差单元的核心,即残差单元的输出等于输入与残差单元输出的和,
        # 其中残差单元输出经过两次卷积后与输入相加,再经过激活函数ReLU。

#2.2 定义网络结构
class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 16, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(16, 32, kernel_size=5)
        self.mp = torch.nn.MaxPool2d(2)
        self.rblock1 = ResidualBlock(16)
        self.rblock2 = ResidualBlock(32)
        self.fc =torch.nn.Linear(512, 10)

    def forward(self, x):
        in_size = x.size(0)
        x = self.mp(F.relu(self.conv1(x)))
        x = self.rblock1(x)
        x = self.mp(F.relu(self.conv2(x)))
        x = self.rblock2(x)
        x = x.view(in_size, -1)
        x = self.fc(x)
        return x

model=Net()
device = torch.device("cuda: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.训练网络
#4.1 训练函数
def train(epoch):
    running_loss=0.0
    for i,data in enumerate(train_loader,0):
        inputs,labels=data
        inputs,labels=inputs.to(device),labels.to(device)
        optimizer.zero_grad()

        #forward + backward + update
        outputs=model(inputs)
        loss=criterion(outputs,labels)
        loss.backward()
        optimizer.step()
        running_loss+=loss.item()
        if i%300==299:
            print('[%d, %5d] loss: %.3f' %(epoch+1,i+1,running_loss/300))
            running_loss=0.0

#4.2 测试函数
acuracy_list=[]
def Net_test():
    correct=0
    total=0
    with torch.no_grad():
        for data in test_loader:
            inputs,targets=data
            inputs,targets=inputs.to(device),targets.to(device)
            outputs=model(inputs)
            _,predicted=torch.max(outputs.data,1)
            total+=targets.size(0)
            correct+=predicted.eq(targets.data).sum().item()
    print('Accuracy of the network  test : %.2f %%' % (100.0*correct/total))
    acuracy_list.append(100.0*correct/total)


#4.开始训练
for epoch in range(10):
    train(epoch)
    Net_test()

#5.绘制准确率变化图
epochs=list(range(len(acuracy_list)))
plt.plot(epochs,acuracy_list)
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Accuracy of the network')
plt.show()

运行结果:

相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习