[PYTHON] Préparation du jeu de données pour PyTorch

Script Python pour préparer l'ensemble de données (image) pour l'apprentissage en profondeur

Utilisez-vous train_test_split de scikit-learn, etc. lors de la division du jeu de données en train-val? Si vous préparez un dossier pour les données d'apprentissage / d'évaluation à l'avance et que vous souhaitez en récupérer les données (même si je pense qu'il est presque inexistant), vous pouvez utiliser la méthode de cet article. Cliquez ici pour le programme GitHub-moriitkys/PrepareDataSet Les spécifications du programme présenté dans cet article sont les suivantes.

--Pour DateLoader de PyTorch (Keras est également disponible) --Il est possible de définir des données pour l'apprentissage en profondeur avec l'interface utilisateur de TKinter (CreatePanel () de mylib / create_panel.py est instancié et le traitement est exécuté)

  1. Choix d'apprentissage ou de devinettes
  2. Sélection de la dorsale du modèle (ResNet, Mobilenet, MyNet)
  3. Sélection de l'exécution fractionnée des données d'entraînement / d'évaluation (s'il faut fractionner nouvellement au hasard, qu'il ait été fractionné ou non la dernière fois)
  4. Taux de données de formation (train: val)
  5. Choix de procéder à l'expansion des données
  6. Nombre total d'époques --Lorsque vous effectuez une expansion de données, MakeDataSetRGB () dans mylib / makedataset_rgb.py est instancié, les données dans le dossier de l'ensemble de données et dataset_val sont gonflées et générées dans dataset_aug et dataset_val_aug.

Comment utiliser réellement l'interface utilisateur pour déterminer les variables et générer / préparer des données

Panel6.gif

Ce qui précède est le paramètre lorsque ・ Mode train ・ ResNet ・ exécution fractionnée train-val ・ train: val = 0,65: 0,35 ・ exécution d'augmentation ・ Epochs = 40. Enfin, la structure de répertoires et la sortie suivantes sont créées.

Keras_dir_def.PNG Keras_Res_val_aug_train.PNG Keras_Res_val_aug_train_jn.PNG
Figure 1-a.État du répertoire par défaut Figure 1-b.Répertoire après fractionnement et expansion de l'ensemble de données Figure 1-c.Sortie lorsque le programme se termine avec succès

Différences entre Keras et Pytorch

La même chose dans les deux est les variables obtenues à partir du panneau (Tkinter) telles que flag_train et total_epochs. La différence réside dans la préparation de l'ensemble de données.

Préparation du jeu de données Keras

Keras prépare les données sous la forme d'un tableau de numpy. Je ne pouvais pas penser à un bon moyen, alors j'ai converti la liste avec numpy.array, l'ai sauvegardée dans npy et l'ai lue à partir de npy lors de l'apprentissage. J'ai l'intention de réduire la consommation de mémoire en vidant la liste, mais j'aimerais réécrire cette partie proprement.

Préparation de l'ensemble de données Keras (Cliquez ici pour voir le programme)
#Settings and prepare your dataset
import glob
import os
import sys
import keras
from keras import layers, models, optimizers
from keras.utils import np_utils
import keras.backend as K
import keras.layers as KL
import tensorflow as tf
from keras.preprocessing.image import load_img, img_to_array, array_to_img
from keras.preprocessing.image import random_rotation, random_shift, random_zoom
import numpy as np
import random
import matplotlib.pyplot as plt
import PIL
from PIL import Image
import cv2
from pathlib import Path
import shutil
from sklearn.model_selection import train_test_split
import mylib.makedataset_rgb as mkdataset
import mylib.create_panel as create_panel
import mylib.utils as myutils

# ------ Setting panels ------
import tkinter
from tkinter import messagebox
img_size_mynet = [28,28]# You can change input image size(Pay attention to network shape)
setting_panel = create_panel.CreatePanel(img_size_mynet = img_size_mynet)
setting_panel.create_buttons()#If you push "start", exit this line.

# ------ set params and preparing dataset ------
flag_train = setting_panel.flag_train
flag_aug = setting_panel.flag_aug
flag_split = setting_panel.flag_split
ratio_train = float(setting_panel.var_sp.get())#0.0 ~ 1.0
total_epochs = int(setting_panel.var_sp_epochs.get())

type_backbone = setting_panel.type_backbone#ex) ResNet, Mobilenet, MyNet
layer_name_gradcam = setting_panel.layer_name_gradcam# Don't use 
img_size = setting_panel.img_size#ex) ResNet:[224,224], Mobilenet:[192,192], MyNet:[28,28]
print(type_backbone)
print("img_size=" + str(img_size))

#How many classes are in "dataset" folder
categories = [i for i in os.listdir(os.getcwd().replace("/mylib", "") + "/dataset")]
categories_idx = {}#ex) HookWrench:0, SpannerWrench:1
for i, name in enumerate(categories):
    categories_idx[name] = i
nb_classes = len(categories)#ex) nb_classes=2

dirname_dataset = "dataset"# dataset folder
dirname_dataset_val = dirname_dataset + "_val"
output_folder = "outputs_keras/"+type_backbone
x_train, y_train, x_val, y_val = [],[],[],[]

def aug_dataset(dirname_dataset_1, dirname_dataset_val_1):
    '''
    This function returns updated dataset dirname 
    Contain MakeDataSetRGB() (mylib/makedataset_rgb.py)
    Argument1: Foldername (String), Argument2: Foldername (String)
    Usage:
    dirname_dataset, dirname_dataset_val = aug_dataset(dirname_dataset, dirname_dataset_val)
    '''
    dirname_dataset_aug = dirname_dataset_1 + "_aug"
    dirname_dataset_val_aug = dirname_dataset_val_1 + "_aug"
    make_dataset = mkdataset.MakeDataSetRGB()
    if os.path.exists(dirname_dataset_aug ) == True \
    or os.path.exists(dirname_dataset_val_aug ) == True:
        #https://pythonbasics.org/tkinter-messagebox/
        tki2 = tkinter.Tk()
        tki2.withdraw()
        ret = messagebox.askyesno('Vérification', '_Il y a un dossier aug._Voulez-vous vraiment supprimer le dossier aug?')
        if ret == True:
            if os.path.exists(dirname_dataset_aug ) == True:
                shutil.rmtree(dirname_dataset_aug)
            if os.path.exists(dirname_dataset_val_aug ) == True:
                shutil.rmtree(dirname_dataset_val_aug)
            make_dataset.do_augmentation(dataset_folder_name = "dataset")
            make_dataset.do_augmentation(dataset_folder_name = "dataset_val")
            tki2.destroy()
        else:
            tki2.destroy()
        tki2.mainloop()
    else:
        make_dataset.do_augmentation(dataset_folder_name = "dataset")
        make_dataset.do_augmentation(dataset_folder_name = "dataset_val")
        
    dirname_dataset_2 = dirname_dataset_1 + "_aug"
    dirname_dataset_val_2 = dirname_dataset_val_1 + "_aug"
    return dirname_dataset_2, dirname_dataset_val_2

def prepare_dataset(dirname_dataset, dirname_dataset_val):
    label = 0
    for j in categories:# Prepare Training Dataset
        files = glob.glob(dirname_dataset + "\\" + str(j) + "/*")
        for imgfile in files:
            img = load_img(imgfile, target_size=(img_size[0], img_size[1]))
            array = img_to_array(img) / 255
            x_train.append(array)
            y_train.append(label)
        label += 1

    label = 0
    for j in categories:# Prepare Validation Dataset
        files = glob.glob(dirname_dataset_val + "\\" + str(j) + "/*")
        for imgfile in files:
            img = load_img(imgfile, target_size=(img_size[0], img_size[1]))
            array = img_to_array(img) / 255
            x_val.append(array)
            y_val.append(label)
        label += 1
            
