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
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.
Das ganze Bild ist wie folgt.
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.
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)
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.
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.
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.
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