開源 AI 食譜文件

使用自定義生物醫學資料集微調 Vision Transformer 模型

Hugging Face's logo
加入 Hugging Face 社群

並獲得增強的文件體驗

開始使用

Open In Colab

使用自定義生物醫學資料集微調 Vision Transformer 模型

作者:Emre Albayrak

本指南概述了在自定義生物醫學資料集上微調 Vision Transformer (ViT) 模型的過程。它包括載入和準備資料集、為不同資料拆分設定影像轉換、配置和初始化 ViT 模型,以及使用評估和視覺化工具定義訓練過程的步驟。

資料集資訊

自定義資料集是手工製作的,包含 780 張影像,分為 3 個類別(良性、惡性、正常)。

attachment:datasetinfo.png

模型資訊

我們將要微調的模型是 Google 的 "vit-large-patch16-224"。它在 ImageNet-21k(14M 影像,21.843 個類別)上進行訓練,並在 ImageNet 2012(1M 影像,1.000 個類別)上以 224x224 解析度進行微調。Google 還有其他幾個不同影像大小和補丁的 ViT 模型。

我們開始吧。

入門

首先,讓我們先安裝庫。

!pip install datasets transformers accelerate torch torchvision scikit-learn matplotlib wandb

(可選)我們將把模型推送到 Hugging Face Hub,所以我們必須登入。

# from huggingface_hub import notebook_login
# notebook_login()

資料集準備

Datasets 庫自動從資料集中提取影像和類別。有關詳細資訊,您可以訪問此連結

from datasets import load_dataset

dataset = load_dataset("emre570/breastcancer-ultrasound-images")
dataset

我們已經得到了資料集。但是我們沒有驗證集。為了建立驗證集,我們將根據測試集的大小,將驗證集的大小計算為訓練集的一部分。然後我們將訓練資料集拆分為新的訓練和驗證子集。

# Get the numbers of each set
test_num = len(dataset["test"])
train_num = len(dataset["train"])

val_size = test_num / train_num

train_val_split = dataset["train"].train_test_split(test_size=val_size)
train_val_split

我們已經得到了分離的訓練集。讓我們將它們與測試集合並。

from datasets import DatasetDict

dataset = DatasetDict(
    {"train": train_val_split["train"], "validation": train_val_split["test"], "test": dataset["test"]}
)
dataset

太棒了!我們的資料集已準備就緒。讓我們將子集分配給不同的變數。我們稍後將使用它們以便於引用。

train_ds = dataset["train"]
val_ds = dataset["validation"]
test_ds = dataset["test"]

我們可以看到影像是一個 PIL.Image,並帶有一個相關的標籤。

train_ds[0]

我們還可以看到訓練集的特徵。

train_ds.features

讓我們從資料集中每個類別中展示一張圖片。

>>> import matplotlib.pyplot as plt

>>> # Initialize a set to keep track of shown labels
>>> shown_labels = set()

>>> # Initialize the figure for plotting
>>> plt.figure(figsize=(10, 10))

>>> # Loop through the dataset and plot the first image of each label
>>> for i, sample in enumerate(train_ds):
...     label = train_ds.features["label"].names[sample["label"]]
...     if label not in shown_labels:
...         plt.subplot(1, len(train_ds.features["label"].names), len(shown_labels) + 1)
...         plt.imshow(sample["image"])
...         plt.title(label)
...         plt.axis("off")
...         shown_labels.add(label)
...         if len(shown_labels) == len(train_ds.features["label"].names):
...             break

>>> plt.show()

資料處理

資料集已準備就緒。但我們還沒有準備好進行微調。我們將依次遵循以下步驟:

  • 標籤對映: 我們在標籤 ID 及其對應名稱之間進行轉換,這對於模型訓練和評估很有用。

  • 影像處理: 然後,我們使用 ViTImageProcessor 來標準化輸入影像大小並應用特定於預訓練模型的歸一化。此外,還將為訓練、驗證和測試定義不同的轉換,以使用 torchvision 改進模型泛化。

  • 轉換函式: 實現將轉換應用於資料集的函式,將影像轉換為 ViT 模型所需的格式和維度。

  • 資料載入: 設定自定義 collate 函式以正確批處理影像和標籤,並建立 DataLoader 以在模型訓練期間高效載入和批處理。

  • 批處理準備: 檢索並顯示示例批處理中的資料形狀,以驗證正確的處理和模型輸入就緒狀態。

