在高斯分布中添加真实噪声,同时保持样本数量在阈值上下大致恒定

1 投票
1 回答
763 浏览
提问于 2025-04-18 10:16

我有一个正态分布和一个阈值函数,用来判断一个值是否为真。

我想给这些真值添加一些噪声,这样的话,越是远离阈值,值被翻转的可能性就越小。比如在极端情况下,翻转的概率可能只有1%,而在阈值附近,翻转的概率是50%。

我还希望在添加噪声之前和之后,阈值上下的样本数量大致保持不变。

下面的代码我觉得可以实现前半部分,但对后半部分的处理不太确定。(也许可以尝试从rnum中减去一个小值,直到翻转前后真值的总和在某个误差范围内)

import numpy as np

mean = .5
std_dev = .2
num_points = 10000

arr =  np.sort( np.random.normal(loc=mean, scale= std_dev, size=(num_points)) )

threshold = .8

trues = arr >= threshold

temp = np.where(trues, 1-arr, arr)
scaling = max(temp)
temp *= .5/scaling

rnum = np.random.random(size=(num_points))

flip = rnum <= temp

trues = np.logical_xor(trues, flip)

1 个回答

1

如果我理解得没错,你想要一个输出向量,具备以下特性:

  • 布尔向量,也就是只有真和假
  • 元素数量和输入向量一样
  • 每个元素为真的概率取决于它与阈值的关系
  • 为真的数量和简单阈值处理的结果一样

所以,你需要一个概率函数,能够告诉你每个输入值变成输出值为真的概率。用简单的阈值处理时,阈值以上的概率是1,阈值以下的概率是0。而你需要的是一种更柔和的方式。

如果不考虑最后一个要求(为真的数量),这个算法会简单很多。概率函数的输出会和0到1之间的随机值进行比较,结果就是这样。根据输入信号的分布和概率函数,这样可能会得到不错的结果。

这里有个例子:

# threshold at 0.8, below 0.7 always false, above 0.9 always True, linear in between
def prob_f(x):
    return np.clip((x - 0.8) / .2 + .5, 0., 1.)


def noisy_threshold(sig):
    p = prob_f(sig)
    return p > random.random(sig.shape)

但是如果需要更好地匹配为真的数量,我们就需要在之后做一些处理。我们需要一个函数,输入期望的为真数量和概率。当然,这样做会改变结果分布的一些特性,所以没有“干净”的方法来实现。

一种可能的方法是稍微调整我们的概率阈值。例如:

def_ noisy_threshold(sig, threshold):
    # number of Trues with simple thresholding
    n_trues = np.asum(sig > threshold)

    # difference between random noise and our probability
    rdiff = prob_f(sig) - random.random(sig.shape)

    # sort the differences
    sortdiff = sorted(rdiff)

    # a new threshold is used so that the number of Trues is correct:
    return rdiff >= sortdiff[-n_trues]

这样做的话,如果我们没有非常不幸地得到一些完全相同的随机差异,就能准确返回n_trues个为真。

撰写回答