نحوه تنظیم دقیق یک LLM با استفاده از Cloud Run Jobs

۱. مقدمه

نمای کلی

در این آزمایشگاه کد، شما از کارهای Cloud Run برای تنظیم دقیق مدل Gemma 3 استفاده خواهید کرد، سپس نتیجه را با استفاده از vLLM در Cloud Run ارائه خواهید داد.

کاری که انجام خواهید داد

با استفاده از مجموعه داده KomeijiForce/Text2Emoji که به عنوان بخشی از EmojiLM: Modeling the New Emoji Language ایجاد شده است، یک مدل را آموزش دهید تا به یک عبارت خاص با یک نتیجه خاص پاسخ دهد.

پس از آموزش، مدل به جمله‌ای که با پیشوند «ترجمه به ایموجی:» همراه است، با مجموعه‌ای از ایموجی‌های مربوط به آن جمله پاسخ می‌دهد.

آنچه یاد خواهید گرفت

  • نحوه انجام تنظیم دقیق با استفاده از Cloud Run Jobs GPU
  • نحوه ارائه یک مدل با استفاده از Cloud Run با vLLM
  • نحوه استفاده از پیکربندی Direct VPC برای یک کار GPU برای آپلود و سرویس‌دهی سریع‌تر مدل

۲. قبل از شروع

فعال کردن APIها

قبل از اینکه بتوانید از این codelab استفاده کنید، API های زیر را با اجرای دستور زیر فعال کنید:

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

سهمیه پردازنده گرافیکی

برای تأیید نحوه درخواست سهمیه، مستندات سهمیه GPU را بررسی کنید.

اگر با خطای «شما سهمیه‌ای برای استفاده از پردازنده‌های گرافیکی ندارید» مواجه شدید، سهمیه خود را در g.co/cloudrun/gpu-quota تأیید کنید.

توجه: اگر از یک پروژه جدید استفاده می‌کنید، ممکن است بین فعال کردن API و نمایش سهمیه‌ها در صفحه سهمیه، چند دقیقه طول بکشد.

صورت در آغوش گرفته

این آزمایشگاه کد از مدلی که روی Hugging Face میزبانی می‌شود استفاده می‌کند. برای دریافت این مدل، توکن دسترسی کاربر Hugging Face را با مجوز "خواندن" درخواست کنید. بعداً به آن به عنوان YOUR_HF_TOKEN ارجاع خواهید داد.

برای استفاده از مدل gemma-3-1b-it ، باید با شرایط استفاده موافقت کنید.

۳. تنظیمات و الزامات

منابع زیر را تنظیم کنید:

  • حساب سرویس IAM و مجوزهای IAM مرتبط،
  • راز مدیر مخفی برای ذخیره توکن چهره در آغوش گرفته شما،
  • فضای ذخیره‌سازی ابری برای ذخیره مدل تنظیم‌شده شما، و
  • مخزن رجیستری مصنوعات برای ذخیره تصویری که برای تنظیم دقیق مدل خود خواهید ساخت.
  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. یک سطل ایجاد کنید که میزبان مدل تنظیم‌شده‌ی دقیق شما باشد:
    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. یک مخزن Artifact Registry برای ذخیره تصویر کانتینر ایجاد کنید:
    gcloud artifacts repositories create $AR_REPO \
        --repository-format=docker \
        --location=$REGION \
        --description="codelab for finetuning using CR jobs" \
        --project=$PROJECT_ID
    

۴. تصویر کار Cloud Run را ایجاد کنید

در مرحله بعد، کدی ایجاد خواهید کرد که کارهای زیر را انجام می‌دهد:

  • مدل Gemma را از Hugging Face وارد می‌کند.
  • تنظیم دقیق مدل را با استفاده از مجموعه داده‌های Hugging Face انجام می‌دهد. این کار از یک پردازنده گرافیکی L4 برای تنظیم دقیق استفاده می‌کند.
  • مدل تنظیم‌شده‌ی جدید با نام new_model را در فضای ذخیره‌سازی ابری شما آپلود می‌کند.
  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 خود بسازید:
    gcloud builds submit \
      --tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
      --region $REGION
    

۵. استقرار و اجرای کار

در این مرحله، برای آپلود سریع‌تر در فضای ذخیره‌سازی ابری گوگل، کار را با خروجی مستقیم VPC ایجاد خواهید کرد.

  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
    

تکمیل این کار حدود ۱۰ دقیقه طول خواهد کشید. می‌توانید وضعیت را با استفاده از لینکی که در خروجی آخرین دستور ارائه شده است، بررسی کنید.

۶. از یک سرویس Cloud Run برای ارائه مدل تنظیم‌شده خود با vLLM استفاده کنید.

در این مرحله، شما یک سرویس Cloud Run را مستقر خواهید کرد. این پیکربندی از VPC مستقیم برای دسترسی به مخزن ذخیره‌سازی ابری از طریق شبکه خصوصی برای دانلود سریع‌تر استفاده می‌کند.

  • سرویس 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
    

۷. مدل تنظیم‌شده‌ی خود را آزمایش کنید

در این مرحله، از مدل خود می‌خواهید که تنظیم دقیق را با استفاده از curl آزمایش کند.

  1. آدرس اینترنتی سرویس 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
    
    

شما باید پاسخی مشابه زیر ببینید:

🍌🤔😋🥣

۸. تبریک می‌گویم!

تبریک می‌گویم که آزمایشگاه کد را تمام کردید!

توصیه می‌کنیم مستندات مربوط به Cloud Run Jobs GPU را بررسی کنید.

آنچه ما پوشش داده‌ایم

  • نحوه انجام تنظیم دقیق با استفاده از Cloud Run Jobs GPU
  • نحوه ارائه یک مدل با استفاده از Cloud Run با vLLM
  • نحوه استفاده از پیکربندی Direct VPC برای یک کار GPU برای آپلود و سرویس‌دهی سریع‌تر مدل

۹. تمیز کردن

برای جلوگیری از هزینه‌های ناخواسته، به عنوان مثال، اگر سرویس‌های Cloud Run سهواً بیشتر از تخصیص فراخوانی ماهانه Cloud Run شما در سطح رایگان فراخوانی شوند، می‌توانید سرویس Cloud Run که در مرحله 6 ایجاد کرده‌اید را حذف کنید.

برای حذف سرویس Cloud Run، به کنسول ابری Cloud Run در آدرس https://console.cloud.google.com/run بروید و سرویس serve-gemma-emoji حذف کنید.

برای حذف کل پروژه، به مدیریت منابع بروید، پروژه‌ای را که در مرحله ۲ ایجاد کرده‌اید انتخاب کنید و حذف را انتخاب کنید. اگر پروژه را حذف کنید، باید پروژه‌ها را در Cloud SDK خود تغییر دهید. می‌توانید با اجرای gcloud projects list لیست تمام پروژه‌های موجود را مشاهده کنید.