Ошибка Tensorflow 2.6.0 во время model.fit, после использования tf.py_function: Ошибка значения: Ожидайте, что x будет непустым массивом или набором данных

#python #tiff #ubuntu-20.04 #tensorflow2.x

Вопрос:

Tensorflow: 2.6.0, Ubuntu 20.04.3 LTS, графический процессор: GeForce MX130, версия CUDA: 11.2

У меня есть набор данных, содержащий 32-разрядные файлы изображений и 8-разрядные файлы масок, оба в формате tiff. Это огромный набор данных. Поэтому я хотел бы загрузить свои данные в tf.data.Формат набора данных вместо массивов numpy (так как это ускоряет загрузку и позволяет избежать проблем с памятью). Декодирование моих файлов tiff с помощью tf.io.read_file(img_path), а затем tfio.experimental.image.decode_tiff(img) здесь не работает, так как это приведет к следующей ошибке: TIFFReadDirectory: Предупреждение, обнаружено неизвестное поле с тегом 42113 (0xa481). Каталог TIFFReadDirectory: Предупреждение, обнаружено неизвестное поле с тегом 42113 (0xa481). память: Извините, не могу обрабатывать изображения с 32-разрядными образцами.

Поэтому я решил использовать библиотеку tiffile для декодирования данных следующим образом: img = tiff.imread(img_path)

Вот фрагмент моего кода:

 full_dataset = tf.data.Dataset.list_files(dataset_path   "*.tif", shuffle=False)
full_dataset = full_dataset.shuffle(buffer_size=100, seed=42)

train_dataset = full_dataset.take(train_size)
test_dataset = full_dataset.skip(train_size)
val_dataset = test_dataset.skip(val_size)
test_dataset = test_dataset.take(test_size)

AUTOTUNE = tf.data.experimental.AUTOTUNE

train_dataset = train_dataset.map(lambda x: tf.py_function(preprocess, inp=[x], Tout= [tf.float32, tf.uint8]))
val_dataset = val_dataset.map(lambda x: tf.py_function(preprocess, inp=[x], Tout=[tf.float32, tf.uint8]))
test_dataset = test_dataset.map(lambda x: tf.py_function(preprocess, inp=[x], Tout=[tf.float32, tf.uint8]))
 

Я использую здесь функцию tf.py_function, потому что я хотел бы использовать библиотеку tiff внутри функции карты для чтения изображения. Для этого мне нужно, чтобы путь к изображению был в строковом формате python. Я заметил, что если я использую обычную функцию map, путь отправляется в функцию в виде тензора строкового типа.

Вот функция предварительной обработки:

 def preprocess(img_path: str):
  f_name = bytes.decode(img_path.numpy())     # for this you need tf.py_function
  img = tiff.imread(f_name)

  img = img[:, :, [0, 2, 3]]  # The image has 4 channels, I need only 3 of them. Therefore extracting those using indices
  img[:, :, 0] = img[:, :, 0] / img[:, :, 0].max() # normaliing values in all 3 channels
  img[:, :, 1] = img[:, :, 1] / img[:, :, 1].max()
  img[:, :, 2] = img[:, :, 2] / img[:, :, 2].max()


  mask_path = tf.strings.regex_replace(img_path, "Images/", "Masks/mask_")
  mask = tf.io.read_file(mask_path)
  mask = tfio.experimental.image.decode_tiff(mask)
  mask = mask[:, :, 0:1]  # 1 channel for mask

  img = tf.image.resize(img, (128, 128))
  mask = tf.image.resize(mask, (128, 128))

  mask = tf.cast(mask, tf.float32) / 255.0  # normalizing mask

  img = tf.image.convert_image_dtype(img, tf.float32)
  mask = tf.image.convert_image_dtype(mask, tf.uint8) # since the tf.py_function is expecting Tout to be tf.float32 and tf.uint8 

  return img, mask
 

После этого я собираюсь начать тренировать свою модель:

 train_dataset = train_dataset.batch(10)
train_dataset = train_dataset.prefetch(buffer_size=AUTOTUNE)

val_dataset = val_dataset.batch(10)
val_dataset = val_dataset.prefetch(buffer_size=AUTOTUNE)

test_dataset = test_dataset.batch(10)
test_dataset = test_dataset.prefetch(buffer_size=AUTOTUNE)
 

To confirm that my dataset isn’t empty I’m plotting an image and mask from it after this. That works fine.

     for images_batch, masks_batch in train_dataset.take(1):
      fig, arr = plt.subplots(1, 2, figsize=(14, 10))
      print(images_batch.shape)
      print(masks_batch.shape)
      arr[0].imshow(images_batch[0], interpolation='nearest')
      arr[1].imshow(masks_batch[0], cmap='gray')
      plt.show()
 

Он также печатает размер пакетов изображений и масок в виде:
(10, 128, 128, 3)
(10, 128, 128, 1)

Печать наборов данных:

 print(train_dataset)
print(val_dataset)
print(test_dataset)
 

выдает следующий результат:

 <PrefetchDataset shapes: ((None, 128, 128, None), (None, 128, 128, None)), types: (tf.float32, tf.uint8)>

<PrefetchDataset shapes: ((None, 128, 128, None), (None, 128, 128, None)), types: (tf.float32, tf.uint8)>

<PrefetchDataset shapes: ((None, 128, 128, None), (None, 128, 128, None)), types: (tf.float32, tf.uint8)>
 

Теперь подгоняем модель:

 model.fit(train_dataset, epochs=20,
          steps_per_epoch=STEPS_PER_EPOCH,
          validation_steps=VALIDATION_STEPS,
          batch_size=10,
          validation_data=val_dataset,
          callbacks=callbacks)
 

выдает следующую ошибку:

 line 39, in train_using_tf_data
    history = model.fit(train_dataset, epochs=attributes.EPOCHS,
  File "/home/user/TfProjects/venv/lib/python3.8/site-packages/tensorflow/python/keras/engine/training.py", line 1204, in fit
    raise ValueError('Expect x to be a non-empty array or dataset.')
ValueError: Expect x to be a non-empty array or dataset.
 

Я думал, что возвращаю tf.данные.Набор данных после функции предварительной обработки. Я также могу построить график данных из возвращенного набора данных. Где здесь может оказаться пустой набор данных?
Я предполагаю, что tf.py_fucntion каким-то образом все портит. Но я не понимаю, где и как это исправить. У кого-нибудь есть какие-нибудь идеи?

Ответ №1:

Хорошо, я нашел решение своей проблемы, и я публикую ответ здесь для всех, кто застрял с тем же самым! Я забыл обновить STEPS_PER_EPOCH переменную, которую я передаю в модель.функция подгонки. Он принимал значение за ноль. Я обновил его с STEPS_PER_EPOCH = train_size // BATCH_SIZE помощью . А потом это сработало как заклинание! Проблема не в обертке tf.py_function . Это была небрежность с моей стороны. Но потребовалось так много времени, чтобы понять, где все идет не так! Я рекомендую всем, у кого есть подобная ошибка, перепроверить все атрибуты, которые вы передаете в свою модель.функция подгонки.