《PyTorch深度学习实践》第十讲 卷积神经网络(基础篇 + 高级篇)

news2024/12/24 20:24:12

b站刘二大人《PyTorch深度学习实践》课程第十讲卷积神经网络(基础篇 + 高级篇)笔记与代码:
https://www.bilibili.com/video/BV1Y7411d7Ys?p=10&vd_source=b17f113d28933824d753a0915d5e3a90
https://www.bilibili.com/video/BV1Y7411d7Ys?p=11&vd_source=b17f113d28933824d753a0915d5e3a90


一、卷积神经网络(基础篇)

上一讲中MNIST数据集的例子采用的是全连接神经网络(Fully Connected Neural Nerwork)

image-20230702145633991
  • 所谓的全连接就是网络中使用的全都是线性层,每一个输入节点都要参与到下一层任意一个输出节点的计算上

Convolutional Neural Network

  • 卷积神经网络可保存图像原本的空间结构,从而保留原始的空间信息

    image-20230702150652516
  • 下采样(Subsampling)操作不改变通道数,宽高会减小

  • 卷积 + 下采样 -> 特征提取;全连接层 -> 分类


image-20230702153933038
  • RGB是三个通道
  • patch取了3个通道
  • 图像原点在左上角
  • 卷积之后通道、宽和高都可变

卷积的运算过程:

  • 例子:输入是一个单通道的1 * 5 * 5的图像,卷积核是 3 * 3的

    • 卷积核现在输入中画出一个3*3的区域,然后做数乘,将结果输出

      image-20230702154325397
    • 然后将块往右移一格,输入与卷积核做数乘求和

      image-20230702154533287 image-20230702154554080 image-20230702154705107
    • 以此往复,直至遍历完整个图像

  • 上述例子是单通道的,但实际中常见到的是多通道的

    • 以3通道为例子,每个通道都要配一个卷积核

      image-20230702155034920
    • 每个通道和一个核做卷积,然后将卷积的结果进行相加

      image-20230702155136540 image-20230702155321775 image-20230702155340907
  • N通道:

    image-20230702155653258
    • N个通道,M个输出:
      • 一个卷积核得到一个通道,那么M个卷积核就能得到M个输出,然后再将M个输出拼接起来
    image-20230702155938637
  • 每一个卷积核的通道数和输入通道数一致,卷积核的总个数和输出通道数一致

    image-20230702160152912

卷积层代码:

image-20230702161029819
import torch
in_channels, out_channels = 5, 10   # 输入通道n,输出通道m
width, height = 100, 100            # 图像的宽和高
kernel_size = 3                     # 卷积核大小
batch_size = 1                      # pytorch中所有的输入数据必须是小批量的

# 生成输入数据,这里是随便取一个随机数
input = torch.randn(batch_size,
                    in_channels,
                    width,
                    height)

# 创建卷积层
conv_layer = torch.nn.Conv2d(in_channels,
                             out_channels,
                             kernel_size=kernel_size)

# 得到卷积输出
output = conv_layer(input)

print(input.shape)
print(output.shape)
print(conv_layer.weight.shape)
image-20230702161305065

卷积层中的几个重要参数:

  • 填充padding

    • 想要输出的图像宽高保持不变,那么可以对输入进行填充0

    • 例如padding = 1

      image-20230702162040306 image-20230702162017969
    import torch
    
    # 输入图像
    input = [3, 4, 6, 5, 7,
             2, 4, 6, 8, 2,
             1, 6, 7, 8, 4,
             9, 7, 4, 6, 2,
             3, 7, 5, 4, 1]
    # 将输入转成张量
    input = torch.Tensor(input).view(1, 1, 5, 5)    # 四个参数分别对应batch_size,C,W,H
    
    # 创建卷积层
    conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3, padding=1, bias=False)
    
    # 创建卷积核
    # view用来改变形状,四个参数分别对应输出通道数,输入通道数,宽和高
    kernel = torch.Tensor([1, 2, 3, 4, 5, 6, 7, 8, 9]).view(1, 1, 3, 3)
    
    # 将卷积核数据赋给卷积层的权重,对卷积层的权重进行初始化
    conv_layer.weight.data = kernel.data
    
    output = conv_layer(input)
    
    print(output)
    
    image-20230702163935243 image-20230702163952860
  • 步长stride

    • 遍历步长

      • 例如stride=2,第一次中心在第二行第二列的4,下一次的中心就直接跳到第二行第四列的8
      image-20230702164313997
    • 可以有效降低图像的宽度和高度

      image-20230702164438962
    '''
    和前面padding的代码相比,仅在conv_layer = torch.nn.Conv2d()中将padding换成stride
    '''
    import torch
    
    # 输入图像
    input = [3, 4, 6, 5, 7,
             2, 4, 6, 8, 2,
             1, 6, 7, 8, 4,
             9, 7, 4, 6, 2,
             3, 7, 5, 4, 1]
    # 将输入转成张量
    input = torch.Tensor(input).view(1, 1, 5, 5)    # 四个参数分别对应batch_size,C,W,H
    
    # 创建卷积层
    conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3, stride=2, bias=False)
    
    # 创建卷积核
    # view用来改变形状,四个参数分别对应输出通道数,输入通道数,宽和高
    kernel = torch.Tensor([1, 2, 3, 4, 5, 6, 7, 8, 9]).view(1, 1, 3, 3)
    
    # 将卷积核数据赋给卷积层的权重,对卷积层的权重进行初始化
    conv_layer.weight.data = kernel.data
    
    output = conv_layer(input)
    
    print(output)
    
    image-20230702164726025

