利用Dataset与Dataloader自定义数据集

利用Dataset与Dataloader自定义数据集

  • 转载学习资源:
  • 学习笔记:利用Dataset与Dataloader自定义数据集
    • 第一种:格式化的数据
    • 第二种:图像数据(jpg)
    • 准备知识——几个概念:
    • Dataset和Dataloader联合使用

转载学习资源:

  1. pytorch教程:https://pytorch.org/docs/1.7.1/data.html
  1. Pytorch之Dataset与DataLoader 打造你自己的数据集,源码阅读
    https://chenllliang.github.io/2020/02/04/dataloader/

Map式数据集必须要重写__getitem__(self, index),len(self) 两个内建方法,用来表示从索引到样本的映射(Map).

这样一个数据集dataset,举个例子,当使用dataset[idx]命令时,可以在你的硬盘中读取你的数据集中第idx张图片以及其标签(如果有的话);len(dataset)则会返回这个数据集的容量。

  1. PyTorch手把手自定义Dataset读取数据:
    https://zhuanlan.zhihu.com/p/35698470
  1. 使用PyTorch中的Dataset与DataLoader构建自己的数据集,给深度学习模型喂数据
    https://www.bilibili.com/video/BV1T64y1X7Yi?spm_id_from=333.337.search-card.all.click

学习笔记:利用Dataset与Dataloader自定义数据集

第一种:格式化的数据

(每一行是一个样本:特征1, 特征2, 特征3,标签1 / 0)
可以全部放入内存

class MyDataset(Dataset):
    def __init__(self, filepath):
        """
        处理数据的两种做法:
            1: All Data load to Memory  (结构化数据)
            2: 定义一个列表,把每个 sample 路径 放到一个列表,标签放到另一个列表里,避免数据一次性全部加入到内存 (非结构化数据)
        """
        xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32)
        self.len = xy.shape[0]
        self.x_data = torch.from_numpy(xy[:, :-1])
        self.y_data = torch.from_numpy(xy[:, [-1]])
        print("数据以准备好...")

    def __getitem__(self, index):    # 为了支持下标操作,即索引 dataset[index]
        return self.x_data[index], self.y_data[index]

    def __len__(self):              # 为了使用 len(dataset)
        return self.len
""" 1.使用 MyDataset类 构建自己的dataset """
mydataset = MyDataset(file)
""" 2.使用 DataLoader 构建train_loader """
train_loader = DataLoader(dataset=mydataset,
                          batch_size=32,
                          shuffle=True,
                          num_workers=2)

第二种:图像数据(jpg)

(包含:一张张图片)
将图像的路径放入列表中,避免数据一次加载数据

import torch
from PIL import Image
from torch.utils.data import Dataset

class MyDataSet(Dataset):
    """
    自定义数据集
        因为数据(sample)是图片,不能一次性全部加载到内存,所以采用第二种方式,
        将其保存的路径加载到内存中,待需要的时候在进行读取。

        对于标签,如果比较简单,则可直接加载到内存
    """

    def __init__(self, images_path: list, images_class: list, transform=None):
        self.images = images_path# images_path图片路径
        self.label = images_class# 
        self.transform = transform

    def __getitem__(self, index):
        img = Image.open(self.images[index])# 读一张图片
        if img.mode != "RGB":
            raise ValueError("Img is not RGB type")
        if self.transform is not None:
            img = self.transform(img)

        label = self.label[index]# 读标签

        return img, label

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

    @staticmethod
    def collate_fn(batch):
        # 官方实现的default_collate可以参考
        # https://github.com/pytorch/pytorch/blob/67b7e751e6b5931a9f45274653f4f653a4e6cdf6/torch/utils/data/_utils/collate.py
        images, labels = tuple(zip(*batch))

        images = torch.stack(images, dim=0)
        labels = torch.as_tensor(labels)
        return images, labels
import os
import random

def read_split_data(root: str, val_rate: float = 0.2):
    """
    对图片数据集进行分割
    :param root: 数据集所在的路径(不同类型图片所在文件夹路径)
    :param val_rate: 验证集在数据集中所占的比例
    :return: 训练图片路径,训练图片标签,验证集图片路径,验证集图片标签
    """
    random.seed(0)  # 保证随机结果可复现

    assert os.path.exists(root), "dataset root: {} does not exist.".format(root)

    # 遍历文件夹,一个文件夹对应一个类别
    flower_class = [cla for cla in os.listdir(root)
                    if os.path.isdir(os.path.join(root, cla))]  # ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
    # 排序,保证顺序一致
    flower_class.sort()
    # 生成类别名称以及对应的数字索引
    class_indices = dict((k, v) for v, k in enumerate(flower_class))
    json_str = json.dumps(dict((val, key) for key, val in class_indices.items()), indent=4)
    with open(root+'/class_indices.json', 'w') as json_file:
        json_file.write(json_str)
