PyTorch DataLoader完整指南|從基礎到應用,錯誤處理全解析

1. 前言

PyTorch 是目前非常受歡迎的深度學習框架之一,廣泛應用於研究與實務領域。特別是為了更有效率地處理資料前處理與迷你批次管理,PyTorch 提供了一個名為「DataLoader」的工具。

本文將詳細說明 PyTorch 中 DataLoader 的功能與使用方式,包括如何建立自訂資料集。此外,也會介紹常見錯誤與對應的解決方法,對於初學者到中階使用者都非常實用。

閱讀本文您將學到:

  • PyTorch 中 DataLoader 的基本功能與使用範例
  • 如何建立自訂資料集及其應用
  • 常見錯誤與解決對策

如果您是剛開始接觸 PyTorch 的新手,或是在資料處理上遇到瓶頸的使用者,請務必閱讀到最後。

2. 什麼是 DataLoader?其角色與重要性

DataLoader 是什麼?
PyTorch 的 DataLoader 是一種從資料集中高效提取資料,並以適合模型訓練的方式提供資料的工具。其主要功能包括以下幾點:

  • 迷你批次處理: 可將大型資料分成較小的批次,讓處理更符合 GPU 記憶體容量。
  • 隨機打亂功能: 可隨機打亂資料順序,降低過度擬合的風險。
  • 平行處理: 可使用多個執行緒同時載入資料,有效縮短訓練時間。

為什麼需要 DataLoader?
在機器學習中,資料前處理與批次處理是非常常見的任務。如果全靠人工管理,會非常耗時且容易使程式碼變得複雜。使用 DataLoader 可以帶來以下好處:

  1. 高效率的資料管理: 自動完成批次分割與順序控制。
  2. 高度自訂化: 可根據任務需求輕鬆實作資料轉換與處理。
  3. 良好的通用性: 不依賴資料型態或格式,能處理多樣的資料集。
RUNTEQ(ランテック)|超実戦型エンジニア育成スクール

3. Dataset 與 DataLoader 的關係

Dataset 類別的角色
Dataset 類別是 PyTorch 中資料管理的基礎,透過它可以方便地載入與自訂資料集。

Dataset 的主要特點

  1. 資料儲存: 將資料有效地儲存在記憶體或硬碟中。
  2. 資料存取功能: 提供以索引方式取得資料的機制。
  3. 可自訂擴充: 支援使用者自訂資料集的建立方式。

以下是 PyTorch 內建的 Dataset 使用範例:

from torchvision import datasets, transforms

transform = transforms.Compose([transforms.ToTensor()])
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)

與 DataLoader 的搭配使用
Dataset 定義的是「資料本身」,而 DataLoader 則是將這些資料送入模型的橋樑。

以下是剛才的 MNIST 資料集搭配 DataLoader 使用的範例:

from torch.utils.data import DataLoader

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

如上所示,DataLoader 負責從 Dataset 中擷取資料,並以批次方式提供給模型,是一個非常方便的介面。

4. DataLoader 的基本用法

本章將說明 PyTorch 中 DataLoader 的實際使用方式。透過掌握基本語法與設定參數,您可以培養實戰技能。

1. DataLoader 的基本語法

以下是 DataLoader 的基本範例程式碼:

import torch
from torch.utils.data import DataLoader, TensorDataset

# 範例資料
data = torch.randn(100, 10)  # 100筆樣本,每筆10個特徵
labels = torch.randint(0, 2, (100,))  # 標籤為 0 或 1

# 使用 TensorDataset 建立資料集
dataset = TensorDataset(data, labels)

# 設定 DataLoader
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

重點說明:

  1. TensorDataset: 將資料與標籤配對,方便管理。
  2. batch_size=32: 每批處理32筆資料。
  3. shuffle=True: 每個 epoch 隨機打亂資料順序,防止模型過度擬合。

2. DataLoader 的主要參數與設定

DataLoader 提供以下常用的重要參數:

參數說明使用範例
batch_size每次處理的樣本數。batch_size=64
shuffle是否隨機打亂資料順序,預設為 False。shuffle=True
num_workers讀取資料時使用的子程序數量,預設為 0(單執行緒)。num_workers=4
drop_last若最後一個 batch 小於 batch_size,是否捨棄它。drop_last=True
pin_memory是否將資料加載到固定記憶體中,加快傳輸到 GPU 的速度。pin_memory=True(當使用 GPU 時建議設為 True)

使用範例:
以下是開啟平行處理與固定記憶體的設定方式:

dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=4, pin_memory=True)

3. 從 DataLoader 取得資料的範例

以下示範如何從 DataLoader 中取出每個 batch 的資料:

for batch_idx, (inputs, targets) in enumerate(dataloader):
    print(f"Batch {batch_idx+1}")
    print("Inputs:", inputs.shape)  # 顯示資料的形狀
    print("Targets:", targets.shape)  # 顯示標籤的形狀

這段程式碼會逐一處理每個 batch 的資料與標籤。

  • inputs.shape: 例如 (32, 10),顯示每個 batch 的資料形狀。
  • targets.shape: 顯示對應的標籤形狀。

4. 為什麼要隨機打亂資料?

DataLoader 的 shuffle=True 選項會將資料順序隨機排列,具有以下優點:

  • 避免偏差學習: 若每次都以相同順序訓練,模型可能會對特定模式過度擬合。
  • 提升泛化能力: 隨機化資料順序,有助於模型學習更多樣的特徵。

5. 如何建立自訂 Dataset

在 PyTorch 中,除了使用內建的標準資料集之外,有時我們也需要使用自己準備的資料。這種情況下,我們可以建立自訂 Dataset,並搭配 DataLoader 使用。本節將詳細說明建立自訂 Dataset 的步驟。

1. 需要自訂 Dataset 的情境

以下是您可能需要自訂 Dataset 的幾種常見情況:

  • 使用特殊格式的資料: 例如圖片、文字或 CSV 檔案等,非內建資料集格式。
  • 想要自動進行資料前處理: 像是資料標準化、篩選或轉換。
  • 標籤結構較複雜: 例如多個標籤、或同時包含圖像與文字的複合資料。

2. 自訂 Dataset 的基本結構

建立自訂 Dataset 時,需要繼承 torch.utils.data.Dataset 並實作以下三個方法:

  1. __init__: 初始化資料集,通常在這裡讀取檔案與進行前處理。
  2. __len__: 回傳資料集中樣本的數量。
  3. __getitem__: 根據索引回傳對應的資料與標籤。

3. 自訂 Dataset 的實作範例

以下是一個讀取 CSV 檔案並建立自訂 Dataset 的範例:

範例:使用 CSV 檔案建立 Dataset

import torch
from torch.utils.data import Dataset
import pandas as pd

class CustomDataset(Dataset):
    def __init__(self, csv_file):
        # 讀取 CSV 檔
        self.data = pd.read_csv(csv_file)
        # 分離特徵與標籤
        self.features = self.data.iloc[:, :-1].values
        self.labels = self.data.iloc[:, -1].values

    def __len__(self):
        # 回傳樣本數
        return len(self.features)

    def __getitem__(self, idx):
        # 取出指定索引的資料與標籤
        sample = torch.tensor(self.features[idx], dtype=torch.float32)
        label = torch.tensor(self.labels[idx], dtype=torch.long)
        return sample, label

重點說明:

  1. __init__: 在初始化中讀取 CSV 並分開特徵與標籤。
  2. __len__: 告知 DataLoader 有多少資料可以使用。
  3. __getitem__: 傳回對應索引的資料與標籤,格式為 Tensor。

4. 搭配 DataLoader 使用

以下是將上述自訂 Dataset 搭配 DataLoader 的範例:

# 建立 Dataset 實例
dataset = CustomDataset(csv_file='data.csv')

# 設定 DataLoader
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

# 資料取得範例
for inputs, labels in dataloader:
    print("Inputs:", inputs.shape)
    print("Labels:", labels.shape)

重點說明:

  • batch_size=32: 每個批次處理 32 筆資料。
  • shuffle=True: 在每個 epoch 隨機排列資料順序。

透過上述方法,您可以彈性地管理各種資料格式的自訂資料集。

5. 進階範例:處理圖片資料的 Dataset

以下是一個處理圖片與標籤的自訂 Dataset 範例:

from PIL import Image
import os

class ImageDataset(Dataset):
    def __init__(self, image_dir, transform=None):
        self.image_dir = image_dir
        self.image_files = os.listdir(image_dir)
        self.transform = transform

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

    def __getitem__(self, idx):
        img_path = os.path.join(self.image_dir, self.image_files[idx])
        image = Image.open(img_path)

        # 圖片轉換處理
        if self.transform:
            image = self.transform(image)

        # 根據檔名設定標籤(範例為二分類)
        label = 1 if 'dog' in img_path else 0
        return image, label