下采样 —— 最大池化层(Max Pooling Layer)

image-20230702165319786

例如使用一个2*2的最大池化层,它默认的stride=2,图像是4*4的

这个池化层会将图像按照2*2一组来分,然后将每组中的最大值提取出来拼成一个2*2的输出

操作是在同一个通道内,通道之间不会,因此通道数不会变

import torch

# 输入图像
input = [3, 4, 6, 5,
         2, 4, 6, 8,
         1, 6, 7, 8,
         9, 7, 4, 6]
# 将输入转成张量
input = torch.Tensor(input).view(1, 1, 4, 4)    # 四个参数分别对应batch_size,C,W,H

maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)    # kernel_size被设成2,那么stride会默认为2

output = maxpooling_layer(input)

print(output)
image-20230702170115683

实现一个简单的CNN来处理MNIST数据集:

image-20230702171655520
  • 第一个卷积层的卷积核是5*5的,输入通道为1,输出通道为10
    • 由此可知输出的通道为10,图像大小变成24*24
      • 卷积核是5,那么中心就在第三行第三列,这意味着输入图像少了两圈,那就是要减掉4,即24
    • 因此参数为(batch_size,10,24,24)
  • 上一步输出做一个最大池化,池化层为2*2的
    • 最大池化层是2*2的,那么就是对图像按照2*2一组进行划分然后取每组的最大值出来进行拼接
    • 上一步输出的图像是24*24的,因此经过池化后就变成了12*12的
    • 通道数不影响,即保持不变
    • 即(batch_size,10,12,12)
  • 接下去再加第二个卷积层,卷积核是5*5的,输入通道为10(和池化层输出通道保持一样),输出通道为20
    • 同理得(batch_size,20,8,8)
  • 然后再做一个池化层,2*2的
    • (batch_size,20,4,4)
    • 这一步最大池化处理后一共有320个数据(20*4*4)
  • 最后经过一个全连接层将上一步池化层输出的数据映射成一个向量
image-20230702171959946
  • 添加了ReLU做非线性激活
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(10, 20, kernel_size=5)     # 第二个卷积层
        self.pooling = torch.nn.MaxPool2d(2)                    # 池化层
        self.fc = torch.nn.Linear(320, 10)                      # 线性层

    def forward(self, x):
        # Flatten data from (n, 1, 28, 28) to (n, 320)
        batch_size = x.size(0)
        x = F.relu(self.pooling(self.conv1(x)))     # 先做卷积,再做池化,最后ReLU
        x = F.relu(self.pooling(self.conv2(x)))     # 第二次
        x = x.view(batch_size, -1)                  # 用view将x转成全连接网络所需要的输入形式
        x = self.fc(x)
        return x

model = Net()

完整的代码:

import torch

# 构造Dataloader
from torchvision import transforms  # 用于对图像进行一些处理
from torchvision import datasets
from torch.utils.data import DataLoader

import torch.nn.functional as F     # 使用更流行的激活函数Relu
import torch.optim as optim         # 构造优化器
import matplotlib.pyplot as plt


batch_size = 64

# 存储训练轮数以及对应的accuracy用于绘图
epoch_list = []
acc_list = []

