Transformers documentation

Phân đoạn hình ảnh

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

and get access to the augmented documentation experience

to get started

Phân đoạn hình ảnh

Mô hình phân đoạn hình ảnh tách các vùng tương ứng với các khu vực quan tâm khác nhau trong một hình ảnh. Các mô hình này hoạt động bằng cách gán một nhãn cho từng pixel. Có một số loại phân đoạn: phân đoạn ngữ nghĩa, phân đoạn thực thể và phân đoạn toàn cảnh.

Trong hướng dẫn này, chúng ta sẽ:

  1. Khám phá các loại phân đoạn khác nhau.
  2. Có một ví dụ fine-tuning end-to-end cho phân đoạn ngữ nghĩa.

Trước khi bắt đầu, hãy đảm bảo bạn đã cài đặt tất cả các thư viện cần thiết:

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

Chúng tôi khuyến khích bạn đăng nhập vào tài khoản Hugging Face của mình để có thể tải lên và chia sẻ mô hình của bạn với cộng đồng. Khi được yêu cầu, hãy nhập token của bạn để đăng nhập:

>>> from huggingface_hub import notebook_login

>>> notebook_login()

Các loại phân đoạn

Phân đoạn ngữ nghĩa (Semantic segmentation) gán một nhãn hoặc lớp cho từng pixel trong ảnh. Hãy xem xét đầu ra của một mô hình phân đoạn ngữ nghĩa. Mô hình sẽ gán cùng một lớp cho mọi thể hiện của một đối tượng mà nó gặp trong ảnh, ví dụ, tất cả các con mèo sẽ được dán nhãn là “cat” thay vì “cat-1”, “cat-2”. Chúng ta có thể sử dụng pipeline phân đoạn ảnh của transformers để suy luận nhanh một mô hình phân đoạn ngữ nghĩa. Hãy xem xét ảnh ví dụ.

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

Chúng ta sẽ sử dụng nvidia/segformer-b1-finetuned-cityscapes-1024-1024.

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

Đầu ra của pipeline phân đoạn bao gồm một mask cho mỗi lớp được dự đoán.

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

Khi xem xét mặt nạ cho lớp ô tô, chúng ta có thể thấy mọi chiếc ô tô đều được phân loại với cùng một mặt nạ.

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

Trong phân đoạn instance, mục tiêu không phải là phân loại từng pixel, mà là dự đoán một mask cho mọi instance của một đối tượng trong một hình ảnh cho trước. Cách hoạt động rất giống với phát hiện đối tượng, trong đó có một bounding box cho mỗi instance, nhưng ở đây là một segmentation mask. Chúng ta sẽ sử dụng facebook/mask2former-swin-large-cityscapes-instance cho mục đích này.

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

Như bạn có thể thấy bên dưới, có nhiều chiếc xe được phân loại, và không có phân loại nào cho các pixel khác ngoài những pixel thuộc về các instance của xe và người.

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

Xem xét một trong các mặt nạ xe hơi bên dưới.

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

Phân đoạn toàn cảnh (Panoptic segmentation) kết hợp phân đoạn ngữ nghĩa và phân đoạn thực thể, trong đó mỗi pixel được phân loại vào một lớp và một thực thể của lớp đó, và có nhiều mặt nạ (mask) cho mỗi thực thể của một lớp. Chúng ta có thể sử dụng facebook/mask2former-swin-large-cityscapes-panoptic cho mục đích này.

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

Như bạn có thể thấy bên dưới, chúng ta có nhiều lớp hơn. Chúng ta sẽ minh họa sau đó để thấy rằng mỗi pixel được phân loại vào một trong các lớp.

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

Hãy cùng so sánh song song tất cả các loại phân đoạn.

Segmentation Maps Compared

Xem xét tất cả các loại phân đoạn, hãy cùng tìm hiểu sâu về việc tinh chỉnh một mô hình cho phân đoạn ngữ nghĩa.

Các ứng dụng thực tế phổ biến của phân đoạn ngữ nghĩa bao gồm huấn luyện xe tự lái để nhận diện người đi bộ và các thông tin giao thông quan trọng, nhận diện tế bào và các bất thường trong hình ảnh y tế, và giám sát các thay đổi môi trường từ hình ảnh vệ tinh.

Chỉnh huấn luyện Mô hình cho Phân đoạn

Chúng ta sẽ:

  1. Huấn luyện lại SegFormer trên bộ dữ liệu SceneParse150.
  2. Sử dụng mô hình đã tinh chỉnh của bạn để suy luận.

Để xem tất cả các kiến trúc và checkpoint tương thích với tác vụ này, chúng tôi khuyến nghị bạn kiểm tra trang tác vụ

Tải bộ dữ liệu SceneParse150

Bắt đầu bằng cách tải một tập con nhỏ hơn của bộ dữ liệu SceneParse150 từ thư viện 🤗 Datasets. Điều này sẽ cho bạn cơ hội thử nghiệm và đảm bảo mọi thứ hoạt động trước khi dành thêm thời gian huấn luyện trên toàn bộ bộ dữ liệu.

>>> from datasets import load_dataset

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

Chia split train của bộ dữ liệu thành tập train và tập test bằng phương thức train_test_split:

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

Sau đó, hãy xem một ví dụ:

>>> 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: một hình ảnh PIL của cảnh.
  • annotation: một ảnh PIL của bản đồ phân đoạn, đây cũng là mục tiêu của mô hình.
  • scene_category: một id danh mục mô tả cảnh trong ảnh, ví dụ như “kitchen” hoặc “office”. Trong hướng dẫn này, bạn chỉ cần image và annotation, cả hai đều là ảnh PIL.

