Transformers documentation
Segmentation d'image
Segmentation d'image
Les modèles de segmentation d’images séparent les zones correspondant à différentes régions d’intérêt dans une image. Ces modèles fonctionnent en attribuant un label à chaque pixel. Il existe plusieurs types de segmentation : la segmentation sémantique, la segmentation d’instances et la segmentation panoptique.
Dans ce guide, nous allons :
- Consultez les différents types de segmentation.
- Disposer d’un exemple de fine-tuning de bout en bout pour la segmentation sémantique.
Avant de commencer, assurez-vous d'avoir installé toutes les bibliothèques nécessaires :
# uncomment to install the necessary libraries
!pip install -q datasets transformers evaluate accelerate trackioNous vous encourage à vous connecter à votre compte Hugging Face afin de pouvoir téléverser et partager votre modèle avec la communauté. Lorsque le système vous le demande, saisissez votre jeton pour vous connecter :
>>> from huggingface_hub import notebook_login
>>> notebook_login()Types de segmentation
La segmentation sémantique attribue une étiquette ou une classe à chaque pixel d’une image. Examinons la sortie d’un modèle de segmentation sémantique. Il attribue la même classe à chaque instance d’un objet qu’il rencontre dans une image ; par exemple, tous les chats sont étiquetés « cat » plutôt que « cat-1 », « cat-2 ». Nous pouvons utiliser le pipeline de segmentation d’images de Transformers pour inférer rapidement un modèle de segmentation sémantique. Examinons l’image d’exemple.
from transformers import pipeline
from PIL import Image
import requests
url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/segmentation_input.jpg"
image = Image.open(requests.get(url, stream=True).raw)
image
Nous utiliserons nvidia/segformer-b1-finetuned-cityscapes-1024-1024.
semantic_segmentation = pipeline("image-segmentation", "nvidia/segformer-b1-finetuned-cityscapes-1024-1024")
results = semantic_segmentation(image)
resultsLa sortie du pipeline de segmentation inclut un masque pour chaque classe prédite.
[{'score': None,
'label': 'road',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'sidewalk',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'building',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'wall',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'pole',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'traffic sign',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'vegetation',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'terrain',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'sky',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': None,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>}]En examinant le masque de la classe « voiture », on constate que chaque voiture est classée avec le même masque.
results[-1]["mask"]
En segmentation d’instances, l’objectif n’est pas de classifier chaque pixel, mais de prédire un masque pour chaque instance d’objet dans une image donnée. Le fonctionnement est très similaire à celui de la détection d’objets, où une boîte englobante est associée à chaque instance, mais avec un masque de segmentation à la place. Nous utiliserons facebook/mask2former-swin-large-cityscapes-instance pour cela.
instance_segmentation = pipeline("image-segmentation", "facebook/mask2former-swin-large-cityscapes-instance")
results = instance_segmentation(image)
resultsComme vous pouvez le voir ci-dessous, plusieurs voitures sont classées, et il n’y a pas de classification pour les pixels autres que ceux qui appartiennent aux instances de voiture et de personne.
[{'score': 0.999944,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999945,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999652,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.903529,
'label': 'person',
'mask': <PIL.Image.Image image mode=L size=612x415>}]Examen d'un des masques de voiture ci-dessous.
results[2]["mask"]
La segmentation panoptique combine la segmentation sémantique et la segmentation d’instances, où chaque pixel est classé dans une classe et une instance de cette classe, et il existe plusieurs masques pour chaque instance d’une classe. Nous pouvons utiliser facebook/mask2former-swin-large-cityscapes-panoptic à cet effet.
panoptic_segmentation = pipeline("image-segmentation", "facebook/mask2former-swin-large-cityscapes-panoptic")
results = panoptic_segmentation(image)
resultsComme vous pouvez le voir ci-dessous, nous disposons de davantage de classes. Nous illustrerons plus tard que chaque pixel est classé dans l'une de ces classes.
[{'score': 0.999981,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999958,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.99997,
'label': 'vegetation',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999575,
'label': 'pole',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999958,
'label': 'building',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999634,
'label': 'road',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.996092,
'label': 'sidewalk',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.999221,
'label': 'car',
'mask': <PIL.Image.Image image mode=L size=612x415>},
{'score': 0.99987,
'label': 'sky',
'mask': <PIL.Image.Image image mode=L size=612x415>}]Faisons une comparaison côte à côte pour tous les types de segmentation.

Après avoir vu tous les types de segmentation, plongeons en profondeur dans le fine-tuning d'un modèle pour la segmentation sémantique.
Les applications courantes de la segmentation sémantique dans le monde réel incluent l’entraînement des voitures autonomes à identifier les piétons et les informations de circulation importantes, l’identification de cellules et d’anomalies dans les images médicales, ainsi que le suivi des changements environnementaux à partir d’images satellites.
Ajustement fin d'un modèle pour la segmentation
Nous allons maintenant :
- Effectuer le fine-tuning de SegFormer sur le jeu de données SceneParse150.
- Utilisez votre modèle affiné pour l'inférence.
Pour consulter toutes les architectures et points de contrôle compatibles avec cette tâche, nous vous recommandons de consulter la page de la tâche
Charger le jeu de données SceneParse150
Commencez par charger un sous-ensemble plus petit du jeu de données SceneParse150 à partir de la bibliothèque 🤗 Datasets. Cela vous permettra de faire des essais et de vous assurer que tout fonctionne correctement avant de consacrer plus de temps à l’entraînement sur l’ensemble du jeu de données.
>>> from datasets import load_dataset
>>> ds = load_dataset("merve/scene_parse_150", split="train[:50]")Divisez le split train du jeu de données en un ensemble d’entraînement et un ensemble de test à l’aide de la méthode train_test_split :
>>> ds = ds.train_test_split(test_size=0.2)
>>> train_ds = ds["train"]
>>> test_ds = ds["test"]Puis, jetez un œil à un exemple :
>>> train_ds[0]
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=512x683 at 0x7F9B0C201F90>,
'annotation': <PIL.PngImagePlugin.PngImageFile image mode=L size=512x683 at 0x7F9B0C201DD0>,
'scene_category': 368}
# view the image
>>> train_ds[0]["image"]image: une image PIL de la scène.annotation: une image PIL de la carte de segmentation, qui est également la cible du modèle.scene_category: un identifiant de catégorie qui décrit la scène de l’image, par exemple « cuisine » ou « bureau ». Dans ce guide, vous n’aurez besoin que deimageetannotation, qui sont toutes deux des images PIL.
Vous devrez également créer un dictionnaire qui associe un identifiant d'étiquette à une classe d'étiquette, ce qui sera utile lors de la configuration du modèle plus tard. Téléchargez les correspondances depuis le Hub et créez les dictionnaires id2label et label2id :
>>> import json
>>> from pathlib import Path
>>> from huggingface_hub import hf_hub_download
>>> repo_id = "huggingface/label-files"
>>> filename = "ade20k-id2label.json"
>>> id2label = json.loads(Path(hf_hub_download(repo_id, filename, repo_type="dataset")).read_text())
>>> id2label = {int(k): v for k, v in id2label.items()}
>>> label2id = {v: k for k, v in id2label.items()}
>>> num_labels = len(id2label)Prétraitement
L’étape suivante consiste à charger un processeur d’images SegFormer afin de préparer les images et les annotations pour le modèle. Certains jeux de données, comme celui-ci, utilisent l’indice zéro comme classe d’arrière-plan. Toutefois, la classe d’arrière-plan n’est pas réellement incluse parmi les 150 classes, il faut donc définir do_reduce_labels=True pour soustraire un à toutes les étiquettes. L’indice zéro est remplacé par 255 afin qu’il soit ignoré par la fonction de perte de SegFormer :
>>> from transformers import AutoImageProcessor
>>> checkpoint = "nvidia/mit-b0"
>>> image_processor = AutoImageProcessor.from_pretrained(checkpoint, do_reduce_labels=True)Il est courant d’appliquer des augmentations de données à un jeu de données d’images afin de rendre un modèle plus robuste face au surapprentissage. Dans ce guide, vous utiliserez la fonction ColorJitter de torchvision pour modifier aléatoirement les propriétés de couleur d’une image, mais vous pouvez également utiliser n’importe quelle bibliothèque d’images de votre choix.
>>> from torchvision.transforms import ColorJitter
>>> jitter = ColorJitter(brightness=0.25, contrast=0.25, saturation=0.25, hue=0.1)Now create two preprocessing functions to prepare the images and annotations for the model. These functions convert the images into pixel_values and annotations to labels. For the training set, jitter is applied before providing the images to the image processor. For the test set, the image processor crops and normalizes the images, and only crops the labels because no data augmentation is applied during testing.
>>> def train_transforms(example_batch):
... images = [jitter(x) for x in example_batch["image"]]
... labels = [x for x in example_batch["annotation"]]
... inputs = image_processor(images, labels)
... return inputs
>>> def val_transforms(example_batch):
... images = [x for x in example_batch["image"]]
... labels = [x for x in example_batch["annotation"]]
... inputs = image_processor(images, labels)
... return inputsPour appliquer le jitter à l'ensemble du jeu de données, utilisez la fonction set_transform de 🤗 Datasets. La transformation est appliquée à la volée, ce qui est plus rapide et consomme moins d'espace disque :
>>> train_ds.set_transform(train_transforms)
>>> test_ds.set_transform(val_transforms)Évaluer
Inclure une métrique pendant l’entraînement est souvent utile pour évaluer les performances de votre modèle. Vous pouvez charger rapidement une méthode d’évaluation avec la bibliothèque 🤗 Evaluate. Pour cette tâche, chargez la métrique mean Intersection over Union (IoU) (consultez le quick tour de 🤗 Evaluate pour en savoir plus sur la façon de charger et de calculer une métrique) :
>>> import evaluate
>>> metric = evaluate.load("mean_iou")Créez ensuite une fonction pour calculer les métriques. Vos prédictions doivent d'abord être converties en logits, puis redimensionnées pour correspondre à la taille des étiquettes avant d'appeler compute :
>>> import numpy as np
>>> import torch
>>> from torch import nn
>>> def compute_metrics(eval_pred):
... with torch.no_grad():
... logits, labels = eval_pred
... logits_tensor = torch.from_numpy(logits)
... logits_tensor = nn.functional.interpolate(
... logits_tensor,
... size=labels.shape[-2:],
... mode="bilinear",
... align_corners=False,
... ).argmax(dim=1)
... pred_labels = logits_tensor.detach().cpu().numpy()
... metrics = metric.compute(
... predictions=pred_labels,
... references=labels,
... num_labels=num_labels,
... ignore_index=255,
... reduce_labels=False,
... )
... for key, value in metrics.items():
... if isinstance(value, np.ndarray):
... metrics[key] = value.tolist()
... return metricsVotre fonction compute_metrics est prête, et vous y reviendrez lors de la configuration de l’entraînement.
Entraînement
Si vous n’êtes pas familier avec l’ajustement fin d’un modèle à l’aide du Trainer, consultez le tutoriel de base ici !
Vous êtes prêt à commencer l’entraînement de votre modèle ! Chargez SegFormer avec AutoModelForSemanticSegmentation et transmettez au modèle la correspondance entre les identifiants de classes et les classes de labels :
>>> from transformers import AutoModelForSemanticSegmentation, TrainingArguments, Trainer
>>> model = AutoModelForSemanticSegmentation.from_pretrained(checkpoint, id2label=id2label, label2id=label2id)À ce stade, il ne reste que trois étapes :
- Définissez vos hyperparamètres d’entraînement dans TrainingArguments. Il est important de ne pas supprimer les colonnes inutilisées, car cela ferait disparaître la colonne
image. Sans la colonneimage, vous ne pouvez pas créerpixel_values. Définissezremove_unused_columns=Falsepour éviter ce comportement ! Le seul autre paramètre requis estoutput_dir, qui spécifie l’endroit où enregistrer votre modèle. Vous pousserez ce modèle vers le Hub en définissantpush_to_hub=True(vous devez être connecté à Hugging Face pour téléverser votre modèle). À la fin de chaque époque, le Trainer évaluera la métrique IoU et enregistrera le point de contrôle d’entraînement. - Transmettez les arguments d’entraînement à Trainer en même temps que le modèle, le jeu de données, le tokenizer, le data collator et la fonction
compute_metrics. - Appel de train() pour l’ajustement fin de votre modèle.
>>> training_args = TrainingArguments(
... output_dir="segformer-b0-scene-parse-150",
... learning_rate=6e-5,
... num_train_epochs=50,
... per_device_train_batch_size=2,
... per_device_eval_batch_size=2,
... save_total_limit=3,
... eval_strategy="steps",
... save_strategy="steps",
... save_steps=20,
... eval_steps=20,
... logging_steps=1,
... report_to="trackio",
... run_name="scene-parse-150",
... eval_accumulation_steps=5,
... remove_unused_columns=False,
... push_to_hub=True,
... )
>>> trainer = Trainer(
... model=model,
... args=training_args,
... train_dataset=train_ds,
... eval_dataset=test_ds,
... compute_metrics=compute_metrics,
... )
>>> trainer.train()Une fois l’entraînement terminé, partagez votre modèle sur le Hub à l’aide de la méthode push_to_hub() afin que chacun puisse l’utiliser :
>>> trainer.push_to_hub()Inférence
Parfait, maintenant que vous avez affiné un modèle, vous pouvez l'utiliser pour l'inférence !
Rechargez le jeu de données et chargez une image pour l'inférence.
>>> from datasets import load_dataset
>>> ds = load_dataset("merve/scene_parse_150", split="train[:50]")
>>> ds = ds.train_test_split(test_size=0.2)
>>> test_ds = ds["test"]
>>> image = ds["test"][0]["image"]
>>> image
Nous allons maintenant voir comment effectuer une inférence sans pipeline. Traitez l’image avec un processeur d’images et placez pixel_values sur un GPU :
>>> from accelerate import Accelerator
>>> device = Accelerator().device
>>> encoding = image_processor(image, return_tensors="pt")
>>> pixel_values = encoding.pixel_values.to(device)Transmettez votre entrée au modèle et renvoyez les logits :
>>> outputs = model(pixel_values=pixel_values)
>>> logits = outputs.logits.cpu()Ensuite, remettez les logits à l'échelle de la taille d'image d'origine :
>>> upsampled_logits = nn.functional.interpolate(
... logits,
... size=image.size[::-1],
... mode="bilinear",
... align_corners=False,
... )
>>> pred_seg = upsampled_logits.argmax(dim=1)[0]Pour visualiser les résultats, chargez la palette de couleurs du jeu de données sous la forme de ade_palette(), qui associe chaque classe à ses valeurs RGB.
def ade_palette():
return np.asarray([
[0, 0, 0],
[120, 120, 120],
[180, 120, 120],
[6, 230, 230],
[80, 50, 50],
[4, 200, 3],
[120, 120, 80],
[140, 140, 140],
[204, 5, 255],
[230, 230, 230],
[4, 250, 7],
[224, 5, 255],
[235, 255, 7],
[150, 5, 61],
[120, 120, 70],
[8, 255, 51],
[255, 6, 82],
[143, 255, 140],
[204, 255, 4],
[255, 51, 7],
[204, 70, 3],
[0, 102, 200],
[61, 230, 250],
[255, 6, 51],
[11, 102, 255],
[255, 7, 71],
[255, 9, 224],
[9, 7, 230],
[220, 220, 220],
[255, 9, 92],
[112, 9, 255],
[8, 255, 214],
[7, 255, 224],
[255, 184, 6],
[10, 255, 71],
[255, 41, 10],
[7, 255, 255],
[224, 255, 8],
[102, 8, 255],
[255, 61, 6],
[255, 194, 7],
[255, 122, 8],
[0, 255, 20],
[255, 8, 41],
[255, 5, 153],
[6, 51, 255],
[235, 12, 255],
[160, 150, 20],
[0, 163, 255],
[140, 140, 140],
[250, 10, 15],
[20, 255, 0],
[31, 255, 0],
[255, 31, 0],
[255, 224, 0],
[153, 255, 0],
[0, 0, 255],
[255, 71, 0],
[0, 235, 255],
[0, 173, 255],
[31, 0, 255],
[11, 200, 200],
[255, 82, 0],
[0, 255, 245],
[0, 61, 255],
[0, 255, 112],
[0, 255, 133],
[255, 0, 0],
[255, 163, 0],
[255, 102, 0],
[194, 255, 0],
[0, 143, 255],
[51, 255, 0],
[0, 82, 255],
[0, 255, 41],
[0, 255, 173],
[10, 0, 255],
[173, 255, 0],
[0, 255, 153],
[255, 92, 0],
[255, 0, 255],
[255, 0, 245],
[255, 0, 102],
[255, 173, 0],
[255, 0, 20],
[255, 184, 184],
[0, 31, 255],
[0, 255, 61],
[0, 71, 255],
[255, 0, 204],
[0, 255, 194],
[0, 255, 82],
[0, 10, 255],
[0, 112, 255],
[51, 0, 255],
[0, 194, 255],
[0, 122, 255],
[0, 255, 163],
[255, 153, 0],
[0, 255, 10],
[255, 112, 0],
[143, 255, 0],
[82, 0, 255],
[163, 255, 0],
[255, 235, 0],
[8, 184, 170],
[133, 0, 255],
[0, 255, 92],
[184, 0, 255],
[255, 0, 31],
[0, 184, 255],
[0, 214, 255],
[255, 0, 112],
[92, 255, 0],
[0, 224, 255],
[112, 224, 255],
[70, 184, 160],
[163, 0, 255],
[153, 0, 255],
[71, 255, 0],
[255, 0, 163],
[255, 204, 0],
[255, 0, 143],
[0, 255, 235],
[133, 255, 0],
[255, 0, 235],
[245, 0, 255],
[255, 0, 122],
[255, 245, 0],
[10, 190, 212],
[214, 255, 0],
[0, 204, 255],
[20, 0, 255],
[255, 255, 0],
[0, 153, 255],
[0, 41, 255],
[0, 255, 204],
[41, 0, 255],
[41, 255, 0],
[173, 0, 255],
[0, 245, 255],
[71, 0, 255],
[122, 0, 255],
[0, 255, 184],
[0, 92, 255],
[184, 255, 0],
[0, 133, 255],
[255, 214, 0],
[25, 194, 194],
[102, 255, 0],
[92, 0, 255],
])Vous pouvez ensuite combiner et afficher votre image et la carte de segmentation prédite :
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> color_seg = np.zeros((pred_seg.shape[0], pred_seg.shape[1], 3), dtype=np.uint8)
>>> palette = np.array(ade_palette())
>>> for label, color in enumerate(palette):
... color_seg[pred_seg == label, :] = color
>>> color_seg = color_seg[..., ::-1] # convert to BGR
>>> img = np.array(image) * 0.5 + color_seg * 0.5 # plot the image with the segmentation map
>>> img = img.astype(np.uint8)
>>> plt.figure(figsize=(15, 10))
>>> plt.imshow(img)
>>> plt.show()