Memvisualisasikan embeddings dengan t-SNE

Lihat di ai.google.dev Menjalankan di Google Colab Lihat sumber di GitHub

Ringkasan

Tutorial ini menunjukkan cara memvisualisasikan dan melakukan pengelompokan dengan embedding dari Gemini API. Anda akan memvisualisasikan subset dari 20 set data Newsgroup menggunakan t-SNE dan mengelompokkan subset tersebut menggunakan algoritma KMeans.

Untuk mengetahui informasi selengkapnya tentang cara mulai menggunakan embedding yang dihasilkan dari Gemini API, lihat panduan memulai Python.

Prasyarat

Anda dapat menjalankan panduan memulai ini di Google Colab.

Untuk menyelesaikan panduan memulai ini di lingkungan pengembangan Anda sendiri, pastikan lingkungan Anda memenuhi persyaratan berikut:

  • Python 3.9 dan yang lebih baru
  • Penginstalan jupyter untuk menjalankan notebook.

Penyiapan

Pertama, download dan instal library 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

Ambil Kunci API

Sebelum dapat menggunakan Gemini API, Anda harus mendapatkan kunci API terlebih dahulu. Jika Anda belum memilikinya, buat kunci dengan sekali klik di Google AI Studio.

Mendapatkan kunci API

Di Colab, tambahkan kunci ke secret manager di bawah "mo" di panel kiri. Beri nama API_KEY.

Setelah Anda memiliki kunci API, teruskan ke SDK. Anda dapat melakukannya dengan dua cara:

  • Masukkan kunci di variabel lingkungan GOOGLE_API_KEY (SDK akan otomatis mengambilnya dari sana).
  • Teruskan kunci ke 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

Set data

20 Newsgroups Text Dataset berisi 18.000 postingan newsgroup tentang 20 topik yang dibagi menjadi set pelatihan dan pengujian. Pembagian antara set data pelatihan dan pengujian didasarkan pada pesan yang diposting sebelum dan setelah tanggal tertentu. Untuk tutorial ini, Anda akan menggunakan subset pelatihan.

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']

Berikut adalah contoh pertama dalam set pelatihan.

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

Selanjutnya, Anda akan mengambil sampel beberapa data dengan mengambil 100 titik data dalam set data pelatihan, dan melepaskan beberapa kategori untuk dijalankan melalui tutorial ini. Pilih kategori sains untuk dibandingkan.

# 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

Membuat embedding

Di bagian ini, Anda akan melihat cara membuat embedding untuk berbagai teks dalam kerangka data menggunakan embedding dari Gemini API.

Perubahan API pada Embeddings dengan model embedding-001

Untuk model embedding baru, embedding-001, ada parameter jenis tugas baru dan judul opsional (hanya valid dengan task_type=RETRIEVAL_DOCUMENT).

Parameter baru ini hanya berlaku untuk model embedding terbaru.Jenis tugasnya adalah:

Jenis Tugas Deskripsi
RETRIEVAL_QUERY Menentukan bahwa teks yang diberikan merupakan kueri dalam setelan penelusuran/pengambilan.
RETRIEVAL_DOCUMENT Menentukan bahwa teks yang diberikan adalah dokumen dalam setelan penelusuran/pengambilan.
SEMANTIC_SIMILARITY Menentukan bahwa teks yang diberikan akan digunakan untuk Kemiripan Teks Semantik (STS).
KLASIFIKASI Menentukan bahwa embedding akan digunakan untuk klasifikasi.
PENGELOLAAN Menentukan bahwa embedding akan digunakan untuk pengelompokan.
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]

Pengurangan dimensi

Panjang vektor embedding dokumen adalah 768. Untuk memvisualisasikan bagaimana dokumen tersemat dikelompokkan bersama, Anda perlu menerapkan pengurangan dimensi karena Anda hanya dapat memvisualisasikan embeddings dalam ruang 2D atau 3D. Dokumen yang serupa secara kontekstual harus saling berdekatan dalam ruang dibandingkan dengan dokumen yang tidak serupa.

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)

Anda akan menerapkan pendekatan t-Distributed Stochastic Neighbor Embedding (t-SNE) untuk melakukan pengurangan dimensi. Teknik ini mengurangi jumlah dimensi, sekaligus mempertahankan kelompok (titik yang berdekatan tetap berdekatan). Untuk data asli, model akan mencoba membuat distribusi di mana titik data lainnya adalah "tetangga" (misalnya, keduanya memiliki arti yang sama). Kemudian mengoptimalkan fungsi objektif untuk menjaga distribusi serupa dalam visualisasi.

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

Bandingkan hasil dengan KMeans

Pengelompokan Kmean adalah algoritma pengelompokan populer dan sering digunakan untuk unsupervised learning. Hal ini secara berulang menentukan titik pusat k terbaik, dan menetapkan setiap contoh ke sentroid terdekat. Masukkan embeddings langsung ke dalam algoritma KMeans untuk membandingkan visualisasi embeddings dengan performa algoritma machine learning.

# 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}

Dapatkan mayoritas cluster per grup, dan lihat berapa banyak anggota sebenarnya dari grup tersebut yang ada di cluster tersebut.

# 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

Untuk memvisualisasikan performa KMean yang diterapkan pada data dengan lebih baik, Anda dapat menggunakan matriks konfusi. Matriks konfusi memungkinkan Anda menilai performa model klasifikasi di luar akurasi. Anda dapat melihat poin yang salah diklasifikasikan sebagai mana yang diklasifikasikan. Anda akan memerlukan nilai aktual dan nilai prediksi, yang telah Anda kumpulkan dalam {i>dataframe<i} di atas.

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

Langkah berikutnya

Anda sekarang telah berhasil membuat visualisasi embeddings sendiri menggunakan pengelompokan. Coba gunakan data tekstual Anda sendiri untuk memvisualisasikannya sebagai embedding. Anda dapat melakukan pengurangan dimensi untuk menyelesaikan langkah visualisasi. Perhatikan bahwa TSNE berfungsi baik dalam pengelompokkan input, tetapi dapat memerlukan waktu lebih lama untuk menyatu atau mungkin macet di nilai minimum lokal. Jika Anda mengalami masalah ini, teknik lain yang dapat Anda pertimbangkan adalah analisis komponen utama (PCA).

Ada juga algoritma pengelompokan lain di luar KMeans, seperti pengelompokan spasial berbasis kepadatan (DBSCAN).

Untuk mempelajari cara menggunakan layanan lain di Gemini API, kunjungi panduan memulai Python. Untuk mempelajari lebih lanjut cara menggunakan embeddings, lihat contoh yang tersedia. Untuk mempelajari cara membuatnya dari awal, lihat tutorial Word Embeddings TensorFlow.