如何避免numpy.random.choice中的舍入误差?

4

假设有n个对象x_1, x_2, ..., x_n,希望从中选出一个对象,使得选择的概率与某个数值u_i成正比。Numpy提供了相应的函数:

x, u = np.array([x_1, x_2, ..., x_n]), np.array([u_1, ..., u_n])
np.random.choice(x, p = u/np.sum(u))

然而,我发现这段代码有时会抛出ValueError错误,提示“概率不总和为1”。这可能是由于有限精度算术的四舍五入误差导致的。如何使该函数正常工作?


你担心的是哪种类型的错误? - Mortz
1
请仅返回翻译后的文本:类似的问题 - Pychopath
@Mortz 确切地说是这样的:"ValueError:概率总和不为1"。 - Fırat Kıyak
@Pychopath指出的问题的解决方案是否有帮助? - Mortz
1
@Mortz提供了一个解决方案。numpy.random.multinomial (https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.random.multinomial.html) 自动调整最后一个概率来解决这个问题,但是需要注意的是不应该依赖这种方法。其他答案并没有给出令人满意的答案。例如,那个问题的被接受的解决方案 https://dev59.com/9FYN5IYBdhLWcg3w_sya#46539921 建议归一化概率,但由于舍入误差可能无法解决问题。请参见pd shah对该答案的评论。 - Fırat Kıyak
1
这真的引出了一个问题,为什么numpy不在内部处理这些东西呢?我的意思是,numpy的一个关键点是使得进行复杂的数值计算变得容易,而不必成为IEEE-754舍入问题的专家。 - Leopd
2个回答

5
在阅读由@Pychopath提出的问题所指向的答案https://dev59.com/9FYN5IYBdhLWcg3w_sya#60386427后,我找到了以下解决方案,受到numpy.random.multinomial文档https://docs.scipy.org/doc/numpy-1.15.0/reference/generated/numpy.random.multinomial.html的启发。
假设p是概率数组,可能由于舍入误差而不完全等于1,即使我们使用p = p/np.sum(p)对其进行了归一化。这并不少见,在回答https://dev59.com/9FYN5IYBdhLWcg3w_sya#46539921中@pd shah的评论中也有提到。
只需执行:
p[-1] = 1 - np.sum(p[0:-1])
np.random.choice(x, p = p)

问题已经解决!由于减法引起的舍入误差要比由于归一化引起的舍入误差小得多。此外,人们不必担心p的变化,它们的数量级是舍入误差。


1
最好使用 p[-1] = max(0, 1 - np.sum(p[0:-1])),因为有时舍入误差会导致最后一个数字变成负数(例如-1e-16),这也会导致 np.random.choice 失败,并出现 ValueError: probabilities are not non-negative 的错误提示。 - Leopd
哦,好吧...这是生成错误的源代码,但我不确定修复问题的最佳方法是什么,或者为什么numpy还没有解决它。https://github.com/numpy/numpy/blob/main/numpy/random/mtrand.pyx - Reza Roboubi
1
好的,我的问题似乎已经得到解决:p = np.array(p, dtype=numpy.float64),即类型转换。我之前使用了Jax数组。是我的错误。 - Reza Roboubi

0
根据NumPy文档,我们必须使用p1-D array-like。所以我认为如果u-array是概率数组,那么你可以尝试它:
x, u = np.array([x_1, x_2, ..., x_n]), np.array([u_1, ..., u_n])
np.random.choice(x, p = u)

或者

x, u = np.array([x_1, x_2, ..., x_n]), np.array([u_1, ..., u_n])
s = sum(u)
u1 = [i/s for i in u]
np.random.choice(x, p = u1)

这并没有回答我的问题。第二段代码几乎和我发布的一样。我担心由于除法中有限精度算术而发生的累积误差。这可能导致概率之和不等于(完全)1。 - Fırat Kıyak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接