Pytorch 入门(一) 数据预处理

此Pytorch系列文章既是记录我的学习(因为我也是新手),也是教程(其中有一些自己的想法)。
这篇文章会直接上一些干货,对于新手来说,这篇文章只会让你更快的上手写程序,但是如果你要了解更细的东西,还需要继续深入的学习。
这篇文章是按照我的上一篇博客:Demo Task 1中的代码进行讲解,可以对照代码中的datasets.py文件来看这篇博客。(代码可能较烂,明白意思就行哈~O(∩_∩)O)


数据预处理-基本框架

数据预处理主要就是编写Dataset和Dataloader,下面介绍一下数据预处理的基本框架:

from torch.utils import data     #必不可少,引入文件中的函数
class MyData(data.Dataset):
    def __init__(self, parameters, transform=None):  
    	# 定义一些全局变量,用于后面使用
    	# 对于Dataset来说,一般这里是将数据文件的路径指定好
    def __getitem__(self, idx):
   		# 这个Dataset类的输入就是idx,表明第idx个数据
   		# 根据上面的路径,取出路径下的第idx个文件数据
   		# 将图片和标签数据都整理好,最后一起输出return
        return img, label
    def __len__(self):
    	# 这里其实用处不大,这里没使用
        return len(self.image_list)

Datasets 和 Dateloader

对于Deaplearning的程序,首先要进行的就是对数据集的处理,因此就需要使用Dataset和Dataloader:

  • 为什么要使用Dataset和Dataloader?==因为每个人对于数据集的存放方式不一样(比如说,图像分类问题中,你可以是将图像文件分别放在对应名称文件夹中,也可以将每个图像的类别存放在单独的标签文件中,这就造成了存放方式不同),而程序本身并不清楚你是怎么存放的,因此,就需要你自己动手写程序,将你的数据集按照标准的方式存储在程序内存中,以方便每次运行的时候取数据。
  • Dataset和Dataloader是干什么的?==Dataset就是将你的数据集进行处理后,能够一次拿出一个数据(这个数据的标准形式就是:一张图片的数据,后面加上你的标签信息),将Dataset导入到Dataloader后,Dataloader会帮助你取数据(每个batch取多少数据,怎么随机取数据,用多少线程取数据等等都可直接由Dataloader指定)。
  • 既然有标准形式的数据集存放方式,是否有直接的代码将数据集的Dataset和Dataloader整理好?==有的,Pytorch官网中写明了可以直接使用现有的数据集(只不过需要下载),如CIFAR10、COCO和ImageNet等等一些比较有名的数据集。这些官网整理的数据集,就不需要我们手动的修改其Dataset,只需要两行代码(我还没有试过这种方式,以后试过踩过坑之后,可能会在此添加一些坑点):
# 必不可少,添加库
import torchvision
# 使用torchvision.datasets.CIFAR10函数,其中root是下载的路径,train是否是训练集(也可以是测试集),tranform指定自己的tranform(后面会讲),download是否下载
train_set = torchvision.datasets.CIFAR10(root="./", train=True, transform=dataset_transform, download=True)
# 然后再将Dataset导入到Dataloader中,使用DataLoader函数(后面会讲)
train_dataloader = DataLoader(val_dataset, batch_size=cfg.VAL_BATCH_SIZE, shuffle=False, num_workers=cfg.NUM_WORKERS)
Dataset编写

其实每个人的数据集存放方式不同,这个Dataset的编写就会有所不同,为了能够让新手更快的了解怎么编写,我把我之前的人脸关键点检测的部分代码放到这里,进行细致的讲解。
注:代码这种东西,一定要上手自己写,即使写一句错一句,也要坚持自己写出来,要不然你就会一直都不会写。

from torch.utils import data     #必不可少,引入文件中的函数
# 也可以from torch.utils.data import Dataset, DataLoader 引入Dataset和Dataloader
# 这样下面就不用data.Dataset,直接使用Dataset就行了
# 这里你需要看一下我的数据集的存放,我以“./data/train”中的“Annotation和Image”文件夹为例
class MyData(data.Dataset):
    def __init__(self, root_dir, image_dir, label_dir, transform=None):  
    	# 把路径给类的全局变量self.<变量名>就是指在这个类内的全局变量
        self.root_dir = root_dir           # 例:self.root_dir = “./data/train”        
        self.image_dir = image_dir         # 例:self.image_dir = “Images”
        self.label_dir = label_dir         # 例:self.label_dir = “Annotation”
        self.transform = transform
        # 把根路径与图像或标签的路径相连接,os.path.join函数可将后面的参数连接。
        self.label_path = os.path.join(self.root_dir, self.label_dir)     # 例:self.label_path = “./data/train/Annotation”
        self.image_path = os.path.join(self.root_dir, self.image_dir)     # 例:self.label_path = “./data/train/Images”
        # self.image_list存放的就是image_path路径下的所有文件名,是一个列表
        self.image_list = os.listdir(self.image_path)                     # 例: self.image_list[0] = 0.jpg
        self.label_list = os.listdir(self.label_path)                     # 例: self.image_list[0] = 0.txt
        # 按照文件名进行一样的排序,可以保证取出的数据和label是一一对应的,就是按照文件名排序
        # 例:self.image_list[0]和self.label_list[0]是一一对应,分别是0.jpg和0.txt 注:这只是文件名,而没有带着路径
        self.image_list.sort()                                            
        self.label_list.sort()
    def __getitem__(self, idx):
    	# 指定取出的是第idx个图片和标签
    	# 这样在Dataloader中就可以指定idx来取出对应的样本数据(图片+标签)
        img_name = self.image_list[idx]        
        label_name = self.label_list[idx]
        # 直接对应到第idx样本数据的路径
        # 例:img_item_path = “./data/train/Images/[idx].jpg” 标签路径同理
        img_item_path = os.path.join(self.root_dir, self.image_dir, img_name)  
        label_item_path = os.path.join(self.root_dir, self.label_dir, label_name)
        # 打开第idx个图片和标签文件,将其存放在各自的变量中。
        img_item = Image.open(img_item_path) 
        label_txt = np.loadtxt(label_item_path,dtype=int,usecols=range(10), delimiter=',') 
        # 这里我对我的标签数据进行了整理,因为我的图片信息需要改变尺寸大小,因此对应的标签数据也要相应的缩放相同的倍数
        # 这里需要看你们的数据集是否需要这样处理。
        a = label_txt.reshape((5,2))
        label_resize = []
        for x,y in a:
            x_resize = x * (112 / img_item.size[0])
            y_resize = y * (112 / img_item.size[1])
            label_resize.append(int(x_resize))
            label_resize.append(int(y_resize))
        # 这里转了成了numpy格式
        label = np.asarray(label_resize)
        # 为了配合模型的需要,我将图片尺寸全部更改为112*112的大小
        trans_resize = transforms.Resize((112,112))
        img = trans_resize(img_item)
        # 使用transform工具箱进行相应的处理,后续会讲
        if self.transform:
            img = self.transform(img)
        # 这里转成了numpy格式,而不是用tensor格式
        # 你可以使用tensor格式,但是注意需要图片和标签格式对应
        img = np.array(img)
        # 最后将第idx个图片和标签数据,一起return
        return img, label
    def __len__(self):
    	# 这里其实用处不大,这里没使用
        return len(self.image_list)

Dataset的代码就是这样,先把你数据集的路径存放在对应变量中,然后将取出第idx个图片和标签数据,进行一些处理(如resize等),最后将图片和标签数据一起输出return。你可以使用如下代码看看Dataset是什么样的:

my_dataset = MyData(root_dir, image_train, label_train, transform)
# 将第0个数据打开(数据=图片数据+标签数据)
print(my_dataset[0])

最后Dataset应该会显示这样:
在这里插入图片描述
注:这里我使用的numpy的格式,而没有使用tensor格式,你可以使用tensor格式,也就是不用np.array()转格式。

Dataloader编写

Dataloader的编写就没有那么多了,因为Dataloader其实是一个已经封装好的类,我们直接使用,指定几个参数就可以了。Dataloader如下使用(配合上面的Dataset):

