[PYTHON] J'ai essayé de refaire la factorisation matricielle non négative (NMF)

C'est un article que j'ai essayé de réimplémenter et de confirmer la décomposition en facteurs matriciels non négatifs (NMF) que j'ai compris il y a environ un an. Support Vector Machine (SVM) et Deep Learning sont populaires ces jours-ci, mais je voudrais vous présenter qu'il existe également une telle méthode ... Je l'ai implémenté en C ++ et Python 2.7 pour la première fois depuis longtemps.

[Mis à jour le 05/02/2020]

J'ai créé une maquette pour la séparation des sources sonores en temps réel à l'aide de NMF. Ici

[Mis à jour le 23 mars 2018]

Certains textes ont été révisés. De plus, nous l'avons également implémenté dans Julia, alors j'ai posté le lien là-bas.

Environnement de développement

Mac OS X 10.11.6 Elcapitan

Python 2.7.11 (je dois bientôt passer au système 3 ...)

numpy 1.11.1

cmake 3.6.2

Factorisation matricielle non négative

NMF est un algorithme qui approche la matrice d'élément de valeur non négative (> = 0) V par le produit des deux autres matrices de valeur non négative W et H. Cet algorithme lui-même est utilisé comme méthode d'extraction de caractéristiques, de traitement du langage naturel et de clustering.

De plus, en raison de sa valeur non négative, il en existe également un qui utilise le spectre de puissance dans le traitement du signal acoustique, et il est également utilisé pour trouver le temps de prononciation du ton d'un instrument spécifique à partir du spectrogramme.

Expression de formule

La formule NMF pour la matrice sous i * j est


k \in R \\

V \approx WH\\

V \in R^{i \times j}\\

W \in R^{i \times k} \qquad H \in R^{k \times j}

Il devient.

Puisque le contenu de la matrice d'entrée V est connu, mais que le contenu de W et H est inconnu, NMF minimise la distance entre V et WH.

Mettre à jour l'algorithme

Un algorithme bien connu (et simple) dans NMF est l'algorithme de mise à jour du multiplicateur. Comme mentionné ci-dessus, la distance (erreur) entre X et WH doit être définie et minimisée. Il existe trois types de distances utilisées dans NMF que je connais.

D_{EUC}(V, WH) = (V-WH)^2
D_{KL}(V, WH) = V log \frac{V}{WH} - V + WH

https://en.wikipedia.org/wiki/Itakura–Saito_distance

D_{IS}(V,WH) = \frac{V}{WH} - log \frac{V}{WH} -1

Il existe également une divergence β, qui exprime ces trois fonctions de distance. Utilisez ces algorithmes comme algorithme de mise à jour du multiplicateur. La dérivation et la transformation des expressions détaillées sont omises.

La formule de mise à jour après transformation est


H_{n+1} \gets H_{n} \frac{W^{T}_{n}V_{n}}{W^{T}_{n}W_{n}H_{n}}, \qquad W_{n+1} \gets W_{n} \frac{V_{n}H^{T}_{n+1}}{W_{n}H_{n+1}H^{T}_{n+1}}


H_{n+1} \gets H_{n} \frac{W^{T}_{n}\frac{V_{n}}{W_{n}H_{n}}}{W^{T}_{n}}, \qquad W_{n+1} \gets W_{n} \frac{\frac{V_{n}}{W_{n}H_{n+1}}H^{T}_{n+1}}{H^{T}_{n+1}}


H_{n+1} \gets H_{n} \sqrt{\frac{\frac{W_{n}}{W_{n}H_{n}}^{T}\frac{V_{n}}{W_{n}H_{n}}}{\frac{W_{n}}{W_{n}H_{n}}^{T}}}, \qquad W_{n+1} \gets W_{n} \sqrt{\frac{\frac{V_{n}}{W_{n}H_{n+1}}\frac{H_{n+1}}{W_{n}H_{n+1}}^{T}}{\frac{H_{n+1}}{W_{n}H_{n+1}}^{T}}}

Je me suis demandé si c'était comme ça ... (je me souviens un peu. Je suis désolé si j'ai fait une erreur) Ce que vous devez faire attention, c'est que H dans la formule est {n + 1}! Si vous oubliez cela, vous vous retrouverez dans une situation où cela ne converge pas du tout. En répétant cela, la distance entre V et WH sera réduite. ..

Documents de référence / URL

http://papers.nips.cc/paper/1861-algorithms-for-non-negative-matrix-factorization.pdf http://www.mitpressjournals.org/doi/pdf/10.1162/neco.2008.04-08-771

la mise en oeuvre

NMF.py



# coding:utf-8

import numpy as np


class NMF():
    """
Une classe qui fait un simple NMF
    """
    def setParam(self, k, row, column):
        """
        :param k
        :param row:Colonne
        :param column:ligne
        :return:
        """
        self.__k = k
        self.__row = row
        self.__column = column

        self.__dictionary = np.random.random_sample([self.__row, self.__k])
        self.__activation = np.random.random_sample([self.__k, self.__column])

    def setDictionary(self, index, data):
        """
S'il existe un modèle, définissez le modèle (la valeur par défaut est un nombre aléatoire)
        :param index:Index de quel modèle(0 <= index < k)
        :param data:Données de modèle
        :return:
        """
        if index >= self.__k and len(data) != self.__row:
            print "Please NMF.setParam(k,row,column)"
            print "k = " + str(self.__k)
            print "row = " + str(self.__row)
            return

        self.__dictionary[:, index] = np.array(data[:self.__row], np.float32)

    def setAnalyzData(self, data, k):
        """
Appelez cette méthode au tout début
        :param data:Données à décomposer
        :param k
        :return:
        """
        if len(np.shape(data)) == 1:
            self.__data = np.ones([np.shape(data)[0], 1], np.float32)
            self.setParam(k, np.shape(data)[0], 1)
        else:
            self.__data = data
            self.setParam(k, np.shape(data)[0], np.shape(data)[1])


    def separate_euc_with_template(self,iter=200):
        """
Spécifications EUC séparées avec des modèles
        :param iter:
        :return:
        """
        counter = 0

        while counter < iter:
            approx = np.dot(self.__dictionary , self.__activation)

            wh = np.dot(np.transpose(self.__dictionary) , self.__data)
            wt = np.dot(np.transpose(self.__dictionary) , approx)

            bias = wh/wt
            bias[np.isnan(bias)] = 0

            self.__activation = self.__activation * bias
            counter += 1
        return self.__dictionary,self.__activation


    def separate_euc_without_template(self,iter=200):
        """
Spécifications EUC distinctes sans modèle
        :param iter:
        :return:
        """
        counter = 0

        while counter < iter:
            approx = np.dot(self.__dictionary , self.__activation)

            wh = np.dot(np.transpose(self.__dictionary) , self.__data)
            wt = np.dot(np.transpose(self.__dictionary) , approx)

            bias = wh/wt
            bias[np.isnan(bias)] = 0

            self.__activation = self.__activation * bias

            approx = np.dot(self.__dictionary,self.__activation)
            wh = np.dot(self.__data,np.transpose(self.__activation))
            wt = np.dot(approx,np.transpose(self.__activation))

            bias = wh/wt
            bias[np.isnan(bias)] = 0
            self.__dictionary = self.__dictionary * bias
            counter += 1

        return self.__dictionary,self.__activation

La classe qui exécute NMF elle-même est implémentée cette fois comme ceci. En raison de la quantité de texte, seules les spécifications EUC sont répertoriées cette fois. Le code est assez gênant, mais si cela ne vous dérange pas ...

tester

J'ai également créé un code de test (y compris comment l'utiliser), alors je l'ai exécuté.

main.py



#coding:utf-8

import numpy as np

"""
C'est un problème, mais je dois importer ces deux
"""
from NMF import NMF


"""
Script de test
Comment utiliser
"""
if __name__ == "__main__":

    """
Fondamentalement, un k gênant avec un constructeur,row,Vous n'êtes pas obligé de passer la colonne
Je viens de le faire correctement ...
    """
    nmf = NMF()

    """
Après avoir créé le constructeur, appelez-le d'abord!
Où k,row,Puisque la colonne est définie, setDictionary ne fonctionnera que si cela est appelé!
    """
    nmf.setAnalyzData([[1,2,3,4],[2,3,4,5]],k=3)

    """
Définissez le modèle ici
    """
    nmf.setDictionary(0,[0.0,2.0])
    nmf.setDictionary(1,[1.0,6.0])
    nmf.setDictionary(2,[11.0,10.0])

    """
NMF a commencé
Passer l'algorithme et le nombre de mises à jour répétées comme arguments
    """
    dic,act = nmf.separate_euc_with_template(iter=200)
    # dic,act = nmf.separate_euc_without_template(iter=200)
    """
Affichage des résultats
    """
    print "=========================== Dictionary ==========================="
    print dic
    print "=========================== Activation ==========================="
    print act

    """
Vérifiez s'il peut être démonté correctement
    """
    print "=========================== Approx ==========================="
    print np.dot(dic,act)



Dans ce test


V = \begin{pmatrix}
1 & 2 & 3 & 4\\
2 & 3 & 4 & 5
\end{pmatrix}
\qquad k=3

NMF est effectué sur une matrice avec de telles conditions. Pour cette matrice, la matrice de dictionnaire définie par setDictionary ()


W = \begin{pmatrix}
0 & 1 & 11 \\
2 & 6 & 10
\end{pmatrix}

Mettez à jour la matrice d'excitation à l'aide de.

résultats de test

Lorsque vous exécutez le script main.py ...


$ python main.py
=========================== Dictionary ===========================
[[  0.   1.  11.]
 [  2.   6.  10.]]
=========================== Activation ===========================
[[ 0.11776982  0.05902263  0.00976704  0.33375605]
 [ 0.168019    0.20895539  0.24616295  0.1367387 ]
 [ 0.07563464  0.16282224  0.25034882  0.35120557]]
=========================== Approx ===========================
[[ 1.  2.  3.  4.]
 [ 2.  3.  4.  5.]]

Correctement, la matrice d'excitation a été mise à jour de sorte que la distance entre V et WH devienne 0.

Code source

Cette fois, je l'ai omis, y compris la version KL / IS, qui est disponible sur GitHub. De plus, la version C ++ du code source (la structure est presque la même que la version Python), qui est omise cette fois, est également publiée. Cliquez ici pour la version Python (https://github.com/T-Sumida/Simple_NMF) Cliquez ici pour la version C ++ (https://github.com/T-Sumida/NMF_for_cpp) La version de Julia est ici

Finalement···

C'était une bonne critique d'écrire et de formuler ce que j'ai étudié il y a environ un an pour la première fois depuis longtemps.

Comme je l'ai écrit au début, DNN etc. attirent l'attention ces jours-ci, mais il existe aussi un algorithme aussi simple! J'espère que ce sera une introduction.

La prochaine fois, je voudrais présenter un exemple pratique utilisant ceci.

Recommended Posts

J'ai essayé de refaire la factorisation matricielle non négative (NMF)
J'ai essayé la décomposition matricielle non négative (NMF) avec TensorFlow
Factorisation matricielle non négative (NMF) avec scikit-learn
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
J'ai essayé de résumer la commande umask
J'ai essayé de reconnaître le mot de réveil
J'ai essayé de résumer la modélisation graphique.
J'ai essayé d'estimer le rapport de circonférence π de manière probabiliste
J'ai essayé de toucher l'API COTOHA
Visualisation de l'apprentissage NMF (Non-Negative Matrix Factor Decomposition)
J'ai essayé Web Scraping pour analyser les paroles.
J'ai essayé de corriger la forme trapézoïdale de l'image
Qiita Job J'ai essayé d'analyser le travail
LeetCode j'ai essayé de résumer les plus simples
J'ai essayé de mettre en œuvre le problème du voyageur de commerce
Problème de valeur initiale de NMF (Décomposition en facteurs matriciels non négatifs)
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé de déboguer.
J'ai essayé d'entraîner la fonction péché avec chainer
J'ai essayé de représenter graphiquement les packages installés en Python
J'ai essayé de détecter l'iris à partir de l'image de la caméra
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé de prédire le match de la J League (analyse des données)
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé d'approcher la fonction sin en utilisant le chainer
J'ai essayé de mettre Pytest dans la bataille réelle
[Python] J'ai essayé de représenter graphiquement le top 10 des ombres à paupières
J'ai essayé de visualiser les informations spacha de VTuber
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé de simuler la méthode de calcul de la moyenne des coûts en dollars
J'ai essayé d'identifier la langue en utilisant CNN + Melspectogram
J'ai essayé de compléter le graphe de connaissances en utilisant OpenKE
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé de compresser l'image en utilisant l'apprentissage automatique
J'ai essayé de résumer les opérations de chaîne de Python
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai essayé d'implémenter PCANet
J'ai essayé la bibliothèque changefinder!
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé de résumer SparseMatrix
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé de découvrir les grandes lignes de Big Gorilla
J'ai essayé d'introduire l'outil de génération de diagramme blockdiag
J'ai essayé de porter le code écrit pour TensorFlow sur Theano
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai essayé d'analyser les émotions de tout le roman "Weather Child" ☔️
[Première API COTOHA] J'ai essayé de résumer l'ancienne histoire
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'ai essayé de notifier les informations de retard de train avec LINE Notify
J'ai essayé de simuler l'optimisation des publicités à l'aide de l'algorithme Bandit
J'ai essayé d'illustrer le temps et le temps du langage C