if flag_train == True:
    print("train mode")
    print("total epochs = " + str(total_epochs))
    if flag_split == True:
        revert_dataset_val()
        prepare_dataset_val()
        print("splitting complete")
    if flag_split == False and os.path.exists(dirname_dataset_val) == False:
        prepare_dataset_val()
        print("You have not splitted dataset, so splitteing automatically done")
    if flag_aug == True:
        dirname_dataset, dirname_dataset_val = aug_dataset(dirname_dataset, dirname_dataset_val)
        print("dataset source is " + dirname_dataset + "&" + dirname_dataset_val)
    elif flag_aug == False:
        dirname_dataset_aug = dirname_dataset + "_aug"
        dirname_dataset_val_aug = dirname_dataset_val + "_aug"
        make_dataset = mkdataset.MakeDataSetRGB()
        if os.path.exists(dirname_dataset_aug ) == True \
        and os.path.exists(dirname_dataset_val_aug ) == True:
            dirname_dataset = dirname_dataset_aug
            dirname_dataset_val = dirname_dataset_val_aug
    prepare_dataset(dirname_dataset, dirname_dataset_val)
    # make directory (weights_folder, outputs)
    if os.path.exists("weights_pytorch/"+type_backbone) == False:
        os.makedirs("weights_pytorch/"+type_backbone)
    if os.path.exists("outputs_pytorch/"+type_backbone) == False:
        os.makedirs("outputs_pytorch/"+type_backbone)
        
if os.path.exists(output_folder) == False:
    os.makedirs(output_folder)

# In Keras, use numpy array for NN model
if os.path.exists("tmp_npy") == False:
    os.makedirs("tmp_npy")
x_train, y_train, x_val, y_val = np.array(x_train), np.array(y_train), np.array(x_val), np.array(y_val)
np.save("tmp_npy/x_train.npy", x_train)
np.save("tmp_npy/y_train.npy", y_train)
np.save("tmp_npy/x_test.npy", x_val)
np.save("tmp_npy/y_test.npy", y_val)
x_train, y_train, x_val, y_val = [],[],[],[]
print("Complete")

En particulier, ce qui suit sont les principales parties de la lecture des données.

for imgfile in files:
    img = load_img(imgfile, target_size=(img_size[0], img_size[1]))
    array = img_to_array(img) / 255
    x_train.append(array)
    y_train.append(label)
# ~ x_val, y_Idem pour val

Voici comment charger les données dans le modèle. Lors de la classification, utilisez np_utils.to_categorical pour transformer la forme du tableau (comme One Hot label ou 1ofK vector).

y_train1=np_utils.to_categorical(y_train,nb_classes)
y_val1=np_utils.to_categorical(y_val,nb_classes)

history = model.fit(x_train,y_train1,epochs=total_epochs, callbacks = [cp_callback],batch_size=32,validation_data=(x_val,y_val1))

Préparation du jeu de données PyTorch

Dans PyTorch, lire un ensemble de données avec ImageFolder etc., diviser le train-val avec train_test_split etc. de scicit-learn et utiliser DataLoader pour collecter des données d'entraînement et des paires d'étiquettes dans des unités par lots est l'une des préparations de l'ensemble de données. Il y a deux manières.

Préparation du jeu de données PyTorch (cliquez ici pour voir le programme)
#Settings and prepare your dataset
import glob
import os
import sys
import numpy as np
import random
import matplotlib.pyplot as plt
import PIL
from PIL import Image
import cv2
import torch
import torchvision.transforms as transforms
from pathlib import Path
from torch.utils.data import DataLoader, Dataset
from torchvision.datasets import ImageFolder
from sklearn.model_selection import train_test_split
import shutil
import mylib.makedataset_rgb as mkdataset
import mylib.create_panel as create_panel
import mylib.utils as myutils

# ----- Setting buttons -----
import tkinter
from tkinter import messagebox
img_size_mynet = [28,28]# You can change input image size(Pay attention to network shape)
setting_panel = create_panel.CreatePanel(img_size_mynet = img_size_mynet)
setting_panel.create_buttons()#If you push "start", exit this line.

# ----- set params and preparing dataset -----
flag_train = setting_panel.flag_train
flag_aug = setting_panel.flag_aug
flag_split = setting_panel.flag_split
ratio_train = float(setting_panel.var_sp.get())#0.0 ~ 1.0
total_epochs = int(setting_panel.var_sp_epochs.get())

type_backbone = setting_panel.type_backbone
layer_name_gradcam = setting_panel.layer_name_gradcam
img_size = setting_panel.img_size
print(type_backbone)
print("img_size=" + str(img_size))

#How many classes are in "dataset" folder
categories = [i for i in os.listdir(os.getcwd().replace("/mylib", "") + "/dataset")]
nb_classes = len(categories)#ex) nb_classes=2

dirname_dataset = "dataset"# dataset folder
dirname_dataset_val = dirname_dataset + "_val"
output_folder = "outputs_pytorch/"+type_backbone

def aug_dataset(dirname_dataset_1, dirname_dataset_val_1):
    '''
    This function returns updated dataset dirname 
    Contain MakeDataSetRGB() (mylib/makedataset_rgb.py)
    Argument1: Foldername (String), Argument2: Foldername (String)
    Usage:
    dirname_dataset, dirname_dataset_val = aug_dataset(dirname_dataset, dirname_dataset_val)
    '''
    dirname_dataset_aug = dirname_dataset_1 + "_aug"
    dirname_dataset_val_aug = dirname_dataset_val_1 + "_aug"
    make_dataset = mkdataset.MakeDataSetRGB(do_reverse=True,
                                            do_gamma_correction=True, 
                                            do_add_noise=True, 
                                            do_cut_out=True, 
                                            do_deformation=True )
    if os.path.exists(dirname_dataset_aug ) == True \
    or os.path.exists(dirname_dataset_val_aug ) == True:
        #https://pythonbasics.org/tkinter-messagebox/
        tki2 = tkinter.Tk()
        tki2.withdraw()
        ret = messagebox.askyesno('Vérification', '_Il y a un dossier aug._Voulez-vous vraiment supprimer le dossier aug?')
        if ret == True:
            if os.path.exists(dirname_dataset_aug ) == True:
                shutil.rmtree(dirname_dataset_aug)
            if os.path.exists(dirname_dataset_val_aug ) == True:
                shutil.rmtree(dirname_dataset_val_aug)
            make_dataset.do_augmentation(dataset_folder_name = "dataset")
            make_dataset.do_augmentation(dataset_folder_name = "dataset_val")
            tki2.destroy()
        else:
            tki2.destroy()
        tki2.mainloop()
        
    else:
        make_dataset.do_augmentation(dataset_folder_name = "dataset")
        make_dataset.do_augmentation(dataset_folder_name = "dataset_val")
        
    dirname_dataset_2 = dirname_dataset_1 + "_aug"
    dirname_dataset_val_2 = dirname_dataset_val_1 + "_aug"
    return dirname_dataset_2, dirname_dataset_val_2
            
def prepare_dataset_val():
    for j in categories:
        if os.path.exists(dirname_dataset_val  + "\\" + str(j) ) == False:
            os.makedirs(dirname_dataset_val + "\\" + str(j))
            files = glob.glob(dirname_dataset + "\\" + str(j) + "/*")
            for imgfile in files:# move some data from "dataset" to "dataset_val"
                if myutils.train_or_val(ratio_train) == "val":
                    shutil.move(imgfile, dirname_dataset_val+"\\" + str(j) + "/")

def revert_dataset_val():
    '''
    Revert Dataset
    This function revert splitted validation dataset directory to dataset directory
    '''
    for j in categories:
        if os.path.exists(dirname_dataset_val  + "\\" + str(j) ) == True:
            files = glob.glob(dirname_dataset_val + "\\" + str(j) + "/*")
            for imgfile in files:#Move all images in "dataset_val" to "dataset"
                shutil.move(imgfile, dirname_dataset + "\\" + str(j))
    if os.path.exists(dirname_dataset_val) == True:
        shutil.rmtree(dirname_dataset_val)#Delete "dataset_val" folder

transform = transforms.Compose([transforms.Resize((img_size[0], img_size[1])), transforms.ToTensor()])
train_loader = []
test_loader = []

def prepare_dataset(transform, dirname_dataset, dirname_dataset_val):
    dataset = ImageFolder(dirname_dataset, transform)# Prepare Training Dataset
    dataset_val = ImageFolder(dirname_dataset_val, transform)# Prepare Validation Dataset
    print(dataset.class_to_idx)
    return dataset, dataset_val
    
