J'essaie de mettre en œuvre une factorisation matricielle non négative en utilisant la divergence de Kullback-Liebler comme mesure de similarité. L'algorithme est décrit dans: http://hebb.mit.edu/people/seung/papers/nmfconverge.pdf. Voici ma mise en œuvre python/numpy, avec un exemple de matrice pour l'exécuter. En un mot, l'algorithme est supposé apprendre les matrices W (n par r) et H (r par m) de sorte que V (n par m) soit approximativement WH. Vous commencez avec des valeurs aléatoires dans W et H, et en suivant les règles de mise à jour décrites dans le document Seung and Lee, vous êtes supposé vous approcher de plus en plus des bonnes approximations pour W et H.La factorisation matricielle non négative ne converge pas
réduire de façon monotone la mesure de divergence, mais ce n'est pas ce qui se passe dans ma mise en œuvre. Au lieu de cela, il s'installe dans une alternance entre deux valeurs de divergence. Si vous regardez W et H, vous pouvez voir que la factorisation résultante n'est pas particulièrement bonne. Je me suis demandé si utiliser l'ancien H mis à jour lors du calcul de la mise à jour pour W. J'ai essayé dans les deux sens, et cela ne change pas le comportement de l'implémentation.
J'ai vérifié mon implémentation contre le papier plusieurs fois, et je ne vois pas ce que je fais de mal. Quelqu'un peut-il faire la lumière sur la question?
import numpy as np
def update(V, W, H, r, n, m):
n,m = V.shape
WH = W.dot(H)
# equation (5)
H_coeff = np.zeros(H.shape)
for a in range(r):
for mu in range(m):
for i in range(n):
H_coeff[a, mu] += W[i, a] * V[i, mu]/WH[i, mu]
H_coeff[a, mu] /= sum(W)[a]
H = H * H_coeff
W_coeff = np.zeros(W.shape)
for i in range(n):
for a in range(r):
for mu in range(m):
W_coeff[i, a] += H[a, mu] * V[i, mu]/WH[i, mu]
W_coeff[i, a] /= sum(H.T)[a]
W = W * W_coeff
return W, H
def factor(V, r, iterations=100):
n, m = V.shape
avg_V = sum(sum(V))/n/m
W = np.random.random(n*r).reshape(n,r)*avg_V
H = np.random.random(r*m).reshape(r,m)*avg_V
for i in range(iterations):
WH = W.dot(H)
divergence = sum(sum(V * np.log(V/WH) - V + WH)) # equation (3)
print "At iteration " + str(i) + ", the Kullback-Liebler divergence is", divergence
W,H = update(V, W, H, r, n, m)
return W, H
V = np.arange(0.01,1.01,0.01).reshape(10,10)
W, H = factor(V, 6)
Oui, vous avez raison recalcul WH = W.dot (H) élimine l'effet d'alternance. Le reste de vos commentaires sont également extrêmement utiles. Je vous suis reconnaissant pour votre réponse détaillée et claire. –
Aargh. J'avais travaillé sur ceci aussi, et ai tiré des conclusions semblables. Aurait dû savoir mieux. ; ^) La seule chose qui pourrait être utile est la version 'einsum', par ex. 'H_coeff = np.einsum ('ia, im, im-> am', W, V, 1.0/W.dot (H))/W.sum (axe = 0, keepdims = True) .T; H = H * H_coeff; W_coeff = np.einsum ('am, im, im-> ia', H, V, 1.0/W.dot (H))/H.sum (axe = 1, keepdims = True) .T; W = W * W_coeff', au motif que 'einsum' est vraiment utile pour confirmer que vous avez entré une formule de la même façon que c'est écrit. – DSM
@DSM: Merci pour cela; J'ai besoin de mieux connaître 'np.einsum'. – unutbu