لمحة عن هذا الدرس التطبيقي حول الترميز
1. مقدمة
نظرة عامة
في هذا الدرس التطبيقي، ستستخدم مهام Cloud Run لتحسين نموذج Gemma، ثم عرض النتيجة على Cloud Run باستخدام vLLM.
لأغراض هذا الإصدار التجريبي من "مختبر الرموز البرمجية"، ستستخدم مجموعة بيانات تحويل النص إلى لغة الاستعلامات البنيوية (SQL)، بهدف جعل "النموذج اللغوي الكبير" يردّ باستعلام SQL عند طرح سؤال باللغة الطبيعية.
المُعطيات
- كيفية إجراء تحسين دقيق باستخدام وحدة معالجة الرسومات في Cloud Run Jobs
- كيفية عرض نموذج باستخدام Cloud Run مع vLLM
- كيفية استخدام إعدادات Direct VPC لإحدى مهام وحدة معالجة الرسومات لتحميل النموذج وعرضه بشكل أسرع
2. قبل البدء
تفعيل واجهات برمجة التطبيقات
قبل أن تتمكّن من بدء استخدام هذا الدرس التطبيقي، عليك تفعيل واجهات برمجة التطبيقات التالية من خلال تنفيذ ما يلي:
gcloud services enable run.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com
حصة وحدة معالجة الرسومات
طلب زيادة الحصة في منطقة تتوفّر فيها الخدمة تبلغ الحصة nvidia_l4_gpu_allocation_no_zonal_redundancy
، ضمن Cloud Run Admin API.
ملاحظة: إذا كنت تستخدم مشروعًا جديدًا، قد يستغرق ظهور الحصص في هذه الصفحة بضع دقائق بعد تفعيل واجهة برمجة التطبيقات.
وجه يحضن
يستخدم هذا الدرس التطبيقي حول الترميز نموذجًا مستضافًا على Hugging Face. للحصول على هذا النموذج، اطلب معرّف أمان وصول مستخدم Hugging Face باستخدام الإذن "قراءة". ويمكنك الإشارة إليه لاحقًا باسم YOUR_HF_TOKEN
.
عليك أيضًا الموافقة على أحكام الاستخدام لاستخدام النموذج: https://huggingface.co/google/gemma-2b
3. الإعداد والمتطلبات
إعداد الموارد التالية:
- حساب خدمة إدارة الهوية وإمكانية الوصول وأذونات إدارة الهوية وإمكانية الوصول المرتبطة به
- مفتاح "مدير الأسرار" السري لتخزين الرمز المميّز لخدمة Hugging Face
- حزمة Cloud Storage لتخزين النموذج المحسَّن
- مستودع Artifact Registry لتخزين الصورة التي ستُنشئها لتحسين نموذجك
- اضبط متغيّرات البيئة لهذا الدرس التطبيقي حول الترميز. لقد ملأنا مسبقًا عددًا من المتغيّرات نيابةً عنك. حدِّد رقم تعريف المشروع والمنطقة ورمز Hugging Face المميّز.
export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION=<YOUR_REGION>
export HF_TOKEN=<YOUR_HF_TOKEN>
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 - أنشئ حساب الخدمة من خلال تنفيذ هذا الأمر:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="Service account for fine-tuning codelab" - استخدِم Secret Manager لتخزين رمز الوصول إلى Hugging Face:
gcloud secrets create $SECRET_ID \
--replication-policy="automatic"
printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=- - امنح حساب الخدمة دور "مُستخدِم سرّ أداة إدارة الأسرار":
gcloud secrets add-iam-policy-binding $SECRET_ID \
--member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role='roles/secretmanager.secretAccessor' - أنشئ حزمة تستضيف النموذج المحسَّن:
gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
- امنح حساب الخدمة إذن الوصول إلى الحزمة:
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin - أنشئ مستودعًا في Artifact Registry لتخزين صورة الحاوية:
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for finetuning using CR jobs" \
--project=$PROJECT_ID
4. إنشاء صورة مهمة Cloud Run
في الخطوة التالية، ستنشئ الرمز البرمجي الذي ينفّذ ما يلي:
- استيراد نموذج Gemma من Hugging Face
- تُجري هذه الوظيفة تحسينًا دقيقًا على النموذج باستخدام مجموعة البيانات من Hugging Face. تستخدِم المهمة وحدة معالجة رسومات L4 واحدة للتحسين.
- تحميل النموذج المحسَّن الذي يُسمى
new_model
إلى حزمة Cloud Storage
- أنشئ دليلاً لرمز مهمة التحسين.
mkdir codelab-finetuning-job
cd codelab-finetuning-job - أنشئ ملفًا باسم
finetune.py
.# Copyright 2024 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 transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
)
from peft import LoraConfig, PeftModel
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-2b")
# The instruction dataset to use
dataset_name = "b-mc2/sql-create-context"
# Fine-tuned model name
new_model = os.getenv("NEW_MODEL", "gemma-2b-sql")
################################################################################
# QLoRA parameters
################################################################################
# LoRA attention dimension
lora_r = int(os.getenv("LORA_R", "4"))
# Alpha parameter for LoRA scaling
lora_alpha = int(os.getenv("LORA_ALPHA", "8"))
# Dropout probability for LoRA layers
lora_dropout = 0.1
################################################################################
# bitsandbytes parameters
################################################################################
# Activate 4-bit precision base model loading
use_4bit = True
# Compute dtype for 4-bit base models
bnb_4bit_compute_dtype = "float16"
# Quantization type (fp4 or nf4)
bnb_4bit_quant_type = "nf4"
# Activate nested quantization for 4-bit base models (double quantization)
use_nested_quant = False
################################################################################
# TrainingArguments parameters
################################################################################
# Output directory where the model predictions and checkpoints will be stored
output_dir = "./results"
# Number of training epochs
num_train_epochs = 1
# Enable fp16/bf16 training (set bf16 to True with an A100)
fp16 = True
bf16 = False
# Batch size per GPU for training
per_device_train_batch_size = int(os.getenv("TRAIN_BATCH_SIZE", "1"))
# Batch size per GPU for evaluation
per_device_eval_batch_size = int(os.getenv("EVAL_BATCH_SIZE", "2"))
# Number of update steps to accumulate the gradients for
gradient_accumulation_steps = int(os.getenv("GRADIENT_ACCUMULATION_STEPS", "1"))
# Enable gradient checkpointing
gradient_checkpointing = True
# Maximum gradient normal (gradient clipping)
max_grad_norm = 0.3
# Initial learning rate (AdamW optimizer)
learning_rate = 2e-4
# Weight decay to apply to all layers except bias/LayerNorm weights
weight_decay = 0.001
# Optimizer to use
optim = "paged_adamw_32bit"
# Learning rate schedule
lr_scheduler_type = "cosine"
# Number of training steps (overrides num_train_epochs)
max_steps = -1
# Ratio of steps for a linear warmup (from 0 to learning rate)
warmup_ratio = 0.03
# Group sequences into batches with same length
# Saves memory and speeds up training considerably
group_by_length = True
# Save checkpoint every X updates steps
save_steps = 0
# Log every X updates steps
logging_steps = int(os.getenv("LOGGING_STEPS", "50"))
################################################################################
# SFT parameters
################################################################################
# Maximum sequence length to use
max_seq_length = int(os.getenv("MAX_SEQ_LENGTH", "512"))
# Pack multiple short examples in the same input sequence to increase efficiency
packing = False
# Load the entire model on the GPU 0
device_map = {'':torch.cuda.current_device()}
# Set limit to a positive number
limit = int(os.getenv("DATASET_LIMIT", "5000"))
dataset = load_dataset(dataset_name, split="train")
if limit != -1:
dataset = dataset.shuffle(seed=42).select(range(limit))
def transform(data):
question = data['question']
context = data['context']
answer = data['answer']
template = "Question: {question}\nContext: {context}\nAnswer: {answer}"
return {'text': template.format(question=question, context=context, answer=answer)}
transformed = dataset.map(transform)
# Load tokenizer and model with QLoRA configuration
compute_dtype = getattr(torch, bnb_4bit_compute_dtype)
bnb_config = BitsAndBytesConfig(
load_in_4bit=use_4bit,
bnb_4bit_quant_type=bnb_4bit_quant_type,
bnb_4bit_compute_dtype=compute_dtype,
bnb_4bit_use_double_quant=use_nested_quant,
)
# Check GPU compatibility with bfloat16
if compute_dtype == torch.float16 and use_4bit:
major, _ = torch.cuda.get_device_capability()
if major >= 8:
print("=" * 80)
print("Your GPU supports bfloat16")
print("=" * 80)
# 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
# Load LLaMA tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
# Load LoRA configuration
peft_config = LoraConfig(
lora_alpha=lora_alpha,
lora_dropout=lora_dropout,
r=lora_r,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "v_proj"]
)
# Set training parameters
training_arguments = TrainingArguments(
output_dir=output_dir,
num_train_epochs=num_train_epochs,
per_device_train_batch_size=per_device_train_batch_size,
gradient_accumulation_steps=gradient_accumulation_steps,
optim=optim,
save_steps=save_steps,
logging_steps=logging_steps,
learning_rate=learning_rate,
weight_decay=weight_decay,
fp16=fp16,
bf16=bf16,
max_grad_norm=max_grad_norm,
max_steps=max_steps,
warmup_ratio=warmup_ratio,
group_by_length=group_by_length,
lr_scheduler_type=lr_scheduler_type,
)
trainer = SFTTrainer(
model=model,
train_dataset=transformed,
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=max_seq_length,
tokenizer=tokenizer,
args=training_arguments,
packing=packing,
)
trainer.train()
trainer.model.save_pretrained(new_model)
# 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 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) - أنشئ ملف
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 - أنشئ
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 - أنشئ الحاوية في مستودع Artifact Registry:
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
--region $REGION
5. نشر المهمة وتنفيذها
في هذه الخطوة، ستنشئ ملف الإعدادات بتنسيق YAML لوظيفتك باستخدام مسار خروج مباشر من شبكة VPC لإجراء عمليات تحميل أسرع إلى Google Cloud Storage.
يُرجى العِلم أنّ هذا الملف يحتوي على متغيّرات ستُعدّلها في خطوة لاحقة.
- أنشئ ملفًا باسم
finetune-job.yaml.tmpl
:apiVersion: run.googleapis.com/v1
kind: Job
metadata:
name: $JOB_NAME
labels:
cloud.googleapis.com/location: $REGION
annotations:
run.googleapis.com/launch-stage: ALPHA
spec:
template:
metadata:
annotations:
run.googleapis.com/execution-environment: gen2
run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
spec:
parallelism: 1
taskCount: 1
template:
spec:
serviceAccountName: $SERVICE_ACCOUNT_ADDRESS
containers:
- name: $IMAGE_NAME
image: $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME
env:
- name: MODEL_NAME
value: "google/gemma-2b"
- name: NEW_MODEL
value: "gemma-2b-sql-finetuned"
- name: BUCKET_NAME
value: "$BUCKET_NAME"
- name: LORA_R
value: "8"
- name: LORA_ALPHA
value: "16"
- name: GRADIENT_ACCUMULATION_STEPS
value: "2"
- name: DATASET_LIMIT
value: "1000"
- name: LOGGING_STEPS
value: "5"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
key: 'latest'
name: HF_TOKEN
resources:
limits:
cpu: 8000m
nvidia.com/gpu: '1'
memory: 32Gi
volumeMounts:
- mountPath: /finetune/new_model
name: finetuned_model
volumes:
- name: finetuned_model
csi:
driver: gcsfuse.run.googleapis.com
readOnly: false
volumeAttributes:
bucketName: $BUCKET_NAME
maxRetries: 3
timeoutSeconds: '3600'
nodeSelector:
run.googleapis.com/accelerator: nvidia-l4 - استبدِل المتغيّرات في ملف YAML بمتغيّرات البيئة من خلال تنفيذ الأمر التالي:
envsubst < finetune-job.yaml.tmpl > finetune-job.yaml
- أنشئ وظيفة Cloud Run:
gcloud alpha run jobs replace finetune-job.yaml
- تنفيذ المهمة:
gcloud alpha run jobs execute $JOB_NAME --region $REGION --async
سيستغرق إكمال المهمة 10 دقائق تقريبًا. يمكنك التحقّق من الحالة باستخدام الرابط المتوفّر في إخراج الأمر الأخير.
6. استخدام خدمة Cloud Run لعرض النموذج المحسَّن باستخدام vLLM
في هذه الخطوة، ستنشر خدمة Cloud Run. يستخدم هذا الإعداد شبكة سحابة إلكترونية خاصة افتراضية (VPC) مباشرةً للوصول إلى حزمة Cloud Storage عبر شبكة خاصة من أجل تسريع عمليات التنزيل.
يُرجى العِلم أنّ هذا الملف يحتوي على متغيّرات ستُعدّلها في خطوة لاحقة.
- أنشئ ملف
service.yaml.tmpl
:apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: serve-gemma-sql
labels:
cloud.googleapis.com/location: $REGION
annotations:
run.googleapis.com/launch-stage: BETA
run.googleapis.com/ingress: all
run.googleapis.com/ingress-status: all
spec:
template:
metadata:
labels:
annotations:
autoscaling.knative.dev/maxScale: '1'
run.googleapis.com/cpu-throttling: 'false'
run.googleapis.com/gpu-zonal-redundancy-disabled: 'true'
run.googleapis.com/network-interfaces: '[{"network":"default","subnetwork":"default"}]'
spec:
containers:
- name: serve-finetuned
image: us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-vllm-serve:20250505_0916_RC00
ports:
- name: http1
containerPort: 8000
resources:
limits:
cpu: 8000m
nvidia.com/gpu: '1'
memory: 32Gi
volumeMounts:
- name: fuse
mountPath: /finetune/new_model
command: ["python3", "-m", "vllm.entrypoints.api_server"]
args:
- --model=/finetune/new_model
- --tensor-parallel-size=1
env:
- name: MODEL_ID
value: 'new_model'
- name: HF_HUB_OFFLINE
value: '1'
volumes:
- name: fuse
csi:
driver: gcsfuse.run.googleapis.com
volumeAttributes:
bucketName: $BUCKET_NAME
nodeSelector:
run.googleapis.com/accelerator: nvidia-l4 - عدِّل ملف
service.yaml
باستخدام اسم الحزمة.envsubst < service.yaml.tmpl > service.yaml
- يمكنك نشر خدمة Cloud Run باتّباع الخطوات التالية:
gcloud alpha run services replace service.yaml
7. اختبار النموذج المحسَّن
في هذه الخطوة، عليك توجيه النموذج لاختبار التحسين الدقيق.
- احصل على عنوان URL للخدمة في Cloud Run:
SERVICE_URL=$(gcloud run services describe serve-gemma-sql --platform managed --region $REGION --format 'value(status.url)')
- أنشئ طلبك للنموذج.
USER_PROMPT="Question: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)"
- يمكنك الاتصال بالخدمة باستخدام CURL لطلب النموذج:
curl -X POST $SERVICE_URL/generate \
-H "Content-Type: application/json" \
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
-d @- <<EOF
{
"prompt": "${USER_PROMPT}"
}
EOF
من المفترض أن يظهر لك ردّ مشابه لما يلي:
{"predictions":["Prompt:\nQuestion: What are the first name and last name of all candidates? Context: CREATE TABLE candidates (candidate_id VARCHAR); CREATE TABLE people (first_name VARCHAR, last_name VARCHAR, person_id VARCHAR)\nOutput:\n CREATE TABLE people_to_candidates (candidate_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people (person_id VARCHAR, person_id VARCHAR) CREATE TABLE people_to_people_to_candidates (person_id VARCHAR, candidate_id"]}
8. تهانينا!
تهانينا على إكمال ورشة رموز البرامج.
ننصحك بمراجعة مستندات Cloud Run.
المواضيع التي تناولناها
- كيفية إجراء تحسين دقيق باستخدام وحدة معالجة الرسومات في Cloud Run Jobs
- كيفية عرض نموذج باستخدام Cloud Run مع vLLM
- كيفية استخدام إعدادات Direct VPC لإحدى مهام وحدة معالجة الرسومات لتحميل النموذج وعرضه بشكل أسرع
9. تَنظيم
لتجنُّب تحصيل رسوم غير مقصودة، على سبيل المثال، إذا تمّ استدعاء خدمات Cloud Run عن طريق الخطأ لعدد مرات يتجاوز المساحة المخصّصة لك شهريًا لاستدعاء Cloud Run في الفئة المجانية، يمكنك حذف خدمة Cloud Run التي أنشأتها في الخطوة 6.
لحذف خدمة Cloud Run، انتقِل إلى Cloud Run Cloud Console على الرابط https://console.cloud.google.com/run واحذِف خدمة serve-gemma-sql
.
لحذف المشروع بأكمله، انتقِل إلى إدارة الموارد، واختَر المشروع الذي أنشأته في الخطوة 2، ثم اختَر "حذف". في حال حذف المشروع، عليك تغيير المشاريع في حزمة Cloud SDK. يمكنك عرض قائمة بجميع المشاريع المتاحة من خلال تشغيل gcloud projects list
.