ปรับแต่ง Gemini บน Vertex AI

1. บทนำ

ในแล็บนี้ คุณจะได้เรียนรู้วิธีการทำเวิร์กโฟลว์ทั้งหมดของการปรับแต่งแบบมีการควบคุมดูแลในโมเดล Google Gemini เพื่อปรับให้เหมาะกับงานที่เฉพาะเจาะจง นั่นคือการสรุปบทความ แม้ว่าโมเดลภาษาขนาดใหญ่จะมีประสิทธิภาพ แต่ลักษณะการใช้งานอเนกประสงค์หมายความว่าคุณสามารถทำให้โมเดลมีประสิทธิภาพมากยิ่งขึ้นสำหรับกรณีการใช้งานที่เฉพาะเจาะจงผ่านการปรับแต่ง การฝึกโมเดลด้วยชุดข้อมูลตัวอย่างคุณภาพสูงจะช่วยปรับปรุงความสอดคล้อง คุณภาพ และประสิทธิภาพของโมเดลสำหรับงานเป้าหมาย

คุณจะใช้ Gemini 2.5 Flash ซึ่งเป็นโมเดลที่มีน้ำหนักเบาและคุ้มค่า และทำการปรับแต่งโดยใช้ Vertex AI

ภาพรวมสถาปัตยกรรม

สิ่งที่เราจะสร้างมีดังนี้

  • Cloud Shell: สภาพแวดล้อมในการพัฒนาซอฟต์แวร์ของคุณ
  • Cloud Storage: จัดเก็บข้อมูลการฝึก/การตรวจสอบในรูปแบบ JSONL
  • การฝึก Vertex AI: จัดการงานการปรับแต่ง
  • อุปกรณ์ปลายทาง Vertex AI: โฮสต์โมเดลที่ปรับแต่งแล้ว

สิ่งที่คุณจะได้เรียนรู้

  • เตรียมชุดข้อมูลคุณภาพสูงสำหรับการปรับแต่งแบบมีการควบคุมดูแล
  • กำหนดค่าและเปิดใช้งานการปรับแต่งโดยใช้ Vertex AI SDK สำหรับ Python
  • ประเมินโมเดลโดยใช้เมตริกอัตโนมัติ (คะแนน ROUGE)
  • เปรียบเทียบโมเดลพื้นฐานกับโมเดลที่ปรับแต่งแล้วเพื่อวัดปริมาณการปรับปรุง

2. การตั้งค่าโปรเจ็กต์

บัญชี Google

หากยังไม่มีบัญชี Google ส่วนบุคคล คุณต้องสร้างบัญชี Google

ใช้บัญชีส่วนบุคคล แทนบัญชีของที่ทำงานหรือบัญชีโรงเรียน

ลงชื่อเข้าใช้คอนโซล Google Cloud

ลงชื่อเข้าใช้ คอนโซล Google Cloud โดยใช้บัญชี Google ส่วนบุคคล

เปิดใช้การเรียกเก็บเงิน

แลกรับเครดิต Google Cloud (ไม่บังคับ)

หากต้องการเข้าร่วมเวิร์กช็อปนี้ คุณต้องมีบัญชีสำหรับการเรียกเก็บเงินที่มีเครดิต ใช้เครดิตจากแบนเนอร์ที่ด้านบนของ Codelab นี้เพื่อเริ่มต้นใช้งาน หากเชื่อมต่อกับบัญชีสำหรับการเรียกเก็บเงินอยู่แล้ว คุณสามารถข้ามขั้นตอนนี้ได้

สร้างโปรเจ็กต์ (ไม่บังคับ)

หากไม่มีโปรเจ็กต์ปัจจุบันที่ต้องการใช้สำหรับแล็บนี้ ให้ สร้างโปรเจ็กต์ใหม่ที่นี่

3. เปิด Cloud Shell Editor

  1. คลิกลิงก์นี้เพื่อไปยัง Cloud Shell Editor โดยตรง
  2. หากระบบแจ้งให้ให้สิทธิ์ในวันนี้ ให้คลิกให้สิทธิ์ เพื่อดำเนินการต่อ คลิกเพื่อให้สิทธิ์ Cloud Shell
  3. หากเทอร์มินัลไม่ปรากฏที่ด้านล่างของหน้าจอ ให้เปิดโดยทำดังนี้
    • คลิกดู
    • คลิก Terminalเปิดเทอร์มินัลใหม่ใน Cloud Shell Editor
  4. ตั้งค่าโปรเจ็กต์ในเทอร์มินัลด้วยคำสั่งนี้
    gcloud config set project [PROJECT_ID]
    
    • ตัวอย่าง
      gcloud config set project lab-project-id-example
      
    • หากจำรหัสโปรเจ็กต์ไม่ได้ คุณสามารถแสดงรหัสโปรเจ็กต์ทั้งหมดได้ด้วยคำสั่งต่อไปนี้
      gcloud projects list
      
      ตั้งค่ารหัสโปรเจ็กต์ในเทอร์มินัล Cloud Shell Editor
  5. คุณควรเห็นข้อความต่อไปนี้
    Updated property [core/project].
    

