Как точно настроить LLM с помощью Cloud Run Jobs

1. Введение

Обзор

В этом практическом занятии вы будете использовать задания Cloud Run для тонкой настройки модели Gemma 3 , а затем развернете результат в Cloud Run с помощью vLLM .

Что вы будете делать

Обучите модель реагировать на определенную фразу определенным результатом, используя набор данных KomeijiForce/Text2Emoji , созданный в рамках проекта EmojiLM: Modeling the New Emoji Language .

После обучения модель реагирует на предложение, начинающееся с "Перевести в эмодзи:", серией эмодзи, соответствующих этому предложению.

Что вы узнаете

  • Как выполнить тонкую настройку с помощью Cloud Run Jobs GPU
  • Как запустить модель с помощью Cloud Run с использованием vLLM
  • Как использовать прямую конфигурацию VPC для задания на GPU для более быстрой загрузки и отображения модели.

2. Прежде чем начать

Включить API

Прежде чем начать использовать этот практический пример, активируйте следующие API, выполнив следующие команды:

gcloud services enable run.googleapis.com \
    compute.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    secretmanager.googleapis.com \
    artifactregistry.googleapis.com

Квота графического процессора

Ознакомьтесь с документацией по квотам на использование графических процессоров , чтобы узнать, как запросить квоту.

Если вы столкнетесь с ошибкой "У вас нет квоты на использование графических процессоров", подтвердите свою квоту на сайте g.co/cloudrun/gpu-quota.

Примечание: Если вы используете новый проект, между включением API и отображением квот на странице квот может пройти несколько минут.

Обнимающее лицо

В этом практическом задании используется модель, размещенная на платформе Hugging Face . Чтобы получить эту модель, запросите токен доступа пользователя Hugging Face с разрешением «Чтение». Вы будете ссылаться на него позже как YOUR_HF_TOKEN .

Для использования модели gemma-3-1b-it необходимо согласиться с условиями использования.

3. Настройка и требования

Настройте следующие ресурсы:

  • Учетная запись службы IAM и связанные с ней разрешения IAM.
  • Секретный ключ Secret Manager для хранения вашего токена Hugging Face.
  • Сегмент облачного хранилища для хранения вашей доработанной модели, а также
  • Репозиторий реестра артефактов для хранения образа, который вы будете создавать для тонкой настройки вашей модели.
  1. Настройте переменные среды для этого практического занятия. Мы предварительно заполнили для вас ряд переменных. Укажите идентификатор вашего проекта, регион и токен Hugging Face.
    export PROJECT_ID=<YOUR_PROJECT_ID>
    export REGION=<YOUR_REGION>
    export HF_TOKEN=<YOUR_HF_TOKEN>
    
    export NEW_MODEL=gemma-emoji
    export AR_REPO=codelab-finetuning-jobs
    export IMAGE_NAME=finetune-to-gcs
    export JOB_NAME=finetuning-to-gcs-job
    export BUCKET_NAME=$PROJECT_ID-codelab-finetuning-jobs
    export SECRET_ID=HF_TOKEN
    export SERVICE_ACCOUNT="finetune-job-sa"
    export SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com
    
  2. Создайте учетную запись службы, выполнив следующую команду:
    gcloud iam service-accounts create $SERVICE_ACCOUNT \
      --display-name="Service account for fine-tuning codelab"
    
  3. Используйте Secret Manager для хранения токена доступа Hugging Face:
    gcloud secrets create $SECRET_ID \
          --replication-policy="automatic"
    
    printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=-
    
  4. Предоставьте своей учетной записи пользователя роль «Управитель секретов, ответственный за доступ к секретам»:
    gcloud secrets add-iam-policy-binding $SECRET_ID \
      --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
      --role='roles/secretmanager.secretAccessor'
    
  5. Создайте хранилище (bucket), в котором будет размещена ваша доработанная модель:
    gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
    
  6. Предоставьте вашей служебной учетной записи доступ к хранилищу:
    gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
      --member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
      --role=roles/storage.objectAdmin
    
  7. Создайте репозиторий в реестре артефактов для хранения образа контейнера:
    gcloud artifacts repositories create $AR_REPO \
        --repository-format=docker \
        --location=$REGION \
        --description="codelab for finetuning using CR jobs" \
        --project=$PROJECT_ID
    

4. Создайте образ задания Cloud Run.

На следующем шаге вы создадите код, который выполнит следующие действия:

  • Импортирует модель Джеммы из Hugging Face.
  • Выполняет тонкую настройку модели с использованием набора данных от Hugging Face. Для тонкой настройки используется один графический процессор L4.
  • Загружает доработанную модель под названием new_model в ваш сегмент Cloud Storage.
  1. Создайте директорию для кода, отвечающего за тонкую настройку.
    mkdir codelab-finetuning-job
    cd codelab-finetuning-job
    
  2. Создайте файл с именем finetune.py
    # Copyright 2025 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #      http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    import os
    
    import torch
    from datasets import load_dataset
    from peft import LoraConfig, PeftModel
    from transformers import (
        AutoModelForCausalLM,
        AutoTokenizer,
        BitsAndBytesConfig,
        TrainingArguments,
    )
    from trl import SFTTrainer
    
    # Cloud Storage bucket to upload the model
    bucket_name = os.getenv("BUCKET_NAME", "YOUR_BUCKET_NAME")
    
    # The model that you want to train from the Hugging Face hub
    model_name = os.getenv("MODEL_NAME", "google/gemma-3-1b-it")
    
    # The instruction dataset to use
    dataset_name = "KomeijiForce/Text2Emoji"
    
    # Fine-tuned model name
    new_model = os.getenv("NEW_MODEL", "gemma-emoji")
    
    ############################ Setup ############################################
    
    # Load the entire model on the GPU 0
    device_map = {"": torch.cuda.current_device()}
    
    # Limit dataset to a random selection
    dataset = load_dataset(dataset_name, split="train").shuffle(seed=42).select(range(1000))
    
    # Setup input formats: trains the model to respond to "Translate to emoji:" with emoji output.
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    
    def format_to_chat(example):
        return {
            "conversations": [
                {"role": "user", "content": f"Translate to emoji: {example['text']}"},
                {"role": "assistant", "content": example["emoji"]},
            ]
        }
    
    formatted_dataset = dataset.map(
        format_to_chat,
        batched=False,                        # Process row by row
        remove_columns=dataset.column_names,  # Optional: Keep only the new column
    )
    
    def apply_chat_template(examples):
        texts = tokenizer.apply_chat_template(examples["conversations"], tokenize=False)
        return {"text": texts}
    
    final_dataset = formatted_dataset.map(apply_chat_template, batched=True)
    
    ############################# Config #########################################
    
    # Load tokenizer and model with QLoRA configuration
    bnb_4bit_compute_dtype = "float16"  # Compute dtype for 4-bit base models
    compute_dtype = getattr(torch, bnb_4bit_compute_dtype)
    
    bnb_config = BitsAndBytesConfig(
        load_in_4bit=True,  # Activate 4-bit precision base model loading
        bnb_4bit_quant_type="nf4",  # Quantization type (fp4 or nf4)
        bnb_4bit_compute_dtype=compute_dtype,
        bnb_4bit_use_double_quant=False,  # Activate nested quantization for 4-bit base models (double quantization)
    )
    
    # Load base model
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        quantization_config=bnb_config,
        device_map=device_map,
        torch_dtype=torch.float16,
    )
    model.config.use_cache = False
    model.config.pretraining_tp = 1
    
    ############################## Train ##########################################
    
    # Load LoRA configuration
    peft_config = LoraConfig(
        lora_alpha=16,     # Alpha parameter for LoRA scaling
        lora_dropout=0.1,  # Dropout probability for LoRA layers,
        r=8,               # LoRA attention dimension
        bias="none",
        task_type="CAUSAL_LM",
        target_modules=["q_proj", "v_proj"],
    )
    
    # Set training parameters
    training_arguments = TrainingArguments(
        output_dir="./results",
        num_train_epochs=1,
        per_device_train_batch_size=1,  # Batch size per GPU for training
        gradient_accumulation_steps=2,  # Number of update steps to accumulate the gradients for
        optim="paged_adamw_32bit",
        save_steps=0,
        logging_steps=5,
        learning_rate=2e-4,    # Initial learning rate (AdamW optimizer)
        weight_decay=0.001,    # Weight decay to apply to all layers except bias/LayerNorm weights
        fp16=True, bf16=False, # Enable fp16/bf16 training
        max_grad_norm=0.3,     # Maximum gradient normal (gradient clipping)
        warmup_ratio=0.03,     # Ratio of steps for a linear warmup (from 0 to learning rate)
        group_by_length=True,  # Group sequences into batches with same length # Saves memory and speeds up training considerably
        lr_scheduler_type="cosine",
    )
    
    trainer = SFTTrainer(
        model=model,
        train_dataset=final_dataset,
        peft_config=peft_config,
        dataset_text_field="text",
        max_seq_length=512,  # Maximum sequence length to use
        tokenizer=tokenizer,
        args=training_arguments,
        packing=False,       # Pack multiple short examples in the same input sequence to increase efficiency
    )
    
    trainer.train()
    trainer.model.save_pretrained(new_model)
    
    ################################# Save ########################################
    
    # Reload model in FP16 and merge it with LoRA weights
    base_model = AutoModelForCausalLM.from_pretrained(
        model_name,
        low_cpu_mem_usage=True,
        return_dict=True,
        torch_dtype=torch.float16,
        device_map=device_map,
    )
    model = PeftModel.from_pretrained(base_model, new_model)
    model = model.merge_and_unload()
    
    # push results to Cloud Storage
    file_path_to_save_the_model = "/finetune/new_model"
    model.save_pretrained(file_path_to_save_the_model)
    tokenizer.save_pretrained(file_path_to_save_the_model)
    
    
  3. Создайте файл requirements.txt :
    accelerate==0.34.2
    bitsandbytes==0.45.5
    datasets==2.19.1
    transformers==4.51.3
    peft==0.11.1
    trl==0.8.6
    torch==2.3.0
    
  4. Создайте Dockerfile :
    FROM nvidia/cuda:12.6.2-runtime-ubuntu22.04
    
    RUN apt-get update && \
        apt-get -y --no-install-recommends install python3-dev gcc python3-pip git && \
        rm -rf /var/lib/apt/lists/*
    
    COPY requirements.txt /requirements.txt
    
    RUN pip3 install -r requirements.txt --no-cache-dir
    
    COPY finetune.py /finetune.py
    
    ENV PYTHONUNBUFFERED 1
    
    CMD python3 /finetune.py --device cuda
    
  5. Создайте контейнер в своем репозитории Artifact Registry:
    gcloud builds submit \
      --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
      --region $REGION
    

5. Развернуть и выполнить задание.

На этом этапе вы создадите задание с прямым исходящим трафиком из VPC для более быстрой загрузки данных в Google Cloud Storage.

  1. Создайте задание Cloud Run:
    gcloud run jobs create $JOB_NAME \
      --region $REGION \
      --image $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
      --set-env-vars BUCKET_NAME=$BUCKET_NAME \
      --set-secrets HF_TOKEN=$SECRET_ID:latest \
      --cpu 8.0 \
      --memory 32Gi \
      --gpu 1 \
      --add-volume name=finetuned_model,type=cloud-storage,bucket=$BUCKET_NAME \
      --add-volume-mount volume=finetuned_model,mount-path=/finetune/new_model \
      --service-account $SERVICE_ACCOUNT_ADDRESS
    
  2. Выполнить задание:
    gcloud run jobs execute $JOB_NAME --region $REGION --async
    

Выполнение задачи займет около 10 минут. Вы можете проверить ее статус, используя ссылку, указанную в выводе последней команды.

6. Используйте сервис Cloud Run для запуска вашей доработанной модели с помощью vLLM.

На этом шаге вы развернете службу Cloud Run. Эта конфигурация использует прямую VPC для доступа к хранилищу Cloud Storage через частную сеть, что обеспечивает более быструю загрузку.

  • Разверните свою службу Cloud Run:
    gcloud run deploy serve-gemma-emoji \
      --image us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250601_0916_RC01 \
      --region $REGION \
      --port 8000 \
      --set-env-vars MODEL_ID=new_model,HF_HUB_OFFLINE=1 \
      --cpu 8.0 \
      --memory 32Gi \
      --gpu 1 \
      --add-volume name=finetuned_model,type=cloud-storage,bucket=$BUCKET_NAME \
      --add-volume-mount volume=finetuned_model,mount-path=/finetune/new_model \
      --service-account $SERVICE_ACCOUNT_ADDRESS \
      --max-instances 1 \
      --command python3 \
      --args="-m,vllm.entrypoints.api_server,--model=/finetune/new_model,--tensor-parallel-size=1" \
      --no-gpu-zonal-redundancy \
      --labels=dev-tutorial=codelab-tuning \
      --no-invoker-iam-check
    

7. Протестируйте свою доработанную модель.

На этом этапе вы зададите вашей модели команду для проверки тонкой настройки с помощью curl.

  1. Получите URL-адрес службы Cloud Run:
    SERVICE_URL=$(gcloud run services describe serve-gemma-emoji \
        --region $REGION --format 'value(status.url)')
    
  2. Создайте подсказку для вашей модели.
    USER_PROMPT="Translate to emoji: I ate a banana for breakfast, later I'm thinking of having soup!"
    
  3. Для запроса модели используйте curl, а результаты отфильтруйте с помощью jq:
    curl -s -X POST ${SERVICE_URL}/v1/chat/completions \
    -H "Content-Type: application/json" \
    -H "Authorization: bearer $(gcloud auth print-identity-token)" \
    -d @- <<EOF | jq ".choices[0].message.content"
    {   "model": "${NEW_MODEL}",
        "messages": [{
            "role": "user",
            "content": [ { "type": "text", "text": "${USER_PROMPT}"}]
        }]
    }
    EOF
    
    

Вы должны увидеть ответ, похожий на следующий:

🍌🤔😋🥣

8. Поздравляем!

Поздравляем с завершением практического занятия!

Рекомендуем ознакомиться с документацией по использованию графических процессоров в Cloud Run Jobs .

Что мы рассмотрели

  • Как выполнить тонкую настройку с помощью Cloud Run Jobs GPU
  • Как запустить модель с помощью Cloud Run с использованием vLLM
  • Как использовать прямую конфигурацию VPC для задания на GPU для более быстрой загрузки и отображения модели.

9. Уборка

Чтобы избежать непреднамеренных списаний средств, например, если службы Cloud Run будут случайно запущены больше раз, чем предусмотрено вашим ежемесячным лимитом на запуск Cloud Run в бесплатном тарифе , вы можете удалить службу Cloud Run, созданную на шаге 6.

Чтобы удалить службу Cloud Run, перейдите в консоль Cloud Run по адресу https://console.cloud.google.com/run и удалите службу serve-gemma-emoji .

Чтобы удалить весь проект, перейдите в раздел «Управление ресурсами» , выберите проект, созданный на шаге 2, и нажмите «Удалить». Если вы удалите проект, вам потребуется изменить проекты в вашем Cloud SDK. Вы можете просмотреть список всех доступных проектов, выполнив gcloud projects list .