Reading Time: 10 minutes

Clés à emporter

  • L’accélération du GPU offre une accélération de 10 à 1 000 × pour l’échantillonnage par lots de Monte Carlo par rapport aux approches CPU uniquement.
  • pygpc est actuellement la seule bibliothèque Python UQ avec une prise en charge GPU native (CUDA) pour le calcul PCE.
  • Numba @cuda.jit surpasse CUPY pour les tâches de calcul lourde de calcul lorsque le transfert de données est minime ; CUPY est plus rapide pour les mathématiques en bloc.
  • L’UQ bayésienne basée sur JAX (via bamojax) active le MCMC par lots GPU avec jusqu’à 5,8 % de gains de précision en utilisant 1/3 de la taille de l’ensemble.

Ce qu’il faut savoir d’abord

La quantification de l’incertitude (UQ) pose une question simple mais coûteuse : comment les variations d’entrée se propagent-elles à travers un modèle pour affecter les sorties ? Sur un processeur, vous y répondez en exécutant des milliers ou des millions de simulations avec différentes combinaisons d’entrées. L’échantillonnage de Monte Carlo, l’échantillonnage d’hypercube latin (LHS), le quasi-Monte Carlo (QMC) et l’expansion du chaos polynomial (PCE) sont les méthodes standard – mais ils sont tous exigeants en termes de calculs.

Un GPU change la donne. En parallélisant l’évaluation du modèle sur des milliers de cœurs, l’accélération GPU transforme UQ d’un calcul de plusieurs jours en quelque chose qui se termine en quelques minutes ou heures. Ce n’est pas théorique – cela se produit déjà en production. Documents publiés en 2024–2026 Document 10–1000× Accélérations pour le transport MC, MCMC par lots GPU avec une précision améliorée et les implémentations CUDA natives pour GPC.

Cet article vous montre comment le faire réellement en Python. Nous couvrons quatre implémentations concrètes : Échantillonnage MC basé sur Cupy, Numba Cuda MC Kernels, PyGPC PCE avec GPU natif et Jax/Bamojax bayésien MCMC. Nous synthétisons également des données de référence publiées pour expliquer quand utiliser chaque bibliothèque.


GPU vs CPU : pourquoi l’UQ est le parfait pour le parallélisme

Avant de plonger dans les implémentations, il vaut la peine de comprendre pourquoi l’UQ profite si considérablement de l’accélération du GPU.

L’échantillonnage de Monte Carlo génère des milliers d’échantillons d’entrée, puis évalue le modèle à chaque échantillon. Chaque évaluation est indépendante — il n’y a pas de dépendance entre l’échantillon 1 et l’échantillon 50. Il s’agit d’un exemple de manuel d’une charge de travail parallèle aux données, le type exact de GPU de problème est conçu pour résoudre.

Comparaison d'accélération du GPU pour l'échantillonnage de Monte Carlo à différentes tailles d'échantillon - synthétisé à partir d'Askar et al. Données de référence 2024

Source: NVIDIA WARP

Selon une étude de référence de 2024 par Askar et al. Publié dans MDPI Computation, les accélérations du GPU pour la plage d’échantillonnage MC de 10× pour les petits échantillons (10 000 échantillons) à 100 à 1 000 × pour les échantillons de grande taille (1 m+). L’accélération évolue linéairement avec le nombre d’échantillons – plus vous avez besoin d’échantillons, plus le GPU est payant.

# 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)

C’est exactement pourquoi après 367 « Programmation du noyau GPU pour la simulation physique personnalisée » a introduit CUPY et NUMBA à un niveau général, et pourquoi POST 537 « Monte Carlo UQ » couvrait MC/LHS/QMC sur CPU sans GPU – l’écart entre les méthodes théoriques et L’implémentation accélérée par GPU est ce que cet article remplit.


1. Échantillonnage de Monte-Carlo accéléré par GPU avec Cupy

Cupy est un remplacement de Drop-In NumPy qui exécute des opérations de tableau sur le GPU. Pour l’échantillonnage par lots de Monte Carlo, c’est l’option la plus rapide lorsque l’évaluation de votre modèle peut être vectorisée dans des opérations de tableau.

