Aller au contenu

Comptez les arrivées avec le processus de Poisson

Une API de prédiction reçoit en moyenne 4 requêtes par seconde. Mais « en moyenne » cache beaucoup de choses : certaines secondes, elle n’en reçoit aucune ; d’autres, elle en reçoit 8 ou 10. Pour dimensionner le service, il faut connaître la loi du nombre de requêtes, pas seulement sa moyenne.

Quand les arrivées proviennent d’un grand nombre d’utilisateurs indépendants, qui agissent chacun rarement, un modèle s’impose presque toujours : le processus de Poisson. C’est le processus d’arrivée de référence des files d’attente, et c’est aussi le plus simple des processus de naissance et de mort. Nous suivrons cette API tout au long du chapitre.

Un processus de comptage (Nt)t≥0(N_t)_{t \geq 0} compte le nombre d’événements survenus dans l’intervalle [0,t][0, t] : ici, le nombre de requêtes reçues depuis l’instant 0. Il part de 0, ne prend que des valeurs entières et ne décroît jamais. Le nombre d’événements dans un intervalle ]s,t]]s, t] est l’accroissement Nt−NsN_t - N_s.

instants d’arrivée S₁, S₂, … des requêtesflèches : inter-arrivées T₁, T₂, T₃0,00,51,01,52,02,53,0t (secondes)0,02,55,07,510,012,515,0NₜNₜ : nombre de requêtes reçues dans [0, t]moyenne E[Nₜ] = 4t
Une trajectoire simulée de l'API sur 3 secondes : le compteur N_t monte d'une marche à chaque requête et ne redescend jamais. Il fluctue autour de la droite de moyenne 4t, parfois au-dessus, parfois en dessous.

Un processus de comptage est un processus de Poisson de taux (ou d’intensité) λ>0\lambda > 0 si :

  1. N0=0N_0 = 0 ;
  2. ses accroissements sont indépendants : les nombres d’arrivées sur des intervalles disjoints sont des variables aléatoires indépendantes (ce qui arrive entre 10 h et 10 h 01 ne dit rien sur ce qui arrive entre 11 h et 11 h 01) ;
  3. ses accroissements sont stationnaires : la loi de Nt+s−NsN_{t+s} - N_s ne dépend que de la durée tt, pas de l’instant ss ;
  4. sur un petit intervalle de durée hh, il arrive une requête avec probabilité λh+o(h)\lambda h + o(h) et deux ou plus avec probabilité o(h)o(h) : les arrivées sont isolées.

La dernière condition dit exactement qu’on a un processus de naissance et de mort de naissance pure : λn=λ\lambda_n = \lambda pour tout nn et μn=0\mu_n = 0. Le graphe est une ligne où l’on ne fait que monter :

λ λ λ λ
0 ─────► 1 ─────► 2 ─────► 3 ··· n ─────► n+1 ···

Écrivons les équations du régime transitoire du chapitre 6 avec λn=λ\lambda_n = \lambda et μn=0\mu_n = 0 :

p0′(t)=−λ p0(t),pn′(t)=λ pn−1(t)−λ pn(t)(n≥1).p_0'(t) = -\lambda\, p_0(t), \qquad p_n'(t) = \lambda\, p_{n-1}(t) - \lambda\, p_n(t) \quad (n \geq 1).

La première donne, avec p0(0)=1p_0(0) = 1, p0(t)=e−λtp_0(t) = e^{-\lambda t}. La deuxième, pour n=1n = 1, est une équation linéaire dont la solution avec p1(0)=0p_1(0) = 0 est p1(t)=λt e−λtp_1(t) = \lambda t\, e^{-\lambda t}. En continuant par récurrence (méthode de variation de la constante), on obtient

P(Nt=n)=e−λt (λt)nn!,n=0,1,2,…P(N_t = n) = e^{-\lambda t}\, \frac{(\lambda t)^n}{n!}, \qquad n = 0, 1, 2, \dots

Le nombre d’arrivées sur une durée tt suit une loi de Poisson de paramètre λt\lambda t. Grâce à la stationnarité des accroissements, c’est vrai sur n’importe quel intervalle de longueur tt : Ns+t−NsN_{s+t} - N_s suit la même loi. On en déduit

E[Nt]=λt,Var⁡(Nt)=λt.E[N_t] = \lambda t, \qquad \operatorname{Var}(N_t) = \lambda t.

Le taux λ\lambda est donc le nombre moyen d’événements par unité de temps, ce qui justifie son nom.

01234567891011121314nombre de requêtes dans une seconde0,000,050,100,150,20probabilitécapacité :7 requêtes/s0,0180,195le service absorbe (≤ 7 requêtes)débordement (≥ 8) : 0,051
La loi de Poisson de paramètre 4 : la valeur moyenne n'arrive qu'une seconde sur cinq, et la queue au-delà de 7 (en rose) pèse encore 5 % des secondes.

Regardons maintenant le processus autrement : non plus « combien d’arrivées ? », mais « quand ? ». Notons T1T_1 l’instant de la première requête. Attendre plus de tt secondes, c’est n’avoir reçu aucune requête dans [0,t][0, t] :

P(T1>t)=P(Nt=0)=e−λt.P(T_1 > t) = P(N_t = 0) = e^{-\lambda t}.

T1T_1 suit donc une loi exponentielle de paramètre λ\lambda. Grâce à l’indépendance et à la stationnarité des accroissements, le processus « repart à zéro » après chaque arrivée : les temps inter-arrivées successifs sont indépendants et de même loi E(λ)\mathcal E(\lambda), de moyenne 1/λ1/\lambda (ici 0,25 seconde).

La réciproque est vraie et très utile : si l’on place des événements séparés par des durées indépendantes de loi E(λ)\mathcal E(\lambda), on obtient un processus de Poisson de taux λ\lambda. C’est ainsi qu’on le simule.

L’instant SnS_n de la nn-ième arrivée est la somme de nn exponentielles indépendantes. Sa loi s’appelle la loi d’Erlang de paramètres (n,λ)(n, \lambda) (c’est une loi Gamma), de moyenne n/λn/\lambda. Pour calculer sa fonction de répartition, on utilise l’équivalence {Sn≤t}={Nt≥n}\lbrace S_n \leq t \rbrace = \lbrace N_t \geq n \rbrace : la nn-ième requête est arrivée avant tt si et seulement si on a reçu au moins nn requêtes dans [0,t][0, t].

0,000,250,500,751,001,251,50durée entre deux requêtes (s)01234densitémoyenne1/λ = 0,25 s10 000 inter-arrivées simuléesdensité4e−4t01234instant de la n-ième arrivée (s)01234lois d’Erlang (n, λ = 4)S₁ : moyenne 0,25 sS₂ : moyenne 0,5 sS₄ : moyenne 1 sS₈ : moyenne 2 s
Les durées entre requêtes suivent une exponentielle de moyenne 0,25 s : les petits écarts sont les plus fréquents. L'instant de la n-ième arrivée (loi d'Erlang) se décale à la moyenne n/4 et s'étale quand n grandit.

Deux propriétés rendent le processus de Poisson très commode pour modéliser des réseaux de files d’attente.

Superposition. Si deux flux indépendants sont des processus de Poisson de taux λ1\lambda_1 et λ2\lambda_2, leur réunion est un processus de Poisson de taux λ1+λ2\lambda_1 + \lambda_2. Supposons que l’API reçoive 3 requêtes par seconde du site web et 1 de l’application mobile, de façon indépendante : au total, c’est bien un Poisson de taux 4. C’est la même propriété que « le minimum d’exponentielles est exponentiel » (chapitre 5).

Décomposition. Inversement, si chaque arrivée d’un Poisson de taux λ\lambda est aiguillée vers la destination ii avec probabilité pip_i, indépendamment des autres, alors les flux obtenus sont des processus de Poisson indépendants de taux pi λp_i\,\lambda. Si l’API envoie 70 % des requêtes vers un modèle A et 30 % vers un modèle B (un test A/B, par exemple), le modèle A reçoit un Poisson de taux 2,8 et le modèle B un Poisson de taux 1,2. La probabilité que le modèle B ne reçoive aucune requête pendant une seconde vaut e−1,2≈0,30e^{-1{,}2} \approx 0{,}30.

0,00,51,01,52,02,53,03,54,0t (secondes)site web, λ = 3application, λ = 1API : réunion, λ = 4modèle A (70 %), λ = 2,8modèle B (30 %), λ = 1,2superposition :les taux s’ajoutentdécomposition :chaque requête tiréeau hasard vers A ou B
Superposition : le flux de l'API est la simple réunion des deux flux (taux 3 + 1 = 4). Décomposition : chaque requête est ensuite aiguillée au hasard, ce qui donne deux flux de Poisson plus clairsemés, de taux 2,8 et 1,2.

L’indépendance des deux flux décomposés est un résultat étonnant : savoir que le modèle A vient de recevoir un pic de requêtes ne dit rien sur ce qu’a reçu le modèle B.

La simulation découle directement de la propriété des temps inter-arrivées :

import numpy as np
rng = np.random.default_rng(42)
lam, duree = 4.0, 10_000.0 # 4 requêtes/s pendant 10 000 s
# Inter-arrivées exponentielles de moyenne 1/lam, puis instants d'arrivée cumulés
inter = rng.exponential(1 / lam, size=int(lam * duree * 1.2))
arrivees = np.cumsum(inter)
arrivees = arrivees[arrivees < duree]
# Nombre de requêtes dans chaque seconde
comptes = np.bincount(arrivees.astype(int), minlength=int(duree))
print(comptes.mean(), comptes.var()) # tous deux proches de 4
print((comptes == 0).mean(), (comptes >= 8).mean()) # proches de 0.018 et 0.051
# Décomposition : chaque requête va vers A avec probabilité 0.7
vers_A = rng.random(arrivees.size) < 0.7
print(vers_A.sum() / duree) # proche de 2.8 requêtes/s

La dernière vérification est aussi la plus utile sur des données réelles : pour un processus de Poisson, la variance du nombre d’arrivées par intervalle égale sa moyenne. Si vous observez une variance bien plus grande que la moyenne, les arrivées arrivent en rafales (un utilisateur qui envoie un lot de requêtes, un pic à la sortie d’une newsletter) et le modèle de Poisson sous-estimera les pics. De même, si le taux varie au cours de la journée, les accroissements ne sont plus stationnaires : on découpe alors la journée en tranches où le taux est à peu près constant.

05101520requêtes par seconde0,000,050,100,150,20fréquencemoyenne 4,0variance 4,1arrivées de Poisson05101520requêtes par secondemoyenne 4,0variance 12,5arrivées en rafales
Même moyenne, 4 requêtes par seconde, mais pas la même variance : pour un flux de Poisson elle vaut aussi environ 4, alors que des rafales la triplent et produisent des secondes à 15 ou 20 requêtes que le modèle de Poisson jugerait presque impossibles.
  • Un processus de Poisson de taux λ\lambda est un processus de comptage avec N0=0N_0 = 0, des accroissements indépendants et stationnaires, et des arrivées isolées (P(Nh=1)=λh+o(h)P(N_h = 1) = \lambda h + o(h)). C’est un processus de naissance pure.
  • Ns+t−NsN_{s+t} - N_s suit une loi de Poisson de paramètre λt\lambda t : P=e−λt(λt)n/n!P = e^{-\lambda t}(\lambda t)^n/n!, avec moyenne et variance λt\lambda t.
  • Les inter-arrivées sont indépendantes de loi E(λ)\mathcal E(\lambda) ; l’instant de la nn-ième arrivée suit une loi d’Erlang (n,λ)(n, \lambda) de moyenne n/λn/\lambda. On simule un Poisson en cumulant des exponentielles.
  • Superposition : Poisson(λ1\lambda_1) + Poisson(λ2\lambda_2) indépendants = Poisson(λ1+λ2\lambda_1 + \lambda_2). Décomposition aléatoire avec probabilités pip_i : Poisson indépendants de taux piλp_i\lambda.
  • Sur des données réelles, vérifiez que la variance des comptes est proche de leur moyenne et que le taux est stable dans le temps.

Exercice · Tickets au support

facile

Un service de support reçoit des tickets selon un processus de Poisson de taux 6 par heure.

  1. Quelle est la probabilité de ne recevoir aucun ticket pendant 20 minutes ?
  2. Combien de tickets reçoit-on en moyenne sur une journée de 8 heures ? Avec quel écart-type ?
  3. Quelle est la durée moyenne entre deux tickets ?
Voir la solution
  1. Sur 20 minutes, soit 1/31/3 d’heure, le nombre de tickets suit une loi de Poisson de paramètre 6×13=26 \times \tfrac13 = 2. La probabilité de n’en recevoir aucun vaut e−2≈0,135e^{-2} \approx 0{,}135.
  2. Sur 8 heures, le paramètre vaut 6×8=486 \times 8 = 48 : 48 tickets en moyenne, avec une variance de 48, donc un écart-type de 48≈6,9\sqrt{48} \approx 6{,}9.
  3. Les inter-arrivées sont exponentielles de moyenne 1/61/6 d’heure, soit 10 minutes.

En code

import numpy as np
from scipy.stats import poisson
taux = 6 # tickets par heure
print(poisson.pmf(0, taux / 3)) # 0.1353 : aucun ticket en 20 minutes
jour = poisson(taux * 8) # journée de 8 heures
print(jour.mean(), jour.std()) # 48.0 6.93
print(60 / taux) # 10.0 minutes entre deux tickets

Exercice · Test A/B sur une API

moyen

L’API du chapitre reçoit un Poisson de taux 4 requêtes par seconde, aiguillées à 70 % vers le modèle A et à 30 % vers le modèle B.

  1. Quelle est la probabilité que le modèle A reçoive au moins une requête pendant une seconde donnée ?
  2. Quelle est la probabilité que l’API reçoive au plus 5 requêtes en 2 secondes ?
  3. Pendant une seconde, le modèle A a reçu 6 requêtes, bien plus que sa moyenne. Quelle est la loi du nombre de requêtes reçues par B pendant cette même seconde ?
Voir la solution
  1. A reçoit un Poisson de taux 2,8 : P(au moins une)=1−e−2,8≈0,939P(\text{au moins une}) = 1 - e^{-2{,}8} \approx 0{,}939.
  2. Sur 2 secondes, le nombre total suit une loi de Poisson de paramètre 8. P(N≤5)=e−8∑k=058k/k!≈0,191P(N \leq 5) = e^{-8}\sum_{k=0}^{5} 8^k/k! \approx 0{,}191.
  3. Par la propriété de décomposition, les flux vers A et vers B sont indépendants. Le nombre de requêtes de B suit donc toujours une loi de Poisson de paramètre 1,2, quoi qu’ait reçu A.

En code

import numpy as np
from scipy.stats import poisson
print(1 - poisson.pmf(0, 0.7 * 4)) # 0.9392 : A reçoit au moins une requête
print(poisson.cdf(5, 4 * 2)) # 0.1912 : au plus 5 requêtes en 2 s
# Indépendance des flux décomposés : simulation seconde par seconde
rng = np.random.default_rng(0)
total = rng.poisson(4, 200_000)
vers_A = rng.binomial(total, 0.7)
vers_B = total - vers_A
print(np.corrcoef(vers_A, vers_B)[0, 1]) # -0.0005, corrélation nulle
print(vers_B[vers_A == 6].mean(), vers_B.mean()) # 1.195 1.201 : B garde sa moyenne 1.2

Exercice · L'instant de la troisième requête

difficile

Toujours avec λ=4\lambda = 4 requêtes par seconde, on note S3S_3 l’instant d’arrivée de la troisième requête.

  1. Quelle est sa moyenne ?
  2. Calculez P(S3≤1)P(S_3 \leq 1), la probabilité que la troisième requête arrive dans la première seconde.
  3. Le serveur met 1 seconde à démarrer et ne commence à traiter qu’à partir de là. Quelle est la probabilité qu’au moins trois requêtes l’attendent à son démarrage ?
Voir la solution
  1. S3S_3 est la somme de trois inter-arrivées exponentielles de moyenne 0,25 s : E[S3]=3/4=0,75E[S_3] = 3/4 = 0{,}75 s.
  2. On utilise {S3≤1}={N1≥3}\lbrace S_3 \leq 1 \rbrace = \lbrace N_1 \geq 3 \rbrace, avec N1N_1 de loi de Poisson de paramètre 4 :
P(S3≤1)=1−e−4(1+4+422)=1−13 e−4≈0,762.P(S_3 \leq 1) = 1 - e^{-4}\left(1 + 4 + \frac{4^2}{2}\right) = 1 - 13\,e^{-4} \approx 0{,}762.
  1. C’est exactement la même question, formulée autrement : au moins trois requêtes attendent si et seulement si la troisième est arrivée avant 1 s. La probabilité vaut environ 0,762.

En code

import numpy as np
from scipy.stats import gamma, poisson
lam = 4
S3 = gamma(a=3, scale=1 / lam) # loi d'Erlang(3, lam)
print(S3.mean()) # 0.75
print(S3.cdf(1), 1 - poisson.cdf(2, lam), 1 - 13 * np.exp(-4)) # 0.7619 (trois fois)
# Simulation : somme de trois inter-arrivées exponentielles
rng = np.random.default_rng(0)
s3 = rng.exponential(1 / lam, (500_000, 3)).sum(axis=1)
print(s3.mean(), (s3 <= 1).mean()) # 0.7498 0.7621