4. เปิดใช้ API

หากต้องการใช้ Vertex AI และบริการอื่นๆ คุณต้องเปิดใช้ API ที่จำเป็นในโปรเจ็กต์ Google Cloud

  1. เปิดใช้ API ในเทอร์มินัลโดยทำดังนี้
    • Vertex AI API (aiplatform.googleapis.com): ช่วยให้ใช้ Vertex AI ในการปรับแต่งและแสดงโมเดล
    • Cloud Storage API (storage.googleapis.com): ช่วยให้จัดเก็บชุดข้อมูลและอาร์ติแฟกต์ของโมเดลได้
    gcloud services enable aiplatform.googleapis.com \
        storage.googleapis.com
    

5. ตั้งค่าสภาพแวดล้อมของโปรเจ็กต์

สร้างไดเรกทอรีงาน

  1. ใน เทอร์มินัล ให้สร้างไดเรกทอรีสำหรับโปรเจ็กต์แล้วไปยังไดเรกทอรีนั้น
    mkdir gemini-finetuning
    cd gemini-finetuning
    

ตั้งค่าตัวแปรสภาพแวดล้อม

  1. ใน เทอร์มินัล ให้กำหนดตัวแปรสภาพแวดล้อมสำหรับโปรเจ็กต์ เราจะสร้างไฟล์ env.sh เพื่อจัดเก็บตัวแปรเหล่านี้เพื่อให้โหลดซ้ำได้ง่ายหากเซสชันของคุณถูกตัดการเชื่อมต่อ
    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
    

สร้าง Bucket ของ Cloud Storage

  1. ใน เทอร์มินัล ให้สร้าง Bucket เพื่อจัดเก็บชุดข้อมูลและอาร์ติแฟกต์ของโมเดล
    gcloud storage buckets create gs://$BUCKET_NAME --project=$PROJECT_ID --location=$REGION
    

ตั้งค่าสภาพแวดล้อมเสมือน

  1. เราจะใช้ uv เพื่อจัดการสภาพแวดล้อม Python เรียกใช้คำสั่งต่อไปนี้ใน เทอร์มินัล
    uv venv .venv
    source .venv/bin/activate
    
  2. ติดตั้งแพ็กเกจ Python ที่จำเป็นใน เทอร์มินัล
    uv pip install google-cloud-aiplatform rouge-score matplotlib pandas tqdm
    

6. เตรียมข้อมูลฝึกฝน

ข้อมูลคุณภาพเป็นพื้นฐานของการปรับแต่งที่ประสบความสำเร็จ คุณจะใช้ชุดข้อมูล WikiLingua แปลงชุดข้อมูลเป็นรูปแบบ JSONL ที่เฉพาะเจาะจงซึ่ง Gemini กำหนด และอัปโหลดไปยัง Bucket ของพื้นที่จัดเก็บ

  1. สร้างไฟล์ชื่อ prepare_data.py ใน เทอร์มินัล
    cloudshell edit prepare_data.py
    
  2. วางโค้ดต่อไปนี้ลงใน 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. เรียกใช้สคริปต์การเตรียมข้อมูลใน เทอร์มินัล
    python prepare_data.py
    

7. กำหนดประสิทธิภาพเกณฑ์พื้นฐาน

คุณต้องมีเกณฑ์มาตรฐานก่อนที่จะปรับแต่ง คุณจะวัดประสิทธิภาพของโมเดล gemini-2.5-flash พื้นฐานในงานการสรุปโดยใช้คะแนน ROUGE

  1. สร้างไฟล์ชื่อ evaluate.py ใน เทอร์มินัล
    cloudshell edit evaluate.py
    
  2. วางโค้ดต่อไปนี้ลงใน 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. เรียกใช้การประเมินเกณฑ์พื้นฐานใน เทอร์มินัล
    python evaluate.py --model "gemini-2.5-flash" --output "baseline.json"
    
    การดำเนินการนี้จะสร้างไฟล์ baseline.json และพล็อตใน plots/baseline_dist.png

8. กำหนดค่าและเปิดใช้งานการปรับแต่ง

ตอนนี้คุณจะเปิดใช้งานงานการปรับแต่งที่มีการจัดการใน Vertex AI

  1. สร้างไฟล์ชื่อ tune.py ใน เทอร์มินัล
    cloudshell edit tune.py
    
  2. วางโค้ดต่อไปนี้ลงใน 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. เรียกใช้สคริปต์การปรับแต่งใน เทอร์มินัล
    python tune.py
    
    หมายเหตุ: กระบวนการนี้อาจใช้เวลาประมาณ 45 นาที คุณสามารถตรวจสอบงานใน คอนโซล Vertex AI ได้

9. ทำความเข้าใจโค้ดการฝึก

