Thông tin về lớp học lập trình này
1. Giới thiệu
Tổng quan
Trong lớp học lập trình này, bạn sẽ sử dụng các công việc trên Cloud Run để tinh chỉnh mô hình Gemma, sau đó phân phát kết quả trên Cloud Run bằng vLLM.
Trong lớp học lập trình này, bạn sẽ sử dụng dữ liệu tập hợp văn bản sang SQL để tạo câu trả lời của LLM bằng truy vấn SQL khi được hỏi một câu hỏi bằng ngôn ngữ tự nhiên.
Kiến thức bạn sẽ học được
- Cách điều chỉnh chi tiết bằng GPU của Công việc trên Cloud Run
- Cách phân phát mô hình bằng Cloud Run với vLLM
- Cách sử dụng cấu hình VPC trực tiếp cho Công việc GPU để tải lên và phân phát mô hình nhanh hơn
2. Trước khi bắt đầu
Bật API
Trước khi bạn có thể bắt đầu sử dụng lớp học lập trình này, hãy bật các API sau bằng cách chạy:
gcloud services enable run.googleapis.com \
compute.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
secretmanager.googleapis.com \
artifactregistry.googleapis.com
Hạn mức GPU
Yêu cầu tăng hạn mức cho một khu vực được hỗ trợ. Hạn mức là nvidia_l4_gpu_allocation_no_zonal_redundancy
, trong Cloud Run Admin API.
Lưu ý: Nếu bạn đang sử dụng một dự án mới, thì có thể mất vài phút từ khi bật API đến khi hạn mức xuất hiện trên trang này.
Khuôn mặt ôm
Lớp học lập trình này sử dụng một mô hình được lưu trữ trên Hugging Face. Để lấy mô hình này, hãy yêu cầu mã thông báo truy cập của người dùng Hugging Face có quyền "Đọc". Bạn sẽ tham chiếu đến lớp này sau dưới dạng YOUR_HF_TOKEN
.
Bạn cũng cần đồng ý với các điều khoản sử dụng để sử dụng mô hình này: https://huggingface.co/google/gemma-2b
3. Cách thiết lập và các yêu cầu
Thiết lập các tài nguyên sau:
- Tài khoản dịch vụ IAM và các quyền IAM được liên kết,
- Khoá bí mật của Trình quản lý khoá để lưu trữ mã thông báo Hugging Face,
- Bộ chứa Cloud Storage để lưu trữ mô hình được tinh chỉnh và
- Kho lưu trữ Cấu phần phần mềm để lưu trữ hình ảnh mà bạn sẽ tạo để tinh chỉnh mô hình.
- Thiết lập các biến môi trường cho lớp học lập trình này. Chúng tôi đã điền sẵn một số biến cho bạn. Chỉ định mã dự án, khu vực và mã thông báo 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 - Tạo tài khoản dịch vụ bằng cách chạy lệnh sau:
gcloud iam service-accounts create $SERVICE_ACCOUNT \
--display-name="Service account for fine-tuning codelab" - Sử dụng Trình quản lý bí mật để lưu trữ mã truy cập Hugging Face:
gcloud secrets create $SECRET_ID \
--replication-policy="automatic"
printf $HF_TOKEN | gcloud secrets versions add $SECRET_ID --data-file=- - Cấp cho tài khoản dịch vụ của bạn vai trò là Trình truy cập bí mật của Trình quản lý bí mật:
gcloud secrets add-iam-policy-binding $SECRET_ID \
--member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role='roles/secretmanager.secretAccessor' - Tạo một bộ chứa sẽ lưu trữ mô hình được tinh chỉnh:
gcloud storage buckets create -l $REGION gs://$BUCKET_NAME
- Cấp quyền truy cập vào bộ chứa cho tài khoản dịch vụ:
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
--member=serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
--role=roles/storage.objectAdmin - Tạo kho lưu trữ Cấu phần phần mềm để lưu trữ hình ảnh vùng chứa:
gcloud artifacts repositories create $AR_REPO \
--repository-format=docker \
--location=$REGION \
--description="codelab for finetuning using CR jobs" \
--project=$PROJECT_ID
4. Tạo hình ảnh công việc trên Cloud Run
Trong bước tiếp theo, bạn sẽ tạo mã thực hiện những việc sau:
- Nhập mô hình Gemma từ Hugging Face
- Điều chỉnh mô hình bằng tập dữ liệu từ Hugging Face. Công việc này sử dụng một GPU L4 để tinh chỉnh.
- Tải mô hình được tinh chỉnh có tên là
new_model
lên bộ chứa Cloud Storage
- Tạo thư mục cho mã công việc tinh chỉnh.
mkdir codelab-finetuning-job
cd codelab-finetuning-job - Tạo một tệp có tên là
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) - Tạo tệp
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 - Tạo một
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 - Tạo vùng chứa trong kho lưu trữ Artifact Registry:
gcloud builds submit \
--tag $REGION-docker.pkg.dev/$PROJECT_ID/$AR_REPO/$IMAGE_NAME \
--region $REGION
5. Triển khai và thực thi công việc
Ở bước này, bạn sẽ tạo cấu hình YAML cho công việc của mình bằng cách sử dụng đường truyền trực tiếp đến VPC để tải lên Google Cloud Storage nhanh hơn.
Xin lưu ý rằng tệp này chứa các biến mà bạn sẽ cập nhật ở bước tiếp theo.
- Tạo một tệp có tên
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 - Thay thế các biến trong tệp YAML bằng biến môi trường bằng cách chạy lệnh sau:
envsubst < finetune-job.yaml.tmpl > finetune-job.yaml
- Tạo công việc trên Cloud Run:
gcloud alpha run jobs replace finetune-job.yaml
- Thực thi công việc:
gcloud alpha run jobs execute $JOB_NAME --region $REGION --async
Quá trình này sẽ mất khoảng 10 phút để hoàn tất. Bạn có thể kiểm tra trạng thái bằng cách sử dụng đường liên kết được cung cấp trong kết quả của lệnh cuối cùng.
6. Sử dụng dịch vụ Cloud Run để phân phát mô hình được tinh chỉnh bằng vLLM
Ở bước này, bạn sẽ triển khai một dịch vụ Cloud Run. Cấu hình này sử dụng VPC trực tiếp để truy cập vào bộ chứa Cloud Storage qua mạng riêng tư nhằm tải xuống nhanh hơn.
Xin lưu ý rằng tệp này chứa các biến mà bạn sẽ cập nhật ở bước tiếp theo.
- Tạo tệp
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 - Cập nhật tên bộ chứa cho tệp
service.yaml
.envsubst < service.yaml.tmpl > service.yaml
- Triển khai Dịch vụ Cloud Run:
gcloud alpha run services replace service.yaml
7. Kiểm thử mô hình được tinh chỉnh
Ở bước này, bạn sẽ nhắc mô hình kiểm thử việc tinh chỉnh.
- Lấy URL dịch vụ cho dịch vụ Cloud Run:
SERVICE_URL=$(gcloud run services describe serve-gemma-sql --platform managed --region $REGION --format 'value(status.url)')
- Tạo câu lệnh cho mô hình.
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)"
- Gọi dịch vụ của bạn bằng CURL để nhắc mô hình:
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
Bạn sẽ thấy một phản hồi tương tự như sau:
{"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. Xin chúc mừng!
Chúc mừng bạn đã hoàn thành lớp học lập trình!
Bạn nên xem tài liệu về Cloud Run.
Nội dung đã đề cập
- Cách điều chỉnh chi tiết bằng GPU của Công việc trên Cloud Run
- Cách phân phát mô hình bằng Cloud Run với vLLM
- Cách sử dụng cấu hình VPC trực tiếp cho Công việc GPU để tải lên và phân phát mô hình nhanh hơn
9. Dọn dẹp
Để tránh bị tính phí do nhầm lẫn, ví dụ: nếu các dịch vụ Cloud Run vô tình được gọi nhiều lần hơn hạn mức gọi Cloud Run hằng tháng trong cấp miễn phí, bạn có thể xoá dịch vụ Cloud Run mà bạn đã tạo ở Bước 6.
Để xoá dịch vụ Cloud Run, hãy truy cập vào Cloud Console của Cloud Run tại https://console.cloud.google.com/run rồi xoá dịch vụ serve-gemma-sql
.
Để xoá toàn bộ dự án, hãy chuyển đến phần Quản lý tài nguyên, chọn dự án bạn đã tạo ở Bước 2 rồi chọn Xoá. Nếu xoá dự án, bạn sẽ cần thay đổi dự án trong SDK trên đám mây. Bạn có thể xem danh sách tất cả dự án hiện có bằng cách chạy gcloud projects list
.