Keras-Code ist einfach und sehr modular, daher einfach zu schreiben, leicht zu verstehen und leicht zu verwenden. Wenn Sie jedoch versuchen, andere als die Standardmuster zu trainieren oder zu schichten, gibt es nicht viele Beispiele und Sie wissen oft nicht, wie man schreibt.
Ich werde die Tipps teilen, die ich gelernt habe, als ich kürzlich einige ungewöhnliche Modelle als Memorandum geschrieben habe.
Es gibt zwei Möglichkeiten, ein Modell in Keras zu schreiben. Sequential Model und Functional API Model -führen /).
Sequentielles Modell ist
model = Sequential()
model.add(Dense(32, input_dim=784))
model.add(Activation('relu'))
Ich denke, es gibt viele Leute, die das zuerst gesehen und gedacht haben: "Keras ist wirklich leicht zu verstehen!"
Abgesehen davon
inputs = Input(shape=(784,))
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)
model = Model(input=inputs, output=predictions)
Es gibt einen Weg zu schreiben. Dies ist eine Schreibweise mit dem Rhythmus "LayerInstance (InputTensor) -> OutputTensor".
Dicht (64, Aktivierung = 'relu') (x)
mag denen seltsam erscheinen, die mit Python-ähnlichen Sprachen nicht vertraut sind.
Ich habe gerade eine Instanz ** der ** Dense
-Klasse im Teil von Dense (64, Aktivierung = 'relu')
undDenseInstance (x)
dafür erstellt.
dense = Dense(64, activation='relu')
x = dense(x)
Die Bedeutung ist die gleiche.
Der Ablauf besteht darin, die ** Eingabeebene ** ** Ausgabeschicht ** zu bestimmen und an die Klasse "Modell" zu übergeben. Wenn es sich bei der Eingabeebene um echte Daten handelt, geben Sie diese mit der Klasse "Eingabe" an (z. B. Platzhalter).
Was Sie hier beachten sollten, ist, dass ** für jede LayerInstance ** ein Gewicht enthält **. Mit anderen Worten, ** wenn Sie dieselbe Ebeneninstanz verwenden, teilen Sie das Gewicht **. Achten Sie auf unbeabsichtigtes und absichtliches Teilen.
Auf diese Weise können Sie einfach denselben Ausgangstensor in eine andere Ebene eingeben. Der Umfang der Beschreibung ändert sich nicht wesentlich. Wenn Sie sich daran gewöhnt haben, wird empfohlen, das Schreiben mit dieser funktionalen API zu üben, um sich auf zukünftige schwierige Modelle vorzubereiten.
Manchmal haben Sie eine andere Eingabeebene und eine andere Ausgabeebene, möchten jedoch das zugrunde liegende Netzwerk und die zugrunde liegende Gewichtung gemeinsam nutzen.
In diesem Fall ist die Handhabung einfacher, wenn Sie sie in der Klasse "Container" zusammenstellen.
Da Container
wie Layer eine Unterklasse von Layer
ist, bedeutet ** die Verwendung derselben ContainerInstance, dass das Gewicht geteilt wird **.
Zum Beispiel
inputs = Input(shape=(784,))
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)
shared_layers = Container(inputs, predictions, name="shared_layers")
Solche "shared_layers" können so behandelt werden, als wäre es eine einzelne Ebene.
Der "Container" selbst hat grundsätzlich kein eigenes Gewicht, sondern dient nur als Bündel für andere "Layer".
Wenn Sie dagegen kein Gewicht teilen möchten, müssen Sie "LayerInstance" einzeln verbinden, ohne "Container" zu teilen.
Oft beim Schreiben eigener Berechnungen oder Tensor-Transformationen
TypeError: ('Not a Keras tensor:', Elemwise{add,no_inplace}.0)
Ich sehe den Fehler.
Dies geschieht normalerweise, wenn Sie "raw Tensor" in die Eingabe von "LayerInstance" anstelle von "Ausgabe einer anderen Ebene" einfügen. Zum Beispiel
from keras import backend as K
inputs = Input((10, ))
x = K.relu(inputs * 2 + 1)
x = Dense(64, activation='relu')(x)
Und so weiter. Ich bin nicht sicher, aber Layer's Output ist ein Objekt mit einer internen Form namens KerasTensor, die sich von dem Berechnungsergebnis wie "K.hogehoge" zu unterscheiden scheint.
In einem solchen Fall funktioniert es gut, wenn Sie das unten stehende "Lambda" verwenden (es ist sicherer, die "_keras_shape" ^^ nicht mit Gewalt auszufüllen ^^).
Angenommen, Sie möchten einen Vektor mit 10 Elementen in der ersten Hälfte in 5 und in der zweiten Hälfte in 5 teilen. Wie oben erwähnt
inputs = Input((10, ))
x0_4 = inputs[:5]
x5_9 = inputs[5:]
d1 = Dense(10)(x0_4)
d2 = Dense(10)(x5_9)
In diesem Fall tritt ein Fehler auf.
Deshalb
inputs = Input((10, ))
x0_4 = Lambda(lambda x: x[:, :5], output_shape=(5, ))(inputs)
x5_9 = Lambda(lambda x: x[:, 5:], output_shape=lambda input_shape: (None, int(input_shape[1]/2), ))(inputs)
d1 = Dense(10)(x0_4)
d2 = Dense(10)(x5_9)
Wickeln Sie mit Lambda
Klasse wie diese und es wird funktionieren.
Hier gibt es einige Punkte.
In Keras ist die erste Dimension konsistent die Beispieldimension (Dimension batch_size). Schreiben Sie beim Implementieren einer Ebene wie "Lambda" eine Berechnungsformel, die die Beispieldimension intern enthält. Sie müssen also "Lambda x: x [:,: 5]" anstelle von "Lambda x: x [: 5]" schreiben.
output_shape
kann weggelassen werden, wenn die Eingabe- und Ausgabeformen gleich sind, muss jedoch angegeben werden, wenn sie unterschiedlich sind.
Sie können Tupel oder Funktion als Argument für output_shape angeben, aber ** Beispieldimension für Tupel nicht einschließen **, ** Beispieldimension für Funktion einschließen **.
Im Fall von Function ist es grundsätzlich in Ordnung, wenn die Sample-Dimension auf "None" gesetzt ist.
Darüber hinaus ist es "input_shape", ein Argument, wenn es von Function angegeben wird. Es sollte jedoch beachtet werden, dass es die Beispieldimension enthält.
Lambda(lambda x: x[:, :5], output_shape=(5, ))(inputs)
Lambda (Lambda x: x [:,: 5], output_shape = (None, 5)) (Eingaben)
# Es ist eigentlich in Ordnung, wenn Eingaben eindimensional sind, aber es ist NG, wenn es zweidimensional ist, also schließen Sie es nicht ein Es ist gut, sich zu erinnern.Lambda(lambda x: x[:, 5:], output_shape=lambda input_shape: (int(input_shape[1]/2), ))(inputs)
Lambda(lambda x: x[:, 5:], output_shape=lambda input_shape: (None, int(input_shape[1]/2)))(inputs)
Sie können die Verlustfunktion mit der Kompilierungsmethode "Modell" angeben und Sie können auch Ihre eigene benutzerdefinierte Verlustfunktion angeben. In Bezug auf die Form der Funktion werden zwei Argumente verwendet, "y_true" und "y_pred", und die Anzahl der ** Samples ** wird zurückgegeben. Zum Beispiel:
def generator_loss(y_true, y_pred): # y_true's shape=(batch_size, row, col, ch)
return K.mean(K.abs(y_pred - y_true), axis=[1, 2, 3])
In diesem LSGAN mit Keras schreiben,
Die Funktionen zum Anwenden von Gewichten und Masken sind genau das. Wenn Sie sie nicht verwenden, können Sie im Gegenteil über Stichproben wie diese Zeit berechnen.
Es wurde darauf hingewiesen, dass ich denke, dass das wahr ist. Wenn Sie nicht vorhaben, es anderweitig zu verwenden, und sample_weight usw. nicht verwenden müssen, ist es daher in Ordnung, einen Verlustwert zurückzugeben.
Wenn Sie von einer Ebene übergeben möchten, können Sie einfach "Ebene # add_loss" aufrufen, aber es ist etwas schwierig, von einer Nicht-Ebene zu übergeben (oder ich kenne den richtigen Weg nicht).
Andere Verlustformeln als die Verlustfunktion werden von jeder Ebene durch "Modell # Verluste" gesammelt, wenn "Kompilieren" der "Modell" -Instanz ausgeführt wird (vom Regularisierer usw.). Mit anderen Worten, Sie können es hier irgendwie weitergeben. Sie können beispielsweise "Container" oder "Modell" erben und "# Verluste" übertreiben. Als ich VATModel erstellt habe, habe ich es auf diese Weise bestanden.
Möglicherweise möchten Sie, dass die Verlustberechnung während des Trainings die vorherigen Verlustergebnisse widerspiegelt. Zum Beispiel im Fall von "DiscriminatorLoss" der BEGAN-Berechnung wie unten gezeigt. https://github.com/mokemokechicken/keras_BEGAN/blob/master/src/began/training.py#L104
Parameteraktualisierung während des Lernens
Die von Model # updates
übergebenen Parameterinformationen werden verwendet, wenn compile
der Model
-Instanz ausgeführt wird.
Es gibt normalerweise keine Möglichkeit, Daten von der Verlustfunktion an diese Model # -Updates zu übergeben (wahrscheinlich), daher mache ich einen kleinen Trick.
__name__
erforderlich zu sein scheint__call__ ()
__call__ ()
K.update ()
, um "Objekt für Parameteraktualisierung" zu generieren und dem Array self.updates
hinzuzufügen.Wenn Sie darüber nachdenken, ist es möglich, Folgendes zu tun.
class DiscriminatorLoss:
__name__ = 'discriminator_loss'
def __init__(self, lambda_k=0.001, gamma=0.5):
self.lambda_k = lambda_k
self.gamma = gamma
self.k_var = K.variable(0, dtype=K.floatx(), name="discriminator_k")
self.m_global_var = K.variable(0, dtype=K.floatx(), name="m_global")
self.loss_real_x_var = K.variable(0, name="loss_real_x") # for observation
self.loss_gen_x_var = K.variable(0, name="loss_gen_x") # for observation
self.updates = []
def __call__(self, y_true, y_pred): # y_true, y_pred shape: (BS, row, col, ch * 2)
data_true, generator_true = y_true[:, :, :, 0:3], y_true[:, :, :, 3:6]
data_pred, generator_pred = y_pred[:, :, :, 0:3], y_pred[:, :, :, 3:6]
loss_data = K.mean(K.abs(data_true - data_pred), axis=[1, 2, 3])
loss_generator = K.mean(K.abs(generator_true - generator_pred), axis=[1, 2, 3])
ret = loss_data - self.k_var * loss_generator
# for updating values in each epoch, use `updates` mechanism
# DiscriminatorModel collects Loss Function's updates attributes
mean_loss_data = K.mean(loss_data)
mean_loss_gen = K.mean(loss_generator)
# update K
new_k = self.k_var + self.lambda_k * (self.gamma * mean_loss_data - mean_loss_gen)
new_k = K.clip(new_k, 0, 1)
self.updates.append(K.update(self.k_var, new_k))
# calculate M-Global
m_global = mean_loss_data + K.abs(self.gamma * mean_loss_data - mean_loss_gen)
self.updates.append(K.update(self.m_global_var, m_global))
# let loss_real_x mean_loss_data
self.updates.append(K.update(self.loss_real_x_var, mean_loss_data))
# let loss_gen_x mean_loss_gen
self.updates.append(K.update(self.loss_gen_x_var, mean_loss_gen))
return ret
class DiscriminatorModel(Model):
"""Model which collects updates from loss_func.updates"""
@property
def updates(self):
updates = super().updates
if hasattr(self, 'loss_functions'):
for loss_func in self.loss_functions:
if hasattr(loss_func, 'updates'):
updates += loss_func.updates
return updates
discriminator = DiscriminatorModel(all_input, all_output, name="discriminator")
discriminator.compile(optimizer=Adam(), loss=DiscriminatorLoss())
Vielleicht sind die letzten beiden Tipps, die nach einer Weile unnötig werden. Keras ist einfach, dem Quellcode zu folgen, daher ist es überraschend hübsch. Mit Keras2 ist die Fehlermeldung außerdem verständlicher geworden, was beim Debuggen hilfreich ist.
Recommended Posts