Day-19 PyTorch 怎麽读取资料? Dataset and DataLoader

  • 今天来聊聊怎麽读取资料和调整资料集,你可能会问说奇怪我们前两天不是就已经可以使用资料了吗?这边有啥好学习的?
  • 其实不然,我们先来看看我们前面两天的资料处里会是如何
  • 就以昨天的 Iris dataset 做 example,我们回顾一下如果我们昨天的 Training 是怎麽做的
data = Iris.dataset()
# training loop
for epoch in range(100):
    
    # y_predicted = model(x) 其实等同於
    for x, y in data:
        # forward + backward + weight updates
  • 这代表什麽意思,我们昨天的程序其实等於跑了双层 for loop,第一层决定了我们要训练过左有资料几次,而第二层竟然是我们的 Data set size
  • 这很可怕,我们昨天的 Iris dataset 只有 1000 笔资料左右,因此可能不会有特别的感觉,但如果我们的资料有数万笔呢?我们的总回圈次数会瞬间变得非常可怕,但是我们又不可能不使用过所有资料,有没有办法用到所有资料,然後又不会这样全部都要 loop 一遍的做法呢?
  • 有的,概念上就是将资料批次使用,什麽意思呢?也就是今天我们不再单笔单笔资料去做观看使用,而是进行分批次的使用,将一定数量的资料和在一起视为一个批次,去使用这些批次的资料
  • 基於上述的概念,我们的实际运作结构会变成怎样呢?
# training loop
for epoch in range(100):
    
    # loop over all batches
    for i in range(total_batches):
        x_batch, y_batch = ...
  • 可以很直观的理解这次的 total time 会大量减少,因为我们的 total_batches 一定少於 total datas
  • 因此这边我们就需要 PyTorch 的 Dataset 跟 Dataloader Classes 来帮我们进行 batches 的计算跟建立,这也是 PyTorch 一个非常实用简单的功能,所以等等就会介绍这些东西
  • 那在开始谈论 Dataset 跟 Dataloader 之前,我们先来好好理解一下前面没解释过的几个重要名词

Epoch、Batch size、number of iterations

  • Epoch、Batch size跟numbers of iterations 是我们在机器学习的世界中很常见的名词,他们在整个机器学习的世界有着举足轻重的地位,我们来好好了解认识他们一下吧~

Epoch

  • 我们在很前面就用到一个名词叫做 Epoch ,使用的情境是这样使用的
    epochs = nums # a number
    # training loop
    for epoch in range(epochs):
        ...
    
  • 那其实我们可以很直观的理解就是我们要基於现在有的资料,去训练更新资料几次
  • 我们来假设我们的资料集是一本书,今天我们就是那个机器,Epoch 的数量就是我们要完整地看完这本书几次的意思,然後利用这本书的知识来更新我们的观念
  • 比较学术的定义就是一个 ==Epoch = 1 forward and backward pass of All training samples==,也就是完整对所有资料做一次 forward 跟 backward pass 的这一整个流程

Batch size

  • Batch size 的学术定义则是 number os training samples in one forward & backward pass,也就是每次我要执行一个 forward & backward pass 要使用的资料量
  • Batch(批次)存在的意义,Batch size 能决定我一次做判断学习时,是对多少笔资料做观察整理的,然後对於这些资料的结论去做资料调整
  • 因此 Batch size 最重要的意义并不是减少 loop 次数,反而应该说她决定了我们如何更好的去学习资料特徵之间的关系

Number of iterations

  • iterations 的定义则是 number of passes, each pass using [batch size] number of samples,也就是我们到底在一次 Epoch 中更新调整了几次

小统整

  • 我们直接拿个例子来辅助大家了解,假设我们有 100 个资料点,我们把 batch_size 设定成 20,那我们每一个 epoch 会有 5 个 iterations
  • 这就是各个名词之间的关联性了~

如何架设 Dataset & Dataloader

Dataset

  • Dataset 就是要帮我们把资料包起来,因此这边我们就来看看怎麽设定相对应的资料集
  • 我们假设我们有一组资料集长这样(偷偷逼逼这个资料集其实我们之後会用到)
    {%gist=78346b637e4aff281377f754d6bf8681%}
  • 那我们来看看建立这个资料集的 PyTorch Dataset 要怎麽建立
  • 首先先引用我们会需要的套件,如果是建立 Dataset 会需要 torch.utils.data 里面的 Dataset
import torch
from torch.utils.data import Dataset

# 这两个是资料处里常用的套件
import numpy as np
import pandas as pd
  • 那我们基本建立 Dataset 会需要三个必要的 function,分别处理资料的读取,特定资料回传跟资料长度,我们先看 code,然後再一一解释
# example of dataset create
class ExampleDataset(Dataset):

    # data loading
    def __init__(self):
        xy = np.loadtxt('./dataset-example.csv', delimiter=',', dtype=np.float32, skiprows=1)
        self.x = torch.from_numpy(xy[:, 1:])
        self.y = torch.from_numpy(xy[:, [0]])
        self.n_samples = xy.shape[0]

    # working for indexing
    def __getitem__(self, index):
        
        return self.x[index], self.y[index]

    # return the length of our dataset
    def __len__(self):
        
        return self.n_samples
  • 因为我们的资料集中,第一 coloum 是 label,也就是答案,因此我们资料特徵跟答案要分开一下,所有关於 data 的基本资料都应该在 __init__ 里面整理好
  • 那资料集整理好之後,读取之後,我们要能够使用每一笔资料,因此当然要能够利用 index 回传资料,所以要设定 __getitem__ 的 return
    • 那这边不只可以设定资料回传,PyTorch 还有一个 Dataset Transform 的功能,如果需要资料型态变化也会写在这里,那这边我们不会特别提到 Dataset Transform,有兴趣的可以自己再去看看
  • 那我们也必须掌控资料集的长度,因此要设定 __init__ 的 return 去回传资料长度
  • 那我们来看看使用上可以怎麽使用
