Dynamic __len__ for Tensorflow sequence-extension?

Viewed 196

Basically, I want a generator that re-read the magic method _len__ at each epoch to recalculate how many batches will be done that epoch.

Here is a snippet of code:

import tensorflow as tf
import numpy as np

class GeneratorFile(tf.keras.utils.Sequence):
    def __init__(self, file_list):
        self.file_list = file_list
        self.desired_file = self.file_list[0]
        print('This should be file 1:', self.desired_file)
    def __len__(self):
        if self.desired_file == 'file1':
            return 2
        else:
            return 3
    def on_epoch_end(self):
        self.desired_file = self.file_list[1]
        print('This should be file 2:', self.desired_file)
    def __getitem__(self, item):
        return np.zeros((16, 1)), np.zeros((16,))

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, input_dim=1, activation="softmax"))
model.compile(
    optimizer='Adam',
    loss='binary_crossentropy',
    metrics=['accuracy']
)

model_file_train = ['file1', 'file2']
generator_train = GeneratorFile(model_file_train)
model.fit(generator_train, epochs=2, initial_epoch = 0)

In the len magic method, I want to switch the number of batches in that epoch when the file has changed, which it does at the end of the first epoch. However, currently, len is run once at the beginning of the training process and then never again. Is there anything to change this?

1 Answers

I tried with simple code to see if __len__ function is called once per program or for every epoch. Turns out it is called multiple times for every epoch. Have given output for different versions of tensorflow.

Code -

import numpy as np
import tensorflow as tf
print(tf.__version__)
from tensorflow.keras import layers, models
from tensorflow.keras.utils import Sequence

FEATURE_SIZE = 512 ** 2

class DataGenerator(Sequence):
    def __init__(self, batch_size):
      self.batch_size = batch_size
    def __len__(self):
      print("in __len__")
      return 1
    def __getitem__(self, i):
      return np.ones((self.batch_size, FEATURE_SIZE)), np.ones((self.batch_size, 1))  # Some dummy data
    def on_epoch_end(self):
      print('on_epoch_end() called')

def train(batch_size):
    print('Training with batch_size =', batch_size)
    training_generator = DataGenerator(batch_size)
    test_generator = DataGenerator(batch_size)
    model = models.Sequential()
    model.add(layers.Dense(4, activation='sigmoid', input_shape=[FEATURE_SIZE]))
    model.add(layers.Dense(1, activation='sigmoid', input_shape=[FEATURE_SIZE]))
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    model.fit(training_generator, validation_data=test_generator, epochs=5, verbose=0 )

train(batch_size=1)

Output - For tensorflow version 1.15.2.

1.15.2
Training with batch_size = 1
in __len__
in __len__
on_epoch_end() called
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
in __len__
in __len__
on_epoch_end() called
in __len__
in __len__
on_epoch_end() called
in __len__
in __len__
on_epoch_end() called

Output - For tensorflow version 2.2.0.

2.2.0
Training with batch_size = 1
in __len__
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
on_epoch_end() called
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
on_epoch_end() called
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
on_epoch_end() called
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
on_epoch_end() called
in __len__
in __len__
in __len__
in __len__
on_epoch_end() called
on_epoch_end() called

Similarly can you please try it out.

Related