Я использую TensorFlow 1.12. Я пытаюсь подобрать модель, используя обратные вызовы Keras:
checkpoint_full = tf.keras.callbacks.ModelCheckpoint(filepath='model/dir/model.{epoch:02d}.hdf5', monitor='val_dice_loss', save_best_only=True, mode = "min")
tensorboard = tf.keras.callbacks.TensorBoard(log_dir='/media/nfs/7_raid/ebos/models/fcn/logs/', write_graph=False)
history = model.fit(train,
steps_per_epoch=int(np.ceil(num_samples / float(BATCH_SIZE))),
epochs=NUM_EPOCHS,
validation_data=val,
verbose=0,
callbacks=[checkpoint_full, tensorboard])
Однако это вызывает следующую ошибку:
WARNING:tensorflow:Can save best model only with val_dice_loss available, skipping.
Во-первых, я не понимаю, как разрешается строка, переданная monitor
. Я следую двум руководствам по Keras (1, 2), и оба предоставляют имена строк, которые больше нигде не упоминаются в их коде. Я предполагаю, что они указывают строки, которые пользователь может позже использовать для получения данных о развитии производительности после обучения, а не указывают, какой показатель fit
должен отслеживаться? Если да, то почему он говорит, что значение недоступно?
Во-вторых, все ответы на подобные вопросы указывают на то, что проблема заключается в отсутствии проверочных данных. Тем не менее, я почти уверен, что предоставляю fit
данные, о чем свидетельствует цикл val
и подсчет количества записей. Что я делаю неправильно?
Для справки, мои наборы данных генерируются следующим образом:
def train_sample_fetcher():
return sample_fetcher()
def val_sample_fetcher():
return sample_fetcher(is_validations=True)
def sample_fetcher(is_validations=False):
sample_names = [filename[:-4] for filename in os.listdir(DIR_DATASET + "ndarrays/")]
if not is_validations: sample_names = sample_names[:int(len(sample_names) * TRAIN_VAL_SPLIT)]
else: sample_names = sample_names[int(len(sample_names) * TRAIN_VAL_SPLIT):]
for sample_name in sample_names:
rgb = tf.image.decode_jpeg(tf.read_file(DIR_DATASET + sample_name + ".jpg"))
rgb = tf.image.resize_images(rgb, (HEIGHT, WIDTH))
#d = tf.image.decode_jpeg(tf.read_file(DIR_DATASET + "depth/" + sample_name + ".jpg"))
#d = tf.image.resize_images(d, (HEIGHT, WIDTH))
#rgbd = tf.concat([rgb,d], axis=2)
onehots = tf.convert_to_tensor(np.load(DIR_DATASET + "ndarrays/" + sample_name + ".npy"), dtype=tf.float32)
yield rgb, onehots
train = tf.data.Dataset.from_generator(generator=train_sample_fetcher, output_types=(tf.uint8, tf.uint8))
#train = train.repeat()
train = train.batch(BATCH_SIZE)
#train = train.shuffle(10)
val = tf.data.Dataset.from_generator(generator=val_sample_fetcher, output_types=(tf.uint8, tf.uint8))
Моя функция потерь выглядит следующим образом:
def dice_loss(y_true, y_pred):
smooth = 1.
y_true_f = tf.reshape(y_true, [-1]) # Flatten
y_pred_f = tf.reshape(y_pred, [-1]) # Flatten
intersection = tf.reduce_sum(y_true_f * y_pred_f)
dice_coefficient = (2. * intersection + smooth) / (tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) + smooth)
loss = 1 - dice_coefficient
return loss
Я выполняю семантическую сегментацию, и потери в кости рассчитываются на пиксель.
Следите за 'val_loss'
, так как ваша функция потери уже настроена на вашу пользовательскую функцию потери кубиков.
Параметр monitor
ожидает метрику. 'loss'
всегда доступен, и если у вас есть данные проверки, то и 'val_loss'
. Некоторым людям нравится использовать 'accuracy'
и его валидацию. Если бы у вас была пользовательская функция метрики, такая как чувствительность, называемая (например) sensitivity_deluxe()
, вы могли бы включить sensitivity_deluxe
в массив метрик в compile()
, и она была бы доступна для любых обратных вызовов, ссылающихся на нее в своем поле monitor
. Каждый раз, когда у вас есть данные проверки, вы можете добавить к строке метрики префикс «val_».
Пример:
model.compile(loss=my_loss_function(), optimizer='Adam', metrics=[sensitivity_deluxe])
reduce_lr = ReduceLROnPlateau(monitor='val_loss', patience=3, mode='min')
earlystop = EarlyStopping(monitor='val_sensitivity_deluxe', patience=5, mode='max')
model.fit(X, y, validation_split=0.5, callbacks=[earlystop, reduce_lr])
Я не могу ответить на ваш второй вопрос о проверке, не увидев вашу функцию fit()
, но похоже, что вы, вероятно, в порядке, поскольку вы настроили отдельные генераторы.
Спасибо за разъяснение(я)! Я до сих пор не понимаю, почему функция потерь передается непосредственно в поле метрик в .compile(), а обратные вызовы ссылаются на эти функции в строках. Разбирает ли TensorFlow эти строки и каким-то образом связывает их с правильной функцией потерь?
Да! Вы правильно поняли, и вы также правы, что это не интуитивно. В случае потери строковое представление остается «потерей», независимо от того, пользовательское оно или нет, потому что в compile(loss=...)
оно стало известно под этим именем. В противном случае для пользовательских метрик имя строки является конкретным, как в случае теоретической пользовательской метрической функции sensitivity_deluxe()
выше. Нелогичная часть заключается в том, что имя функции становится строкой под капотом,, но так оно и работает. Я подозреваю, что причина, по которой loss
отделена от массива metrics
, заключается в том, что он (в отличие от любых других) является обязательным.
Какие потери и показатели вы указываете для своей модели?