Vertex AI: Multi-Worker Training and Transfer Learning with TensorFlow

1. Übersicht

In diesem Lab verwenden Sie Vertex AI, um einen Trainingsjob mit mehreren Workern für ein TensorFlow-Modell auszuführen.

Lerninhalte

Die folgenden Themen werden behandelt:

  • Trainingsanwendungscode für das Training mit mehreren Workern ändern
  • Multi-Worker-Trainingsjob über die Vertex AI-Benutzeroberfläche konfigurieren und starten
  • Multi-Worker-Trainingsjob mit dem Vertex SDK konfigurieren und starten

Die Gesamtkosten für die Ausführung dieses Labs in Google Cloud belaufen sich auf etwa 5$.

2. Einführung in Vertex AI

In diesem Lab wird das neueste KI-Angebot von Google Cloud verwendet. Vertex AI integriert die ML-Angebote in Google Cloud für eine nahtlose Entwicklung. Bisher konnten auf mit AutoML trainierte Modelle und benutzerdefinierte Modelle über separate Dienste zugegriffen werden. Das neue Angebot kombiniert beide zusammen mit anderen neuen Produkten in einer einzigen API. Sie können auch vorhandene Projekte zu Vertex AI migrieren. Wenn du Feedback hast, sieh auf der Supportseite nach.

Vertex AI umfasst viele verschiedene Produkte zur Unterstützung von End-to-End-ML-Workflows. In diesem Lab liegt der Schwerpunkt auf den unten aufgeführten Produkten: Training und Workbench.

Vertex-Produktübersicht

3. Anwendungsfall – Übersicht

In diesem Lab trainieren Sie mithilfe von Transferlernen ein Bildklassifizierungsmodell auf dem Cassava-Dataset aus TensorFlow Datasets. Sie verwenden ein ResNet50-Modell aus der tf.keras.applications-Bibliothek, das auf dem ImageNet-Dataset vortrainiert wurde.

Vorteile eines verteilten Trainings

Wenn Sie eine einzelne GPU haben, verwendet TensorFlow diesen Beschleuniger, um das Modelltraining ohne zusätzlichen Aufwand für Sie zu beschleunigen. Wenn Sie jedoch eine zusätzliche Leistungssteigerung durch die Verwendung mehrerer GPUs auf einem einzelnen Computer oder mehreren Computern (jeweils mit potenziell mehreren GPUs) erzielen möchten, müssen Sie tf.distribute verwenden. Dies ist die TensorFlow-Bibliothek zum Ausführen einer Berechnung auf mehreren Geräten. Ein Gerät bezieht sich auf eine CPU oder einen Beschleuniger wie GPUs oder TPUs auf einem Computer, auf dem TensorFlow Vorgänge ausführen kann.

Der einfachste Einstieg in das verteilte Training ist ein einzelner Computer mit mehreren GPU-Geräten. Eine TensorFlow-Verteilungsstrategie aus dem tf.distribute-Modul verwaltet die Koordination der Datenverteilung und Gradientenaktualisierungen auf allen GPUs. Wenn Sie das Training auf einem einzelnen Host gemeistert haben und noch weiter skalieren möchten, können Sie mit mehreren Maschinen in Ihrem Cluster eine noch größere Leistungssteigerung erzielen. Sie können einen Cluster von Maschinen verwenden, die nur eine CPU oder eine oder mehrere GPUs haben. In diesem Lab wird der letzte Fall behandelt und gezeigt, wie Sie mit MultiWorkerMirroredStrategy das Training eines TensorFlow-Modells auf mehrere Maschinen in Vertex AI verteilen.

MultiWorkerMirroredStrategy ist eine synchrone Datenparallelitätsstrategie, die Sie mit nur wenigen Codeänderungen verwenden können. Auf jedem Gerät in Ihrem Cluster wird eine Kopie des Modells erstellt. Die nachfolgenden Farbverlaufsaktualisierungen erfolgen synchron. Das bedeutet, dass jedes Worker-Gerät die Vorwärts- und Rückwärtsdurchläufe durch das Modell für einen anderen Ausschnitt der Eingabedaten berechnet. Die berechneten Gradienten aus jedem dieser Ausschnitte werden dann über alle Geräte auf einem Computer und alle Maschinen im Cluster aggregiert und in einem Prozess, der als All-Reduce bezeichnet wird, reduziert (normalerweise ein Mittelwert). Der Optimierer führt dann die Parameteraktualisierungen mit diesen reduzierten Gradienten aus und hält die Geräte so synchronisiert. Weitere Informationen zum verteilten Training mit TensorFlow finden Sie im folgenden Video:

4. Umgebung einrichten

Sie benötigen ein Google Cloud-Projekt mit aktivierter Abrechnung, um dieses Codelab ausführen zu können. Eine Anleitung zum Erstellen eines Projekts finden Sie hier.

Schritt 1: Compute Engine API aktivieren

Rufen Sie Compute Engine auf und wählen Sie Aktivieren aus, falls die Option noch nicht aktiviert ist. Sie benötigen sie, um Ihre Notebookinstanz zu erstellen.

Schritt 2: Container Registry API aktivieren

Rufen Sie die Container Registry auf und wählen Sie Aktivieren aus, falls noch nicht geschehen. Damit erstellen Sie einen Container für Ihren benutzerdefinierten Trainingsjob.

Schritt 3: Vertex AI API aktivieren

Rufen Sie den Bereich „Vertex AI“ der Cloud Console auf und klicken Sie auf Vertex AI API aktivieren.

Vertex AI-Dashboard

Schritt 4: Vertex AI Workbench-Instanz erstellen

Klicken Sie in der Cloud Console im Bereich „Vertex AI“ auf „Workbench“:

Vertex AI-Menü

Aktivieren Sie die Notebooks API, falls sie noch nicht aktiviert ist.

Notebook_api

Klicken Sie nach der Aktivierung auf VERWALTETE NOTEBOOKS:

Notebooks_UI

Wählen Sie dann NEUES NOTIZBUCH aus.

new_notebook

Geben Sie einen Namen für das Notebook ein und klicken Sie dann auf Erweiterte Einstellungen.

create_notebook

Aktivieren Sie unter „Erweiterte Einstellungen“ das Herunterfahren bei Inaktivität und legen Sie die Anzahl der Minuten auf 60 fest. Das Notebook wird also automatisch heruntergefahren, wenn es nicht verwendet wird, sodass keine unnötigen Kosten entstehen.

idle_timeout

Wählen Sie unter Sicherheit die Option „Terminal aktivieren“ aus, falls sie noch nicht aktiviert ist.

enable_terminal

Alle anderen erweiterten Einstellungen können Sie unverändert lassen.

Klicken Sie dann auf Erstellen. Die Bereitstellung der Instanz dauert einige Minuten.

Wählen Sie nach dem Erstellen der Instanz JupyterLab öffnen aus.

open_jupyterlab

Wenn Sie eine neue Instanz zum ersten Mal verwenden, werden Sie zur Authentifizierung aufgefordert. Folgen Sie dazu den Schritten in der Benutzeroberfläche.

Authentifizieren

5. Code der Trainingsanwendung containerisieren

Sie reichen diesen Trainingsjob bei Vertex ein, indem Sie den Code Ihrer Trainingsanwendung in einen Docker-Container einfügen und diesen Container in die Google Container Registry hochladen. Mit diesem Ansatz können Sie ein Modell trainieren, das mit einem beliebigen Framework erstellt wurde.

Öffnen Sie zuerst über das Launcher-Menü ein Terminalfenster in Ihrer Notebook-Instanz:

Terminal im Notebook öffnen

Erstellen Sie ein neues Verzeichnis mit dem Namen cassava und wechseln Sie dorthin:

mkdir cassava
cd cassava

Schritt 1: Dockerfile erstellen

Der erste Schritt zum Containerisieren Ihres Codes besteht darin, ein Dockerfile zu erstellen. Im Dockerfile geben Sie alle Befehle an, die zum Ausführen des Images erforderlich sind. Dadurch werden alle erforderlichen Bibliotheken installiert und der Einstiegspunkt für den Trainingscode eingerichtet.

Erstellen Sie über Ihr Terminal ein leeres Dockerfile:

touch Dockerfile

Öffnen Sie das Dockerfile und fügen Sie Folgendes ein:

FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-7

WORKDIR /

# Copies the trainer code to the docker image.
COPY trainer /trainer

# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.task"]

Dieses Dockerfile verwendet das Deep Learning Container TensorFlow Enterprise 2.7 GPU Docker-Image. Die Deep Learning Container in Google Cloud sind mit vielen gängigen ML- und Data-Science-Frameworks vorinstalliert. Nach dem Herunterladen dieses Images wird in diesem Dockerfile der Einstiegspunkt für den Trainingscode eingerichtet. Sie haben diese Dateien noch nicht erstellt. Im nächsten Schritt fügen Sie den Code zum Trainieren und Optimieren des Modells hinzu.

Schritt 2: Cloud Storage-Bucket erstellen

In diesem Trainingsjob exportieren Sie das trainierte TensorFlow-Modell in einen Cloud Storage-Bucket. Führen Sie im Terminal den folgenden Befehl aus, um eine Umgebungsvariable für Ihr Projekt zu definieren. Ersetzen Sie dabei your-cloud-project durch die ID Ihres Projekts:

PROJECT_ID='your-cloud-project'

Führen Sie als Nächstes den folgenden Befehl im Terminal aus, um einen neuen Bucket in Ihrem Projekt zu erstellen.

BUCKET="gs://${PROJECT_ID}-bucket"
gsutil mb -l us-central1 $BUCKET

Schritt 3: Code zum Trainieren des Modells hinzufügen

Führen Sie im Terminal den folgenden Befehl aus, um ein Verzeichnis für den Trainingscode und eine Python-Datei zu erstellen, in die Sie den Code einfügen:

mkdir trainer
touch trainer/task.py

In Ihrem cassava/-Verzeichnis sollte nun Folgendes vorhanden sein:

+ Dockerfile
+ trainer/
    + task.py

Öffnen Sie als Nächstes die soeben erstellte Datei task.py und kopieren Sie den folgenden Code. Ersetzen Sie {your-gcs-bucket} durch den Namen des Cloud Storage-Buckets, den Sie gerade erstellt haben.

import tensorflow as tf
import tensorflow_datasets as tfds
import os


PER_REPLICA_BATCH_SIZE = 64
EPOCHS = 2

# TODO: replace {your-gcs-bucket} with the name of the Storage bucket you created earlier
BUCKET = 'gs://{your-gcs-bucket}/mwms'

def preprocess_data(image, label):
  '''Resizes and scales images.'''

  image = tf.image.resize(image, (300,300))
  return tf.cast(image, tf.float32) / 255., label


def create_dataset(batch_size):
  '''Loads Cassava dataset and preprocesses data.'''

  data, info = tfds.load(name='cassava', as_supervised=True, with_info=True)
  number_of_classes = info.features['label'].num_classes
  train_data = data['train'].map(preprocess_data,
                                 num_parallel_calls=tf.data.experimental.AUTOTUNE)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(batch_size)
  train_data  = train_data.prefetch(tf.data.experimental.AUTOTUNE)

  # Set AutoShardPolicy
  options = tf.data.Options()
  options.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.DATA
  train_data = train_data.with_options(options)

  return train_data, number_of_classes


def create_model(number_of_classes):
  '''Creates and compiles pretrained ResNet50 model.'''

  base_model = tf.keras.applications.ResNet50(weights='imagenet', include_top=False)
  x = base_model.output
  x = tf.keras.layers.GlobalAveragePooling2D()(x)
  x = tf.keras.layers.Dense(1016, activation='relu')(x)
  predictions = tf.keras.layers.Dense(number_of_classes, activation='softmax')(x)
  model = tf.keras.Model(inputs=base_model.input, outputs=predictions)

  model.compile(
      loss='sparse_categorical_crossentropy',
      optimizer=tf.keras.optimizers.Adam(0.0001),
      metrics=['accuracy'])

  return model


def _is_chief(task_type, task_id):
  '''Helper function. Determines if machine is chief.'''

  return task_type == 'chief'


def _get_temp_dir(dirpath, task_id):
  '''Helper function. Gets temporary directory for saving model.'''

  base_dirpath = 'workertemp_' + str(task_id)
  temp_dir = os.path.join(dirpath, base_dirpath)
  tf.io.gfile.makedirs(temp_dir)
  return temp_dir


def write_filepath(filepath, task_type, task_id):
  '''Helper function. Gets filepath to save model.'''

  dirpath = os.path.dirname(filepath)
  base = os.path.basename(filepath)
  if not _is_chief(task_type, task_id):
    dirpath = _get_temp_dir(dirpath, task_id)
  return os.path.join(dirpath, base)


def main():
  # Create strategy
  strategy = tf.distribute.MultiWorkerMirroredStrategy()

  # Get data
  global_batch_size = PER_REPLICA_BATCH_SIZE * strategy.num_replicas_in_sync
  train_data, number_of_classes = create_dataset(global_batch_size)

  # Wrap variable creation within strategy scope
  with strategy.scope():
    model = create_model(number_of_classes)

  model.fit(train_data, epochs=EPOCHS)

  # Determine type and task of the machine from
  # the strategy cluster resolver
  task_type, task_id = (strategy.cluster_resolver.task_type,
                        strategy.cluster_resolver.task_id)

  # Based on the type and task, write to the desired model path
  write_model_path = write_filepath(BUCKET, task_type, task_id)
  model.save(write_model_path)

if __name__ == "__main__":
    main()

Bevor Sie den Container erstellen, werfen wir einen genaueren Blick auf den Code, der MultiWorkerMirroredStrategy aus der tf.distribute.Strategy API verwendet.

Der Code enthält einige Komponenten, die erforderlich sind, damit der Code mit MultiWorkerMirroredStrategy funktioniert.

  1. Die Daten müssen in Shards aufgeteilt werden, d. h., jedem Worker wird ein Teil des gesamten Datensatzes zugewiesen. Daher wird in jedem Schritt eine globale Batchgröße von sich nicht überlappenden Dataset-Elementen von jedem Worker verarbeitet. Diese Sharding-Funktion wird bei tf.data.experimental.AutoShardPolicy automatisch aktiviert. tf.data.experimental.AutoShardPolicy kann auf FILE oder DATA gesetzt werden. In diesem Beispiel wird durch die Funktion create_dataset() die AutoShardPolicy auf DATA festgelegt, da das Cassava-Dataset nicht als mehrere Dateien heruntergeladen wird. Wenn Sie die Richtlinie jedoch nicht auf DATA festgelegt haben, wird die Standardrichtlinie AUTO angewendet und das Endergebnis ist dasselbe. Weitere Informationen zum Sharding von Datensätzen mit MultiWorkerMirroredStrategy
  2. In der Funktion main() wird das MultiWorkerMirroredStrategy-Objekt erstellt. Als Nächstes schließen Sie die Erstellung Ihrer Modellvariablen in den Bereich der Strategie ein. Bei diesem wichtigen Schritt wird TensorFlow mitgeteilt, welche Variablen über die Replikate hinweg gespiegelt werden sollen.
  3. Die Batchgröße wird um den Wert von num_replicas_in_sync skaliert. So wird sichergestellt, dass jedes Replik bei jedem Schritt dieselbe Anzahl von Beispielen verarbeitet. Die Batchgröße zu skalieren, ist eine Best Practice bei der Verwendung von Strategien für die synchrone Datenparallelität in TensorFlow.
  4. Bei mehreren Workern ist das Speichern des Modells etwas komplizierter, da das Ziel für jeden Worker unterschiedlich sein muss. Der Chief Worker speichert das Modell im gewünschten Modellverzeichnis, während die anderen Worker das Modell in temporären Verzeichnissen speichern. Es ist wichtig, dass diese temporären Verzeichnisse eindeutig sind, um zu verhindern, dass mehrere Worker in denselben Speicherort schreiben. Das Speichern kann kollektive Vorgänge umfassen, d. h., alle Worker müssen speichern, nicht nur der Haupt-Worker. Die Funktionen _is_chief(), _get_temp_dir(), write_filepath() und main() enthalten Boilerplate-Code, mit dem das Modell gespeichert werden kann.

Wenn Sie MultiWorkerMirroredStrategy bereits in einer anderen Umgebung verwendet haben, haben Sie möglicherweise die Umgebungsvariable TF_CONFIG eingerichtet. Vertex AI legt TF_CONFIG automatisch für Sie fest, sodass Sie diese Variable nicht auf jeder Maschine im Cluster definieren müssen.

Schritt 4: Container erstellen

Führen Sie im Terminal den folgenden Befehl aus, um eine Umgebungsvariable für Ihr Projekt zu definieren. Ersetzen Sie dabei your-cloud-project durch die ID Ihres Projekts:

PROJECT_ID='your-cloud-project'

Definieren Sie in Google Container Registry eine Variable mit dem URI Ihres Container-Images:

IMAGE_URI="gcr.io/$PROJECT_ID/multiworker:cassava"

Docker konfigurieren

gcloud auth configure-docker

Erstellen Sie dann den Container, indem Sie im Stammverzeichnis von cassava Folgendes ausführen:

docker build ./ -t $IMAGE_URI

Übertragen Sie die Datei anschließend per Push in Google Container Registry:

docker push $IMAGE_URI

Nachdem der Container in Container Registry hochgeladen wurde, können Sie den Trainingsjob starten.

6. Mehrere Jobs für die verteilte Modellerstellung in Vertex AI ausführen

In diesem Lab wird ein benutzerdefiniertes Training über einen benutzerdefinierten Container in der Google Container Registry verwendet. Sie können aber auch einen Trainingsjob mit den vordefinierten Containern ausführen.

Rufen Sie zuerst in der Cloud Console im Bereich „Vertex“ den Bereich Training auf:

uCAIP-Menü

Schritt 1: Trainingsjob konfigurieren

Klicken Sie auf Erstellen, um die Parameter für den Trainingsjob einzugeben.

  • Wählen Sie unter Dataset die Option Kein verwaltetes Dataset aus.
  • Wählen Sie dann Benutzerdefiniertes Training (erweitert) als Trainingsmethode aus und klicken Sie auf Weiter.
  • Geben Sie multiworker-cassava (oder einen beliebigen Namen) für Modellname ein.
  • Klicken Sie auf Weiter.

Wählen Sie im Schritt „Containereinstellungen“ die Option Benutzerdefinierter Container aus:

Option „Benutzerdefinierter Container“

Geben Sie im ersten Feld (Container-Image) den Wert der Variablen IMAGE_URI aus dem vorherigen Abschnitt ein. Es sollte gcr.io/your-cloud-project/multiworker:cassava mit Ihrer eigenen Projekt-ID sein. Lassen Sie die anderen Felder leer und klicken Sie auf Continue (Weiter).

Überspringen Sie den Schritt „Hyperparameter“ und klicken Sie noch einmal auf Weiter.

Schritt 2: Rechencluster konfigurieren

Vertex AI bietet 4 Worker-Pools, um die verschiedenen Arten von Maschinenaufgaben abzudecken.

Worker-Pool 0 konfiguriert den Primär-, Chief-, Planer- oder „Master“. In MultiWorkerMirroredStrategy werden alle Maschinen als Worker bezeichnet. Das sind die physischen Maschinen, auf denen die replizierte Berechnung ausgeführt wird. Zusätzlich dazu, dass jeder Rechner ein Worker ist, muss es einen Worker geben, der zusätzliche Arbeit übernimmt, z. B. das Speichern von Prüfpunkten und das Schreiben von Zusammenfassungsdateien in TensorBoard. Diese Maschine wird als „Chief“ bezeichnet. Es gibt immer nur einen Haupt-Worker. Die Anzahl der Worker für Worker-Pool 0 ist daher immer 1.

Lassen Sie unter Computing und Preise die ausgewählte Region unverändert und konfigurieren Sie Worker-Pool 0 so:

Worker_pool_0

Unter „Worker-Pool 1“ konfigurieren Sie die Worker für Ihren Cluster.

Konfigurieren Sie Worker-Pool 1 so:

Worker_pool_1

Der Cluster ist jetzt so konfiguriert, dass er zwei reine CPU-Maschinen hat. Wenn der Code der Trainingsanwendung ausgeführt wird, verteilt MultiWorkerMirroredStrategy das Training auf beide Maschinen.

MultiWorkerMirroredStrategy hat nur die Aufgabentypen „Leiter“ und „Arbeiter“. Daher müssen keine zusätzlichen Worker-Pools konfiguriert werden. Wenn Sie jedoch ParameterServerStrategy von TensorFlow verwenden, würden Sie Ihre Parameterserver in Worker-Pool 2 konfigurieren. Wenn Sie Ihrem Cluster einen Evaluator hinzufügen möchten, konfigurieren Sie diese Maschine in Worker-Pool 3.

Klicken Sie auf Training starten, um den Hyperparameter-Abstimmungsjob zu starten. Im Bereich „Training“ der Console wird auf dem Tab TRAININGSPIPELINES der neu gestartete Job angezeigt:

Trainingjobs

🎉 Glückwunsch! 🎉

Sie haben gelernt, wie Sie Vertex AI für Folgendes verwenden:

  • Einen Trainingsjob für mehrere Worker für Trainingscode starten, der in einem benutzerdefinierten Container bereitgestellt wird. In diesem Beispiel wurde ein TensorFlow-Modell verwendet. Sie können aber auch ein mit einem beliebigen Framework erstelltes Modell mit benutzerdefinierten oder integrierten Containern trainieren.

Weitere Informationen zu den verschiedenen Bereichen von Vertex finden Sie in der Dokumentation.

7. [Optional] Vertex SDK verwenden

Im vorherigen Abschnitt wurde gezeigt, wie Sie den Trainingsjob über die Benutzeroberfläche starten. In diesem Abschnitt erfahren Sie, wie Sie den Trainingsjob mithilfe der Vertex Python API einreichen können.

Kehren Sie zu Ihrer Notebook-Instanz zurück und erstellen Sie über den Launcher ein TensorFlow 2-Notebook:

new_notebook

Importieren Sie das Vertex AI SDK.

from google.cloud import aiplatform

Um den Trainingsjob mit mehreren Workern zu starten, müssen Sie zuerst die Worker-Pool-Spezifikation definieren. Die Verwendung von GPUs in der Spezifikation ist völlig optional. Sie können accelerator_type und accelerator_count entfernen, wenn Sie einen reinen CPU-Cluster wie im vorherigen Abschnitt gezeigt benötigen.

# The spec of the worker pools including machine type and Docker image
# Be sure to replace {YOUR-PROJECT-ID} with your project ID.
worker_pool_specs=[
     {
        "replica_count": 1,
        "machine_spec": {
          "machine_type": "n1-standard-8", "accelerator_type": "NVIDIA_TESLA_V100", "accelerator_count": 1
        },
        "container_spec": {"image_uri": "gcr.io/{YOUR-PROJECT-ID}/multiworker:cassava"}
      },
      {
        "replica_count": 1,
        "machine_spec": {
          "machine_type": "n1-standard-8", "accelerator_type": "NVIDIA_TESLA_V100", "accelerator_count": 1
        },
        "container_spec": {"image_uri": "gcr.io/{YOUR-PROJECT-ID}/multiworker:cassava"}
      }
]

Erstellen und führen Sie als Nächstes einen CustomJob aus. Ersetzen Sie {YOUR_BUCKET} in Ihrem Projekt durch einen Bucket für das Staging. Sie können denselben Bucket verwenden, den Sie zuvor erstellt haben.

# Replace YOUR_BUCKET
my_multiworker_job = aiplatform.CustomJob(display_name='multiworker-cassava-sdk',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')

my_multiworker_job.run()

Im Abschnitt "Training" der Konsole auf dem Tab BENUTZERDEFINIERTE JOBS sehen Sie Ihren Trainingsjob:

Benutzerdefinierte Jobs

8. Bereinigen

Da wir das Notebook so konfiguriert haben, dass nach 60 Minuten Inaktivität eine Zeitüberschreitung auftritt, müssen wir uns nicht um das Herunterfahren der Instanz kümmern. Wenn Sie die Instanz manuell herunterfahren möchten, klicken Sie in der Console im Bereich „Vertex AI Workbench“ auf die Schaltfläche „Beenden“. Wenn Sie das Notizbuch vollständig löschen möchten, klicken Sie auf die Schaltfläche „Löschen“.

Instanz beenden

Wenn Sie den Speicher-Bucket löschen möchten, klicken Sie in der Cloud Console im Navigationsmenü auf „Speicher“, wählen Sie den Bucket aus und klicken Sie auf „Löschen“:

Speicher löschen