#02 Clustering semántico de keywords con NLP: cómo lo aplico en proyectos reales
Clustering semántico de keywords con NLP: cómo lo aplico en proyectos reales
Uno de los trabajos más tediosos del SEO clásico es el de agrupar keywords. Si alguna vez has tenido que organizar un listado de 2.000 queries en categorías temáticas a mano, sabes de lo que hablo. Acabas con los ojos cuadrados, criterios inconsistentes entre el inicio y el final de la hoja, y la sensación de haber perdido medio día en algo que debería ser más inteligente.
Hace un par de años empecé a resolver esto con clustering semántico usando NLP. No es magia, pero sí cambia bastante cómo trabajo la investigación de keywords. Te cuento el proceso que uso en proyectos reales.
Por qué el clustering por n-grams se queda corto
El enfoque más básico para agrupar keywords es buscar palabras compartidas: si dos queries tienen el mismo término principal, van al mismo grupo. Rápido, fácil, y bastante malo.
El problema es que dos keywords pueden compartir intención sin compartir ni una sola palabra:
- “cómo bajar de peso rápido”
- “adelgazar en poco tiempo”
Son la misma intención, pero un agrupador por n-grams las metería en grupos distintos. El NLP, en cambio, trabaja con significado, no con texto literal. Y eso lo cambia todo.
Las herramientas que uso
Para este proceso trabajo habitualmente con estas librerías:
sentence-transformers: para convertir cada keyword en un vector semántico (embedding)scikit-learn: para el algoritmo de clusteringpandas: para manipular los datos antes y despuésumap-learn(opcional): para visualizar los clusters en 2D
Si no tienes el entorno preparado, puedes instalarlas así:
pip install sentence-transformers scikit-learn pandas umap-learn
El proceso paso a paso
1. Cargar las keywords
Parto siempre de un CSV con las keywords que quiero agrupar. Puede venir de GSC, de Semrush, de Ahrefs, o de una combinación de varias fuentes. Lo importante es tener una columna con el texto de la query.
import pandas as pd
df = pd.read_csv("keywords.csv")
keywords = df["query"].tolist()
print(f"Total keywords: {len(keywords)}")
2. Generar los embeddings semánticos
Aquí es donde entra el NLP. Uso el modelo paraphrase-multilingual-MiniLM-L12-v2 porque funciona bien con español y es ligero para correr en local sin GPU.
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")
embeddings = model.encode(keywords, show_progress_bar=True)
print(f"Shape de embeddings: {embeddings.shape}")
Cada keyword queda representada como un vector de 384 dimensiones. A partir de ahí, el algoritmo puede calcular qué keywords son semánticamente cercanas entre sí.
3. Clustering con K-Means
Uso K-Means como algoritmo principal porque es rápido y los resultados son fáciles de interpretar. El único parámetro que hay que decidir antes es el número de clusters.
Para eso, si no tengo una referencia clara del proyecto, uso el método del codo:
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
inertias = []
rango = range(5, 50, 5)
for k in rango:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(embeddings)
inertias.append(km.inertia_)
plt.plot(rango, inertias, marker="o")
plt.xlabel("Número de clusters")
plt.ylabel("Inercia")
plt.title("Método del codo")
plt.show()
Una vez elegido el número de clusters (suelo quedarme con el punto donde la curva empieza a aplanarse), lanzo el clustering definitivo:
n_clusters = 20 # ajustar según el método del codo
km = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
df["cluster"] = km.fit_predict(embeddings)
4. Etiquetar los clusters automáticamente
Esto es algo que me ahorra mucho tiempo. En lugar de revisar cada cluster manualmente para ponerle nombre, extraigo las keywords más representativas de cada grupo (las más cercanas al centroide) y las uso como etiqueta provisional.
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
etiquetas = {}
for cluster_id in range(n_clusters):
indices = df[df["cluster"] == cluster_id].index.tolist()
vecs = embeddings[indices]
centroide = km.cluster_centers_[cluster_id]
similitudes = cosine_similarity([centroide], vecs)[0]
top_idx = np.argsort(similitudes)[::-1][:3]
top_keywords = [keywords[indices[i]] for i in top_idx]
etiquetas[cluster_id] = " / ".join(top_keywords)
df["cluster_label"] = df["cluster"].map(etiquetas)
El resultado es un dataframe donde cada keyword tiene su cluster asignado y una etiqueta legible. No siempre son perfectas, pero sí dan un punto de partida muy útil para la revisión manual posterior.
5. Exportar y revisar
df.sort_values("cluster").to_csv("keywords_clusterizadas.csv", index=False)
La revisión manual sigue siendo necesaria, especialmente en los clusters fronterizos donde el modelo duda. Pero en lugar de revisar 2.000 keywords una a una, revisas 20 grupos. El tiempo baja de horas a minutos.
Cómo aplico esto en proyectos reales
El clustering semántico me resulta especialmente útil en tres momentos concretos:
- Al arrancar un proyecto nuevo: para estructurar el universo de keywords y diseñar la arquitectura de información antes de tocar nada en la web.
- En auditorías de contenido: para detectar si hay grupos temáticos sin cobertura o con demasiadas URLs compitiendo entre sí (canibalización potencial).
- Al planificar el calendario editorial: los clusters se convierten directamente en pilares de contenido, con sus subtemas ya organizados dentro de cada grupo.
Lo que no hace este método
Seré directo: el clustering semántico no detecta intención de búsqueda de forma automática. Puede agrupar “comprar zapatillas running” y “zapatillas running precio” en el mismo cluster (correcto), pero no te dice si esa intención es transaccional o informacional. Eso sigue requiriendo criterio humano.
Tampoco es infalible con queries muy cortas o ambiguas. Cuanto más contexto tiene la keyword, mejor funciona el modelo.
Si quieres profundizar en cómo conecto este proceso con la priorización de URLs o con la detección de canibalización, eso lo veremos en los siguientes posts. El clustering es el primer paso; lo que haces con esos grupos después es donde está el valor real.