Gemini in Vertex AI abstimmen

1. Einführung

In diesem Lab lernen Sie, wie Sie den gesamten Workflow der überwachten Feinabstimmung für ein Google Gemini-Modell durchführen, um es für eine bestimmte Aufgabe anzupassen: die Zusammenfassung von Artikeln. Large Language Models sind zwar leistungsstark, aber da sie für allgemeine Zwecke konzipiert sind, können sie durch Feinabstimmung für bestimmte Anwendungsfälle noch effektiver gemacht werden. Wenn Sie das Modell mit einem hochwertigen Dataset mit Beispielen trainieren, können Sie seine Konsistenz, Qualität und Effizienz für Ihre Zielaufgabe verbessern.

Sie verwenden Gemini 2.5 Flash, ein leichtgewichtiges und kostengünstiges Modell, und führen die Feinabstimmung mit Vertex AI durch.

Überblick über die Architektur

Das werden wir erstellen:

  • Cloud Shell: Ihre Entwicklungsumgebung.
  • Cloud Storage: Speichert Trainings- und Validierungsdaten im JSONL-Format.
  • Vertex AI Training: Verwaltet den Feinabstimmungsjob.
  • Vertex AI-Endpunkt: Hier wird Ihr feinabgestimmtes Modell gehostet.

Lerninhalte

  • Hochwertige Datasets für das überwachte Fine-Tuning vorbereiten
  • Konfigurieren und starten Sie Jobs zum Feinabstimmen mit dem Vertex AI SDK für Python.
  • Bewerten Sie Modelle anhand automatisierter Messwerte (ROUGE-Werte).
  • Vergleichen Sie Basis- und abgestimmte Modelle, um Verbesserungen zu quantifizieren.

2. Projekt einrichten

Google-Konto

Wenn Sie noch kein privates Google-Konto haben, müssen Sie ein Google-Konto erstellen.

Verwenden Sie stattdessen ein privates Konto.

In der Google Cloud Console anmelden

Melden Sie sich mit einem privaten Google-Konto in der Google Cloud Console an.

Abrechnung aktivieren

Google Cloud-Guthaben im Wert von 5 $einlösen (optional)

Für diesen Workshop benötigen Sie ein Rechnungskonto mit Guthaben. Wenn Sie Ihre eigene Abrechnung verwenden möchten, können Sie diesen Schritt überspringen.

  1. Klicken Sie auf diesen Link und melden Sie sich mit einem privaten Google-Konto an.Sie sehen dann Folgendes:Klicken Sie, um Cloud Shell zu autorisieren.
  2. Klicken Sie auf die Schaltfläche HIER KLICKEN, UM AUF IHR GUTHABEN ZUZUGREIFEN. Sie werden dann auf eine Seite weitergeleitet, auf der Sie Ihr Abrechnungsprofil einrichten können.Klicken Sie, um Cloud Shell zu autorisieren.
  3. Klicken Sie auf Bestätigen.

Sie sind jetzt mit einem Google Cloud Platform-Testrechnungskonto verbunden.

Screenshot der Abrechnungsübersicht

Projekt erstellen (optional)

Wenn Sie kein aktuelles Projekt haben, das Sie für dieses Lab verwenden möchten, erstellen Sie hier ein neues Projekt.

3. Cloud Shell-Editor öffnen

  1. Klicken Sie auf diesen Link, um direkt zum Cloud Shell-Editor zu gelangen.
  2. Wenn Sie heute an irgendeinem Punkt zur Autorisierung aufgefordert werden, klicken Sie auf Autorisieren, um fortzufahren.Klicken Sie, um Cloud Shell zu autorisieren.
  3. Wenn das Terminal nicht unten auf dem Bildschirm angezeigt wird, öffnen Sie es:
    • Klicken Sie auf Ansehen.
    • Klicken Sie auf TerminalNeues Terminal im Cloud Shell-Editor öffnen.
  4. Legen Sie im Terminal Ihr Projekt mit diesem Befehl fest:
    gcloud config set project [PROJECT_ID]
    
    • Beispiel:
      gcloud config set project lab-project-id-example
      
    • Wenn Sie sich nicht mehr an Ihre Projekt-ID erinnern, können Sie alle Ihre Projekt-IDs mit folgendem Befehl auflisten:
      gcloud projects list
      
      Projekt-ID im Cloud Shell Editor-Terminal festlegen
  5. Es sollte folgende Meldung angezeigt werden:
    Updated property [core/project].
    

4. APIs aktivieren

Wenn Sie Vertex AI und andere Dienste verwenden möchten, müssen Sie die erforderlichen APIs in Ihrem Google Cloud-Projekt aktivieren.

  1. Aktivieren Sie die APIs im Terminal:
    • Vertex AI API (aiplatform.googleapis.com): Ermöglicht die Verwendung von Vertex AI zum Feinabstimmen und Bereitstellen von Modellen.
    • Cloud Storage API (storage.googleapis.com): Ermöglicht das Speichern von Datasets und Modellartefakten.
    gcloud services enable aiplatform.googleapis.com \
        storage.googleapis.com
    

5. Projektumgebung einrichten

Arbeitsverzeichnis erstellen

  1. Erstellen Sie im Terminal ein Verzeichnis für Ihr Projekt und wechseln Sie zu diesem Verzeichnis.
    mkdir gemini-finetuning
    cd gemini-finetuning
    

Umgebungsvariablen einrichten

  1. Definieren Sie im Terminal die Umgebungsvariablen für Ihr Projekt. Wir erstellen eine env.sh-Datei, in der diese Variablen gespeichert werden, damit sie bei einer Trennung der Verbindung problemlos neu geladen werden können.
    cat <<EOF > env.sh
    export PROJECT_ID=\$(gcloud config get-value project)
    export REGION="us-central1"
    export BUCKET_NAME="\${PROJECT_ID}-gemini-tuning"
    EOF
    
    source env.sh
    

Cloud Storage-Bucket erstellen

  1. Erstellen Sie im Terminal einen Bucket zum Speichern Ihres Datasets und Ihrer Modellartefakte.
    gcloud storage buckets create gs://$BUCKET_NAME --project=$PROJECT_ID --location=$REGION
    

Virtuelle Umgebung einrichten

  1. Wir verwenden uv, um unsere Python-Umgebung zu verwalten. Führen Sie im Terminal folgenden Befehl aus:
    uv venv .venv
    source .venv/bin/activate
    
  2. Installieren Sie im Terminal die erforderlichen Python-Pakete.
    uv pip install google-cloud-aiplatform rouge-score matplotlib pandas tqdm
    

6. Trainingsdaten vorbereiten

Hochwertige Daten sind die Grundlage für eine erfolgreiche Feinabstimmung. Sie verwenden das WikiLingua-Dataset, wandeln es in das spezifische JSONL-Format um, das für Gemini erforderlich ist, und laden es in Ihren Speicher-Bucket hoch.

  1. Erstellen Sie im Terminal eine Datei mit dem Namen prepare_data.py.
    cloudshell edit prepare_data.py
    
  2. Fügen Sie den folgenden Code in prepare_data.py ein.
    import json
    import os
    import pandas as pd
    from google.cloud import storage
    import subprocess
    
    # Configuration
    BUCKET_NAME = os.environ["BUCKET_NAME"]
    PROJECT_ID = os.environ["PROJECT_ID"]
    
    def download_data():
        print("Downloading WikiLingua dataset...")
        # Using gsutil to copy from public bucket
        subprocess.run(["gsutil", "cp", "gs://github-repo/generative-ai/gemini/tuning/summarization/wikilingua/*", "."], check=True)
    
    def convert_to_gemini_format(input_file, output_file, max_samples=1000):
        print(f"Converting {input_file} to Gemini format (first {max_samples} samples)...")
        converted_data = []
        with open(input_file, 'r') as f:
            for i, line in enumerate(f):
                if i >= max_samples:
                    break
                obj = json.loads(line)
                messages = obj.get("messages", [])
    
                # Convert messages to Gemini 2.5 format
                # Input: {"messages": [{"role": "user", "content": "..."}, {"role": "model", "content": "..."}]}
                # Output: {"contents": [{"role": "user", "parts": [{"text": "..."}]}, {"role": "model", "parts": [{"text": "..."}]}]}
    
                contents = []
                for msg in messages:
                    role = msg["role"]
                    content = msg["content"]
                    contents.append({
                        "role": role,
                        "parts": [{"text": content}]
                    })
    
                converted_data.append({"contents": contents})
    
        with open(output_file, 'w') as f:
            for item in converted_data:
                f.write(json.dumps(item) + "\n")
    
        print(f"Saved {len(converted_data)} examples to {output_file}")
    
    def upload_to_gcs(local_file, destination_blob_name):
        print(f"Uploading {local_file} to gs://{BUCKET_NAME}/{destination_blob_name}...")
        storage_client = storage.Client(project=PROJECT_ID)
        bucket = storage_client.bucket(BUCKET_NAME)
        blob = bucket.blob(destination_blob_name)
        blob.upload_from_filename(local_file)
        print("Upload complete.")
    
    def main():
        download_data()
    
        # Process Training Data
        convert_to_gemini_format("sft_train_samples.jsonl", "train_gemini.jsonl")
        upload_to_gcs("train_gemini.jsonl", "datasets/train/train_gemini.jsonl")
    
        # Process Validation Data
        convert_to_gemini_format("sft_val_samples.jsonl", "val_gemini.jsonl")
        upload_to_gcs("val_gemini.jsonl", "datasets/val/val_gemini.jsonl")
    
        print("Data preparation complete!")
    
    if __name__ == "__main__":
        main()
    
  3. Führen Sie im Terminal das Skript zur Datenaufbereitung aus.
    python prepare_data.py
    

7. Referenzleistung ermitteln

Bevor Sie mit dem Fine-Tuning beginnen, benötigen Sie einen Benchmark. Sie messen die Leistung des Basismodells gemini-2.5-flash bei der Zusammenfassungsaufgabe anhand von ROUGE-Werten.

  1. Erstellen Sie im Terminal eine Datei mit dem Namen evaluate.py.
    cloudshell edit evaluate.py
    
  2. Fügen Sie den folgenden Code in evaluate.py ein.
    import argparse
    import json
    import os
    import pandas as pd
    from google.cloud import aiplatform
    import vertexai
    from vertexai.generative_models import GenerativeModel, GenerationConfig, HarmCategory, HarmBlockThreshold
    from rouge_score import rouge_scorer
    from tqdm import tqdm
    import matplotlib.pyplot as plt
    import time
    
    # Configuration
    PROJECT_ID = os.environ["PROJECT_ID"]
    REGION = os.environ["REGION"]
    
    aiplatform.init(project=PROJECT_ID, location=REGION)
    
    def evaluate(model_name, test_file, max_samples=50, output_json="results.json"):
        print(f"Evaluating model: {model_name}")
    
        # Load Test Data
        test_df = pd.read_csv(test_file)
        test_df = test_df.head(max_samples)
    
        model = GenerativeModel(model_name)
    
        safety_settings = {
            HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
            HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
            HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
            HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
        }
    
        generation_config = GenerationConfig(
            temperature=0.1,
            max_output_tokens=1024,
        )
    
        scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
        results = []
    
        for index, row in tqdm(test_df.iterrows(), total=len(test_df)):
            input_text = row['input_text']
            reference_summary = row['output_text']
    
            try:
                response = model.generate_content(
                    input_text,
                    generation_config=generation_config,
                    safety_settings=safety_settings
                )
                generated_summary = response.text
    
                scores = scorer.score(reference_summary, generated_summary)
    
                results.append({
                    "generated": generated_summary,
                    "reference": reference_summary,
                    "rouge1": scores['rouge1'].fmeasure,
                    "rouge2": scores['rouge2'].fmeasure,
                    "rougeL": scores['rougeL'].fmeasure
                })
            except Exception as e:
                print(f"Error processing example {index}: {e}")
                # Sleep briefly to avoid quota issues if hitting limits
                time.sleep(1)
    
        # Save results
        with open(output_json, 'w') as f:
            json.dump(results, f, indent=2)
    
        return pd.DataFrame(results)
    
    def plot_results(df, title, filename):
        os.makedirs("plots", exist_ok=True)
    
        metrics = ['rouge1', 'rouge2', 'rougeL']
        fig, axes = plt.subplots(1, 3, figsize=(15, 5))
    
        for i, metric in enumerate(metrics):
            axes[i].hist(df[metric], bins=10, alpha=0.7, color='skyblue', edgecolor='black')
            axes[i].set_title(f'{metric} Distribution')
            axes[i].set_xlabel('Score')
            axes[i].set_ylabel('Count')
    
        plt.suptitle(title)
        plt.tight_layout()
        plt.savefig(f"plots/{filename}")
        print(f"Plot saved to plots/{filename}")
    
    def compare_results(baseline_file, tuned_file):
        with open(baseline_file, 'r') as f:
            baseline_data = pd.DataFrame(json.load(f))
        with open(tuned_file, 'r') as f:
            tuned_data = pd.DataFrame(json.load(f))
    
        print("\n--- Comparison ---")
        metrics = ['rouge1', 'rouge2', 'rougeL']
        for metric in metrics:
            base_mean = baseline_data[metric].mean()
            tuned_mean = tuned_data[metric].mean()
            diff = tuned_mean - base_mean
            print(f"{metric}: Base={base_mean:.4f}, Tuned={tuned_mean:.4f}, Diff={diff:+.4f}")
    
        # Comparative Plot
        os.makedirs("plots", exist_ok=True)
        comparison_df = pd.DataFrame({
            'Metric': metrics,
            'Baseline': [baseline_data[m].mean() for m in metrics],
            'Tuned': [tuned_data[m].mean() for m in metrics]
        })
    
        comparison_df.plot(x='Metric', y=['Baseline', 'Tuned'], kind='bar', figsize=(10, 6))
        plt.title('Baseline vs Tuned Model Performance')
        plt.ylabel('Average Score')
        plt.xticks(rotation=0)
        plt.tight_layout()
        plt.savefig("plots/comparison.png")
        print("Comparison plot saved to plots/comparison.png")
    
    def main():
        parser = argparse.ArgumentParser()
        parser.add_argument("--model", type=str, default="gemini-2.5-flash", help="Model resource name")
        parser.add_argument("--baseline", type=str, help="Path to baseline results json for comparison")
        parser.add_argument("--output", type=str, default="results.json", help="Output file for results")
        args = parser.parse_args()
    
        # Ensure test data exists (it was downloaded in prepare_data step)
        if not os.path.exists("sft_test_samples.csv"):
            # Fallback download if needed
            subprocess.run(["gsutil", "cp", "gs://github-repo/generative-ai/gemini/tuning/summarization/wikilingua/sft_test_samples.csv", "."], check=True)
    
        df = evaluate(args.model, "sft_test_samples.csv", output_json=args.output)
    
        print("\n--- Results Summary ---")
        print(df.describe())
    
        plot_filename = "baseline_dist.png" if not args.baseline else "tuned_dist.png"
        plot_results(df, f"ROUGE Scores - {args.model}", plot_filename)
    
        if args.baseline:
            compare_results(args.baseline, args.output)
    
    if __name__ == "__main__":
        main()
    
  3. Führen Sie im Terminal die Baseline-Bewertung aus.
    python evaluate.py --model "gemini-2.5-flash" --output "baseline.json"
    
    Dadurch wird eine baseline.json-Datei und ein Diagramm in plots/baseline_dist.png generiert.

8. Feinabstimmung konfigurieren und starten

Als Nächstes starten Sie einen verwalteten Optimierungsjob in Vertex AI.

  1. Erstellen Sie im Terminal eine Datei mit dem Namen tune.py.
    cloudshell edit tune.py
    
  2. Fügen Sie den folgenden Code in tune.py ein.
    import os
    import time
    from google.cloud import aiplatform
    import vertexai
    from vertexai.preview.tuning import sft
    
    # Configuration
    PROJECT_ID = os.environ["PROJECT_ID"]
    REGION = os.environ["REGION"]
    BUCKET_NAME = os.environ["BUCKET_NAME"]
    
    aiplatform.init(project=PROJECT_ID, location=REGION)
    
    def train():
        print("Launching fine-tuning job...")
    
        sft_tuning_job = sft.train(
            source_model="gemini-2.5-flash", # Using specific version for stability
            train_dataset=f"gs://{BUCKET_NAME}/datasets/train/train_gemini.jsonl",
            validation_dataset=f"gs://{BUCKET_NAME}/datasets/val/val_gemini.jsonl",
            epochs=1, # Keep it short for the lab
            adapter_size=4,
            learning_rate_multiplier=1.0,
            tuned_model_display_name="gemini-2.5-flash-wikilingua",
        )
    
        print(f"Job started: {sft_tuning_job.resource_name}")
        print("Waiting for job to complete... (this may take ~45 minutes)")
    
        # Wait for the job to complete
        while not sft_tuning_job.has_ended:
            time.sleep(60)
            sft_tuning_job.refresh()
            print(f"Status: {sft_tuning_job.state.name}")
    
        print("Job completed!")
        print(f"Tuned Model Endpoint: {sft_tuning_job.tuned_model_endpoint_name}")
        return sft_tuning_job.tuned_model_endpoint_name
    
    if __name__ == "__main__":
        train()
    
  3. Führen Sie das Fine-Tuning-Skript im Terminal aus.
    python tune.py
    
    Hinweis: Dieser Vorgang kann etwa 45 Minuten dauern. Sie können den Job in der Vertex AI-Konsole überwachen.

9. Trainingscode verstehen

Während Ihr Job ausgeführt wird, sehen wir uns das tune.py-Skript genauer an, um zu verstehen, wie das Fine-Tuning funktioniert.

Verwaltete überwachte Feinabstimmung

Im Skript wird die Methode vertexai.tuning.sft.train verwendet, um einen verwalteten Abstimmungsjob zu senden. Dadurch wird die Komplexität der Bereitstellung der Infrastruktur, der Verteilung des Trainings und der Verwaltung von Checkpoints abstrahiert.

sft_tuning_job = sft.train(
    source_model="gemini-2.5-flash",
    train_dataset=f"gs://{BUCKET_NAME}/datasets/train/train_gemini.jsonl",
    # ...
)

LoRA-Konfiguration

Anstatt eine LoraConfig manuell zu definieren, wie Sie es in Open-Source-Frameworks tun würden, vereinfacht Vertex AI dies auf einige wenige Schlüsselparameter:

  • adapter_size: Dieser Parameter (in unserem Skript auf 4 festgelegt) steuert den Rang der LoRA-Adapter. Eine größere Größe ermöglicht es dem Modell, komplexere Anpassungen zu lernen, erhöht aber die Anzahl der trainierbaren Parameter.
  • epochs: In diesem Lab wird dieser Wert auf 1 festgelegt, um die Trainingszeit kurz zu halten (ca. 20 Minuten). In einem Produktionsszenario können Sie diesen Wert erhöhen, damit das Modell mehr aus Ihren Daten lernen kann. Achten Sie jedoch auf eine Überanpassung.

Modellauswahl

Wir geben source_model="gemini-2.5-flash" explizit an. Vertex AI unterstützt verschiedene Versionen von Gemini. Wenn Sie eine bestimmte Version fixieren, bleibt Ihre Pipeline stabil und reproduzierbar.

10. Modelle vergleichen

Nach Abschluss des Jobs zum Feinabstimmen können Sie die Leistung Ihres neuen Modells mit der Baseline vergleichen.

  1. Rufen Sie den Endpunkt des feinabgestimmten Modells ab. Sie wurde am Ende des tune.py-Skripts ausgegeben. Sie sollte etwa so projects/.../locations/.../endpoints/... aussehen:
  2. Führen Sie das Bewertungsskript noch einmal aus. Übergeben Sie dieses Mal Ihr optimiertes Modell und die Baseline-Ergebnisse zum Vergleich.
    # Replace [YOUR_TUNED_MODEL_ENDPOINT] with the actual endpoint name
    export TUNED_MODEL="projects/[YOUR_PROJECT_ID]/locations/[YOUR_REGION]/endpoints/[YOUR_ENDPOINT_ID]"
    
    python evaluate.py --model "$TUNED_MODEL" --baseline "baseline.json" --output "tuned.json"
    
  3. Rufen Sie die Ergebnisse auf. Das Skript gibt einen Vergleich der ROUGE-Werte aus und generiert ein plots/comparison.png-Diagramm, das die Verbesserung zeigt.Sie können die Diagramme ansehen, indem Sie den Ordner plots im Cloud Shell-Editor öffnen.

11. Bereinigen

Löschen Sie die erstellten Ressourcen, um Gebühren zu vermeiden.

  1. Löschen Sie im Terminal den Cloud Storage-Bucket und das abgestimmte Modell.
    gcloud storage rm -r gs://$BUCKET_NAME
    # Note: You can delete the model endpoint from the Vertex AI Console
    

12. Glückwunsch!

Sie haben Gemini 2.5 Flash in Vertex AI erfolgreich feinabgestimmt.

Zusammenfassung

Die Aufgaben in diesem Lab:

  • Sie haben ein Dataset im JSONL-Format für die Gemini-Feinabstimmung vorbereitet.
  • Es wurde eine Baseline mit dem Gemini 2.5 Flash-Basismodell erstellt.
  • Sie haben einen Job für überwachte Feinabstimmung in Vertex AI gestartet.
  • Das abgestimmte Modell wurde bewertet und mit der Baseline verglichen.

Nächste Schritte

Dieses Lab ist Teil des Lernpfads Produktionsreife KI mit Google Cloud.

Gesamten Lehrplan ansehen, um die Lücke zwischen Prototyp und Produktion zu schließen

Teilen Sie Ihren Fortschritt unter dem Hashtag #ProductionReadyAI.