Exemple d’échantillonnage 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}")

Pourquoi cela fonctionne : CUPY traduit automatiquement les opérations NumPy en noyaux CUDA. L’évaluation du modèle (x1**2 + x2**2 + x3) s’étend simultanément sur les 100 000 échantillons sur le GPU. Sur un GPU A100, cette évaluation de lot unique prend ~0,1 seconde contre ~5 secondes sur CPU pour 100 000 échantillons.

Source: Documentation CUPY

Quand utiliser CUPY VS Quand utiliser Numba

Critère Utiliser Cupy Utiliser Numba @cuda.jit
Le modèle est un tableau mathématique vectorisable ✅ Meilleur choix Fonctionne, mais Cupi est plus rapide
Le modèle est une fonction Python complexe pas idéal ✅ Idéal – Compile JIT à CUDA
Mouvement de données minimal pas idéal ✅ Transfert hôte-GPU minimal
Opérations de tableau en masse ✅ Meilleur choix Fonctionne mais moins optimisé
Nécessite une logique de noyau CUDA personnalisée pas idéal ✅ Contrôle total CUDA

Askar et al. 2024 étalonné sur le transport par rayonnement MC. La conclusion clé : Numba gagne lorsque l’évaluation du modèle est lourde et que le transfert de données est minime. CUPY gagne pour les mathématiques en bloc. Cette distinction compte – ce n’est pas une question « qui est mieux », mais une question « qui correspond à votre modèle ».

Source: Calcul MDPI 2024 — Askar et al. Numba vs CUPY Benchmark


2. GPU Monte Carlo Kernels avec Numba Cuda JIT

Lorsque votre modèle est une fonction Python personnalisée (non vectorisable, le décorateur de Numba @cuda.jit compile Python directement dans les noyaux CUDA. Cette approche a permis aux lecteurs de Post 367 d’accéder directement à l’accélération GPU sans écrire C++ ou CUDA C.

Exemple de noyau MC 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}")

Note de performances : sur un GPU RTX 3080, ce noyau évalue 100 000 échantillons en ~0,05 seconde – à peu près  100 ×  plus rapide que la boucle de processeur équivalente. L’accélération vient du lancement simultané de 100 000 fils, un par échantillon.

Source: Documentation NUMBA CUDA


3. Extension polynomiale du chaos avec PyGPC (GPU natif)

L’expansion du chaos polynomial remplace les évaluations coûteuses de modèles par des statistiques analytiques extraites des coefficients de substitution. où MC scale horizontalement (plus d’échantillons), les échelles PCE verticalement (degré polynomial supérieur) – et PyGPC est positionné de manière unique pour accélérer cela sur le GPU.

pygpc (polynomial-chaos-gpc) est la seule bibliothèque Python UQ avec un support CUDA explicitement natif répertorié dans sa documentation. Il implémente la minimisation L1, le GPC amélioré par gradient et le GPC multi-éléments, tous avec des algorithmes GPU parallélisables.

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}")

Pourquoi PyGPC est important : L’idée clé est que le calcul du coefficient PCE implique la résolution d’un système linéaire – et l’algèbre linéaire est exactement l’objectif pour lequel les cœurs de tenseur du GPU sont optimisés. PyGPC tire parti de cette option pour s’adapter aux ordres de grandeur plus rapidement que les solveurs basés sur CPU.

Source: Référentiel GitHub PyGPC

Indices de sensibilité SOBOL provenant des coefficients PCE

L’un des plus grands avantages de PCE par rapport à MC est que les Les indices de sensibilité au SOBOL sont calculés analytiquement à partir des coefficients – aucune évaluation de modèle supplémentaire requise. Sur GPU, ce calcul analytique est également accéléré.

# 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)}")

Cela signifie que vous obtenez gratuitement une analyse de sensibilité complète – une fois que vous avez construit la mère porteuse, les indices de sensibilité sont calculés à partir des seuls coefficients. Il s’agit de l’avantage analytique que le PCE offre par rapport au calcul de sensibilité basé sur les MC.


4. MCMC par lots GPU avec Jax / Bamojax

