2017-10-19 2 views
1

J'utilise Keras pour une application RNN de séquence à séquence (caractère). Comme j'ai un ensemble relativement petit d'exemples de A -> B et un ensemble beaucoup plus grand d'exemples de B, j'ai décidé d'essayer une approche d'autoencoder: d'abord former un réseau pour apprendre la fonction d'identité sur B de B, puis former un réseau pour apprendre A -> intégration (B). En combinant le second réseau avec le demi-décodeur du premier réseau, nous espérons généraliser pour produire des Bs plausibles.Formation de deux modèles Keras en tandem avec des poids partiellement partagés

Le code, le modèle du tutoriel Building Autoencoders in Keras, ressemble à quelque chose comme ça (plusieurs couches, abandons, régularisations, etc., ont été laissés par souci de simplicité):

class Example: 
    def __init(self, ...): 
     # Sets dense_size, rnn, rnn_size, embed_size, input_len, output_len, etc. 

    def apply_encoder(self, input_word): 
     input_masked = Masking()(input_word) 
     input_dense = TimeDistributed(Dense(self.dense_size), name='input_dense')(input_masked) 
     rnn = self.rnn(self.rnn_size, name='input_rnn')(input_dense) 
     embedding = Dense(self.embed_size, name='embedding')(rnn) 
     return embedding 

    def apply_decoder(self, embedding): 
     repeated = RepeatVector(self.output_len, name='repeat')(embedding) 
     rnn = self.rnn(self.rnn_size, name='output_rnn')(repeated) 
     output_dense = TimeDistributed(Dense(self.dense_size), name='output_dense')(rnn) 
     output_word = TimeDistributed(
      Dense(self.chars, activation='softmax'), 
      name='output_word' 
      )(output_dense) 
     return output_word 

    def build_net(self): 
     input_word = Input(shape=(self.input_len, self.chars), name='input_word') 
     embedding = self.apply_encoder(input_word) 
     output_word = self.apply_decoder(embedding) 

     self.autoencoder = Model(input_word, output_word) 
     self.encoder = Model(input_word, embedding) 

     embed_input = Input(shape=(self.embed_size,), name='input_embedding') 
     decoder_output = self.apply_decoder(embed_input) 

     self.decoder = Model(embed_input, decoder_output) 

    def save_models(self): 
     open('models/autoencoder.json', 'w').write(self.autoencoder.to_json()) 
     open('models/encoder.json', 'w').write(self.encoder.to_json()) 
     open('models/decoder.json', 'w').write(self.decoder.to_json()) 

trains premier scénario autoencoder sur B -> B; puis un autre script instancie deux fois encoder et forme encoderA sur A ->encoderB.predict(B); enfin, le script de requête utilise encoderA et decoderB pour effectuer des prédictions.

Tout cela fonctionne très bien, mais les performances ne sont pas aussi bonnes que je le voudrais, donc ce que je voudrais vraiment faire est de former les deux modèles en tandem. Ce que je veux, c'est deux modèles d'autoencodeur avec séparés moitiés de codeur, mais poids partagés pour les moitiés du décodeur. Ensuite, j'alterne entre le modèle d'apprentissage A sur un lot de A -> B et le modèle d'apprentissage B sur un lot de B -> B, qui doit mettre à jour les deux encodeurs sur d'autres lots, mais mettre à jour le décodeur partagé sur chaque lot.

Ma question est, simplement, comment puis-je construire ces deux modèles de sorte que les poids soient partagés de la manière que je veux? Here est une question similaire, mais elle explique seulement comment faire ce que j'ai déjà fait. Dans le cas où le backend compte (ce n'est probablement pas le cas), je peux utiliser TF ou Theano.

Répondre

1

Créez des modèles de pièces à l'aide de l'API fonctionnelle et joignez-les comme s'il s'agissait de couches.

La différence est que vous créez deux décodeurs (en appelant appliquer décodeur deux fois)

codeur A:

aInput = Input(...) 
encodedA = LotsOfLayers(...)(aInput) 

self.encoderA = Model(aInput,encodedA) 

codeur B:

bInput = Input(...) 
encodedB = LotsOfLayers(...)(bInput) 

self.encoderB = Model(bInput,encodedB) 

décodeur :

Nous créons un seul décodeur ici:

encodedInput = Input(...) 
decodedOutput = LotsOfLayers(...)(encodedInput) 

self.decoder = Model(encodedInput, decodedOutput) 

AutoencoderB:

est ici le "saut du chat":

autoInput = Input(sameShapeAsEncoderBInput) 
encoded = self.encoderB(autoInput) 
decoded = self.decoder(encoded) 

self.autoencoderB = Model(autoInput,decoded) 

Predictor de A:

Suivre la même logique :

anotherAInput = Input(sameShapeAsEncoderAInput) 
encoded = self.encoderA(anotherAInput) 
decoded = self.decoder(encoded) 

self.predictorFromA = Model(anotherAInput,decoded) 

Cela rendra le décodeur soit les mêmes (poids de partage) pour les deux AutoencoderB et Predictor de A.

+1

Merci, je pense que je vois! Je vais essayer de l'appliquer ce soir, et ensuite vous donner la coche :) – hobbs

+1

Cela fonctionne magnifiquement. Je pense que ce qui me manquait était que 'Input' est comme un espace réservé, et quand vous composez deux modèles, la sortie des premiers slots dans l'entrée de la seconde. – hobbs