Skip to content

Multiplicacion de matrices en NumPy: Guia completa de np.dot, matmul y @

Updated on

La multiplicacion de matrices es fundamental para el aprendizaje automatico, graficos por computadora, procesamiento de senales y computacion cientifica. Pero NumPy ofrece tres formas de multiplicar matrices -- np.dot(), np.matmul() y el operador @ -- y las diferencias entre ellos son confusas. Usar el incorrecto puede producir silenciosamente resultados erroneos, especialmente al trabajar con arrays de diferentes dimensiones o cuando confundes la multiplicacion elemento a elemento (*) con la multiplicacion de matrices.

Esta guia aclara cada metodo, muestra cuando usar cual y proporciona ejemplos practicos para cada patron comun de algebra lineal.

📚

Multiplicacion elemento a elemento vs matricial

Primero, la distincion critica. El operador * realiza multiplicacion elemento a elemento, NO multiplicacion de matrices:

import numpy as np
 
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
 
# Multiplicacion elemento a elemento (producto de Hadamard)
print(A * B)
# [[ 5 12]
#  [21 32]]
 
# Multiplicacion de matrices
print(A @ B)
# [[19 22]
#  [43 50]]

La multiplicacion de matrices sigue la regla: C[i,j] = sum(A[i,k] * B[k,j]) para todo k.

El operador @ (recomendado)

El operador @ (introducido en Python 3.5) es la forma mas limpia y legible de hacer multiplicacion de matrices:

import numpy as np
 
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
 
C = A @ B
print(C)
# [[19 22]
#  [43 50]]

Reglas de forma

Para que la multiplicacion de matrices A @ B funcione, el numero de columnas en A debe ser igual al numero de filas en B:

import numpy as np
 
# (2, 3) @ (3, 4) = (2, 4)
A = np.ones((2, 3))
B = np.ones((3, 4))
C = A @ B
print(C.shape)  # (2, 4)
 
# (5, 3) @ (3, 2) = (5, 2)
A = np.random.randn(5, 3)
B = np.random.randn(3, 2)
C = A @ B
print(C.shape)  # (5, 2)
 
# Incompatibilidad genera ValueError
# np.ones((2, 3)) @ np.ones((4, 2))  # ValueError

np.matmul()

np.matmul() hace exactamente lo que hace @. Son equivalentes para todos los propositos practicos:

import numpy as np
 
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
 
# Estos producen resultados identicos
result1 = A @ B
result2 = np.matmul(A, B)
 
print(np.array_equal(result1, result2))  # True

Usa @ para codigo mas limpio. Usa np.matmul() cuando necesites pasarlo como argumento de funcion (ej. a reduce).

np.dot()

np.dot() es mas antiguo y se comporta diferente de @/matmul para arrays de mayor dimension:

import numpy as np
 
# Para arrays 1D: producto escalar (resultado escalar)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b))  # 32 (1*4 + 2*5 + 3*6)
 
# Para arrays 2D: multiplicacion de matrices (igual que @)
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B))
# [[19 22]
#  [43 50]]

Diferencia clave: Dimensiones superiores

Para arrays con mas de 2 dimensiones, np.dot() y @ se comportan diferente:

import numpy as np
 
A = np.random.randn(2, 3, 4)
B = np.random.randn(2, 4, 5)
 
# @ trata como lote de multiplicaciones de matrices: (2, 3, 4) @ (2, 4, 5) = (2, 3, 5)
result_matmul = A @ B
print(result_matmul.shape)  # (2, 3, 5)
 
# np.dot usa broadcasting diferente: suma sobre el ultimo eje de A y penultimo de B
result_dot = np.dot(A, B)
print(result_dot.shape)  # (2, 3, 2, 5) -- forma diferente!

Recomendacion: Usa @ o np.matmul() para multiplicacion de matrices. Usa np.dot() solo para productos escalares explicitos de vectores 1D.

Comparacion de metodos

CaracteristicaOperador @np.matmul()np.dot()Operador *
OperacionMultiplicacion matricialMultiplicacion matricialProducto escalarElemento a elemento
1D x 1DProducto escalarProducto escalarProducto escalarElemento a elemento
2D x 2DMultiplicacion matricialMultiplicacion matricialMultiplicacion matricialElemento a elemento
N-D por lotesMatmul por lotesMatmul por lotesSemantica diferenteElemento a elemento
LegibilidadMejorBuenaOKN/A
Escalares permitidosNoNoSiSi
RecomendadoSiSi (funcional)Solo para punto 1DPara Hadamard

Producto escalar (vectores)

El producto escalar de dos vectores es la suma de sus productos elemento a elemento:

import numpy as np
 
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
 
# Todos equivalentes para vectores 1D
print(np.dot(a, b))    # 32
print(a @ b)            # 32
print(np.sum(a * b))    # 32

Aplicaciones

import numpy as np
 
# Similitud del coseno
def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
 
vec1 = np.array([1, 2, 3])
vec2 = np.array([4, 5, 6])
print(f"Cosine similarity: {cosine_similarity(vec1, vec2):.4f}")
# 0.9746
 
# Proyeccion de a sobre b
def project(a, b):
    return (np.dot(a, b) / np.dot(b, b)) * b
 
print(project(np.array([3, 4]), np.array([1, 0])))
# [3. 0.]