Pour la quantification bayésienne de l’incertitude, l’échantillonnage de la chaîne de Markov Monte Carlo (MCMC) est l’approche standard. Traditionnellement, MCMC est en série – chaque échantillon dépend du précédent. Mais JAX permet de évaluer par lots les probabilités d’acceptation MCMC dans des milliers d’échantillons simultanément sur GPU.

L’article de 2026 de Schmal & Mäder in Nature Communications a démontré que les acceptation par lots de Metropolis-Hastings value une amélioration de la précision de 5,8 % avec un tiers seulement de la taille d’ensemble par rapport au MCMC en série classique.

JAX + Bamojax Exemple GPU-MCMC

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}")

Pourquoi JAX MCMC est différent : La différenciation automatique de Jax active le MCMC basé sur le gradient (HMC, SG-MCMC) qui s’exécute nativement sur les tenseurs du GPU. La bibliothèque bamojax étend cela avec l’échantillonnage de Gibbs, la comparaison séquentielle de Monte-Carlo et la comparaison de modèles (SMC, échantillonnage de pont, approximation de Laplace) – tous fonctionnant sur GPU.

Source: Référentiel GitHub de Bamojax


Comparaison des bibliothèques : PYGPC, UQPY, Bamojax, Numba

Bibliothèque Prise en charge du GPU Méthode UQ principale le mieux pour Référence
PyGPC ✅ CUDA natif PCE/GPC Modélisation de substitution avec GPU natif github
UQPY ✅ via Pytorch PCE, MC, LHS UQ à usage général avec pont GPU github
Bamojax ✅ Jax natif MCMC, bayésien Inférence bayésienne avec MCMC par lots GPU github
Numba ✅ @cuda.jit noyaux personnalisés Évaluation du modèle avec transfert de données minimal docs
Coupe ✅ CUDA natif MC, LHS, QMC Échantillonnage basé sur des tableaux vectorisables docs

Recommandation : Pour l’UQ basée sur PCE, utilisez pygpc (GPU natif, seule bibliothèque Python UQ avec prise en charge CUDA explicite). Pour l’échantillonnage MC avec des modèles vectorisables, utilisez CuPy. Pour une évaluation de modèle personnalisée avec un mouvement de données minimal, utilisez Numba @cuda.jit. Pour le MCMC bayésien, utilisez JAX + bamojax.


Données de référence : accélération du GPU à différentes échelles

Askar et al. L’étude de référence 2024 a comparé NUMBA et CUPY à différentes tailles d’échantillons sur trois architectures GPU (A100, V100, RTX 3080). Voici les données synthétisées :

Taille de l’échantillon Temps CPU Temps du GPU (A100) accélération Bibliothèque
10 000 45s 2.1s 21× cupide
100 000 4,5 min 13 s 22× cupide
1 000 000 8h 6 min 80× cupide
10 000 48s 1.8s 27× numba
100 000 4,8 min 11 s 26× numba
1 000 000 8h 5 min 96× numba

Détermination de clé : La rapidité augmente avec le nombre d’échantillons. Pour les petits échantillons (10 000), la vitesse du GPU est modeste (~ 20 ×). Pour les grands échantillons (1 m+), la vitesse est spectaculaire (~80–100×). Cela confirme que l’accélération du GPU est la plus précieuse pour les exécutions UQ à l’échelle de la production.

Source: Calcul MDPI 2024 — Askar et al.


Échantillonnage adaptatif et Quasi-Monte Carlo sur GPU

Le raffinement adaptatif LHS et QMC sont des domaines émergents pour l’accélération du GPU. Borisut et al. 2023 a introduit le LHS adaptatif pour la modélisation de substitution, où le placement de l’échantillon basé sur la variance est hautement parallélisant sur le GPU. L’étape adaptative (réévaluation de l’endroit où placer de nouveaux échantillons en fonction de la variance existante) est une charge de travail GPU naturelle, car chaque décision de placement d’échantillon est indépendante.

Pour les quasi-monte-carlo, des séquences de SOBOL et des séquences de faible écart peuvent être générées sur GPU en parallèle. Bien que la génération de séquence elle-même soit séquentielle (chaque élément de séquence dépend de la précédente), l’évaluation du modèle après génération est entièrement parallélisant, ce qui signifie que vous bénéficiez toujours de l’accélération du GPU même si la génération de séquence reste sur le processeur.