dataset = ExampleDataset()

# pick first data
first_data = dataset[0]
features, labels = first_data
print(features, labels)
print(len(dataset))
  • 所以我们可以看到如果我们要使用第一笔资料就可以直接用 index 去索引,想知道资料长度也可以直接用 len() 去做显示

切割资料集

  • 我们有说过在训练模型时,会将资料分成 training 跟 testing 来训练跟检查资料,那如果我们有 dataset 之後,想切分资料要如何去做切分?
  • 这时会用到 from torch.utils.data.sampler import SubsetRandomSampler 提供一种迭代数据集元素索引的__len__()方法,以及一个返回返回迭代器长度的方法
  • 利用这个工具搭配资料的整理就能切分 training dataset 跟 testing dataset
  • 我们直接来看范例
from torch.utils.data.sampler import SubsetRandomSampler

# create data
...


# split data
# set testing data size
test_split = 0.2
# need shuffle or not
shuffle_dataset = True
# random seed of shuffle
random_seed = 1234

# creat data indices for training and testing splits
dataset_size = len(dataset)
indices = list(range(dataset_size))
# count out split size
split = int(np.floor(test_split * dataset_size))
if shuffle_dataset:
    np.random.seed(random_seed)
    np.random.shuffle(indices)
train_indices, test_indices = indices[split:], indices[:split]

# creating data samplers and loaders:
train_sampler = SubsetRandomSampler(train_indices)
test_sampler = SubsetRandomSampler(test_indices)

train_loader = DataLoader(dataset, batch_size=batch_size, sampler=train_sampler)
test_loader = DataLoader(dataset, batch_size=batch_size, sampler=test_sampler)

DataLoader

  • 如果说 Dataset 是定义了资料的结构跟资料本身的一个包装,那 DataLoader 就是定义了使用读取资料的方式(换句话说就是一定要先有 Dataset 才可以用 DataLoader 操作)
  • 那 DataLoader 可以设定那些部分呢?就是包含我们一开始提到的 Batch_size 之类的部分啦~
  • 我们来看个示范
# example of dataloader use
dataloader = DataLoader(dataset=dataset, batch_size=4, shuffle=True)

dataiter = iter(dataloader)
data = dataiter.next()
features, labels = data
print(features, labels)

# tensor([[0., 0., 0.,  ..., 0., 0., 0.],
#         [0., 0., 0.,  ..., 0., 0., 0.],
#         [0., 0., 0.,  ..., 0., 0., 0.],
#         [0., 0., 0.,  ..., 0., 0., 0.]]) tensor([[1.],
#         [0.],
#         [1.],
#         [4.]])
  • 那我们上面做了那些设定,分别就是说明了我们是使用刚刚定义过的 ExampleDataset() 作为我们的资料集,另外我们希望每 4 笔资料一起看,所以设定 batch_size 为 4
  • 另外还可以在这边设定资料打乱啊等等,都可以在这边设定好
  • 那实际看输出会发现就是会有四笔资料,也就是达到我们希望四笔资料一起比较的概念,且资料是全随机的

实际带入训练应用

# example of dataloader use
dataloader = DataLoader(dataset=dataset, batch_size=4, shuffle=True)

# training loop
epochs = 2
total_samples = len(dataset)
n_iterations = np.ceil(total_samples / 4)
print(total_samples, n_iterations)

for epoch in range(epochs):

    for i, (features, targets) in enumerate(dataloader):
        # forward backward pass, update
        if (i+1) % 1 == 0:
            print(f'epoch {epoch+1}/{epochs}, step{i+1}/{n_iterations}')
            
# 9 3.0
# epoch 1/2, step1/3.0
# epoch 1/2, step2/3.0
# epoch 1/2, step3/3.0
# epoch 2/2, step1/3.0
# epoch 2/2, step2/3.0
# epoch 2/2, step3/3.0

每日小结

  • 资料训练都会有资料集,那如何更好的利用这些资料,达到节省时间和更好的准确率的目标,这些都是值得去研究测试的
  • PyTorch 提供了一个更加简洁的创建资料的方式和一个更好定义使用的方式,就是 Dataset 跟 Dataloader,分别负责了创建读取资料的工作和定义如何使用资料的工作
  • 那到这里我们已经基本上可以说是把最基础需要知道的 PyTorch 工具都介绍完整了~明天就可以试试看利用 PyTorch 从零开始建立第一个神经网路啦~

<<:  Day22: WAF、Firewall Manager、Shield简介

>>:  诶那个...跳坑吗?

大共享时代系列_019_水电、装潢、建筑工程交流与媒合

找水电、装潢师傅,大家都怎麽找? 亲友介绍?路边的实体店面?看附近有没有贴纸? 那些年,遇到的水电装...

Day 24:检查GPS状态

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...

资讯治理(Data Governance)

数据管理员(Data Steward) 数据管理员是组织中的一个角色,负责利用组织的数据治理流程来...

汇入大量资料到 docker 上的资料库:mysql-client

前言 想透过 phpMyAdmin 把正式机资料拉下来,汇入本机 docker 上的资料库做开发;但...

Day 13. 模板语法Template Syntax – 插值 v-once、v-html

昨天我们讲了Vue的一生,今天来说说模板语法,看看要怎麽把vue instance中的资料变化渲染到...