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 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, é possível melhorar a consistência, a qualidade e a eficiência dele para a tarefa desejada.

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

Visão geral da arquitetura

Confira o que vamos criar:

  • 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 seu modelo refinado.

O que você vai aprender

  • Prepare conjuntos de dados de alta qualidade para ajuste supervisionado.
  • Configure e inicie jobs de ajuste refinado usando o SDK da Vertex AI para Python.
  • Avalie modelos usando métricas automatizadas (pontuações ROUGE).
  • Compare os modelos de base e ajustados para quantificar as melhorias.

2. Configurar o projeto

Conta do Google

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

Use uma conta pessoal em vez de uma conta escolar ou de trabalho.

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 fazer 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ê vai ver algo assim: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 avaliação 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 neste laboratório, crie um novo aqui.

3. Abrir editor do Cloud Shell

  1. Clique neste link para navegar diretamente até o editor do Cloud Shell.
  2. Se for preciso autorizar em algum 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 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 na nuvem do Google.

  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 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 seu 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 e facilitar a recarga delas 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 o 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 detalhado bem-sucedido. Você vai usar o conjunto de dados WikiLingua, transformá-lo no formato JSONL específico exigido pelo Gemini e fazer upload dele para seu 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 um comparativo de mercado. 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 do valor 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 refinado

Agora você vai iniciar um job de ajuste 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 refinado.
    python tune.py
    
    Observação: esse processo pode levar cerca de 45 minutos. É possível monitorar o job no console da Vertex AI.

9. Entenda o código de treinamento

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

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 da infraestrutura, da distribuição do treinamento e do gerenciamento de pontos de verificação.

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

Configuração do 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 manter o tempo de treinamento curto (~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 fique atento ao 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 seu pipeline permaneça estável e reproduzível.

10. Comparação de modelos

Depois que o job de ajuste fino for concluído, compare a performance 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 vai ficar parecido com 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.Para ver os gráficos, abra a pasta plots no editor do Cloud Shell.

11. Limpar

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:

  • Preparou um conjunto de dados no formato JSONL para ajuste fino do Gemini.
  • Estabelecemos um valor de referência usando o modelo básico Gemini 2.5 Flash.
  • Iniciou um job de ajuste supervisionado na Vertex AI.
  • Avaliou e comparou o modelo ajustado com o valor de referência.

A seguir

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

Confira o currículo completo para diminuir a distância entre o protótipo e a produção.

Compartilhe seu progresso com a hashtag #ProductionReadyAI.