《树莓派开发实战(第2版)》——2.4 使用复合元素组合原子元素

    xiaoxiao2024-06-03  102

    本节书摘来异步社区《概率编程实战》一书中的第2章,第2.4节,作者:【美】Avi Pfeffer(艾维·费弗),更多章节内容可以访问云栖社区“异步社区”公众号查看。

    2.4 使用复合元素组合原子元素

    在本节中,您将看到一些复合元素。前面已经说过,复合元素是构建于其他元素之上的更为复杂的元素。复合元素的例子很多,您将首先考察两个特殊的例子,If和Dist,然后了解如何使用大部分原子元素的复合版本。

    2.4.1 If

    您已经看到了If复合元素的一个例子。它由3个元素组成:一个测试,一个then子句和一个else子句。If表示这样的随机过程:首先检查测试的结果,如果测试值为true,则生成then子句的值;否则,生成else子句的值。图2-7展示了If元素的一个例子。If取3个参数,第一个参数是Element[Boolean],例中是sunnyToday元素。这个参数表示测试。第二个参数是then子句,当测试元素的值为true,则选中这个元素。如果测试元素值为false,则选择第三个参数——else子句。then和else子句必须有相同的值类型,这将成为If元素的值类型。

    下面是If元素的执行情况:

    val sunnyToday = Flip(0.2) val greetingToday = If(sunnyToday, Select(0.6 -> "Hello, world!", 0.4 -> "Howdy, universe!"), Select(0.2 -> "Hello, world!", 0.8 -> "Oh no, not again")) println(VariableElimination.probability(greetingToday, "Hello world!")) // prints 0.27999999999999997``` 上述代码打印输出0.28(在舍入误差范围内)是因为then子句被选中的概率为0.2(当sunnyToday为true),而这种情况下“Hello, world!”出现的概率为0.6。同时,else子句被选中的概率为0.8,此时“Hello, world!”出现的概率为0.2。所以,“Hello, world!”出现的总概率为(0.2 × 0.6) + (0.8 × 0.2) = 0.28。您可以通过明确评估两种情况看到这一结果:

    sunnyToday.observe(true)println(VariableElimination.probability(greetingToday, "Hello, world!"))// prints 0.6 because the then clause is always taken

    sunnyToday.observe(false)println(VariableElimination.probability(greetingToday, "Hello, world!"))// prints 0.2 because the else clause is always taken`

    2.4.2 Dist

    Dist是一个实用的复合元素,Dist与Select类似,但是它选择一组元素中的一个而不是选择一组值中的一个。每个选择本身是一个元素,这也就是Dist是复合元素的原因。如果您想要在本身是随机过程的复杂选择之间选择,Dist很实用。例如,足球中的角球可能从短传或者高吊传中开始。这可以由一个Dist元素表示,该元素在两个过程之间选择,一个从短传开始,另一个从高吊传中开始。

    Dist在com.cra.figaro.language包中,下面是Dist元素的一个例子:

    val goodMood = Dist(0.2 -> Flip(0.6), 0.8 -> Flip(0.2))如图2-8所示,Dist元素的结构类似于图2-5中Select的结构。不同之处在于,选择的不是结果值而是结果元素。您可以这样认为:Select直接选择一组可能值中的一个,没有任何中间过程。Dist是间接的:它选择一组过程中的一个运行,在运行中产生一个值。在例子中,由Flip(0.6)表示的过程被选中的概率为0.2,由Flip(0.2)表示的过程被选中的概率为0.8。

    下面是查询这一元素得到的结果:

    println (VariableElimination.probability(goodMood, true)) // prints 0.28 = 0.2 * 0.6 + 0.8 * 0.2``` ####2.4.3 原子元素的复合版本 您已经看到了采用数值参数的原子元素。例如,Flip以概率为参数,Normal以均值和方差为参数。如果您对这些数值参数不确定该怎么办?在Figaro中,答案很简单:将它们自身作为元素。 制作数值参数元素时,您就得到了原子元素的复合版本。例如,下面的代码定义一个复合Flip元素,并用它进行推理:

    val sunnyTodayProbability = Uniform(0, 0.5)val sunnyToday = Flip(sunnyTodayProbability)println(Importance.probability(sunnyToday, true))// prints something like 0.2548`这里,sunnyTodayProbability表示对今天是晴天的概率的不确定性,您认为0~0.5的任何值可能性一样大。那么,sunnyToday为true的概率等于sunnyTodayProbability元素的值。一般来说,复合Flip取单一参数,即表示Flip为true概率的一个Element[Double]。

    Normal提供了更多可能性。在概率推理中,假定正态分布有一个特定的已知方差,通过均值表示不确定性的情况很常见。例如,您可能假定温度的方差为100,而均值在40左右,但是对此不太确定。此时可以用如下代码捕捉这种不确定性:

    val tempMean = Normal(40, 9) val temperature = Normal(tempMean, 100) println(Importance.probability(temperature, (d: Double) => d > 50)) // prints something like 0.164``` 此外,您也可能对方差不确定,认为它可能是80或者105。此时可以使用如下代码:

    val tempMean = Normal(40, 9)val tempVariance = Select(0.5 -> 80.0, 0.5 -> 105.0)val temperature = Normal(tempMean, tempVariance)println(Importance.probability(temperature, (d: Double) => d > 50)// prints something like 0.1549`

    最新回复(0)