latentSource

Mapeando el universo de Star Wars

Un experimento de datos divertido: generar embeddings de personajes de Star Wars en un espacio 3D basados en su sensibilidad a la Fuerza y sus facciones.

·5 min read
Compartir
Mapeando el universo de Star Wars

Mapeando el universo de Star Wars

La ciencia de datos no siempre tiene que ser seria. A veces, la forma más limpia de demostrar una técnica es aplicarla a algo que realmente te importa. Por eso, voy a tomar sentence embeddings, grafos de similitud y reducción de dimensionalidad, y los orientaré hacia una galaxia muy, muy lejana.

La pregunta que me planteo es: ¿podemos generar embeddings de personajes de Star Wars en un espacio vectorial y encontrar una estructura significativa en sus relaciones? Spoiler: sí, y lo que surge dice mucho sobre cómo está conectada la narrativa de la franquicia.

Construyendo descripciones de personajes

El primer paso es escribir descripciones de texto que capturen los rasgos esenciales de cada personaje. No busco entradas exhaustivas de Wookieepedia. Quiero resúmenes concisos que codifiquen lo que nos interesa: facción, rol, sensibilidad a la Fuerza, era y relaciones clave.

characters = { "Luke Skywalker": "Jedi Knight, son of Anakin Skywalker, strong in the Force, " "hero of the Rebellion, trained by Obi-Wan and Yoda, redeemed his father", "Darth Vader": "Sith Lord, formerly Anakin Skywalker, fallen Jedi, serves the Emperor, " "father of Luke and Leia, mechanical suit, immense Force power", "Han Solo": "Smuggler, captain of the Millennium Falcon, no Force sensitivity, " "pragmatic, allied with the Rebellion, close friend of Chewbacca", "Leia Organa": "Princess of Alderaan, leader of the Rebellion, Force sensitive, " "twin sister of Luke, diplomat and military strategist", "Emperor Palpatine": "Sith Master, ruler of the Galactic Empire, manipulator, " "immense dark side power, orchestrated the Clone Wars", "Yoda": "Grand Master of the Jedi Order, 900 years old, immense Force wisdom, " "trained generations of Jedi, exiled on Dagobah", "Obi-Wan Kenobi": "Jedi Master, trained Anakin Skywalker, guardian of Luke, " "wise and disciplined, killed by Darth Vader on the Death Star", "Boba Fett": "Bounty hunter, clone of Jango Fett, no Force sensitivity, " "works for the highest bidder, Mandalorian armor", "Ahsoka Tano": "Former Jedi Padawan of Anakin Skywalker, left the Jedi Order, " "Force sensitive, independent warrior, fought in the Clone Wars", "Din Djarin": "Mandalorian bounty hunter, no Force sensitivity, foundling, " "protector of Grogu, follows the Mandalorian creed", "Kylo Ren": "Dark side Force user, son of Han Solo and Leia Organa, " "formerly Ben Solo, trained by Luke then Snoke, conflicted", "Rey": "Jedi, granddaughter of Palpatine, strong in the Force, scavenger from Jakku, " "trained by Luke and Leia, chose the Skywalker name", "Mace Windu": "Jedi Master, member of the Jedi Council, invented Vaapad lightsaber form, " "strong in the Force, confronted Palpatine", "Count Dooku": "Sith apprentice, formerly a Jedi Master, trained by Yoda, " "leader of the Separatists, elegant duelist", "Chewbacca": "Wookiee warrior, co-pilot of the Millennium Falcon, loyal companion " "of Han Solo, fought in the Clone Wars and Galactic Civil War", }

Las escribí deliberadamente para apoyarme en atributos relacionales y de facción. "Trained by Yoda" crea un vínculo entre dos personajes. "No Force sensitivity" es una característica negativa importante que aparece en el embedding.

Generando embeddings

Un modelo de sentence embedding convierte cada descripción en un vector denso. Los modelos Sentence-BERT (o sucesores como E5 o BGE) son adecuados para esto; la similitud de coseno (cosine similarity) en su espacio de salida rastrea bien la similitud semántica.

from sentence_transformers import SentenceTransformer import numpy as np model = SentenceTransformer('all-MiniLM-L6-v2') names = list(characters.keys()) descriptions = list(characters.values()) embeddings = model.encode(descriptions) # Check similarity between two characters from sklearn.metrics.pairwise import cosine_similarity sim_matrix = cosine_similarity(embeddings)

La matriz de similitud ya cuenta una historia. Los vecinos más cercanos de Luke serán Obi-Wan, Yoda y Leia. Darth Vader se sitúa junto a Palpatine y Kylo Ren. Han y Chewbacca se agrupan, cerca de Boba Fett y Din Djarin.

Construyendo un grafo de similitud

Una matriz de similitud pura es difícil de leer. Un grafo hace que las relaciones sean visuales y permite explorarlas realmente.

