כוונון עדין של EmbeddingGemma

לצפייה ב-ai.google.dev הרצה ב-Google Colab הפעלה ב-Kaggle פתיחה ב-Vertex AI צפייה במקור ב-GitHub

התאמה עדינה עוזרת לצמצם את הפער בין ההבנה הכללית של המודל לבין הדיוק המיוחד והביצועים הגבוהים שהאפליקציה שלכם דורשת. מכיוון שאין מודל מושלם לכל משימה, כוונון עדין מאפשר להתאים אותו לדומיין הספציפי שלכם.

נניח שהחברה שלכם, Shibuya Financial, מציעה מגוון מוצרים פיננסיים מורכבים כמו קרנות נאמנות, חשבונות NISA (חשבונות חיסכון עם הטבות מס) ומשכנתאות. צוות תמיכת הלקוחות שלכם משתמש במאגר ידע פנימי כדי למצוא במהירות תשובות לשאלות של לקוחות.

הגדרה

לפני שמתחילים את המדריך הזה, צריך לבצע את השלבים הבאים:

  • כדי לקבל גישה ל-EmbeddingGemma, צריך להתחבר ל-Hugging Face ולבחור באפשרות Acknowledge license (אישור רישיון) עבור מודל Gemma.
  • יוצרים אסימון גישה ב-Hugging Face ומשתמשים בו כדי להתחבר מ-Colab.

ה-notebook הזה יפעל ב-CPU או ב-GPU.

התקנת חבילות Python

מתקינים את הספריות שנדרשות להרצת מודל EmbeddingGemma וליצירת הטמעות. ‫Sentence Transformers היא מסגרת Python להטמעות של טקסט ותמונות. מידע נוסף זמין במאמר בנושא Sentence Transformers.

pip install -U sentence-transformers git+https://github.com/huggingface/transformers@v4.56.0-Embedding-Gemma-preview

אחרי שתאשרו את הרישיון, תצטרכו טוקן תקף של Hugging Face כדי לגשת למודל.

# Login into Hugging Face Hub
from huggingface_hub import login
login()

טעינת מודל

משתמשים בספריות sentence-transformers כדי ליצור מופע של מחלקת מודל עם EmbeddingGemma.

import torch
from sentence_transformers import SentenceTransformer

device = "cuda" if torch.cuda.is_available() else "cpu"

model_id = "google/embeddinggemma-300M"
model = SentenceTransformer(model_id).to(device=device)

print(f"Device: {model.device}")
print(model)
print("Total number of parameters in the model:", sum([p.numel() for _, p in model.named_parameters()]))
Device: cuda:0
SentenceTransformer(
  (0): Transformer({'max_seq_length': 2048, 'do_lower_case': False, 'architecture': 'Gemma3TextModel'})
  (1): Pooling({'word_embedding_dimension': 768, 'pooling_mode_cls_token': False, 'pooling_mode_mean_tokens': True, 'pooling_mode_max_tokens': False, 'pooling_mode_mean_sqrt_len_tokens': False, 'pooling_mode_weightedmean_tokens': False, 'pooling_mode_lasttoken': False, 'include_prompt': True})
  (2): Dense({'in_features': 768, 'out_features': 3072, 'bias': False, 'activation_function': 'torch.nn.modules.linear.Identity'})
  (3): Dense({'in_features': 3072, 'out_features': 768, 'bias': False, 'activation_function': 'torch.nn.modules.linear.Identity'})
  (4): Normalize()
)
Total number of parameters in the model: 307581696

הכנת מערך הנתונים לכוונון עדין

זה החלק הכי חשוב. צריך ליצור מערך נתונים שיסביר למודל מה המשמעות של 'דומה' בהקשר הספציפי שלכם. הנתונים האלה מובנים בדרך כלל כשלישיות: (עוגן, חיובי, שלילי)

  • עוגן: השאילתה או המשפט המקוריים.
  • חיובי: משפט שדומה מאוד או זהה למשפט העוגן מבחינה סמנטית.
  • שלילי: משפט שקשור לנושא אבל שונה מבחינה סמנטית.

בדוגמה הזו הכנו רק 3 שלישיות, אבל כדי שהאפליקציה תפעל בצורה טובה, צריך להשתמש במערך נתונים גדול בהרבה.

from datasets import Dataset

dataset = [
    ["How do I open a NISA account?", "What is the procedure for starting a new tax-free investment account?", "I want to check the balance of my regular savings account."],
    ["Are there fees for making an early repayment on a home loan?", "If I pay back my house loan early, will there be any costs?", "What is the management fee for this investment trust?"],
    ["What is the coverage for medical insurance?", "Tell me about the benefits of the health insurance plan.", "What is the cancellation policy for my life insurance?"],
]

# Convert the list-based dataset into a list of dictionaries.
data_as_dicts = [ {"anchor": row[0], "positive": row[1], "negative": row[2]} for row in dataset ]

# Create a Hugging Face `Dataset` object from the list of dictionaries.
train_dataset = Dataset.from_list(data_as_dicts)
print(train_dataset)
Dataset({
    features: ['anchor', 'positive', 'negative'],
    num_rows: 3
})

לפני הכוונון העדין

