Trainieren Sie Metas Section Something Mannequin (SAM), um hochpräzise Masken für jede Domäne zu segmentieren

Die Veröffentlichung mehrerer leistungsstarker Open-Supply-Grundmodelle in Verbindung mit Fortschritten bei der Feinabstimmung haben ein neues Paradigma im maschinellen Lernen und in der künstlichen Intelligenz hervorgebracht. Im Zentrum dieser Revolution steht die Transformatormodell.

Während hochpräzise domänenspezifische Modelle früher für alle außer den finanzkräftigsten Unternehmen unerreichbar waren, ermöglicht das grundlegende Modellparadigma heute sogar den bescheidenen Ressourcen von Studenten oder unabhängigen Forschern, Ergebnisse zu erzielen, die mit den modernsten proprietären Modellen mithalten können.

Durch Feinabstimmung kann die Leistung bei Aufgaben außerhalb der Verteilung erheblich verbessert werden (Bildquelle: vom Autor).

Dieser Artikel untersucht die Anwendung von Metas Section Something Mannequin (SAM) auf die Fernerkundungsaufgabe der Flusspixelsegmentierung. Wenn Sie direkt in den Code einsteigen möchten, ist die Quelldatei für dieses Projekt verfügbar unter GitHub und die Daten sind auf Umarmendes Gesichtobwohl es ratsam ist, zuerst den gesamten Artikel zu lesen.

Der erste Schritt besteht darin, einen geeigneten Datensatz zu finden oder zu erstellen. Basierend auf vorhandener Literatur enthält ein guter Feinabstimmungsdatensatz für SAM mindestens 200–800 Bilder. Eine wichtige Lektion der letzten zehn Jahre der Weiterentwicklung des Deep Studying ist, dass mehr Daten immer besser sind. Mit einem größeren Feinabstimmungsdatensatz kann man additionally nichts falsch machen. Das Ziel grundlegender Modelle besteht jedoch darin, dass auch relativ kleine Datensätze für eine starke Leistung ausreichen.

Außerdem ist ein HuggingFace-Konto erforderlich. hier erstellt. Mit HuggingFace können wir unseren Datensatz jederzeit und von jedem Gerät aus problemlos speichern und abrufen, was die Zusammenarbeit und Reproduzierbarkeit erleichtert.

Die letzte Voraussetzung ist ein Gerät mit einer GPU, auf dem wir den Trainingsworkflow ausführen können. Eine Nvidia T4 GPU, die kostenlos erhältlich ist über Google Colabist leistungsstark genug, um den größten SAM-Modell-Checkpoint (sam-vit-huge) für 50 Epochen in weniger als 12 Stunden anhand von 1000 Bildern zu trainieren.

Um zu vermeiden, dass der Fortschritt aufgrund von Nutzungsbeschränkungen bei gehosteten Laufzeiten verloren geht, können Sie Google Drive einbinden und jeden Modellprüfpunkt dort speichern. Alternativ können Sie eine Verbindung zu einem Virtuelle GCP-Maschine um Beschränkungen vollständig zu umgehen. Wenn Sie GCP noch nie zuvor verwendet haben, haben Sie Anspruch auf ein kostenloses Guthaben von 300 US-Greenback, das ausreicht, um das Modell mindestens ein Dutzend Mal zu trainieren.

Bevor wir mit dem Coaching beginnen, müssen wir die Architektur von SAM verstehen. Das Modell enthält drei Komponenten: einen Bildencoder aus einem minimal modifizierten maskierter Autoencoderein flexibler Immediate-Encoder, der verschiedene Immediate-Typen verarbeiten kann, und ein schneller und leichter Masken-Decoder. Eine Motivation hinter dem Design ist es, eine schnelle Segmentierung in Echtzeit auf Edge-Geräten (z. B. im Browser) zu ermöglichen, da die Bildeinbettung nur einmal berechnet werden muss und der Masken-Decoder in ca. 50 ms auf der CPU ausgeführt werden kann.

Die Modellarchitektur von SAM zeigt uns, welche Eingaben das Modell akzeptiert und welche Teile des Modells trainiert werden müssen (Bildquelle: SAM GitHub).

Theoretisch hat der Bildencoder bereits gelernt, wie man ein Bild optimum einbettet, indem er Formen, Kanten und andere allgemeine visuelle Merkmale erkennt. Ebenso ist der Immediate-Encoder theoretisch bereits in der Lage, Prompts optimum zu kodieren. Der Maskendecoder ist der Teil der Modellarchitektur, der diese Bild- und Immediate-Einbettungen übernimmt und tatsächlich die Maske erstellt, indem er die Bild- und Immediate-Einbettungen bearbeitet.

Ein Ansatz besteht daher darin, die mit den Bild- und Eingabeaufforderungs-Encodern verbundenen Modellparameter während des Trainings einzufrieren und nur die Masken-Decodergewichte zu aktualisieren. Dieser Ansatz hat den Vorteil, dass sowohl überwachte als auch unbeaufsichtigte nachgelagerte Aufgaben möglich sind, da Kontrollpunkt- und Begrenzungsrahmen-Eingabeaufforderungen sowohl automatisierbar als auch von Menschen verwendbar sind.

Diagramm, das den Frozen-SAM-Bildencoder und Maskendecoder neben dem überladenen Immediate-Encoder zeigt, der in der AutoSAM-Architektur verwendet wird (Quelle: AutoSAM-Papier).

Ein alternativer Ansatz besteht darin, den Immediate-Encoder zu überladen, den Bild-Encoder und den Masken-Decoder einzufrieren und einfach den ursprünglichen SAM-Masken-Encoder nicht zu verwenden. Beispielsweise verwendet die AutoSAM-Architektur ein auf Harmonic Dense Web basierendes Netzwerk, um Immediate-Einbettungen basierend auf dem Bild selbst zu erzeugen. In diesem Tutorial behandeln wir den ersten Ansatz, bei dem die Bild- und Immediate-Encoder eingefroren und nur der Masken-Decoder trainiert werden. Code für diesen alternativen Ansatz finden Sie jedoch im AutoSAM GitHub Und Papier.

Der nächste Schritt besteht darin, zu bestimmen, welche Artwork von Eingabeaufforderungen das Modell während der Inferenzzeit erhält, damit wir diese Artwork von Eingabeaufforderung zur Trainingszeit bereitstellen können. Persönlich würde ich angesichts der unvorhersehbaren/inkonsistenten Natur der Verarbeitung natürlicher Sprache die Verwendung von Texteingabeaufforderungen für eine ernsthafte Pc-Imaginative and prescient-Pipeline nicht empfehlen. Es bleiben Punkte und Begrenzungsrahmen, wobei die Wahl letztendlich von der besonderen Natur Ihres spezifischen Datensatzes abhängt, obwohl in der Literatur festgestellt wurde, dass Begrenzungsrahmen Kontrollpunkte ziemlich konsistent übertreffen.

Die Gründe hierfür sind nicht ganz klar, es könnte jedoch einer der folgenden Faktoren oder eine Kombination davon sein:

  • Gute Kontrollpunkte sind zum Zeitpunkt der Inferenz (wenn die Floor-Reality-Maske unbekannt ist) schwieriger auszuwählen als Begrenzungsrahmen.
  • Der Raum der möglichen Punktaufforderungen ist um Größenordnungen größer als der Raum der möglichen Begrenzungsrahmenaufforderungen und wurde daher nicht so gründlich trainiert.
  • Die ursprünglichen SAM-Autoren konzentrierten sich auf die Zero-Shot- und Few-Shot-Fähigkeiten des Modells (gezählt als menschliche Interaktionen), sodass der Schwerpunkt beim Vortraining möglicherweise mehr auf Begrenzungsrahmen lag.

Unabhängig davon ist die Flusssegmentierung tatsächlich ein seltener Fall, in dem Punkteingabeaufforderungen tatsächlich besser abschneiden als Begrenzungsrahmen (wenn auch nur geringfügig, selbst bei einer äußerst günstigen Domäne). Da sich in jedem Bild eines Flusses das Gewässer von einem Ende des Bildes zum anderen erstreckt, bedeckt ein umgebender Begrenzungsrahmen quick immer den größten Teil des Bildes. Daher können die Eingabeaufforderungen für Begrenzungsrahmen für sehr unterschiedliche Flussabschnitte äußerst ähnlich aussehen, was theoretisch bedeutet, dass Begrenzungsrahmen dem Modell deutlich weniger Informationen liefern als Kontrollpunkte und daher zu einer schlechteren Leistung führen.

Kontrollpunkte, Begrenzungsrahmen-Eingabeaufforderungen und die Floor-Reality-Segmentierung, überlagert auf zwei Beispiel-Trainingsbildern (Bildquelle: vom Autor).

