English · Español
Break — Trampa silenciosa de broadcasting con un vector columna¶
La trampa más sutil de NumPy: sumar un vector de shape
(3,)a una matriz(3, 4)no hace lo que crees. La forma compatible es(3, 1). Sin el[:, None], broadcasting elige el otro eje y los números salen mal — pero no hay error.
Objetivo: cualquier suma de logits batch + bias del §A13. Setup típico: logits tiene shape (3 personas, 4 features); un bias por persona tiene shape (3,). Quieres sumar el bias a cada fila.
Hipótesis¶
El aprendiz predice: "logits + bias donde logits.shape = (3, 4) y bias.shape = (3,) no sumará el bias por filas. NumPy alineará (3,) al eje final (longitud 4), que es incompatible — así que broadcastea contra el eje columna (longitud 3) en su lugar, produciendo la suma incorrecta o, según los shapes, un crash."
El break¶
En una función que debería sumar un bias por fila:
def add_row_bias(logits: np.ndarray, bias: np.ndarray) -> np.ndarray:
- # logits: (P, F); bias: (P,) -> reshape a (P, 1) para que el broadcasting funcione
- return logits + bias[:, None]
+ return logits + bias # /break: depende de un alineamiento accidental de shapes
Procedimiento de ejecución¶
uv run python -c "
import numpy as np
# Caso A: P=3 personas, F=4 features. bias por persona.
logits_A = np.array([[1., 2., 3., 4.],
[5., 6., 7., 8.],
[9.,10.,11.,12.]])
bias_A = np.array([100., 200., 300.]) # uno por persona
# Caso B: P=3, F=3 (cuadrado). Mismo bias.
logits_B = np.array([[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]])
bias_B = np.array([100., 200., 300.])
print('--- Caso A (3x4) + (3,) ---')
try:
print(logits_A + bias_A)
except Exception as e:
print('CRASH:', e)
print('--- Caso B (3x3) + (3,) ---')
print(logits_B + bias_B)
print('--- correcto: suma por filas ---')
print(logits_B + bias_B[:, None])
"
Modo de fallo esperado¶
--- Caso A (3x4) + (3,) ---
CRASH: operands could not be broadcast together with shapes (3,4) (3,)
--- Caso B (3x3) + (3,) ---
[[101. 202. 303.]
[104. 205. 306.]
[107. 208. 309.]] <-- ¡biases aplicados POR COLUMNAS, no por filas!
--- correcto: suma por filas ---
[[101. 102. 103.]
[204. 205. 206.]
[307. 308. 309.]] <-- bias 100 a la fila 0, 200 a la fila 1, 300 a la fila 2
El Caso A crashea sonoramente (bien — fácil de cazar). El Caso B es la trampa: los shapes accidentalmente cuadran porque la matriz es cuadrada, pero el bias se aplica por columnas en lugar de por filas. Sin error, respuesta incorrecta. Este es el bug que se va silenciosamente a producción.
Diagnóstico¶
Solo desde los logs:
- Imprime los shapes de los operandos antes de cada op por elemento al menos en desarrollo.
print(f'{logits.shape=} {bias.shape=}'). Caza la trampa en 5 segundos. - Escribe un test de respuesta conocida con un shape no cuadrado. Las matrices cuadradas ocultan muchos bugs de broadcasting; las rectangulares los exponen.
- Usa
np.broadcast_shapes(a.shape, b.shape)para ver qué producirá NumPy. Si no coincide con tu modelo mental, arregla el alineamiento. - Añade un property test: para
(P, F)aleatorios conP != F,add_row_bias(logits, bias).shape == (P, F)y(add_row_bias(logits, bias) - logits)[i, :] == bias[i]para cada filai.
Lección¶
El broadcasting de NumPy alinea shapes desde el eje más a la derecha. Un (3,) se alinea al último eje del otro operando. Si el último eje tiene longitud 3 (matriz cuadrada), las matemáticas "funcionan" pero por columnas, no por filas.
El arreglo es un único carácter: bias[:, None] (o bias.reshape(-1, 1), o bias[:, np.newaxis]). Reshapea (3,) a (3, 1), que broadcastea inambiguamente contra (3, 4) → (3, 4) por filas.
Esta es la misma trampa que la del broadcasting de tensores de la Fase 8 (donde los gradientes backward tienen que sumarse a lo largo del eje broadcasteado). Apréndela aquí al coste de una sesión de depuración; en la Fase 8 costaría un fallo de gradcheck de 4 horas para diagnosticar.
Referencias¶
- Docs de broadcasting de NumPy: https://numpy.org/doc/stable/user/basics.broadcasting.html — la Figura 4 muestra la regla de alineamiento visualmente.
- El archivo de lab
lab/02-broadcasting-trap.mden esta fase está construido exactamente sobre este modo de fallo.