הדמיה של הטמעות באמצעות t-SNE

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

סקירה

מדריך זה מדגים כיצד להציג ולבצע אשכולות עם ההטמעות מ-Gemini API. תיצור קבוצת משנה של 20 מערך הנתונים של קבוצת החדשות באמצעות t-SNE ואת קבוצת הנתונים באמצעות האלגוריתם KMeans.

למידע נוסף על תחילת העבודה עם הטמעות שנוצרו מ-Gemini API, אפשר לעיין במדריך למתחילים של Python.

דרישות מוקדמות

אפשר להריץ את המדריך למתחילים ב-Google Colab.

כדי להשלים את המדריך למתחילים בסביבת הפיתוח שלכם, ודאו שהסביבה עומדת בדרישות הבאות:

  • Python 3.9 ואילך
  • התקנה של jupyter להפעלת ה-notebook.

הגדרה

קודם כול, מורידים ומתקינים את ספריית Python ל-Gemini API.

pip install -U -q google.generativeai
import re
import tqdm
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

import google.generativeai as genai
import google.ai.generativelanguage as glm

# Used to securely store your API key
from google.colab import userdata

from sklearn.datasets import fetch_20newsgroups
from sklearn.manifold import TSNE
from sklearn.cluster import KMeans
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

תפוס מפתח API

כדי להשתמש ב-Gemini API, עליך לקבל מפתח API. אם עדיין אין לך מפתח, אפשר ליצור מפתח בלחיצה אחת ב-Google AI Studio.

קבלת מפתח API

ב-Colab, מוסיפים את המפתח למנהל הסודות בקטע '🔑' בחלונית שמשמאל. נותנים לו את השם API_KEY.

אחרי שמוצאים את מפתח ה-API, מעבירים אותו ל-SDK. תוכל לעשות זאת בשתי דרכים:

  • מזינים את המפתח במשתנה הסביבה GOOGLE_API_KEY (ה-SDK יאסוף אותו משם באופן אוטומטי).
  • יש להעביר את המפתח אל genai.configure(api_key=...)
# Or use `os.getenv('API_KEY')` to fetch an environment variable.
API_KEY=userdata.get('API_KEY')

genai.configure(api_key=API_KEY)
for m in genai.list_models():
  if 'embedContent' in m.supported_generation_methods:
    print(m.name)
models/embedding-001
models/embedding-001

מערך נתונים

מערך נתוני הטקסט של קבוצות דיון של 20 קבוצות דיון מכיל 18,000 פוסטים של קבוצות דיון ב-20 נושאים, המחולקים לקבוצות אימון ולקבוצות בדיקה. החלוקה בין מערכי הנתונים של האימון לבין מערכי הנתונים של הבדיקה מבוססת על הודעות שפורסמו לפני ואחרי תאריך מסוים. במדריך הזה תשתמשו בקבוצת המשנה של האימון.

newsgroups_train = fetch_20newsgroups(subset='train')

# View list of class names for dataset
newsgroups_train.target_names
['alt.atheism',
 'comp.graphics',
 'comp.os.ms-windows.misc',
 'comp.sys.ibm.pc.hardware',
 'comp.sys.mac.hardware',
 'comp.windows.x',
 'misc.forsale',
 'rec.autos',
 'rec.motorcycles',
 'rec.sport.baseball',
 'rec.sport.hockey',
 'sci.crypt',
 'sci.electronics',
 'sci.med',
 'sci.space',
 'soc.religion.christian',
 'talk.politics.guns',
 'talk.politics.mideast',
 'talk.politics.misc',
 'talk.religion.misc']

זוהי הדוגמה הראשונה בערכת האימון.

idx = newsgroups_train.data[0].index('Lines')
print(newsgroups_train.data[0][idx:])
Lines: 15

 I was wondering if anyone out there could enlighten me on this car I saw
the other day. It was a 2-door sports car, looked to be from the late 60s/
early 70s. It was called a Bricklin. The doors were really small. In addition,
the front bumper was separate from the rest of the body. This is 
all I know. If anyone can tellme a model name, engine specs, years
of production, where this car is made, history, or whatever info you
have on this funky looking car, please e-mail.

Thanks,

- IL
   ---- brought to you by your neighborhood Lerxst ----
# Apply functions to remove names, emails, and extraneous words from data points in newsgroups.data
newsgroups_train.data = [re.sub(r'[\w\.-]+@[\w\.-]+', '', d) for d in newsgroups_train.data] # Remove email
newsgroups_train.data = [re.sub(r"\([^()]*\)", "", d) for d in newsgroups_train.data] # Remove names
newsgroups_train.data = [d.replace("From: ", "") for d in newsgroups_train.data] # Remove "From: "
newsgroups_train.data = [d.replace("\nSubject: ", "") for d in newsgroups_train.data] # Remove "\nSubject: "
# Put training points into a dataframe
df_train = pd.DataFrame(newsgroups_train.data, columns=['Text'])
df_train['Label'] = newsgroups_train.target
# Match label to target name index
df_train['Class Name'] = df_train['Label'].map(newsgroups_train.target_names.__getitem__)
# Retain text samples that can be used in the gecko model.
df_train = df_train[df_train['Text'].str.len() < 10000]

df_train

בשלב הבא דוגמים חלק מהנתונים באמצעות 100 נקודות נתונים במערך הנתונים לאימון, ומשחררים כמה מהקטגוריות כדי לעבור על המדריך. בחר את הקטגוריות המדעיות להשוואה.

# Take a sample of each label category from df_train
SAMPLE_SIZE = 150
df_train = (df_train.groupby('Label', as_index = False)
                    .apply(lambda x: x.sample(SAMPLE_SIZE))
                    .reset_index(drop=True))

# Choose categories about science
df_train = df_train[df_train['Class Name'].str.contains('sci')]

# Reset the index
df_train = df_train.reset_index()
df_train
df_train['Class Name'].value_counts()
sci.crypt          150
sci.electronics    150
sci.med            150
sci.space          150
Name: Class Name, dtype: int64

יצירת ההטמעות

בקטע הזה נסביר איך ליצור הטמעות לטקסטים השונים במסגרת הנתונים באמצעות ההטמעות מ-Gemini API.

שינויים ב-API בהטמעות עם מודל embedding-001

במודל ההטמעות (embedsing-001) החדש, יש פרמטר חדש של סוג המשימה והכותרת האופציונלית (רלוונטי רק ל-task_type=RETRIEVAL_DOCUMENT).

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

סוג המשימה תיאור
RETRIEVAL_QUERY מציין שהטקסט הנתון הוא שאילתה בהגדרת חיפוש/אחזור.
RETRIEVAL_DOCUMENT מציינת שהטקסט הנתון הוא מסמך בהגדרת חיפוש/אחזור.
SEMANTIC_SIMILARITY קובעת שהטקסט הנתון ישמש לדמיון טקסט סמנטי (STS).
סיווג מציינת שההטמעות ישמשו לסיווג.
אשכול המדיניות קובעת שההטמעות ישמשו לאשכולות.
from tqdm.auto import tqdm
tqdm.pandas()

from google.api_core import retry

def make_embed_text_fn(model):

  @retry.Retry(timeout=300.0)
  def embed_fn(text: str) -> list[float]:
    # Set the task_type to CLUSTERING.
    embedding = genai.embed_content(model=model,
                                    content=text,
                                    task_type="clustering")
    return embedding["embedding"]

  return embed_fn

def create_embeddings(df):
  model = 'models/embedding-001'
  df['Embeddings'] = df['Text'].progress_apply(make_embed_text_fn(model))
  return df

df_train = create_embeddings(df_train)
0%|          | 0/600 [00:00<?, ?it/s]

הפחתת מימדים

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

len(df_train['Embeddings'][0])
768
# Convert df_train['Embeddings'] Pandas series to a np.array of float32
X = np.array(df_train['Embeddings'].to_list(), dtype=np.float32)
X.shape
(600, 768)

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

tsne = TSNE(random_state=0, n_iter=1000)
tsne_results = tsne.fit_transform(X)
df_tsne = pd.DataFrame(tsne_results, columns=['TSNE1', 'TSNE2'])
df_tsne['Class Name'] = df_train['Class Name'] # Add labels column from df_train to df_tsne
df_tsne
fig, ax = plt.subplots(figsize=(8,6)) # Set figsize
sns.set_style('darkgrid', {"grid.color": ".6", "grid.linestyle": ":"})
sns.scatterplot(data=df_tsne, x='TSNE1', y='TSNE2', hue='Class Name', palette='hls')
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('Scatter plot of news using t-SNE');
plt.xlabel('TSNE1');
plt.ylabel('TSNE2');
plt.axis('equal')
(-46.191162300109866,
 53.521015357971194,
 -39.96646995544434,
 37.282975387573245)

