利用Dataset与Dataloader自定义数据集
转载学习资源:
- pytorch教程:https://pytorch.org/docs/1.7.1/data.html
- Pytorch之Dataset与DataLoader 打造你自己的数据集,源码阅读
https://chenllliang.github.io/2020/02/04/dataloader/
Map式数据集必须要重写__getitem__(self, index),len(self) 两个内建方法,用来表示从索引到样本的映射(Map).
这样一个数据集dataset,举个例子,当使用dataset[idx]命令时,可以在你的硬盘中读取你的数据集中第idx张图片以及其标签(如果有的话);len(dataset)则会返回这个数据集的容量。
- PyTorch手把手自定义Dataset读取数据:
https://zhuanlan.zhihu.com/p/35698470
- 使用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,实现init、getitem、len方法