在高斯分布中添加真实噪声,同时保持样本数量在阈值上下大致恒定
我有一个正态分布和一个阈值函数,用来判断一个值是否为真。
我想给这些真值添加一些噪声,这样的话,越是远离阈值,值被翻转的可能性就越小。比如在极端情况下,翻转的概率可能只有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
个为真。