重點說明:

  • 圖片轉換: 使用 transform 進行 resize、normalize 等處理。
  • 標籤產生: 根據圖片檔名進行簡易標籤分類。

6. 進階技巧與最佳實踐

本節將介紹如何進一步活用 PyTorch 的 DataLoader,透過進階技巧提升資料處理效率與彈性,幫助您更專業地應用於實際專案。

1. 使用平行處理加速資料讀取

問題點:
當資料集很大時,單一程序讀取資料會變得非常慢。尤其處理圖片或音訊資料時,讀取過程容易成為瓶頸。

解決方案:
設定 num_workers 參數,讓多個程序同時載入資料,以加速處理。

範例:使用多個程序的 DataLoader

from torch.utils.data import DataLoader

# 設定 DataLoader
dataloader = DataLoader(dataset, batch_size=64, shuffle=True, num_workers=4)

for batch in dataloader:
    # 資料處理
    pass

重點說明:

  • num_workers=4: 使用 4 個子程序並行讀取資料,可根據系統性能調整。
  • 注意事項: 若在 Windows 環境中,記得使用 if __name__ == '__main__': 避免多程序錯誤。

2. 最佳化記憶體使用效率

問題點:
當使用 GPU 訓練模型時,若資料從 CPU 傳輸至 GPU 的速度太慢,會影響整體效能。

解決方案:
設定 pin_memory=True,讓資料載入至固定記憶體,加快資料傳輸速度。

範例:啟用固定記憶體加速傳輸

dataloader = DataLoader(dataset, batch_size=64, shuffle=True, pin_memory=True)

重點:

  • 使用 GPU 訓練時特別有效,若只使用 CPU 則不必設定。

3. 使用 Sampler 控制資料選取

問題點:
在資料分布不均(例如分類不平衡)時,單純使用 shuffle 難以控制訓練效果。

解決方案:
使用 Sampler 來自訂資料的抽樣策略。

範例:使用 WeightedRandomSampler 處理不平衡資料

from torch.utils.data import WeightedRandomSampler

# 建立每個樣本的權重(假設 0 類少、1 類多)
weights = [0.1 if label == 0 else 0.9 for label in dataset.labels]
sampler = WeightedRandomSampler(weights, num_samples=len(weights), replacement=True)

# 設定 DataLoader
dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)

重點:

  • 不平衡資料處理: 透過調整樣本出現機率,改善學習表現。
  • 自訂抽樣策略: 可根據任務需求,設計條件式的抽樣邏輯。

4. 使用資料擴增技術提高模型準確率

問題點:
當資料集太小時,模型容易過擬合,泛化能力下降。

解決方案:
對圖片或文字進行「資料擴增(Data Augmentation)」以增加資料多樣性。

範例:使用 torchvision.transforms 擴增圖片

from torchvision import transforms

transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),   # 隨機水平翻轉
    transforms.RandomRotation(10),      # 隨機旋轉 10 度以內
    transforms.ToTensor(),              # 轉為 Tensor
    transforms.Normalize((0.5,), (0.5,))# 標準化處理
])

重點:

  • 資料擴增是防止過擬合、提升泛化能力的重要方法。
  • 可搭配自訂 Dataset 彈性應用不同的轉換流程。

5. 處理大型資料集的批次與分散式訓練

問題點:
當資料集龐大時,容易造成記憶體不足或訓練時間過長。

解決方案:
使用「批次處理(Batch Processing)」與「分散式訓練(Distributed Training)」加速訓練。

範例:使用 DistributedSampler 進行分散式訓練

from torch.utils.data.distributed import DistributedSampler

sampler = DistributedSampler(dataset)
dataloader = DataLoader(dataset, batch_size=32, sampler=sampler)

重點:

  • 在多 GPU 或多機系統中,可使用分散式學習平衡計算負載。
  • 搭配 Sampler,可有效控制每個訓練節點讀取的資料範圍。

7. 常見錯誤與對應解決方法

雖然 PyTorch 的 DataLoader 十分方便,但在實際使用中仍可能遇到各種錯誤。本節將整理常見的錯誤訊息、可能的原因,以及對應的解決方案。

1. 錯誤一:記憶體不足(Out of Memory)

錯誤訊息:

RuntimeError: CUDA out of memory.

可能原因:

  • batch size 設定過大。
  • 嘗試一次處理高解析度圖片或龐大的資料集。
  • GPU 記憶體未正確釋放。

解決方法:

  1. 降低 batch size:
dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
  1. 使用較輕量的模型或資料型別(如半精度):
