首页 > 解决方案 > “无效参数:索引 [0,0,0,0] = 30 不在 [0, 30) 中

问题描述

错误:

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

历史:

基于此示例,我有一个tf.keras用于语义分割的基于 U-Net的自定义数据加载器。它是这样写的:

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我找到了这个教程,它与上面的示例非常相似。但是,他们还提供了损失的加权版本,使用:

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)

我将收到上述错误。

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

这个错误来自哪里,我该如何解决?

标签: pythontensorflowkerastf.keras

解决方案


图片tf.gather作为一种奇特的方式来做索引。您得到的错误类似于 python 中的以下示例:

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

如果你想使用tf.gather,那么你的值的范围indices不应该大于你愿意索引的张量的维度大小。

在您的情况下,在 calltf.gather(class_weights,indices = tf.cast(segmentation_mask, tf.int32))中,class_weights作为维度的张量,(30,)值的范围segmentation_mask应该在 0 到 29 之间。据我从您的数据管道中可以看出,segmentation_mask值的范围在 0 到 255 之间。修复将取决于问题。


推荐阅读