MNISTデータセットでニューラルネットワークを訓練:PyTorchによるカスタムDatasetとDataLoaderの活用法

2024-05-24

PyTorch "Datasets and Data Loaders" (torch.utils.data) の分かりやすい解説

Dataset クラスは、データの集合を表す抽象クラスです。このクラスは、データの読み込み、前処理、およびモデルへの供給に必要なメソッドを実装する必要があります。Dataset クラスは、以下のような様々なデータ形式をサポートします。

  • 画像
  • テキスト
  • 音声

DataLoader クラスは、Dataset オブジェクトからミニバッチを効率的に読み込み、モデルに供給するためのイテレータを提供します。DataLoader クラスは、以下のような機能を提供します。

  • バッチング: データをミニバッチに分割し、モデルに効率的に供給します。
  • サンプリング: データからランダムまたは順序的にサンプルを抽出し、訓練と検証に使用します。
  • データ変換: データの前処理と正規化を行います。

DatasetDataLoader の組み合わせにより、PyTorch モデルを訓練するために必要なデータを効率的に供給することができます。

以下の例では、MNIST データセットを使用して、ニューラルネットワークを訓練する方法を示します。

import torch
import torch.utils.data as data
from torchvision import datasets, transforms

# データセットを定義
train_dataset = datasets.MNIST(root='./data', train=True, download=True,
                               transform=transforms.ToTensor())

test_dataset = datasets.MNIST(root='./data', train=False, download=True,
                               transform=transforms.ToTensor())

# データローダーを定義
train_loader = data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = data.DataLoader(test_dataset, batch_size=64, shuffle=False)

# モデルを定義
model = torch.nn.Sequential(
    torch.nn.Linear(784, 128),
    torch.nn.ReLU(),
    torch.nn.Linear(128, 10)
)

# 損失関数と最適化アルゴリズムを定義
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())

# モデルを訓練
for epoch in range(10):
    for i, (images, labels) in enumerate(train_loader):
        # データを GPU に転送
        images = images.cuda()
        labels = labels.cuda()

        # 予測を計算
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 勾配をゼロ化
        optimizer.zero_grad()

        # 勾配を計算
        loss.backward()

        # パラメータを更新
        optimizer.step()

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(
                epoch + 1, 10, i + 1, len(train_loader), loss.item()
            ))

# モデルを評価
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.cuda()
        labels = labels.cuda()
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))

この例では、Dataset クラスを使用して MNIST データセットを読み込み、DataLoader クラスを使用してデータをミニバッチに分割し、モデルに供給しています。

torch.utils.data モジュールは、PyTorch モデルを訓練するために必要なデータを効率的に供給するための重要なツールです。DatasetDataLoader の組み合わせにより、様々なデータ形式からデータを簡単に読み込み、モデルに供給することができます。

  • [Writing Custom Datasets, DataLoaders and Transforms - Py


import torch
import torch.utils.data as data
from torchvision import datasets, transforms

# データセットを定義
class CustomDataset(data.Dataset):
    def __init__(self, root, train=True, transform=None):
        self.root = root
        self.train = train
        self.transform = transform

        if self.train:
            dataset = datasets.MNIST(root=self.root, train=True, download=True)
            self.data = dataset.data
            self.targets = dataset.targets
        else:
            dataset = datasets.MNIST(root=self.root, train=False, download=True)
            self.data = dataset.data
            self.targets = dataset.targets

    def __getitem__(self, index):
        image = self.data[index]
        label = self.targets[index]

        if self.transform is not None:
            image = self.transform(image)

        return image, label

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

# データローダーを定義
train_dataset = CustomDataset(root='./data', train=True, transform=transforms.ToTensor())
test_dataset = CustomDataset(root='./data', train=False, transform=transforms.ToTensor())

train_loader = data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = data.DataLoader(test_dataset, batch_size=64, shuffle=False)

# モデルを定義
model = torch.nn.Sequential(
    torch.nn.Linear(784, 128),
    torch.nn.ReLU(),
    torch.nn.Linear(128, 10)
)

# 損失関数と最適化アルゴリズムを定義
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())

