Ajustar o Gemini na Vertex AI

1. Introdução

Neste laboratório, você vai aprender a realizar o fluxo de trabalho completo de ajuste supervisionado em um modelo do Google Gemini para adaptá-lo a uma tarefa específica: resumo de artigos. Embora os modelos de linguagem grandes sejam poderosos, a natureza de uso geral deles significa que eles podem ser ainda mais eficazes para casos de uso específicos com o ajuste fino. Ao treinar o modelo em um conjunto de dados de exemplos de alta qualidade, você pode melhorar a consistência, a qualidade e a eficiência dele para a tarefa de destino.

Você vai usar o Gemini 2.5 Flash, um modelo leve e econômico, e realizar o ajuste fino usando a Vertex AI.

Visão geral da arquitetura

Vamos criar o seguinte:

  • Cloud Shell: seu ambiente de desenvolvimento.
  • Cloud Storage: armazena dados de treinamento/validação no formato JSONL.
  • Vertex AI Training: gerencia o job de ajuste fino.
  • Endpoint da Vertex AI: hospeda o modelo ajustado.

O que você vai aprender

  • Preparar conjuntos de dados de alta qualidade para ajuste supervisionado.
  • Configurar e iniciar jobs de ajuste fino usando o SDK da Vertex AI para Python.
  • Avaliar modelos usando métricas automatizadas (pontuações ROUGE).
  • Comparar modelos de base e ajustados para quantificar melhorias.

2. Configurar o projeto

Conta do Google

Se você ainda não tiver uma Conta do Google pessoal, crie uma Conta do Google.

Use uma conta pessoal em vez de uma conta do trabalho ou da escola.

Fazer login no console do Google Cloud

Faça login no console do Google Cloud usando uma Conta do Google pessoal.

Ativar faturamento

Resgatar US $5 em créditos do Google Cloud (opcional)

Para executar este workshop, você precisa de uma conta de faturamento com algum crédito. Se você planeja usar seu próprio faturamento, pule esta etapa.

  1. Clique neste link e faça login com uma Conta do Google pessoal.Você verá algo como isto:Clique para autorizar o Cloud Shell
  2. Clique no botão CLIQUE AQUI PARA ACESSAR SEUS CRÉDITOS.Isso vai abrir uma página para configurar seu perfil de faturamentoClique para autorizar o Cloud Shell
  3. Clique em Confirmar.

Agora você está conectado a uma conta de faturamento de teste do Google Cloud Platform.

Captura de tela da visão geral do faturamento

Criar um projeto (opcional)

Se você não tiver um projeto atual que gostaria de usar para este laboratório, crie um novo projeto aqui.

3. Abrir editor do Cloud Shell

  1. Clique neste link para navegar diretamente até o editor do Cloud Shell.
  2. Se for solicitado a autorizar a qualquer momento hoje, clique em Autorizar para continuar.Clique para autorizar o Cloud Shell
  3. Se o terminal não aparecer na parte de baixo da tela, abra-o:
    • Clique em Visualizar.
    • Clique em TerminalAbrir um novo terminal no editor do Cloud Shell
  4. No terminal, defina o projeto com este comando:
    gcloud config set project [PROJECT_ID]
    
    • Exemplo:
      gcloud config set project lab-project-id-example
      
    • Se você não se lembrar do ID do projeto, liste todos os IDs de projeto com:
      gcloud projects list
      
      Definir o ID do projeto no terminal do Editor do Cloud Shell
  5. Você vai receber esta mensagem:
    Updated property [core/project].
    

4. Ativar APIs

Para usar a Vertex AI e outros serviços, é necessário ativar as APIs necessárias no seu projeto do Google Cloud.

  1. No terminal, ative as APIs:
    • API Vertex AI (aiplatform.googleapis.com): permite o uso da Vertex AI para ajuste fino e veiculação de modelos.
    • API do Cloud Storage (storage.googleapis.com): permite o armazenamento de conjuntos de dados e artefatos de modelo.
    gcloud services enable aiplatform.googleapis.com \
        storage.googleapis.com
    

5. configurar o ambiente do projeto

Criar um diretório de trabalho

  1. No terminal, crie um diretório para o projeto e navegue até ele.
    mkdir gemini-finetuning
    cd gemini-finetuning
    

Configurar variáveis de ambiente

  1. No terminal, defina as variáveis de ambiente do projeto. Vamos criar um arquivo env.sh para armazenar essas variáveis para que elas possam ser recarregadas facilmente se a sessão for desconectada.
    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
    

Crie um bucket do Cloud Storage

  1. No terminal, crie um bucket para armazenar o conjunto de dados e os artefatos do modelo.
    gcloud storage buckets create gs://$BUCKET_NAME --project=$PROJECT_ID --location=$REGION
    

Configurar o ambiente virtual

  1. Vamos usar uv para gerenciar nosso ambiente Python. No terminal, execute:
    uv venv .venv
    source .venv/bin/activate
    
  2. No terminal, instale os pacotes Python necessários.
    uv pip install google-cloud-aiplatform rouge-score matplotlib pandas tqdm
    

6. Preparar os dados de treinamento

Dados de qualidade são a base de um ajuste fino bem-sucedido. Você vai usar o conjunto de dados WikiLingua, transformá-lo no formato JSONL específico exigido pelo Gemini e fazer o upload dele para o bucket de armazenamento.

  1. No terminal, crie um arquivo chamado prepare_data.py.
    cloudshell edit prepare_data.py
    
  2. Cole o código a seguir em prepare_data.py.
    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. No terminal, execute o script de preparação de dados.
    python prepare_data.py
    

7. Estabelecer a performance de referência

Antes do ajuste fino, você precisa de uma referência. Você vai medir o desempenho do modelo gemini-2.5-flash na tarefa de resumo usando pontuações ROUGE.

  1. No terminal, crie um arquivo chamado evaluate.py.
    cloudshell edit evaluate.py
    
  2. Cole o código a seguir em evaluate.py.
    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. No terminal, execute a avaliação de referência.
    python evaluate.py --model "gemini-2.5-flash" --output "baseline.json"
    
    Isso vai gerar um arquivo baseline.json e um gráfico em plots/baseline_dist.png.

8. Configurar e iniciar o ajuste fino

Agora você vai iniciar um job de ajuste fino gerenciado na Vertex AI.

  1. No terminal, crie um arquivo chamado tune.py.
    cloudshell edit tune.py
    
  2. Cole o código a seguir em tune.py.
    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. No terminal, execute o script de ajuste fino.
    python tune.py
    
    Observação: esse processo pode levar cerca de 45 minutos. É possível monitorar o job no console da Vertex AI.

9. Entender o código de treinamento

Enquanto o job está em execução, vamos analisar o script tune.py para entender como o ajuste fino funciona.

Ajuste supervisionado gerenciado

O script usa o método vertexai.tuning.sft.train para enviar um job de ajuste gerenciado. Isso abstrai a complexidade do provisionamento de infraestrutura, da distribuição do treinamento e do gerenciamento de checkpoints.

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

Configuração LoRA

Em vez de definir manualmente um LoraConfig, como você faria em frameworks de código aberto, a Vertex AI simplifica isso em alguns parâmetros principais:

  • adapter_size: esse parâmetro (definido como 4 no nosso script) controla a classificação dos adaptadores LoRA. Um tamanho maior permite que o modelo aprenda adaptações mais complexas, mas aumenta o número de parâmetros treináveis.
  • epochs: definimos esse valor como 1 para este laboratório para manter o tempo de treinamento curto (cerca de 20 minutos). Em um cenário de produção, você pode aumentar esse valor para permitir que o modelo aprenda mais profundamente com seus dados, mas é preciso ter cuidado com o overfitting.

Seleção de modelo

Especificamos explicitamente source_model="gemini-2.5-flash". A Vertex AI oferece suporte a várias versões do Gemini, e fixar uma versão específica garante que o pipeline permaneça estável e reproduzível.

10. Comparação de modelos

Quando o job de ajuste fino for concluído, você poderá comparar o desempenho do novo modelo com o valor de referência.

  1. Receba o endpoint do modelo ajustado. Ele foi impresso no final do script tune.py. Ele será semelhante a projects/.../locations/.../endpoints/....
  2. Execute o script de avaliação novamente, desta vez transmitindo o modelo ajustado e os resultados de referência para comparação.
    # 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. Veja os resultados. O script vai gerar uma comparação das pontuações ROUGE e um gráfico plots/comparison.png mostrando a melhoria.É possível conferir os gráficos abrindo a pasta plots no editor do Cloud Shell.

11. Liberar espaço

Para evitar cobranças, exclua os recursos criados.

  1. No terminal, exclua o bucket do Cloud Storage e o modelo ajustado.
    gcloud storage rm -r gs://$BUCKET_NAME
    # Note: You can delete the model endpoint from the Vertex AI Console
    

12. Parabéns!

Você ajustou o Gemini 2.5 Flash na Vertex AI.

Recapitulação

Neste laboratório, você vai:

  • Preparar um conjunto de dados no formato JSONL para ajuste fino do Gemini.
  • Estabelecer uma linha de base usando o modelo Gemini 2.5 Flash.
  • Lançar um job de ajuste supervisionado na Vertex AI.
  • Avaliar e comparar o modelo ajustado com a linha de base.

A seguir

Este laboratório faz parte do programa de aprendizado IA pronta para produção com o Google Cloud.

Explore o currículo completo para preencher a lacuna entre o protótipo e a produção.

Compartilhe seu progresso com a hashtag #ProductionReadyAI.