Bag-of-Words y TF-IDF representan el texto mediante frecuencias de palabras en vectores dispersos. En cambio, los embeddings utilizan vectores densos en un espacio compartido, lo que permite capturar similitud semántica basada en el contexto, donde palabras cercanas en el espacio vectorial tienden a tener significados similares, y no solo por si aparecen o no en el mismo documento.
Se utiliza el modelo preentrenado glove-wiki-gigaword-50, disponible en la librería gensim, este presenta embeddings tipo GloVe entrenados con los corpus Wikipedia 2014 y Gigaword 5, en este modelo cada palabra se representa mediante un vector numérico de 50 dimensiones, es decir, una lista de 50 valores que codifican información semántica.
import sys
import numpy as np
import pandas as pd
import gensim
import gensim.downloader as api
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import TfidfVectorizer
np.random.seed(42)
model_name = "glove-wiki-gigaword-50"
model = api.load(model_name)
print("Python version:", sys.version)## Python version: 3.10.20 | packaged by Anaconda, Inc. | (main, Mar 11 2026, 17:42:35) [MSC v.1942 64 bit (AMD64)]
## gensim version: 4.4.0
## numpy version: 2.2.6
## pandas version: 2.3.3
## Model: glove-wiki-gigaword-50
## Vector dimensionality: 50
El modelo cuenta con un vocabulario de aproximadamente 400000 palabras, cualquier palabra que no se encuentre dentro de este vocabulario será considerada como fuera de vocabulario (OOV), esto puede afectar el análisis si el corpus incluye términos muy específicos o poco frecuentes.
## Vocabulary size: 400000
words = [
"music", "song", "guitar", "data", "statistics",
"model", "truck", "road", "market", "price",
"goodbye", "trust", "heartbreak", "algorithm", "python"
]
vocab_check = pd.DataFrame({
"word": words,
"in_vocabulary": [word in model for word in words]
})
vocab_check## word in_vocabulary
## 0 music True
## 1 song True
## 2 guitar True
## 3 data True
## 4 statistics True
## 5 model True
## 6 truck True
## 7 road True
## 8 market True
## 9 price True
## 10 goodbye True
## 11 trust True
## 12 heartbreak True
## 13 algorithm True
## 14 python True
missing_words = vocab_check[vocab_check["in_vocabulary"] == False]["word"].tolist()
print("Missing words:", missing_words)## Missing words: []
En este caso, todas las palabras seleccionadas se encuentran dentro
del vocabulario del modelo (in_vocabulary = True), esto
significa que el modelo presenta representaciones vectoriales para cada
término, lo cual permite realizar el análisis de similitud semántica sin
pérdida de información.
Por otro lado, en caso de que se hubiesen presentado OOV, esto indicaría que algunos términos no forman parte del conjunto de palabras con el que fue entrenado el modelo. Esto puede deberse a que son términos muy específicos de un dominio, poco frecuentes, recientes o con una forma distinta a la utilizada en el corpus original.
Los vecinos más cercanos muestran que el modelo agrupa palabras según su contexto semántico. A continuación, se eligen las palabras: ‘music’, ‘data’ y y ‘truck’ y se determinan las 5 palabras más similares según similitud del coseno.
target_words = ["music", "data", "truck"]
neighbors_results = []
for word in target_words:
similar_words = model.most_similar(word, topn=5)
for neighbor, similarity in similar_words:
neighbors_results.append({
"target_word": word,
"neighbor": neighbor,
"cosine_similarity": round(similarity, 4)
})
neighbors_table = pd.DataFrame(neighbors_results)
neighbors_table## target_word neighbor cosine_similarity
## 0 music musical 0.8854
## 1 music pop 0.8682
## 2 music dance 0.8531
## 3 music songs 0.8526
## 4 music recording 0.8392
## 5 data information 0.8330
## 6 data tracking 0.8125
## 7 data database 0.8122
## 8 data analysis 0.7967
## 9 data applications 0.7924
## 10 truck car 0.9209
## 11 truck vehicle 0.8651
## 12 truck trucks 0.8635
## 13 truck tractor 0.8452
## 14 truck parked 0.8431
Como se observa en la tabla, las palabras cercanas a ‘music’ suelen estar relacionadas con canciones, artistas o interpretación. Las palabras cercanas a ‘data’ suelen asociarse con información, sistemas o análisis. Las palabras cercanas a ‘truck’ están relacionadas con transporte, vehículos y carreteras.
El razonamiento por analogías evalúa si el modelo de embeddings captura relaciones semánticas entre palabras. A continuación se hace uso de dos analogías, la primera analogía explora una relación de género, mientras que la segunda explora una categoría religiosa.
analogies = [
{
"analogy": "king - man + woman",
"positive": ["king", "woman"],
"negative": ["man"]
},
{
"analogy": "religion - catholic + jewish",
"positive": ["religion", "jewish"],
"negative": ["catholic"]
}
]
analogy_results = []
for item in analogies:
result = model.most_similar(
positive=item["positive"],
negative=item["negative"],
topn=5
)
for predicted_word, similarity in result:
analogy_results.append({
"analogy": item["analogy"],
"predicted_word": predicted_word,
"cosine_similarity": round(similarity, 4)
})
analogy_table = pd.DataFrame(analogy_results)
analogy_table## analogy predicted_word cosine_similarity
## 0 king - man + woman queen 0.8524
## 1 king - man + woman throne 0.7664
## 2 king - man + woman prince 0.7592
## 3 king - man + woman daughter 0.7474
## 4 king - man + woman elizabeth 0.7460
## 5 religion - catholic + jewish judaism 0.7307
## 6 religion - catholic + jewish culture 0.7001
## 7 religion - catholic + jewish jews 0.6929
## 8 religion - catholic + jewish religious 0.6782
## 9 religion - catholic + jewish arabs 0.6661
Los resultados de las analogías muestran que el modelo es capaz de capturar relaciones semánticas mediante operaciones vectoriales. En el caso de la analogía king - man + woman, el modelo predice correctamente la palabra ‘queen’ como el resultado más cercano, lo que indica que ha aprendido una relación de género entre los términos. Adicionalmente, aparecen palabras relacionadas con la realeza como ‘throne’, ‘prince’ y ‘daughter’, lo que refuerza la coherencia semántica del resultado.
De manera similar, en la analogía religion - catholic + jewish, al restar los componentes específicos del catolicismo y sumar la carga semántica de lo judío, el modelo identifica correctamente el sistema de creencias resultante (“judaism”)
analogies = [
{
"analogy": "king - woman + man",
"positive": ["king", "man"],
"negative": ["woman"]
},
{
"analogy": "religion - jewish + catholic",
"positive": ["religion", "catholic"],
"negative": ["jewish"]
}
]
analogy_results = []
for item in analogies:
result = model.most_similar(
positive=item["positive"],
negative=item["negative"],
topn=5
)
for predicted_word, similarity in result:
analogy_results.append({
"analogy": item["analogy"],
"predicted_word": predicted_word,
"cosine_similarity": round(similarity, 4)
})
analogy_table = pd.DataFrame(analogy_results)
analogy_table## analogy predicted_word cosine_similarity
## 0 king - woman + man ruler 0.7396
## 1 king - woman + man prince 0.7367
## 2 king - woman + man ii 0.7365
## 3 king - woman + man lord 0.7310
## 4 king - woman + man iii 0.7169
## 5 religion - jewish + catholic theology 0.7264
## 6 religion - jewish + catholic beliefs 0.7218
## 7 religion - jewish + catholic teachings 0.7172
## 8 religion - jewish + catholic catholicism 0.7160
## 9 religion - jewish + catholic faith 0.7140
Al invertir las operaciones, la analogía king - woman + man refuerza la dimensión de poder masculino al predecir títulos como ‘ruler’ y ‘prince’, junto con sufijos de linaje histórico (‘ii’, ‘iii’). En el segundo ejemplo, los resultados de religion - jewish + catholic revelan una transición semántica hacia lo institucional y dogmático, prediciendo términos como ‘theology’, ‘beliefs’ y ‘teachings’, a diferencia de la operación inversa, que asociaba lo judío a la identidad cultural, el modelo vincula el catolicismo con la estructura formal de la fe.
En conjunto, el razonamiento de analogías permite observar la capacidad del modelo para desplazar conceptos entre dimensiones de género e institucionalidad con una similitud de coseno superior a 0.71. lo cual indica que los embeddings han codificado con éxito relaciones complejas que van más allá de la simple coincidencia de palabras.
matrix_words = ["music", "song", "guitar", "data", "truck", "road"]
vectors = np.array([model[word] for word in matrix_words])
similarity_matrix = cosine_similarity(vectors)
similarity_df = pd.DataFrame(
similarity_matrix,
index=matrix_words,
columns=matrix_words
)
similarity_df.round(4)## music song guitar data truck road
## music 1.0000 0.7985 0.7736 0.3565 0.1565 0.2536
## song 0.7985 1.0000 0.7063 0.2311 0.1944 0.3224
## guitar 0.7736 0.7063 1.0000 0.1895 0.2238 0.2147
## data 0.3565 0.2311 0.1895 1.0000 0.3049 0.2500
## truck 0.1565 0.1944 0.2238 0.3049 1.0000 0.5338
## road 0.2536 0.3224 0.2147 0.2500 0.5338 1.0000
Palabras como ‘music’, ‘song’ y ‘guitar’ presentan una mayor
similitud porque pertenecen al mismo campo semántico.
En contraste, palabras como ‘data’ y ‘guitar’ muestran menor similitud
debido a que aparecen en contextos distintos.
La matriz confirma que la similitud en embeddings se basa en el
significado y en el uso contextual de las palabras.
Los resultados de similitud basados en embeddings muestran cómo las oraciones pueden compararse a partir de su significado global, y no únicamente por las palabras exactas que contienen. A continuación, se usan los primeros tres documentos trabajados en la actividad de Text to Vector-base similarity:
sentences = [
"Where's the good in goodbye?, Where'sthe nice in nice try?",
"where's the us in trust gone?, Where's the soul in soldier on?",
"Now I'm the low in lonely,'Cause I don't own you only"
]
sentences_df = pd.DataFrame({
"sentence_id": ["S1", "S2", "S3"],
"sentence": sentences
})
sentences_df## sentence_id sentence
## 0 S1 Where's the good in goodbye?, Where'sthe nice ...
## 1 S2 where's the us in trust gone?, Where's the sou...
## 2 S3 Now I'm the low in lonely,'Cause I don't own y...
def sentence_embedding(sentence, model):
tokens = sentence.lower().split()
valid_tokens = [token for token in tokens if token in model]
if len(valid_tokens) == 0:
return np.zeros(model.vector_size)
return np.mean([model[token] for token in valid_tokens], axis=0)
sentence_vectors = np.array([
sentence_embedding(sentence, model) for sentence in sentences
])
text_similarity_matrix = cosine_similarity(sentence_vectors)
text_similarity_df = pd.DataFrame(
text_similarity_matrix,
index=["S1", "S2", "S3"],
columns=["S1", "S2", "S3"]
)
text_similarity_df.round(4)## S1 S2 S3
## S1 1.0000 0.8736 0.9397
## S2 0.8736 1.0000 0.9110
## S3 0.9397 0.9110 1.0000
El par con mayor similitud (S1-S3) corresponde a las oraciones que comparten un contexto emocional similar, relacionado con temas de ruptura, soledad y sentimientos negativos, lo que indica que el modelo logra capturar este campo semántico común.
pairs = []
for i in range(len(sentences)):
for j in range(i + 1, len(sentences)):
pairs.append({
"pair": f"S{i+1} - S{j+1}",
"embedding_similarity": round(text_similarity_matrix[i, j], 4)
})
pairs_df = pd.DataFrame(pairs)
most_similar = pairs_df.loc[pairs_df["embedding_similarity"].idxmax()]
least_similar = pairs_df.loc[pairs_df["embedding_similarity"].idxmin()]
pairs_df## pair embedding_similarity
## 0 S1 - S2 0.8736
## 1 S1 - S3 0.9397
## 2 S2 - S3 0.9110
Por otro lado, el par con menor similitud corresponde a aquellas oraciones que, aunque pertenecen al mismo corpus, presentan menor cercanía en términos de las palabras que las componen o de su construcción semántica. Esto refleja que, incluso dentro de un mismo tema general, existen variaciones en el uso del lenguaje que afectan la representación vectorial.
## Most similar pair:
## pair S1 - S3
## embedding_similarity 0.9397
## Name: 1, dtype: object
##
## Least similar pair:
## pair S1 - S2
## embedding_similarity 0.8736
## Name: 0, dtype: object
También se aplica TF-IDF:
tfidf = TfidfVectorizer()
tfidf_matrix = tfidf.fit_transform(sentences)
tfidf_similarity = cosine_similarity(tfidf_matrix)
tfidf_similarity_df = pd.DataFrame(
tfidf_similarity,
index=["S1", "S2", "S3"],
columns=["S1", "S2", "S3"]
)
tfidf_similarity_df.round(4)## S1 S2 S3
## S1 1.0000 0.3808 0.1022
## S2 0.3808 1.0000 0.1420
## S3 0.1022 0.1420 1.0000
En comparación con TF-IDF, se observa que este último depende principalmente del solapamiento exacto de palabras entre oraciones. Como resultado, las similitudes calculadas con TF-IDF resultan bajas incluso cuando las oraciones comparten un significado similar. En cambio, los embeddings permiten identificar relaciones semánticas más profundas, ya que consideran el contexto en el que las palabras suelen aparecer, en este caso el par más similar resulta ser S1-S2.
En conjunto, estos resultados evidencian que los embeddings ofrecen una representación más flexible y semánticamente rica del texto, permitiendo comparar oraciones incluso cuando no comparten exactamente el mismo vocabulario.
Los word embeddings permiten capturar relaciones semánticas que TF-IDF no puede representar directamente. Mientras TF-IDF se enfoca en la frecuencia y relevancia de las palabras dentro de un documento, los embeddings representan las palabras según su uso en contexto, lo que permite identificar similitudes incluso cuando los textos utilizan vocabulario diferente.
Esto hace que los embeddings sean especialmente útiles cuando se desea comparar el significado de los textos más allá de la coincidencia exacta de términos. Sin embargo, una limitación importante de modelos como Word2Vec y GloVe es que asignan una única representación vectorial a cada palabra, independientemente del contexto en el que aparezca. Esto implica que el modelo no puede diferenciar completamente entre distintos significados de una misma palabra, como en el caso de ‘bank’, que puede referirse a una entidad financiera o a la orilla de un río.
En este sentido, aunque los embeddings ofrecen una representación más rica y flexible del lenguaje, también presentan limitaciones en términos de sensibilidad al contexto. A pesar de ello, resultan especialmente útiles en tareas como análisis de similitud semántica, sistemas de recomendación, clustering y clasificación de texto.
Todos los resultados se generan automáticamente cuando se compila el documento
import sys
import pandas as pd
import numpy as np
import gensim
import sklearn
pd.DataFrame({
"Library": ["Python", "pandas", "numpy", "gensim", "scikit-learn"],
"Version": [
sys.version.split()[0],
pd.__version__,
np.__version__,
gensim.__version__,
sklearn.__version__
]
})## Library Version
## 0 Python 3.10.20
## 1 pandas 2.3.3
## 2 numpy 2.2.6
## 3 gensim 4.4.0
## 4 scikit-learn 1.7.2