• 深度学习案例分享 | PyTorch 实现 Fashion-MNIST 服装数据分类


    原文链接

    大家好,我是小寒。

    我们在上一篇文章分享了如何进行手写数字识别

    今天我们使用 LeNet-5 (它是最早发布的卷积神经⽹络之⼀)来实现 Fashion-MNIST 服装图⽚的分类任务。

    读取数据集

    Fashion-MNIST 数据集中的图像是一个 28*28 的灰度图像。我们通过 pytorch 的内置函数将 FashionMNIST 下载并读到内存中。

    # 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式,
    # 并除以255使得所有像素的数值均在0到1之间
    trans = transforms.ToTensor()
    mnist_train = torchvision.datasets.FashionMNIST(
                root="../data", train=True, transform=trans, download=True)
    mnist_test = torchvision.datasets.FashionMNIST(
                root="../data", train=False, transform=trans, download=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Fashion-MNIST 由10个类别的图像组成,每个类别由训练数据集(train dataset)中的6000张图像和测试数据集(test dataset)中的1000张图像组成。

    len(mnist_train), len(mnist_test)
    
    • 1
    60000,10000
    
    • 1

    可视化数据集

    我们来显示看一下数据集中的图像样本是什么样的。

    通过如下方式,我们来可视化的展示训练集中前几个样本。

    # 显示数据集
    mnist_show = torchvision.datasets.FashionMNIST(root="../data", train=True, transform=torchvision.transforms.ToTensor(), download=True)
    images, label = next(iter(data.DataLoader(mnist_show, 20, shuffle=True)))
    images=images.reshape(20,28,28)
    fig,axes = plt.subplots(2,10,figsize=(15,3))  
    axes = axes.flatten()   
    for i, (ax, img) in enumerate(zip(axes, images)):
        if torch.is_tensor(img):
            ax.imshow(img.numpy())
        else:
            # PIL图⽚
            ax.imshow(img)
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LSadFPKF-1661500243513)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4a0a45acea2f4f31b063cea5128d9b81~tplv-k3u1fbpfcp-zoom-1.image)]

    LeNet-5 网络结构

    总体来看,LeNet-5 由两个部分组成:

    • 两个卷积层。
    • 三个全连接层。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vzvV2Yz-1661500243515)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/36120bab58b745bbbbb03d9bbf012992~tplv-k3u1fbpfcp-zoom-1.image)]

    每个卷积块中的基本单元是⼀个卷积层、⼀个 sigmoid 激活函数和平均汇聚层。

    每个卷积层使⽤ 5 × 5 卷积核和⼀个 sigmoid 激活函数。

    第⼀卷积层有 6 个输出通道,⽽第⼆个卷积层有16个输出通道。

    每个 2 ×2 的池操作(步幅2)通过空间下采样将维数减少4倍。卷积的输出形状由批量⼤⼩、通道数、⾼度、宽度决定。

    为了将卷积块的输出传递给稠密块,我们必须在⼩批量中展平每个样本。换⾔之,我们将这个四维输⼊转换成全连接层所期望的⼆维输⼊。这⾥的⼆维表⽰的第⼀个维度索引⼩批量中的样本,第⼆个维度给出每个样本的平⾯向量表⽰。

    LeNet-5 的稠密块有三个全连接层,分别有120、84和10个输出。因为我们在执⾏分类任务,所以输出层的10维对应于最后输出结果的数量。

    下面我们看一下网络结构的定义。

    net = nn.Sequential(
            nn.Conv2d(1, 6, kernel_size=5, padding=2), 
            nn.Sigmoid(),
            nn.AvgPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, kernel_size=5), 
            nn.Sigmoid(),
            nn.AvgPool2d(kernel_size=2, stride=2),
            nn.Flatten(),
            nn.Linear(16 * 5 * 5, 120), 
            nn.Sigmoid(),
            nn.Linear(120, 84), 
            nn.Sigmoid(),
            nn.Linear(84, 10))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0prOWTFJ-1661500243516)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a113d7703bbd482eaf89f9365c0a3baf~tplv-k3u1fbpfcp-zoom-1.image)]

    X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
    for layer in net:
        X = layer(X)
        print(layer.__class__.__name__, 'output shape: \t', X.shape)
    
    • 1
    • 2
    • 3
    • 4

    输出如下所示:

    Conv2d output shape: 	 torch.Size([1, 6, 28, 28])
    Sigmoid output shape: 	 torch.Size([1, 6, 28, 28])
    AvgPool2d output shape: 	 torch.Size([1, 6, 14, 14])
    Conv2d output shape: 	 torch.Size([1, 16, 10, 10])
    Sigmoid output shape: 	 torch.Size([1, 16, 10, 10])
    AvgPool2d output shape: 	 torch.Size([1, 16, 5, 5])
    Flatten output shape: 	 torch.Size([1, 400])
    Linear output shape: 	 torch.Size([1, 120])
    Sigmoid output shape: 	 torch.Size([1, 120])
    Linear output shape: 	 torch.Size([1, 84])
    Sigmoid output shape: 	 torch.Size([1, 84])
    Linear output shape: 	 torch.Size([1, 10])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    初始化模型参数

    这里我们使用 Xavier 来进行参数的初始化。

    def init_weights(m):
        if type(m) == nn.Linear or type(m) == nn.Conv2d:
            nn.init.xavier_uniform_(m.weight)
    
    net.apply(init_weights)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    定义损失函数和优化器

    #交叉熵作为损失函数
    loss = nn.CrossEntropyLoss()
    #定义优化器
    optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
    
    • 1
    • 2
    • 3
    • 4

    训练及预测

    让我们看看 LeNet-5 在 Fashion-MNIST 数据集上的表现吧。

    def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
        """⽤GPU训练模型"""
        #初始化权重
        def init_weights(m):
            if type(m) == nn.Linear or type(m) == nn.Conv2d:
                nn.init.xavier_uniform_(m.weight)
        net.apply(init_weights)
        print('training on', device)
    
        #net复制到相应的设备上
        net.to(device)
    
        #定义优化器
        optimizer = torch.optim.SGD(net.parameters(), lr=lr)
        #交叉熵作为损失函数
        loss = nn.CrossEntropyLoss()
        
        num_batches = len(train_iter)
        train_loss=[]
        train_accs=[]
        starttime = datetime.datetime.now()
        for epoch in range(num_epochs):
            #训练损失之和,训练正确数之和,样本数
            metric = Accumulator(3)
            net.train()
            for i, (X, y) in enumerate(train_iter):
                optimizer.zero_grad()
                X, y = X.to(device), y.to(device)
                y_hat = net(X)
                l = loss(y_hat, y)
                l.backward()
                optimizer.step()
                with torch.no_grad():
                    metric.add(l * X.shape[0], accuracy(y_hat, y), X.shape[0])
                #训练损失、训练正确率
                train_l = metric[0] / metric[2]
                train_acc = metric[1] / metric[2]
    
            train_loss.append(train_l)
            train_accs.append(train_acc)
            print(f'ecoch {epoch+1}, loss {train_l:.3f}, train acc {train_acc:.3f}')
    
        endtime = datetime.datetime.now()
        time_second= (endtime - starttime).seconds
        print(f'总耗时 {time_second}')
        show_image(num_epochs, train_loss, train_accs)
    
        test_acc = evaluate_accuracy_gpu(net, test_iter)
        print(f'test acc {test_acc:.3f}')
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print(device)
    lr, num_epochs = 0.5, 50
    train_ch6(net, train_iter, test_iter, num_epochs, lr, device)
    
    • 1
    • 2
    • 3
    • 4

    我们在 GPU 上进行 50 轮的迭代训练,测试集的正确率可以达到89%。

    cuda:0
    training on cuda:0
    ecoch 1, loss 2.320, train acc 0.100
    ecoch 2, loss 2.079, train acc 0.219
    ecoch 3, loss 1.058, train acc 0.589
    ecoch 4, loss 0.859, train acc 0.670
    ecoch 5, loss 0.751, train acc 0.709
    ecoch 6, loss 0.691, train acc 0.729
    ecoch 7, loss 0.648, train acc 0.746
    ecoch 8, loss 0.611, train acc 0.762
    ecoch 9, loss 0.581, train acc 0.777
    ecoch 10, loss 0.556, train acc 0.786
    ecoch 11, loss 0.531, train acc 0.797
    ecoch 12, loss 0.510, train acc 0.806
    ecoch 13, loss 0.491, train acc 0.815
    ecoch 14, loss 0.478, train acc 0.821
    ecoch 15, loss 0.463, train acc 0.827
    ecoch 16, loss 0.452, train acc 0.832
    ecoch 17, loss 0.441, train acc 0.837
    ecoch 18, loss 0.434, train acc 0.839
    ecoch 19, loss 0.425, train acc 0.843
    ecoch 20, loss 0.420, train acc 0.845
    ecoch 21, loss 0.408, train acc 0.851
    ecoch 22, loss 0.403, train acc 0.851
    ecoch 23, loss 0.396, train acc 0.855
    ...
    ecoch 48, loss 0.303, train acc 0.888
    ecoch 49, loss 0.305, train acc 0.886
    ecoch 50, loss 0.300, train acc 0.889
    总耗时 378
    test acc 0.890
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BX3HR0li-1661500243517)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/22e3f0b901f547e6ba9f7b25b4f5c776~tplv-k3u1fbpfcp-zoom-1.image)]

  • 相关阅读:
    【入门-07】GPIO
    k8s容器定时伸缩(CronHPA)
    GitOps 工具 Argo CD 实战教程
    如何编辑图片合成图片?让我们来看看这些合成方法
    Unit 1 开发环境介绍及快速上手
    SpringMVC工作流程
    web框架
    RT-DETR算法优化改进:Backbone改进|RIFormer:无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023
    【Java从入门到精通 08】:面向对象编程(进阶部分)
    我与LBM的句号
  • 原文地址:https://blog.csdn.net/m0_60001307/article/details/126545014