ขณะที่งานกำลังทำงานอยู่ เรามาดูสคริปต์ tune.py อย่างละเอียดเพื่อทำความเข้าใจวิธีการทำงานของการปรับแต่ง

การปรับแต่งแบบมีการควบคุมดูแลที่มีการจัดการ

สคริปต์ใช้วิธี vertexai.tuning.sft.train เพื่อส่งงานการปรับแต่งที่มีการจัดการ ซึ่งจะตัดความซับซ้อนของการจัดสรรโครงสร้างพื้นฐาน การกระจายการฝึก และการจัดการจุดตรวจสอบออก

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

การกำหนดค่า LoRA

Vertex AI ทำให้การกำหนด LoraConfig ด้วยตนเองง่ายขึ้นเป็นพารามิเตอร์หลัก 2-3 รายการ ซึ่งแตกต่างจากที่คุณอาจทำในเฟรมเวิร์กโอเพนซอร์ส

  • adapter_size: พารามิเตอร์นี้ (ตั้งค่าเป็น 4 ในสคริปต์ของเรา) จะควบคุมอันดับของอะแดปเตอร์ LoRA ขนาดที่ใหญ่ขึ้นจะช่วยให้โมเดลเรียนรู้การปรับเปลี่ยนที่ซับซ้อนมากขึ้น แต่จะเพิ่มจำนวนพารามิเตอร์ที่ฝึกได้
  • epochs: เราตั้งค่านี้เป็น 1 สำหรับแล็บนี้เพื่อให้เวลาการฝึกสั้นลง (ประมาณ 20 นาที) ในสถานการณ์จริง คุณอาจเพิ่มค่านี้เพื่อให้โมเดลเรียนรู้จากข้อมูลของคุณได้ลึกซึ้งยิ่งขึ้น แต่คุณควรระวังการปรับให้เข้ากับข้อมูลการฝึกมากเกินไป

การเลือกโมเดล

เราระบุ source_model="gemini-2.5-flash" อย่างชัดเจน Vertex AI รองรับ Gemini หลายเวอร์ชัน และการปักหมุดเวอร์ชันที่เฉพาะเจาะจงจะช่วยให้ไปป์ไลน์ของคุณมีความเสถียรและทำซ้ำได้

10. เปรียบเทียบโมเดล

เมื่อการปรับแต่งเสร็จสมบูรณ์ คุณจะเปรียบเทียบประสิทธิภาพของโมเดลใหม่กับเกณฑ์พื้นฐานได้

  1. รับอุปกรณ์ปลายทางของโมเดลที่ปรับแต่งแล้ว ระบบจะพิมพ์อุปกรณ์ปลายทางที่ส่วนท้ายของสคริปต์ tune.py โดยจะมีลักษณะคล้ายกับ projects/.../locations/.../endpoints/...
  2. เรียกใช้สคริปต์การประเมินอีกครั้ง โดยคราวนี้ส่งโมเดลที่ปรับแต่งแล้วและผลลัพธ์เกณฑ์พื้นฐานเพื่อเปรียบเทียบ
    # 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. ดูผลลัพธ์ สคริปต์จะแสดงการเปรียบเทียบคะแนน ROUGE และสร้างแผนภูมิ plots/comparison.png ที่แสดงการปรับปรุง คุณสามารถดูพล็อตได้โดยเปิดโฟลเดอร์ plots ใน Cloud Shell Editor

11. ล้างข้อมูล

ลบทรัพยากรที่คุณสร้างขึ้นเพื่อหลีกเลี่ยงค่าใช้จ่าย

  1. ลบ Bucket ของ Cloud Storage และโมเดลที่ปรับแต่งแล้วใน เทอร์มินัล
    gcloud storage rm -r gs://$BUCKET_NAME
    # Note: You can delete the model endpoint from the Vertex AI Console
    

12. ยินดีด้วย

คุณปรับแต่ง Gemini 2.5 Flash ใน Vertex AI ได้สำเร็จแล้ว

สรุป

ในแล็บนี้ คุณได้ทำสิ่งต่อไปนี้

  • เตรียมชุดข้อมูลในรูปแบบ JSONL สำหรับการปรับแต่ง Gemini
  • กำหนดเกณฑ์พื้นฐานโดยใช้โมเดล Gemini 2.5 Flash พื้นฐาน
  • เปิดใช้งานงานการปรับแต่งแบบมีการควบคุมดูแลใน Vertex AI
  • ประเมินและเปรียบเทียบโมเดลที่ปรับแต่งแล้วกับเกณฑ์พื้นฐาน

ขั้นตอนถัดไป

แล็บนี้เป็นส่วนหนึ่งของเส้นทางการเรียนรู้ AI พร้อมใช้งานจริงกับ Google Cloud

สำรวจหลักสูตรทั้งหมดเพื่อเชื่อมช่องว่างจากต้นแบบไปสู่การใช้งานจริง

แชร์ความคืบหน้าของคุณด้วยแฮชแท็ก #ProductionReadyAI