Batching in tf.data.dataset nell'analisi di serie temporali

Aug 22 2020

Sto cercando di creare una pipeline per un modello LSTM di serie temporali. Ho due feed di input, chiamiamoli series1e series2.

Inizializzo l' tf.dataoggetto chiamando from.tensor.slices:

ds = tf.data.Dataset.from_tensor_slices((series1, series2))

Li lotto ulteriormente in finestre di una dimensione di finestre impostata e sposto 1 tra le finestre:

ds = ds.window(window_size + 1, shift=1, drop_remainder=True)

A questo punto voglio giocare con come sono raggruppati insieme. Voglio produrre un certo input come il seguente come esempio:

series1 = [1, 2, 3, 4, 5]
series2 = [100, 200, 300, 400, 500]

batch 1: [1, 2, 100, 200]
batch 2: [2, 3, 200, 300]
batch 3: [3, 4, 300, 400]

Quindi ogni batch restituirà due elementi di serie1 e quindi due elementi di serie2. Questo snippet di codice non funziona per raggrupparli separatamente:

ds = ds.map(lambda s1, s2: (s1.batch(window_size + 1), s2.batch(window_size + 1))

Perché restituisce due mapping di oggetti del set di dati. Poiché sono oggetti non sono abbonabili, quindi non funziona neanche:

ds = ds.map(lambda s1, s2: (s1[:2], s2[:2]))

Sono sicuro che la soluzione sia un utilizzo .applycon una funzione lambda personalizzata. Ogni aiuto è molto apprezzato.

modificare

Sto anche cercando di produrre un'etichetta che rappresenti il ​​prossimo elemento della serie. Quindi, ad esempio, i lotti produrranno quanto segue:

batch 1: (tf.tensor([1, 2, 100, 200]), tf.tensor([3]))
batch 2: (tf.tensor([2, 3, 200, 300]), tf.tensor([4]))
batch 3: (tf.tensor([3, 4, 300, 400]), tf.tensor([5]))

Dove [3], [4]e [5]rappresentano i prossimi elementi series1da prevedere.

Risposte

2 JamieDimon Sep 02 2020 at 13:12

La soluzione è stata quella di visualizzare separatamente i due set di dati .zip(), quindi .concat()gli elementi per includere l'etichetta.

ds = tf.data.Dataset.from_tensor_slices(series1)
ds = ds.window(window_size + 1, shift=1, drop_remainder=True)
ds = ds.flat_map(lambda window: window.batch(window_size + 1))
ds = ds.map(lambda window: (window[:-1], window[-1]))

ds2 = tf.data.Dataset.from_tensor_slices(series2)
ds2 = ds2.window(window_size, shift=1, drop_remainder=True)
ds2 = ds2.flat_map(lambda window: window.batch(window_size))

ds = tf.data.Dataset.zip((ds, ds2))
ds = ds.map(lambda i, j: (tf.concat([i[0], j], axis=0), i[-1]))

Ritorna:

(<tf.Tensor: shape=(7,), dtype=int32, numpy=array([  1,   2,   3, 100, 200, 300])>, <tf.Tensor: shape=(), dtype=int32, numpy=4>)
(<tf.Tensor: shape=(7,), dtype=int32, numpy=array([  2,   3,   4, 200, 300, 400])>, <tf.Tensor: shape=(), dtype=int32, numpy=5>)
(<tf.Tensor: shape=(7,), dtype=int32, numpy=array([  3,   4,   5, 300, 400, 500])>, <tf.Tensor: shape=(), dtype=int32, numpy=6>)
1 NicolasGervais Aug 22 2020 at 15:39

Penso che questa sia la linea che ti manca:

ds = ds.batch(2).map(lambda x, y: (tf.concat([x, y], axis=0)))

Esempio completo:

import tensorflow as tf

series1 = tf.range(1, 16)
series2 = tf.range(100, 1600, 100)

ds = tf.data.Dataset.from_tensor_slices((series1, series2))

ds = ds.batch(2).map(lambda x, y: (tf.concat([x, y], axis=0)))

for row in ds:
    print(row)
tf.Tensor([  1   2 100 200], shape=(4,), dtype=int32)
tf.Tensor([  3   4 300 400], shape=(4,), dtype=int32)
tf.Tensor([  5   6 500 600], shape=(4,), dtype=int32)
tf.Tensor([  7   8 700 800], shape=(4,), dtype=int32)
tf.Tensor([   9   10  900 1000], shape=(4,), dtype=int32)
tf.Tensor([  11   12 1100 1200], shape=(4,), dtype=int32)
tf.Tensor([  13   14 1300 1400], shape=(4,), dtype=int32)
BrownOwl Sep 09 2020 at 06:24

Ecco la mia soluzione quando si tratta di dati di serie temporali.

dataset = tf.data.Dataset.from_tensor_slices(series)
dataset = dataset.window(window_size + 1, shift=1, drop_remainder=True)
dataset = dataset.flat_map(lambda window: window.batch(window_size + 1))
dataset = dataset.shuffle(shuffle_buffer).map(lambda window: (window[:-1], window[-1]))
dataset = dataset.batch(batch_size).prefetch(1)

La riga seguente è importante per dividere la finestra in xs e ys.

dataset.shuffle(shuffle_buffer).map(lambda window: (window[:-1], window[-1]))

Sebbene non sia importante usare shuffle, puoi usare solo la funzione map per dividere la finestra in xs e ys.