Gemini in Vertex AI abstimmen

1. Einführung

In diesem Lab lernen Sie, wie Sie den vollständigen Workflow der überwachten Feinabstimmung für ein Google Gemini-Modell ausführen, um es an eine bestimmte Aufgabe anzupassen: die Zusammenfassung von Artikeln. Large Language Models sind zwar leistungsstark, aber aufgrund ihrer allgemeinen Natur können sie durch Feinabstimmung für bestimmte Anwendungsfälle noch effektiver gemacht werden. Wenn Sie das Modell mit einem hochwertigen Dataset von 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-/Validierungsdaten im JSONL-Format.
  • Vertex AI Training: Verwaltet den Feinabstimmungsjob.
  • Vertex AI-Endpunkt: Hostet Ihr abgestimmtes Modell.

Lerninhalte

  • Hochwertige Datasets für die überwachte Feinabstimmung vorbereiten.
  • Feinabstimmungsjobs mit dem Vertex AI SDK für Python konfigurieren und starten.
  • Modelle mit automatisierten Messwerten (ROUGE-Werten) bewerten.
  • Basis- und abgestimmte Modelle vergleichen, 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 ein privates Konto anstelle eines Arbeitskontos oder eines Kontos einer Bildungseinrichtung.

In der Google Cloud Console anmelden

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

Abrechnung aktivieren

5 $Google Cloud-Guthaben 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 etwa Folgendes:Klicken Sie, um Cloud Shell zu autorisieren.
  2. Klicken Sie auf die Schaltfläche HIER KLICKEN, UM AUF IHR GUTHABEN ZUZUGREIFEN. Sie werden zu einer 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-Testabrechnungskonto 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 zu Cloud Shell-Editor zu gelangen.
  2. Wenn Sie heute aufgefordert werden, die Autorisierung zu bestätigen, 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 an Ihre Projekt-ID erinnern, können Sie alle Ihre Projekt-IDs mit diesem 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 im Terminal die APIs:
    • Vertex AI API (aiplatform.googleapis.com): Ermöglicht die Verwendung von Vertex AI für die Feinabstimmung und Bereitstellung 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, um diese Variablen zu speichern, damit sie bei einer Trennung der Verbindung einfach 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, um Ihr Dataset und Ihre Modellartefakte zu speichern.
    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

Qualitativ 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 Datenvorbereitung aus.
    python prepare_data.py
    

7. Referenzleistung festlegen

Vor der Feinabstimmung benötigen Sie einen Benchmark. Sie messen mit ROUGE-Werten, wie gut das Basismodell gemini-2.5-flash bei der Zusammenfassungsaufgabe abschneidet.

  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 Referenzbewertung aus.
    python evaluate.py --model "gemini-2.5-flash" --output "baseline.json"
    
    Dadurch werden eine baseline.json-Datei und ein Diagramm in plots/baseline_dist.png generiert.

8. Feinabstimmung konfigurieren und starten

Jetzt starten Sie einen verwalteten Feinabstimmungsjob 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 im Terminal das Skript zur Feinabstimmung aus.
    python tune.py
    
    Hinweis: Dieser Vorgang kann etwa 45 Minuten dauern. Sie können den Job in der Vertex AI Console überwachen.

9. Trainingscode verstehen

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

Verwaltete überwachte Feinabstimmung

Das Skript verwendet die Methode vertexai.tuning.sft.train, 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: Wir haben diesen Parameter für dieses Lab 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. Sie sollten jedoch auf eine Überanpassung achten.

Modellauswahl

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

10. Modelle vergleichen

Nach Abschluss des Feinabstimmungsjobs können Sie die Leistung Ihres neuen Modells mit der Referenz vergleichen.

  1. Rufen Sie den Endpunkt Ihres abgestimmten Modells ab. Er wurde am Ende des Skripts tune.py ausgegeben. Er sieht etwa so aus: projects/.../locations/.../endpoints/....
  2. Führen Sie das Bewertungsskript noch einmal aus und übergeben Sie dieses Mal Ihr abgestimmtes Modell und die Referenzergebnisse 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 aufrufen, indem Sie im Cloud Shell-Editor den Ordner plots ö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 abgestimmt.

Zusammenfassung

Die Aufgaben in diesem Lab:

  • Dataset im JSONL-Format für die Feinabstimmung von Gemini vorbereitet.
  • Referenz mit dem Basismodell Gemini 2.5 Flash festgelegt.
  • Überwachten Feinabstimmungsjob in Vertex AI gestartet.
  • Abgestimmtes Modell bewertet und mit der Referenz verglichen.

Nächste Schritte

Dieses Lab ist Teil des Lernpfads Production-Ready AI with Google Cloud.

Sehen Sie sich den vollständigen Lehrplan an, um die Lücke zwischen Prototyp und Produktion zu schließen.

Teilen Sie Ihre Fortschritte mit dem Hashtag #ProductionReadyAI.