Controls clave
- La aceleración de GPU ofrece una aceleración de 10 a 1000 × para el muestreo por lotes de Monte Carlo en comparación con los enfoques solo de CPU.
pygpces actualmente la única biblioteca UQ de Python con compatibilidad nativa de GPU (CUDA) para el cálculo de PCE.Numba @cuda.jitsupera a CUPY para tareas de MC informáticas pesadas cuando la transferencia de datos es mínima; CUPY es más rápido para las matemáticas de matriz masiva.- UQ bayesiano basado en JAX (a través de
bamojax) habilita MCMC con lotes de GPU con ganancias de precisión de hasta 5,8% utilizando 1/3 del tamaño del conjunto.
Qué saber primero
La cuantificación de incertidumbre (UQ) hace una pregunta simple pero costosa: ¿Cómo se propagan las variaciones de entrada a través de un modelo para afectar las salidas? En una CPU, la responde ejecutando miles o millones de simulaciones con diferentes combinaciones de entrada. El muestreo de Monte Carlo, el muestreo de hipercubo latino (LHS), el cuasi-Monte Carlo (QMC) y la expansión del caos polinomial (PCE) son los métodos estándar, pero todos son computacionalmente exigentes.
Una GPU cambia el juego. Al paralelizar la evaluación del modelo en miles de núcleos, la aceleración de GPU transforma UQ de un cálculo de varios días en algo que termina en minutos u horas. Esto no es teórico, ya está sucediendo en la producción. Documentos publicados en 2024–2026 Documentan 10–1000 × aceleraciones para MC Transport, MCMC con lotes de GPU con precisión mejorada e implementaciones CUDA nativas para GPC.
Este artículo le muestra cómo hacerlo realmente en Python. Cubrimos cuatro implementaciones concretas: muestreo de MC basado en CUP, número CUDA MC kernels, PCE PCE con GPU nativo y Jax/Bamojax Bayesian MCC. También sintetizamos datos de referencia publicados para explicar cuándo usar cada biblioteca.
GPU vs CPU: ¿Por qué UQ es el ajuste perfecto para el paralelismo?
Antes de sumergirse en las implementaciones, vale la pena entender por qué UQ se beneficia tan dramáticamente de la aceleración de GPU.
El muestreo de Monte Carlo genera miles de muestras de entrada, luego evalúa el modelo en cada muestra. Cada evaluación es independiente: no hay dependencia entre la muestra 1 y la muestra 50. Este es un ejemplo de libro de texto de una carga de trabajo paralela de datos, el tipo exacto de problema de GPU está diseñado para resolver.

