Problem with my first U-net : RAM

Salut, j’ai essayé de créer un réseau Unet , mais au moment de l’entraîner sur des photos de tailles 768,1024 avec un batch de 32 j’avais des problèmes de connexion GPU ou mémoire qui était toujours limitante. j’ai suivi les recommandations de chat gpt, réduit le batch à 1, j’ai fait un resize de mes images de moitié 384 et 512 et j’ai utilisé le CPU à la place du GPU, et mon kernel meurt. Je ne sais pas quoi faire la version de tensorflow que j’utilise est la 2.6.2. Je travaille dans un environnement créé par des pros. et ne l’étant pas, j’ai pris la dernière version de tf, puis une qui correspond à peu près à la date de création de leur environnement en me disant que depuis (02/2023) il y avait peut être des incompatibilités.

Mon code ci-dessous (si déjà au niveau du unet il n y a pas d 'erreurs tant mieux) et j’ai mis une sorte de fonction pour essayer de recycler l’outil du cours qui déforme les images

import des librairies

`import tensorflow
import cv2
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam

import os
import shutil
import re
import time
import datetime
import sys
import pickle

from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os
import numpy as np
from tensorflow.keras.preprocessing.image import img_to_array, load_img

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Conv2D, Dropout
from tensorflow.keras.layers import BatchNormalization, Activation, MaxPool2D, Conv2DTranspose, Concatenate

#Préliminaires

Chemins

path = ‘/home/data/results/odo_unet’
origine = path+‘/’+‘origine’
mask = path+‘/’+‘mask’

image_folder = origine
mask_folder = mask

Liste des noms de fichiers dans les dossiers

image_files = os.listdir(image_folder)
mask_files = os.listdir(mask_folder)

Nombre total d’images dans le dataset

num_images = len(image_files)

Indice pour diviser le dataset entre entraînement et validation (par exemple, 80% pour l’entraînement)

train_val_split = int(0.8 * num_images)

train_indices = np.arange(train_val_split)
val_indices = np.arange(train_val_split, num_images)

Définition du générateur de données avec augmentation

def custom_image_generator(indices, batch_size, image_folder, mask_folder)):
# Configurer l’augmentation de données
data_gen_args = dict(
rescale=1/255,
shear_range=0.2,
zoom_range=0.2,
rotation_range=40,
width_shift_range=0.5,
height_shift_range=0.5,
horizontal_flip=True,
vertical_flip=True,
fill_mode=‘nearest’
)

image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)

while True:
    # Mélange aléatoire des indices à chaque époque
    np.random.shuffle(indices)
    
    for start in range(0, len(indices), batch_size):
        end = min(start + batch_size, len(indices))
        batch_indices = indices[start:end]

        # Initialisation des listes pour les images et les masques
        input_images = []
        output_images = []

        for i in batch_indices:
            # Chargement de l'image
            input_array = cv2.imread(os.path.join(image_folder, image_files[i]))
            input_array = cv2.resize(input_array, (1024//2, 768//2))
            
            # Augmentation de l'image
            img_augmented = image_datagen.random_transform(input_array)
            input_images.append(img_augmented / 255.0)

            # Chargement du masque
            mask_array = cv2.imread(os.path.join(mask_folder, mask_files[i]))
            mask_array = cv2.resize(mask_array, (1024//2, 768//2))
            
            # Augmentation du masque
            mask_augmented = mask_datagen.random_transform(mask_array)
            output_images.append(mask_augmented / 255.0)

        yield (np.array(input_images), np.array(output_images))

Taille du batch

batch_size = 32

Générateurs pour les ensembles d’entraînement et de validation

train_generator = custom_image_generator(train_indices, batch_size, image_folder, mask_folder)
val_generator = custom_image_generator(val_indices, batch_size, image_folder, mask_folder)

Reseau Unet

def conv_block(input, num_filters):
x = Conv2D(num_filters, 3, padding=“same”)(input)
x = BatchNormalization()(x)
x = Activation(“relu”)(x)

x = Conv2D(num_filters, 3, padding="same")(x)
x = BatchNormalization()(x)
x = Activation("relu")(x)

return x

def encoder_block(input, num_filters):
x = conv_block(input, num_filters)
p = MaxPool2D((2, 2))(x)
return x, p

def decoder_block(input, skip_features, num_filters):
x = Conv2DTranspose(num_filters, (2, 2), strides=2, padding=“same”)(input)
x = Concatenate()([x, skip_features])
x = conv_block(x, num_filters)
return x

def build_unet(input_shape):
inputs = Input(input_shape)

s1, p1 = encoder_block(inputs, 64)
s2, p2 = encoder_block(p1, 128)
s3, p3 = encoder_block(p2, 256)
s4, p4 = encoder_block(p3, 512)

b1 = conv_block(p4, 1024)

d1 = decoder_block(b1, s4, 512)
d2 = decoder_block(d1, s3, 256)
d3 = decoder_block(d2, s2, 128)
d4 = decoder_block(d3, s1, 64)
num_classes = 7
# outputs = Conv2D(num_classes, 1, padding="same", activation="sigmoid")(d4)
outputs = Conv2D(num_classes, 1, padding="same", activation="softmax")(d4)

model = Model(inputs, outputs, name="U-Net")
return model

unet = build_unet((768//2,1024//2, 3))
print(unet.summary())

model_checkpoint = ModelCheckpoint(‘best_model.h5’, save_best_only=True, save_weights_only=True, monitor=‘val_loss’, mode=‘min’, verbose=1)
early_stopping = EarlyStopping(monitor=‘val_loss’, patience=5, mode=‘min’, verbose=1)
reduce_lr = ReduceLROnPlateau(monitor=‘val_loss’, factor=0.2, patience=3, min_lr=1e-6, mode=‘min’, verbose=1)

Compile the model with a specific initial learning rate

initial_learning_rate = 0.0001
optimizer = Adam(learning_rate=initial_learning_rate)

Compile the model

unet.compile(optimizer=optimizer, loss=‘categorical_crossentropy’, metrics=[‘accuracy’])

print(“modèle compilé”)

epochs = 10

Taille du batch

batch_size = 1

Fit

history = unet.fit(
train_generator,
steps_per_epoch=len(train_indices) // batch_size,
epochs=epochs,
validation_data=val_generator,
validation_steps=len(val_indices) // batch_size,
callbacks=[ early_stopping, reduce_lr]
)

print(‘ok’)
import time
time.sleep(10)

`

voilà si qq1 voit qqchose de suspect… Qu’il n’hésite pas.

Cordialement ,

Bonjour Olivier,

Dans l’ensemble, le code me semble être correct. Il y’a potentiellement un problème sur l’augmentation des données. En effet, il me semble que le code proposé n’assure pas que l’augmentation est la même (même translation, rotation, ect) que le mask:

img_augmented = image_datagen.random_transform(input_array)
mask_augmented = mask_datagen.random_transform(mask_array)

Albumentations peut être par exemple une excellente solution pour la partie augmentation de données : Mask augmentation for segmentation - Albumentations Documentation

Le générateur est aussi séquentiel, ce qui va grandement augmenter le temps de calcul (le module 173 donnera plus d’éléments sur ça). On peut par exemple créer son propre générateur, par exemple l’imbriquer dans un dataset de tensorflow (ou dataloader pour pytorch) : Creating a TF Dataset using a Data Generator | by Siladittya Manna | The Owl | Medium

Concernant le modèle, il me semble être assez imposant surtout pour les résolutions proposées. Peut être limiter son nombre de paramètre à moins de 5M (on peut diminuer d’un certain rapport le nombre de convolution 512 → 128, 64 → 16, ect)