基于方差/标准差矩阵的高斯噪声破坏张量

Corrupting Tensor with Gaussian Noise based on a Variance / Stddev Matrix

我有一个 CNN 输出掩码 (logits, shape = [batch, x, y, classes]) 和一个矩阵 (sigma) 将高斯噪声的标准差分配给每个 logit(即 sigma.shape = [x, y, classes])。 我想根据相应的 sigma 用高斯噪声破坏每个 logit。 在 tensorflow 中,我只找到了一个适用于标量的高斯:tf.random_normal

因此我使用循环,"computed" 分别为每个 logit 噪声(mean = logit[b, x, y, c]stddev = sigma[x, y, c])并使用 tf.stack 取回我的 4-D 张量。但是:对于 [1, 1024, 1024, 2] -Tensor 这已经需要很长时间了(在 "didn't finish" 的范围内),我想这是有道理的,因为它必须创建和堆叠> 100 万个张量对象。 无论如何,我很确定这不是要走的路...

但是我应该怎么做呢? 是否有tf.random_normal允许在更高维度上工作?

我知道 tf.random_normal 可以 return 具有(任意)shape 的更多维张量,但这对我不起作用,因为它对每个张量应用相同的 stddev元素(均值无关紧要,因为我可以生成 0 均值噪声和 tf.add())。

如果它以任何方式相关:我现在可以接受的妥协(为了加快速度)是使用仅基于像素的 stddev 生成噪声(而不是 class ),即 *sigma.shape = [x, y]*。但这只消除了一个循环而不是主要问题 (x*y).

此处是 "loop" 方法的代码。我测试了它的小值([1, 8, 8, 2]。我知道我可以省去 for b 循环,但这不是 "meat" 成本,x*y 计算才是真正的问题:

logits = self.output_mask   # e.g. shape: [1, 1024, 1024, 2]
sigma = tf.ones(shape=[1024, 1024, 2], dtype=logits.dtype)  # dummy values, will be a learned parameter later

corrupted_logits_b = []
for b in range(logits.shape[0]):
    corrupted_logits_x = []
    for x in range(logits.shape[1]):
        corrupted_logits_y = []
        for y in range(logits.shape[2]):
            corrupted_logits_c = []
            for c in range(logits.shape[3]):
                # this is where the noise is computed
                # (added to the logit since mean = logit)
                corrupted_logit = tf.random_normal(tf.shape(logits[b, x, y, c]), 
                                                            mean=logits[b, x, y, c], 
                                                            stddev=sigma_val[x, y, c])
                # "values"/logit-tensors are appended to lists and
                # ... and stacked to form higher-dim tensors
                corrupted_logits_c.append(corrupted_logit)
            corrupted_logits_y.append(tf.stack(corrupted_logits_c, axis=-1))
        corrupted_logits_x.append(tf.stack(corrupted_logits_y))
    corrupted_logits_b.append(tf.stack(corrupted_logits_x))
corrupted_logits = tf.stack(corrupted_logits_b, axis=-1)

所以我实际上找到了一个很好的解决方法,使用正态分布 N(0, 1) 乘以 σ (sigma)与正态分布相同 with stddev σ (sigma) N(0, σ**2):

N(0,σ**2) = N(0,1)⋅σ

因此你可以这样做:

# sigma can be of shape of logits or needs to be broadcastable
sigma = tf.Placeholder(shape=tf.shape(logits), dtype=logits.dtype)
def corrupt_logits(logits, sigma):
    # generate a normal distribution N(0,1) of desired shape (4D in my case)
    gaussian = tf.random_normal(tf.shape(logits), mean=0.0, stddev=1.0, dtype=logits.dtype)
    # turn into normal distribution N(0,σ**2) by multiplying with σ
    # since sigma is a 4D tensor of tf.shape(logits), it can "assign" an individual σ to each logit
    noise = tf.multiply(gaussian, sigma)
    # add zero-mean/stddev-σ noise to logits
    return tf.add(logits, noise)

这会比 for-loops 好很多,并且支持各种变体,因为如果某个维度的 sigma 相同(例如,每个 x、y 仅不同),它会自动广播其他维度(例如 batch_size、类)。