#python #tensorflow #object-detection #loss-function #yolo
Вопрос:
Я реализую модель обнаружения объектов с пользовательским генератором данных и пользовательской функцией потери. Я храню свои изображения и коробки в словарях, а затем передаю их своему генератору данных. Я получаю эту ошибку:
InvalidArgumentError: The second input must be a scalar, but it has shape [14,16,16]
[[{{node custom_loss_function/cond/switch_pred/_2}}]] [Op:__inference_train_function_19285]
Function call stack:
train_function
Я не уверен, чем это вызвано, и мой код довольно большой, но я думаю, что это связано с функцией потери(и может быть генератором данных??) поэтому я предоставлю их:
class custom_data_generator(tf.keras.utils.Sequence):
def __init__(self, images, boxes, new_image_size, batch_size = 8, cell_size = 16, amount_of_cells = 16, shuffle = True):
self.boxes = boxes
self.images = images
self.list_IDs = list(self.images.keys())
self.batch_size = batch_size
self.cell_size = cell_size
self.new_image_size = new_image_size
self.amount_of_cells = amount_of_cells
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
return int(np.floor(len(self.images)/self.batch_size))
def on_epoch_end(self):
self.indexes = np.arange(len(self.list_IDs))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __getitem__(self, index):
#we need to slice indexes for one batch
indexes = self.indexes[index * self.batch_size:(index 1) * self.batch_size]
batch_indexes = [self.list_IDs[i] for i in indexes]
X, y = self.__data_generation(batch_indexes)
return X, y
def __data_generation(self, batch_indexes):
#the y_matrix is a gridcell for every picture
#in the end we sum them all in y[] for one batch
y_matrix = np.zeros((16, 16, 10))
y = []
X = []
#create a dict with boxes for one batch
batch_boxes = {}
for index in batch_indexes:
batch_boxes[index] = boxes[index]
for one_image in batch_boxes:
X.append(self.images[one_image])
#iterate though all boxes in one picture
for one_box in batch_boxes[one_image]:
x_norm, y_norm, width_norm, height_norm = one_box[1:]
#check which cell they belong to
cell_x = int(x_norm*self.amount_of_cells)
cell_y = int(y_norm*self.amount_of_cells)
#now rescale thoose coords to the given cell
x_cell = self.amount_of_cells * x_norm - cell_x
y_cell = self.amount_of_cells * y_norm - cell_y
width_cell = width_norm*self.cell_size
height_cell = height_norm*self.cell_size
#now append information to the y_matrix
#first, we check wheather this anchor box already has an object
if y_matrix[cell_x][cell_y][0]==0:
y_matrix[cell_x][cell_y][0]=1
y_matrix[cell_x][cell_y][1:5]=x_cell, y_cell, width_cell, height_cell
#maybe it does(for example, if there are two objects in one cell)
#so we check if there is an object in another anchor box
elif y_matrix[cell_x][cell_y][5]==0:
y_matrix[cell_x][cell_y][5]=1
y_matrix[cell_x][cell_y][6:10]=x_cell, y_cell, width_cell, height_cell
else:
break
y.append(y_matrix)
return np.array(X), np.array(y)
Функция пользовательских потерь:
def custom_loss_function(y_true, y_pred):
obj_lambda = 5
no_obj_lambda = 0.5
#(X, Y) LOSS
#we check whether a bounding box is supposed to have an object in it
#and if it does we calculate the loss
if y_true[..., 0]==1 and y_true[..., 5]==1:
xy_pred = tf.concat([y_pred[..., 1:3], y_pred[..., 6:8]], axis = 0)
xy_true = tf.concat([y_pred[..., 1:3], y_pred[..., 6:8]], axis = 0)
elif y_true[..., 5]==1:
xy_pred = y_pred[..., 6:8]
xy_true = y_pred[..., 6:8]
else:
xy_pred = y_pred[..., 1:3]
xy_true = y_pred[..., 1:3]
xy_loss = obj_lambda*tf.keras.losses.MSE(xy_pred, xy_true)
#(W, H) LOSS
#because width and height are naturally bigger than coordinates the paper suggests
#to use their squares to stabilize the loss function
def sqrt_func(x):
return tf.vectorized_map(tf.sqrt, x)
if y_true[..., 0]==1 and y_true[..., 5] ==1:
wh_pred = tf.concat([sqrt_func(y_pred[..., 3:5]), sqrt_func(y_pred[..., 8:10])], axis = 0)
wh_true = tf.concat([sqrt_func(y_true[..., 3:5]), sqrt_func(y_true[..., 8:10])], axis = 0)
elif y_true[..., 5]==1:
wh_pred = sqrt_func(y_pred[..., 8:10])
wh_true = sqrt_func(y_true[..., 8:10])
else :
wh_pred = sqrt_func(y_pred[..., 3:5])
wh_true = sqrt_func(y_true[..., 3:5])
wh_loss = obj_lambda*tf.keras.losses.MSE(wh_pred, wh_true)
#PROBABILITY LOSS(if there is object)
if y_true[..., 0]==1 and y_true[..., 5]==1:
prob_pred = tf.concat([y_pred[..., 0:1], y_pred[..., 5:6]], axis = 0)
prob_true = tf.concat([y_true[..., 0:1], y_true[..., 5:6]], axis = 0)
elif y_true[..., 5]==1:
prob_pred = y_pred[..., 5:6]
prob_true = y_true[..., 5:6]
else:
prob_pred = y_pred[..., 0:1]
prob_true = y_true[..., 0:1]
#it is better to use Binary Crossentropy if we have only two options
binary_crossentropy = tf.keras.losses.BinaryCrossentropy(
reduction=tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE
)
prob_loss = binary_crossentropy(prob_pred, prob_true)
#NO OBJECT LOSS
if y_true[..., 0]==0 and y_true[..., 5]==0:
noobj_pred = tf.concat([y_pred[..., 0:1], y_pred[..., 5:6]], axis =0)
noobj_true = tf.concat([y_true[..., 0:1], y_true[..., 5:6]], axis =0)
elif y_true[..., 5]==0:
noobj_pred = y_pred[..., 5:6]
noobj_true = y_true[..., 5:6]
else:
noobj_pred = y_pred[..., 0:1]
noobj_true = y_true[..., 0:1]
noobj_loss = no_obj_lambda*binary_crossentropy(noobj_pred, noobj_true)
return xy_loss wh_loss prob_loss noobj_loss
Заранее спасибо, любые советы были бы очень полезны
РЕДАКТИРОВАТЬ Я понял, что мне действительно нужен оператор if в моем коде, и это исправило мою текущую ошибку
Комментарии:
1. Вы не можете использовать оператор if в функции потерь. Она не поддается дифференциации. Вы можете использовать вместо этого
tf.where
2. Спасибо, что удалили инструкции if, исправили эту ошибку!