# Compose的实例化
transform = transforms.Compose([
    transforms.ToTensor(),  # 将PIL图像转成Tensor
    transforms.Normalize((0.1307, ), (0.3081, ))  # 归一化。0.1307是均值,0.3081是标准差
])

# 训练集
train_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                               train=True,
                               download=True,
                               transform=transform)  # 读取到某个数据后就直接进行transform处理
train_loader = DataLoader(train_dataset,
                          shuffle=True,
                          batch_size=batch_size)
# 测试集
test_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                              train=False,
                              download=True,
                              transform=transform)
test_loader = DataLoader(train_dataset,
                         shuffle=False,
                         batch_size=batch_size)


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(10, 20, kernel_size=5)     # 第二个卷积层
        self.pooling = torch.nn.MaxPool2d(2)                    # 池化层
        self.fc = torch.nn.Linear(320, 10)                      # 线性层

    def forward(self, x):
        # Flatten data from (n, 1, 28, 28) to (n, 320)
        batch_size = x.size(0)
        x = F.relu(self.pooling(self.conv1(x)))     # 先做卷积,再做池化,最后ReLU
        x = F.relu(self.pooling(self.conv2(x)))     # 第二次
        x = x.view(batch_size, -1)                  # 用view将x转成全连接网络所需要的输入形式
        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):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # inputs输入x,target输出y
        optimizer.zero_grad()

        # forward + backward + update
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()  # loss累加

        # 每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
            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))
    acc_list.append(correct / total)


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


# loss曲线绘制,x轴是epoch,y轴是loss值
plt.plot(epoch_list, acc_list)
plt.ylabel('Accuracy')
plt.xlabel('epoch')
plt.show()
image-20230702180120452 image-20230702180150297 image-20230702180211252

如何使用GPU进行训练:

  • Move Model to GPU
image-20230702174634740
# “cuda:0”表示使用第一块GPU
# if - else表达式:
# 如果当前的cuda可用那么torch.cuda.is_available()=true,则使用gpu,不可用即false,则使用cpu
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 将模型迁移到GPU上
model.to(device)
  • Move Tensor to GPU

    • 将用于计算的张量迁移到GPU,注意要在同一块显卡

    • 训练的时候:

      image-20230702174820581
    • 测试的时候:

      image-20230702174918888
  • 课程中经过10轮训练后准确率从97%提升到98%,从错误率的角度来看是从3%降到了2%,即降低了三分之一

image-20230702175535563

二、卷积神经网络(高级篇)

上一讲中的卷积神经网络以及之前的多层感知机(全连接网络)在结构上都是串行

  • 即上一层的输出是这一层的输入,这一层的输出是下一层的输入
image-20230702211117968

在卷积神经网络中还有更复杂的结构


GoogLeNet

image-20230702211306449
  • 存在相似的结构
    • 减少代码冗余:函数 / 类(封装)

GoogLeNet —— Inception Module的实现

  • 在构造神经网络的时候有一些超参数不好选择,例如卷积核的大小kernel,不好确定什么大小比较好用

    • GoogLeNet的出发点就是如果不知道哪个卷积核好用,那么在一个块中把几种卷积都用上,再将它们的结果放在一起。将来如果3*3的卷积核好用,那么它的权重就会变得比较大,其他卷积核的权重就会相应的变小

      • 提供几种候选的卷积神经网络配置,通过训练自动找到最优的卷积组合
      image-20230702212226205
      • 括号里的数字是输出通道数

      • Concatenate:将张量沿着通道方向拼接到一块

        image-20230702212536416
      • Average Pooling:均值池化,求平均值

        • 通过设置padding和stride来保证输入输出的图像大小一样
      • 1 x 1 Conv

        • 就是1*1的卷积核

        • 1 x 1 Conv的数量取决于输入张量的通道**

          • 融合了不同通道相同位置的信息
            • 例如图中最终输出的正中间的5,是三个通道正中间那个数据(2.5,1.5,1.0)的均值,也没有包含其他位置的信息
          image-20230702213233343 image-20230702213449228
        • 作用:降低计算量

          • 假设输入张量有192个通道,图像大小是28*28,使用5*5的卷积

            • 运算量很大
            image-20230702215314814
          • 使用1 x 1的卷积可以直接改变通道的数量

            • 运算量缩小了10倍
            image-20230702215509147

    代码实现:

    image-20230702220852708
  • 接下去将输出拼接起来(torch.cat())

    image-20230702221003483
image-20230702221031941
class InceptionA(torch.nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()
        self.branch1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)

        self.branch5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)

        self.branch3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)

        self.branch_pool = torch.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)

完整代码:

import torch

# 构造Dataloader
from torchvision import transforms  # 用于对图像进行一些处理
from torchvision import datasets
from torch.utils.data import DataLoader

import torch.nn.functional as F     # 使用更流行的激活函数Relu
import torch.optim as optim         # 构造优化器
import matplotlib.pyplot as plt

batch_size = 64

# 存储训练轮数以及对应的accuracy用于绘图
epoch_list = []
acc_list = []

# Compose的实例化
transform = transforms.Compose([
    transforms.ToTensor(),  # 将PIL图像转成Tensor
    transforms.Normalize((0.1307, ), (0.3081, ))  # 归一化。0.1307是均值,0.3081是标准差
])

# 训练集
train_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                               train=True,
                               download=True,
                               transform=transform)  # 读取到某个数据后就直接进行transform处理
train_loader = DataLoader(train_dataset,
                          shuffle=True,
                          batch_size=batch_size)
# 测试集
test_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                              train=False,
                              download=True,
                              transform=transform)
test_loader = DataLoader(train_dataset,
                         shuffle=False,
                         batch_size=batch_size)


class InceptionA(torch.nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()
        self.branch1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)

        self.branch5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)

        self.branch3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)

        self.branch_pool = torch.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)


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.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):
        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


model = Net()

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)  # 带冲量的梯度下降


# 一轮训练
def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # inputs输入x,target输出y
        optimizer.zero_grad()

        # forward + backward + update
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()  # loss累加

        # 每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
            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))
    acc_list.append(correct / total)


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


# loss曲线绘制,x轴是epoch,y轴是loss值
plt.plot(epoch_list, acc_list)
plt.ylabel('Accuracy')
plt.xlabel('epoch')
plt.show()
image-20230702224917330

残差网络(ResNet)

image-20230702225101699
  • 网络层数增多,性能反而变差

    • 其中一种可能是梯度消失
      • 做反向传播,要用链式法则将一连串的梯度乘起来,假设每一处的梯度都小于1,那么将这些值相乘则会越来越小,使得梯度趋近与零
      • 权重更新公式: w = w − α g w = w-\alpha g w=wαg,梯度 g g g趋于零的时候,权重 w w w就得不到更新了,从而造成离输入比较近的一些块没办法得到充分的训练
  • Residual Net就是为了解决梯度消失的问题

    • 输入 x x x经过两个权重层输出 F ( x ) F(x) F(x),然后这个 F ( x ) F(x) F(x)还要和 x x x进行相加,即输入 x x x经过两层输出结果为 H ( x ) = F ( x ) + x H(x) = F(x) + x H(x)=F(x)+x
    • 有了 x x x的存在,在求梯度的时候即便梯度越来越小,但最终也只是趋近于1
    image-20230702230131870
image-20230702231026055
  • 中间那个跳接是虚线,原因是该块的输入和输出的张量维度不同,需要进行单独处理

代码实现:

image-20230702231246448

Residual Block:

  • 假设两个权重层是卷积神经网络,用的3*3的卷积核
  • 为了保证输出图像大小不变,那么就要将padding设置为1
image-20230702231755778
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))   # 先做第一次卷积,然后relu
        y = self.conv2(y)           # F(x),第二次卷积
        return F.relu(x + y)        # H(x) = F(x) + x
image-20230702232459116
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

完整的代码:

import torch

# 构造Dataloader
from torchvision import transforms  # 用于对图像进行一些处理
from torchvision import datasets
from torch.utils.data import DataLoader

import torch.nn.functional as F     # 使用更流行的激活函数Relu
import torch.optim as optim         # 构造优化器
import matplotlib.pyplot as plt

batch_size = 64

# 存储训练轮数以及对应的accuracy用于绘图
epoch_list = []
acc_list = []

# Compose的实例化
transform = transforms.Compose([
    transforms.ToTensor(),  # 将PIL图像转成Tensor
    transforms.Normalize((0.1307, ), (0.3081, ))  # 归一化。0.1307是均值,0.3081是标准差
])

# 训练集
train_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                               train=True,
                               download=True,
                               transform=transform)  # 读取到某个数据后就直接进行transform处理
train_loader = DataLoader(train_dataset,
                          shuffle=True,
                          batch_size=batch_size)
