深度学习笔记——pytorch学习02——自定义数据集与Sequential模块

1Dataset定义

1.1Dataset

自定义Dataset继承torch.utils.data.Dataset类,创建这个类需要实现三个方法:
** __init __**:
主要用于创建对象,在此方法中一般需要传入图片的路径,通过os模块读取所有图片的路径,同时完成对训练、实验、测试集的划分;

#读取所有图片的路径
	imgs=[os.path.join(root,img) for img in os.listdir(root)]

__getitem __:
主要实现对data、label的获取,此方法中通过os模块、PIL模块、numpy模块、transforms模块实现对图片的提取、变形、转化为Tensor数据,同时通过文件名或标签文件提取与图片对应的label,并将两者同步返回.

#提取图片
        image = Image.open(self.imgs[index])
        #图片变换,并转化为Tensor
        data=self.transform(image)
        #返回图片数据和标签
        return data,label

__len __:
主要用于返回训练集的大小,一般直接返回len(imgs)即可

    def __len__(self):
        return len(self.imgs)

1.2Transforms

原始获取的图片大小不统一,神经网络无法处理,且需要对图片进行随机裁剪、旋转等变换提升神经网络的鲁棒性。另外图片传入神经网络需要转换为Tensor数据,同时进行标准化可以增加神经网络的迭代速度。因此在Dataset中需要对图片进行Transforms变换,Transforms的变换可以参考这篇博客。
https://blog.csdn.net/weixin_38533896/article/details/86028509
多个Transforms变换可以通过Transforms.Compose组合起来使用。

self.transform = transforms.Compose(
                    [
                        transforms.Resize(224),
                        transforms.RandomResizedCrop(224),
                        transforms.ToTensor(),
                        transforms.Normalize(mean=[0.485, 0.456, 0.406]
                                             , std=[0.229, 0.224, 0.225])
                    ]
                )

1.3整体代码

####数据集作用:加载本地文件,划分训练集、测试集,读取图片转化为tensor,并获取对应label
import torchvision
import torch
import numpy
from PIL import Image
import os
from torch.utils import data
import torchvision.transforms as transforms
#创建猫狗分类数据集
class DogCat(data.Dataset):
    #目的:获取所有图片的地址,并划分训练、测试、开发集
    def __init__(self,root,transform=None,train=True,test=False):
        #三种集
        #01.test=True测试集
        #02.test=False,train=True训练集
        #03.test=False,train=False验证集

        #三种集对应不同的transforms转换方式
        #测试集从test文件夹读取,训练集和验证集从train文件夹读取,且比例为:7:3
        self.test=test
        #读取所有图片的路径
        imgs=[os.path.join(root,img) for img in os.listdir(root)]

        if(self.test):
            imgs=sorted(imgs,key=lambda x:int(x.split('.')[-2].split('/'[-1])))
        else:
            imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2]))

        imgs_num=len(imgs)

        ##分配测试集、训练集、验证集
        if(self.test):
            self.imgs=imgs
        elif(train):
            self.imgs=imgs[:int(0.7*imgs_num)]
        else:
            self.imgs=imgs[int(0.7*imgs_num):]

        ##设置测试、训练、验证集的变换方式
        if transform==None:
            if(test or not train):
                self.transform=transforms.Compose(
                    [
                        transforms.Resize(224),
                        transforms.CenterCrop(224),
                        transforms.ToTensor(),
                        transforms.Normalize(mean=[0.485,0.456,0.406]
                                             ,std=[0.229,0.224,0.225])
                    ]
                )
            else:
                self.transform = transforms.Compose(
                    [
                        transforms.Resize(224),
                        transforms.RandomResizedCrop(224),
                        transforms.ToTensor(),
                        transforms.Normalize(mean=[0.485, 0.456, 0.406]
                                             , std=[0.229, 0.224, 0.225])
                    ]
                )

    #针对训练、测试、验证集返回不同的label
    #训练、验证集来自train,其格式自带cat,dog标致,提取即可
    #test没有猫狗标志,直接返回其编号值
    def __getitem__(self, index):
        #提取label
        if(self.test):
            label=int(self.imgs[index].split('.')[-2].split('/')[-1])
        else:
            label=1 if 'dog' in self.imgs[index].split('.')[-1] else 0

        #提取图片
        image = Image.open(self.imgs[index])
        #图片变换,并转化为Tensor
        data=self.transform(image)
        #返回图片数据和标签
        return data,label

    def __len__(self):
        return len(self.imgs)