Bạn cũng sẽ cần tạo một từ điển ánh xạ từ id nhãn sang lớp nhãn, điều này sẽ hữu ích khi bạn thiết lập mô hình sau này. Tải các ánh xạ từ Hub và tạo các từ điển id2label và 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)

Tiền xử lý

Bước tiếp theo là nạp một bộ xử lý ảnh SegFormer để chuẩn bị ảnh và chú thích cho mô hình. Một số bộ dữ liệu, như bộ này, sử dụng chỉ số không làm lớp nền. Tuy nhiên, lớp nền thực tế không được bao gồm trong 150 lớp, vì vậy bạn cần đặt do_reduce_labels=True để trừ một khỏi tất cả các nhãn. Chỉ số không được thay thế bằng 255 để hàm mất mát của SegFormer bỏ qua nó:

>>> from transformers import AutoImageProcessor

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

Việc áp dụng một số kỹ thuật tăng cường dữ liệu cho bộ dữ liệu ảnh là phổ biến nhằm giúp mô hình chống lại hiện tượng quá khớp tốt hơn. Trong hướng dẫn này, bạn sẽ sử dụng hàm ColorJitter từ torchvision để thay đổi ngẫu nhiên các thuộc tính màu của một ảnh, nhưng bạn cũng có thể sử dụng bất kỳ thư viện ảnh nào bạn thích.

>>> from torchvision.transforms import ColorJitter

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

Bây giờ hãy tạo hai hàm tiền xử lý để chuẩn bị ảnh và chú thích cho mô hình. Các hàm này chuyển đổi ảnh thành pixel_values và chú thích thành labels. Đối với tập huấn luyện, jitter được áp dụng trước khi cung cấp ảnh cho bộ xử lý ảnh. Đối với tập kiểm tra, bộ xử lý ảnh cắt và chuẩn hóa images, và chỉ cắt labels vì không áp dụng tăng cường dữ liệu trong quá trình kiểm tra.

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

Để áp dụng jitter cho toàn bộ bộ dữ liệu, hãy sử dụng hàm set_transform của 🤗 Datasets. Biến đổi được áp dụng trực tiếp, giúp nhanh hơn và tiết kiệm dung lượng ổ đĩa hơn:

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

Đánh giá

Việc đưa một chỉ số vào trong quá trình huấn luyện thường hữu ích để đánh giá hiệu suất của mô hình. Bạn có thể nhanh chóng nạp một phương pháp đánh giá bằng thư viện 🤗 Evaluate. Đối với tác vụ này, hãy nạp chỉ số mean Intersection over Union (IoU) (xem quick tour của 🤗 Evaluate để tìm hiểu thêm về cách nạp và tính toán một chỉ số):

>>> import evaluate

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

Sau đó, tạo một hàm để tính toán các chỉ số. Dự đoán của bạn cần được chuyển đổi thành logits trước, sau đó được định dạng lại để khớp với kích thước của nhãn trước khi bạn có thể gọi 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

Hàm compute_metrics của bạn đã sẵn sàng, và bạn sẽ quay lại với nó khi thiết lập quá trình huấn luyện.

Huấn luyện

Nếu bạn chưa quen với việc tinh chỉnh mô hình bằng Trainer, hãy xem hướng dẫn cơ bản tại đây!

Bạn đã sẵn sàng để bắt đầu huấn luyện mô hình của mình! Hãy nạp SegFormer bằng AutoModelForSemanticSegmentation, và truyền cho mô hình ánh xạ giữa các id nhãn và các lớp nhãn:

>>> from transformers import AutoModelForSemanticSegmentation, TrainingArguments, Trainer

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

Tới bước này, chỉ còn ba bước nữa:

  1. Định nghĩa các siêu tham số huấn luyện của bạn trong TrainingArguments. Điều quan trọng là bạn không được xóa các cột không sử dụng vì điều này sẽ làm mất cột image. Nếu không có cột image, bạn sẽ không thể tạo pixel_values. Hãy đặt remove_unused_columns=False để ngăn chặn hành vi này! Tham số bắt buộc duy nhất khác là output_dir, chỉ định nơi lưu trữ mô hình của bạn. Bạn sẽ đẩy mô hình này lên Hub bằng cách đặt push_to_hub=True (bạn cần đăng nhập vào Hugging Face để tải lên mô hình). Vào cuối mỗi epoch, Trainer sẽ đánh giá chỉ số IoU và lưu checkpoint huấn luyện.
  2. Truyền các đối số huấn luyện vào Trainer cùng với mô hình, tập dữ liệu, tokenizer, data collator và hàm compute_metrics.
  3. Gọi train() để tinh chỉnh mô hình của bạn.
>>> 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()

Khi quá trình huấn luyện hoàn tất, hãy chia sẻ mô hình của bạn lên Hub bằng phương thức push_to_hub() để mọi người có thể sử dụng mô hình của bạn:

>>> trainer.push_to_hub()

Suy luận

Tuyệt, giờ đây bạn đã tinh chỉnh một mô hình, bạn có thể sử dụng nó để suy luận!

Tải lại bộ dữ liệu và nạp một hình ảnh để suy luận.

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

Chúng ta sẽ xem cách suy luận mà không dùng pipeline. Xử lý ảnh bằng image processor và đặt pixel_values lên GPU:

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

Truyền đầu vào của bạn vào mô hình và trả về logits:

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

Tiếp theo, điều chỉnh lại kích thước của logits về kích thước ảnh gốc:

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

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

Để trực quan hóa kết quả, hãy nạp bảng màu tập dữ liệu dưới dạng ade_palette(), ánh xạ mỗi lớp sang giá trị RGB tương ứng.

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],
  ])

Sau đó, bạn có thể kết hợp và vẽ ảnh của bạn cùng với bản đồ phân đoạn dự đoán:

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