import networkx as nx G = nx.Graph() threshold = 0.45 # Only draw edges for meaningfully similar characters for i, name_i in enumerate(names): G.add_node(name_i) for j, name_j in enumerate(names): if i < j and sim_matrix[i][j] > threshold: G.add_edge(name_i, name_j, weight=float(sim_matrix[i][j]))

El umbral (threshold) controla la densidad. Si es muy bajo, todo se conecta con todo. Si es muy alto, el grafo se desmorona en islas. Normalmente empiezo con la similitud mediana y ajusto hasta que la imagen sea legible.

Visualizando en 2D y 3D

Para la vista espacial, reducimos las dimensiones del embedding con UMAP:

import umap import matplotlib.pyplot as plt reducer = umap.UMAP(n_components=2, n_neighbors=5, min_dist=0.3, metric='cosine', random_state=42) coords_2d = reducer.fit_transform(embeddings) # Color by faction faction_colors = { 'Jedi': '#4488ff', 'Sith': '#ff4444', 'Neutral': '#88cc88', 'Mandalorian': '#ffaa00' } factions = { 'Luke Skywalker': 'Jedi', 'Darth Vader': 'Sith', 'Han Solo': 'Neutral', 'Leia Organa': 'Jedi', 'Emperor Palpatine': 'Sith', 'Yoda': 'Jedi', 'Obi-Wan Kenobi': 'Jedi', 'Boba Fett': 'Mandalorian', 'Ahsoka Tano': 'Jedi', 'Din Djarin': 'Mandalorian', 'Kylo Ren': 'Sith', 'Rey': 'Jedi', 'Mace Windu': 'Jedi', 'Count Dooku': 'Sith', 'Chewbacca': 'Neutral', } fig, ax = plt.subplots(figsize=(12, 9)) for name, coord in zip(names, coords_2d): color = faction_colors[factions[name]] ax.scatter(coord[0], coord[1], c=color, s=120, edgecolors='white', zorder=2) ax.annotate(name, (coord[0], coord[1]), fontsize=8, ha='center', va='bottom') ax.set_title('Star Wars Characters in Embedding Space') plt.tight_layout() plt.savefig('star_wars_embeddings.png', dpi=150)

Lo que revelan los clusters

Agunos patrones aparecen de forma consistente en la proyección 2D.

El eje de la Fuerza. Los personajes sensibles a la Fuerza (Jedi y Sith) se ubican en una región. Los que no usan la Fuerza (Han, Chewie, Boba Fett, Din Djarin) se ubican en otra. Es el eje de separación más fuerte, lo cual tiene sentido: la sensibilidad a la Fuerza es el rasgo definitorio de la franquicia y es el atributo más mencionado en las descripciones.

Luz vs. oscuridad entre los usuarios de la Fuerza. Los Jedi y los Sith se dividen claramente. Yoda, Obi-Wan y Mace Windu se agrupan estrechamente. Palpatine y Dooku forman su propio grupo. Los personajes interesantes son los que están en medio. Ahsoka, que dejó la Orden Jedi, se sitúa ligeramente fuera del cluster principal de los Jedi. Kylo Ren, descrito como "conflicted", se desplaza hacia el límite.

La zona gris. Han, Chewbacca, Boba Fett y Din Djarin forman un cluster de usuarios pragmáticos que no usan la Fuerza. Dentro de ese cluster, los cazarrecompensas (Boba y Din) están más cerca entre sí que de los personajes alineados con la Rebelión (Han y Chewie). La facción importa incluso entre los "neutrales".

Puentes entre eras. Darth Vader a menudo aterriza entre los clusters de Jedi y Sith porque su descripción conlleva ambas identidades. Él es el puente narrativo de la saga, y el embedding lo detecta. Count Dooku, descrito como "formerly a Jedi Master", se inclina ligeramente hacia el lado luminoso en comparación con un Sith puro como Palpatine.

Qué demuestra esto realmente

Es un ejemplo sencillo, pero el pipeline es real. Generar embeddings de descripciones de texto, calcular similitudes, visualizar con UMAP, construir un grafo... esa receta exacta se aplica a:

  • Catálogos de productos: generar embeddings de descripciones de productos para encontrar artículos similares y huecos en el inventario.
  • Artículos de investigación: mapear la literatura de un campo para encontrar clusters de trabajos relacionados y conexiones poco exploradas.
  • Ofertas de empleo: generar embeddings de descripciones de puestos para ver cómo la contratación de una empresa refleja su estrategia.
  • Tickets de soporte al cliente: generar embeddings de tickets para identificar categorías de problemas recurrentes sin necesidad de etiquetado manual.

El conjunto de Star Wars es lo suficientemente pequeño como para inspeccionarlo a mano y lo suficientemente rico como para mostrar una estructura real. Cada cluster, cada outlier y cada personaje puente coincide con una verdad narrativa de la franquicia. Eso es en lo que los embeddings son buenos: recuperan una estructura que ya sabías que estaba ahí y, a veces, una estructura que no conocías.

Que la Fuerza acompañe a tus datos.