model.half()
inputs = inputs.half()
  1. 手動釋放 GPU 記憶體:
import torch
torch.cuda.empty_cache()
  1. 啟用 pin_memory 以優化資料傳輸:
dataloader = DataLoader(dataset, batch_size=16, shuffle=True, pin_memory=True)

2. 錯誤二:平行處理出錯

錯誤訊息:

RuntimeError: DataLoader worker (pid 12345) is killed by signal: 9

可能原因:

  • num_workers 設定過高,超過系統資源上限。
  • 資料加載過重或發生資源衝突。

解決方法:

  1. 減少 num_workers 數量:
dataloader = DataLoader(dataset, batch_size=32, num_workers=2)
  1. 將資料切分為多份或分批載入。
  2. 在 Windows 環境中,加入保護程式碼:
if __name__ == '__main__':
    dataloader = DataLoader(dataset, batch_size=32, num_workers=2)

3. 錯誤三:資料格式錯誤

錯誤訊息:

IndexError: list index out of range

可能原因:

  • __getitem__ 嘗試存取不存在的索引。
  • __len__ 回傳的長度與實際資料不一致。

解決方法:

  1. 確認 __len__ 是否正確:
def __len__(self):
    return len(self.data)
  1. __getitem__ 中加上邊界檢查:
def __getitem__(self, idx):
    if idx >= len(self.data):
        raise IndexError("Index out of range")
    return self.data[idx]

4. 錯誤四:資料型別錯誤(TypeError)

錯誤訊息:

TypeError: default_collate: batch must contain tensors, numpy arrays, numbers, dicts or lists; found <class 'str'>

可能原因:

  • 自訂 Dataset 傳回的資料格式不符合 Tensor 格式。

解決方法:

  1. 將資料轉換為 Tensor:
import torch

def __getitem__(self, idx):
    feature = torch.tensor(self.features[idx], dtype=torch.float32)
    label = torch.tensor(self.labels[idx], dtype=torch.long)
    return feature, label
  1. 使用自訂的 collate 函數處理非標準格式:
def custom_collate(batch):
    inputs, labels = zip(*batch)
    return torch.stack(inputs), torch.tensor(labels)

dataloader = DataLoader(dataset, batch_size=32, collate_fn=custom_collate)

5. 錯誤五:隨機性與結果不一致

錯誤訊息:

Randomness in shuffling produces inconsistent results.

可能原因:

  • 未設定隨機種子,導致每次執行結果不一致。

解決方法:

  1. 設定隨機種子(seed)以確保重現性:
import torch
import numpy as np
import random

def seed_everything(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)

seed_everything(42)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)

8. 實作範例:資料前處理與模型訓練的應用

本節將透過實際範例說明如何使用 PyTorch 的 DataLoader 來處理資料並訓練模型。我們將使用經典的 CIFAR-10 圖片分類資料集,示範整個訓練流程。

1. 資料集下載與前處理

首先下載 CIFAR-10 資料集並進行必要的前處理:

import torch
import torchvision
import torchvision.transforms as transforms

# 資料轉換(資料擴增 + 標準化)
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),  # 隨機水平翻轉
    transforms.RandomCrop(32, padding=4),  # 隨機裁切
    transforms.ToTensor(),  # 轉換為 Tensor
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))  # 標準化處理
])

# 載入訓練與測試資料集
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)

重點說明:

  1. 資料擴增: 使用隨機翻轉與裁切來增加樣本多樣性。
  2. 標準化: 將圖片像素值正規化至 -1~1 範圍,加速訓練。
  3. CIFAR-10: 小型圖片分類資料集,共 10 個分類。

2. 設定 DataLoader

接下來建立 DataLoader,以批次方式讀取資料:

from torch.utils.data import DataLoader

# 設定訓練與測試的 DataLoader
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=4, pin_memory=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False, num_workers=4, pin_memory=True)

重點說明:

  • batch_size: 每個 batch 處理 64 筆資料。
  • shuffle: 訓練資料打亂,測試資料保持原順序。
  • num_workers: 使用 4 個執行緒讀取資料。

3. 建立模型

定義一個簡單的卷積神經網路(CNN):

import torch.nn as nn
import torch.nn.functional as F

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(64 * 8 * 8, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 64 * 8 * 8)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

重點說明:

  1. 卷積層(Conv2d): 提取圖像特徵。
  2. 池化層(MaxPooling): 降低資料維度與位置敏感度。
  3. 全連接層(Linear): 用於最終的分類。

4. 模型訓練

使用訓練資料進行模型訓練:

import torch.optim as optim

# 使用 GPU(如可用)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = CNN().to(device)

# 設定損失函數與優化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練迴圈
for epoch in range(10):
    model.train()
    running_loss = 0.0
    for inputs, labels in train_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()

    print(f"Epoch {epoch+1}, Loss: {running_loss / len(train_loader)}")

重點說明:

  • 裝置設定: 若有 CUDA 裝置,則使用 GPU 加速。
  • Adam 優化器: 提供自動學習率調整與快速收斂。
  • 交叉熵損失: 適用於分類問題。

5. 模型評估

使用測試資料來評估模型準確率:

model.eval()
correct = 0
total = 0
with torch.no_grad():
    for inputs, labels in test_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = model(inputs)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f"Test Accuracy: {100 * correct / total:.2f}%")

重點說明:

  • eval 模式: 關閉 dropout 等訓練專用設定。
  • 準確率計算: 統計預測正確的比例。

9. 總結與下一步行動

在本教學中,我們從基礎到實戰,全面介紹了 PyTorch 中 DataLoader 的使用方法。最後讓我們一起回顧重點,並提供後續學習的方向。

1. 重點回顧

第1~4章:

  • DataLoader 的基礎: 說明 DataLoader 的功能與如何簡化資料管理與前處理流程。
  • 與 Dataset 的搭配: 使用內建與自訂 Dataset 的方式靈活整合資料處理流程。

第5~6章:

  • 自訂 Dataset 建立方法: 學會處理非標準資料格式(如圖片、CSV等),並提供可執行的範例。
  • 進階技巧與最佳實踐: 探討平行處理、記憶體最佳化、資料抽樣與資料擴增等技術。

第7~8章:

  • 常見錯誤與排除方式: 提供實際錯誤訊息、原因與對應的修正方案,提升問題處理能力。
  • 實作範例: 從 CIFAR-10 圖片分類任務中,完整展示資料載入、模型訓練與評估流程。

2. 實務應用建議

① 嘗試修改並擴充程式碼
本文提供的範例屬於基礎模板,實際開發中情況會更複雜。您可以依照下列建議進行擴充:

  • 加入更多資料擴增技術,減少過擬合。
  • 使用學習率調整器(scheduler)、正則化(如 Dropout)提高泛化能力。
  • 若資料量龐大,考慮使用分散式訓練來提升效率。

② 嘗試不同的資料集
除了 MNIST 和 CIFAR-10,您也可以挑戰以下資料集:

  • 圖片分類: ImageNet、COCO。
  • 自然語言處理: IMDB、SNLI、TREC 等。
  • 語音辨識: Librispeech、Common Voice。

③ 練習調整超參數
DataLoader 中的 batch_sizenum_workers 等參數會直接影響訓練速度與資源使用效率。多嘗試不同組合以找出最適配置。

④ 改變模型架構
除了 CNN,還可嘗試以下常見模型以增進理解:

  • RNN / LSTM: 適用於時序資料與語言模型。
  • Transformer: 適用於 NLP 任務的高效模型。
  • ResNet / EfficientNet: 在圖像分類中表現優異的深度模型。

3. 下一步學習建議

① 善用 PyTorch 官方文件
想進一步掌握 API 細節或了解新功能,建議參考官方文件:

② 動手做實戰專案
將本篇學到的技巧應用在實際專案中,例如:

  • 圖片分類應用: 開發一個簡易的 Web App 或行動應用。
  • 自然語言處理: 製作情感分析系統或聊天機器人。
  • 強化學習: 建立遊戲 AI 或最適化模型。

③ 分享與交流
將您的程式碼發布至 GitHub 或參加 Kaggle 比賽,與其他開發者互相學習並提升實力。

4. 結語

DataLoader 是 PyTorch 中資料處理與訓練效率提升的關鍵工具。本文從基礎觀念、應用實例到錯誤排除,為初學者與中階使用者提供一套完整的學習地圖。

重點總結:

  1. DataLoader 提供高效的資料管理與模型輸入流程。
  2. 自訂 Dataset 能處理任何格式的資料集。
  3. 搭配進階技巧如平行處理、資料擴增、sampler 可大幅提升訓練效率與模型表現。
  4. 透過實作範例,學會從資料預處理到模型訓練與評估的全流程。

若您正準備踏入 PyTorch 的機器學習與深度學習領域,希望這篇文章能成為您實作與思考的起點。持續學習與實踐,未來將能掌握更複雜的模型設計與應用技巧。