Transformers documentation

Сегментация изображений

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Сегментация изображений

Модели сегментации изображений выделяют области, соответствующие различным объектам интереса на изображении. Эти модели работают, назначая метку каждому пикселю. Существует несколько типов сегментации: семантическая сегментация, сегментация экземпляров и паноптическая сегментация.

В этом руководстве мы:

  1. Ознакомьтесь с различными типами сегментации.
  2. Пример дообучения «от и до» для семантической сегментации.

Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:

# uncomment to install the necessary libraries
!pip install -q datasets transformers evaluate accelerate trackio

Мы рекомендуем вам войти в свой аккаунт Hugging Face, чтобы вы могли загрузить свою модель и поделиться ею с сообществом. При появлении запроса введите токен для входа:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Типы сегментации

Семантическая сегментация присваивает метку или класс каждому пикселю изображения. Рассмотрим вывод модели семантической сегментации. Она присваивает один и тот же класс каждому экземпляру объекта, который обнаруживает на изображении, например, все кошки будут помечены как «кошка», а не «кошка-1», «кошка-2». Мы можем использовать конвейер сегментации изображений из transformers, чтобы быстро выполнить инференс модели семантической сегментации. Рассмотрим пример изображения.

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
Segmentation Input

Мы будем использовать nvidia/segformer-b1-finetuned-cityscapes-1024-1024.

semantic_segmentation = pipeline("image-segmentation", "nvidia/segformer-b1-finetuned-cityscapes-1024-1024")
results = semantic_segmentation(image)
results

Вывод конвейера сегментации включает маску для каждого предсказанного класса.

[{'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>}]

Рассматривая маску для класса «автомобиль», мы видим, что каждый автомобиль классифицируется с использованием одной и той же маски.

results[-1]["mask"]
Semantic Segmentation Output

В задаче сегментации инстансов цель состоит не в классификации каждого пикселя, а в предсказании маски для каждого инстанса объекта на заданном изображении. Принцип работы очень похож на обнаружение объектов: вместо ограничивающего прямоугольника для каждого инстанса используется маска сегментации. Для этого мы будем использовать facebook/mask2former-swin-large-cityscapes-instance.

instance_segmentation = pipeline("image-segmentation", "facebook/mask2former-swin-large-cityscapes-instance")
results = instance_segmentation(image)
results

Как видно ниже, классифицировано несколько автомобилей, при этом пиксели, не принадлежащие экземплярам автомобилей и людей, не получают классификации.

[{'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>}]

Рассмотрим одну из масок автомобиля ниже.

results[2]["mask"]
Semantic Segmentation Output

Паноптическая сегментация объединяет семантическую и инстантную сегментацию, при которой каждый пиксель классифицируется в класс и инстанс этого класса, а для каждого инстанса класса существует несколько масок. Для этого можно использовать facebook/mask2former-swin-large-cityscapes-panoptic.

panoptic_segmentation = pipeline("image-segmentation", "facebook/mask2former-swin-large-cityscapes-panoptic")
results = panoptic_segmentation(image)
results

Как видно ниже, у нас больше классов. Позже мы покажем, что каждый пиксель классифицируется в один из этих классов.

[{'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>}]

Давайте сравним все типы сегментации в формате «рядом».

Segmentation Maps Compared

Раз мы рассмотрели все виды сегментации, давайте подробно разберем дообучение модели для семантической сегментации.

Среди типичных практических применений семантической сегментации — обучение беспилотных автомобилей распознавать пешеходов и важную дорожную информацию, выявление клеток и аномалий на медицинских снимках, а также мониторинг изменений окружающей среды по спутниковым изображениям.

Доработка модели для сегментации

Теперь мы:

  1. Настройте SegFormer на датасете SceneParse150.
  2. Используйте дообученную модель для инференса.

Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к странице задачи

Загрузить набор данных SceneParse150

Начните с загрузки небольшой подвыборки набора данных SceneParse150 из библиотеки 🤗 Datasets. Это позволит вам провести эксперименты и убедиться, что всё работает, прежде чем тратить время на обучение на полном наборе данных.

>>> from datasets import load_dataset

>>> ds = load_dataset("merve/scene_parse_150", split="train[:50]")

Разбейте train-подмножество датасета на обучающую и тестовую выборки с помощью метода train_test_split:

>>> ds = ds.train_test_split(test_size=0.2)
>>> train_ds = ds["train"]
>>> test_ds = ds["test"]

Теперь рассмотрим пример:

>>> 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: изображение сцены в формате PIL.
  • annotation: изображение карты сегментации в формате PIL, которое также является целевым значением модели.
  • scene_category: идентификатор категории, описывающий сцену на изображении, например «кухня» или «офис». В этом руководстве вам понадобятся только image и annotation, оба из которых являются изображениями в формате PIL.

Также вам понадобится словарь, сопоставляющий идентификатор метки с классом метки, который пригодится при последующей настройке модели. Скачайте соответствия с Hub и создайте словари id2label и 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)

Предобработка

Следующий шаг — загрузить процессор изображений SegFormer, чтобы подготовить изображения и аннотации для модели. Некоторые наборы данных, как и этот, используют нулевой индекс в качестве класса фона. Однако класс фона фактически не входит в 150 классов, поэтому необходимо установить do_reduce_labels=True, чтобы вычесть единицу из всех меток. Нулевой индекс заменяется на 255, чтобы функция потерь SegFormer его игнорировала:

>>> from transformers import AutoImageProcessor

>>> checkpoint = "nvidia/mit-b0"
>>> image_processor = AutoImageProcessor.from_pretrained(checkpoint, do_reduce_labels=True)

Часто к набору изображений применяют некоторые преобразования данных, чтобы сделать модель более устойчивой к переобучению. В этом руководстве вы используете функцию ColorJitter из torchvision, чтобы случайно изменять цветовые свойства изображения, но вы можете использовать любую другую библиотеку для работы с изображениями.

>>> from torchvision.transforms import ColorJitter

>>> jitter = ColorJitter(brightness=0.25, contrast=0.25, saturation=0.25, hue=0.1)

Теперь создайте две функции предобработки для подготовки изображений и аннотаций для модели. Эти функции преобразуют изображения в pixel_values, а аннотации — в labels. Для обучающей выборки перед передачей изображений в процессор изображений применяется jitter. Для тестовой выборки процессор изображений обрезает и нормализует images, а labels — только обрезает, так как при тестировании аугментация данных не применяется.

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

Чтобы применить jitter ко всему набору данных, используйте функцию set_transform из 🤗 Datasets. Преобразование применяется на лету, что быстрее и требует меньше места на диске:

>>> train_ds.set_transform(train_transforms)
>>> test_ds.set_transform(val_transforms)

Оценка

Включение метрики в процесс обучения часто помогает оценить качество модели. Вы можете быстро загрузить метод оценки с помощью библиотеки 🤗 Evaluate. Для этой задачи загрузите метрику mean Intersection over Union (IoU) (ознакомьтесь с быстрым обзором 🤗 Evaluate, чтобы узнать больше о том, как загружать и вычислять метрики):

>>> import evaluate

>>> metric = evaluate.load("mean_iou")

Затем создайте функцию для вычисления метрик. Сначала ваши предсказания нужно преобразовать в логиты, а затем изменить их размерность, чтобы она соответствовала размеру меток, прежде чем вызывать 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 metrics

Ваша функция compute_metrics готова, и вы вернётесь к ней при настройке обучения.

Обучение

Если вы не знакомы с дообучением модели с помощью Trainer, загляните в базовое руководство здесь!

Теперь вы готовы приступить к обучению модели! Загрузите SegFormer с помощью AutoModelForSemanticSegmentation и передайте модели соответствие между идентификаторами меток и классами меток:

>>> from transformers import AutoModelForSemanticSegmentation, TrainingArguments, Trainer

>>> model = AutoModelForSemanticSegmentation.from_pretrained(checkpoint, id2label=id2label, label2id=label2id)

На этом этапе осталось выполнить только три шага:

  1. Определите гиперпараметры обучения в TrainingArguments. Важно не удалять неиспользуемые столбцы, иначе будет удалён столбец image. Без столбца image невозможно создать pixel_values. Установите remove_unused_columns=False, чтобы предотвратить такое поведение! Единственный другой обязательный параметр — output_dir, который указывает, куда сохранять модель. Вы сможете загрузить эту модель в Hub, установив push_to_hub=True (для загрузки модели необходимо войти в аккаунт Hugging Face). В конце каждой эпохи Trainer будет оценивать метрику IoU и сохранять контрольную точку обучения.
  2. Передайте аргументы обучения в Trainer вместе с моделью, набором данных, токенизатором, data collator и функцией compute_metrics.
  3. Вызовите train(), чтобы дообучить модель.
>>> 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()

После завершения обучения используйте метод push_to_hub(), чтобы опубликовать модель на Hub и дать всем возможность её использовать:

>>> trainer.push_to_hub()

Вывод

Отлично, теперь, когда вы дообучили модель, вы можете использовать её для инференса!

Перезагрузите набор данных и загрузите изображение для инференса.

>>> 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
Image of bedroom

Теперь рассмотрим, как выполнять инференс без использования пайплайна. Обработайте изображение с помощью image processor и переместите pixel_values на GPU:

>>> from accelerate import Accelerator
>>> device = Accelerator().device
>>> encoding = image_processor(image, return_tensors="pt")
>>> pixel_values = encoding.pixel_values.to(device)

Передайте входные данные в модель и верните logits:

>>> outputs = model(pixel_values=pixel_values)
>>> logits = outputs.logits.cpu()

Далее масштабируйте logits до исходного размера изображения:

>>> upsampled_logits = nn.functional.interpolate(
...     logits,
...     size=image.size[::-1],
...     mode="bilinear",
...     align_corners=False,
... )

>>> pred_seg = upsampled_logits.argmax(dim=1)[0]

Для визуализации результатов загрузите палитру датасета как ade_palette(), которая сопоставляет каждый класс с его значениями 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],
  ])

Затем вы можете объединить и построить график вашего изображения и предсказанной карты сегментации:

>>> 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()
Image of bedroom overlaid with segmentation map
Update on GitHub