Curso de Matemáticas para IA · Lección 44 de 180

Ángulos y similitud del coseno

Calcula ángulos y similitud del coseno a partir de coordenadas vectoriales, normaliza embeddings de forma responsable y gestiona los vectores nulos en código de recuperación.

Comparte este artículo

Dos documentos pueden representarse mediante los vectores x=(1,2)x=(1,2) y y=(10,20)y=(10,20). Su producto escalar sin normalizar es 5050, así que una búsqueda basada en el producto escalar considera que el segundo vector es mucho mayor. Sin embargo, sus direcciones son idénticas: después de dividir por las longitudes de ambos vectores, su similitud del coseno es 11. La regla de puntuación cambia lo que significa “similar”.

Al terminar esta lección, podrás calcular un ángulo y la similitud del coseno a partir de coordenadas, explicar por qué un reescalado positivo no cambia el coseno y gestionar un vector nulo sin asignarle una dirección que no tiene.

Resultado de aprendizaje: Calcular un ángulo y la similitud del coseno, y gestionar los límites de los vectores nulos.

Esta lección combina dos ideas anteriores: la Lección 039: Las normas miden el tamaño de un vector nos proporciona longitudes euclídeas, y la Lección 041: Los productos internos miden la alineación nos proporciona productos escalares. La Lección 043: La distancia depende de la geometría elegida mostró después por qué una distancia conserva la magnitud en la comparación. La sección 3.4 del libro oficial Mathematics for Machine Learning desarrolla la relación angular. Los ejemplos, el código, el visual y las evaluaciones de aquí son originales.

Un ángulo separa la dirección de la longitud

Para dos vectores no nulos x,y∈Rnx,y\in\mathbb{R}^n, usamos θ\theta para representar el ángulo entre ellos. El producto escalar y la norma euclídea habituales son

La primera expresión combina las coordenadas correspondientes en un escalar. La segunda mide la longitud euclídea de cada vector. La tercera divide la puntuación de alineación por las dos longitudes y deja una comparación direccional. La desigualdad de Cauchy–Schwarz garantiza que el cociente final está entre −1-1 y 11 para vectores no nulos, por lo que el ángulo se recupera de forma unívoca en el intervalo [0,π][0,\pi]:

θ=arccos⁡ ⁣(xTy∥x∥2 ∥y∥2).\theta = \arccos\!\left(\frac{x^\mathsf{T}y}{\lVert x\rVert_2\,\lVert y\rVert_2}\right).

Este valor normalizado es la similitud del coseno. Un valor cercano a 11 significa que los vectores apuntan casi en la misma dirección; 00 significa un ángulo recto bajo la geometría habitual del producto escalar; y −1-1 significa direcciones opuestas. Estas interpretaciones suponen que ambos vectores son no nulos y que usamos el producto interno euclídeo estándar.

Un ángulo calculado a partir de coordenadas

Tomemos x=(3,1)x=(3,1) y y=(1,2)y=(1,2). Son vectores bidimensionales, así que cada uno tiene dos coordenadas y no se sobreentiende ninguna unidad física. Podemos calcular cada parte antes de aplicar un coseno inverso.

Los valores intermedios importan. Un producto escalar de 55 por sí solo no nos dice cuál es el ángulo, porque otro par de vectores podría tener el mismo producto escalar pero longitudes distintas. Dividir por ambas normas hace que el resultado sea comparable entre distintas magnitudes vectoriales.

El reescalado positivo no cambia el coseno

Supón que un vector no nulo se estira mediante un número positivo cc. Su dirección no cambia, y el álgebra muestra por qué la puntuación del coseno permanece fija:

cos⁡(cx,y)=(cx)Ty∥cx∥2∥y∥2=c xTyc∥x∥2∥y∥2=cos⁡(x,y),c>0.\cos(cx,y)=\frac{(cx)^\mathsf{T}y}{\lVert cx\rVert_2\lVert y\rVert_2} =\frac{c\,x^\mathsf{T}y}{c\lVert x\rVert_2\lVert y\rVert_2} =\cos(x,y),\qquad c>0.

La misma cancelación ocurre cuando yy se reescala positivamente. Esta es una característica útil para comparar embeddings por su dirección: un vector de documento con la misma orientación pero una norma mayor recibe la misma puntuación del coseno. También es una decisión de modelado. Si la magnitud contiene una señal sobre confianza, frecuencia u otra propiedad de la representación, eliminarla puede descartar información.

Explora el ángulo, la puntuación y las longitudes juntos

El explorador comienza con dos vectores no nulos. Mueve cualquier coordenada con el ratón, una entrada táctil o las teclas de flecha. El gráfico muestra los mismos vectores desde el origen, mientras que la tabla repite en texto el producto escalar, ambas normas, la similitud del coseno y el ángulo recuperado. Establece cualquiera de los vectores en (0,0)(0,0) para ver el límite del vector nulo.

La similitud del coseno cambia cuando cambia la dirección del vector
Elige las coordenadas de los dos vectores

Cada control de rango cambia una coordenada en 0.5. La tabla es la alternativa textual completa a la geometría representada.

Vector x

Vector y

x = (1, 0,5); y = (0,5, 1,5); producto escalar = 1,25; norma de x = 1,118; norma de y = 1,581; similitud del coseno = 0,707; ángulo = 0,785 radianes (45 grados). Interpretación: la puntuación refleja una alineación direccional parcial.

Dos vectores y su ángulo desde un origen compartidoEl gráfico muestra el vector x = (1, 0,5) y el vector y = (0,5, 1,5) desde el origen. x = (1, 0,5); y = (0,5, 1,5); producto escalar = 1,25; norma de x = 1,118; norma de y = 1,581; similitud del coseno = 0,707; ángulo = 0,785 radianes (45 grados). Interpretación: la puntuación refleja una alineación direccional parcial.-2-2-1-101122x = (1, 0,5)y = (0,5, 1,5)primera coordenadasegunda coordenada
Valores textuales de los vectores seleccionados
CantidadValor
Producto escalar x · y1,25
Norma de x1,118
Norma de y1,581
Similitud del coseno0,707
Ángulo0,785 radianes / 45 grados

Las líneas de los vectores usan patrones de guiones y etiquetas distintos, no solo color. La tabla numérica sigue siendo significativa cuando el gráfico no está disponible o se reduce el movimiento.

El ángulo representado es una ayuda para razonar, no una definición distinta. Cuando cambian los controles, la tabla numérica vuelve a calcular la misma fórmula mostrada arriba. Un par que se hace más largo sin cambiar de dirección conserva el valor del coseno, mientras que girar cualquiera de los vectores cambia tanto el ángulo como la puntuación.

Usa la puntuación con cuidado en la recuperación mediante embeddings

Un embedding es una representación vectorial de un elemento, como un documento, una consulta, una imagen o un usuario. Si la coincidencia buscada depende de la orientación, la similitud del coseno es una puntuación natural: compara vectores normalizados en lugar de sus longitudes sin procesar. La puntuación sigue dependiendo del modelo de embedding, el preprocesamiento, el corpus y la tarea; no convierte “similar” en una propiedad universal de los documentos subyacentes.

La guía actual de métricas por pares de scikit-learn describe la similitud del coseno como un producto escalar normalizado mediante L2 y señala su uso para vectores de documentos. Su API cosine_similarity trata las filas como muestras y devuelve una matriz de puntuaciones por pares. Es fácil perder esa forma al pasar de un vector a un lote, así que haz explícita la convención de las filas:

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

query = np.array([[3.0, 1.0]])       # shape (1 sample, 2 features)
documents = np.array([
    [3.0, 1.0],
    [1.0, 2.0],
    [30.0, 10.0],
])                                    # shape (3 samples, 2 features)

scores = cosine_similarity(query, documents)
print(scores.round(4))
# [[1.     0.7071 1.    ]]

Para una implementación transparente en NumPy, bastan el producto escalar unidimensional y las normas vectoriales. La documentación de dot de NumPy define el caso 1-D como el producto interno de dos vectores, mientras que numpy.linalg.norm acepta un argumento axis cuando un array contiene varios vectores. La comprobación forma parte de la implementación, no es una comprobación decorativa:

import numpy as np

def cosine_similarity(x, y):
    x = np.asarray(x, dtype=float)
    y = np.asarray(y, dtype=float)
    x_norm = np.linalg.norm(x)
    y_norm = np.linalg.norm(y)
    if x_norm == 0 or y_norm == 0:
        raise ValueError('cosine similarity needs two nonzero vectors')

    raw = np.dot(x, y) / (x_norm * y_norm)
    return np.clip(raw, -1.0, 1.0)

print(cosine_similarity(np.array([3.0, 1.0]), np.array([1.0, 2.0])))
# 0.7071067811865475

clip evita que un pequeño exceso de coma flotante envíe arccos fuera de su dominio. No debe usarse para ocultar un error numérico o de forma de los datos más grande. Para un lote con forma (number_of_samples, number_of_features), calcula la norma en axis=1 y conserva una puntuación por fila; la documentación actual de NumPy muestra esta convención de eje para las normas vectoriales.

Límites que cambian la interpretación

Un vector nulo no tiene ángulo