png

השוואת תוצאות ל-KMeans

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

# Apply KMeans
kmeans_model = KMeans(n_clusters=4, random_state=1, n_init='auto').fit(X)
labels = kmeans_model.fit_predict(X)
df_tsne['Cluster'] = labels
df_tsne
fig, ax = plt.subplots(figsize=(8,6)) # Set figsize
sns.set_style('darkgrid', {"grid.color": ".6", "grid.linestyle": ":"})
sns.scatterplot(data=df_tsne, x='TSNE1', y='TSNE2', hue='Cluster', palette='magma')
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('Scatter plot of news using KMeans Clustering');
plt.xlabel('TSNE1');
plt.ylabel('TSNE2');
plt.axis('equal')
(-46.191162300109866,
 53.521015357971194,
 -39.96646995544434,
 37.282975387573245)

png

def get_majority_cluster_per_group(df_tsne_cluster, class_names):
  class_clusters = dict()
  for c in class_names:
    # Get rows of dataframe that are equal to c
    rows = df_tsne_cluster.loc[df_tsne_cluster['Class Name'] == c]
    # Get majority value in Cluster column of the rows selected
    cluster = rows.Cluster.mode().values[0]
    # Populate mapping dictionary
    class_clusters[c] = cluster
  return class_clusters
classes = df_tsne['Class Name'].unique()
class_clusters = get_majority_cluster_per_group(df_tsne, classes)
class_clusters
{'sci.crypt': 1, 'sci.electronics': 3, 'sci.med': 2, 'sci.space': 0}

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

# Convert the Cluster column to use the class name
class_by_id = {v: k for k, v in class_clusters.items()}
df_tsne['Predicted'] = df_tsne['Cluster'].map(class_by_id.__getitem__)

# Filter to the correctly matched rows
correct = df_tsne[df_tsne['Class Name'] == df_tsne['Predicted']]

# Summarise, as a percentage
acc = correct['Class Name'].value_counts() / SAMPLE_SIZE
acc
sci.space          0.966667
sci.med            0.960000
sci.electronics    0.953333
sci.crypt          0.926667
Name: Class Name, dtype: float64
# Get predicted values by name
df_tsne['Predicted'] = ''
for idx, rows in df_tsne.iterrows():
  cluster = rows['Cluster']
  # Get key from mapping based on cluster value
  key = list(class_clusters.keys())[list(class_clusters.values()).index(cluster)]
  df_tsne.at[idx, 'Predicted'] = key

df_tsne

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

cm = confusion_matrix(df_tsne['Class Name'].to_list(), df_tsne['Predicted'].to_list())
disp = ConfusionMatrixDisplay(confusion_matrix=cm,
                              display_labels=classes)
disp.plot(xticks_rotation='vertical')
plt.title('Confusion Matrix for Actual and Clustered Newsgroups');
plt.grid(False)

png

השלבים הבאים

יצרת עכשיו תצוגה חזותית משלך של הטמעות עם אשכולות! נסו להשתמש בנתוני טקסט משלכם כדי להמחיש אותם כהטמעות. אפשר לבצע הפחתת מימדים כדי להשלים את שלב התצוגה החזותית. שימו לב ש-TSNE טוב בקיבוץ מקורות קלט, אבל יכול להיות שייקח יותר זמן עד שהם יתכנסו או תיתקע במינימום המקומי. נתקלתם בבעיה הזו? כדאי להשתמש בשיטה אחרת שנקראת ניתוח רכיבים עיקריים (PCA).

קיימים גם אלגוריתמים אחרים של אשכולות מחוץ ל-KMeans, כמו אשכולות מרחביים מבוססי צפיפות (DBSCAN).

על מנת ללמוד איך להשתמש בשירותים אחרים ב-Gemini API, אפשר לעיין במדריך למתחילים של Python. כדי ללמוד איך להשתמש בהטמעות, אפשר לעיין בדוגמאות הזמינות. במדריך של TensorFlow בנושא הטמעת מילים מוסבר איך ליצור אותן מאפס.