[PYTHON] Compression dimensionnelle par auto-encodeur et analyse des composants principaux

introduction

Nous avons implémenté un auto-encodeur et une analyse des composants principaux comme méthode de compression de dimension. En tant que manuels, ["Deep Learning"](https://www.amazon.co.jp/ Deep Learning-Machine Learning Professional Series-Okaya-Takayuki / dp / 4061529021) et ["First Pattern Recognition"](https: / /www.amazon.co.jp/ La première reconnaissance de formes-Hirai-Yuzo / dp / 4627849710) a été utilisée.

Structure de cet article

Auto-encodeur

** auto encoder ** est un réseau neuronal qui utilise l'entrée comme données d'entraînement et acquiert des fonctionnalités qui représentent bien les données. Il est classé comme apprentissage non supervisé qui n'utilise pas les données des enseignants et est utilisé pour le pré-apprentissage des réseaux de neurones et la détermination des valeurs initiales. Maintenant, passons à l'explication de l'auto-encodeur. Considérez un réseau à trois couches comme indiqué dans la figure ci-dessous. La fonction d'activation est la fonction de mappage constante $ g (a) = a $.

network.png

La valeur de l'unité dans la couche intermédiaire $ z_j $ et la valeur de l'unité dans la couche de sortie $ y_k $ sont obtenues en propageant l'entrée vers l'avant.

\begin{align}
& z_j = \sum_i w_{ji} x_i \\
& \\
& y_k = \sum_j \tilde w_{kj} z_j
\end{align}

Le vecteur avec les valeurs des unités dans la couche du milieu est $ \ boldsymbol z $, et le vecteur avec les valeurs des unités dans la couche de sortie est $ \ boldsymbol y $, Si la matrice avec $ w_ {ji} $ comme composant $ (\ j, i ) $ est $ \ boldsymbol W $, elle peut être exprimée comme suit.

\begin{align}
& \boldsymbol z = \boldsymbol W \boldsymbol x \\
& \\
& \boldsymbol y = \boldsymbol{\tilde W} \boldsymbol z
\end{align}

La conversion vers la couche intermédiaire $ \ boldsymbol z $ est appelée ** encoding **, et la conversion vers la couche de sortie $ \ boldsymbol y $ est appelée ** déchiffrement **. L'auto-encodeur entraîne le $ \ boldsymbol y $ déchiffré pour qu'il soit proche de l'entrée $ \ boldsymbol x $. Si le nombre d'unités dans la couche intermédiaire est inférieur à la dimension d'entrée et que l'entrée peut être reproduite, la dimension peut être compressée. Pour une méthode d'apprentissage détaillée, reportez-vous à "Implémentation d'un réseau neuronal avec python". Puisque la fonction d'activation est une fonction de mappage constante, la valeur différentielle est toujours $ 1 $, ce qui est facile à calculer.

Analyse des composants principaux

** L'analyse en composantes principales (ACP) ** est une transformation linéaire des données d'apprentissage $ \ boldsymbol x_i = (x_ {i1}, ..., x_ {id}) ^ T $ dans le sens de la dispersion maximale. C'est la méthode à trouver. Une matrice de données constituée de $ N $ data est $ \ boldsymbol X = (\ boldsymbol x_1, ..., \ boldsymbol x_N) ^ T $, et un vecteur moyen est $ \ boldsymbol {\ bar x} = (\ bar x_1, ..., \ bar x_d) ^ T $. La matrice de données $ \ boldsymbol {\ bar X} $, qui est la matrice de données moins le vecteur moyen, et la matrice de covariance $ \ boldsymbol \ Sigma $ sont les suivantes.

\begin{align}
& \boldsymbol{\bar X} = (\boldsymbol x_1 - \boldsymbol{\bar x}, ..., \boldsymbol x_N - \boldsymbol{\bar x})^T \\
& \\
& \boldsymbol \Sigma = Var\bigl\{\boldsymbol{\bar X}\bigr\} = \frac{1}{N} \boldsymbol{\bar X}^T \boldsymbol{\bar X}
\end{align}

Si la matrice de données $ \ boldsymbol {\ bar X} $ est convertie linéairement par un certain vecteur de coefficient $ \ boldsymbol a_j $ et $ \ boldsymbol s_j $ est utilisé, la distribution des données converties est la suivante.

\begin{align}
& \boldsymbol s_j = (s_{1j}, ..., s_{Nj})^T \\
& \\
& Var\bigl\{\boldsymbol s_j \bigr\} \varpropto \boldsymbol s_j^T \boldsymbol s_j = \bigl(\boldsymbol{\bar X} \boldsymbol a_j \bigr)^T \boldsymbol{\bar X} \boldsymbol a_j =  \boldsymbol a_j^T \boldsymbol{\bar X}^T \boldsymbol{\bar X} \boldsymbol a_j \varpropto \boldsymbol a_j^T Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j
\end{align}

Le vecteur de projection $ \ boldsymbol a_j $ qui maximise cette variance est obtenu en maximisant la fonction de Lagrange avec la norme contrainte à $ 1 $.

\begin{align}
& E(\boldsymbol a_j) = \boldsymbol a_j^T Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j - \lambda (\boldsymbol a_j^T \boldsymbol a_j - 1) \\
& \\
& \frac{\partial E(\boldsymbol a_j)}{\partial \boldsymbol a_j} = 2 Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j - 2 \lambda \boldsymbol a_j = 0 \\
& \\
& Var\bigl\{\boldsymbol{\bar X}\bigr\} \boldsymbol a_j = \lambda \boldsymbol a_j \tag{*}
\end{align}

L'équation ($ \ ast $) montre qu'en résolvant le problème des valeurs propres pour la matrice de covariance des données d'origine, le vecteur de projection $ \ boldsymbol a_j $ qui maximise la variance peut être obtenu. Soit la valeur propre obtenue en résolvant l'équation ($ \ ast $) $ \ lambda_1 \ geq ... \ geq \ lambda_d $, et le vecteur propre correspondant $ \ boldsymbol a_1, ..., \ boldsymbol a_d $. Puisque la matrice de covariance est une matrice symétrique réelle, les vecteurs propres sont orthogonaux les uns aux autres.

\boldsymbol a_i^T \boldsymbol a_j = \delta_{ij} = \begin{cases}
1 \ ( \ i = j \ ) \\
\\
0 \ ( \ i \ne j \ )
\end{cases}

La quantité caractéristique transformée linéairement par le vecteur propre correspondant à la valeur propre maximale est la composante principale $ 1 $, La quantité de caractéristiques transformée linéairement par le vecteur propre correspondant à la valeur propre $ k $ th est appelée la composante principale $ k $. Le montant de dispersion totale $ V_ {total} $, le taux de cotisation de la composante principale $ k $ $ c_k $ et le taux de cotisation cumulatif $ r_k $ jusqu'à la composante principale $ k $ sont les suivants.

\begin{align}
& V_{total} = \sum_{i = 1}^d \lambda_i \\
& \\
& c_k = \frac{\lambda_k}{V_{total}} \\
& \\
& r_k = \frac{\sum_{i = 1}^k \lambda_i}{V_{total}}
\end{align}

Relation entre les deux méthodes

Sélectionnez $ D_y $ vecteurs propres de $ \ boldsymbol \ Sigma $ dans l'ordre décroissant des valeurs propres, et laissez la matrice qui les stocke en tant que vecteurs de ligne être $ \ boldsymbol U_ {D_y} $. Si le nombre de dimensions des données d'entrée est $ D_x $, les dimensions seront compressées lorsque $ D_y <D_x $. $ \ boldsymbol U_ {D_y} $ et $ \ boldsymbol {\ bar x} $ sont les solutions au problème de minimisation suivant $ (\ boldsymbol \ Gamma, \ boldsymbol \ xi) = (\ boldsymbol U_ {D_y}, \ boldsymbol C'est {\ bar x}) $.

\min_{\boldsymbol \Gamma, \boldsymbol \xi} \sum_{n = 1}^N \bigl\| \ (\boldsymbol x_n - \boldsymbol \xi) - \boldsymbol \Gamma^T \boldsymbol \Gamma(\boldsymbol x_n - \boldsymbol \xi) \ \bigr\|^2 \tag{**}

Dans l'analyse en composantes principales, chaque élément de données dans l'espace dimensionnel $ D_x $ peut être interprété comme donnant le sous-espace dimensionnel $ D_y $ qui représente le mieux la distance la moins carrée. $ \ Boldsymbol W = \ boldsymbol \ Gamma, \ boldsymbol b = - \ boldsymbol \ Gamma \ boldsymbol \ xi, \ boldsymbol {\ tilde W} = \ boldsymbol \ Gamma ^ T, \ Si boldsymbol {\ tilte b} = \ boldsymbol \ xi , la fonction d'erreur du self-encoder correspond à l'équation ( \ ast \ ast $). Autrement dit, dans un auto-encodeur, $ (\ boldsymbol \ Gamma, \ boldsymbol \ xi) = (\ boldsymbol U_ {D_y}, \ boldsymbol {\ bar x}) $ est le paramètre réseau qui minimise la fonction d'erreur. ..

Implémentation en python

Je l'ai implémenté comme suit. L'auto-encodeur et l'analyse des composants principaux sont répertoriés séparément.

auto encoder

La dimension de compression est traitée comme 50 $. Avec un taux d'apprentissage de $ \ varepsilon = 0,00001 $, nous tournons 10000 $ à l'époque.

auto_encoder.py


import numpy

class AutoEncoder:

    def __init__(self, n_input, n_hidden):
        self.hidden_weight = numpy.random.randn(n_hidden, n_input + 1)
        self.output_weight = numpy.random.randn(n_input, n_hidden + 1)

    def fit(self, X, epsilon, epoch):
        self.error = numpy.zeros(epoch)
        N = X.shape[0]
        for epo in range(epoch):
            print u'epoch: %d' % epo
            for x in X:
                self.__update_weight(x, epsilon)

            self.error[epo] = self.__calc_error(X)

    def encode(self, X):
        Z = numpy.zeros((X.shape[0], self.hidden_weight.shape[0]))
        Y = numpy.zeros(X.shape)
        for (i, x) in enumerate(X):
            z, y = self.__forward(x)
            Z[i, :] = z
            Y[i, :] = y

        return (Z, Y)

    def __forward(self, x):
        z = self.hidden_weight.dot(numpy.hstack((1, x)))
        y = self.output_weight.dot(numpy.hstack((1, z)))

        return (z, y)

    def __update_weight(self, x, epsilon):
        z, y = self.__forward(x)

        # update output_weight
        output_delta = y - x
        self.output_weight -= epsilon * output_delta.reshape(-1, 1) * numpy.hstack((1, z))

        # update hidden_weight
        hidden_delta = self.output_weight[:, 1:].T.dot(output_delta)
        self.hidden_weight -= epsilon * hidden_delta.reshape(-1, 1) * numpy.hstack((1, x))

    def __calc_error(self, X):
        N = X.shape[0]
        err = 0.0
        for x in X:
            _, y = self.__forward(x)
            err += (y - x).dot(y - x) / 2.0

        return err

main.py


import numpy
import cv2
from sklearn.datasets import fetch_mldata
from auto_encoder import AutoEncoder

if __name__ == '__main__':

    print 'read data...'
    mnist = fetch_mldata('MNIST original', data_home = '.')
    _max = mnist.data[:, :-1].max()
    X = mnist.data[:, :-1] * 1.0 / _max
    input_size = X.shape[1]
    hidden_size = 50
    epsilon = 0.00001
    epoch = 10000
    stride = 50

    print 'auto encoder init...'
    auto = AutoEncoder(input_size, hidden_size)

    print 'train...'
    auto.fit(X[::stride], epsilon, epoch)

    print 'encode...'
    Z, Y = auto.encode(X[::stride])
    for (i, y) in enumerate(Y * _max):
        cv2.imwrite('result/%04d.png' % i, y.reshape(28, 28))

PCA

La dimension de compression est traitée comme 50 $. Le vecteur de projection et l'image décodée sont enregistrés.

pca.py


import numpy

class PCA:

    def __init__(self, n_components):
        self.n_components = n_components

    def fit(self, X):
        self.bar = self.__bar(X)
        self.cov = self.__cov()
        self.lamda, self.A, self.ccr = self.__solve_eigen_porblem()
        self.S = self.__transformation()

    def decode(self):
        return self.S.dot(self.A.T)

    def __bar(self, X):
        return X - X.mean(axis = 0)

    def __cov(self):
        return numpy.cov(self.bar, rowvar = 0)

    def __solve_eigen_porblem(self):
        _lamda, _A = numpy.linalg.eig(self.cov)
        lamda = _lamda[:self.n_components]
        A = _A[:, :self.n_components]
        ccr = lamda.sum() / _lamda.sum()

        return (lamda, A, ccr)

    def __transformation(self):
        return self.bar.dot(self.A)

main.py


import cv2
from matplotlib import pyplot
from sklearn.datasets import fetch_mldata
import os
from pca import PCA

if __name__ == '__main__':

    print 'read data...'
    mnist = fetch_mldata('MNIST original', data_home = '.')
    X = mnist.data
    n_components = 50
    p_dir = 'projection/'
    d_dir = 'decoded/'

    print 'train...'
    pca = PCA(n_components)
    pca.fit(X)
    print 'cumulative contribution ratio: %f' % pca.ccr

    print 'decode...'
    Xhat = pca.decode()

    print 'save projection vector...'
    if not os.path.exists(p_dir):
        os.mkdir(p_dir)
    for (i, a) in enumerate(pca.A.T):
        fig, ax = pyplot.subplots()
        heatmap = ax.pcolor(a.reshape(28, 28)[:, ::-1], cmap = pyplot.cm.RdYlBu)
        pyplot.savefig(p_dir + '%04d.png' % i, dpi = 25)
        pyplot.clf()

    print 'save decoded images...'
    if not os.path.exists(d_dir):
        os.mkdir(d_dir)
    for (i, xhat) in enumerate(Xhat[::50]):
        cv2.imwrite(d_dir + '%04d.png' % i, xhat.reshape(28, 28))

    n_components = 30
    pca = PCA(n_components)
    pca.fit(X)

    Xhat = pca.A.dot(pca.S.T)
    for (i, xhat) in enumerate(Xhat.T):
        cv2.imwrite('result/%04d.png' % i, xhat.reshape(28, 28))

résultat

Les résultats suivants ont été obtenus.

auto encoder

Image déchiffrée

decode_auto_encoder.png

Erreur

error_log.png

J'ai choisi une belle image pour l'image décodée. La quantité d'entités compressée à une dimension de 50 $ peut être décodée en une dimension de 784 $. Dans le graphique d'erreur, l'axe horizontal correspond au nombre d'époques et l'axe vertical correspond à la valeur d'erreur. La différence entre les chiffres de l'erreur est trop grande, le logarithme est donc pris. Ce n'est pas une erreur exacte, mais vous pouvez voir qu'elle diminue de manière monotone. ..

PCA

Vecteur de projection visualisé

projection_vector.png

Image déchiffrée

decode_pca.png

Pour le vecteur de projection, 10 vecteurs correspondants ont été sélectionnés par ordre décroissant de valeur propre. Le vecteur de projection en haut à gauche se projette sur la première composante principale avec la plus grande dispersion. L'image décodée donne de bons résultats ainsi que l'auto-encodeur.

en conclusion

Nous avons pu compresser les dimensions par auto-encodeur et analyse des composants principaux.

Recommended Posts

Compression dimensionnelle par auto-encodeur et analyse des composants principaux
Filtrage coordonné avec analyse des composants principaux et clustering K-means
Analyse des composants principaux avec Spark ML
Ceci et cela de l'analyse en composantes principales
Traitement du langage 100 knock-85 (SVD tronqué): Compression dimensionnelle par analyse en composantes principales
Analyse des composants principaux avec Livedoor News Corpus --Préparation--
J'ai essayé d'analyser les principaux composants avec les données du Titanic!
Analyse en composantes principales (Analyse en composantes principales: ACP)
Commençons l'analyse multivariée et l'analyse des composants principaux avec Pokemon! Coopération entre R et Tableau
Clustering et analyse en composantes principales par méthode K-means (débutant)
Défiez l'analyse des composants principaux des données textuelles avec Python
Analyse des composants principaux à l'aide de python de nim avec nimpy
Analyse en composants principaux (PCA) et analyse en composants indépendants (ICA) avec python
Apprendre sans enseignant 3 Analyse des principales composantes
Analyse pratique des composants principaux avec PyCaret [Normalisation + visualisation (tracé)] Mémo
Reconnaissance faciale à l'aide de l'analyse des composants principaux
Python: apprentissage non supervisé: analyse principale
Reconnaître le contour et la direction d'un objet façonné avec OpenCV3 et Python3 (analyse des composants principaux: PCA, vecteur propre)
Analyse des tweets avec Python, Mecab et CaboCha
<Cours> Machine learning Chapitre 4: Analyse des principaux composants
[Python] Comparaison de la théorie de l'analyse des composants principaux et de l'implémentation par Python (PCA, Kernel PCA, 2DPCA)
Essayez l'analyse de transfert de chaleur non stationnaire de 1,5 dimension avec Heatrapy
PRML Chapitre 12 Mise en œuvre de l'analyse principale bayésienne Python
Compressez les vecteurs en 2 dimensions à l'aide de l'analyse des composants principaux et visualisez-les avec matplotlib.
2. Analyse multivariée décrite dans Python 3-2. Analyse en composantes principales (algorithme)
Analyse d'images par apprentissage profond à partir de Kaggle et Keras
Compréhension mathématique de l'analyse en composantes principales depuis le début
Analyse en composantes principales Analyser les nombres manuscrits à l'aide de l'ACP. Partie 2
Analyse en composantes principales Analyser les nombres manuscrits à l'aide de l'ACP. Partie 1
2. Analyse multivariée expliquée dans Python 3-1. Analyse en composantes principales (scikit-learn)