Beachten Sie, dass in der obigen Abbildung die jeweiligen Begrenzungsrahmen nahezu identisch sind, obwohl die tatsächlichen Segmentierungsmasken für die beiden Flussabschnitte völlig unterschiedlich sind, während ihre Punktaufforderungen sich (relativ) stärker unterscheiden.

Der andere wichtige zu berücksichtigende Faktor ist, wie einfach Eingabeaufforderungen zum Zeitpunkt der Inferenz generiert werden können. Wenn Sie davon ausgehen, dass ein Mensch in die Schleife eingebunden ist, sind sowohl Begrenzungsrahmen als auch Kontrollpunkte zum Zeitpunkt der Inferenz relativ einfach zu erfassen. Wenn Sie jedoch eine vollständig automatisierte Pipeline haben möchten, wird die Beantwortung dieser Fragen komplizierter.

Unabhängig davon, ob Kontrollpunkte oder Begrenzungsrahmen verwendet werden, umfasst das Generieren der Eingabeaufforderung normalerweise zunächst die Schätzung einer groben Maske für das Objekt von Interesse. Begrenzungsrahmen können dann einfach die kleinste Field sein, die die grobe Maske umschließt, während Kontrollpunkte aus der groben Maske abgetastet werden müssen. Dies bedeutet, dass Begrenzungsrahmen einfacher zu erhalten sind, wenn die Grundmaske unbekannt ist, da die geschätzte Maske für das Objekt von Interesse nur ungefähr der Größe und Place des tatsächlichen Objekts entsprechen muss, während die geschätzte Maske für Kontrollpunkte genauer mit den Konturen des Objekts übereinstimmen müsste.

Bei Verwendung einer geschätzten Maske im Gegensatz zur Grundwahrheit kann die Platzierung der Kontrollpunkte falsch beschriftete Punkte enthalten, während sich Begrenzungsrahmen im Allgemeinen an der richtigen Stelle befinden (Bildquelle: vom Autor).

Wenn wir für die Flusssegmentierung sowohl auf RGB als auch auf NIR zugreifen können, können wir Schwellenwertmethoden für Spektralindizes verwenden, um unsere grobe Maske zu erhalten. Wenn wir nur auf RGB zugreifen können, können wir das Bild in HSV konvertieren und alle Pixel innerhalb eines bestimmten Farbton-, Sättigungs- und Wertebereichs mit Schwellenwerten versehen. Dann können wir verbundene Komponenten unterhalb einer bestimmten Größenschwelle entfernen und verwenden erosion aus skimage.morphology um sicherzustellen, dass die einzigen 1 Pixel in unserer Maske diejenigen sind, die sich in Richtung der Mitte der großen blauen Flecken befinden.

Um unser Modell zu trainieren, benötigen wir einen Datenlader, der alle unsere Trainingsdaten enthält, die wir für jede Trainingsepoche durchlaufen können. Wenn wir unseren Datensatz von HuggingFace laden, nimmt er die Type eines datasets.Dataset Klasse. Wenn der Datensatz privat ist, installieren Sie zuerst die HuggingFace CLI und melden Sie sich mit !huggingface-cli login.

from datasets import load_dataset, load_from_disk, Dataset

hf_dataset_name = "stodoran/elwha-segmentation-v1"
training_data = load_dataset(hf_dataset_name, break up="prepare")
validation_data = load_dataset(hf_dataset_name, break up="validation")

Wir müssen dann unsere eigene benutzerdefinierte Dataset-Klasse codieren, die nicht nur ein Bild und ein Label für jeden Index zurückgibt, sondern auch die Eingabeaufforderung. Unten sehen Sie eine Implementierung, die sowohl Kontrollpunkt- als auch Begrenzungsrahmen-Eingabeaufforderungen verarbeiten kann. Zur Initialisierung wird ein HuggingFace benötigt. datasets.Dataset Instanz und eine SAM-Prozessorinstanz.

from torch.utils.information import Dataset

class PromptType:
CONTROL_POINTS = "pts"
BOUNDING_BOX = "bbox"

class SAMDataset(Dataset):
def __init__(
self,
dataset,
processor,
prompt_type = PromptType.CONTROL_POINTS,
num_positive = 3,
num_negative = 0,
erode = True,
multi_mask = "imply",
perturbation = 10,
image_size = (1024, 1024),
mask_size = (256, 256),
):
# Asign all values to self
...

def __len__(self):
return len(self.dataset)