# モデルを訓練
for epoch in range(10):
    for i, (images, labels) in enumerate(train_loader):
        # データを GPU に転送
        images = images.cuda()
        labels = labels.cuda()

        # 予測を計算
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 勾配をゼロ化
        optimizer.zero_grad()

        # 勾配を計算
        loss.backward()

        # パラメータを更新
        optimizer.step()

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(
                epoch + 1, 10, i + 1, len(train_loader), loss.item()
            ))

# モデルを評価
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.cuda()
        labels = labels.cuda()
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
    print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))

この例では、CustomDataset というカスタムデータセットクラスを作成しています。このクラスは、MNIST データセットを読み込み、前処理し、モデルに供給するために必要なメソッドを実装しています。

  • CustomDataset クラスを使用して、MNIST データセットを独自に読み込み、前処理します。
  • DataLoader クラスを使用して、データをミニバッチに分割し、モデルに供給します。
  • モデルを訓練し、評価します。


サブクラス化

最も一般的な方法は、torch.utils.data.Dataset クラスを継承したカスタムクラスを作成することです。この方法では、データの読み込み、前処理、およびモデルへの供給に必要なメソッドを独自に実装することができます。

ジェネレータを使用する

データをイテレータとして生成できる場合は、ジェネレータを使用してカスタムデータセットを作成することができます。これは、データがメモリに効率的にロードされない場合や、ストリーミングデータソースを使用する場合に役立ちます。

データローダーを使用して、既存のデータセットからカスタムデータセットを作成することができます。これは、データセットの一部のみを使用したい場合や、データを特定の方法で並べ替えたい場合に役立ちます。

最適な方法を選択するには、データの形式と要件を考慮する必要があります。

以下の例では、MNIST データセットをジェネレータを使用してカスタムデータセットとして作成する方法を示します。

import torch
import numpy as np

def generate_data(train=True):
    if train:
        dataset = datasets.MNIST(root='./data', train=True, download=True)
        data = dataset.data.numpy()
        targets = dataset.targets.numpy()
    else:
        dataset = datasets.MNIST(root='./data', train=False, download=True)
        data = dataset.data.numpy()
        targets = dataset.targets.numpy()

    for i in range(len(data)):
        image = data[i]
        label = targets[i]

        # 前処理
        image = image.astype('float32') / 255.0

        yield image, label

class CustomDataset(data.Dataset):
    def __init__(self, train=True):
        self.train = train

    def __getitem__(self, index):
        image, label = next(generate_data(self.train))
        return image, label

    def __len__(self):
        if self.train:
            dataset = datasets.MNIST(root='./data', train=True, download=True)
        else:
            dataset = datasets.MNIST(root='./data', train=False, download=True)
        return len(dataset.data)

# データローダーを定義
train_dataset = CustomDataset(train=True)
test_dataset = CustomDataset(train=False)

train_loader = data.DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = data.DataLoader(test_dataset, batch_size=64, shuffle=False)

# モデルを定義
model = torch.nn.Sequential(
    torch.nn.Linear(784, 128),
    torch.nn.ReLU(),
    torch.nn.Linear(128, 10)
)

# 損失関数と最適化アルゴリズムを定義
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())

# モデルを訓練
for epoch in range(10):
    for i, (images, labels) in enumerate(train_loader):
        # データを GPU に転送
        images = images.cuda()
        labels = labels.cuda()

        # 予測を計算
        outputs = model(images)
        loss = criterion(outputs, labels)

        # 勾配をゼロ化
        optimizer.zero_grad()

        # 勾配を計算
        loss.backward()

        # パラメータを更新
        optimizer.step()

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'.format(
                epoch + 1, 10, i + 1, len(train_loader), loss.item()
            ))

# モデルを評価
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in test_loader:
        images = images.cuda()
        labels = labels.cuda()
        outputs = model(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (