[FIXED] Apply weighting to generator fed model

Issue

Due to the size of my data, I have my training, validation and test data fed via a generator as below:

def sequence_generator(data_type):
        data_type = data_type.decode('ascii')
        sequence_folder = f"{model_attributes['sequences_path']}/{data_type}"
        for numpy_file in random.sample(os.listdir(sequence_folder),len(os.listdir(sequence_folder))):
            if numpy_file.endswith(".npz"):
                with np.load(f"{sequence_folder}/{numpy_file}") as numpy_data:
                    sequences = numpy_data['x']
                    labels = numpy_data['y']
                    labels = tf.one_hot(labels, 3)
                    for X,y in random.sample(list(zip(sequences,labels)),len(sequences)):
                        yield X,y

train_dataset = tf.data.Dataset.from_generator(generator=sequence_generator,args= ['train'], output_types = (tf.float32, tf.float32))# output_shapes = ((train_shape), (3,)))

train_dataset = train_dataset.cache().batch(BATCH).prefetch(tf.data.AUTOTUNE)

history = model.fit(train_dataset, epochs=EPOCHS, validation_data=validation_dataset, callbacks=[tensorboard,checkpoint,earlystop,lr_callback])

From my previous questions accepted answer, I want to be able to apply weighting to 2 of the 3 classes. The accepted answers code is:

sample_weight = np.ones(shape=(len(train_y),))
sample_weight[(train_y[:, 1] == 1) | (train_y[:, 2] == 1)] = 1.5

model = get_model()
model.fit(
    train_x,
    train_y,
    sample_weight=sample_weight,
    ....,
    ...,
    ..,
)

But given that my training data is behind the generator code I am using, I am scratching my head on how best to apply the weighting and with minimal overhead


EDIT

I added the following code as recommended

class_weight = {0: 1.,
                1: 2.,
                2: 2.}

history = model.fit(train_dataset, epochs=EPOCHS, validation_data=validation_dataset,class_weight=class_weight, callbacks=[tensorboard,checkpoint,earlystop,lr_callback])

but I get this error

  File "Train.py", line 1993, in build_and_train_model
    history = model.fit(train_dataset, epochs=EPOCHS, validation_data=validation_dataset,class_weight=class_weight, callbacks=[tensorboard,checkpoint,earlystop,lr_callback])
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\keras\engine\training.py", line 1147, in fit
    steps_per_execution=self._steps_per_execution)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\keras\engine\data_adapter.py", line 1364, in get_data_handler
    return DataHandler(*args, **kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\keras\engine\data_adapter.py", line 1175, in __init__
    class_weight, distribute)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\keras\engine\data_adapter.py", line 1186, in _configure_dataset_and_inferred_steps
    dataset = dataset.map(_make_class_weight_map_fn(class_weight))
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\data\ops\dataset_ops.py", line 1925, in map
    return MapDataset(self, map_func, preserve_cardinality=True)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\data\ops\dataset_ops.py", line 4487, in __init__
    use_legacy_function=use_legacy_function)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\data\ops\dataset_ops.py", line 3712, in __init__
    self._function = fn_factory()
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\eager\function.py", line 3135, in get_concrete_function
    *args, **kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\eager\function.py", line 3100, in _get_concrete_function_garbage_collected
    graph_function, _ = self._maybe_define_function(args, kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\eager\function.py", line 3444, in _maybe_define_function
    graph_function = self._create_graph_function(args, kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\eager\function.py", line 3289, in _create_graph_function
    capture_by_value=self._capture_by_value),
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\framework\func_graph.py", line 999, in func_graph_from_py_func
    func_outputs = python_func(*func_args, **func_kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\data\ops\dataset_ops.py", line 3687, in wrapped_fn
    ret = wrapper_helper(*args)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\data\ops\dataset_ops.py", line 3617, in wrapper_helper
    ret = autograph.tf_convert(self._func, ag_ctx)(*nested_args)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\autograph\impl\api.py", line 692, in wrapper
    return converted_call(f, args, kwargs, options=options)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\autograph\impl\api.py", line 382, in converted_call
    return _call_unconverted(f, args, kwargs, options)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\autograph\impl\api.py", line 463, in _call_unconverted
    return f(*args, **kwargs)
  File "Location\\AppData\Local\Programs\Python\Python36\lib\site-packages\tensorflow\python\keras\engine\data_adapter.py", line 1400, in _class_weights_map_fn
    if y.shape.rank > 2:
TypeError: '>' not supported between instances of 'NoneType' and 'int

Not sure what is being referred to as NoneType, from the research I read I get the inclination that it has something to do with one_hotting maybe?

Solution

I ended up doing this by modifying the generator:

train_dataset  = tf.data.Dataset.from_generator(generator=sequence_generator,args= ['train'], output_types = (tf.float32, tf.float32, tf.float32))# output_shapes = ((train_shape), (3,)))

def sequence_generator(data_type):
    data_type = data_type.decode('ascii')
    sequence_folder = f"{model_attributes['sequences_path']}/{data_type}"
    for numpy_file in random.sample(os.listdir(sequence_folder),len(os.listdir(sequence_folder))):
        if numpy_file.endswith(".npz"):
            with np.load(f"{sequence_folder}/{numpy_file}") as numpy_data:
                sequences = numpy_data['x']
                labels = numpy_data['y']
                class_weight = {0: 1, 1: 2 ,2: 2}
                labels = tf.one_hot(labels, 3)
                sample_weights = np.ones(shape=(len(labels),))
                sample_weights[(labels[:, 1] == 1) | (labels[:, 2] == 1) ] = 2

                for X,y,sample_weight in random.sample(list(zip(sequences,labels,sample_weights)),len(sequences)):
                    yield X,y,sample_weight

The class_weights method did not seem to work. This article seemed to shed some light on it

Answered By – Panda

Answer Checked By – Willingham (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published