"Invalid argument: indices[0,0,0,0] = 30 is not in [0, 30)"

"Invalid argument: indices[0,0,0,0] = 30 is not in [0, 30)"

错误:

InvalidArgumentError: indices[0,0,0,0] = 30 is not in [0, 30)
     [[{{node GatherV2}}]] [Op:IteratorGetNext]

历史:

我有一个自定义数据加载器,用于基于 tf.keras 的 U-Net,用于语义分割,基于 this example。写法如下:

def parse_image(img_path: str) -> dict:
    # read image
    image = tf.io.read_file(img_path)
    #image = tfio.experimental.image.decode_tiff(image)
    if xf == "png":
        image = tf.image.decode_png(image, channels = 3)
    else:
        image = tf.image.decode_jpeg(image, channels = 3)
    image = tf.image.convert_image_dtype(image, tf.uint8)
    #image = image[:, :, :-1]
    # read mask
    mask_path = tf.strings.regex_replace(img_path, "X", "y")
    mask_path = tf.strings.regex_replace(mask_path, "X." + xf, "y." + yf)
    mask = tf.io.read_file(mask_path)
    #mask = tfio.experimental.image.decode_tiff(mask)
    mask = tf.image.decode_png(mask, channels = 1)
    #mask = mask[:, :, :-1]
    mask = tf.where(mask == 255, np.dtype("uint8").type(NoDataValue), mask)
    return {"image": image, "segmentation_mask": mask}

train_dataset = tf.data.Dataset.list_files(
    dir_tls(myear = year, dset = "X") + "/*." + xf, seed = zeed)
train_dataset = train_dataset.map(parse_image)

val_dataset = tf.data.Dataset.list_files(
    dir_tls(myear = year, dset = "X_val") + "/*." + xf, seed = zeed)
val_dataset = val_dataset.map(parse_image)

## data transformations--------------------------------------------------------
@tf.function
def normalise(input_image: tf.Tensor, input_mask: tf.Tensor) -> tuple:
    input_image = tf.cast(input_image, tf.float32) / 255.0
    return input_image, input_mask

@tf.function
def load_image_train(datapoint: dict) -> tuple:
    input_image = tf.image.resize(datapoint["image"], (imgr, imgc))
    input_mask = tf.image.resize(datapoint["segmentation_mask"], (imgr, imgc))
    if tf.random.uniform(()) > 0.5:
        input_image = tf.image.flip_left_right(input_image)
        input_mask = tf.image.flip_left_right(input_mask)
    input_image, input_mask = normalise(input_image, input_mask)
    return input_image, input_mask

@tf.function
def load_image_test(datapoint: dict) -> tuple:
    input_image = tf.image.resize(datapoint["image"], (imgr, imgc))
    input_mask = tf.image.resize(datapoint["segmentation_mask"], (imgr, imgc))
    input_image, input_mask = normalise(input_image, input_mask)
    return input_image, input_mask

## create datasets-------------------------------------------------------------
buff_size = 1000
dataset = {"train": train_dataset, "val": val_dataset}
# -- Train Dataset --#
dataset["train"] = dataset["train"]\
    .map(load_image_train, num_parallel_calls = tf.data.experimental.AUTOTUNE)
dataset["train"] = dataset["train"].shuffle(buffer_size = buff_size,
                                            seed = zeed)
dataset["train"] = dataset["train"].repeat()
dataset["train"] = dataset["train"].batch(bs)
dataset["train"] = dataset["train"].prefetch(buffer_size = AUTOTUNE)
#-- Validation Dataset --#
dataset["val"] = dataset["val"].map(load_image_test)
dataset["val"] = dataset["val"].repeat()
dataset["val"] = dataset["val"].batch(bs)
dataset["val"] = dataset["val"].prefetch(buffer_size = AUTOTUNE)

print(dataset["train"])
print(dataset["val"])

现在我想为我的模型使用 tf.keras.losses.SparseCategoricalCrossentropy 加权版本 ,我发现 this tutorial,这与上面的示例非常相似。 但是,他们还提供了损失的加权版本,使用:

def add_sample_weights(image, label):
  # The weights for each class, with the constraint that:
  #     sum(class_weights) == 1.0
  class_weights = tf.constant([2.0, 2.0, 1.0])
  class_weights = class_weights/tf.reduce_sum(class_weights)

  # Create an image of `sample_weights` by using the label at each pixel as an 
  # index into the `class weights` .
  sample_weights = tf.gather(class_weights, indices=tf.cast(label, tf.int32))

  return image, label, sample_weights

weighted_model.fit(
    train_dataset.map(add_sample_weights),
    epochs=1,
    steps_per_epoch=10)

我结合了这些方法,因为后一个教程使用以前加载的数据,而我想从光盘绘制图像(没有足够的 RAM 来一次加载所有)。

生成第一个示例的代码(上面的长代码块),然后是

def add_sample_weights(image, segmentation_mask):
  class_weights = tf.constant(inv_weights, dtype = tf.float32)
  class_weights = class_weights/tf.reduce_sum(class_weights)
  sample_weights = tf.gather(class_weights,
                             indices = tf.cast(segmentation_mask, tf.int32))
  return image, segmentation_mask, sample_weights

inv_weights 是我的权重,一个包含 30 个 float64 值的数组)和

 model.fit(dataset["train"].map(add_sample_weights),
                     epochs = 45, steps_per_epoch = np.ceil(N_img/bs),
                     validation_data = dataset["val"],
                     validation_steps = np.ceil(N_val/bs),
                     callbacks = cllbs)

当我运行 dataset["train"].map(add_sample_weights).element_spec 与第二个示例一样,我得到了一个对我来说合理的输出(类似于示例中的输出):

Out[58]: 
(TensorSpec(shape=(None, 512, 512, 3), dtype=tf.float32, name=None),
 TensorSpec(shape=(None, 512, 512, 1), dtype=tf.float32, name=None),
 TensorSpec(shape=(None, 512, 512, 1), dtype=tf.float32, name=None))

但是,当我尝试拟合模型或 运行 类似

的东西时
a, b, c = dataset["train"].map(add_sample_weights).take(1)

我会收到上面提到的错误。

到目前为止,我发现了很多关于这个错误的问题(例如,, b, c, d),但是,他们都在谈论“嵌入层”和我不知道使用的东西。

这个错误是从哪里来的,我该如何解决?

tf.gather 想象成一种建立索引的奇特方式。您得到的错误类似于 python 中的以下示例:

>>> my_list = [1,2,3]
>>> my_list[3] 
IndexError: list index out of range

如果你想使用tf.gather,那么你的indices取值范围不能大于你要索引的Tensor的维度大小

在您的例子中,在 tf.gather(class_weights,indices = tf.cast(segmentation_mask, tf.int32)) 调用中,class_weights 是维度 (30,) 的张量,segmentation_mask 的值范围应介于 0 和29. 据我从您的数据管道中得知,segmentation_mask 的值范围在 0 到 255 之间。修复将视问题而定。