天天看点

《树莓派开发实战(第2版)》——2.2 创建模型和运行推理:重回Hello World

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

您已经概要了解了figaro概念,接下来看看它们是如何融合在一起的。您将回顾第1章的hello world示例,特别注意图2-2中的所有概念是如何出现在这个例子中的。您将关注如何从原子和复合元素中构建模型,观测证据,提出查询,运行推理算法,得到答案。

本章的代码可以两种方式运行。一种是使用scala控制台,逐行输入语句并获得即时响应。为此,进入本书项目根目录practicalprobprog/examples并输入sbt console,将会看到scala提示符。然后输入每行代码,查看响应。

第二种方式是通常的方法:编写一个包含main方法的程序,该方法包含想要执行的代码。在本章中,我不提供将代码转换为可运行程序的模板,只提供与figaro相关的代码。我将确保指出您需要导入的内容及将其导入的位置。

首先,您将构建最简单的figaro模型。这个模型包含一个原子元素。构建模型之前,必须导入必要的figaro结构:

val sunnytoday = flip(0.2)<code>`</code>

图2-3解释了这一行代码。搞清楚哪一部分是scala,哪一部分是figaro,是很重要的。在这行代码中,创建了一个名为sunnytoday的变量,并赋值flip(0.2)。scala值flip(0.2)是一个figaro元素,表示true值概率为0.2、false值概率为0.8的一个随机过程。元素是表示随机产生一个值的过程的数据结构。随机过程可能产生任意数量的结果。每个可能结果被称为过程的一个值。因此,flip(0.2)是可能取值为布尔值true及false的元素。总结起来就是,您有了一个包含scala值的scala变量。该scala值是figaro元素,它包含表示过程不同结果的任意个可能取值。

《树莓派开发实战(第2版)》——2.2 创建模型和运行推理:重回Hello World

在scala中,类型可以由另外一种描述其内容的类型参数化。您可能从java泛型中已经熟悉了这个概念,例如,在java中可以得到一个整数或者字符串的列表。所有figaro元素都是element类的实例。element类由元素可能取值的类型参数化。这种类型称作元素的值类型。因为flip(0.2)可以取布尔值,flip(0.2)的值类型为boolean。这一事实的标记方法是:flip(0.2)是element[boolean]的一个实例。

关键定义

元素——代表一个随机过程的figaro数据结构。

值——随机过程的一个可能结果。

值类型——代表元素可能取值的scala类型。

关于这个简单模型有许多值得说明的地方。幸运的是,您已经学到的知识适用于所有figaro模型。figaro模型通过取得和组合简单的figaro元素(构件)创建更复杂的元素和相关元素集合而创建。您刚刚学到的元素、值和值类型的定义是figaro中最为重要的定义。

在继续构建更复杂的模型之前,我们先来看看如何用这个简单模型进行推理。

您已经构建了一个简单模型。我们运行推理,查询sunnytoday为true的概率。首先,需要导入将要使用的推理算法:

println(variableelimination.probability(sunnytoday, true))<code>`</code>

上述命令打印输出0.2。您的模型只包含元素flip(0.2),结果为true的概率为0.2。变量消除算法正确计算出sunnytoday为true的概率是0.2。

详细说来,您刚刚看到的命令完成好几件工作:首先创建变量消除算法的一个实例,告诉实例查询目标是sunnytoday。然后运行算法并返回sunnytoday值为true的概率。这条命令还负责执行完毕的清理,释放算法所用的任何资源。

现在,我们开始构建一个更有趣的模型。您需要一个figaro结构——if,因此要导入它。还需要一个名为select的结构,但是这已经随着com.cra.figaro.language导入:

val greetingtoday = if(sunnytoday,

对此的思维方式是元素代表一个随机过程。在本例中,名为greetingtoday的元素代表着这样的过程:首先检查sunnytoday的值,如果为true,选择“hello,world!”的概率为0.6,“howdy, universe!”的概率为0.4。如果sunnytoday的值为false,选择“hello, world!”的概率为0.2,“oh no, not again”的概率为0.8。greetingtoday是一个复合元素,因为它由3个元素构建而成。由于greetingtoday的可能取值为字符串,所以它是element[string]。

现在,假定您已经看到今天的问候语是“hello, world!”,您可以使用一个观测值说明这一证据:

这条命令打印输出0.4285714285714285。注意,结果明显高于前一个答案(0.2)。这是因为问候语是“hello, world!”时,今天是晴天的可能性高于其他情况,所以证据支持今天是晴天。这一推理是贝叶斯法则的简单实例,第9章将介绍这一法则。

您打算扩展该模型,用不同证据运行更多查询,所以应该移除变量greetingtoday的观测值。用如下命令可以完成:

您将得到和指定证据之前一样的答案0.2。

在本节的最后,我们进一步细化模型:

println(variableelimination.probability(greetingtomorrow, "hello, world!"))

// prints 0.27999999999999997

greetingtoday.observe("hello, world!")

// prints 0.3485714285714286<code>`</code>

可以看到,在观察到今天的问候语是“hello, world!”时,明天的问候语是“hello, world!”的概率增大,为什么?因为今天的问候语是“hello, world!”,今天就更有可能是晴天,明天是晴天的可能性也就更大,最终使明天的问候语更可能是“hello, world!”。正如在第1章中所看到的,这是推断过去更好预测未来的一个例子,figaro负责所有的计算。

现在,您已经看到了创建模型、指定证据和查询、运行推理得到答案的所有步骤,接下来我们更仔细地观察hello world模型,理解如何从构件(原子元素)和连接器(复合元素)构建它。

图2-4是模型的图形描述。该图首先重现了模型定义,每个scala变量在一个单独的方框中。在下半部分中,每个节点表示模型中的对应元素,同样在单独的方框中显示。有些元素本身就是scala变量值。例如,scala变量sunnytoday的值是flip(0.2)元素。如果元素是scala变量值,图上显示变量名称和元素。该模型还包含了一些不是特定scala变量值,但仍出现在模型中的元素。例如,因为sunnytomorrow的定义是if(sunnytoday, flip(0.8), flip(0.05)),flip(0.8) 和flip(0.05)也是模型的一部分,所以它们显示为图中的节点。

《树莓派开发实战(第2版)》——2.2 创建模型和运行推理:重回Hello World

该图包含了元素之间的边。例如,从flip(0.8)到取sunnytomorrow值的if元素之间有一条边,表明if元素使用flip(0.8)元素。一般来说,如果第二个元素的定义中使用了第一个元素,则两者之间存在一条边。因为只有复合元素是由其他元素构建而成的,所以只有复合元素可能成为边的终点。

需要注意的一点是,select(0.6 -&gt; "hello, world!", 0.4 -&gt; "howdy, universe!")在图中出现了两次,select(0.2 -&gt; "hello, world!", 0.8 -&gt; "oh no, not again")也是如此。这是因为代码中定义出现了两次,一次用于greetingtoday,另一次用于greetingtomorrow。尽管定义相同,但是这是两个不同的元素。它们在figaro模型定义的随机过程的同一次执行中可能取不同的值。例如,该元素的第一个实例可能取值“hello, world!”,而第二个实例可能取值“howdy, universe!”。这是有意义的,因为第一个元素实例用于定义greetingtoday,第二个则用于定义greetingtomorrow。今天和明天的问候语很可能不一样。

这和常规编程类似,想象一下您有一个greeting类和如下代码:

val greetingtoday = new greeting

val anothergreetingtoday = greetingtoday

anothergreetingtoday.string = "howdy, universe!"<code>`</code>

anothergreetingtoday和greetingtoday是相同的scala变量,所以运行上述代码之后,greetingtoday的值也是“howdy, universe!”,同样,如果同一个scala变量代表程序中出现多次的一个元素,它在每次运行中也取相同的值。

理解这一点对于了解figaro模型的构建方式是必不可少的,所以我建议反复阅读本节以确保理解。此时,您应该已经概要了解所有的figaro主要概念以及它们的组合方式。在下面几节中,您将更详细地研究其中一些概念,下一节首先介绍原子元素。