Ich setze das zusätzliche Experiment des vorherigen Artikels ohne sexuelle Disziplin fort. Ich wollte DCGAN schreiben, also habe ich es geschrieben, aber ich habe einige unbedeutende Kenntnisse gesammelt, also werde ich es schreiben. Der Inhalt ist hauptsächlich wie folgt.
In anderen Artikeln finden Sie eine Beschreibung von DCGAN selbst. Ich habe mich hauptsächlich auf diesen Bereich bezogen.
Lassen Sie den Computer mit Chainer eine Illustration zeichnen Automatische Generierung von Gesichtsillustrationen mit Chainer keras-dcgan
Es wird nur Keras erwähnt. Wenn Sie also nicht interessiert sind, überspringen Sie es bitte.
Wenn Sie nach "Keras DCGAN" suchen, wird oben keras-dcgan angezeigt. Wenn ich als Referenz einen Blick darauf werfen möchte, Trainierbarer Wert während des Trainings wechseln während des Generatorlernens Es scheint, dass das Gewicht von Discriminator nicht aktualisiert wird. Wenn Sie die Keras-Dokumentation gelesen haben, wissen Sie, dass Sie verhindern können, dass Ebenengewichte aktualisiert werden, indem Sie "trainable = False" angeben. Nach dem Lesen des obigen Codes bleiben jedoch zwei Dinge hängen. tat. Selbst wenn Sie das "trainable" des Modells festlegen, sind die darin enthaltenen Ebenen weiterhin "trainable = True" und die Gewichte werden aktualisiert. Dies wurde im vorherigen Artikel kurz erwähnt. Zweitens wird in den häufig gestellten Fragen zur Keras-Dokumentation Folgendes zum Stoppen von Gewichtsaktualisierungen angegeben.
Außerdem kann die trainierbare Eigenschaft der Ebene nach der Instanziierung mit Wahr oder Falsch angegeben werden. Dies hat zur Folge, dass Sie compile () für das geänderte Modell der trainierbaren Eigenschaft aufrufen müssen. Hier ist ein Beispiel:
Da in keras-dcgan das Kompilieren nach dem Ändern von trainable
nicht durchgeführt wird, spiegelt es sich nicht im Lernen wider und ich habe das Gefühl, dass es falsch abläuft (im Beispiel wird ein ziemlich anständiges Bild ausgegeben und ich habe es nicht selbst ausprobiert. Was passiert also? Es ist ein Mysterium).
Dann war ich es leid zu denken, dass ich das "trainierbare" aller Ebenen einstellen und "kompilieren" und ~ für jeden abwechselnden Schritt wiederholen würde, also versuchte ich verschiedene Dinge und fand die beste Methode. Wie bereits erwähnt, hat Keras die Spezifikation, dass es nicht wiedergegeben wird, es sei denn, es wird nach der Aktualisierung "trainierbar" kompiliert, aber ich habe mich gefragt, ob es gut verwendet werden kann (oder ob es nicht gut entworfen wurde). Hier ist ein Teilauszug des experimentellen Codes. Klicken Sie hier für den vollständigen Text
modelA = Sequential([
Dense(10, input_dim=100, activation='sigmoid')
])
modelB = Sequential([
Dense(100, input_dim=10, activation='sigmoid')
])
modelB.compile(optimizer='adam', loss='binary_crossentropy')
set_trainable(modelB, False)
connected = Sequential([modelA, modelB])
connected.compile(optimizer='adam', loss='binary_crossentropy')
Es ist ein Modell, das DCGAN bis an die Grenzen reduziert.
Unmittelbar nach der Instanziierung befindet sich alles im trainierbaren Zustand. Durch compile`` modelB
in diesem Zustand können Sie das Gewicht mit modelB.fit
aktualisieren.
Als nächstes wird mit "set_trainable" "trainable = False" für alle Schichten von "Modell B" gesetzt, und das Modell "verbunden", das "Modell A" und "Modell B" verbindet, ist "kompilieren".
Was passiert mit dem Gewicht von "Modell B", wenn "Modell", "verbunden" in diesem Zustand "passt"?
w0 = np.copy(modelB.layers[0].get_weights()[0])
connected.fit(X1, X1)
w1 = np.copy(modelB.layers[0].get_weights()[0])
print('Freezed in "connected":', np.array_equal(w0, w1))
# Freezed in "connected": True
modelB.fit(X2, X1)
w2 = np.copy(modelB.layers[0].get_weights()[0])
print('Freezed in "modelB":', np.array_equal(w1, w2))
# Freezed in "modelB": False
connected.fit(X1, X1)
w3 = np.copy(modelB.layers[0].get_weights()[0])
print('Freezed in "connected":', np.array_equal(w2, w3))
# Freezed in "connected": True
Überraschenderweise ist die Ausgabe des obigen Codes (?) Wie im Kommentar beschrieben, und die Einstellungen zum Zeitpunkt des "Kompilierens" sind lebendig, wobei "Modell B" lernen kann und "verbunden" nicht lernen kann. Das heißt, wenn Sie es beim ersten Mal richtig einstellen, müssen Sie es überhaupt nicht ändern. Damit müssen Sie nicht jedes Mal im Lernteil wechseln, und Sie können ihn bereinigen. (Abgesehen davon scheint keras-dcgan verdächtig zu sein, da es viele unnötige Kompilierungen gibt.)
Ich habe den Code geschrieben, weil ich das Problem im vorherigen Abschnitt lösen konnte. Es ist fast so, dass ich geschrieben habe, ohne an irgendetwas zu denken.
discriminator = Sequential([
Convolution2D(64, 3, 3, border_mode='same', subsample=(2,2), input_shape=[32, 32, 1]),
LeakyReLU(),
Convolution2D(128, 3, 3, border_mode='same', subsample=(2,2)),
BatchNormalization(),
LeakyReLU(),
Convolution2D(256, 3, 3, border_mode='same', subsample=(2,2)),
BatchNormalization(),
LeakyReLU(),
Flatten(),
Dense(2048),
BatchNormalization(),
LeakyReLU(),
Dense(1, activation='sigmoid')
], name="discriminator")
generator = Sequential([
#Kürzung
])
# setup models
print("setup discriminator")
opt_d = Adam(lr=1e-5, beta_1=0.1)
discriminator.compile(optimizer=opt_d,
loss='binary_crossentropy',
metrics=['accuracy'])
print("setup dcgan")
set_trainable(discriminator, False)
dcgan = Sequential([generator, discriminator])
opt_g = Adam(lr=2e-4, beta_1=0.5)
dcgan.compile(optimizer=opt_g,
loss='binary_crossentropy',
metrics=['accuracy'])
Als ich versuchte, diesen Kerl zu trainieren, bekam ich den folgenden Fehler.
Exception: You are attempting to share a same `BatchNormalization` layer across different data flows. This is not possible. You should use `mode=2` in `BatchNormalization`, which has a similar behavior but is shareable (see docs for a description of the behavior).
Es wird gesagt, dass "mode = 2" auf "Batch Normalization" gesetzt wird. Dies ist der Fall, wenn Sie versuchen, dieselbe Ebene an anderer Stelle wiederzuverwenden, wie im Beispiel im Abschnitt Freigegebene Ebenen. Ich denke, Sie sagen, dass das Teilen von "BatchNormalization" zu Unannehmlichkeiten führen kann, wenn beispielsweise die Verteilungen von zwei Eingaben unterschiedlich sind. Im obigen Code werden Discriminator allein und Generator + Discriminator "kompiliert", und es scheint, dass die Ebenen als gemeinsam genutzt betrachtet werden. In DCGAN hat Generator + Discriminator "trainable = False", was nicht mit dem Lernen zusammenhängt. Ich denke, es ist in Ordnung, "mode = 2" anzugeben. Wenn Sie darüber nachdenken, können Sie es auf der Batch-Normalisierungsseite sehen ...
Jetzt, wo ich ein Modell habe, habe ich angefangen, es richtig zu trainieren. Ein mysteriöses Phänomen trat auf, wenn der Generator in Chargen von Zufallszahlen mit Bezug auf Keras-DCgan trainiert wurde und der Diskriminator abwechselnd in Chargen von Bildern trainiert wurde, die aus denselben Zufallszahlen und richtigen Antworten erzeugt wurden. Insbesondere [dieser Artikel](http://qiita.com/rezoolab/items/5cc96b6d31153e0c86bc#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3 % 82% BF% E8% AA% BF% E6% 95% B4% E3% 81% AB% E3% 81% A4% E3% 81% 84% E3% 81% A6% E3% 81% AE% E6% 84 Dies entspricht dem in% 9F% E6% 83% B3) genannten Gehalt.
Es gibt zwei Möglichkeiten, die Parameter von Discriminator zu aktualisieren: Eine besteht darin, den Stapel realer Bilder zu aktualisieren, und die andere darin, einen Stapel gefälschter Bilder in einem Stapel zu aktualisieren, und die andere darin, die Verlustfunktion explizit in zwei zu aktualisieren. .. Wenn es keine Ebene für die Stapelnormalisierung gibt, ändert sich der endgültige Farbverlauf nicht. Wenn er jedoch enthalten ist, gibt es einen deutlichen Unterschied (es ist kompliziert). Wenn ich mit der früheren Methode aktualisierte, bekam ich zunächst ein seltsames Ergebnis, dass die Gewinnrate sowohl für G als auch für D 100% betrug. Zuerst vermutete ich, dass es sich um einen Fehler auf der Kettenseite handelte, aber als ich mich schließlich auf die Natur von BN konzentrierte und letzteres implementierte, konvergierte es sauber (es war kein Fehler).
Ich verstehe, dass es nicht gut ist, das richtige Bild und das zufällige Bild in denselben Stapel zu legen und zu normalisieren, aber ich weiß überhaupt nicht, warum dieses Phänomen auftritt (Übrigens hat der Diskriminator von Keras-DCgan BN. Scheint dieses Problem nicht zu beheben, da es nicht enthält). Wie geschrieben steht, scheint es gut zu sein, "die Verlustfunktion explizit in zwei Teile zu teilen und zu aktualisieren", aber ich habe noch nicht verstanden, was es bedeutet, die Verlustfunktion explizit in zwei Teile zu teilen. Es gab keinen Quellcode für diesen Artikel. Schauen Sie sich stattdessen den Quellcode für [diesen Artikel] an (http://qiita.com/mattya/items/e5bfe5e04b9d2f0bbd47). Anwendbarer Teil
DCGAN.py
# train generator
z = Variable(xp.random.uniform(-1, 1, (batchsize, nz), dtype=np.float32))
x = gen(z)
yl = dis(x)
L_gen = F.softmax_cross_entropy(yl, Variable(xp.zeros(batchsize, dtype=np.int32)))
L_dis = F.softmax_cross_entropy(yl, Variable(xp.ones(batchsize, dtype=np.int32)))
# train discriminator
x2 = Variable(cuda.to_gpu(x2))
yl2 = dis(x2)
L_dis += F.softmax_cross_entropy(yl2, Variable(xp.zeros(batchsize, dtype=np.int32)))
#print "forward done"
o_gen.zero_grads()
L_gen.backward()
o_gen.update()
o_dis.zero_grads()
L_dis.backward()
o_dis.update()
sum_l_gen += L_gen.data.get()
sum_l_dis += L_dis.data.get()
#print "backward done"
Ich kenne Chainer überhaupt nicht, aber es scheint, dass die Berechnung des scheinbaren Verlusts für das richtige Bild und das zufällige Bild getrennt durchgeführt wird und dann das Gewicht entsprechend aktualisiert wird.
Also dachte ich, ich würde dasselbe mit Keras machen, aber ich konnte nichts um Functional API herum finden.
Schließlich habe ich mich als Kompromiss entschlossen, das richtige Bild und das zufällige Bild zu trennen und train_on_batch
getrennt auszuführen (ich werde es später erklären, aber als ich BN in Discriminator einfügte, war dies nie erfolgreich, daher ist dies nicht korrekt Es gibt viel Potenzial).
DCGAN
Ubuntu16.04 Core i7 2600k Geforce GTX1060 6GB
Der Datensatz besteht aus 11664 Bildern von 54 32 x 32 Graustufen-Abgrundzeichenbildern, die im vorherigen Artikel verwendet wurden. Sie wurden skaliert und nach oben, unten, links und rechts verschoben, indem die Helligkeit zwangsweise erhöht wurde ([Ich behalte nur das Originalbild]. (https://github.com/t-ae/abyss-letter2/tree/master/abyss_letters)). Aus diesem Grund gehen Sie bitte davon aus, dass die folgenden Inhalte nicht allgemein sind.
Ich weiß nicht, wie ich DCGAN bewerten soll, deshalb werde ich es mit einer Methode messen, die ich für angemessen halte.
Der letzte ist [dieser Artikel](http://qiita.com/mattya/items/e5bfe5e04b9d2f0bbd47#z%E3%81%AE%E7%A9%BA%E9%96%93%E3%82%92%E8 % AA% BF% E3% 81% B9% E3% 82% 8B) ist detailliert, bitte lesen Sie es.
Zunächst habe ich, wie im Artikel erwähnt, versucht, die Chargennormalisierung sowohl in den Diskriminator als auch in den Generator zu integrieren. Das Bild unten zeigt train_loss und train_accuracy. Ich habe verschiedene Dinge versucht, während ich die Parameter von Optimizer geändert habe, aber ich befand mich gegen Ende des Verlusts in einem scheinbar unnatürlichen Zustand. Das Folgende ist ein Beispiel für die Bildausgabe in diesem Zustand. Darüber hinaus ist alles schwarz, und es werden ohnehin nur ähnliche Bilder für die Eingabe von Zufallszahlen generiert. Die wahrscheinliche Ursache ist der Zugschritt, der den oben genannten Keras-Spezifikationen entspricht. Da er jedoch nicht behoben werden kann, habe ich mich entschieden, BN nicht für den Diskriminator zu verwenden, nachdem ich von keras-dcgan gelernt habe. Für diejenigen, die dem Papier folgen möchten oder die Implementierungsbeispiele ihrer Vorgänger verwenden möchten, ist es meiner Meinung nach besser, dies anders als Keras zu tun.
Da ich BN aus Discriminator entfernt habe (obwohl die Parametereinstellung immer noch schwierig ist), begann es, anständige Bilder auszuspucken. Obwohl es sich in [Repository] befindet (https://github.com/t-ae/abyss-letter2/tree/master/dcgan), hat sich der folgende Code möglicherweise geändert, da er gerade manipuliert wird.
train_dcgan.py
# define models
discriminator = Sequential([
Convolution2D(64, 3, 3, border_mode='same', subsample=(2,2), input_shape=[32, 32, 1]),
LeakyReLU(),
Convolution2D(128, 3, 3, border_mode='same', subsample=(2,2)),
LeakyReLU(),
Convolution2D(256, 3, 3, border_mode='same', subsample=(2,2)),
LeakyReLU(),
Flatten(),
Dense(2048),
LeakyReLU(),
Dense(1, activation='sigmoid')
], name="discriminator")
generator = Sequential([
Convolution2D(64, 3, 3, border_mode='same', input_shape=[4, 4, 4]),
UpSampling2D(), # 8x8
Convolution2D(128, 3, 3, border_mode='same'),
BatchNormalization(),
ELU(),
UpSampling2D(), #16x16
Convolution2D(128, 3, 3, border_mode='same'),
BatchNormalization(),
ELU(),
UpSampling2D(), # 32x32
Convolution2D(1, 5, 5, border_mode='same', activation='tanh')
], name="generator")
# setup models
print("setup discriminator")
opt_d = Adam(lr=1e-5, beta_1=0.1)
discriminator.compile(optimizer=opt_d,
loss='binary_crossentropy',
metrics=['accuracy'])
print("setup dcgan")
set_trainable(discriminator, False)
dcgan = Sequential([generator, discriminator])
opt_g = Adam(lr=2e-4, beta_1=0.5)
dcgan.compile(optimizer=opt_g,
loss='binary_crossentropy',
metrics=['accuracy'])
Das Modell ist eine geeignete Modifikation der Papierbasis. Der Diskriminator lernt so schnell, dass der Generator überhaupt nicht aufgeholt hat. Deshalb haben wir die Anzahl der verborgenen Ebeneneinheiten mehr als nötig erhöht und die Lernrate verringert.
train_dcgan.py
def create_random_features(num):
return np.random.uniform(low=-1, high=1,
size=[num, 4, 4, 4])
for epoch in range(1, sys.maxsize):
print("epoch: {0}".format(epoch))
np.random.shuffle(X_train)
rnd = create_random_features(len(X_train))
# train on batch
for i in range(math.ceil(len(X_train)/batch_size)):
print("batch:", i, end='\r')
X_batch = X_train[i*batch_size:(i+1)*batch_size]
rnd_batch = rnd[i*batch_size:(i+1)*batch_size]
loss_g, acc_g = dcgan.train_on_batch(rnd_batch, [0]*len(rnd_batch))
generated = generator.predict(rnd_batch)
X = np.append(X_batch, generated, axis=0)
y = [0]*len(X_batch) + [1]*len(generated)
loss_d, acc_d = discriminator.train_on_batch(X,y)
met_curve = np.append(met_curve, [[loss_d, acc_d, loss_g, acc_g]], axis=0)
# val_Die Berechnung des Verlusts usw., die Ausgabe und das Speichern des Modells werden fortgesetzt
Ich habe mich auf keras-dcgan bezogen, aber ich habe die Reihenfolge so geändert, dass ich zuerst Generator und dann Discriminator gelernt habe. Dies liegt daran, dass ich dachte, wenn G an erster Stelle steht, ist die Wahrscheinlichkeit, dass die Zufallszahl D täuscht, höher und das Lernen am Anfang effizienter. Der Effekt ist nicht gut verstanden.
Hier ist ein Beispiel dafür, wie gut die Parameter angepasst wurden. Es dauerte ungefähr 8 Stunden mit 3000 Epochen. Es scheint gut, eine Bedingung wie val_acc zu setzen, wenn sie viele Male hintereinander 0 ist. In 100 Epochen Zur Zeit von 300 Epochen In 1000 Epochen Ab der Epoche 2000 Bei 3000 Epoche
Die 1000-2000-Epoche sieht am besten aus. Es ist kaputt, wenn Sie am Ende 3000 erreichen.
Der Zugverlust / die Genauigkeit war wie folgt.
Der Verlust von G schwingt und steigt nach rechts. Ich habe verschiedene Dinge ausprobiert, aber ich konnte den Anstieg des G-Verlusts nicht unterdrücken, selbst wenn ich die Lernrate von D erheblich verringert hätte. Ich werde den Mittelteil herausnehmen. Obwohl train_loss häufig flog, schien es insgesamt ziemlich stabil zu sein. train_acc ist ein Tiefflug, aber ich denke, dass das Lernen effizienter verlaufen sollte, wenn es sich dort stabilisiert, wo es größer ist. Insbesondere beim Lernen in der Reihenfolge G-> D wie im obigen Code ist train_acc von G direkt mit der Anzahl der getäuschten Bilder verknüpft, die in D eingegeben werden.
Lassen Sie uns abschließend sehen, dass die beiden ergänzt werden können. Die Spalten ganz links und ganz rechts sind Bilder, die aus zufälligen Vektoren erzeugt werden, wobei Leerzeichen diese ergänzen. Es scheint, dass der Raum gut geformt ist, weil er ziemlich glatt deformiert wird, unabhängig davon, ob er charakterartig ist.
Während ich verschiedene Probleme hatte, dachte ich, dass DCGAN selbst mit roher Gewalt verwaltet werden könnte (einige Parameter und Zeit). Früher habe ich früh aufgegeben, wenn es unmöglich schien, in den frühen Stadien Verluste / Acc zu sehen, aber selbst diese können verwaltet werden, wenn ich mir die Zeit nehme. Ich betrachtete train_loss / acc als eine Bewertung, wie gut ich lernte, aber der Verlust des Generators begann zu Beginn zu steigen, so dass es als Kriterium nützlich war, aufzugeben, wenn er endlos zu steigen schien. Im Gegenteil, selbst wenn der Wert stabil war, war es schwierig, ein gutes Bild zu erhalten, so dass es für die Bewertung nach der mittleren Stufe nicht sehr nützlich zu sein schien. Obwohl val_loss / acc überhaupt nicht in der Bulletin-Liste enthalten war, ist der Wert, der einmal in der Epoche ausgewertet werden soll, nicht sehr aussagekräftig, da die Abweichungen von Schritt zu Schritt zu groß sind und auch die Fahrtrichtung unterschiedlich ist. Es war. Es war nützlich als Stopp-Urteil. Eine ergänzende Bewertung zwischen beiden ist wahrscheinlich ein objektives Kriterium für den Erfolg von DCGAN. Selbst wenn es geklärt werden kann, scheint es, dass der Fortschritt der Anzahl der Epochen und der Ausgabe nicht immer gleich sind, so dass wir eine subjektive Beurteilung vornehmen müssen, welche Stufe des Modells am besten geeignet ist ( Es mag gut sein, den Feature-Bereich zu scannen und zu überprüfen, wie viel Prozent des Originalbilds den Features entsprechen, aber es scheint wahnsinnig schwierig zu sein.
Chainer fragte sich, warum vorwärts und rückwärts getrennt waren, aber dieser Zugschritt scheint eine effektive Situation gewesen zu sein. Ich habe mir auch einige TensorFlow-Implementierungen angesehen, aber das Beispiel nicht ganz verstanden. Das Schreiben in Keras erleichtert das Verständnis, aber ich war immer noch besorgt über die Einschränkungen bei der Zugstufe. In diesem Artikel habe ich festgestellt, dass der Freiheitsgrad niedrig war, aber ich denke, dass er am besten geeignet ist, um allgemeine Probleme wie die Bildklassifizierung schnell zu schreiben. Wenn Sie TF nicht wie ich studieren möchten, versuchen Sie es bitte.
Zur Zeit habe ich ein Modell zur Generierung von Abyss-Charakteren erstellt, das so aussieht. Daher möchte ich zum Zeitpunkt der Veröffentlichung des 5. Bandes von Made in Abyss eine aufgefrischte Version des vorherigen Artikels veröffentlichen.
20.11.: Nachtrag Wenn die Anzahl der Dimensionen der Zufallszahl auf 30 reduziert wurde, wurde selbst mit etwa 200 Epochen eine gute Ausgabe erzielt. Obwohl dies ein Kompromiss mit der Ausdruckskraft ist, scheint es, dass die Lerngeschwindigkeit verbessert werden kann, indem die Anzahl der Dimensionen entsprechend der Vielfalt des Originalbilds angepasst wird. Wenn es ein Maß für die Vielfalt der Ausgabe gibt, können wir anscheinend mehr erforschen.
Recommended Posts