Pytorch 基于Resnet-18 实战Cifar-10

     前言:这段时间一直在研究深度学习的相关内容,也依据工作需要从github上研究了一些项目,对基础知识有了一定的了解,但是从学习到完全掌握和应用是两回事并且有相当大的一段距离,这里我通过从头开始准备数据,构建网络的,调整参数,整个流程完整跑了一遍,才对之前只知其一不知其二的问题有了更好的理解,这里通过文字分享出来,欢迎指正!再次,强烈建议,如果想认认真真学深度学习,一定要做一次完整的项目,才能收获更大,废话不多说了,言归正传。

一、Resnet网络初探

    Resnet名为残差网络,关于它的由来这里就不多介绍了,有兴趣的可以自行百度,这里主要要说明一下的就是它之所以产生如此优良的效果的最本质的原因:在残差块中,输入可以跨层向前传播。说到这里,还是深度神经网络的问题,网络层数越多,深度越大,就无可避免地在越深的层中丢掉之前的信息,因此,resnet很好地解决了这一问题,输入可以跨层传播,只要保证在最终连接处的输入channels一致即可,Resnet到目前有多种结构resnet18,resnet50,resnet101,这里主要以简单的结构resnet18为例进行分析。

      首先,比较重要的一点是,这些基础的模型,在主流深度学习框架中已经有预训练好的模型,一般可以拿来直接做迁移学习。关于如何做迁移学习,请参考我的另一篇博客,我这里想说的并不是直接拿来做迁移学习,而是利用其网络结构构建自己的训练模型,在CiFar-10中图片大小是32*32,如果直接用resnet18,那么最终得到size为-5 * -5,显然这是不合理,到这里有童鞋就会说了,把图片size调大,不就可以了嘛,但是我建议,最好不要这样做,图片size随意调大,会丢失像素信息影响最终结果,那是不是说不能用这个模型了呢,答案肯定是可以用的,只是需要我们自己重新构建模型,设定kernel_size,stride等这些参数,从而适配我们自己的数据。

二、数据集准备

     首先我们拿到的cifar-10数据集结构是一个train.7z、一个test.7z以及一个trainlabels,这里我们需要整理数据集,我们知道cifar-10一共是10类,那么我们需要按照每一类别划分数据集,这样便于我们使用pytorch中对数据处理的方法,另外,我们需要划分训练集和验证集,验证集的作用主要是便于优选合适的模型以及调整超参数,一般比例为0.1

1.根据标签文件计算每一类别个数与标签字典

def read_label_file(data_dir, label_file, train_dir, valid_ratio):
    with open(os.path.join(data_dir, label_file), 'r')as f:
        lines = f.readlines()[1:]
        tokens = [l.rstrip().split(',') for l in lines]
        idx_label = dict(((int(idx), label) for idx, label in tokens))
        labels = set(idx_label.values())
        n_train_valid = len(os.listdir(os.path.join(data_dir, train_dir)))
        n_train = int(n_train_valid * (1 - valid_ratio))
    assert 0 < n_train < n_train_valid
    return n_train // len(labels), idx_label

2.构建训练与验证数据集

def reorg_train_valid(data_dir, train_dir, input_dir, n_train_per_label, idx_label):
    label_count = {}
    for train_file in os.listdir(os.path.join(data_dir, train_dir)):
        # print(train_file)
        idx = int(train_file.split('.')[0])
        label = idx_label[idx]
        mkdir_if_not_exist([data_dir, input_dir, 'train_valid', label])

        shutil.copy(os.path.join(data_dir, train_dir, train_file),
                    os.path.join(data_dir, input_dir, 'train_valid', label))
        if label not in label_count or label_count[label] < n_train_per_label:
            mkdir_if_not_exist([data_dir, input_dir, 'train', label])
            shutil.copy(os.path.join(data_dir, train_dir, train_file),
                        os.path.join(data_dir, input_dir, 'train', label))
            label_count[label] = label_count.get(label, 0) + 1
        else:
            mkdir_if_not_exist([data_dir, input_dir, 'valid', label])
            shutil.copy(os.path.join(data_dir, train_dir, train_file),
                        os.path.join(data_dir, input_dir, 'valid', label))