標籤對映

id2label = {id: label for id, label in enumerate(train_ds.features["label"].names)}
label2id = {label: id for id, label in id2label.items()}
id2label, id2label[train_ds[0]["label"]]

影像處理

from transformers import ViTImageProcessor

model_name = "google/vit-large-patch16-224"
processor = ViTImageProcessor.from_pretrained(model_name)
from torchvision.transforms import (
    CenterCrop,
    Compose,
    Normalize,
    RandomHorizontalFlip,
    RandomResizedCrop,
    ToTensor,
    Resize,
)

image_mean, image_std = processor.image_mean, processor.image_std
size = processor.size["height"]

normalize = Normalize(mean=image_mean, std=image_std)

train_transforms = Compose(
    [
        RandomResizedCrop(size),
        RandomHorizontalFlip(),
        ToTensor(),
        normalize,
    ]
)
val_transforms = Compose(
    [
        Resize(size),
        CenterCrop(size),
        ToTensor(),
        normalize,
    ]
)
test_transforms = Compose(
    [
        Resize(size),
        CenterCrop(size),
        ToTensor(),
        normalize,
    ]
)

建立轉換函式

def apply_train_transforms(examples):
    examples["pixel_values"] = [train_transforms(image.convert("RGB")) for image in examples["image"]]
    return examples


def apply_val_transforms(examples):
    examples["pixel_values"] = [val_transforms(image.convert("RGB")) for image in examples["image"]]
    return examples


def apply_test_transforms(examples):
    examples["pixel_values"] = [val_transforms(image.convert("RGB")) for image in examples["image"]]
    return examples

將轉換函式應用於每個集合

train_ds.set_transform(apply_train_transforms)
val_ds.set_transform(apply_val_transforms)
test_ds.set_transform(apply_test_transforms)
train_ds.features
train_ds[0]

看來我們已經將畫素值轉換為張量。

資料載入

import torch
from torch.utils.data import DataLoader


def collate_fn(examples):
    pixel_values = torch.stack([example["pixel_values"] for example in examples])
    labels = torch.tensor([example["label"] for example in examples])
    return {"pixel_values": pixel_values, "labels": labels}


train_dl = DataLoader(train_ds, collate_fn=collate_fn, batch_size=4)

批處理準備

>>> batch = next(iter(train_dl))
>>> for k, v in batch.items():
...     if isinstance(v, torch.Tensor):
...         print(k, v.shape)
pixel_values torch.Size([4, 3, 224, 224])
labels torch.Size([4])

太棒了!現在我們準備好進行微調過程了。

微調模型

現在我們將配置和微調模型。我們首先使用特定的標籤對映和預訓練設定初始化模型,並調整大小不匹配的情況。訓練引數被設定為定義模型的學習過程,包括儲存策略、批處理大小和訓練週期,並使用 Weights & Biases 記錄結果。然後將例項化 Hugging Face Trainer 來管理訓練和評估,利用自定義資料整理器和模型內建處理器。最後,訓練完成後,將在測試資料集上評估模型的效能,並列印指標以評估其準確性。

首先,我們呼叫我們的模型。

from transformers import ViTForImageClassification

model = ViTForImageClassification.from_pretrained(
    model_name, id2label=id2label, label2id=label2id, ignore_mismatched_sizes=True
)

這裡有一個細微的細節:`ignore_mismatched_sizes` 引數。

當您在新資料集上微調預訓練模型時,有時影像的輸入大小或模型架構的細節(例如分類層中的標籤數量)可能與模型最初訓練時的資料不完全匹配。這可能由於各種原因發生,例如當使用在一種影像資料(如 ImageNet 中的自然影像)上訓練的模型應用於完全不同型別的影像資料(如醫學影像或專用相機影像)時。

將 `ignore_mismatched_sizes` 設定為 `True` 允許模型調整其層以適應大小差異而不會引發錯誤。

