web-dev-qa-db-ja.com

カスタムデータセットをトレーニングデータセットとテストデータセットに分割するにはどうすればよいですか?

import pandas as pd
import numpy as np
import cv2
from torch.utils.data.dataset import Dataset

class CustomDatasetFromCSV(Dataset):
    def __init__(self, csv_path, transform=None):
        self.data = pd.read_csv(csv_path)
        self.labels = pd.get_dummies(self.data['emotion']).as_matrix()
        self.height = 48
        self.width = 48
        self.transform = transform

    def __getitem__(self, index):
        pixels = self.data['pixels'].tolist()
        faces = []
        for pixel_sequence in pixels:
            face = [int(pixel) for pixel in pixel_sequence.split(' ')]
            # print(np.asarray(face).shape)
            face = np.asarray(face).reshape(self.width, self.height)
            face = cv2.resize(face.astype('uint8'), (self.width, self.height))
            faces.append(face.astype('float32'))
        faces = np.asarray(faces)
        faces = np.expand_dims(faces, -1)
        return faces, self.labels

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

これは、他のリポジトリからの参照を使用することで管理できます。ただし、このデータセットをトレーニングとテストに分割します。

このクラス内でそれを行うにはどうすればよいですか?または、それを行うために別のクラスを作成する必要がありますか?

23
nirvair

Pytorchの SubsetRandomSampler を使用:

import torch
import numpy as np
from torchvision import datasets
from torchvision import transforms
from torch.utils.data.sampler import SubsetRandomSampler

class CustomDatasetFromCSV(Dataset):
    def __init__(self, csv_path, transform=None):
        self.data = pd.read_csv(csv_path)
        self.labels = pd.get_dummies(self.data['emotion']).as_matrix()
        self.height = 48
        self.width = 48
        self.transform = transform

    def __getitem__(self, index):
        # This method should return only 1 sample and label 
        # (according to "index"), not the whole dataset
        # So probably something like this for you:
        pixel_sequence = self.data['pixels'][index]
        face = [int(pixel) for pixel in pixel_sequence.split(' ')]
        face = np.asarray(face).reshape(self.width, self.height)
        face = cv2.resize(face.astype('uint8'), (self.width, self.height))
        label = self.labels[index]

        return face, label

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


dataset = CustomDatasetFromCSV(my_path)
batch_size = 16
validation_split = .2
shuffle_dataset = True
random_seed= 42

# Creating data indices for training and validation splits:
dataset_size = len(dataset)
indices = list(range(dataset_size))
split = int(np.floor(validation_split * dataset_size))
if shuffle_dataset :
    np.random.seed(random_seed)
    np.random.shuffle(indices)
train_indices, val_indices = indices[split:], indices[:split]

# Creating PT data samplers and loaders:
train_sampler = SubsetRandomSampler(train_indices)
valid_sampler = SubsetRandomSampler(val_indices)

train_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, 
                                           sampler=train_sampler)
validation_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size,
                                                sampler=valid_sampler)

# Usage Example:
num_epochs = 10
for Epoch in range(num_epochs):
    # Train:   
    for batch_index, (faces, labels) in enumerate(train_loader):
        # ...
40
benjaminplanche

PyTorch 0.4.1以降では、 random_split を使用できます。

train_size = int(0.8 * len(full_dataset))
test_size = len(full_dataset) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(full_dataset, [train_size, test_size])
58
Fábio Perez

現在の回答はランダムな分割を行いますが、クラスごとのサンプル数のバランスが保証されていないという欠点があります。これは、クラスあたりのサンプル数を減らしたい場合に特に問題になります。たとえば、MNISTには60,000の例、つまり1桁あたり6000があります。トレーニングセットで1桁あたり30のサンプルのみが必要であると仮定します。この場合、ランダムな分割により、クラス間で不均衡が生じる場合があります(1桁が他よりも多くのトレーニングデータを持つ)。したがって、各桁に正確に30のラベルのみが含まれるようにする必要があります。これは、層別サンプリングと呼ばれます。

これを行う1つの方法は、Pytorchでサンプラーインターフェースを使用し、 サンプルコードはこちら です。

これを行う別の方法は、単にあなたの方法をハックすることです:)。たとえば、以下はMNISTの簡単な実装です。dsはMNISTデータセットで、kは各クラスに必要なサンプルの数です。

def sampleFromClass(ds, k):
    class_counts = {}
    train_data = []
    train_label = []
    test_data = []
    test_label = []
    for data, label in ds:
        c = label.item()
        class_counts[c] = class_counts.get(c, 0) + 1
        if class_counts[c] <= k:
            train_data.append(data)
            train_label.append(torch.unsqueeze(label, 0))
        else:
            test_data.append(data)
            test_label.append(torch.unsqueeze(label, 0))
    train_data = torch.cat(train_data)
    for ll in train_label:
        print(ll)
    train_label = torch.cat(train_label)
    test_data = torch.cat(test_data)
    test_label = torch.cat(test_label)

    return (TensorDataset(train_data, train_label), 
        TensorDataset(test_data, test_label))

この関数は次のように使用できます。

def main():
    train_ds = datasets.MNIST('../data', train=True, download=True,
                       transform=transforms.Compose([
                           transforms.ToTensor()
                       ]))
    train_ds, test_ds = sampleFromClass(train_ds, 3)
12
Shital Shah

カスタムデータセットはPyTorchで特別な意味を持ちますが、あなたは任意のデータセットを意味すると思います。 MNISTデータセットをチェックしてみましょう(これはおそらく初心者にとって最も有名なデータセットです)。

import torch, torchvision
import torchvision.datasets as datasets
from torch.utils.data import DataLoader, Dataset, TensorDataset
train_loader = DataLoader(
  torchvision.datasets.MNIST('/data/mnist', train=True, download=True,
                             transform=torchvision.transforms.Compose([
                               torchvision.transforms.ToTensor(),
                               torchvision.transforms.Normalize(
                                 (0.5,), (0.5,))
                             ])),
  batch_size=16, shuffle=False)

print(train_loader.dataset.data.shape)

test_ds =  train_loader.dataset.data[:50000, :, :]
valid_ds =  train_loader.dataset.data[50000:, :, :]
print(test_ds.shape)
print(valid_ds.shape)

test_dst =  train_loader.dataset.targets.data[:50000]
valid_dst =  train_loader.dataset.targets.data[50000:]
print(test_dst, test_dst.shape)
print(valid_dst, valid_dst.shape)

これにより、元の[60000, 28, 28]、テスト用の[50000, 28, 28]、検証用の[10000, 28, 28]のサイズが大きくなります。

torch.Size([60000, 28, 28])
torch.Size([50000, 28, 28])
torch.Size([10000, 28, 28])
tensor([5, 0, 4,  ..., 8, 4, 8]) torch.Size([50000])
tensor([3, 8, 6,  ..., 5, 6, 8]) torch.Size([10000])

実際に画像とラベル(ターゲット)をペアリングする場合の追加情報

bs = 16
test_dl = DataLoader(TensorDataset(test_ds, test_dst), batch_size=bs, shuffle=True)

for xb, yb in test_dl:
    # Do your work
0
prosti