# 先写Dataset
my_dataset = MyData(root_dir, image_train, label_train, transform)
# 将Dataset导入到Dataloader中
train_dataloader = DataLoader(my_dataset, batch_size=32, shuffle=True, num_workers=4, drop_last=False)

参数讲解:

  • my_dataset:指定你自己的Dataset。
  • batch_size:指定你的bitch大小,就是说一次会从Dataset中取出几个数据。
  • shuffle:设置为True,就是每次取数据后,都会将数据打乱,不是按照顺序取数据,反之同理。
  • num_work:指定选择多少个线程取数据,一般windows系统中需要设置为0,而linux系统可以逐渐增加,试试看多大最好,可以加快运算速度。
  • drop_last:在最后一次取数据的时候,是否丢弃不够batch_size大小的数据。默认为False,不丢弃。例:共70个数据,batch选择32,那么在第三次取出数据的时候,只有70-32*2=6个数据,drop_last为True时,选择丢弃这6个数据,为False时留下。

Dataloader编写只有这么几行代码,那么数据取出怎么编写,如下(配合上面):

# 可以使用for循环,将Dataloader中数据取出来
# 因为你指定了batch_size为32,所以取数据的时候就是32个图片+32个标签数据一起存放在img和landmark中
for img, landmark in train_dataloader:
# for循环里就可以写模型的运行了

Transforms编写

Transforms其实是一个工具箱,里面有很多的工具我们可以使用,下面我们来看看。
首先是引入from torchvision import transforms
然后看看怎么写,其实在Dataset中已经添加了Transforms:

# 使用transforms的resize功能,可以将图片都指定到112*112的大小
trans_resize = transforms.Resize((112,112))
img = trans_resize(img_item)
# 所有的img都是112*112大小

Transforms也就是这么几行代码,最后我们看看都有那些工具,这些工具我们可以查看transforms的源文件中查看(在pycharm中引入的import中,右键点击transforms选择Go TO --> Declaration or Usages,然后看见transforms继续右键选择,就能看见源文件了),打开Pycharm中的Structure框,能够看到Transforms的所有功能:
在这里插入图片描述
功能很多,我们只简单介绍几个常用的(用法与上面的Resize一样):

  • ToTensor:将数据转换为tensor格式,并将图片每个像素点的数值[0,255]缩放到[0,1]之间,用法:trans_totensor = transforms.ToTensor()
  • Resize:一般将图片转化为指定大小,用法:trans_resize = transforms.Resize((512,512))
  • Normalize:需要输入tensor格式,一般在ToTensor后面使用,将数据归一化,使原本的[0,1]之间的数值转换为[-1,1]之间。可以使用transforms.Normalize(mean=(0.5,0.5,0.5),std=(0.5,0.5,0.5)),公式就是输入x,进行(x-mean)/std后,转换为[-1,1]之间(也可以使用其他数值,一般对不同的数据集可以使用不同的数值,都是大佬们多次实验后得到的最佳数值)
  • RandomCrop:随机裁剪,属于数据增强的功能。用法:transforms.RandomCrop(224)
  • Compose:组合,将transforms的功能自定义的组合在一起,用法如下:
# 可以将各种功能添加进来一起使用
# 如下添加了随机裁剪,随机翻转,tensor转换,归一化
trans_train = transforms.Compose([
        transforms.RandomCrop(32,4),
        transforms.RandomRotation(15),
        transforms.ToTensor(),
        transforms.Normalize(mean = [0.4674, 0.3867, 0.3830],std = [0.1725, 0.1709, 0.1739])
        ])
# 可以将transforms添加进Dataset中,就可以对每张图片数据进行处理了
my_dataset = MyData(root_dir, image_train, label_train, trans_train)
# 最后再放进Dataloader中,就是整个所有的数据集处理了
train_dataloader = DataLoader(my_dataset, batch_size=32, shuffle=True, num_workers=4, drop_last=False)

Transforms还有很多功能,并不能一一列举,如果用到哪个就上网查查参数,然后添加到compose就可以使用了。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值