ResNet18迁移学习CIFAR10分类任务(附python代码)

  • 实验项目名称:ResNet18迁移学习CIFAR10分类任务

  • 实验目的:利用卷积神经网络ResNet18对CIFAR10数据集进行学习与测试,使网络能够完成高准确率的分类任务,最后爬取网页图片进行实际测试。

  • 实验原理:
  • ResNet网络介绍
  • 深度残差网络(Deep residual network, ResNet)由何恺明等人于2015年首次提出,于2016年获得CVPR best paper。

    GoogLeNet,VGG等神经网络随着网络深度不断提升,会出现梯度消失,梯度爆炸以及网络退化等问题。ResNet提出残差结构模块并使用Batch Normalization。

    Batch Normalization的目的就是使feature map满足均值为0,方差为1的分布规律。批归一化层位于连接层后,非线性激活函数之前,可以有效解决梯度消失与梯度爆炸的问题。

    残差结构使用了shortcut连接方式,人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系,其原理图如图3.1.1所示。映射x→H(x)难以学习,通过使H(x)=F(x)+x,F(x)就是输入与输出之间的残差,将学习任务转变为学习映射x→F(x)。

    ResNet在上百层都有很好的表现,但是当达到上千层了之后仍然会出现退化现象。

  • 损失函数
  • 交叉熵损失(cross entropy):用来判定实际的输出与期望的输出的接近程度。其数学计算公式如下,其中概率分布p为期望输出,概率分布q为实际输出。

    由于希望输出为one-hot向量,所以计算可简化为:


  • 图像预处理
  • CIFAR10数据集共10个类别(飞机,小轿车,鸟,猫,鹿,狗,青蛙,马,船,卡车),图片为3*32*32大小,共50000张训练图片以及10000张测试图片。由于ResNet18网络input为3*224*224的图片,故利用torchvision的transforms模块将3*32*32的原图像resize成3*224*224。


  • 实验内容
  • 训练集,验证集,测试集
  • 将CIFAR10数据集的测试样本进行1:1随机划分出验证集与测试集,从而训练集50000张图片,验证集,测试集各5000张图片,训练集:验证集:测试集=10:1:1。

  • 利用50000张训练样本进行首轮训练
  • 超参数设计如下表:

    超参

    epoch

    12

    batch_size

    32

    learning rate

    0.001

    优化

    随机梯度下降

    Momentum

    0.9

  • loss变化情况
  • train_acc变化情况

     验证集正确率变化情况

    验证集正确率可达94.78%。 笔者后续进行了调参处理进行进一步优化训练,验证集正确率可达95.7%并无过拟合现象,但验证集准确率高并不意味着模型就更优,故在此不多加赘述。

  • 实验结果
  • 测试集测试模型效果

 

