J'ai un problème d'échantillonnage simple, où ma boucle intérieure ressemble à:
v = sample_gamma(k, a)
où des sample_gamma
échantillons de la distribution Gamma pour former un échantillon de Dirichlet.
Cela fonctionne bien, mais pour certaines valeurs de k / a, certains des calculs en aval sont sous-jacents.
Je l'ai adapté pour utiliser des variables d'espace de log:
v = log(sample_gamma(k, a))
Après avoir adapté tout le reste du programme, il fonctionne correctement (au moins, il me donne les mêmes résultats exacts sur les cas de test). Cependant, il est plus lent qu'auparavant.
Existe-t-il un moyen d'échantillonner directement sans utiliser de fonctions lentes comme ? J'ai essayé de googler pour cela, mais je ne sais même pas si cette distribution a un nom commun (log-gamma?).log ( )
sampling
gamma-distribution
luispedro
la source
la source
Réponses:
Considérons un petit paramètre de forme près de 0, tel que . Dans la plage comprise entre 0 et , est d'environ , donc le pdf Gamma est d'environ . Cela peut être intégré à un CDF approximatif, . En l'inversant, nous voyons une puissance : un énorme exposant. Pour cela entraîne un risque de sous-dépassement (une valeur de double précision inférieure à , plus ou moins). Voici un graphique de la chance d'obtenir un dépassement de capacité en fonction du logarithme en base dix deα = 1 / 100 α e - α 1 x α - 1 d x / Γ ( α ) F de ( la x ) = x αα α=1/100 α e−α 1 xα−1dx/Γ(α) 1/αα=1/10010-300αFα(x)=xααΓ(α) 1/α α=1/100 10−300 α :
Une solution consiste à exploiter cette approximation pour générer des variables log (Gamma): en effet, essayez de générer une variable Gamma et si elle est trop petite, générez son logarithme à partir de cette distribution de puissance approximative (comme illustré ci-dessous). (Effectuez cette opération à plusieurs reprises jusqu'à ce que le journal se trouve dans la plage de sous-dépassement, afin qu'il soit un substitut valide pour la variable de sous-flux d'origine.) Pour le calcul de Dirichlet, soustrayez le maximum de tous les logarithmes de chacune des valeurs de journal: cela redimensionne implicitement tous le gamma varie donc il n'affectera pas les valeurs de Dirichlet. Traitez tout journal résultant qui est trop petit (par exemple, moins de -100) comme étant le journal d'un vrai zéro. Exponentiate les autres journaux. Vous pouvez maintenant continuer sans débordement.
Cela va prendre encore plus de temps qu'auparavant, mais au moins ça marchera!
Pour générer une variable Gamma logarithmique approximative avec le paramètre de forme , précalculez . C'est facile, car il existe des algorithmes pour calculer directement les valeurs du journal Gamma . Générer un flotteur aléatoire uniforme entre 0 et 1, prendre son logarithme, diviser par , et ajouter à elle.C = log ( Γ ( α ) ) + log ( α ) α Cα C=log(Γ(α))+log(α) α C
Étant donné que le paramètre d'échelle ne fait que redimensionner la variable, il n'y a aucun problème à l'adapter dans ces procédures. Vous n'en avez même pas besoin si tous les paramètres d'échelle sont identiques.
Éditer
Dans une autre réponse, l'OP décrit une méthode dans laquelle la puissance d'une variable uniforme (une variable ) est multipliée par une variable . Cela fonctionne car le pdf de la distribution conjointe de ces deux variables est égal à . Pour trouver le pdf de nous substituons , divisons par le jacobéen et intégrons . L'intégrale doit aller de à car , d'où1/α B(α) Γ(α+1) (αxα−1)(yαe−ydy/Γ(α+1)) z=xy y→z/x x x z ∞ 0≤y≤1
qui est le pdf d'une distribution .Γ(α)
Le fait est que lorsque , une valeur tirée de est peu susceptible de dépasser et en sommant son log et fois le log d'une variable uniforme indépendante, nous aura le journal d'une variable . Le journal est probablement très négatif, mais nous aurons contourné la construction de son antilog, qui débordera dans une représentation en virgule flottante.0<α<1 Γ(α+1) 1/α Γ(α)
la source
Je réponds à ma propre question, mais j'ai trouvé une assez bonne solution, même si je ne la comprends pas bien. En regardant le code de la bibliothèque scientifique GNU, voici comment il échantillonne les variables gamma (α β
r
est le générateur de nombres aléatoires,a
est et est ):b
gsl_ran_gamma
est la fonction qui retourne un échantillon aléatoire gamma (donc ce qui précède est un appel récursif), tandis quegsl_rng_uniform_pos
retourne un nombre uniformément distribué en (le est pour strictement positif car il est garanti de ne pas retourner 0,0)._pos
Par conséquent, je peux prendre le journal de la dernière expression et utiliser
Pour obtenir ce que je voulais. J'ai maintenant deux1/a 1/a
log()
appels (mais un de moinspow()
), mais le résultat est probablement meilleur. Avant, comme l'a souligné Whuber, j'avais quelque chose élevé à la puissance de , potentiellement un nombre énorme. Maintenant, dans l'espace de journal, je multiplie par . Il est donc moins susceptible de déborder.1 / ala source