# 测试集
test_dataset = datasets.MNIST(root='D:/pycharm_workspace/Liuer_lecturer/dataset/mnist',
                              train=False,
                              download=True,
                              transform=transform)
test_loader = DataLoader(train_dataset,
                         shuffle=False,
                         batch_size=batch_size)


# residual block
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))   # 先做第一次卷积,然后relu
        y = self.conv2(y)           # F(x),第二次卷积
        return F.relu(x + y)        # H(x) = F(x) + x


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()

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)  # 带冲量的梯度下降


# 一轮训练
def train(epoch):
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data  # inputs输入x,target输出y
        optimizer.zero_grad()

        # forward + backward + update
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()  # loss累加

        # 每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
            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))
    acc_list.append(correct / total)


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


# loss曲线绘制,x轴是epoch,y轴是loss值
plt.plot(epoch_list, acc_list)
plt.ylabel('Accuracy')
plt.xlabel('epoch')
plt.show()
image-20230702233550664

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构--特殊矩阵的压缩存储

数据结构–特殊矩阵的压缩存储 一维数组的存储结构 ElemType a[10]; //ElemType型一维数组各数组元素大小相同&#xff0c;且物理上连续存放。 数组元素a[i]的存放地址 LOC i * sizeof(ElemType) ( 0 ≤ i < 10 ) (0\le i < 10) (0≤i<10) 注:除非题目特别说明&…

Win10不香了吗?微软 Win11 份额涨至 35.75% 创新高

根据 Valve 公司的最新调查结果&#xff0c;Windows 11 在 Steam 平台上的用户数量正稳步增长&#xff0c;预计将超过36%的比例。Steam定期进行用户软硬件情况调查&#xff0c;旨在提供可靠的数据支持供公司和游戏开发者用于未来的决策制定。此调查选择参与用户的方式是随机的&…

树莓派学习笔记12-安装使用PyZbar

树莓派学习笔记12-安装使用PyZbar 前言 通过树莓派外接USB摄像头&#xff0c;实现条形码的识别&#xff0c;并串口&#xff08;USB串口&#xff09;打印条形码的内容。 前期准备 硬件&#xff1a;树莓派4B 系统&#xff1a;Raspios-2021-03-04 编译器&#xff1a;Thonny 视…

如何基于GeoToolKit/INT实现矢量流线的聚集动画效果示例

继续在上一篇文章的基础上&#xff0c;利用相同的数据处理方法统一了不同年代地层的数据格式&#xff08;目前js解析支持的格式有ZMap、TS、XYZ和XYZA等&#xff09;&#xff0c;本文主要基于GeoToolKit/INT组件&#xff0c;针对地质研究经常在二维等值线基础上模拟计算地层中物…

【C++3】crontab,ftp

文章目录 1.生成数据&#xff1a;crontab2.ftp&#xff1a;ftp是tcp/ip协议族中一员&#xff0c;分客户端和服务端2.1 安装&#xff1a;linux操作系统的用户也是ftp的用户&#xff0c;可以配置专用的ftp用户&#xff0c;专用的ftp用户只能用于ftp&#xff0c;不能登录操作系统2…

ROS2移动机器人导航仿真

环境&#xff1a;Ubuntu 18.04 ros2 eloquent wget http://fishros.com/install -O fishros && . fishros1.安装turtlebot3相关功能包 sudo apt install ros-eloquent-turtlebot3* ref&#xff1a; https://docs.ros.org/ ROS2导航SLAM建图探索_鱼香ROS的博客-CSD…

蛋白组学 代谢组

https://www.cnblogs.com/yanzhi123/p/11712926.htmlhttps://www.cnblogs.com/yanzhi123/p/11712926.html【3】蛋白质组学鉴定软件之Mascot - 简书 (jianshu.com) 【6】蛋白质组学鉴定定量软件之MaxQuant - 简书 (jianshu.com) 基于Maxquant软件处理的LabelFree蛋白质组学 首…

图灵第4期MySQL调优专题学习笔记

目录 一、首先复习打印的课件 二、Explain中的列 三、解读extra 四、索引最佳实践 五、MySQL的内部组结构 2. bin-log归档&#xff1a; 六、常见SQL深入优化 1. order by 与 group by 优化 2. 索引设计原则 3. 分页查询优化&#xff08;根据非主键字段排序的分页查询…

Verilog基础之十三、ROM实现

