Dieser Artikel ist eine Fortsetzung von "CGAN (bedingte GAN) generiert MNIST (KMNIST)". Dies ist ein Datensatz, wenn versucht wird, ACGAN basierend auf cGAN auszuführen.
Ich denke, es ist eine natürliche Idee in Bezug auf die Evolution von cGAN, aber wenn ich es implementiere, ist es ziemlich ...
Da ich cGAN im vorherigen Artikel kurz vorgestellt habe, werde ich ACGAN kurz erläutern. ACGAN ist kurz gesagt ** "cGAN, bei dem Discriminator auch Klassifizierungsaufgaben ausführt" **. Diese Methode ermöglicht die Ausgabe von Bildern mit mehr Variationen.
Das Originalpapier ist [hier](Bedingte Bildsynthese mit Hilfsklassifikator-GANs)
A. Odena, C. Olah, J. Shlens. Conditional Image Synthesis With Auxiliary Classifier GANs. CVPR, 2016
In Bezug auf die Papiere von ACGAN haben einige Leute die Originalpapiere veröffentlicht, so dass dies hilfreich sein wird.
Referenzartikel
AC-GAN-Papierkommentar (Conditional Image Synthesis with Auxiliary Classifier GANs)
In cGAN wurden die Original- / Fälschungsbild- und Etiketteninformationen in Discriminator eingegeben und die Identifizierung von Original- oder Fälschungsinformationen ausgegeben. Andererseits ist in ACGAN die Eingabe von Discriminator nur ein Bild und nicht nur die Identifizierung von echt oder falsch, sondern auch das Klassenurteil, um zu erraten, welche Klasse der Ausgabe hinzugefügt wird. In einem Diagramm sieht es wie folgt aus.
Der "Klasse" -Teil in der Figur ist die Ausgabe der von Discriminator vorhergesagten Klassifizierung. Wie label
hat es die Form eines Klassennummern-Dimensionsvektors.
ACGAN hat PyTorch-Implementierung auf GitHub. Lassen Sie uns als Referenz die Implementierung von cGAN ändern, die ich im vorherigen Artikel geschrieben habe.
Was ist zu tun
Ist fast alles. Dann sieht die Struktur von Discriminator so aus. Dies ist eine Zeichnung des Strukturdiagramms des cGAN-Diskriminators, das im vorherigen Artikel veröffentlicht wurde. Der rot dargestellte Teil ist jedoch die Änderung in ACGAN.
Eine Implementierung von Discriminator.
python
class Discriminator(nn.Module):
def __init__(self, num_class):
super(Discriminator, self).__init__()
self.num_class = num_class
self.conv = nn.Sequential(
nn.Conv2d(1, 64, kernel_size=4, stride=2, padding=1), #Eingang ist 1 Kanal(Weil es schwarz und weiß ist),Anzahl der Filter 64,Filtergröße 4*4
nn.LeakyReLU(0.2),
nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1),
nn.LeakyReLU(0.2),
nn.BatchNorm2d(128),
)
self.fc = nn.Sequential(
nn.Linear(128 * 7 * 7, 1024),
nn.BatchNorm1d(1024),
nn.LeakyReLU(0.2),
)
self.fc_TF = nn.Sequential(
nn.Linear(1024, 1),
nn.Sigmoid(),
)
self.fc_class = nn.Sequential(
nn.Linear(1024, num_class),
nn.LogSoftmax(dim=1),
)
self.init_weights()
def init_weights(self):
for module in self.modules():
if isinstance(module, nn.Conv2d):
module.weight.data.normal_(0, 0.02)
module.bias.data.zero_()
elif isinstance(module, nn.Linear):
module.weight.data.normal_(0, 0.02)
module.bias.data.zero_()
elif isinstance(module, nn.BatchNorm1d):
module.weight.data.normal_(1.0, 0.02)
module.bias.data.zero_()
elif isinstance(module, nn.BatchNorm2d):
module.weight.data.normal_(1.0, 0.02)
module.bias.data.zero_()
def forward(self, img):
x = self.conv(img)
x = x.view(-1, 128 * 7 * 7)
x = self.fc(x)
x_TF = self.fc_TF(x)
x_class = self.fc_class(x)
return x_TF, x_class
Es scheint verschiedene Möglichkeiten zu geben, die Ausgabe der Klassifizierung hinzuzufügen. In der PyTorch-Implementierung des Links, den ich zuvor gepostet habe, wurde die Ebene "Linear" am Ende gegabelt, daher implementiere ich sie hier auf die gleiche Weise.
Entsprechend dieser Änderung sieht die Funktion pro Epoche so aus.
python
def train_func(D_model, G_model, batch_size, z_dim, num_class, TF_criterion, class_criterion,
D_optimizer, G_optimizer, data_loader, device):
#Trainingsmodus
D_model.train()
G_model.train()
#Das echte Label ist 1
y_real = torch.ones((batch_size, 1)).to(device)
D_y_real = (torch.rand((batch_size, 1))/2 + 0.7).to(device) #Geräuschetikett zum Einfügen von D.
#Gefälschtes Etikett ist 0
y_fake = torch.zeros((batch_size, 1)).to(device)
D_y_fake = (torch.rand((batch_size, 1)) * 0.3).to(device) #Geräuschetikett zum Einfügen von D.
#Initialisierung des Verlustes
D_running_TF_loss = 0
G_running_TF_loss = 0
D_running_class_loss = 0
D_running_real_class_loss = 0
D_running_fake_class_loss = 0
G_running_class_loss = 0
#Chargenweise Berechnung
for batch_idx, (data, labels) in enumerate(data_loader):
#Ignorieren, wenn weniger als die Stapelgröße
if data.size()[0] != batch_size:
break
#Geräuschentwicklung
z = torch.normal(mean = 0.5, std = 1, size = (batch_size, z_dim)) #Durchschnitt 0.Generieren Sie Zufallszahlen nach einer Normalverteilung von 5
real_img, label, z = data.to(device), labels.to(device), z.to(device)
#Diskriminator-Update
D_optimizer.zero_grad()
#Setzen Sie ein reales Bild in Discriminator und verbreiten Sie vorwärts ⇒ Verlustberechnung
D_real_TF, D_real_class = D_model(real_img)
D_real_TF_loss = TF_criterion(D_real_TF, D_y_real)
CEE_label = torch.max(label, 1)[1].to(device)
D_real_class_loss = class_criterion(D_real_class, CEE_label)
#Fügen Sie das durch Einfügen von Rauschen in Generator in Discriminator erzeugte Bild ein und verbreiten Sie es vorwärts ⇒ Verlustberechnung
fake_img = G_model(z, label)
D_fake_TF, D_fake_class = D_model(fake_img.detach()) #fake_Stop Loss wird in Bildern berechnet, damit es nicht zurück zum Generator übertragen wird
D_fake_TF_loss = TF_criterion(D_fake_TF, D_y_fake)
D_fake_class_loss = class_criterion(D_fake_class, CEE_label)
#Minimieren Sie die Summe von zwei Verlusten
D_TF_loss = D_real_TF_loss + D_fake_TF_loss
D_class_loss = D_real_class_loss + D_fake_class_loss
D_TF_loss.backward(retain_graph=True)
D_class_loss.backward()
D_optimizer.step()
D_running_TF_loss += D_TF_loss.item()
D_running_class_loss += D_class_loss.item()
D_running_real_class_loss += D_real_class_loss.item()
D_running_fake_class_loss += D_fake_class_loss.item()
#Generator-Update
G_optimizer.zero_grad()
#Das Bild, das durch Einfügen von Rauschen in den Generator erzeugt wird, wird in den Diskriminator eingefügt und vorwärts weitergegeben. ⇒ Der erkannte Teil wird zu Verlust
fake_img_2 = G_model(z, label)
D_fake_TF_2, D_fake_class_2 = D_model(fake_img_2)
#G Verlust(max(log D)Optimiert mit)
G_TF_loss = -TF_criterion(D_fake_TF_2, y_fake)
G_class_loss = class_criterion(D_fake_class_2, CEE_label) #Aus Gs Sicht wäre es schön, wenn er D für real halten und ihm eine Klasse geben würde.
G_TF_loss.backward(retain_graph=True)
G_class_loss.backward()
G_optimizer.step()
G_running_TF_loss += G_TF_loss.item()
G_running_class_loss -= G_class_loss.item()
D_running_TF_loss /= len(data_loader)
D_running_class_loss /= len(data_loader)
D_running_real_class_loss /= len(data_loader)
D_running_fake_class_loss /= len(data_loader)
G_running_TF_loss /= len(data_loader)
G_running_class_loss /= len(data_loader)
return D_running_TF_loss, G_running_TF_loss, D_running_class_loss, G_running_class_loss, D_running_real_class_loss, D_running_fake_class_loss
Zusätzlich zu den zuvor erwähnten Änderungen habe ich auch das hinzuzufügende Rauschen geändert. Beim letzten Mal war es eine Normalverteilung mit 30 Dimensionen, Durchschnitt 0,5 und Standardabweichung 0,2, aber diesmal ist es eine Normalverteilung mit 100 Dimensionen, Durchschnitt 0,5 und Standardabweichung 1.
Der Klassifizierungsverlust ist "torch.nn.NLLLoss ()". Dies stimmte auch mit der früheren Implementierung des Links überein.
Erstens ist das Verlustdiagramm. In ACGAN gibt es zwei Arten von Verlusten: echte oder gefälschte Identifikationsverluste und Klassifizierungsverluste. Beide Verluste werden sowohl an den Generator als auch an den Diskriminator weitergegeben. Es wird auch separat in der Grafik dargestellt.
"T / F_loss" ist der Verlust (durchgezogene Linie) für die echte / gefälschte Identifizierung, und "class_loss" ist der Verlust (gepunktete Linie) für die Klassifizierung.
Wenn man das betrachtet, sieht es so aus, als würde es funktionieren. Jedoch... Dies ist ein GIF, wenn für jede Epoche ein Bild von jedem Etikett generiert wird. Ich habe die Etiketteninformationen so eingegeben, dass die obere Zeile von links "Ah, I, U ..." und die untere rechte "..., N, ゝ" lautet. Es gibt fast keine Übereinstimmung zwischen dem Etikett und dem erzeugten Bild. Aber es sieht so aus, als würde "Text auf einem anderen Etikett" erzeugt und kein völlig bedeutungsloses Bild.
Ähnlich wie bei cGAN habe ich versucht, nach 100-tägigem Training mit Generator 5 "A" bis "ゝ" zu generieren. Ist es nicht nur "ke", das Label-chan zu entsprechen scheint? (Vielmehr bricht der Modus vollständig zusammen ...)
Dies ist übrigens das Ergebnis der cGAN-Generierung nach 100 Epochen Training unter den gleichen Bedingungen. Offensichtlich gibt cGAN Zeichen aus, die näher an der Beschriftung liegen.
Auf den ersten Blick denken sowohl Generator als auch Diskriminator ** bei ACGAN, dass Zeichen mit einer anderen Form die Zeichen auf diesem Etikett sind ** (Beispiel: Diskriminator und Generator sind beide "I"). Ist es nicht so (derjenige, der der Form ähnelt, wird als "A" -Label behandelt)? Ich dachte.
Dies ist ein Diagramm, das den Verlust der Diskriminatorklassifizierung in den Verlust aus dem realen Bild und den Verlust aus dem gefälschten Bild (= vom Generator erstelltes Bild) unterteilt. sum_class_loss
ist der Gesamtwert (= entspricht der rot gepunkteten Linie im vorherigen Diagramm).
Wenn man sich diese Grafik ansieht, macht Discriminator einen Fehler bei der Beurteilung des realen Bildes (insbesondere in den frühen Lernphasen) und errät die Beurteilung des gefälschten Bildes.
(In numerischen Begriffen ist "real_class_loss" am Anfang von "fake_class_loss" 20-mal höher und am Ende 5-mal höher.)
Mit anderen Worten, ** das vom Generator mit der Bezeichnung "A" erstellte Bild wird vom Diskriminator als "A" behandelt, selbst wenn die tatsächliche Form von "A" ** sehr unterschiedlich ist **. Ich kann mir vorstellen, dass.
Idealerweise sollte der Verlust der Klassifizierung sowohl für echte als auch für gefälschte Bilder ungefähr gleich sein.
Wie im Original-ACGAN-Dokument erwähnt, scheint sich ** bei zu vielen Klassen die Qualität des Ausgabebilds im selben Netzwerk zu verschlechtern **. In der Originalarbeit ist ImageNet (1000 Klassen) zum Experimentieren in 10 Klassen x 100 Fälle unterteilt.
Deshalb habe ich mich entschlossen, dies einmal in 5 Klassen zu versuchen.
Lassen Sie uns die Netzwerkstruktur gleich machen und versuchen, 5 Zeichen von "A" bis "O" zu generieren.
Das Verlustdiagramm sieht ähnlich aus. Es scheint, dass noch Platz für den T / F_loss
ist, um zu fallen.
Auch hier gibt es einige Unebenheiten, aber die zweite Hälfte ist ziemlich schön.
Als nächstes generieren wir nach 100 Epochentraining jeweils 5 Bilder.
Es scheint, dass der Modus nicht zusammenbricht.
Dann ist es der Verlust der Klassifizierung des Diskriminators.
Auf numerischer Basis gab es in der frühen Phase einen Unterschied von ungefähr 10, aber es ist fast der gleiche Wert in der letzten Phase, aber es ist schwierig, diese Grafik zu sehen, so dass ich sie erst nach 3 Epochen anzeigen werde.
Wenn Sie sich das ansehen, können Sie sehen, dass real_class_loss
und fake_class_loss
ziemlich nahe beieinander liegen.
Gibt es überhaupt einen 10- bis 20-fachen Unterschied zwischen der echten Klassifikation und der falschen Klassifikation aus der 1. Epoche in den frühen Lernphasen? ?? Ich dachte, also habe ich versucht, den Verlust für jeden Iter (für jeden Mini-Batch) anzuzeigen. Es ist wahr, dass sich der Verlustwert zunächst nicht zwischen "real_class_loss" und "fake_class_loss" ändert, aber Sie können sehen, dass "fake_class_loss" stark abfällt.
Richtig / Falsch-Verlust ist fast das gleiche wie ohne Vorlernen. Klassifizierungsverlust Es ist von Anfang an ziemlich klein geworden.
Betrachten wir nun den Klassifizierungsverlust, der sich aus dem realen Bild und dem gefälschten Bild ergibt. Ich habe versucht, bis zu 300 Epochen zu lernen. Im Vergleich zu nicht vorgelernten ist der aus dem realen Bild abgeleitete Verlustwert ebenfalls erheblich niedriger. Es ist ungefähr viermal so viel wie der Verlust, der aus dem gefälschten Bild resultiert, aber es ist immer noch nicht der gleiche Wert.
Werfen wir einen Blick auf das Bild, das ACGAN nach diesem 300-Epochen-Training erstellt hat. Hmm. .. Es ist kein Effekt zu sehen. Die Anzahl der erfolgreichen Zeichen nimmt nicht zu, und es kommt zu einem Zusammenbruch des Modus.
Es gibt mehrere Kuzuji-Datensätze mit einer großen Anzahl von Daten pro Zeichen, 6000 und nur etwa 300 bis 400. Ich denke, je größer die Anzahl der Daten pro Klasse ist, desto besser. Daher dachte ich, dass es funktionieren könnte, wenn die Anzahl der Daten größer als CIFAR-10 ist, aber es war nicht gut.
Ist der Abstand zwischen den Zeichen auf jeder Beschriftung im latenten Raum nicht eng (= Zeichen mit unterschiedlichen Beschriftungen sind im latenten Raum ziemlich eng)? Ich denke. Im Experiment des Originalpapiers habe ich für jeweils 10 Klassen mit CIFAR-10 und ImageNet experimentiert, aber im Fall von Junk-Zeichen gab es nur etwas mehr als die Hälfte der Zeichen, die in 10 Klassen arbeiteten, und es funktionierte nur in 5 Klassen.
Auf jeden Fall scheint es ziemlich schwierig zu sein, die 49er-Klasse mit ACGAN zu zielen und auszugeben, also werde ich aufgeben ...
Recommended Posts