【问题标题】:Probability distributions and float variables, probability must add to 1概率分布和浮点变量,概率必须加到 1
【发布时间】:2014-08-27 08:57:49
【问题描述】:

我正在编写这样一个脚本:程序分析一堆特定语言的文本文档,绘制每个 k 的概率分布,其中 k 是出现在字母表中每个给定字母之后的第一个字符在文本的每个单词中。然后程序利用这些知识尝试使用马尔可夫链写出“真实”的单词。

我已经写了大部分脚本,而且它已经吐出了有趣的单词,关键是生成单词的函数正在使用 try 和 except 机制来避免卡住。它卡住了,因为某些概率分布不会加到 1(我猜是因为浮点类型不是那么精确或类似的东西)并且应该与这些分布一起使用的 numpy 函数会引发 ValueError 因为概率加起来不等于 1 .

通过触发某些分布的异常,根本不会生成某些单词,最终结果不如它可能的有趣。

现在,我的问题是:有没有办法让这些概率分布在生成时加起来等于 1? 我试过 gmpy2,round() 函数,但似乎没有人工作。也许这是一个愚蠢的问题,我只需要呼吸新鲜空气......无论如何,一些帮助会很有用!

这是生成概率分布的代码

def FreqRel(self,listValues):
    absFreq = self.AbsFreq(listValues)
    freqRel = []
    for i in absFreq:
        freqRel.append(i/sum(absFreq))
    if sum(freqRel) != 1:
        print("Frequencies do not add up to 1")
        if sum(freqRel) - 1 < 0:
            diff = sum(freqRel) - 1
            #This should be an adjustment which should not interfere
            #that much on the probability distribution
            freqRel[1] = freqRel[1] - diff
            print("missing",diff)
        elif sum(freqRel) - 1 > 0:
            diff = sum(freqRel) - 1
            #This should be an adjustment which should not interfere
            #that much on the probability distribution
            freqRel[1] = freqRel[1] - diff
            print("Too much",diff)
    return freqRel

这是我在运行此函数时在控制台上打印的内容:

这是当总和不是 1 时崩溃的代码。numpy 行是崩溃的那些。 错误是:ValueError: probabilities do not add up to 1.

def spitText(n):
    i = 0
    while i < n:
        try:
            word = ""
            #This oldChar setting is arbitrary, later I'm going to fix it
            oldChar = "b"
            for k in range(np.random.choice(distributions[0],replace=True,p=distributions[1])):
                newChar = np.random.choice(alphabet,replace=True,p=distRel[alphabet.index(oldChar)])
                word = word + newChar
                oldChar = newChar
            print(word)
            time.sleep(0.2)
            i+=1
        except:
            pass

【问题讨论】:

  • 你的freqAss 是什么?它起源于哪里?
  • 对不起,应该是 absFreq 而不是 freqAss。那是一个错字。刚刚编辑,谢谢!
  • 你能告诉我们总和不为1时崩溃的代码吗?
  • 预计 1e-17 量级的舍入误差是完全合理且不可避免的,除非您的所有概率恰好是精确的二进制分数。这就是花车的工作原理。您无法通过消除舍入误差来解决此问题;您只能通过更改代码以某种合理的方式处理该舍入错误来解决它。由于您没有向我们展示相关代码,我们无法向您展示如何更改它。
  • 当我运行np.random.choice 时,只有当概率总和偏离-5 个相对数量级时,我才会得到该错误。对1+3e-17 的总和感到非常满意;只有当您高于1+1e-5 时,它才会抱怨。所以,我很确定这不是你的真实数据,而且你在某个地方有一个实际的错误,而不仅仅是一个舍入错误。

标签: python python-3.x numpy distribution probability


【解决方案1】:

你有一些看起来像这样的输出:

1.0
1.0
1.0
0
1.0
1.0

来自评论:

这是这个函数之外的一个简单的 for 循环,它打印出每个分布的总和,存储为这个函数的返回值

因此,您的一些频率分布总和为0。那是你的问题。

大概你构建分布的代码有一些极端情况,要么返回一个空分布,要么返回一个全为零的分布。不管怎样,这显然是行不通的。


许多1.0 值因累积舍入误差高达 8e-17 而偏离这一事实是一个红鲱鱼。你可以看到 numpy 是为处理这些而构建的:

>>> np.random.choice(2, 3, p=[0.4, 0.6+3e-17])
array([1, 0, 0])

只有当错误变得足够大时(大多数 numpy 的默认相对 epsilon 是 1e-5)它才会抱怨:

>>> np.random.choice(2, 3, p=[0.4, 0.6+3e-5])
ValueError: probabilities do not sum to 1

因此,您必须有一些概率分布,其总和与1 相差超过1e-5。你当然知道;你有一些被整个 1 关闭。


这意味着你的主要问题:

有没有办法让这些概率分布在生成时加起来为 1?

…真的是XY problem:这不是你需要在这里解决的问题。

但无论如何我都会回答。简短的回答是:不。浮点数是具有固定位数精度的二进制分数。如果您尝试将任意实数存储在浮点数中,则会出现舍入错误。你可以很容易地看到这一点:

>>> 1.0 + 1e-17
1.0

没有足够的位将1.01.00000000000000001 存储为不同的二进制分数。

如果您想进一步了解(并且应该),请阅读What every computer scientist should know about floating point,这是关于该主题的经典介绍性论文。

【讨论】:

  • 就是这样!有些字母根本没有出现,因此它们的分布总和为 0,从而导致采样时出现错误。我怀疑这是愚蠢的……谢谢阿巴纳特!我去看看论文!
  • @mickkk:我想到了类似的东西。大多数错误是由您事先从未想到的简单边缘情况引起的,但是一旦您知道要查找它们,就会很明显。 (嗯,那个,还有愚蠢的错别字,无论我多么努力地盯着它,我都无法在自己的代码中看到......)你不高兴计算机科学不是你的主要领域吗? :)
  • +1 很棒的收获! @mickkk:你可以试试Laplace Smoothing 来解决这个问题。
  • @abarnert:就此而言:是的 :) 然而,CS 如此有用和令人着迷的事实使得这些烦人的“问题”即使对于像我这样的新手来说也完全可以忍受 :)
猜你喜欢
  • 1970-01-01
  • 2015-11-03
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-06
  • 2020-07-02
  • 1970-01-01
相关资源
最近更新 更多