目录 一、前言 二、非IP核设计 2.1 工程设计文件读取初始化 2.2 测试代码 2.3 仿真结果 三、IP核创建ROM 3.1 IP核生成ROM 3.2 设计代码 3.3 测试代码 3.4 仿真结果 四、modelsim设置 4.1 模拟信号显示 4.2 信号范围显示设置 五、数据文件 一、前言 对于工程中的…

IMX6ULL系统移植篇-uboot启动Log信息

一. 进入uboot 命令模式 只有启动 uboot&#xff0c;进入 uboot的命令行模式时&#xff0c;才能使用 uboot 的命令。 当开发板启动时&#xff0c;快速按下回车键即可进入 uboot命令行模式。这时&#xff0c;进入 uboot 的命令行模式以后输入“help” 或者 “&#xff1f;” &a…

基因遗传进化算法-找最优路径

import random import matplotlib.pyplot as pltplt.rcParams["font.sans-serif"]["SimHei"] #设置字体 plt.rcParams["axes.unicode_minus"]False #该语句解决图像中的“-”负号的乱码问题# 创建初始种群 def create_initial_population():popu…

1024天,CSDN上的时间之旅

1024天&#xff0c;CSDN上的时间之旅 感想收获未来规划职业规划创作规划 感想 今天是在CSDN这个博客上成为博主已经迈入了1024天。这个数字对于计算机领域来说&#xff0c;具有特殊的含义和重要性。 在计算机科学中&#xff0c;1024是2的十次方&#xff0c;也就是2^10。这意味…

rt-thread------串口V1(三)接收

系列文章目录 rt-thread 之 fal移植 rt-thread 之 生成工程模板 STM32------串口理论篇 rt-thread------串口V1版本&#xff08;一&#xff09;配置 rt-thread------串口V1版本&#xff08;二&#xff09;发送篇 文章目录 系列文章目录一、串口的接收中断接收DMA接收 一、串口…

从一次netty分享漫谈

从一次netty分享漫谈 1.前言 上周五&#xff0c;笔者所在的开发小组&#xff0c;组织了一场分享&#xff0c;内容是netty的入门。笔者所在的团队&#xff0c;基本上就是在各条业务线中活蹦乱跳&#xff0c;有经验的看官&#xff0c;到这里已经可以给出分享效果的总体预测&…

Gradle 各个版本下载

每次都要找下载地址&#xff0c;还是记录一下好找点。 http://services.gradle.org/distributions

Unreal 5 官方在Niagara里模拟大型群体笔记

官方视频地址&#xff1a;https://www.bilibili.com/video/BV1FX4y1T7z2/ 如果需要&#xff0c;请查看官方视频。 性能测试 在讲解Niagara之前&#xff0c;视频首先做了一个性能测试&#xff0c;首先放置了100个AI角色&#xff0c;可以想目标角色移动的ai&#xff0c;然后测试…

C语言:猜凶手

题目&#xff1a; 日本某地发生了一件谋杀案&#xff0c;警察通过排查确定杀人凶手必为4个嫌疑犯的一个。 以下为4个嫌疑犯的供词: A说&#xff1a;不是我。 B说&#xff1a;是C。 C说&#xff1a;是D。 D说&#xff1a;C在胡说 已知3个人说了真话&#xff0c;1个人说的是假话。…

山西电力市场日前价格预测【2023-07-03】

日前价格预测 预测明日&#xff08;2023-07-03&#xff09;山西电力市场全天平均日前电价为333.50元/MWh。其中&#xff0c;最高日前电价为398.66元/MWh&#xff0c;预计出现在15: 15。最低日前电价为280.73元/MWh&#xff0c;预计出现在24: 00。 以上预测仅供学习参考&#x…

Spring第一讲:Spring基础概念和环境搭建

一、Spring是什么 Spring 是 Java EE 编程领域的一款轻量级的开源框架&#xff0c;由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立&#xff0c;它的目标就是要简化 Java 企业级应用程序的开发难度和周期。 Spring 自诞生以来备受青睐&#xff0c;一直被广大开发…

二叉树各种函数的实现

如果你觉得迷茫&#xff0c;那就尽可能选择比较困难的路。 目录 前言&#xff1a; &#x1f340;一.通过前序遍历创建二叉树 &#x1f341;二.二叉树的四种遍历 &#x1f342;1.二叉树的前序遍历 &#x1f33c;2.二叉树的中序遍历 &#x1f34c;3.二叉树的后序遍历 …