爬取网图进行识别任务

 利用requests爬取狗,猫,鸟三类图片,随机选取部分图片进行测试,基本准确分类出图片类别。列举的三张图分别是狗的侧脸照,猫信息较少图片噪声大的照片以及与鸟特征相对差异较大的鹦鹉照片,得出分类结果均正确,故分类器分类效果泛化能力较强。

  • Python代码实现
  • download dataset.py
    import torch
    import torchvision
    import torchvision.transforms as transforms
    import matplotlib.pyplot as plt
    import numpy as np
    
    transform = transforms.Compose(
        [transforms.ToTensor(),
         transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    
    trainset = torchvision.datasets.CIFAR10(root='./dataset/train', train=True,download=True, transform=transform)
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=2)
    
    testset = torchvision.datasets.CIFAR10(root='./dataset/test', train=False,download=True, transform=transform)
    testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=2)
    
    classes = ('plane', 'car', 'bird', 'cat',
               'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
    
    def imshow(img):
        img = img / 2 + 0.5
        npimg = img.numpy()
        plt.imshow(np.transpose(npimg, (1, 2, 0)))
        plt.show()
    
    
    dataiter = iter(trainloader)
    images, labels = dataiter.next()
    
    imshow(torchvision.utils.make_grid(images))
    print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

  • train.py
    import torch.optim as optim
    import torch.nn as nn
    import torch
    import torchvision.transforms as transforms
    import torchvision
    import os
    import scipy.io as scio
    import numpy as np
    import time
    from torchvision import models
    
    os.environ["CUDA_VISIBLE_DEVICES"] = "0"
    transform = transforms.Compose(
        [transforms.RandomResizedCrop(224),
            transforms.ToTensor(),
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    trainset = torchvision.datasets.CIFAR10(root='./dataset/train', train=True, download=False, transform=transform)
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True, num_workers=2)
    
    
    net = models.resnet18(pretrained=True)    
    inchannel = net.fc.in_features
    net.fc = nn.Linear(inchannel, 10)
    print(net)
    net.cuda()
    
    
    resume="./ResNet/model/lr_0.001epoch_11_iters_1499.model"
    if resume is not None:
        print('Resuming, initializing using weight from {}.'.format(resume))
        net.load_state_dict(torch.load(resume))
    
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
    lr = 0.001
    
    all_loss = []
    all_acc = []
    all_train_acc = []
    print("start training.....")
    for epoch in range(12):
        net.train()
        running_loss = 0.0
        train_acc = 0.0
        for i, data in enumerate(trainloader, 0):
            inputs, labels = data
            inputs = inputs.cuda()
            labels = labels.cuda()
    
            optimizer.zero_grad()
    
            outputs = net(inputs)
    
            loss = criterion(outputs, labels)
    
            loss.backward()
            optimizer.step()
    
            running_loss += loss.item()
    
            train_acc += (outputs.argmax(1) == labels).sum().item()
            if (i + 1) % 500 == 0:
                print('[%d, %5d] loss: %.5f train_acc: %2f' %
                      (epoch + 1, i + 1, running_loss / 500, train_acc / 500 / 32))
                all_loss.append(running_loss / 500)
                all_train_acc.append(train_acc)
                running_loss = 0.0
                train_acc = 0.0
    
                # save model
                net.eval()
                net.cpu()
                save_model_dir = "./ResNet/model"
                if os.path.exists(save_model_dir) is False:
                    os.mkdir(save_model_dir)
                save_model_filename = "lr_" + str(lr) + "epoch_" + str(epoch) + "_iters_" + str(i) + ".model"
                save_model_path = os.path.join(save_model_dir, save_model_filename)
                torch.save(net.state_dict(), save_model_path)
                # save loss
                all_loss_total = np.array(all_loss)
                save_loss_dir = "./ResNet/loss"
                if os.path.exists(save_loss_dir) is False:
                    os.mkdir(save_loss_dir)
                loss_filename_path = "lr_" + str(lr) + "epoch_" + str(epoch) + "_iters_" + str(i) + ".mat"
                save_loss_path = os.path.join(save_loss_dir, loss_filename_path)
                scio.savemat(save_loss_path, {'loss_total': all_loss_total})
                # save train_acc
                all_train_acc_total = np.array(all_train_acc)
                save_train_acc_dir = "./ResNet/train_acc"
                if os.path.exists(save_train_acc_dir) is False:
                    os.mkdir(save_train_acc_dir)
                train_acc_filename_path = "lr_" + str(lr) + "epoch_" + str(epoch) + "_iters_" + str(i) + ".mat"
                save_train_acc_path = os.path.join(save_train_acc_dir, train_acc_filename_path)
                scio.savemat(save_train_acc_path, {'train_acc_total': all_train_acc_total})
                net.train()
                net.cuda()
                print("time:{}".format(time.ctime()))
    
    net.eval()
    net.cpu()
    # save final model
    save_model_dir = "./ResNet/model"
    save_model_filename = "lr_" + str(lr) + "epochs_" + str(epoch + 1) + "final.model"
    save_model_path = os.path.join(save_model_dir, save_model_filename)
    torch.save(net.state_dict(), save_model_path)
    
    print('Finished Training')
  • val.py
    import torch
    import torchvision
    import torch.nn as nn
    import torchvision.transforms as transforms
    import matplotlib.pyplot as plt
    import numpy as np
    from torchvision import models
    import glob
    import os
    import scipy.io as scio
    
    def imshow(img):
        img = img / 2 + 0.5
        npimg = img.numpy()
        plt.imshow(np.transpose(npimg, (1, 2, 0)))
        plt.show()
    transform = transforms.Compose(
        [transforms.Resize(256),
         transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    classes = ('plane', 'car', 'bird', 'cat',
               'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
    testset = torchvision.datasets.CIFAR10(root='./dataset/test', train=False,download=False, transform=transform)
    testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=2)
    
    all_acc = []
    num = 0
    net = models.resnet18()
    inchannel = net.fc.in_features
    net.fc = nn.Linear(inchannel, 10)
    #net.load_state_dict(torch.load("./ResNet/model/lr_0.001epoch_22_iters_499.model"))
    path = "./ResNet/model"
    for model_path in glob.glob(path+'/*.model'):
        num += 1
        net.load_state_dict(torch.load(model_path))
    
    
        correct = 0
        total = 0
        with torch.no_grad():
            for i, data in enumerate(testloader, 0):
                if i < 1250:
                    images, labels = data
                    images = images.cuda()
                    labels = labels.cuda()
                    net.eval()
                    net = net.cuda()
                    outputs = net(images)
                    _, predicted = torch.max(outputs.data, 1)
                    total += labels.size(0)
                    correct += (predicted == labels).sum().item()
    
        print('Accuracy of the network on the 5000 test images: %d %%' % (
            100 * correct / total))
        # save_acc
        acc = correct / total
        all_acc.append(acc)
        all_acc_total = np.array(all_acc)
        save_acc_dir = "./ResNet/val_acc"
        if os.path.exists(save_acc_dir) is False:
            os.mkdir(save_acc_dir)
        acc_filename_path = "num_" + str(num) + ".mat"
        save_acc_path = os.path.join(save_acc_dir, acc_filename_path)
        scio.savemat(save_acc_path, {'val_acc_total': all_acc_total})

  • visualization.py
    import scipy.io as scio
    import matplotlib.pyplot as plt
    loss_path = ".\loss\lr_0.0001epoch_11_iters_1499.mat"
    data = scio.loadmat(loss_path)
    loss_data = data["loss_total"]
    x = range(len(loss_data[0]))
    y = loss_data[0]
    plt.ylabel('loss')
    plt.xlabel('times')
    plt.plot(x, y, '.-')
    plt.grid()

 如有错误的地方,还请指正!有疑惑的同学可以在评论区留言,无论能否解决,笔者看到一定回复。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
以下是一个使用PyTorch实现的迁移学习示例代码,使用的是预训练的ResNet-18模型,用于对CIFAR-10数据集进行分: ``` import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader # 定义模型 class TransferModel(nn.Module): def __init__(self, num_classes): super(TransferModel, self).__init__() self.resnet = torchvision.models.resnet18(pretrained=True) self.resnet.fc = nn.Linear(512, num_classes) def forward(self, x): output = self.resnet(x) return output # 数据预处理 transform_train = transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) ]) transform_test = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)) ]) # 加载数据集 trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) trainloader = DataLoader(trainset, batch_size=32, shuffle=True, num_workers=4) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) testloader = DataLoader(testset, batch_size=32, shuffle=False, num_workers=4) # 定义模型和损失函数 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") num_classes = 10 model = TransferModel(num_classes).to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 训练模型 num_epochs = 10 for epoch in range(num_epochs): running_loss = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() if i % 200 == 199: print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 200)) running_loss = 0.0 print('Finished Training') # 评估模型 correct = 0 total = 0 with torch.no_grad(): for data in testloader: images, labels = data images, labels = images.to(device), labels.to(device) outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Accuracy of the network on the test images: %d %%' % ( 100 * correct / total)) ``` 这个示例代码中使用了预训练的ResNet-18模型,将其最后一层全连接层替换为一个新的全连接层,来适应CIFAR-10数据集的10别。然后通过训练这个新模型来完成迁移学习
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我还能再学!

学到就是赚到!

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

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

打赏作者

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

抵扣说明:

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

余额充值