חיפוש של 'השקעה פטורה ממס' עשוי להניב את התוצאות הבאות, עם ציוני דמיון:

  1. מסמך: פתיחת חשבון NISA (ציון: 0.45)
  2. מסמך: פתיחת חשבון חיסכון רגיל (ציון: 0.48) <- ציון דומה, עלול לגרום לבלבול
  3. מסמך: מדריך לבקשה למשכנתה (ציון: 0.42)
task_name = "STS"

def get_scores(query, documents):
  # Calculate embeddings by calling model.encode()
  query_embeddings = model.encode(query, prompt=task_name)
  doc_embeddings = model.encode(documents, prompt=task_name)

  # Calculate the embedding similarities
  similarities = model.similarity(query_embeddings, doc_embeddings)

  for idx, doc in enumerate(documents):
    print("Document: ", doc, "-> 🤖 Score: ", similarities.numpy()[0][idx])

query = "I want to start a tax-free installment investment, what should I do?"
documents = ["Opening a NISA Account", "Opening a Regular Savings Account", "Home Loan Application Guide"]

get_scores(query, documents)
Document:  Opening a NISA Account -> 🤖 Score:  0.45698774
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.48092696
Document:  Home Loan Application Guide -> 🤖 Score:  0.42127067

הדרכה

באמצעות מסגרת כמו sentence-transformers ב-Python, מודל הבסיס לומד בהדרגה את ההבדלים הדקים במונחים הפיננסיים שלכם.

from sentence_transformers import SentenceTransformerTrainer, SentenceTransformerTrainingArguments
from sentence_transformers.losses import MultipleNegativesRankingLoss
from transformers import TrainerCallback

loss = MultipleNegativesRankingLoss(model)

args = SentenceTransformerTrainingArguments(
    # Required parameter:
    output_dir="my-embedding-gemma",
    # Optional training parameters:
    prompts=model.prompts[task_name],    # use model's prompt to train
    num_train_epochs=5,
    per_device_train_batch_size=1,
    learning_rate=2e-5,
    warmup_ratio=0.1,
    # Optional tracking/debugging parameters:
    logging_steps=train_dataset.num_rows,
    report_to="none",
)

class MyCallback(TrainerCallback):
    "A callback that evaluates the model at the end of eopch"
    def __init__(self, evaluate):
        self.evaluate = evaluate # evaluate function

    def on_log(self, args, state, control, **kwargs):
        # Evaluate the model using text generation
        print(f"Step {state.global_step} finished. Running evaluation:")
        self.evaluate()

def evaluate():
  get_scores(query, documents)

trainer = SentenceTransformerTrainer(
    model=model,
    args=args,
    train_dataset=train_dataset,
    loss=loss,
    callbacks=[MyCallback(evaluate)]
)
trainer.train()
Step 3 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.6449194
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.44123
Document:  Home Loan Application Guide -> 🤖 Score:  0.46752414
Step 6 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.68873787
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.34069622
Document:  Home Loan Application Guide -> 🤖 Score:  0.50065553
Step 9 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.7148906
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.30480516
Document:  Home Loan Application Guide -> 🤖 Score:  0.52454984
Step 12 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.72614634
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.29255486
Document:  Home Loan Application Guide -> 🤖 Score:  0.5370023
Step 15 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.7294032
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.2893038
Document:  Home Loan Application Guide -> 🤖 Score:  0.54087913
Step 15 finished. Running evaluation:
Document:  Opening a NISA Account -> 🤖 Score:  0.7294032
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.2893038
Document:  Home Loan Application Guide -> 🤖 Score:  0.54087913
TrainOutput(global_step=15, training_loss=0.009651281436261646, metrics={'train_runtime': 63.2486, 'train_samples_per_second': 0.237, 'train_steps_per_second': 0.237, 'total_flos': 0.0, 'train_loss': 0.009651281436261646, 'epoch': 5.0})

אחרי כוונון עדין

אותו חיפוש מניב עכשיו תוצאות ברורות הרבה יותר:

  1. מסמך: פתיחת חשבון NISA (ציון: 0.72) <- הרבה יותר בטוח
  2. מסמך: פתיחת חשבון חיסכון רגיל (ניקוד: 0.28) <- רלוונטיות נמוכה באופן ברור
  3. מסמך: מדריך להגשת בקשה למשכנתה (ציון: 0.54)
get_scores(query, documents)
Document:  Opening a NISA Account -> 🤖 Score:  0.7294032
Document:  Opening a Regular Savings Account -> 🤖 Score:  0.2893038
Document:  Home Loan Application Guide -> 🤖 Score:  0.54087913

כדי להעלות את המודל שלכם ל-Hugging Face Hub, אתם יכולים להשתמש בשיטה push_to_hub מהספרייה Sentence Transformers.

העלאת המודל מאפשרת גישה נוחה להסקת מסקנות ישירות מ-Hub, שיתוף עם אחרים וניהול גרסאות של העבודה. אחרי ההעלאה, כל אחד יכול לטעון את המודל שלכם באמצעות שורת קוד אחת, פשוט על ידי הפניה למזהה המודל הייחודי שלו <username>/my-embedding-gemma

# Push to Hub
model.push_to_hub("my-embedding-gemma")

סיכום והשלבים הבאים

למדתם איך להתאים מודל EmbeddingGemma לדומיין ספציפי באמצעות כוונון עדין עם ספריית Sentence Transformers.

מה עוד אפשר לעשות עם EmbeddingGemma: