Ajustement d'un modèle de mélange gaussien par descente de gradient stochastique

8

Je travaille sur un modèle d'apprentissage de catégorie en ligne qui utilise la descente de gradient stochastique pour s'adapter à un modèle de mélange gaussien. Le modèle est basé sur le modèle d'apprentissage en ligne utilisé dans Toscano et McMurray (2010).

Bien que la descente de gradient semble fonctionner assez bien pour estimer les moyennes et les fréquences / probabilités de mélange des catégories, j'ai des problèmes avec l'estimation des covariances des composants du mélange. Les dérivées partielles que j'ai utilisées pour la mise à jour de la descente de gradient proviennent de Petersen & Pedersen (2008) (p. 44)

Commençant par

p(x)=kρkNx(μk,Σk)

Petersen & Pedersen donnent la dérivée partielle par rapport à la matrice de covariance commeΣ

δlnp(X)δΣj=ρjNX(μj,Σj)kρkNX(μk,Σk)12[-Σj-1+Σj-1(X-μj)(X-μj)TΣj-1]

L'étape de descente de gradient pour chaque , telle que je l'ai implémentée en Python, est (c'est une légère simplification et le pour tous les composants est calculé avant d'effectuer la mise à jour):ΣjΔΣ

j.sigma += learning_rate*(G(x)/M(x))*0.5*(-inv(j.sigma) + inv(j.sigma).dot((x-j.mu).dot((x-j.mu).transpose())).dot(inv(j.sigma)))

Où j est un objet représentant la ème composante du mélange et j.sigma et j.mu sont la moyenne et la variance de cette composante. G (x) / M (x) un code qui calculejρjNX(μj,Σj)kρkNX(μk,Σk)

Donc, je me demande s'il y a quelque chose qui ne va pas avec mon code (très probablement) ou si c'est juste une très mauvaise façon d'adapter ce type de modèle lorsque vous traitez des données avec plus de deux dimensions (Voir Toscano & McMurray pour les algorithmes pour univariée et des données bivariées qui fonctionnent vraiment).

références: Toscano, JC et McMurray, B. (2010). Intégration d'indices avec des catégories: pondération des indices acoustiques dans la parole à l'aide d'un apprentissage non supervisé et de statistiques de distribution Sciences cognitives, 34, 434-464.

Petersen & Pederson. The Matrix Cookbook, Version: 14 novembre 2008

phased_chirp
la source

Réponses:

3

En supposant que mus[d]c'est , est , et calcule en effet la probabilité postérieure de la composante étant donné les données , le dégradé lui-même me semble correct. Mais voici certaines choses que j'ai remarquées qui pourraient vous aider à trouver votre problème:μjj.sigmaΣjG(x)/M(x)jx

p(jX)=ρjNX(μj,Σj)kρkNX(μk,Σk),
  • Je m'attendrais à ce que l'accès à la moyenne, la covariance et le calcul de la partie postérieure impliquent tous soit jou d, quelle que soit la variable représentant le composant pour lequel vous souhaitez calculer le gradient dans votre code. Si vous nous dites quoi jet que vous dreprésentez, nous pourrons peut-être vous en dire plus.
  • Si vous G(x)/M(x)accédez j.Sigmaà calculer le postérieur, votre code peut ne pas calculer ce que vous pensez qu'il fait. Il peut être préférable de calculer d'abord tous les gradients de tous les paramètres, puis d'effectuer la mise à jour.
  • La descente de gradient stochastique n'est généralement pas le premier choix pour optimiser les mélanges de gaussiens. Le plus souvent, la maximisation des attentes (ME) est utilisée (voir, par exemple, Bishop, 2007). Même si vous n'utilisez pas EM, vous voudrez peut-être envisager BFGS ou L-BFGS (implémenté dans scipy.optimize) avant d'utiliser SGD. Et même si vous vous en tenez à SGD, vous devriez envisager d'utiliser plusieurs points de données ("lots") à la fois pour estimer le gradient, ou tout au moins inclure un terme de momentum . En examinant brièvement l'article de Toscano et McMurray, je suppose qu'ils ont choisi d'utiliser SGD parce qu'ils étaient intéressés à modéliser l'acquisition de la parole d'une manière biologiquement plus plausible, plutôt que d'obtenir le meilleur ajustement possible, et à le faire en ligne (c'est-à-dire, une donnée point à la fois). Si vous n'en avez pas besoin, mon conseil serait d'utiliser EM.

    (Je viens de réaliser que vous avez spécifiquement demandé un apprentissage en ligne, donc la seule option viable pour vous pourrait être d'ajouter le terme d'élan pour accélérer un peu les choses.)

  • La façon dont vous avez choisi de calculer le gradient est assez inefficace, ce qui ralentira encore l'apprentissage. Vous n'avez peut-être pas vu de résultats raisonnables, car il faut une éternité avant que l'algorithme ne converge vers quelque chose d'intéressant. Voici une façon légèrement meilleure de calculer le gradient:

    sigmaInv = inv(j.sigma)
    dSigma = G(x)/M(x) * 0.5 * (-sigmaInv + numpy.sum(sigmaInv.dot(x - mus[d]) * x))
    

    Il existe encore des moyens d'améliorer encore le calcul du gradient. Par exemple, nous obtenons toujours une direction de remontée valide (mais pas une montée la plus raide) si nous multiplions le gradient par une matrice définie positive (telle que , ce qui simplifierait un peu le gradient). Cela pourrait également fonctionner mieux si nous utilisions une paramétrisation différente de la covariance, comme les facteurs de Cholesky , et calculions plutôt les gradients de ceux-ci.Σj

Lucas
la source
Merci pour les suggestions @Lucas. Désolé pour le code légèrement flou. Cela fait partie d'une fonction plus large que j'ai réécrite afin qu'elle ait un peu plus de sens par elle-même. SigmaInv n'est calculé qu'une seule fois et tous les gradients sont calculés avant la mise à jour. Cela doit être un modèle en ligne pour ce que je fais, donc je ne peux pas utiliser EM. J'ai essayé une version légèrement différente qui utilise la factorisation Cholesky de sigma, mais elle s'est comportée un peu bizarrement.
phased_chirp