# class_indices.json内容:{"0":"daisy","1":"dandelion","2":"roses",...}
    train_images_path = []      # 存储训练集的所有图片路径
    train_images_label = []     # 存储训练集图片对应索引信息
    val_images_path = []        # 存储验证集的所有图片路径
    val_images_label = []       # 存储验证集图片对应索引信息

    every_class_num = []  # 存储每个类别的样本总数
    supported = [".jpg", ".JPG", ".png", ".PNG"]  # 支持的文件后缀类型

    # 遍历每个文件夹下的文件
    for cla in flower_class:
        cla_path = os.path.join(root, cla)
        # 遍历获取supported支持的所有文件路径
        images = [os.path.join(root, cla, i) for i in os.listdir(cla_path)
                  if os.path.splitext(i)[-1] in supported]
        # 获取该类别对应的索引
        image_class = class_indices[cla]
        # 记录该类别的样本数量
        every_class_num.append(len(images))
        # 按比例随机采样验证样本
        val_path = random.sample(images, k=int(len(images) * val_rate))

        for img_path in images:
            if img_path in val_path:  # 如果该路径在采样的验证集样本中则存入验证集
                val_images_path.append(img_path)
                val_images_label.append(image_class)
            else:  # 否则存入训练集
                train_images_path.append(img_path)
                train_images_label.append(image_class)

    print("{} images were found in the dataset.".format(sum(every_class_num)))
    print("{} images for training.".format(len(train_images_path)))
    print("{} images for validation.".format(len(val_images_path)))

    # 绘制不同类别图片在 测试集 和 验证集 中的数量
    plot_image = False
    if plot_image:
        # 绘制每种类别个数柱状图
        plt.bar(range(len(flower_class)), every_class_num, align='center')
        # 将横坐标0,1,2,3,4替换为相应的类别名称
        plt.xticks(range(len(flower_class)), flower_class)
        # 在柱状图上添加数值标签
        for i, v in enumerate(every_class_num):
            plt.text(x=i, y=v + 5, s=str(v), ha='center')
        # 设置x坐标
        plt.xlabel('image class')
        # 设置y坐标
        plt.ylabel('number of images')
        # 设置柱状图的标题
        plt.title('flower class distribution')
        plt.show()

    return train_images_path, train_images_label, val_images_path, val_images_label

root="/Users/steven/Documents/DeepLearning/DLShare/0707DatasetsDataLoader/LoadDataset/"
def main():
    # 选择计算设备
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print("using {} device.".format(device))
 """【Coding】根据图片所在分类文件夹,使用 read_split_data() 获取图片路径,图片标签(训练测试 8:2)"""
train_images_path, train_images_label, test_images_path, test_images_label = read_split_data(root)
    # 对图片进行 随机裁剪、水平翻转、ToTensor、标准化 操作
    data_transform = {
        "train": transforms.Compose([transforms.RandomSizedCrop(224),
                                     transforms.RandomHorizontalFlip(),
                                     transforms.ToTensor(),
                                     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),  # 期望,标准差
        "val": transforms.Compose([transforms.Resize(256),
                                   transforms.CenterCrop(224),
                                   transforms.ToTensor(),
                                   transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
    }


    """【Coding】使用 MyDataset类 构造 Dataset:以 train_data_set 为例 """
    train_data_set = MyDataSet(train_images_path, train_images_label, transform=data_transform["train"])

    # batch_size = 8
    # nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    # print('Using {} dataloader workers'.format(nw))

    """【Coding】使用 DataLoader 构建 mini-batch """
    train_loader = DataLoader(dataset=train_data_set,
                              batch_size=32,
                              shuffle=True,
                              num_workers=2)
                              

准备知识——几个概念:

Epoch、Batch-Size、Iterations

# Training cycle
for epoch in range(training_epochs):
	# Loop over all batches
	for i in range(total batch):
  • Definition:Epoch轮数
    在所有的训练中,一次forward和一次backward
    One forward pass and one backward pass of all the training examples.

  • Definition:Batch-Size批处理大小
    The number of training examples in one forward backward pass.
    在一次forward和一次backward中,训练例子的数量

  • Definition:Iteration迭代
    Number of passes,each pass using [batch size] number of examples.
    通过的数量,每次通过使用[batch size]examples的数量。

Dataset和Dataloader联合使用

构建数据集:Dataset
加载数据集中的文件到模型中:Dataloader
利用Dataset与Dataloader自定义数据集_第1张图片
继承Dataset,实现init、getitem、len方法
利用Dataset与Dataloader自定义数据集_第2张图片

你可能感兴趣的:(NLP自然语言处理,Python学习笔记,dataloader)