def reorg_test(data_dir, test_dir, input_dir):
    mkdir_if_not_exist([data_dir, test_dir, input_dir, 'test', 'unknown'])
    for test_file in os.listdir(os.path.join(data_dir, test_dir)):
        shutil.copy(os.path.join(data_dir, test_dir, test_file), os.path.join(data_dir, input_dir, 'test', 'unknown'))


def reorg_cifar10_data(data_dir, label_file, train_dir, test_dir, input_dir, valid_ratio):
    n_train_per_label, idx_label = read_label_file(data_dir, label_file, train_dir, valid_ratio)
    reorg_train_valid(data_dir, train_dir, input_dir, n_train_per_label, idx_label)
    # reorg_test(data_dir,test_dir,input_dir)

这样就构造好了数据集,目录结构如图

三、数据集预处理

单单准备好了数据还是远远不够的,我们的数据是一系列的图片,我们需要转换为计算机可以读取的数值才能计算,这里需要将图片转化为对应的多维数组,至此,pytorch丰富以及强大的功能就出场了,除此之外,我们还可以利用pytorch进行可视化,以及对图片做剪切、旋转等增强处理

1.利用transform可以对图片做简单的随机剪切,其中 transforms.ToTensor()比较重要,这里强调一下,pytorch中对tensor和numpy有一个转换,主要是为了便于计算

    transform_train = transforms.Compose([transforms.Resize(40), transforms.RandomResizedCrop(32), transforms.ToTensor(),transforms.Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010])])

2.Pytorch中有一个ImageFolder方法能够很好地读取训练数据中文件夹内容

train_ds = ImageFolder(os.path.join(data_dir, input_dir, 'train'), transform=transform_train)

3.利用DataLoader方法就可以按照设定一次读取一定批次的数据了,当然你也可以自己定义数据类,然后利用DataLoader加载数据

train_data = DataLoader(train_ds, batch_size=batch_size, shuffle=True)

四、模型构建

在前面的步骤中,数据已经准备好了,米已经准备好,就等锅烧热了(^_^)

1.构建基础残差块

这里是两个卷积加上一个残差连接卷积层

class Residual(nn.Module):
    def __init__(self,in_channels,num_channels,use_conv11=False,strides=1):
        super(Residual,self).__init__()
        self.conv1=nn.Conv2d(in_channels=in_channels,out_channels=num_channels,kernel_size=3,padding=1,stride=strides)
        self.conv2=nn.Conv2d(num_channels,num_channels,kernel_size=3,padding=1)
        if use_conv11:
            self.conv3=nn.Conv2d(in_channels,num_channels,kernel_size=3,stride=strides,padding=1)
        else:
            self.conv3=None
        self.b1=nn.BatchNorm2d(num_channels)
        self.b2=nn.BatchNorm2d(num_channels)
    def forward(self, input):
        #print(input.shape)

        y=F.relu(self.b1(self.conv1(input)))
        y=self.b2(self.conv2(y))
        if self.conv3:
            input=self.conv3(input)
        #print(y.shape,input.shape)
        return F.relu(y+input)


def resnet18(num_classes, input_channels):
    net = nn.Sequential(
        nn.Conv2d(in_channels=input_channels, out_channels=64, kernel_size=3, stride=1, padding=1),
        nn.BatchNorm2d(64),
        nn.ReLU(),)
    #print(net)

    def resnet_block(input_channel, num_channels, num_residuals, first_block=False):
        blk = nn.Sequential()
        for i in range(num_residuals):
            if i == 0 and not first_block:
                blk.add_module('Residual1', Residual(input_channel, num_channels, use_conv11=True, strides=2))
            else:
                blk.add_module('Residual', Residual(num_channels, num_channels,use_conv11=False))

        return blk

    net.add_module('block1', resnet_block(64, 64, 2, first_block=True))
    net.add_module('block2', resnet_block(64, 128, 2))
    net.add_module('block3', resnet_block(128, 256, 2))
    net.add_module('block4', resnet_block(256, 512, 2))
    net.add_module('pool', nn.AvgPool2d(3))
    net.add_module('dense1', nn.Conv2d(512,num_classes,kernel_size=1))
    #net.add_module('dense1', nn.Linear(120, num_classes))
    return net

