Transformers documentation
Сегментация изображений
Сегментация изображений
Модели сегментации изображений выделяют области, соответствующие различным объектам интереса на изображении. Эти модели работают, назначая метку каждому пикселю. Существует несколько типов сегментации: семантическая сегментация, сегментация экземпляров и паноптическая сегментация.
В этом руководстве мы:
- Ознакомьтесь с различными типами сегментации.
- Пример дообучения «от и до» для семантической сегментации.
Прежде чем начать, убедитесь, что у вас установлены все необходимые библиотеки:
# 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
Мы будем использовать 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"]
В задаче сегментации инстансов цель состоит не в классификации каждого пикселя, а в предсказании маски для каждого инстанса объекта на заданном изображении. Принцип работы очень похож на обнаружение объектов: вместо ограничивающего прямоугольника для каждого инстанса используется маска сегментации. Для этого мы будем использовать 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"]
Паноптическая сегментация объединяет семантическую и инстантную сегментацию, при которой каждый пиксель классифицируется в класс и инстанс этого класса, а для каждого инстанса класса существует несколько масок. Для этого можно использовать 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>}]Давайте сравним все типы сегментации в формате «рядом».

Раз мы рассмотрели все виды сегментации, давайте подробно разберем дообучение модели для семантической сегментации.
Среди типичных практических применений семантической сегментации — обучение беспилотных автомобилей распознавать пешеходов и важную дорожную информацию, выявление клеток и аномалий на медицинских снимках, а также мониторинг изменений окружающей среды по спутниковым изображениям.
Доработка модели для сегментации
Теперь мы:
- Настройте SegFormer на датасете SceneParse150.
- Используйте дообученную модель для инференса.
Чтобы увидеть все архитектуры и контрольные точки, совместимые с этой задачей, рекомендуем обратиться к странице задачи
Загрузить набор данных 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)На этом этапе осталось выполнить только три шага:
- Определите гиперпараметры обучения в TrainingArguments. Важно не удалять неиспользуемые столбцы, иначе будет удалён столбец
image. Без столбцаimageневозможно создатьpixel_values. Установитеremove_unused_columns=False, чтобы предотвратить такое поведение! Единственный другой обязательный параметр —output_dir, который указывает, куда сохранять модель. Вы сможете загрузить эту модель в Hub, установивpush_to_hub=True(для загрузки модели необходимо войти в аккаунт Hugging Face). В конце каждой эпохи Trainer будет оценивать метрику IoU и сохранять контрольную точку обучения. - Передайте аргументы обучения в Trainer вместе с моделью, набором данных, токенизатором, data collator и функцией
compute_metrics. - Вызовите 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 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()