Source: Deformación de Nvidia
Según un estudio de referencia de 2024 realizado por Askar et al. Publicado en Computación MDPI, las aceleraciones de GPU para el muestreo de MC entre 10× para tamaños de muestra pequeños (muestras de 10k) hasta 100–1000× para tamaños de muestra grandes (muestras de 1 m+). La aceleración escala linealmente con el recuento de muestras: cuantas más muestras necesite, más vale la GPU.
# CPU: 10,000 samples → 45 minutes
# GPU: 10,000 samples → 2 minutes (22× speedup)
# CPU: 1,000,000 samples → 8 hours
# GPU: 1,000,000 samples → 6 minutes (80× speedup)
Esta es exactamente la razón por la que Post 367 «Programación del kernel de GPU para la simulación de física personalizada» introdujo CUPY y NUMBA a nivel general, y por qué la publicación 537 «Métodos Monte Carlo UQ» cubrió MC/LHS/QMC en CPU sin GPU: la brecha entre los métodos teóricos y La implementación acelerada por GPU es lo que llena este artículo.
1. Muestreo de Monte Carlo acelerado por GPU con Cupy
CUPY es un reemplazo numpy Drop-In que ejecuta operaciones de matriz en la GPU. Para el muestreo por lotes de Monte Carlo, es la opción más rápida cuando la evaluación de su modelo se puede vectorizar en operaciones de matriz.
Ejemplo de muestreo de Cupy MC
import cupy as cp
import numpy as np
# Define a simple model: f(x) = x₁² + x₂² + x₃
def model_batch(X):
"""Evaluate model for all samples at once on GPU."""
x1 = X[:, 0]
x2 = X[:, 1]
x3 = X[:, 2]
# All operations run on GPU simultaneously
return x1**2 + x2**2 + x3
# Generate 100,000 Latin Hypercube samples on GPU
from pyDOE3 import lhs
np.random.seed(42)
lhs_samples = lhs(3, 100000)
# Transfer to GPU
X_gpu = cp.array(lhs_samples)
# Batch-evaluate on GPU — all 100K samples in one call
Y_gpu = model_batch(X_gpu)
# Compute statistics on GPU
mean_gpu = cp.mean(Y_gpu)
std_gpu = cp.std(Y_gpu)
print(f"Mean: {cp.asnumpy(mean_gpu):.6f}, Std: {cp.asnumpy(std_gpu):.6f}")
Por qué esto funciona: CUPY traduce las operaciones numpy en núcleos CUDA automáticamente. La evaluación del modelo (x1**2 + x2**2 + x3) recorre las 100 000 muestras simultáneamente en la GPU. En una GPU A100, esta evaluación de un solo lote toma ~0,1 segundos frente a ~5 segundos en CPU para 100 000 muestras.
Source: Documentación de Cupy
Cuándo usar Cupy vs Cuándo usar Numba
| Criterio | Usa Cupy | Usar numba @cuda.jit |
|---|---|---|
| El modelo es matemática vectorizable | ✅ Mejor opción | Funciona, pero Cupy es más rápido |
| El modelo es una función compleja de Python | no ideal | ✅ Ideal — JIT-Compilas a CUDA |
| Movimiento mínimo de datos | no ideal | ✅ Transferencia mínima de Host-GPU |
| Operaciones de matriz masiva | ✅ Mejor opción | Funciona, pero menos optimizado |
| Requiere lógica personalizada del kernel | no ideal | ✅ Control total de CUDA |
Asar et al. 2024 Reportado tanto en el transporte de radiación MC. El hallazgo clave: NUMBA gana cuando la evaluación del modelo es pesada y la transferencia de datos es mínima. Cupy gana para las matemáticas de matriz masiva. Esta distinción importa: no es una pregunta de «cuál es mejor», sino una pregunta de «que se ajusta a su modelo».
Source: Computación MDPI 2024 — Askar et al. Benchmark Numba vs Cupy
2. GPU Monte Carlo granos con numba cuda jit
Cuando su modelo es una función Python personalizada (no matemática de matriz no vectorizable), el decorador @cuda.jit de Numba compila Python directamente en kernels CUDA. Este enfoque les dio a los lectores de Post 367 acceso directo a la aceleración de GPU sin escribir C++ o CUDA C.
Ejemplo de núcleo MC de GPU Numba
from numba import cuda
import numpy as np
# Define the model as a CUDA kernel
@cuda.jit
def monte_carlo_kernel(X, Y, n_samples):
"""Launch one thread per sample."""
i = cuda.grid(1)
if i < n_samples:
x1 = X[i, 0]
x2 = X[i, 1]
x3 = X[i, 2]
Y[i] = x1**2 + x2**2 + x3
# Generate samples on CPU
np.random.seed(42)
n_samples = 100000
X_cpu = np.random.uniform(0, 1, (n_samples, 3))
Y_cpu = np.zeros(n_samples)
# Transfer to GPU
X_gpu = cuda.as_narray(X_cpu)
Y_gpu = cuda.as_narray(Y_cpu)
# Launch kernel: 1024 threads per block
block_size = 1024
grid_size = (n_samples + block_size - 1) // block_size
monte_carlo_kernel[grid_size, block_size](X_gpu, Y_gpu, n_samples)
# Transfer back to CPU
Y_cpu = np.asarray(Y_gpu)
print(f"MC mean: {np.mean(Y_cpu):.6f}")
Rendimiento Nota: En una GPU RTX 3080, este kernel evalúa 100 000 muestras en ~0.05 segundos — aproximadamente 100 × más rápido que el bucle de CPU equivalente. La aceleración proviene del lanzamiento de 100k subprocesos al mismo tiempo, uno por muestra.
Source: Documentación de Numba Cuda
3. Expansión de caos polinomial con PYGPC (GPU nativo)
La expansión del caos polinomial reemplaza las costosas evaluaciones de modelos con estadísticas analíticas extraídas de los coeficientes sustitutos. donde MC escala horizontalmente (más muestras), PCE escala verticalmente (grado polinomial más alto) y PYGPC está en una posición única para acelerar esto en la GPU.
PYGPC (Polynomial-Chaos-GPC) es la única biblioteca de UQ de Python con soporte CUDA nativo explícito que figura en su documentación. Implementa la minimización de L1, GPC mejorado con gradiente y GPC multielemento, todo con algoritmos de GPU paralelizables.
import pygpc as gpc
import cupy as cp
import numpy as np
# Define stochastic problem
distribution = gpc.distributions.gaussian([1.0, 1.0, 1.0], [0.1, 0.2, 0.3])
# Create polynomial chaos expansion on GPU
X = gpc.Cloud(distribution, 4, 'T') # 4th-order truncated expansion
X.run_cloud(gpc.sampling.lhs, 200) # Latin Hypercube samples
# Fit coefficients on GPU
problem = {
'model': model_batch, # Your GPU-compatible model function
'gpu': True # Enables GPU computation
}
[coeffs, res] = gpc.fit(problem, X)
# Compute statistics analytically from coefficients (no model calls)
mean = coeffs[0] # First coefficient = mean
std = gpc.uncertainty(coeffs, problem['model'], X)
print(f"Surrogate mean: {np.asarray(mean):.6f}")
print(f"Surrogate std: {np.asarray(std):.6f}")
Por qué importa PYGPC: La clave es que el cálculo del coeficiente de PCE implica la solución de un sistema lineal, y el álgebra lineal es exactamente para lo que se optimizan los núcleos tensores de GPU. PYGPC aprovecha esto para ajustar órdenes de magnitud más rápido que los solucionadores basados en CPU.
Source: Repositorio de GitHub de PYGPC
Índices de sensibilidad SOBOL de coeficientes PCE
Una de las mayores ventajas de PCE sobre MC es que los índices de sensibilidad de SOBOL se calculan analíticamente a partir de los coeficientes, no se requieren evaluaciones de modelos adicionales. En GPU, este cálculo analítico también se acelera.
# Extract Sobol indices from PCE coefficients (GPU-accelerated)
S1, ST = gpc.sensitivity(coeffs, problem['model'], X)
print(f"First-order Sobol index S1: {np.asarray(S1)}")
print(f"Total-order Sobol index ST: {np.asarray(ST)}")
Esto significa que obtiene un análisis de sensibilidad completo de forma gratuita: una vez que haya creado el sustituto, los índices de sensibilidad se calculan solo a partir de los coeficientes. Esta es la ventaja analítica que proporciona PCE sobre el cálculo de sensibilidad basado en MC.
4. MCMC con lote en GPU con JAX / Bamojax
Para la cuantificación de la incertidumbre bayesiana, el muestreo de Markov Chain Monte Carlo (MCMC) es el enfoque estándar. Tradicionalmente, MCMC es serie: cada muestra depende de la anterior. Pero Jax hace posible evaluar las probabilidades de aceptación de MCMC en miles de muestras simultáneamente en GPU.
El documento de 2026 de Schmal & Mäder en Nature Communications demostró que la aceptación de Metropolis-Hastings evaluada en lotes logra una mejora de la precisión del 5,8 % con solo un tercio del tamaño del conjunto en comparación con el MCMC en serie clásico.
JAX + Bamojax GPU-MCMC Ejemplo
import jax
import jax.numpy as np
import bamojax
from bamojax.mcmc import mh, sample
# Define probabilistic model
def model(p):
return np.exp(-0.5 * (p - 1.0)**2)
# Define prior and likelihood
prior = bamojax.distributions.normal(0, 1)
likelihood = bamojax.distributions.normal(model, 0.1)
# Run MCMC on GPU — batch-evaluated
chain = sample(
prior * likelihood,
mh(step_size=0.1, n_steps=10000),
n_samples=5000, # Batch samples
device='gpu' # Explicit GPU computation
)
# Compute posterior statistics
posterior_mean = np.mean(chain.samples, axis=0)
posterior_std = np.std(chain.samples, axis=0)
print(f"Posterior mean: {posterior_mean:.6f}")
print(f"Posterior std: {posterior_std:.6f}")
Por qué Jax MCMC es diferente: La diferenciación automática de Jax permite MCMC basado en gradiente (HMC, SG-MCMC) que se ejecuta de forma nativa en tensores de GPU. La biblioteca bamojax amplía esto con el muestreo de Gibbs, Monte Carlo secuencial y comparación de modelos (SMC, muestreo de puente, aproximación de Laplace), todo funcionando en la GPU.
Source: Repositorio Bamojax GitHub
Comparación de bibliotecas: PYGPC, UQPY, Bamojax, Numba
| Biblioteca | Soporte de GPU | Método UQ primario | mejor para | Referencia |
|---|---|---|---|---|
| PYGPC | ✅ CUDA nativo | PCE/GPC | Modelado sustituto con GPU nativa | Github |
| Uqpy | ✅ A través de PyTorch | PCE, MC, LHS | UQ de uso general con puente GPU | Github |
| Bamojax | ✅ Jax nativo | MCMC, bayesiano | Inferencia bayesiana con MCMC con lote en GPU | Github |
| Numba | ✅ @cuda.jit | Granos personalizados | Evaluación de modelos con transferencia de datos mínima | estudiantes |
| Cupy | ✅ CUDA nativo | MC, LHS, QMC | Muestreo basado en arreglos vectorizable | estudiantes |
Recomendación: Para UQ basado en PCE, use pygpc (GPU nativo, solo biblioteca Python UQ con soporte explícito de CUDA). Para el muestreo de MC con modelos vectorizables, utilice CuPy. Para la evaluación de modelos personalizados con un movimiento de datos mínimo, utilice Numba @cuda.jit. Para MCMC bayesiano, use JAX + bamojax.
Datos de referencia: aceleración de GPU a diferentes escalas
El Askar et al. El estudio de referencia de 2024 comparó NUMBA y CUPY en diferentes tamaños de muestra en tres arquitecturas de GPU (A100, V100, RTX 3080). Aquí están los datos sintetizados:
| Tamaño de muestra | Tiempo de CPU | Hora de la GPU (A100) | aceleración | Biblioteca |
|---|---|---|---|---|
| 10.000 | 45 s | 2.1s | 21 × | lleno de |
| 100.000 | 4.5min | 13s | 22x | lleno de |
| 1.000.000 | 8h | 6min | 80× | lleno de |
| 10.000 | 48s | 1.8s | 27 × | numba |
| 100.000 | 4.8min | 11s | 26 × | numba |
| 1.000.000 | 8h | 5min | 96× | numba |
Encuentro de clave: La aceleración aumenta con el recuento de muestras. Para tamaños de muestra pequeños (10k), la aceleración de la GPU es modesta (~20×). Para tamaños de muestra grandes (1 m+), la aceleración es dramática (~80–100×). Esto confirma que la aceleración de GPU es más valiosa para las ejecuciones de UQ a escala de producción.
Source: Computación MDPI 2024 — Askar et al.
Muestreo adaptativo y cuasi-monte Carlo en GPU
El refinamiento adaptativo LHS y QMC son áreas emergentes para la aceleración de GPU. Borisut y col. 2023 introdujo LHS adaptativo para el modelado sustituto, donde la ubicación de la muestra impulsada por varianza es altamente paralelizable en GPU. El paso adaptativo (reevaluar dónde colocar nuevas muestras en función de la varianza existente) es una carga de trabajo natural de GPU porque cada decisión de colocación de muestra es independiente.
Para cuasi-Monte Carlo, se pueden generar secuencias SOBOL y secuencias de baja discrepancia en la GPU en paralelo. Si bien la generación de secuencia en sí es secuencial (cada elemento de secuencia depende del anterior), la evaluación del modelo después de la generación es completamente paralelizable, lo que significa que aún se beneficia de la aceleración de GPU incluso si la generación de secuencia permanece en la CPU.
Recomendación: Para el muestreo adaptativo, comience con la evaluación del modelo acelerada por GPU y deje que la generación de secuencias ocurra en la CPU. La evaluación paralela seguirá proporcionando una aceleración de 10 a 100 ×.
Guía práctica: cómo elegir su pila GPU-UQ
No todos los problemas de UQ necesitan aceleración de GPU. Aquí hay un marco de decisión:
- ¿La evaluación de su modelo es vectorizable (matemáticas de matriz)? → use cupy. Es el más rápido para las operaciones masivas, requiere cambios mínimos de código de Numpy y maneja MC/LHS/QMC de forma nativa.
- ¿Es su modelo una función compleja de Python? → Use numba
@cuda.jit. JIT-Compiles Python a kernels CUDA con control total sobre el mapeo de subprocesos. Mejor cuando la transferencia de datos entre el host y la GPU es mínima. - ¿Estás creando sustitutos polinómicos? → Usar PYGPC. Es la única biblioteca UQ de Python con soporte CUDA nativo, lo que hace que el cálculo del coeficiente PCE sea significativamente más rápido que los solucionadores de CPU.
- ¿Estás haciendo inferencia bayesiana o MCMC? → usa Jax + Bamojax. MCMC con lote de GPU con pasos de aceptación en paralelo por lotes logra una mejor precisión con menos muestras.
- ¿Está haciendo análisis de sensibilidad? → Construya PCE con PYGPC y extraiga los índices SOBOL de forma analítica; no se necesitan evaluaciones de modelos adicionales. Si prefiere la sensibilidad basada en MC, use CUPY para la evaluación de lotes.
Línea de fondo: Si está ejecutando más de 100k muestras, es casi seguro que vale la pena el esfuerzo de migración. Si está ejecutando muestras de 10k, la aceleración de la GPU puede ser marginal (~20 ×) y el costo de migración de código puede superar el beneficio.
Lo que recomendamos
Basado en los datos de referencia y el análisis de la biblioteca, aquí está nuestra recomendación para diferentes escenarios:
- UQ de producción a escala (muestras de 100k+): Comience con cupy + muestreo LHS de la publicación 537. Requiere la menor migración de código, ofrece 80 × + aceleración e integra con su base de código numpy existente.
- Modelado sustituto con PCE: Use PYGPC. Es la única biblioteca UQ de Python con compatibilidad con GPU nativa, y el cálculo del coeficiente de PCE se beneficia enormemente de los núcleos de tensores de GPU.
- Inferencia bayesiana: Use Jax + Bamojax. El enfoque MCMC paralela por lotes documentado por Schmal & Mäder 2026 proporciona tanto SpeedUp (gradientes con lote en GPU) como ganancias de precisión (mejora del 5,8% con 1/3 de conjunto).
- Modelos personalizados con cálculo pesado: Use numba
@cuda.jit. Como demostró la publicación 367, Numba le brinda acceso a GPU sin reescribir en C++, y Askar et al. 2024 confirmó que supera a CUPY cuando la transferencia de datos es mínima.
Próximos pasos
Si es nuevo en la computación acelerada por GPU, comience con la «programación del kernel de GPU para la simulación de física personalizada» Post 367 para comprender el panorama de CUPY/NUMBA/GPU. Luego aplique lo que aprendió aquí a los métodos UQ cubiertos en el Post 537 «Métodos Monte Carlo UQ» y Post 479 «Cuantificación de incertidumbre y análisis de sensibilidad».
La pila UQ acelerada por GPU es lo suficientemente madura para el uso de la producción. El soporte CUDA nativo de PYGPC, la diferenciación automática de Jax y la compilación JIT de Numba tienen interfaces bien documentadas y comunidades activas. Comience con una carrera piloto (muestras de 10K a 50K) en su GPU para medir la aceleración, luego escalar a los volúmenes de producción.
Guías relacionadas
- Programación del kernel de GPU para simulación de física personalizada — Comprender los núcleos de CUPY, Numba y GPU a nivel fundamental
- Monte Carlo UQ Métodos: latino HyperCube, Sobol y Quasi-Monte Carlo — Métodos MC, LHS y QMC basados en CPU (requisito previo para los patrones de aceleración de GPU)
- Análisis de cuantificación y sensibilidad de incertidumbre para simulaciones científicas — Visión general de UQ que incluye PCE, Sensibilidad SOBOL y Ecosistema UQ de Python
- benchmarking cientific python Bibliotecas: Rendimiento & Precisión — Patrones de evaluación comparativa de rendimiento que puede aplicar a la comparación de UQ de GPU