De las operaciones del álgebra lineal, el producto por un escalar es la más simple de definir y la que menos atención suele recibir. Sin embargo, dos de las decisiones más consecuentes en aprendizaje automático —cómo se compara la similitud entre representaciones y a qué velocidad aprende un modelo— se reducen a elegir un número por el que multiplicar.
Definición
Dado y , el producto se define entrada a entrada:
import numpy as np
A = np.array([[1., 2.],
[3., 4.]])
2 * A
# array([[2., 4.],
# [6., 8.]])
La expresión 2 * A no recorre la matriz en Python. El escalar se trata como un array de forma vacía y el mecanismo de broadcasting lo extiende sobre las entradas, que se procesan en un bucle compilado.
Propiedades
El producto por un escalar satisface cuatro identidades que, junto con las de la suma, constituyen los axiomas de espacio vectorial mencionados al tratar :
B = np.ones((2, 2))
lam, mu = 3.0, -0.5
np.allclose(lam * (A + B), lam * A + lam * B) # True
np.allclose(lam * (mu * A), (lam * mu) * A) # True
El escalar además conmuta con la transposición y se desplaza libremente entre los factores de un producto matricial:
C = np.random.randn(2, 3)
np.allclose((lam * C).T, lam * C.T) # True
Esa libertad de movimiento tiene una consecuencia práctica: en una expresión larga, el escalar puede aplicarse donde resulte más barato. Multiplicar una matriz por cuesta un millón de operaciones; aplicar al vector resultante, mil.
La norma
El efecto geométrico del escalar se enuncia en términos de la longitud del vector. La norma euclídea de es
Es la notación que apareció en la lección anterior al acotar la propagación del error; aquí queda definida. Con ella, el efecto del escalar se expresa exactamente:
v = np.array([3., 4.])
np.linalg.norm(v) # 5.0
np.linalg.norm(2 * v) # 10.0
El valor absoluto es necesario: con el vector invierte su sentido, pero su longitud es una magnitud no negativa. En términos geométricos, dilata, contrae y refleja respecto del origen. La dirección, entendida como la recta que el vector genera, no cambia en ningún caso.
La norma euclídea no es la única en uso. np.linalg.norm admite el parámetro ord:
| Norma | Definición | Dónde aparece |
|---|---|---|
| regularización L1, que induce dispersión | ||
| regularización L2, mínimos cuadrados | ||
| cotas, recorte de gradientes |
Las tres cumplen , propiedad conocida como homogeneidad absoluta y exigida por la definición axiomática de norma.
Normalización
Para , elegir produce un vector de norma unitaria en la misma dirección:
u = v / np.linalg.norm(v)
np.linalg.norm(u) # 1.0
La operación separa la información de un vector en dos partes independientes: la dirección, que retiene , y la magnitud, que queda en el escalar . Esa separación es la razón de su uso en aprendizaje automático.
Aplicación: similitud coseno
Al comparar dos embeddings, lo relevante suele ser la dirección y no la magnitud. Dos textos sobre el mismo tema deberían considerarse próximos aunque uno sea mucho más largo que el otro, y la longitud del vector tiende a reflejar esa extensión antes que el contenido.
La similitud coseno descarta la magnitud por construcción:
Los extremos son arrastrables. Las flechas interiores son el par normalizado, sobre la circunferencia unidad.
‖λu‖ = 3.16 · ‖v‖ = 2.69
cos θ = 0.6459 · θ = 49.8°
λ cambia ‖λu‖ pero nunca cos θ: es exactamente la propiedad que se busca al comparar embeddings.
El deslizador reescala . Su norma cambia, su flecha se alarga o se acorta, y el valor de permanece idéntico. La verificación algebraica es inmediata:
Para los factores se cancelan. Esa invariancia frente al escalado es la propiedad que hace de la similitud coseno la medida habitual en recuperación semántica.
def cosine(u, v):
return (u @ v) / (np.linalg.norm(u) * np.linalg.norm(v))
cosine(v, 100 * v) # 1.0: misma dirección, magnitudes incomparables
Cuando los vectores se normalizan de antemano, la similitud se reduce a un producto escalar, y la comparación de un vector contra un corpus completo pasa a ser un único producto matricial:
E = E / np.linalg.norm(E, axis=1, keepdims=True) # filas unitarias
sims = E @ q # similitud contra la consulta q
Esa es la operación que ejecuta una base de datos vectorial en cada consulta.
Aplicación: la tasa de aprendizaje
El descenso por gradiente es, en su forma básica, producto por un escalar:
El gradiente indica la dirección de máximo crecimiento; el escalar , la tasa de aprendizaje, decide cuánto se avanza en sentido contrario. Es un único número, y de él depende que el entrenamiento converja.
Las elipses son las curvas de nivel de f. Lo único que cambia es α.
Converge
estable mientras α < 0.50
‖w‖ tras 24 pasos = 0.011
Con , cada paso contrae la coordenada por un factor , donde es la curvatura en ese eje. El método converge si y solo si para toda coordenada, es decir
En la figura ese umbral vale . Tres regímenes son observables: con pequeño el avance es correcto pero lento; cerca del umbral aparece la oscilación característica en zigzag a lo largo del eje de mayor curvatura; por encima, la iteración diverge.
grad = np.array([0.5, -1.2])
w = np.array([2.0, 1.0])
lr = 0.1
w = w - lr * grad # un paso
La dificultad práctica es que se elige una vez y la curvatura varía según la dirección. Esa disparidad —el cociente entre la curvatura mayor y la menor, que es el número de condición de la Hessiana— es la que fuerza a tomar un pequeño y hace lenta la convergencia. Los métodos adaptativos como Adam sustituyen el escalar único por un factor por coordenada, y toda su ventaja procede de ahí.
Ejercicio. Verificar que cosine(v, -v) vale y explicar por qué la invariancia del coseno frente al escalado exige . Determinar después, para , el valor de que anula en un solo paso la coordenada de mayor curvatura, y observar qué le ocurre a la otra.