例如,該模型訓練的類別數為 1000,即 `torch.Size([1000])`,它期望輸入具有 `torch.Size([1000])` 個類別。我們的資料集有 3 個類別,即 `torch.Size([3])` 個類別。如果我們直接輸入,它將引發錯誤,因為類別數不匹配。

然後,為該模型定義來自 Google 的訓練引數。

(可選)請注意,指標將儲存到 Weights & Biases 中,因為我們將 `report_to` 引數設定為 `wandb`。W&B 將要求您提供 API 金鑰,因此您應該建立一個帳戶和 API 金鑰。如果您不想,可以刪除 `report_to` 引數。

from transformers import TrainingArguments, Trainer
import numpy as np

train_args = TrainingArguments(
    output_dir="output-models",
    save_total_limit=2,
    report_to="wandb",
    save_strategy="epoch",
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=10,
    per_device_eval_batch_size=4,
    num_train_epochs=40,
    weight_decay=0.01,
    load_best_model_at_end=True,
    logging_dir="logs",
    remove_unused_columns=False,
)

我們現在可以使用 `Trainer` 開始微調過程了。

trainer = Trainer(
    model,
    train_args,
    train_dataset=train_ds,
    eval_dataset=val_ds,
    data_collator=collate_fn,
    tokenizer=processor,
)
trainer.train()
輪次 訓練損失 驗證損失 準確率
40 0.174700 0.596288 0.903846

微調過程已完成。讓我們繼續評估測試集上的模型。

>>> outputs = trainer.predict(test_ds)
>>> print(outputs.metrics)
{'test_loss': 0.40843912959098816, 'test_runtime': 4.9934, 'test_samples_per_second': 31.242, 'test_steps_per_second': 7.81}

{'test_loss': 0.3219967782497406, 'test_accuracy': 0.9102564102564102, 'test_runtime': 4.0543, 'test_samples_per_second': 38.478, 'test_steps_per_second': 9.619}

(可選)將模型推送到 Hub

我們可以使用 `push_to_hub` 將模型推送到 Hugging Face Hub

model.push_to_hub("your_model_name")

太棒了!讓我們視覺化結果。

結果

我們已經完成了微調。現在讓我們看看我們的模型如何使用 scikit-learn 的混淆矩陣顯示和召回率分數來預測類別。

什麼是混淆矩陣?

混淆矩陣是一種特定的表格佈局,用於視覺化演算法(通常是監督學習模型)在一組已知真實值的測試資料上的效能。它對於檢查分類模型的效能特別有用,因為它顯示了真實標籤與預測標籤的頻率。

讓我們畫出我們模型的混淆矩陣

>>> from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

>>> y_true = outputs.label_ids
>>> y_pred = outputs.predictions.argmax(1)

>>> labels = train_ds.features["label"].names
>>> cm = confusion_matrix(y_true, y_pred)
>>> disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)
>>> disp.plot(xticks_rotation=45)

什麼是召回率?

召回率是分類任務中使用的效能指標,用於衡量模型正確識別資料集中所有相關例項的能力。具體來說,召回率評估模型正確預測為陽性的實際陽性例項的比例。

讓我們使用 scikit-learn 列印召回率分數

>>> from sklearn.metrics import recall_score

>>> # Calculate the recall scores
>>> # 'None' calculates recall for each class separately
>>> recall = recall_score(y_true, y_pred, average=None)

>>> # Print the recall for each class
>>> for label, score in zip(labels, recall):
...     print(f"Recall for {label}: {score:.2f}")
Recall for benign: 0.90
Recall for malignant: 0.86
Recall for normal: 0.78

良性召回率:0.90,惡性召回率:0.86,正常召回率:0.78

結論

在本食譜中,我們介紹瞭如何使用醫學資料集訓練 ViT 模型。它涵蓋了資料集準備、影像預處理、模型配置、訓練、評估和結果視覺化等關鍵步驟。透過利用 Hugging Face 的 Transformers 庫、scikit-learn 和 PyTorch Torchvision,它有助於高效的模型訓練和評估,為模型的效能及其準確分類生物醫學影像的能力提供了寶貴的見解。

< > 在 GitHub 上更新

© . This site is unofficial and not affiliated with Hugging Face, Inc.