106 votes

Comment diviser un jeu de données personnalisé en jeux de données d'entraînement et de test ?

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)

C'est ce que j'ai réussi à faire en utilisant des références provenant d'autres dépôts. Cependant, je veux diviser ce jeu de données en deux parties : train et test.

Comment puis-je faire cela à l'intérieur de cette classe ? Ou dois-je créer une classe distincte pour le faire ?

184voto

Fábio Perez Points 1716

A partir de PyTorch 0.4.1, vous pouvez utiliser 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])

1 votes

J'ai suivi votre réponse et j'ai eu ce problème en itérant à travers le split. train_loader stackoverflow.com/questions/53916594/

4 votes

AttributeError: 'Subset' object has no attribute 'targets' comment puis-je accéder aux cibles d'un seul des sous-ensembles ? Je veux imprimer quelque chose comme ceci pour les données de formation et de test séparément. {0: 111, 1: 722, 2: 813, 3: 175, 4: 283, 5: 2846, 6: 290, 7: 106}

1 votes

Pour les autres : Si vous obtenez TypeError 'DataLoader' object is not subscriptable vous voudrez probablement aussi regarder stackoverflow.com/a/60150673/12068941

144voto

Aldream Points 7700

En utilisant la méthode de 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):
        # ...

0 votes

Qu'est-ce que num_train ?

1 votes

C'est ma faute, il a été renommé en conséquence ( dataset_size ).

0 votes

De même, lorsque je mets cela dans le modèle, la fonction forward prend les données d'entrée. Et la forme de ces données est un tenseur 5D - (32L, 35887L, 48L, 48L, 1L) . 32 est la taille du lot, puis la longueur du jeu de données et enfin la hauteur, la largeur et le canal de l'image.

26voto

ShitalShah Points 2213

Les réponses actuelles font des divisions aléatoires, ce qui présente l'inconvénient que le nombre d'échantillons par classe n'est pas garanti comme étant équilibré. Ceci est particulièrement problématique lorsque l'on veut avoir un petit nombre d'échantillons par classe. Par exemple, MNIST a 60.000 exemples, soit 6000 par chiffre. Supposons que vous ne vouliez que 30 exemples par chiffre dans votre ensemble d'entraînement. Dans ce cas, la répartition aléatoire peut produire un déséquilibre entre les classes (un chiffre ayant plus de données d'entraînement que les autres). Vous voulez donc vous assurer que chaque chiffre n'a précisément que 30 étiquettes. C'est ce qu'on appelle échantillonnage stratifié .

Une façon de le faire est d'utiliser l'interface sampler dans Pytorch et un exemple de code est ici .

Une autre façon de faire est de pirater votre chemin :). Par exemple, voici une implémentation simple pour MNIST où ds est l'ensemble de données MNIST et k est le nombre d'échantillons nécessaires pour chaque classe.

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))

Vous pouvez utiliser cette fonction comme suit :

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

6voto

prosti Points 4630

C'est le PyTorch Subset classe attachée tenant le random_split méthode. Notez que cette méthode est de base pour le SubsetRandomSampler .

enter image description here

Pour MNIST, si nous utilisons random_split :

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(loader.dataset.data.shape)
test_ds, valid_ds = torch.utils.data.random_split(loader.dataset, (50000, 10000))
print(test_ds, valid_ds)
print(test_ds.indices, valid_ds.indices)
print(test_ds.indices.shape, valid_ds.indices.shape)

Nous avons :

torch.Size([60000, 28, 28])
<torch.utils.data.dataset.Subset object at 0x0000020FD1880B00> <torch.utils.data.dataset.Subset object at 0x0000020FD1880C50>
tensor([ 1520,  4155, 45472,  ..., 37969, 45782, 34080]) tensor([ 9133, 51600, 22067,  ...,  3950, 37306, 31400])
torch.Size([50000]) torch.Size([10000])

Notre site test_ds.indices y valid_ds.indices sera aléatoire dans l'intervalle (0, 600000) . Mais si je souhaite obtenir une séquence d'indices à partir de (0, 49999) et de (50000, 59999) Je ne peux pas le faire pour le moment malheureusement, sauf que este manière.

Pratique au cas où vous courriez le benchmark MNIST où il est prédéfini ce que doit être l'ensemble de données de test et ce que doit être l'ensemble de données de validation.

0 votes

Clairement la voie la plus facile à suivre

3 votes

Y a-t-il une raison pour laquelle le code est une capture d'écran ? Veuillez éviter cela.

0voto

prosti Points 4630

N'oubliez pas que la plupart des exemples canoniques sont déjà cités. Par exemple sur cette page vous trouverez MNIST. Une croyance commune est qu'il a 60.000 images. Bang ! C'est faux ! Il contient 70.000 images, dont 60.000 images d'entraînement et 10.000 images de validation (test).

Ainsi, pour les ensembles de données canoniques, la saveur de PyTorch est de vous fournir des ensembles de données déjà crachés.

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, Dataset, TensorDataset
from torch.optim import *
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import os
import numpy as np
import random

bs=512

t = transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize(mean=(0), std=(1))]
                       )

dl_train = DataLoader( torchvision.datasets.MNIST('/data/mnist', download=True, train=True, transform=t), 
                batch_size=bs, drop_last=True, shuffle=True)
dl_valid = DataLoader( torchvision.datasets.MNIST('/data/mnist', download=True, train=False, transform=t), 
                batch_size=bs, drop_last=True, shuffle=True)

0 votes

Il me semble que le pipeline devrait être le chargement des données, la division puis la transformation - en particulier dans votre cas, vous avez codé en dur les entrées pour Normaliser. En général, celles-ci devraient être déterminées uniquement à partir du jeu de données d'entraînement, mais avec pytorch, les transformations semblent toujours être appliquées au jeu de données complet.

0 votes

À partir des données dont vous disposez, vous créez idéalement des ensembles de données de formation, de validation et de test. (TRAVALTES). La formation pour la formation, la validation pour vérifier si l'ajustement est excessif ou insuffisant. Vous calculez le score de précision, ou un autre score (f1...) pour obtenir quelques indices et idéalement créer une matrice de confusion si vous avez un problème comme la classification. Donc mon post est nul. Je vais l'améliorer dans la journée.

1 votes

Ouais, mon commentaire est plus sur la façon dont la plupart des exemples canoniques de pytorch semblent coder en dur la moyenne / std des caractéristiques comme une entrée dans Transform, généralement avec des données de test / validation pré-split. Cela semble un peu circulaire car en réalité, vous voudriez diviser les données et calculer les paramètres de Transformer à partir de l'ensemble de formation, puis les appliquer à la validation (et/ou au test). Mais la conception de DataSet / Transformer ne rend pas cela aussi facile que, par exemple, sklearn. Parfois je me demande si la mise à l'échelle ne devrait pas être effectuée par une couche nn et donc être des paramètres apprenables - mais je suppose que cela pourrait affecter la convergence.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X