How to pass model input to loss function in tensorflow keras?

Viewed 383

I am training a neural networks with three different output prediction. For computing the loss of one output I need one of the input that is passed into the network. I am not able to access it as the training data is feed into the network by a keras data generator object. Is there any workaround for this problem.

This is the Generator class that feds data into the model

class DataGenerator(tf.keras.utils.Sequence):

    def __init__(self,list_ID,centers,sizes,batch_size=2,dims=(512,512),n_channels=3,n_classes=10,shuffle=True) -> None:
        assert len(list_ID) == len(centers)
        self.dims = dims
        self.batch_size = batch_size
        self.list_ID  = list_ID
        self.centers = centers
        self.n_channels = n_channels
        self.n_classes = n_classes
        self.shuffle = shuffle
        self.sizes = sizes
        self.on_epoch_end()
        self.mask = None
    
    def __len__(self):
        return int(np.floor(len(self.list_ID) / self.batch_size))
    
    def on_epoch_end(self):
        self.indexes = np.arange(len(self.list_ID))
        if self.shuffle:
            np.random.shuffle(self.indexes)

    def __getitem__(self, index):
        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
        list_ID_temp = [self.list_ID[k] for k in indexes]
        centers_temp = [self.centers[k] for k in indexes]
        sizes_temp = [self.sizes[k] for k in indexes]
        X, y = self.__datageneration(list_ID_temp, centers_temp,sizes_temp)
        return X, y
    
    def __datageneration(self, list_ID_temp,centers_temp,sizes_temp):

        X = np.empty((self.batch_size,*self.dims,self.n_channels))
        Y_center = np.empty((self.batch_size,128,128,1))
        Y_dimension = np.empty((self.batch_size,128,128,2))
        Y_offset = np.empty((self.batch_size,128,128,2))
        self.mask = np.empty((self.batch_size,128,128,1))
        for i,ID in enumerate(list_ID_temp):
            image = cv2.imread(path+'/'+ID) / 255.0
            heat_center, self.mask[i,] = gaussian_2d(centers_temp[i],image.shape)
'''Here I tried to save mask which is what I need,
 as an attribute to data generator but when accessed by loss function 
the value is just None which is what I initialized it as in init method'''

            heat_size,heat_off = size_off_heatmap(sizes_temp[i], centers_temp[i],image.shape)
            image = cv2.resize(image,(512,512))
            X[i,] = image
        
            Y_center[i,] = heat_center
            Y_dimension[i,] = heat_size
            Y_offset[i,] = heat_off
        return (X,{'center_output':Y_center,'size_output':Y_dimension,'offset_output':Y_offset})

This is the generator class I implemented and I needed the mask , which I tried to write as an attribute of data generator object(I have commented the code. For reference I will also include the function that will return the mask and the error function that requires the mask.

Function returning mask

def gaussian_2d(centers, img_shape):
    heatmap = []
    y_index = np.tile(np.arange(128), (128, 1))
    mask = np.zeros((128,128,1))
    width = img_shape[1]
    height = img_shape[0]
    for x_o, y_o in centers:
        x = int(x_o / width * 128)
        y = int(y_o / height * 128)
        mask[y,x] = 1
        gauss = np.exp(-((y_index.T - y) ** 2 + (y_index - x) ** 2) / 2 * 0.2 ** 2)
        heatmap.append(gauss)
    if len(heatmap) > 1:
        heatmap = np.stack(heatmap)
        heatmap = np.max(heatmap, axis=0)
    else:
        heatmap = np.array(heatmap)
    heatmap = heatmap.reshape((128, 128,1))
    return heatmap,mask

Loss function

def final_loss(mask):
  def l1_loss(y_true, y_pred):
    y_true = tf.cast(y_true, tf.float32)
    y_pred = tf.cast(y_pred, tf.float32)
    n = tf.reduce_sum(tf.cast(tf.equal(mask, 1.0),dtype=tf.float32))
    tot_loss = tf.reduce_sum(tf.abs(y_pred - y_true))
    if tf.greater(n,0):
      loss = tot_loss / (n)
    else:
      loss = tot_loss
    return loss
  return l1_loss

The error show is as below

Epoch 1/10
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-27-74a28b075f52> in <module>()
----> 1 model.fit(gen,epochs=10,verbose=1,callbacks=Callback(patience=4))

9 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/func_graph.py in wrapper(*args, **kwargs)
    975           except Exception as e:  # pylint:disable=broad-except
    976             if hasattr(e, "ag_error_metadata"):
--> 977               raise e.ag_error_metadata.to_exception(e)
    978             else:
    979               raise

ValueError: in user code:

    /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:805 train_function  *
        return step_function(self, iterator)
    <ipython-input-24-c45fe131feb7>:5 l1_loss  *
        n = tf.reduce_sum(tf.cast(tf.equal(mask, 1.0),dtype=tf.float32))
    /usr/local/lib/python3.6/dist-packages/tensorflow/python/util/dispatch.py:201 wrapper  **
        return target(*args, **kwargs)
    /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:1679 equal
        return gen_math_ops.equal(x, y, name=name)
    /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/gen_math_ops.py:3179 equal
        name=name)
    /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py:540 _apply_op_helper
        (input_name, err))

    ValueError: Tried to convert 'x' to a tensor and failed. Error: None values not supported.
'''
0 Answers
Related