Multiplicacion matriz-vector

import numpy as np
 
# Transformacion lineal
A = np.array([[2, 0], [0, 3]])  # Matriz de escalado
v = np.array([1, 1])
 
result = A @ v
print(result)  # [2 3]
 
# Aplicar pesos en una capa de red neuronal
weights = np.random.randn(10, 5)  # 10 salidas, 5 entradas
inputs = np.random.randn(5)
output = weights @ inputs
print(output.shape)  # (10,)

Multiplicacion de matrices por lotes

Para deep learning y procesamiento por lotes, @ maneja lotes automaticamente:

import numpy as np
 
# Lote de 32 matrices, cada una 4x3, por lote de 32 matrices, cada una 3x5
batch_A = np.random.randn(32, 4, 3)
batch_B = np.random.randn(32, 3, 5)
 
result = batch_A @ batch_B
print(result.shape)  # (32, 4, 5)

Ejemplos practicos

Sistema de ecuaciones lineales

import numpy as np
 
# Resolver Ax = b
# 2x + 3y = 8
# 4x + y = 10
A = np.array([[2, 3], [4, 1]])
b = np.array([8, 10])
 
x = np.linalg.solve(A, b)
print(f"x = {x[0]:.2f}, y = {x[1]:.2f}")
# x = 2.20, y = 1.20
 
# Verificar: A @ x deberia ser igual a b
print(A @ x)  # [8. 10.]

Pasada directa de red neuronal simple

import numpy as np
 
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
 
# Red: 3 entradas -> 4 ocultas -> 2 salidas
np.random.seed(42)
W1 = np.random.randn(4, 3) * 0.5  # Pesos primera capa
b1 = np.zeros(4)
W2 = np.random.randn(2, 4) * 0.5  # Pesos segunda capa
b2 = np.zeros(2)
 
# Pasada directa
X = np.array([1.0, 0.5, -1.0])  # Entrada
h = sigmoid(W1 @ X + b1)         # Capa oculta
y = sigmoid(W2 @ h + b2)         # Capa de salida
 
print(f"Input:  {X}")
print(f"Hidden: {h}")
print(f"Output: {y}")

Proyeccion PCA

import numpy as np
 
# Generar datos de ejemplo
np.random.seed(42)
data = np.random.randn(100, 5)  # 100 muestras, 5 caracteristicas
 
# Centrar datos
data_centered = data - data.mean(axis=0)
 
# Calcular matriz de covarianza usando multiplicacion de matrices
cov = (data_centered.T @ data_centered) / (len(data) - 1)
 
# Descomposicion en valores propios
eigenvalues, eigenvectors = np.linalg.eigh(cov)
 
# Proyectar a los 2 componentes principales
top_2 = eigenvectors[:, -2:]  # Ultimos 2 (mayores valores propios)
projected = data_centered @ top_2
print(f"Original shape: {data.shape}")
print(f"Projected shape: {projected.shape}")  # (100, 2)

Visualizar operaciones matriciales

Para explorar los efectos de las transformaciones matriciales en datos, PyGWalker (opens in a new tab) te permite visualizar datos proyectados de forma interactiva en Jupyter:

import pandas as pd
import pygwalker as pyg
 
df = pd.DataFrame(projected, columns=['PC1', 'PC2'])
walker = pyg.walk(df)

FAQ

Cual es la diferencia entre np.dot y np.matmul?

Para arrays 1D y 2D, producen el mismo resultado. La diferencia clave es con arrays de mayor dimension: np.matmul() (y @) los trata como lotes de matrices, mientras que np.dot() usa una regla de broadcasting diferente. Para codigo nuevo, prefiere @ o np.matmul().

Que hace el operador @ en NumPy?

El operador @ realiza multiplicacion de matrices, equivalente a np.matmul(). Fue introducido en Python 3.5. Para arrays 2D, A @ B calcula el producto matricial estandar. Para arrays de mayor dimension, realiza multiplicacion de matrices por lotes.

Por que A * B da resultados diferentes que A @ B?

A * B realiza multiplicacion elemento a elemento (producto de Hadamard), multiplicando elementos correspondientes. A @ B realiza multiplicacion de matrices, siguiendo la regla C[i,j] = sum(A[i,k] * B[k,j]). Son operaciones fundamentalmente diferentes.

Como calculo el producto escalar de dos vectores en NumPy?

Para arrays 1D (vectores), usa np.dot(a, b), a @ b o np.sum(a * b). Los tres devuelven el mismo resultado escalar: la suma de los productos elemento a elemento.

Que formas se requieren para la multiplicacion de matrices?

Para A @ B, la ultima dimension de A debe ser igual a la penultima dimension de B. Para 2D: (m, n) @ (n, p) = (m, p). El numero de columnas en A debe ser igual al numero de filas en B.

Conclusion

Para multiplicacion de matrices en NumPy, usa el operador @ como tu opcion predeterminada -- es el mas legible y maneja operaciones por lotes correctamente. Usa np.dot() solo para productos escalares explicitos de vectores 1D. Nunca confundas * (elemento a elemento) con @ (multiplicacion de matrices). Recuerda la regla de forma: (m, n) @ (n, p) = (m, p), y evitaras los errores mas comunes de multiplicacion de matrices.

📚