Si x=0x=0, entonces ∥x∥2=0\lVert x\rVert_2=0 y la fórmula del coseno divide por cero. El producto escalar 0Ty=00^\mathsf{T}y=0 sigue estando definido, pero eso no significa que el vector nulo forme un ángulo recto con yy. Un vector nulo no tiene dirección, así que matemáticamente el ángulo y la similitud del coseno no están definidos.

Las bibliotecas pueden elegir una política para esta entrada. El ejemplo actual de cosine_similarity incluye una fila nula y muestra una puntuación de 0.0; es una convención de la API que debes comprobar para la versión y la ruta de datos que uses, no una dirección recién definida. Un sistema de recuperación en producción debe decidir si rechaza, filtra o enruta por separado los embeddings nulos, y probar explícitamente esa política.

Los vectores opuestos no son “no relacionados”

Para un xx no nulo, el par (x,−x)(x,-x) cumple

cos⁡(x,−x)=−∥x∥22∥x∥22=−1,θ=π.\cos(x,-x)=\frac{-\lVert x\rVert_2^2}{\lVert x\rVert_2^2}=-1, \qquad \theta=\pi.

La puntuación es máximamente negativa porque los vectores apuntan en direcciones opuestas. Una puntuación cercana a 00 significa ortogonalidad bajo esta geometría; no significa automáticamente que dos elementos no estén relacionados semánticamente. La interpretación pertenece a la representación y a la tarea.

El coseno no es lo mismo que una distancia o un producto escalar sin normalizar

La similitud del coseno ignora el reescalado positivo. La distancia euclídea no lo hace. Para x=(1,1)x=(1,1) y y=(10,10)y=(10,10), la similitud del coseno es 11, pero

∥x−y∥2=(−9)2+(−9)2=92≈12.728.\lVert x-y\rVert_2=\sqrt{(-9)^2+(-9)^2}=9\sqrt{2}\approx12.728.

El par es idéntico en dirección y aun así está muy separado como puntos. En cambio, un producto escalar sin normalizar mezcla la dirección con ambas longitudes. Elige el coseno cuando la concordancia direccional sea la señal buscada; elige una distancia inducida por una norma o una puntuación sin normalizar cuando la magnitud deba seguir formando parte de la decisión. La API cosine_distances de scikit-learn define su valor como 11 menos la similitud del coseno, así que conserva la misma elección direccional en lugar de recuperar la separación euclídea.

La siguiente lección, Lección 045: La ortogonalidad significa que no hay solapamiento lineal, se centra en el caso especial en que el producto interno es cero y explica qué significa “no hay solapamiento lineal” (este enlace abre contenido en inglés). La página del curso Math for AI mantiene visible la secuencia de dependencias a medida que cada lección está disponible.

Comprueba tu comprensión

Question 1

Para x = (3, 1) e y = (1, 2), calcula el producto escalar, ambas normas L2, la similitud del coseno y el ángulo.

Show the step-by-step solution

El producto escalar es 3⋅1+1⋅2=53\cdot1+1\cdot2=5. Las normas son ∥x∥2=3⋅3+1⋅1=10\lVert x\rVert_2=\sqrt{3\cdot3+1\cdot1}=\sqrt{10} y ∥y∥2=1⋅1+2⋅2=5\lVert y\rVert_2=\sqrt{1\cdot1+2\cdot2}=\sqrt{5}. Por tanto, cos⁡(θ)=5/(105)=1/2≈0.7071\cos(\theta)=5/(\sqrt{10}\sqrt{5})=1/\sqrt{2}\approx0.7071. Aplicar el coseno inverso da θ=π/4\theta=\pi/4 radianes, o 45∘45^\circ. Cada paso usa los vectores de dos coordenadas y la suposición de normas no nulas.

Question 2

¿Por qué x = (1, 2) e y = (10, 20) tienen productos escalares y longitudes propias distintos, pero la misma similitud del coseno?

Show the step-by-step solution

Los vectores cumplen y=10xy=10x, así que xTy=1⋅10+2⋅20=50x^\mathsf{T}y=1\cdot10+2\cdot20=50. Sus normas son ∥x∥2=5\lVert x\rVert_2=\sqrt{5} y ∥y∥2=105\lVert y\rVert_2=10\sqrt{5}. La puntuación normalizada es 50/(5 105)=150/(\sqrt{5}\,10\sqrt{5})=1. El producto escalar crece con la escala de yy, pero esa misma escala aparece en su norma y se cancela en el coseno.

Question 3

Sean x = (1, 0) e y = (0, 2). ¿Cuáles son su similitud del coseno y su ángulo, y qué relación geométrica muestran?

Show the step-by-step solution

El producto escalar es 1⋅0+0⋅2=01\cdot0+0\cdot2=0. Las normas son 11 y 22, así que la similitud del coseno es 0/(1⋅2)=00/(1\cdot2)=0. Como arccos⁡(0)=π/2\arccos(0)=\pi/2, el ángulo es 90∘90^\circ. Por tanto, los vectores son ortogonales en la geometría estándar del producto escalar, aunque ninguno de los dos sea nulo.

Question 4

Para x = (1, 0) e y = (−4, 0), calcula la similitud del coseno y el ángulo. ¿Por qué la puntuación no es cero?

Show the step-by-step solution

El producto escalar es 1⋅(−4)+0⋅0=−41\cdot(-4)+0\cdot0=-4, y las normas son 11 y 44. Por tanto, cos⁡(θ)=−4/(1⋅4)=−1\cos(\theta)=-4/(1\cdot4)=-1. El coseno inverso da θ=π=180∘\theta=\pi=180^\circ. Los vectores están en la misma recta pero apuntan en direcciones opuestas, así que tienen una alineación negativa máxima, no son perpendiculares.

Question 5

¿Qué debería hacer una implementación de similitud del coseno con x = (0, 0) e y = (2, 3), y por qué devolver 0 no es un ángulo matemático?

Show the step-by-step solution

La norma de xx es 00, así que el denominador ∥x∥2∥y∥2\lVert x\rVert_2\lVert y\rVert_2 es cero. La fórmula no está definida y xx no tiene dirección. Una implementación puede rechazar el par, filtrar el vector nulo o adoptar una convención de API documentada, como devolver 00; esa convención debe probarse y no debe describirse como un ángulo recto.

Question 6

Si x = (1, 2), y = (2, 1) y x se sustituye por −3x, ¿cómo cambia la similitud del coseno? Muestra el cálculo del signo.

Show the step-by-step solution

Primero, xTy=1⋅2+2⋅1=4x^\mathsf{T}y=1\cdot2+2\cdot1=4 y ∥x∥2∥y∥2=55=5\lVert x\rVert_2\lVert y\rVert_2=\sqrt5\sqrt5=5, por lo que el coseno original es 4/5=0.84/5=0.8. Con c=−3c=-3, el numerador se multiplica por −3-3, mientras que la norma de cxcx es ∣c∣∥x∥2=3∥x∥2|c|\lVert x\rVert_2=3\lVert x\rVert_2. Por tanto, la nueva puntuación es (−3)/3⋅0.8=−0.8(-3)/3\cdot0.8=-0.8: el reescalado negativo invierte la dirección.

Question 7

¿Por qué la implementación de NumPy recorta el producto escalar normalizado antes de llamar a arccos, y qué debería indicar un exceso grande?

Show the step-by-step solution

Matemáticamente, Cauchy–Schwarz sitúa el coseno en [−1,1][-1,1], pero la aritmética de coma flotante puede producir un valor como 1.00000000000000021.0000000000000002 después de un cálculo largo o casi paralelo. Como arccos⁡\arccos solo tiene valores reales en ese intervalo, np.clip(raw, -1.0, 1.0) elimina un pequeño exceso de redondeo. Un exceso grande sugiere un problema de datos, forma o condicionamiento numérico; el recorte no debe utilizarse para ocultarlo.

Question 8

Una consulta es q = (3, 1) y tres vectores de documentos son a = (3, 1), b = (1, 2) y c = (30, 10). Compara la clasificación por producto escalar sin normalizar con la clasificación por similitud del coseno.

Show the step-by-step solution

Los productos escalares sin normalizar son qTa=10q^\mathsf{T}a=10, qTb=5q^\mathsf{T} b=5 y qTc=100q^\mathsf{T}c=100, así que la clasificación por producto escalar es cc, después aa y después bb. Como c=10ac=10a, cos⁡(q,c)=cos⁡(q,a)=1\cos(q,c)=\cos(q,a)=1. Para bb, cos⁡(q,b)=5/(105)≈0.7071\cos(q,b)=5/(\sqrt{10}\sqrt{5})\approx0.7071. Por tanto, la clasificación por coseno empata a aa y cc en la primera posición y después coloca a bb. La diferencia expone la decisión de modelado: los productos escalares premian tanto la magnitud como la dirección, mientras que el coseno elimina la escala positiva.

Fuentes

  1. Mathematics for Machine Learning book PDF, Section 3.4
  2. NumPy documentation: numpy.dot
  3. NumPy documentation: numpy.linalg.norm
  4. NumPy documentation: numpy.arccos
  5. scikit-learn documentation: Pairwise metrics, affinities and kernels
  6. scikit-learn documentation: cosine_similarity
  7. scikit-learn documentation: cosine_distances