tensorflow 2 keras shuffle 每行梯度问题

tensorflow 2 keras shuffle each row gradient problem

我需要一个神经网络,它将为相同输入的任何排列提供相同的输出。正在尝试搜索解决方案 ('permutation invariance'),找到了一些层,但未能使它们起作用。

我选择了不同的方法:我想创建一个层,像我第一次添加到模型中一样,它将随机打乱输入(每一行独立)——请让我们遵循这种方法,我知道它可以在外面完成模型,但我希望它成为模型的一部分。我试过了:

class ShuffleLayer(tf.keras.layers.Layer):
    def __init__(self, **kwargs):
        super(ShuffleLayer, self).__init__(**kwargs)

    def call(self, inputs):
        batchSize = tf.shape(inputs)[0]
        cols = tf.shape(inputs)[-1]
        order0 = tf.tile(tf.expand_dims(tf.range(0, batchSize), -1), [1, cols])
        order1 = tf.argsort(tf.random.uniform(shape=(batchSize, cols)))
        indices = tf.stack([tf.reshape(order0, [-1]), tf.reshape(order1, [-1])], axis=-1)
        outputs = tf.reshape(tf.gather_nd(inputs, indices), [batchSize, cols])
        return outputs

我收到以下错误:

ValueError: Variable has None for gradient. Please make sure that all of your ops have a gradient defined (i.e. are differentiable). Common ops without gradient: K.argmax, K.round, K.eval.

如何避免??我尝试使用 tf.stop_gradient,但没有成功。

使用Lambda层:

首先,如果您的图层没有可训练的权重,您应该使用 Lambda 图层,而不是自定义图层。它更简单,更容易。

def shuffleColumns(inputs):
    batchSize = tf.shape(inputs)[0]
    cols = tf.shape(inputs)[-1]
    order0 = tf.tile(tf.expand_dims(tf.range(0, batchSize), -1), [1, cols])
    order1 = tf.argsort(tf.random.uniform(shape=(batchSize, cols)))
    indices = tf.stack([tf.reshape(order0, [-1]), tf.reshape(order1, [-1])], axis=-1)
    outputs = tf.reshape(tf.gather_nd(inputs, indices), [batchSize, cols])
    return outputs

在模型中,使用 Lambda(shuffleColumns) 层。

关于错误

如果这是第一层,这个错误很可能不是这一层引起的。 (除非较新版本的 Tensorflow 要求自定义层具有权重和 def build(self, input_shape): 定义,这似乎不太合逻辑)。

看来你在另一个地方做别的事情。错误是:您正在使用一些阻止反向传播的操​​作,因为不可能拥有该操作的导数。

由于导数是相对于模型的 "weights" 取的,这意味着操作必须在模型中的第一个权重张量之后(即:在包含可训练权重的第一层之后)。

您需要在模型中搜索任何没有导数的东西,如错误提示:round、argmax、return 常量的条件、return 排序的损失 y_true 但不要对 y_pred 进行 return 操作,等等

当然那个K.stop_gradients也是一个阻塞反向传播的操​​作,你随便用肯定会报这个错。 (这甚至可能是您问题的 "cause",而不是解决方案)

下面有更简单的操作建议,但其中 none 将修复此错误,因为此错误在其他地方。

建议操作1

现在,使用 tf.random.shuffle 会更容易:

def shuffleColumns(x):
    x = tf.transpose(x)
    x = tf.random.shuffle(x)
    return tf.transpose(x)

在模型中使用 Lambda(shuffleColumns) 层。确实,这会平等地洗牌所有列,但每批都会有不同的排列。并且由于您将有许多时期,并且您将在每个时期之间洗牌(我假设)样本(这在 fit 中是自动的),您几乎不会有重复的批次。所以:

  • 每批都会有不同的排列
  • 同一批次出现两次几乎是不可能的

这种方法可能比你的方法快得多。

建议操作2

如果你想要它们排列不变,为什么不使用 tf.sort 而不是排列?对列进行排序,而不是有无限的排列来训练,您只需消除排列的任何可能性。该模型应该学习得更快,但不会考虑输入中列的顺序。

使用图层Lambda(lambda x: tf.sort(x, axis=-1))

训练和推理都必须使用此建议。