Dali工具箱🕶2——Torch 数据集准备 Pipline

"transform to train.txt, Dataset, DataLoader, 预处理"

Posted by fuhao7i on March 12, 2021

0. 生成train.txt

像一个文件夹代表一类的数据集,我们可以将它的文件名读取出来,按照{ 文件名;类别 }的格式将它们保存到train.txt文件中以供我们使用;同理,对于语义分割或目标检测的数据集,我们按照 { img;label } 的格式保存到train.txt文件中。

文件目录:

这里我们先从ImageNet数据集每个类别中随机抽取30%来作为训练集,剩下的70%以后再用。

to_train_txt.py

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
import os, random

_label = {'n01924916':0, 'n01943899':1, 'n01950731':2, 'n01968897':3, 'n02317335':4, 'n02319095':5, 'n02321529':6}

train_path = './train/'

pathDir = os.listdir(train_path)

f = open('./train_3.txt', 'w')
ff = open('./train_7.txt', 'w')

for ii in pathDir:
    if ii != '.DS_Store':
        label = str(_label[ii])
        
        rate = 0.3
        filename = os.listdir(train_path + ii)
        filenumber = len(filename)
        picknumber = int(filenumber * rate)

        sample = random.sample(filename, picknumber)

        for ob in filename:
            if ob in sample:
                f.write(ii + '/' + ob + ';' + label + '\n')
            else:
                ff.write(ii + '/' + ob + ';' + label + '\n')


f.close()
ff.close()

部分train_3.txt展示:

...

n02319095/n02319095_350.JPEG;5
n02319095/n02319095_3527.JPEG;5
n02319095/n02319095_10100.JPEG;5
n02319095/n02319095_583.JPEG;5
n02319095/n02319095_1263.JPEG;5
n02319095/n02319095_8412.JPEG;5
n02319095/n02319095_7026.JPEG;5
n02319095/n02319095_2398.JPEG;5
n02319095/n02319095_7030.JPEG;5
n02319095/n02319095_487.JPEG;5
n02319095/n02319095_6733.JPEG;5
n02319095/n02319095_1019.JPEG;5
n02319095/n02319095_8238.JPEG;5
n02319095/n02319095_4077.JPEG;5
n02319095/n02319095_4630.JPEG;5
n02319095/n02319095_444.JPEG;5
n02319095/n02319095_1936.JPEG;5
n02319095/n02319095_2025.JPEG;5
n02319095/n02319095_956.JPEG;5
n02319095/n02319095_3272.JPEG;5

...

random.sample()函数用法

用于随机截取指定长度的列表,不会改变原列表;

1
2
3
4
5
6
7
list = [0,1,2,3,4]
rs = random.sample(list, 2)
print(rs)
print(list)

# [2, 4] 
# [0, 1, 2, 3, 4]        

PyTorch输入数据Pipline:

  1. 创建一个Dataset对象;
  2. 创建一个DataLoader对象;
  3. 循环这个DataLoader对象,将imglabel加载到模型中进行训练;

1. 创建Dataset对象

创建的时候需继承from torch.utils.data.dataset import Dataset类。

Dataset中主要有3个方法:

  1. init: 初始化信息,包括训练数据和标签的路径, transform信息等;
  2. getitem: 在这个方法里根据传入的下标返回label和transform之后的图片tensor;
  3. len: 返回Dataset的长度;
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
from PIL import Image
import torch
import torchvision.transforms as transforms
from torch.utils.data import Dataset


class MyDataset(Dataset):  # 创类:MyDataset,继承torch.utils.data.Dataset
    def __init__(self, datatxt, transform=None):
        super(MyDataset, self).__init__()
        fh = open(datatxt, 'r')  # 打开txt,读取内容
        data = []
        for line in fh:  # 按行循环txt文本中的内容
            words = line.split(';')  # 通过指定分隔符对字符串进行切片
            data.append((words[0], int(words[1])))  # 把txt里的内容读入data列表保存,words[0]是图片信息,words[1]是label

        self.data = data
        self.transform = transform

    def __getitem__(self, index):  # 按照索引读取每个元素的具体内容
        fn, label = self.data[index]  # fn是图片path
        img = Image.open(fn).convert('RGB')  # from PIL import Image

        if self.transform is not None:  # 是否进行transform
            img = self.transform(img)
        return img, label  # return回哪些内容,在训练时循环读取每个batch,就能获得哪些内容

    def __len__(self):  # 它返回的是数据集的长度,必须有
        return len(self.imgs)


'''标准化、图片变换'''
train_transforms = transforms.Compose([
    transforms.RandomCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor()
    ])

train_data = MyDataset(datatxt='train.txt', transform=train_transforms)

train_loader = torch.utils.data.DataLoader(dataset=train_data, batch_size=64, shuffle=True)

""" 训练时:"""
for epoch in range(num_epoches):
    for i, (img, label) in enumerate(dataloader):
        ...

transform各参数的作用:

1. 裁剪——Crop

中心裁剪:transforms.CenterCrop
随机裁剪:transforms.RandomCrop
随机长宽比裁剪:transforms.RandomResizedCrop
上下左右中心裁剪:transforms.FiveCrop
上下左右中心裁剪后翻转,transforms.TenCrop

2. 翻转和旋转——Flip and Rotation

依概率p水平翻转:transforms.RandomHorizontalFlip(p=0.5)
依概率p垂直翻转:transforms.RandomVerticalFlip(p=0.5)
随机旋转:transforms.RandomRotation

3. 图像变换

resize:transforms.Resize
标准化:transforms.Normalize
转为tensor,并归一化至[0-1]:transforms.ToTensor
填充:transforms.Pad
修改亮度、对比度和饱和度:transforms.ColorJitter
转灰度图:transforms.Grayscale
线性变换:transforms.LinearTransformation()
仿射变换:transforms.RandomAffine
依概率p转为灰度图:transforms.RandomGrayscale
将数据转换为PILImage:transforms.ToPILImage
transforms.Lambda:Apply a user-defined lambda as a transform.

4. 对transforms操作,使数据增强更灵活

transforms.RandomChoice(transforms), 从给定的一系列transforms中选一个进行操作
transforms.RandomApply(transforms, p=0.5),给一个transform加上概率,依概率进行操作
transforms.RandomOrder,将transforms中的操作随机打乱

2. 创建DataLoader对象

DataLoader: 将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。

1
2
3
from torch.utils.data import Dataset, DataLoader

train_loader = DataLoader(dataset=train_data, batch_size=64, shuffle=True)

3. 循环DataLoader进行训练

1
2
3
for epoch in range(num_epoches):
    for i, (img, label) in enumerate(dataloader):
        ...

** 注意

损失函数torch.nn.CrossEntropyLoss()中已经包含了Softmax函数,所以我们的神经网络直接线性输出即可。

1
2
3
4
5
net = nn.Sequential(
            nn.Linear(8,50),
            nn.ReLU(),
            nn.Linear(50,4)
            )

4. transforms.Normalize 计算

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
import numpy as np
import cv2
import os
import torch
import torchvision

means = [0, 0, 0]
stdevs = [0, 0, 0]

imgs_path = '/content/drive/MyDrive/search/mmdetection/data/imagenet-underwater/train/' # 图片路径
_txt_path = '/content/drive/MyDrive/search/mmdetection/data/imagenet-underwater/train_3.txt'    # train.txt
imgs_path_list = []
_txt = open(_txt_path, 'r')

for line in _txt:
    name, _ = line.split(';')
    print(name)
    imgs_path_list.append(imgs_path + name)

num_imgs = 0
for data in imgs_path_list:

    num_imgs += 1
    img = cv2.imread(data)
    img = img.astype(np.float32) / 255.
    for i in range(3):
        means[i] += img[:, :, i].mean()
        stdevs[i] += img[:, :, i].std()

means.reverse()
stdevs.reverse()

means = np.asarray(means) / num_imgs
stdevs = np.asarray(stdevs) / num_imgs

print("normMean = {}".format(means))
print("normStd = {}".format(stdevs))
print('transforms.Normalize(normMean = {}, normStd = {})'.format(means, stdevs))

#.引用

  1. PyTorch 学习笔记(三):transforms的二十二个方法
  2. 动手学深度学习
  3. pytorch一步一步在VGG16上训练自己的数据集
  4. PyTorch ImageNet 基于预训练六大常用图片分类模型的实战
  5. pytorch搭建分类网络并进行训练和测试
  6. 二十二种 transforms 图片数据预处理方法
  7. 深度学习神经网络对于尺寸不同的图片如何处理?resize?crop?
  8. pytorch transforms.Normalize中,图像集的像素均值(mean)和标准差(std)怎么计算?
  9. Pytorch框架学习(6)——transforms与normalize