Ver en ai.google.dev | Prueba un notebook de Colab | Ver notebook en GitHub |
Descripción general
En este instructivo, se muestra cómo visualizar y realizar agrupamiento en clústeres con las incorporaciones de la API de Gemini. Visualizarás un subconjunto de los 20 conjuntos de datos de Newsgroup mediante t-SNE y agruparás ese subconjunto con el algoritmo KMeans.
Si quieres obtener más información para comenzar a usar las incorporaciones generadas a partir de la API de Gemini, consulta la guía de inicio rápido de Python.
Requisitos previos
Puedes ejecutar esta guía de inicio rápido en Google Colab.
Para completar esta guía de inicio rápido en tu entorno de desarrollo, asegúrate de que tu entorno cumpla con los siguientes requisitos:
- Python 3.9 y versiones posteriores
- Una instalación de
jupyter
para ejecutar el notebook
Configuración
Primero, descarga e instala la biblioteca de Python de la API de Gemini.
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
# 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
Obtén una clave de API
Para poder usar la API de Gemini, primero debes obtener una clave de API. Si aún no tienes una, crea una clave con un clic en Google AI Studio.
En Colab, agrega la clave al administrador de Secrets en la "Notebook" que aparece a continuación. en el panel izquierdo. Asígnale el nombre API_KEY
.
Una vez que tengas la clave de API, pásala al SDK. Puedes hacerlo de dos maneras:
- Coloca la clave en la variable de entorno
GOOGLE_API_KEY
(el SDK la recogerá automáticamente desde allí). - Pasa la clave a
genai.configure(api_key=...)
genai.configure(api_key=GOOGLE_API_KEY)
for m in genai.list_models():
if 'embedContent' in m.supported_generation_methods:
print(m.name)
models/embedding-001 models/embedding-001
Conjunto de datos
El conjunto de datos de texto de 20 grupos de noticias contiene 18,000 publicaciones de grupos de noticias sobre 20 temas divididos en conjuntos de capacitación y prueba. La división entre los conjuntos de datos de entrenamiento y prueba se basa en los mensajes publicados antes y después de una fecha específica. Para este instructivo, usarás el subconjunto de entrenamiento.
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']
Este es el primer ejemplo del conjunto de entrenamiento.
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
A continuación, tomarás 100 datos del conjunto de datos de entrenamiento y descartarás algunas de las categorías para ejecutar en este instructivo, para tomar muestras de algunos de los datos. Elige las categorías científicas que deseas comparar.
# 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
Crea las incorporaciones
En esta sección, verás cómo generar incorporaciones para los diferentes textos en el marco de datos usando las incorporaciones de la API de Gemini.
Cambios en la API a las incorporaciones con el modelo embedding-001
Para el nuevo modelo de incorporaciones, embedding-001, hay un nuevo parámetro de tipo de tarea y el título opcional (solo válido con task_type=RETRIEVAL_DOCUMENT
).
Estos parámetros nuevos se aplican solo a los modelos de incorporaciones más recientes.Los tipos de tareas son los siguientes:
Tipo de tarea | Descripción |
---|---|
RETRIEVAL_QUERY | Especifica que el texto dado es una consulta en un parámetro de configuración de búsqueda/recuperación. |
RETRIEVAL_DOCUMENT | Especifica que el texto dado de un documento en un parámetro de configuración de búsqueda y recuperación. |
SEMANTIC_SIMILARITY | Especifica que el texto dado se usará para la similitud textual semántica (STS). |
CLASIFICACIÓN | Especifica que las incorporaciones se usarán para la clasificación. |
Agrupamiento en clústeres | Especifica que las incorporaciones se usarán para el agrupamiento en clústeres. |
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]
Reducción de la dimensionalidad
La longitud del vector de incorporación de documentos es 768. Para visualizar cómo se agrupan los documentos incorporados, deberás aplicar la reducción de dimensionalidad, ya que solo puedes visualizar las incorporaciones en un espacio 2D o 3D. Los documentos contextualmente similares deben estar más juntos en el espacio a diferencia de los documentos que no son tan similares.
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)
Aplicarás el enfoque de incorporación estocástica de vecinos estocástico distribuida (t-SNE) para realizar una reducción de la dimensionalidad. Esta técnica reduce la cantidad de dimensiones y, al mismo tiempo, preserva los clústeres (los puntos que están juntos permanecen cerca). Para los datos originales, el modelo intenta construir una distribución sobre la cual otros datos son "vecinos" (p.ej., comparten un significado similar). Luego, optimiza una función objetiva para mantener una distribución similar en la visualización.
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)
Compara los resultados con KMeans
El agrupamiento en clústeres de KMeans es un algoritmo de agrupamiento en clústeres popular y se usa con frecuencia para el aprendizaje no supervisado. Determina de manera iterativa los mejores puntos centrales k y asigna cada ejemplo al centroide más cercano. Ingresa las incorporaciones directamente en el algoritmo KMeans para comparar la visualización de las incorporaciones con el rendimiento de un algoritmo de aprendizaje automático.
# 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)
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}
Obtén la mayoría de los clústeres por grupo y observa cuántos de los miembros reales de ese grupo se encuentran en ese clúster.
# 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
Para visualizar mejor el rendimiento de los KMeans aplicados a tus datos, puedes usar una matriz de confusión. La matriz de confusión te permite evaluar el rendimiento del modelo de clasificación más allá de la exactitud. Puedes ver cómo se clasifican los puntos mal clasificados. Necesitarás los valores reales y los valores predichos, que recopilaste en el marco de datos anterior.
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)
Próximos pasos
Ya creaste tu propia visualización de incorporaciones con agrupamiento en clústeres. Intenta usar tus propios datos textuales para visualizarlos como incorporaciones. Puedes realizar una reducción de la dimensionalidad para completar el paso de visualización. Ten en cuenta que el TSNE es bueno para agrupar entradas en clústeres, pero puede tardar más en converger o detenerse en los mínimos locales. Si te encuentras con este problema, otra técnica que puedes considerar es el análisis de componentes principales (ACP).
También hay otros algoritmos de agrupamiento en clústeres fuera de KMeans, como el agrupamiento en clústeres espacial basado en densidad (DBSCAN).
Para obtener más información sobre cómo usar las incorporaciones, consulta estos otros instructivos: