[PYTHON] Deep Embedded Clustering mit Chainer 2.0

Als ich die Methode zur Komprimierung von Datendimensionen untersuchte, recherchierte und lehrte mich mein Junior die Deep-Learning-Methode "** Deep Embedded Clustering **", mit der gleichzeitig die Komprimierung und das Clustering von Dimensionen gelernt werden. Implementieren wir sie also mit Chainer. Das ist dieser Artikel. Der implementierte Code wird auf Github veröffentlicht. https://github.com/ymym3412/DeepEmbeddedClustering

Was ist Deep Embedded Clustering?

Deep Embedded Clustering ist eine Clustering-Methode, die im Artikel "Unüberwachtes Deep Embedding für die Clustering-Analyse" vorgeschlagen wird. Andere Verfahren zur Dimensionskomprimierung und Clusterbildung sind wie folgt.

--k-bedeutet, gemischtes Gauß-Modell (GMM)

Deep Embedded Clustering im Vergleich zur obigen Methode

Es gibt einen Vorteil wie.

Das Lernen ist grob in zwei Schritte unterteilt.

  1. Vorlernen des neuronalen Netzes
  2. Optimierung von Mapping und Schwerpunkt

Das ganze Bild ist wie folgt. DEC.png

1. Vorlernen des neuronalen Netzes

Verwenden Sie den gestapelten (Entrauschungs-) AutoEncoder, um neuronale Netze vorab zu trainieren. Der Code definiert Layer 1 als Denoising Autoencoder und Stacked AutoEncoder als ChainList.

class DenoisingAutoEncoder(chainer.Chain):
    def __init__(self, input_size, output_size, encoder_activation=True, decoder_activation=True):
        w = chainer.initializers.Normal(scale=0.01)
        super(DenoisingAutoEncoder, self).__init__()
        with self.init_scope():
            self.encoder = L.Linear(input_size, output_size, initialW=w)
            self.decoder = L.Linear(output_size, input_size, initialW=w)
        # encode,Parameter, die das Vorhandensein oder Fehlen einer Aktivierungsfunktion während der Dekodierung bestimmen
        self.encoder_activation = encoder_activation
        self.decoder_activation = decoder_activation


    def __call__(self, x):
        if self.encoder_activation:
            h = F.relu(self.encoder(F.dropout(x, 0.2)))
        else:
            h = self.encoder(F.dropout(x, 0.2))

        if self.decoder_activation:
            h = F.relu(self.decoder(F.dropout(h, 0.2)))
        else:
            h = self.decoder(F.dropout(h, 0.2))
        return h


    def encode(self, x):
        if self.encoder_activation:
            h = F.relu(self.encoder(x))
        else:
            h = self.encoder(x)
        return h


    def decode(self, x):
        if self.decoder_activation:
            h = F.relu(self.decoder(x))
        else:
            h = self.decoder(x)
        return h


class StackedDenoisingAutoEncoder(chainer.ChainList):
    def __init__(self, input_dim):
        #Die Abmessung jeder Ebene ist die Eingabeabmessung gemäß dem Papier->500->500->2000->10
        super(StackedDenoisingAutoEncoder, self).__init__(
            DenoisingAutoEncoder(input_dim, 500, decoder_activation=False),
            DenoisingAutoEncoder(500, 500),
            DenoisingAutoEncoder(500, 2000),
            DenoisingAutoEncoder(2000, 10, encoder_activation=False)
        )

    def __call__(self, x):
        # encode
        models = []
        for dae in self.children():
            x = dae.encode(x)
            models.append(dae)

        # decode
        for dae in reversed(models):
            x = dae.decode(x)
        return x

Das Vorlernen ist ebenfalls in zwei Schritte unterteilt: "Layer-Wise Pretrain", mit dem jede Schicht als Auto Encoder trainiert wird, und "Fine Turing", mit dem das gesamte Netzwerk trainiert wird.

# Layer-Wise Pretrain
print("Layer-Wise Pretrain")
#Holen Sie sich jede Ebene und lernen Sie als AutoEncoder
for i, dae in enumerate(model.children()):
    print("Layer {}".format(i+1))
    train_tuple = tuple_dataset.TupleDataset(train, train)
    train_iter = iterators.SerialIterator(train_tuple, batchsize)
    clf = L.Classifier(dae, lossfun=mean_squared_error)
    clf.compute_accuracy = False
    if chainer.cuda.available and args.gpu >= 0:
        clf.to_gpu(gpu_id)
    optimizer = optimizers.MomentumSGD(lr=0.1)
    optimizer.setup(clf)
    updater = training.StandardUpdater(train_iter, optimizer, device=gpu_id)
    trainer = training.Trainer(updater, (50000, "iteration"), out="mnist_result")
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.PrintReport(['iteration', 'main/loss', 'elapsed_time']))
    #Lernrate für jede 20000 Iteration(lr)1/Auf 10 setzen
    trainer.extend(ChangeLearningRate(), trigger=(20000, "iteration"))
    trainer.run()
    #784 Dimensionen von Trainingsdaten gemäß Training für jede Schicht->500->500->Auf 2000 konvertieren
    train = dae.encode(train).data

# Finetuning
print("fine tuning")
#Kein Ausfall während der Feinabstimmung
with chainer.using_config("train", False):
    train, _ = mnist.get_mnist()
    train, _ = convert.concat_examples(train, device=gpu_id)
    train_tuple = tuple_dataset.TupleDataset(train, train)
    train_iter = iterators.SerialIterator(train_tuple, batchsize)
    model = L.Classifier(model, lossfun=mean_squared_error)
    model.compute_accuracy = False
    if chainer.cuda.available and args.gpu >= 0:
        model.to_gpu(gpu_id)
    optimizer = optimizers.MomentumSGD(lr=0.1)
    optimizer.setup(model)
    updater = training.StandardUpdater(train_iter, optimizer, device=gpu_id)
    trainer = training.Trainer(updater, (100000, "iteration"), out="mnist_result")
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.PrintReport(['iteration', 'main/loss', 'elapsed_time']))
    trainer.extend(ChangeLearningRate(), trigger=(20000, "iteration"))
    trainer.run()

Der hier trainierte Encode-Teil des neuronalen Netzes wird wiederverwendet, da er als Abbildung auf eine niedrigere Dimension der Daten dient.

2. Optimierung von Mapping und Schwerpunkt

Das Merkmal von Deep Embedded Clustering ist, dass es gleichzeitig "Verlagerung von Daten in eine niedrigere Dimension" und "Schwerpunkt des Clusters" lernt. Ein neuronales Netz wird verwendet, um die ursprünglichen Daten $ x_i $ in eine niedrigere Dimension umzuwandeln. Sei $ z_i $ die niedrigdimensionale Version von $ x_i $ und multipliziere $ z_i $ mit k-Mitteln, um den Cluster-Schwerpunkt $ u_j $ zu initialisieren. Neuronale Netzparameter, die $ x_i $ in $ z_i $ und den Cluster-Schwerpunkt $ u_j $ konvertieren, werden trainiert. Das Parametertraining wird durchgeführt, um die KL-Divergenz der beiden unten dargestellten Verteilungen Q und P zu minimieren. Beim unbeaufsichtigten Lernen können die Parameter nicht durch Kreuzvalidierung angepasst werden, sodass $ \ alpha $ auf 1 festgelegt ist.

q_{ij} = \frac{(1 + \|z_i - u_j\|^2 / \alpha)^{-\frac{\alpha+1}{2}}}{\sum_{j^{'}}(1 + \|z_i - u_{j^{'}}\|^2 / \alpha)^{-\frac{\alpha + 1}{2}}} 
p_{ij} = \frac{q^2_{ij} / f_j}{\sum_{j^{'}}q^2_{ij^{'}} / f_{j^{'}}}\\
* Jedoch f_j = \sum_i q_{ij}

Das Training wird fortgesetzt, bis die Anzahl der Daten, deren Clusterzuordnung sich ändert, weniger als 0,1% beträgt. Dieses Mal hatte ich nicht die richtige Fehlerfunktion (glaube ich), also habe ich es selbst gemacht.

class TdistributionKLDivergence(chainer.Function):

    def forward(self, inputs):
        xp = cuda.get_array_module(*inputs)
        z, us = inputs[0], xp.array(inputs[1:], dtype=xp.float32)

        #Eine Matrix, in der die ij-Komponente der euklidische Abstand zwischen zi und uj ist
        dist_matrix = xp.linalg.norm(xp.vstack(chain.from_iterable(map(lambda v: repeat(v, us.shape[0]), z))) - xp.vstack(repeat(us, z.shape[0])), axis= 1).reshape(z.shape[0], us.shape[0])
        q_matrix = (self.tdistribution_kernel(dist_matrix).T / self.tdistribution_kernel(dist_matrix).sum(axis=1)).T
        p_matrix = self.compute_pmatrix(q_matrix)
        kl_divergence = (p_matrix * (xp.log(p_matrix) - xp.log(q_matrix))).sum()
        return xp.array(kl_divergence),


    def backward(self, inputs, grad_outputs):
        xp = cuda.get_array_module(*inputs)
        z, us = inputs[0], xp.array(inputs[1:], dtype=xp.float32)
        gloss, = grad_outputs
        gloss = gloss / z.shape[0]

        # z
        norms = xp.vstack(chain.from_iterable(map(lambda v: repeat(v, us.shape[0]), z))) - xp.vstack(repeat(us, z.shape[0]))
        #ij Komponente(zi-uj)10-dimensionaler Vektor von
        #Form ist({Die Anzahl der Daten},{Schwerpunktnummer},10)
        z_norm_matrix = norms.reshape(z.shape[0], us.shape[0], z.shape[1])

        dist_matrix = xp.linalg.norm(norms, axis= 1).reshape(z.shape[0], us.shape[0])
        q_matrix = (self.tdistribution_kernel(dist_matrix).T / self.tdistribution_kernel(dist_matrix).sum(axis=1)).T
        p_matrix = self.compute_pmatrix(q_matrix)

        #Berechnen Sie den Gradienten von zi und uj
        gz = 2 * ((((p_matrix - q_matrix) * self.tdistribution_kernel(dist_matrix)) * z_norm_matrix.transpose(2,0,1)).transpose(1,2,0)).sum(axis=1) * gloss
        gus = -2 * ((((p_matrix - q_matrix) * self.tdistribution_kernel(dist_matrix)) * z_norm_matrix.transpose(2,0,1)).transpose(1,2,0)).sum(axis=0) * gloss

        g = [gz]
        g.extend(gus)
        grads = tuple(g)
        return grads


    def tdistribution_kernel(self, norm):
        xp = cuda.get_array_module(norm)
        return xp.power((1 + norm), -1)


    def compute_pmatrix(self, q_matrix):
        xp = cuda.get_array_module(q_matrix)
        fj = q_matrix.sum(axis=0)
        matrix = xp.power(q_matrix, 2) / fj
        p_matrix = (matrix.T / matrix.sum(axis=1)).T
        return p_matrix


def tdistribution_kl_divergence(z, us):

    return TdistributionKLDivergence()(z, *us)

Versuche dich zu bewegen

Ich habe versucht, MNIST-Clustering mit dem trainierten Modell durchzuführen. Die Farbe wird für jedes Etikett geändert, und die Schwerpunkte des Clusters werden mit schwarzen Dreiecken dargestellt. 500 Punkte wurden zufällig extrahiert und mit t-SNE zweidimensional komprimiert.

DEC_final.png

Ein Experiment mit MNIST ist in der Arbeit geschrieben, konnte aber nicht so sauber getrennt werden. Besonders "4" "7" "9" sind ziemlich chaotisch. Ich hatte das Gefühl, dass es vom Vorlernen und der Ausgangsposition des Schwerpunkts abhängt, ob er sich sauber teilt.

Am Ende

Dieses Mal haben wir Chainer 2.0 verwendet, um die Deep-Learning-Clustering-Methode "Deep Embedded Clustering" zu implementieren. Ich habe es nicht angesprochen, seit die Version von Chainer auf 2 gestiegen ist, also war es eine gute Studie. Wenn Sie eine GPU verwenden, kann die Berechnungszeit vom Vorlernen bis zur Optimierung von Mapping und Schwerpunkt in etwa 40 Minuten liegen. Abschließend möchte ich meinen Junioren dafür danken, dass sie mich in eine interessante Technik eingeführt haben.

Verweise

Unsupervised Deep Embedding for Clustering Analysis [ICML-Lesesitzung] Unüberwachte Tiefeneinbettung für die Clusteranalyse Chainer: Tutorial für Anfänger Vol.1 Ich habe Stacked Auto-Encoder mit Chainer ausprobiert Chainer Docs-Define your own function

Recommended Posts

Deep Embedded Clustering mit Chainer 2.0
Clustering mit Python-Louvain
Klassifizieren Sie Anime-Gesichter mit tiefem Lernen mit Chainer
Versuchen Sie es mit Chainer Deep Q Learning - Launch
Clustering mit Scikit-Learn (1)
Clustering mit Scikit-Learn (2)
Clustering mit scikit-learn + DBSCAN
Verwenden Sie Tensorboard mit Chainer
DBSCAN (Clustering) mit Scikit-Learn
Versuchen Sie es mit TensorFlow
Testen Sie eingebettete Software mit Google Test
Versuchen Sie, RBM mit Chainer zu implementieren.
Ich habe versucht, mit PyCaret zu clustern
Lernen Sie mit Chainer elliptische Bahnen
Deep Kernel Learning mit Pyro
Versuchen Sie Deep Learning mit FPGA
Seq2Seq (3) ~ CopyNet Edition ~ mit Chainer
Verwendung von Chainer mit Jetson TK1
Neuronales Netz beginnend mit Chainer
Bedingte GAN mit Chainer implementiert
SmoothGrad mit Chainer v2 implementiert
Ein bisschen im Kettenschiff stecken
Generiere Pokemon mit Deep Learning
Einführung in Deep Learning (2) - Versuchen Sie Ihre eigene nichtlineare Regression mit Chainer-
Probieren Sie Deep Learning mit FPGA-Select-Gurken aus
Machen Sie ASCII-Kunst mit tiefem Lernen
Lass uns mit Selene Deep SEA lernen
Versuchen Sie es mit TensorFlow Part 2
[Chainer] Lernen von XOR mit mehrschichtigem Perzeptron
Erste Anime-Gesichtserkennung mit Chainer
Überprüfen Sie die Kniebeugenform mit tiefem Lernen
Kategorisieren Sie Nachrichtenartikel mit Deep Learning
Verwenden von Chainer mit CentOS7 [Umgebungskonstruktion]
Snack-Umsatzprognose mit Deep Learning
Versuchen Sie Common Representation Learning mit Chainer
Bringen Sie Menschen mit Deep Learning zum Lächeln
Seq2Seq (2) ~ Achtung Model Edition ~ mit Chainer
(Python) Deep Learning Library Chainer-Grundlagen Grundlagen