Addestramento di un RNN con esempi di diverse lunghezze in Keras


63

Sto cercando di iniziare a conoscere gli RNN e sto usando Keras. Comprendo le premesse di base dei livelli Vanilla RNN e LSTM, ma ho difficoltà a comprendere un certo punto tecnico per l'allenamento.

Nella documentazione di keras , dice che l'input di un layer RNN deve avere forma (batch_size, timesteps, input_dim). Ciò suggerisce che tutti gli esempi di addestramento hanno una lunghezza di sequenza fissa, vale a dire timesteps.

Ma questo non è particolarmente tipico, vero? Potrei voler far funzionare l'RNN su frasi di varia lunghezza. Quando lo alleno su alcuni corpus, gli darò da mangiare gruppi di frasi, tutte di diversa lunghezza.

Suppongo che la cosa ovvia da fare sarebbe trovare la lunghezza massima di qualsiasi sequenza nel set di allenamento e zero pad. Ma allora significa che non posso fare previsioni al momento del test con una lunghezza di input maggiore di quella?

Questa è una domanda sulla particolare implementazione di Keras, suppongo, ma sto anche chiedendo cosa fanno di solito le persone di fronte a questo tipo di problema in generale.


@kbrose è corretto. Tuttavia, ho una preoccupazione. Nell'esempio, hai un generatore molto speciale di rendimenti infiniti. Ancora più importante, è progettato per produrre lotti di dimensioni 1000. In pratica, questo è troppo difficile da soddisfare, se non impossibile. È necessario riorganizzare le voci in modo che quelle della stessa lunghezza siano organizzate insieme e impostare con cura le posizioni di suddivisione in lotti. Inoltre, non hai alcuna possibilità di fare shuffle tra i lotti. Quindi la mia opinione è: non usare mai input di lunghezza variabile in Keras a meno che tu non sappia esattamente cosa stai facendo. Usa l'imbottitura e imposta il Maskinglivello da ignorare
Bs He

Risposte:


59

Ciò suggerisce che tutti gli esempi di addestramento hanno una lunghezza di sequenza fissa, vale a dire timesteps.

Questo non è del tutto corretto, dal momento che quella dimensione può essere None, cioè lunghezza variabile. All'interno di un singolo batch , devi avere lo stesso numero di timestep (questo è in genere il punto in cui vedi 0-padding e mascheramento). Ma tra i lotti non esiste tale restrizione. Durante l'inferenza, puoi avere qualsiasi lunghezza.

Codice di esempio che crea batch casuali di lunghezza dei dati di allenamento.

from keras.models import Sequential
from keras.layers import LSTM, Dense, TimeDistributed
from keras.utils import to_categorical
import numpy as np

model = Sequential()

model.add(LSTM(32, return_sequences=True, input_shape=(None, 5)))
model.add(LSTM(8, return_sequences=True))
model.add(TimeDistributed(Dense(2, activation='sigmoid')))

print(model.summary(90))

model.compile(loss='categorical_crossentropy',
              optimizer='adam')

def train_generator():
    while True:
        sequence_length = np.random.randint(10, 100)
        x_train = np.random.random((1000, sequence_length, 5))
        # y_train will depend on past 5 timesteps of x
        y_train = x_train[:, :, 0]
        for i in range(1, 5):
            y_train[:, i:] += x_train[:, :-i, i]
        y_train = to_categorical(y_train > 2.5)
        yield x_train, y_train

model.fit_generator(train_generator(), steps_per_epoch=30, epochs=10, verbose=1)

E questo è ciò che stampa. Si noti che le forme di output (None, None, x)indicano dimensioni batch variabili e dimensioni timestep variabili.

__________________________________________________________________________________________
Layer (type)                            Output Shape                        Param #
==========================================================================================
lstm_1 (LSTM)                           (None, None, 32)                    4864
__________________________________________________________________________________________
lstm_2 (LSTM)                           (None, None, 8)                     1312
__________________________________________________________________________________________
time_distributed_1 (TimeDistributed)    (None, None, 2)                     18
==========================================================================================
Total params: 6,194
Trainable params: 6,194
Non-trainable params: 0
__________________________________________________________________________________________
Epoch 1/10
30/30 [==============================] - 6s 201ms/step - loss: 0.6913
Epoch 2/10
30/30 [==============================] - 4s 137ms/step - loss: 0.6738
...
Epoch 9/10
30/30 [==============================] - 4s 136ms/step - loss: 0.1643
Epoch 10/10
30/30 [==============================] - 4s 142ms/step - loss: 0.1441

Grazie per questo. Tuttavia, se 0 riordiniamo le sequenze, ciò influenzerà gli stati nascosti e la cella di memoria perché continuiamo a passare x_t come 0s, quando in realtà non dovrebbe essere passato nulla. Nella norma fit(), possiamo passare il sequence_lenthparametro per specificare la lunghezza della sequenza per escluderlo. Sembra che l'approccio del generatore non consenta di ignorare 0 sequenze?
GRS

1
@GRS Il tuo generatore può restituire una 3-tupla di (inputs, targets, sample_weights), e puoi impostare i sample_weightstuoi 0-pad su 0. Tuttavia, non sono sicuro che funzionerebbe perfettamente con RNN bidirezionali.
kbrose,

Questo è stato utile, ma vorrei che includesse anche un esempio di utilizzo model.predict_generatorcon un set di test. Quando provo a prevedere con un generatore viene visualizzato un errore relativo alla concatenazione (il set di test ha anche sequenze di lunghezza variabile). La mia soluzione è stata quella di utilizzare lo standard model.predictin modo confuso. Forse questo sarebbe più adatto per una nuova domanda?
Topolino il

@mickey che sembra una domanda diversa. Questa domanda riguarda l'allenamento, non la previsione.
Krose

Se la domanda nei commenti è stata effettivamente posta come una nuova domanda, puoi collegarti ad essa?
Itamar Mushkin,

7

@kbrose sembra avere una soluzione migliore

Suppongo che la cosa ovvia da fare sarebbe trovare la lunghezza massima di qualsiasi sequenza nel set di allenamento e zero pad.

Questa è di solito una buona soluzione. Forse prova la lunghezza massima della sequenza + 100. Usa ciò che funziona meglio per la tua applicazione.

Ma allora significa che non posso fare previsioni al momento del test con una lunghezza di input maggiore di quella?

Non necessariamente. Il motivo per cui una lunghezza fissa viene utilizzata in keras è perché migliora notevolmente le prestazioni creando tensori di forme fisse. Ma questo è solo per l'allenamento. Dopo l'allenamento, avrai imparato i pesi giusti per il tuo compito.

Supponiamo, dopo l'allenamento per ore, ti rendi conto che la lunghezza massima del tuo modello non era abbastanza grande / piccola e ora devi cambiare i passi del tempo, basta estrarre i pesi appresi dal vecchio modello, costruire un nuovo modello con i nuovi passi del tempo e iniettare i pesi appresi in esso.

Probabilmente puoi farlo usando qualcosa come:

new_model.set_weights(old_model.get_weights())

Non l'ho provato da solo. Provalo e pubblica i risultati qui a beneficio di tutti. Ecco alcuni link: uno due


1
Puoi infatti avere input di lunghezza variabile, non è necessario introdurre hack come max length + 100. Vedi la mia risposta per esempio codice.
kbrose,

1
Il trasferimento dei pesi su un modello con più timestep funziona davvero perfettamente! Ho aumentato i timestep per Bidirectional(LSTM)()e i RepeatVector()livelli e le previsioni sono perfettamente praticabili.
komodovaran_

@kbrose Questo non è un trucco, è come lo fai normalmente. L'uso di un batch_size di uno è troppo lento e le keras abilitano i livelli di mascheramento in modo che il mascheramento non influisca sulla perdita.
Ferus,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.