每次调用自定义层时,在自定义层的调用方法中创建张量是否会在图形上创建新节点?

Does creating tensors in the call method of a custom layer create new nodes on the graph each time the custom layer is called?

TLDR

每次调用我的自定义层时我都需要随机效果。如果我在 call 方法而不是 init 或 build 方法中创建一个随机掩码(并且还通过打乱输入数据创建一个新张量),这是否会在每次函数调用时在计算图上创建新节点?


我正在创建一个类似 CutMix 的自定义图层以用于表格数据。该层将 1) 采用一个小批量,2) 创建原始小批量的打乱版本,3) 在 bernouilli(p) 处替换打乱后的原始值。这在 Kaggle 上通常被称为 SwapNoise

该层依靠随机掩码(绘制的 bernoulli(p))来切换出随机值的原始值。在 official custom layer guide 中,我在 class 的初始化或构建方法中看到自定义层内的新层。由于我的层在每个小批量中都需要一个唯一的随机掩码,因此我将掩码生成放在 class 的调用方法中。代码如下:

class CutMix(tf.keras.layers.Layer):
    def __init__(self, noise):
        super(CutMix, self).__init__()
        self.noise = noise
    
    def call(self, inputs, training=None):
        if training:
            shuffled = tf.stop_gradient(tf.random.shuffle(inputs))
            msk = tf.keras.backend.random_bernoulli(inputs.shape, p=1 - self.noise, dtype=tf.float32)
            print(msk)
            return msk * inputs + (tf.ones_like(msk) - msk) * shuffled
        return inputs

在 class 方法中创建此掩码层(以及与此相关的混洗层)是否会在每次调用时创建一个新的掩码层,从而使计算图的大小爆炸?如果是这种情况,我如何在没有这个错误的情况下将随机性纳入层内(例如洗牌小批量或创建掩码)?

I need random effects at each call of my custom layer. If I create a random mask (and also create a new tensor by shuffling the input data) in the call method instead of the init or build methods, will this create new nodes on the computation graph with every function call?

这样做,即 msk = tf.keras.backend.random_bernoulli(inputs.shape, p=1 - self.noise, dtype=tf.float32)在调用方法中不会在每次调用模型时在图中创建新节点。

来自他们的源代码:

def random_bernoulli(shape, p=0.0, dtype=None, seed=None):
  """Returns a tensor with random bernoulli distribution of values.
  Args:
      shape: A tuple of integers, the shape of tensor to create.
      p: A float, `0. <= p <= 1`, probability of bernoulli distribution.
      dtype: String, dtype of returned tensor.
      seed: Integer, random seed.
  Returns:
      A tensor.
  """
  if dtype is None:
    dtype = floatx()
  if seed is None:
    seed = np.random.randint(10e6)
  return array_ops.where_v2(
      random_ops.random_uniform(shape, dtype=dtype, seed=seed) <= p,
      array_ops.ones(shape, dtype=dtype), array_ops.zeros(shape, dtype=dtype))

显示 tf.keras.backend.random_bernoulli 是使用 tf.random.uniformtf.where 实现的。至少在您显示的代码中它会正常工作,即每次执行图形时生成随机(不同的)张量并且图形为 static。在更复杂的图形中,tf.random.uniform 和其他旧的 RNG API 可能会失败。检查 https://www.tensorflow.org/guide/random_numbers 以获取新的 RNG API。

需要说明的是,不要create/initCutMix在其他自定义keras层的调用方法中。您只需在其他自定义 keras 层的调用方法中 调用 它,每次执行图形时 tf.random.uniform 都会生成随机(不同的)张量。