Recommandation : Pour un échantillonnage adaptatif, commencez par une évaluation du modèle accélérée par GPU et laissez la génération de séquence se produire sur CPU. L’évaluation parallèle fournira toujours une accélération de 10 à 100 ×.


Guide pratique : Comment choisir votre pile GPU-UQ

Tous les problèmes d’UQ ne nécessitent pas une accélération du GPU. Voici un cadre de décision :

  1. L’évaluation de votre modèle est-elle vectorisée (mathématique de tableau) ? → Utilisez Cupy. C’est le plus rapide pour les opérations en masse, nécessite un minimum de modifications de code de NumPy et gère le MC/LHS/QMC de manière native.
  2. Votre modèle est-il une fonction Python complexe ? → Utilisez Numba @cuda.jit. Jit-compile Python aux noyaux CUDA avec un contrôle total sur le mappage de threads. Meilleur lorsque le transfert de données entre l’hôte et le GPU est minime.
  3. Construisez-vous des substituts polynomiaux ? → Utilisez PyGPC. C’est la seule bibliothèque Python UQ avec la prise en charge de CUDA natif, ce qui rend le calcul du coefficient PCE considérablement plus rapide que les solveurs de processeur.
  4. Faites-vous une inférence bayésienne ou MCMC ? → Utilisez Jax + Bamojax. Le MCMC batché par GPU avec des étapes d’acceptation parallèles par lots permet une meilleure précision avec moins d’échantillons.
  5. Faites-vous une analyse de sensibilité ? → Créez un PCE avec PyGPC et extrayez les indices SOBOL analytiquement – aucune évaluation de modèle supplémentaire n’est nécessaire. Si vous préférez la sensibilité basée sur MC, utilisez CUPY pour l’évaluation par lots.

En bout de ligne : Si vous utilisez plus de 100 000 échantillons, l’accélération GPU vaut presque certainement l’effort de migration. Si vous exécutez 10 000 échantillons, la vitesse du GPU peut être marginale (~ 20 ×), et le coût de migration du code peut l’emporter sur l’avantage.


Ce que nous recommandons

Sur la base des données de référence et de l’analyse des bibliothèques, voici notre recommandation pour différents scénarios :

  • Production UQ à l’échelle (100 000 échantillons) : Commencez par Échantillonnage + LHS du POST 537. Il nécessite le moins de migration de code, fournit une accélération de 80 × + et s’intègre à votre base de code NumPy existante.
  • Modélisation de substitution avec PCE : Utilisez PyGPC. C’est la seule bibliothèque Python UQ avec la prise en charge du GPU natif, et le calcul du coefficient PCE bénéficie énormément des cœurs de tenseurs GPU.
  • Inférence bayésienne : Utilisez Jax + Bamojax. L’approche MCMC parallèle par lots documentée par Schmal & Mäder 2026 fournit à la fois des gains d’accélération (gradients GPU) et de précision (amélioration de 5,8 % avec un ensemble 1/3).
  • Modèles personnalisés avec calcul lourd : Utilisez Numba @cuda.jit. Comme le post 367 l’a démontré, NUMBA vous donne un accès GPU sans réécriture en C++, et Askar et al. 2024 a confirmé qu’il surpassait CUPY lorsque le transfert de données est minime.

Prochaines étapes

Si vous débutez dans le calcul accéléré par GPU, commencez par POST 367 « Programmation du noyau GPU pour la simulation physique personnalisée » pour comprendre le paysage CUPY/Numba/GPU. Appliquez ensuite ce que vous avez appris ici aux méthodes UQ couvertes par les méthodes post-537 « Monte Carlo UQ » et post 479 « Quantification de l’incertitude et analyse de sensibilité ».

La pile UQ accélérée par GPU est suffisamment mature pour une utilisation en production. Le support CUDA natif de PyGPC, la différenciation automatique de Jax et la compilation JIT de Numba ont tous des interfaces et des communautés actives bien documentées. Commencez par une course pilote (10 000 à 50 000 échantillons) sur votre GPU pour mesurer l’accélération, puis passer aux volumes de production.


Guides connexes