def __getitem__(self, idx):
datapoint = self.dataset(idx)
input_image = cv2.resize(np.array(datapoint("picture")), self.image_size)
ground_truth_mask = cv2.resize(np.array(datapoint("label")), self.mask_size)

if self.prompt_type == PromptType.CONTROL_POINTS:
inputs = self._getitem_ctrlpts(input_image, ground_truth_mask)
elif self.prompt_type == PromptType.BOUNDING_BOX:
inputs = self._getitem_bbox(input_image, ground_truth_mask)

inputs("ground_truth_mask") = ground_truth_mask
return inputs

Wir müssen auch definieren, SAMDataset._getitem_ctrlpts Und SAMDataset._getitem_bbox Funktionen, obwohl, wenn Sie nur planen, einen Eingabeaufforderungstyp zu verwenden, dann können Sie den Code umgestalten, um nur diesen Typ direkt zu verarbeiten in SAMDataset.__getitem__ und entfernen Sie die Hilfsfunktion.

class SAMDataset(Dataset):
...

def _getitem_ctrlpts(self, input_image, ground_truth_mask):
# Get management factors immediate. See the GitHub for the supply
# of this operate, or change with your individual level choice algorithm.
input_points, input_labels = generate_input_points(
num_positive=self.num_positive,
num_negative=self.num_negative,
masks=ground_truth_mask,
dynamic_distance=True,
erode=self.erode,
)
input_points = input_points.astype(float).tolist()
input_labels = input_labels.tolist()
input_labels = ((x) for x in input_labels)

# Put together the picture and immediate for the mannequin.
inputs = self.processor(
input_image,
input_points=input_points,
input_labels=input_labels,
return_tensors="pt"
)

# Take away batch dimension which the processor provides by default.
inputs = {ok: v.squeeze(0) for ok, v in inputs.gadgets()}
inputs("input_labels") = inputs("input_labels").squeeze(1)

return inputs

def _getitem_bbox(self, input_image, ground_truth_mask):
# Get bounding field immediate.
bbox = get_input_bbox(ground_truth_mask, perturbation=self.perturbation)

# Put together the picture and immediate for the mannequin.
inputs = self.processor(input_image, input_boxes=((bbox)), return_tensors="pt")
inputs = {ok: v.squeeze(0) for ok, v in inputs.gadgets()} # Take away batch dimension which the processor provides by default.

return inputs

Wenn wir alles zusammenfassen, können wir eine Funktion erstellen, die einen PyTorch-Datenlader erstellt und zurückgibt, wenn der HuggingFace-Datensatz aufgeteilt wird. Das Schreiben von Funktionen, die Datenlader zurückgeben, anstatt nur Zellen mit demselben Code auszuführen, ist nicht nur eine gute Praxis zum Schreiben von flexiblem und wartbarem Code, sondern auch notwendig, wenn Sie planen, HuggingFace Beschleunigen um verteiltes Coaching durchzuführen.

from transformers import SamProcessor
from torch.utils.information import DataLoader

def get_dataloader(
hf_dataset,
model_size = "base", # Certainly one of "base", "giant", or "large"
batch_size = 8,
prompt_type = PromptType.CONTROL_POINTS,
num_positive = 3,
num_negative = 0,
erode = True,
multi_mask = "imply",
perturbation = 10,
image_size = (256, 256),
mask_size = (256, 256),
):
processor = SamProcessor.from_pretrained(f"fb/sam-vit-{model_size}")

sam_dataset = SAMDataset(
dataset=hf_dataset,
processor=processor,
prompt_type=prompt_type,
num_positive=num_positive,
num_negative=num_negative,
erode=erode,
multi_mask=multi_mask,
perturbation=perturbation,
image_size=image_size,
mask_size=mask_size,
)
dataloader = DataLoader(sam_dataset, batch_size=batch_size, shuffle=True)

return dataloader

Danach besteht das Coaching lediglich darin, das Modell zu laden, die Bild- und Eingabeaufforderungs-Encoder einzufrieren und für die gewünschte Anzahl von Iterationen zu trainieren.

mannequin = SamModel.from_pretrained(f"fb/sam-vit-{model_size}")
optimizer = AdamW(mannequin.mask_decoder.parameters(), lr=learning_rate, weight_decay=weight_decay)

# Prepare solely the decoder.
for identify, param in mannequin.named_parameters():
if identify.startswith("vision_encoder") or identify.startswith("prompt_encoder"):
param.requires_grad_(False)