2利用Sequential实现神经网络代码的精简化

神经网络的Conv2d、Pool、BatchNorm2d本质上都是Module的子类,因此神经网络的创建过程实质是Module中嵌套子Module的过程。而Sequential同样是Module的子类,其内部可以存放多个Conv2d、Pool、BatchNorm2d子类对其进行封装。因此可以通过Sequential很方便的对神经网络进行再次封装,一个Sequential内部存放重复的Conv2d→Pool→BatchNorm2d→ReLU操作,然后神经网络本体内部重复调用Sequential即可完成整体的搭建。这种操作可以将神经网络的代码精简,同时也符合神经网络层/模块(ResNet/Incepion/VGG等)的定义,同时对与不同层使用不同训练步长也比较容易实现。测试的demo为使用Sequential实现一个残差块,完成resnet34网络的编写。代码如下图所示:

import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.models as models
#设置残差块
class ResidualBlock(nn.Module):
    def __init__(self,inChannels,outChannels,stride=1,shortcut=None):
        super(ResidualBlock, self).__init__()
        #左侧传输路径
        self.left=nn.Sequential(
            nn.Conv2d(inChannels,outChannels,3,stride,1,bias=False),
            nn.BatchNorm2d(outChannels),
            nn.ReLU(inplace=True),
            nn.Conv2d(outChannels,outChannels,3,1,1,bias=False),
            nn.BatchNorm2d(outChannels))
        #右侧路径
        self.right=shortcut
    def forward(self,x):
            out=self.left(x)
            #如果右侧直连为None则默认自身传过去,反之传入设置的参数
            residual=x if self.right is None else self.right(x)
            out+=residual
            return F.relu(out)
#利用残差块设置残差网络
class ResNet(nn.Module):
    def __init__(self,num_classes=1000):
        super(ResNet, self).__init__()
        #设置前几层
        self.pre=nn.Sequential(
            nn.Conv2d(3,64,7,2,3,bias=False),
            #批标准化,也就是归一化,防止由于神经网络太深而导致的梯度消失和梯度爆炸的问题
            #输入的参数为传入的通道数
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True))
        #设置残差层
        self.layer1=self._make_layer(64,128,3)
        self.layer2 =self._make_layer(128,256,4,stride=2)
        self.layer1=self._make_layer(256,512,6,stride=2)
        self.layer1=self._make_layer(512,512,3,stride=2)
        #全连接层
        self.fc=nn.Linear(512,num_classes)
    def _make_layer(self,inChannels,outChannels,block_num,stride=1):
        shortcut=nn.Sequential(
            #尺寸不变通道数增加
            nn.Conv2d(inChannels,outChannels,1,stride,bias=False),
            nn.BatchNorm2d(outChannels))
        layers=[]
        #一次通道增加残差块
        layers.append(ResidualBlock(inChannels,outChannels,stride,shortcut))
        #多次通道不变残差块
        for i in range(1,block_num):
            layers.append(ResidualBlock(outChannels,outChannels))
        #*表示迭代传入列表中的元素,否则汇报数据类型错误
        return nn.Sequential(*layers)
    def forward(self,x):
        x=self.pre(x)
        x=self.layer1(x)
        x=self.layer2(x)
        x=self.layer3(x)
        x=self.layer4(x)
        x=F.avg_pool2d(x,7)
        x=x.view(x.size(0),-1)
        return self.fc(x)
#自建ResNet
model1=ResNet()
print(model1)
#网络模块
model2=models.resnet34()
print(model2)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值