Aller au contenu

Probabilites et statistiques

Dimensionner un système sans probabilites c'est deviner — et les bonnes surprises sont rares en production.


Le coût de l'intuition

L'intuition humaine est mauvaise pour les probabilites. On sous-estime la fréquence des événements rares, on confond correlation et causalite, on tire des conclusions de trop peu de données, et on interprété mal les intervalles de confiance. Un architecte qui dimensionne une infrastructure "à l'oeil" fait confiance a son instinct là où les mathématiques sont disponibles et moins cheres à l'usage.

Les statistiques et les probabilites répondent a des questions concrètes :

  • Combien de serveurs pour absorber 99.9% du trafic prévu ?
  • Mon p99 de latence s'est dégradé — est-ce du bruit ou une vraie régression ?
  • Le test A/B montre 3% d'amélioration du taux de conversion — est-ce significatif avec 500 utilisateurs ?
  • Mon SLO a 99.9% laisse combien de minutes de downtime par mois ?

Espaces probabilises

Définitions de base

Un espace probabilise est un triplet \((\Omega, \mathcal{F}, P)\) ou :

  • Ω est l'espace des résultats (ensemble de tous les résultats possibles d'une expérience)
  • F est une tribu (famille d'événements — sous-ensembles de \(\Omega\) qu'on peut mesurer)
  • P est une mesure de probabilite : \(F \to [0, 1]\) avec \(P(\Omega) = 1\)

Un événement \(A\) est un sous-ensemble de \(\Omega\). \(P(A)\) est sa probabilite.

Propriétés fondamentales :

  • \(P(\emptyset) = 0\), \(P(\Omega) = 1\)
  • \(0 \leq P(A) \leq 1\) pour tout événement \(A\)
  • Si \(A\) et \(B\) sont disjoints : \(P(A \cup B) = P(A) + P(B)\)
  • Formule générale : \(P(A \cup B) = P(A) + P(B) - P(A \cap B)\)
  • Complement : \(P(A^c) = 1 - P(A)\)

Independance

Deux événements \(A\) et \(B\) sont indépendants si \(P(A \cap B) = P(A) \times P(B)\). La survenue de l'un n'affecte pas la probabilite de l'autre.

Attention aux correlations cachees : deux serveurs dans le même rack ne sont pas indépendants vis-à-vis d'une panne d'alimentation. Deux instances dans la même zone de disponibilité ne sont pas indépendantes vis-à-vis d'un incident zonal. La disponibilité d'un système avec \(N\) composants redundants est surestimee si on suppose l'independance quand il y a des modes de panne communs.

\[ \text{Disponibilite naive (fausse) : } 1 - (1 - p)^N \]

Disponibilite reelle (avec correlation \(\rho\)) : elle depend du modele de correlation.

Probabilite conditionnelle

La probabilite de \(A\) sachant \(B\) (conditionnellement a \(B\)) est :

\[ P(A \mid B) = \frac{P(A \cap B)}{P(B)} \quad \text{(définie pour } P(B) > 0\text{)} \]

Interpretation : restreindre l'espace des résultats aux cas où \(B\) est arrive, et mesurer la proportion ou \(A\) est aussi arrive.

Formule de la probabilite totale : si \(B_1, B_2, \ldots, B_n\) forment une partition de \(\Omega\) :

\[ P(A) = \sum_i P(A \mid B_i) \times P(B_i) \]

Théorème de Bayes

Le théorème de Bayes inverse la conditionnalite :

\[ P(B \mid A) = \frac{P(A \mid B) \times P(B)}{P(A)} \]

Ou, avec la formule de probabilite totale au denominateur :

\[ P(B_i \mid A) = \frac{P(A \mid B_i) \times P(B_i)}{\sum_j P(A \mid B_j) \times P(B_j)} \]

Application concrète : filtrage anti-spam bayesien. On veut \(P(\text{spam} \mid \text{mot\_present})\). On dispose de \(P(\text{mot\_present} \mid \text{spam})\) (fréquence du mot dans les spams connus) et \(P(\text{spam})\) (proportion de spams dans l'historique).

Application au diagnostic : une alerte se déclenche. Quelle est la probabilite d'un vrai incident vs un faux positif ?

\[ P(\text{incident} \mid \text{alerte}) = \frac{P(\text{alerte} \mid \text{incident}) \times P(\text{incident})}{P(\text{alerte})} \]

Exemple :

  • \(P(\text{incident}) = 0.01\) (1% du temps il y a vraiment un incident)
  • \(P(\text{alerte} \mid \text{incident}) = 0.95\) (le detecteur capte 95% des incidents)
  • \(P(\text{alerte} \mid \text{pas\_incident}) = 0.10\) (10% de faux positifs)
\[ P(\text{alerte}) = 0.95 \times 0.01 + 0.10 \times 0.99 = 0.0095 + 0.099 = 0.1085 \]
\[ P(\text{incident} \mid \text{alerte}) = \frac{0.95 \times 0.01}{0.1085} \approx 0.088 = 8.8\% \]

Warning

Avec 10% de faux positifs et 1% d'incidence réelle, 91% des alertes sont des faux positifs. C'est pourquoi l'alert fatigue est un problème serieux — un detecteur très sensible dans un contexte de faible incidence généré essentiellement du bruit. La solution n'est pas uniquement d'améliorer la sensibilité, mais de combiner plusieurs signaux indépendants (Bayes avec plusieurs evidences).


Variables aléatoires

Définitions

Une variable aléatoire \(X\) est une fonction \(X : \Omega \to \mathbb{R}\) qui associe une valeur réelle à chaque résultat. Elle est discrète si elle prend un nombre fini ou denomerable de valeurs, continue si elle prend des valeurs dans un intervalle.

Esperance (moyenne théorique) :

  • Discrète : \(E[X] = \sum_x x \times P(X = x)\)
  • Continue : \(E[X] = \int x \times f(x) \, dx\)

Variance (dispersion autour de la moyenne) : $\(\text{Var}(X) = E[(X - E[X])^2] = E[X^2] - (E[X])^2\)$

Ecart-type : \(\sigma = \sqrt{\text{Var}(X)}\) — même unite que \(X\), plus interpretable.

Propriétés utiles :

  • Linearite de l'esperance : \(E[aX + bY] = aE[X] + bE[Y]\) (toujours, même si \(X\) et \(Y\) sont dependants)
  • Si \(X\) et \(Y\) indépendants : \(\text{Var}(X + Y) = \text{Var}(X) + \text{Var}(Y)\)
  • Si \(X\) et \(Y\) indépendants : \(E[X \times Y] = E[X] \times E[Y]\)

Percentiles et quantiles

Le p-ieme percentile est la valeur en dessous de laquelle se trouvent p% des observations. En monitoring :

  • p50 (mediane) : la moitie des requêtes est plus rapide que cette valeur
  • p95 : 95% des requêtes sont plus rapides
  • p99 : 99% des requêtes sont plus rapides
  • p999 : 99.9% des requêtes sont plus rapides

Tip

La moyenne est trompeuse pour les latences. Une distribution bimodale (90% des requêtes en 10ms, 10% en 1000ms) donne une moyenne de 109ms — qui ne décrit ni l'expérience des utilisateurs rapides ni celle des lents. Le p95 et le p99 capturent l'expérience des utilisateurs les plus impactes, qui sont souvent les plus actifs et les plus susceptibles de partir.


Distributions clés

Bernoulli

Une expérience de Bernoulli a deux issues : succès (1) avec probabilite \(p\), échec (0) avec probabilite \(1-p\).

\[ E[X] = p, \quad \text{Var}(X) = p(1-p) \]

Usage : une requête reussit ou échoué, un paquet est perdu ou non, un test est vert ou rouge.

Binomiale \(B(n, p)\)

Nombre de succès en \(n\) expériences de Bernoulli indépendantes de parametre \(p\).

\[ P(X = k) = \binom{n}{k} \times p^k \times (1-p)^{n-k} \]
\[ E[X] = np, \quad \text{Var}(X) = np(1-p) \]

Usage : nombre de requêtes échouées sur \(n\) requêtes totales. Si le taux d'erreur est \(p = 0.01\) et on envoie \(n = 1000\) requêtes, on attend en moyenne 10 erreurs avec un ecart-type de \(\sqrt{1000 \times 0.01 \times 0.99} \approx 3.1\).

Poisson \(P(\lambda)\)

Nombre d'événements dans un intervalle de temps fixe, quand les événements arrivent à un taux moyen \(\lambda\) de façon indépendante.

\[ P(X = k) = \frac{e^{-\lambda} \times \lambda^k}{k!} \]
\[ E[X] = \lambda, \quad \text{Var}(X) = \lambda \]

Usage : nombre de requêtes arrivant par seconde, nombre d'erreurs par heure, nombre de pannes par mois. Si un service reçoit en moyenne 100 req/s (\(\lambda = 100\)), la probabilite de recevoir plus de 130 req/s dans une seconde donnée est \(P(X > 130) \approx 0.1\%\) — utile pour le dimensionnement.

Exponentielle \(\text{Exp}(\lambda)\)

Temps d'attente entre deux événements de Poisson de taux \(\lambda\).

\[ f(x) = \lambda e^{-\lambda x} \quad \text{pour } x \geq 0 \]
\[ E[X] = \frac{1}{\lambda}, \quad \text{Var}(X) = \frac{1}{\lambda^2} \]

Propriété sans mémoire : \(P(X > s+t \mid X > s) = P(X > t)\). Le temps d'attente restant ne dépend pas du temps déjà ecoule.

Usage : temps entre deux requêtes (inter-arrivee), durée de vie d'un composant (modèle exponentiel), temps de service d'une requête simple. Un serveur qui traite en moyenne 200 req/s a un temps de service moyen de \(1/200 = 5\text{ms}\) par requête.

Normale \(\mathcal{N}(\mu, \sigma^2)\)

La distribution gaussienne, en forme de cloche, symetrique autour de \(\mu\).

\[ f(x) = \frac{1}{\sigma\sqrt{2\pi}} \times \exp\!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right) \]
\[ E[X] = \mu, \quad \text{Var}(X) = \sigma^2 \]

Règles empiriques :

  • 68% des valeurs dans \([\mu - \sigma, \mu + \sigma]\)
  • 95% dans \([\mu - 2\sigma, \mu + 2\sigma]\)
  • 99.7% dans \([\mu - 3\sigma, \mu + 3\sigma]\)

Usage : approximation de nombreuses distributions par le théorème central limite, erreurs de mesure, valeurs agregees.

Distributions et cas d'usage en informatique

Distribution Parametres E[X] Usage typique
Bernoulli(p) p ∈ [0,1] p Succès/échec d'une opération
Binomiale(n,p) n, p np Nombre d'échecs sur n essais
Poisson(λ) λ > 0 λ Arrivees de requêtes, pannes
Exponentielle(λ) λ > 0 1/λ Inter-arrivees, temps de service
Normale(μ,σ²) μ, σ² > 0 μ Agregats, erreurs, capacité
Log-normale μ, σ e^(μ+σ²/2) Latences (toujours positives, longue queue)
Pareto α, xₘ αxₘ/(α-1) Tailles de fichiers, trafic internet
Uniforme(a,b) a < b (a+b)/2 Jitter, tirage aléatoire

Note

Les latences ne suivent pas une distribution normale — elles sont toujours positives et ont une queue longue (quelques requêtes très lentes). La distribution log-normale (dont le logarithme suit une loi normale) est souvent un meilleur modèle. C'est pourquoi on utilise des percentiles plutôt que la moyenne : la moyenne d'une distribution a queue longue peut être très éloignée de l'expérience typique.


Loi des grands nombres et théorème central limite

Loi des grands nombres (LGN)

La moyenne empirique d'un échantillon converge vers l'esperance théorique quand la taille de l'échantillon croit.

Si \(X_1, X_2, \ldots, X_n\) sont indépendantes et identiquement distribuées avec \(E[X] = \mu\), alors :

\[ \frac{X_1 + X_2 + \cdots + X_n}{n} \to \mu \quad \text{quand } n \to \infty \]

Conséquence pratique : une métrique mesuree sur un grand volume de données est fiable. Une métrique mesuree sur 10 observations ne l'est pas. Un taux d'erreur calcule sur 100 requêtes a un intervalle de confiance large — sur 10 000 requêtes, il est beaucoup plus précis.

Théorème central limite (TCL)

La somme (ou la moyenne) d'un grand nombre de variables indépendantes de même distribution converge vers une distribution normale, quelle que soit la distribution d'origine.

Si \(X_1, \ldots, X_n\) i.i.d. avec \(E[X] = \mu\) et \(\text{Var}(X) = \sigma^2\), alors :

\[ \frac{X_1 + \cdots + X_n - n\mu}{\sigma\sqrt{n}} \to \mathcal{N}(0, 1) \quad \text{quand } n \to \infty \]

Ce que cela implique :

  • La moyenne d'un échantillon de taille \(n\) a un ecart-type de \(\sigma/\sqrt{n}\) (erreur standard)
  • Pour diviser par deux l'erreur standard, il faut 4 fois plus de données
  • Les tests d'hypothèse et les intervalles de confiance reposent sur cette convergence

Intervalles de confiance et tests d'hypothèse

Intervalle de confiance

Un intervalle de confiance a 95% pour un parametre \(\theta\) est un intervalle \([a, b]\) construit de sorte que si on repetait l'expérience de nombreuses fois, 95% des intervalles construits contiendraient la vraie valeur de \(\theta\).

Pour une proportion \(p\) estimée sur \(n\) observations (cas d'un taux d'erreur) :

\[ \text{IC 95\%} : \hat{p} \pm 1.96 \times \sqrt{\frac{\hat{p} \times (1 - \hat{p})}{n}} \]
import math

def intervalle_confiance_proportion(succes, n, niveau=0.95):
    """Intervalle de Wilson (plus precis que Wald pour les petits n)."""
    p_hat = succes / n
    z = 1.96 if niveau == 0.95 else 2.576  # z pour 95% et 99%

    # Borne de Wilson
    denominateur = 1 + z**2 / n
    centre = (p_hat + z**2 / (2*n)) / denominateur
    marge = z * math.sqrt(p_hat*(1-p_hat)/n + z**2/(4*n**2)) / denominateur

    return centre - marge, centre + marge

# Exemple : 50 erreurs sur 1000 requetes
bas, haut = intervalle_confiance_proportion(50, 1000)
print(f"Taux d'erreur : {50/1000:.1%} [{bas:.2%}, {haut:.2%}]")
# Output : Taux d'erreur : 5.0% [3.76%, 6.56%]
import math

def erreur_standard_moyenne(ecart_type, n):
    """Erreur standard = precision de l'estimation de la moyenne."""
    return ecart_type / math.sqrt(n)

# Si la latence a un ecart-type de 50ms mesure sur 100 requetes :
se = erreur_standard_moyenne(50, 100)
print(f"Erreur standard : {se:.1f}ms")  # 5.0ms

# IC 95% pour la moyenne : moyenne +/- 1.96 * erreur_standard
moyenne = 200  # ms
print(f"IC 95% : [{moyenne - 1.96*se:.0f}ms, {moyenne + 1.96*se:.0f}ms]")
# IC 95% : [190ms, 210ms]

Tests d'hypothèse

Un test d'hypothèse decide si une observation est compatible avec une hypothèse nulle \(H_0\) ou si elle constitue une évidence suffisante pour la rejeter.

Procédure :

  1. Formuler \(H_0\) (hypothèse nulle) et \(H_1\) (alternative)
  2. Choisir un seuil de signification \(\alpha\) (souvent 0.05)
  3. Calculer la statistique de test
  4. Calculer la p-value : probabilite d'observer une statistique aussi extreme sous \(H_0\)
  5. Rejeter \(H_0\) si p-value \(< \alpha\)

Erreurs :

  • Erreur de type I (faux positif) : rejeter \(H_0\) quand elle est vraie (probabilite \(= \alpha\))
  • Erreur de type II (faux negatif) : ne pas rejeter \(H_0\) quand elle est fausse (probabilite \(= \beta\))
  • Puissance du test : \(1 - \beta\) (capacité a détecter un vrai effet)

Warning

Une p-value ne mesure pas la probabilite que \(H_0\) soit vraie. Elle mesure la probabilite d'observer des données aussi extremes si \(H_0\) etait vraie. Un test A/B avec \(p = 0.03\) ne signifie pas que la variante B est meilleure avec 97% de probabilite — il signifie que si B n'etait pas meilleur, on observerait un effet aussi grand dans seulement 3% des cas. La distinction est subtile mais fondamentale.


Applications : capacity planning, SLO, tests A/B

Capacity planning et percentiles

Le dimensionnement d'un système se base sur les percentiles du trafic, pas sur la moyenne.

import numpy as np

def analyser_trafic(observations):
    """Statistiques cles pour le capacity planning."""
    return {
        'moyenne': np.mean(observations),
        'mediane': np.percentile(observations, 50),
        'p95': np.percentile(observations, 95),
        'p99': np.percentile(observations, 99),
        'p999': np.percentile(observations, 99.9),
        'ecart_type': np.std(observations),
        'max': np.max(observations),
    }

# Si on dimensionne pour le p99, on accepte que 1% des requetes
# depassent la capacite nominale. Pour un trafic de 1000 req/s,
# cela represente 10 req/s degradees — acceptable selon le SLO.

Théorie des files d'attente (Little's Law) : \(N = \lambda \times W\)

Ou \(N\) est le nombre moyen de requêtes dans le système, \(\lambda\) est le taux d'arrivee, et \(W\) est le temps moyen de traitement. Si \(\lambda \to \mu\) (capacité du serveur), la file explose. Un serveur a 90% d'utilisation a une latence ~10x supérieure à un serveur a 50% d'utilisation.

\[\text{Utilisation : } \rho = \frac{\lambda}{\mu}\]
\[\text{Temps moyen dans le systeme (M/M/1) : } W = \frac{1}{\mu} \times \frac{1}{1-\rho}\]

Exemple :

  • \(\lambda = 90\) req/s, \(\mu = 100\) req/s, \(\rho = 0.9\)
  • \(W = \frac{1}{100} \times \frac{1}{0.1} = 100\text{ms}\) (vs \(10\text{ms}\) a \(\rho = 0\))

SLO et error budget : une formulation probabiliste

Un SLO de disponibilité a 99.9% signifie :

\[P(\text{systeme\_disponible dans une minute donnée}) = 0.999\]

Le budget d'erreur mensuel est :

\[\text{Budget} = (1 - \text{SLO}) \times \text{duree\_periode} = (1 - 0.999) \times 43\,800 \text{ minutes} = 43.8 \text{ minutes par mois}\]

La probabilite de dépasser le budget d'erreur suit une distribution binomiale. Si le taux de défaillance réel est \(p > (1 - \text{SLO})\), le budget sera dépassé. On peut calculer la probabilite de respecter le SLO selon le taux de défaillance réel :

from scipy.stats import binom

def probabilite_respect_slo(slo, taux_reel, nb_minutes=43800):
    """Probabilite de respecter le SLO sur un mois."""
    budget_minutes = (1 - slo) * nb_minutes
    # P(nombre de pannes <= budget) avec taux_reel
    return binom.cdf(budget_minutes, nb_minutes, 1 - taux_reel)

Tests A/B : combien d'utilisateurs ?

La taille d'échantillon nécessaire pour un test A/B dépend de :

  • L'effet minimal détecter (MDE — minimum detectable effect)
  • Le taux de base (baseline conversion rate)
  • La puissance souhaitee (\(1 - \beta\), typiquement 80%)
  • Le niveau de signification (\(\alpha\), typiquement 5%)
import math
from scipy.stats import norm

def taille_echantillon_ab(
    taux_base: float,
    effet_minimum: float,
    alpha: float = 0.05,
    puissance: float = 0.80
) -> int:
    """Taille d'echantillon par groupe pour un test A/B a deux bras."""
    p1 = taux_base
    p2 = taux_base + effet_minimum

    z_alpha = norm.ppf(1 - alpha / 2)  # Test bilateral
    z_beta = norm.ppf(puissance)

    p_moy = (p1 + p2) / 2

    n = (z_alpha * math.sqrt(2 * p_moy * (1 - p_moy))
         + z_beta * math.sqrt(p1*(1-p1) + p2*(1-p2))) ** 2
    n /= (p2 - p1) ** 2

    return math.ceil(n)

# Exemple : taux de conversion baseline 5%, on veut detecter +1pp
n = taille_echantillon_ab(0.05, 0.01)
print(f"Echantillon necessaire par groupe : {n:,}")
# ~14 744 utilisateurs par groupe (29 488 au total)

Tip

Calculer la taille d'échantillon AVANT de lancer le test, pas après. Un test arrêté dès que p < 0.05 est détecté gonfle le taux de faux positifs (p-hacking). Fixer une durée et une taille au départ, puis ne pas regarder les résultats intermédiaires — ou utiliser des méthodes séquentielles (SPRT, méthodes bayesiennes) qui gèrent correctement le problème des regards multiples.


Formulaire de référence

Concept Formule Usage
Probabilite conditionnelle P(A|B) = P(A∩B)/P(B) Diagnostics, classification
Théorème de Bayes P(B|A) = P(A|B)P(B)/P(A) Mise à jour de croyances
Esperance E[X] = Σ x·P(X=x) Valeur moyenne attendue
Variance Var(X) = E[X²] - E[X]² Dispersion
Erreur standard SE = σ/√n Précision d'une estimation
IC 95% proportion p ± 1.96√(p(1-p)/n) Intervalle autour d'un taux
Little's Law N = λW Queuing theory
Budget erreur (1-SLO) × durée Temps de downtime toléré
p-value P(T ≥ t_obs | H₀) Signification statistique