整个模型就是首先是一个卷积层,初步提取特征,然后是四个计算块,每个计算块重复残差两次,注意,当strides=2时,表示图像size要除以2,所以整体一共是3个strides为2,下来是32*32、16 * 16、8 *8、4 *4,最后有一个pool层,kernel_size为3因此最后是128 *512 *1 * 1,由于在构建模型时,没有之间构建类,导致debug不方便,在最后一层出了很多问题,这里是需要对维度处理的,要将128 *512 *1 * 1处理成128 *512 才能做nn.Linear,我这里为了简洁直接做了一个卷积

五、训练过程

这里米准备好了,锅也烧热了,可以下锅了

需要注意的是,这里用了gpu训练,所以需要将数据和模型移到GPU上,也很简单

def get_net():
    num_classes=10
    net=resnet18(10,3)
    return net
#Wmodel_fit = models.resnet18(pretrained=True)
model_fit=get_net()
print(model_fit)
#print(help(models))
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
model_fit=model_fit.to(device)
#print(model_fit)
optimizer = optim.SGD(model_fit.parameters(), lr=0.001, momentum=0.9)
print(model_fit.parameters())
criterion = nn.CrossEntropyLoss()

此外,也可以定义学习率自动下降训练

def adjust_learning_rate(optimizer,lr):
    lr=lr*0.1
    for param_group in optimizer.param_groups:
        param_group['lr']=lr

def trainCIFAR(net, train_iter, num_epochs):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        running_loss=0.0
        running_corrects=0
        if epoch>0 and (epoch+1)%80==0:
            adjust_learning_rate(optimizer,0.001)

        if (epoch+1)%2==0:
            torch.save(net, 'model_'+str(epoch)+'.pkl')


        for x ,y in train_iter:
            x=x.to(device)
            #print(x.shape,y.shape)
            y=y.to(device)

            #print(type(x))
            optimizer.zero_grad()
            #print(x,y)
            #print(x.size()

            y_hat=net(x)
            y_hat=y_hat.view(y_hat.size(0),-1)
            #print(y_hat.shape)
            _,pred=torch.max(y_hat,1)
            #print(pred.shape)
            loss=criterion(y_hat,y)
            #print(loss)
            loss.backward()
            optimizer.step()
            running_loss+=loss.item()
            #print(torch.sum(pred==y))
            running_corrects+=torch.sum(pred==y).item()

        epoch_loss=running_loss/(len(train_iter))
        epoch_acc=running_corrects/(batch_size*len(train_iter))
        print(len(train_iter))
        print('epoch_loss',epoch_loss)
        print('epoch_correct',running_corrects,epoch_acc)

至此,整个过程介绍完毕

差不多100个epoch后精度达到92.5%,还可以调整参数修改

本文代码参考了gluon深度学习,不过它的框架是mxnet,地址为http://zh.gluon.ai/chapter_computer-vision/kaggle-gluon-cifar10.html