Unten sehen Sie die grundlegende Gliederung des Trainingsschleifencodes. Beachten Sie, dass die forward_pass, calculate loss, evaluate_modelUnd save_model_checkpoint Funktionen wurden der Kürze halber weggelassen, aber Implementierungen sind auf GitHub verfügbar. Der Vorwärtspasscode unterscheidet sich je nach Eingabeaufforderungstyp leicht, und die Verlustberechnung erfordert ebenfalls einen Sonderfall basierend auf dem Eingabeaufforderungstyp. Bei der Verwendung von Punkteingabeaufforderungen gibt SAM für jeden einzelnen Eingabepunkt eine vorhergesagte Maske zurück. Um additionally eine einzelne Maske zu erhalten, die mit der Grundwahrheit verglichen werden kann, müssen entweder die vorhergesagten Masken gemittelt oder die beste vorhergesagte Maske ausgewählt werden (identifiziert basierend auf den vorhergesagten IoU-Werten von SAM).

train_losses = ()
validation_losses = ()
epoch_loop = tqdm(whole=num_epochs, place=epoch, go away=False)
batch_loop = tqdm(whole=len(train_dataloader), place=0, go away=True)

whereas epoch < num_epochs:
epoch_losses = ()

batch_loop.n = 0 # Loop Reset
for idx, batch in enumerate(train_dataloader):
# Ahead Cross
batch = {ok: v.to(accelerator.system) for ok, v in batch.gadgets()}
outputs = forward_pass(mannequin, batch, prompt_type)

# Compute Loss
ground_truth_masks = batch("ground_truth_mask").float()
train_loss = calculate_loss(outputs, ground_truth_masks, prompt_type, loss_fn, multi_mask="greatest")
epoch_losses.append(train_loss)

# Backward Cross & Optimizer Step
optimizer.zero_grad()
accelerator.backward(train_loss)
optimizer.step()
lr_scheduler.step()

batch_loop.set_description(f"Prepare Loss: {train_loss.merchandise():.4f}")
batch_loop.replace(1)

validation_loss = evaluate_model(mannequin, validation_dataloader, accelerator.system, loss_fn)
train_losses.append(torch.imply(torch.Tensor(epoch_losses)))
validation_losses.append(validation_loss)

if validation_loss < best_loss:
save_model_checkpoint(
accelerator,
best_checkpoint_path,
mannequin,
optimizer,
lr_scheduler,
epoch,
train_history,
validation_loss,
train_losses,
validation_losses,
loss_config,
model_descriptor=model_descriptor,
)
best_loss = validation_loss

epoch_loop.set_description(f"Finest Loss: {best_loss:.4f}")
epoch_loop.replace(1)
epoch += 1

Für das Elwha-River-Projekt wurde im besten Fall das Modell „sam-vit-base“ mithilfe eines Datensatzes von über 1.000 Segmentierungsmasken unter Verwendung einer GCP-Instanz in weniger als 12 Stunden trainiert.

Im Vergleich zum Foundation-SAM verbesserte sich die Leistung durch die Feinabstimmung drastisch, wobei die Medianmaske von unbrauchbar zu hochpräzise wurde.

Durch die Feinabstimmung von SAM wird die Segmentierungsleistung im Vergleich zum Foundation-SAM mit der Standardaufforderung erheblich verbessert (Bildquelle: vom Autor).

Wichtig zu beachten ist, dass der Trainingsdatensatz mit 1.000 Flussbildern nicht perfekt conflict und die Segmentierungsbezeichnungen hinsichtlich der Anzahl korrekt klassifizierter Pixel stark variierten. Daher wurden die oben gezeigten Metriken anhand eines pixelgenauen Datensatzes mit 225 Flussbildern berechnet.

Ein interessantes beobachtetes Verhalten conflict, dass das Modell lernte, aus den unvollkommenen Trainingsdaten zu verallgemeinern. Bei der Auswertung von Datenpunkten, bei denen das Trainingsbeispiel offensichtliche Fehlklassifizierungen enthielt, können wir beobachten, dass die Vorhersage des Modells den Fehler vermeidet. Beachten Sie, dass die Bilder in der oberen Reihe, die Trainingsbeispiele zeigen, Masken enthalten, die den Fluss nicht bis zum Ufer füllen, während die untere Reihe, die Modellvorhersagen zeigt, die Flussgrenzen enger segmentiert.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert