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.
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
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.
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.
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
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 ...
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.
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.
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
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