- 今天來聊聊怎麼讀取資料和調整資料集,你可能會問說奇怪我們前兩天不是就已經可以使用資料了嗎?這邊有啥好學習的?
- 其實不然,我們先來看看我們前面兩天的資料處裡會是如何
- 就以昨天的 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
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 從零開始建立第一個神經網路啦~