### 回答1: PyTorchResNet-18CIFAR-10数据集的预训练模型是指在经过大规模的图像数据集上进行预训练后的ResNet-18模型,以便在CIFAR-10数据集上进行更好的图像分类任务。 ResNet-18是一个由18个卷积层和全连接层组成的深度神经网络。预训练模型是指在大规模数据上进行训练得到的模型参数,因此具有更好的泛化性能。CIFAR-10是一个包含10个类别的图像分类数据集,用于在小尺寸图像上进行模型训练和评估。 通过使用预训练的ResNet-18模型,在CIFAR-10数据集上进行图像分类任务时,我们可以利用预训练模型的权重参数来加快训练过程并提高准确率。预训练模型的好处是可以从大规模数据中学习到更多的特征表示,这些特征表示通常具有更高的鉴别性,因此可以更好地捕捉图像的关键特征。 对于CIFAR-10数据集,预训练模型可以有效地缩短训练时间并提高模型的收敛速度,因为在预训练模型中已经包含了对图像的一些共享特征的学习。通过在CIFAR-10数据集上进行微调,即在预训练模型的基础上进行进一步的训练,可以逐步调整模型参数以适应CIFAR-10数据集的特定要求,从而提高最终的图像分类性能。 总而言之,PyTorchResNet-18CIFAR-10的预训练模型是通过在大规模数据上进行训练,在CIFAR-10数据集上进行图像分类任务时使用的预训练模型。这个预训练模型可以帮助提高训练速度和分类准确率,并且在模型训练和微调时起到了重要作用。 ### 回答2: PyTorchResNet-18是一种在CIFAR-10数据集上进行预训练的深度神经网络模型。CIFAR-10是一个包含10个类别的图像分类数据集,包括飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。 ResNet-18是指由18个卷积层和全连接层组成的深度残差网络。该网络的设计思想是通过残差连接(即跳过连接)来解决深度网络中的梯度消失问题,使得网络具有更好的训练效果。这意味着在每个卷积层之后,输入信号可以通过两条路径传递:一条直接连接到后续层,另一条通过卷积操作后再进行连接。这种设计可以使网络更加容易学习输入和输出之间的映射关系。 在CIFAR-10上预训练的ResNet-18模型具有多个优点。首先,这个模型具有较小的参数量和计算复杂度,适合在资源有限的环境下使用。其次,该模型经过在CIFAR-10数据集上的预训练,可以直接用于图像分类任务。通过在CIFAR-10上进行预训练,模型可以学习到一般的图像特征和模式,使其能够更好地泛化到其他类似的图像分类任务中。 通过使用预训练的ResNet-18模型,我们可以利用其已经学到的特征和知识,节省训练时间,并为我们的具体图像分类任务提供一个良好的起点。此外,该模型可以通过微调(fine-tuning)进一步优化,以适应特定任务的需求。 综上所述,PyTorchResNet-18CIFAR-10的预训练模型是一个有价值的工具,可以用于图像分类任务,具有较小的参数量和计算复杂度,预先学习了一般的图像特征和模式,并可以通过微调进一步适应特定任务的需求。 ### 回答3: PyTorch的预训练模型ResNet-18CIFAR-10数据集上表现出色。首先,CIFAR-10是一个包含10个不同类别的图像数据集,每个类别有6000个图像,共计60000个图像。ResNet-18是一个基于深度残差网络的模型,它具有18个卷积层和全连接层。该模型在ImageNet数据集上进行了预训练,其中包含了1000个类别的图像。 当我们将预训练的ResNet-18模型应用于CIFAR-10数据集时,可以得到很好的结果。因为CIFAR-10数据集的图像尺寸较小(32x32),相对于ImageNet数据集中的图像(224x224),所以ResNet-18模型在CIFAR-10上的训练速度更快。此外,ResNet-18模型通过残差连接解决了深度网络中的梯度消失问题,这使得它在CIFAR-10数据集上的表现也非常稳定。 通过使用预训练模型,我们可以通过迁移学习的方式节省训练时间。我们可以先将ResNet-18加载到内存中,然后只需针对CIFAR-10数据集的最后一层或几层进行微调即可。这样可以有效地提高模型在CIFAR-10上的性能。 总之,PyTorch中的预训练模型ResNet-18CIFAR-10数据集上表现优秀。它通过残差连接解决了深度网络中的梯度消失问题,具有较快的训练速度和较好的稳定性。使用预训练模型可以节省训练时间,并通过微调模型的方式进一步提高性能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值