Découvrez les étapes de l’augmentation des données de vision par ordinateur
J’ai récemment utilisé Azure Custom Vision pour entraîner un classificateur d’images et exporter un modèle entraîné au format tensorflow. À l’aide de l’apprentissage par transfert, il permet d’entraîner un modèle avec seulement quelques échantillons (au moins 50 images par classe). Super, n’est-ce pas ?
Cependant, si vous démarrez votre projet à partir de zéro, vous n’aurez peut-être même pas 50 images par classe. Ou, vous pouvez avoir un nombre suffisant d’articles pour une classe et seulement quelques-uns pour l’autre. Dans ce court article, nous allons explorer quelques extraits de code qui nous permettront de générer facilement des données synthétiques.
En lieu d’introduction :
- Les techniques d’augmentation des données dans l’analyse des données sont utilisées pour augmenter la quantité de données en ajoutant des copies légèrement modifiées de données déjà existantes ou de données synthétiques nouvellement créées à partir de données existantes. Cela agit comme un régularisateur et aide à réduire le surapprentissage lors de la formation d’un modèle d’apprentissage automatique. C’est étroitement lié au suréchantillonnage dans l’analyse des données… Les transformations géométriques, le retournement, la modification des couleurs, le recadrage, la rotation, l’injection de bruit et l’effacement aléatoire sont utilisés pour augmenter l’image dans l’apprentissage en profondeur.
Wikipedia « Data augmentation »
Alors, allons y !
Étape 1 : Créer un dossier avec des classes
Pour cette expérience, j’ai créé un seul dossier appelé images avec de nombreux sous-dossiers, chacun correspondant à une classe.
Quelque chose comme ça :
-\images
image_augmentation.py
—-\class1
—-\class2
—-\class3
—-\class4
Rien de compliqué, n’est ce pas ?
Étape 2 : placez vos données dans les dossiers
Encore une fois, placez simplement vos échantillons dans un dossier correspondant. Par exemple, j’ai créé un simple classificateur d’interrupteurs électriques et préparé 2 images par classe, 2 pour la classe « ON » et deux pour la classe « OFF »
Dossier OFF
Dossier ON
Étape 3 Préparer les fonctions communes
Voici quelques fonctions utiles qui peuvent être utilisées dans différents projets, comme l’enregistrement d’images ou la reconnaissance de sous-dossiers
import os
import cv2
import imageio
import numpy as np
import imgaug as ia
import imgaug.augmenters as iaa
from PIL import Image
from datetime import datetime
from imgaug.augmentables.batches import UnnormalizedBatch
def count_files_in_folder(folder):
files_count = len([name for name in os.listdir(folder) if os.path.isfile(os.path.join(folder, name))])
return(files_count)
def save_image(image, folder):
« » »Save an image with unique name
Arguments:
image {Pillow} — image object to be saved
folder {string} — output folder
« » »
# check whether the folder exists and create one if not
if not os.path.exists(folder):
os.makedirs(folder)
# to not erase previously saved photos counter (image name) = number of photos in a folder + 1
image_counter = count_files_in_folder(folder)+1
# save image to the dedicated folder (folder name = label)
image_name = folder + ‘/’ + str(image_counter) + ‘.png’
image.save(image_name)
def get_files_in_folder(folder):
return [os.path.join(folder, name) for name in os.listdir(folder) if os.path.isfile(os.path.join(folder, name))]
def list_oversample(initial_list, max_size):
« » »duplicate a list n times or take a part of a list
Arguments:
initial_list {list} — array to be resized
max_size {int} — majority class size
« » »
resized_array = []
initial_length = len(initial_list)
new_size = max_size – initial_length
if new_size >= initial_length:
augment_rate = int(new_size/initial_length)
resized_array = initial_list*augment_rate
else:
resized_array = initial_list[:new_size]
return resized_array
def save_image_array(image_array, folder):
for image in image_array:
save_image(Image.fromarray(image), folder)
Étape 4 : paramétrer les augmentateurs
# Set augmenters
ia.seed(1)
seq = iaa.Sequential([
iaa.Fliplr(0.5), # horizontal flips
iaa.Crop(percent=(0, 0.1)), # random crops
# Small gaussian blur with random sigma between 0 and 0.5.
# But we only blur about 50% of all images.
iaa.Sometimes(
0.5,
iaa.GaussianBlur(sigma=(0, 0.5))
),
# Strengthen or weaken the contrast in each image.
iaa.LinearContrast((0.75, 1.5)),
# Add gaussian noise.
# For 50% of all images, we sample the noise once per pixel.
# For the other 50% of all images, we sample the noise per pixel AND
# channel. This can change the color (not only brightness) of the
# pixels.
iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05*255), per_channel=0.5),
# Make some images brighter and some darker.
# In 20% of all cases, we sample the multiplier once per channel,
# which can end up changing the color of the images.
iaa.Multiply((0.8, 1.2), per_channel=0.2),
# Apply affine transformations to each image.
# Scale/zoom them, translate/move them, rotate them and shear them.
iaa.Affine(
scale={« x »: (0.8, 1.2), « y »: (0.8, 1.2)},
translate_percent={« x »: (-0.2, 0.2), « y »: (-0.2, 0.2)},
rotate=(-25, 25),
shear=(-8, 8)
)
], random_order=True) # apply augmenters in random order
Étape 5 (option 1) : Définissez manuellement le nombre d’articles souhaités
Ici, le processus est assez simple, j’ordonne simplement à l’augmenter de générer N éléments par classe, disons 50 par classe
# input image
IMAGE_FOLDER = ‘images’
# all subfolders in the initial directory
image_subfolders = [os.path.join(IMAGE_FOLDER, subfolder) for subfolder in os.listdir(IMAGE_FOLDER)]
max_image_count = 50
image_target_subfolders = [subfolder for subfolder in image_subfolders if count_files_in_folder(subfolder) < max_image_count]
Étape 5 (option 2) : Définissez le nombre d’articles par classe en fonction de la classe majoritaire
Celui-ci est plus intéressant. Par exemple, nous avons 100k images pour la classe A, et seulement <1k images pour les autres classes (B, C, D etc). Il n’est pas nécessaire de générer plus de données synthétiques pour la classe majoritaire, nous définissons donc automatiquement le nombre d’éléments pour chaque classe minoritaire, en fonction de la taille de la plus grande
# input image
IMAGE_FOLDER = ‘../data/categories_resized’
# all subfolders in the initial directory
image_subfolders = [os.path.join(IMAGE_FOLDER, subfolder) for subfolder in os.listdir(IMAGE_FOLDER)]
# number of instances in the majority class
max_image_count = max([count_files_in_folder(subfolder) for subfolder in image_subfolders])
image_target_subfolders = [subfolder for subfolder in image_subfolders if count_files_in_folder(subfolder) < max_image_count]
Étape 6 : générer des données synthétiques
for subfolder in image_target_subfolders:
print (subfolder)
# =============Time calculation===============
start_time = datetime.now()
# =============Time calculation===============
# create images array per folder
image_files = get_files_in_folder(subfolder)
synthetic_image_files = list_oversample(image_files, 50)
images = [imageio.imread(image_file) for image_file in synthetic_image_files]
# apply imge augmentation on a subfolder
augmented_images = seq(images=images)
save_image_array(augmented_images, subfolder)
# =============Time calculation===============
# check the endtime
end_time = datetime.now()
# get the total time spent
time_spent = end_time – start_time
spent_minutes, spent_seconds = divmod(
time_spent.days * 86400 + time_spent.seconds, 60)
print(« {} min {} sec ».format(spent_minutes, spent_seconds))
# =============Time calculation===============
Voyons maintenant nos dossiers initiaux
Dossier OFF augmenté
Dossier ON augmenté
Nous pouvons désormais tout apporter à Azure Custom Vision pour former un classificateur.
J’espère vous avoir été utile, amusez-vous bien!
Ecrit par Alibek Jakupov, Data scientist et Microsoft MVP Artifical Intelligence.