batch_size_train = 32
batch_size_val = 16
def get_device(gpu_id=-1):
    global batch_size_train, batch_size_val
    if gpu_id >= 0 and torch.cuda.is_available():
        print("GPU mode")
        batch_size_train = 32
        batch_size_val = 16
        return torch.device("cuda", gpu_id)
    else:
        return torch.device("cpu")
device = get_device(gpu_id=0)    

if flag_train == True:
    print("train mode")
    print("total epochs = " + str(total_epochs))
    if flag_split == True:
        revert_dataset_val()
        prepare_dataset_val()
        print("splitting complete")
    elif flag_split == False and os.path.exists(dirname_dataset_val) == False:
        prepare_dataset_val()
        print("You have not splitted dataset, so splitteing automatically done")
    if flag_aug == True:
        dirname_dataset, dirname_dataset_val = aug_dataset(dirname_dataset, dirname_dataset_val)
        print("dataset source is " + dirname_dataset + "&" + dirname_dataset_val)
    elif flag_aug == False:
        dirname_dataset_aug = dirname_dataset + "_aug"
        dirname_dataset_val_aug = dirname_dataset_val + "_aug"
        make_dataset = mkdataset.MakeDataSetRGB()
        if os.path.exists(dirname_dataset_aug ) == True \
        and os.path.exists(dirname_dataset_val_aug ) == True:
            dirname_dataset = dirname_dataset_aug
            dirname_dataset_val = dirname_dataset_val_aug
    #prepare_dataset()
    train_data, test_data = prepare_dataset(transform, dirname_dataset, dirname_dataset_val)
    # make directory (weights_folder, outputs)
    if os.path.exists("weights_pytorch/"+type_backbone) == False:
        os.makedirs("weights_pytorch/"+type_backbone)
    if os.path.exists("outputs_pytorch/"+type_backbone) == False:
        os.makedirs("outputs_pytorch/"+type_backbone)
    # In PyTorch, use DataLoader for NN model
    train_loader = DataLoader(train_data, batch_size=batch_size_train, shuffle=True)
    test_loader = DataLoader(test_data, batch_size=batch_size_val, shuffle=True)

if os.path.exists(output_folder) == False:
    os.makedirs(output_folder)
print("Complete")

--DataSet: renvoie une paire de données et une étiquette correcte --DataLoader: vous permet de récupérer des données dans un mini-lot


def prepare_dataset(transform, dirname_dataset, dirname_dataset_val):
    dataset = ImageFolder(dirname_dataset, transform)# Prepare Training Dataset
    dataset_val = ImageFolder(dirname_dataset_val, transform)# Prepare Validation Dataset
    print(dataset.class_to_idx)
    return dataset, dataset_val
#~Abréviation~
train_data, test_data = prepare_dataset(transform, dirname_dataset, dirname_dataset_val)
#~Abréviation~
# In PyTorch, use DataLoader for NN model
    train_loader = DataLoader(train_data, batch_size=batch_size_train, shuffle=True)
    test_loader = DataLoader(test_data, batch_size=batch_size_val, shuffle=True)

À l'avenir, je publierai un article détaillant l'apprentissage à l'aide de cela, mais la méthode de mise en place des données dans le modèle est la suivante.

for batch_idx, (image, label) in enumerate(train_loader):
    #image, label = Variable(image), Variable(label)#cpu
    image, label = Variable(image).cuda(), Variable(label).cuda()
    optimizer.zero_grad()
    output = model(image)

À propos de l'application

Ce programme sera utilisé dans un article ultérieur sur Keras VS PyTorch. Le programme d'extension de données (makedataset_rgb.py) et le programme de panneau d'interface utilisateur (create_panel.py) sont séparés en mylib, donc j'espère que cela vous aidera.

référence

https://pytorch.org/docs/stable/data.html https://qiita.com/mathlive/items/2a512831878b8018db02 https://qiita.com/takurooo/items/e4c91c5d78059f92e76d

Recommended Posts