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

2.2 创建模型和运行推理:重回Hello World

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

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

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

2.2.1 构建第一个模型

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

import com.cra.figaro.language._```
上述语句导入com.cra.figaro.language包中的所有类,该包包含最基本的Figaro结构。这些类中有一个称为Flip。可以用Flip构建一个简单模型:

val sunnyToday = Flip(0.2)`
图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元素,它包含表示过程不同结果的任意个可能取值。

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

关键定义
元素——代表一个随机过程的Figaro数据结构。

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

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

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

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

2.2.2 运行推理和回答查询

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

import com.cra.figaro.algorithm.factored.VariableElimination```
上述语句导入所谓的“变量消除”(variable elimination)算法,这是一种精确的推理算法,也就是说,它可以准确地计算您的模型和证据隐含的概率。概率推理很复杂,所以精确的算法有时候需要花费很长时间,或者耗尽内存。Figaro提供近似算法,这种算法通常提供与准确答案大致相同的答案。本章使用简单的模型,所以变量消除算法在大部分情况下可行。

现在,Figaro提供一个简单命令以指定查询、运行算法并获得答案。可以编写如下的代码:

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

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

2.2.3 构建模型和生成观测值

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

import com.cra.figaro.library.compound.If```
我们使用If和Select构建更复杂的元素:

val greetingToday = If(sunnyToday,

 Select(0.6 -> "Hello, world!", 0.4 -> "Howdy, universe!"),
 Select(0.2 -> "Hello, world!", 0.8 -> "Oh no, not again"))```

对此的思维方式是元素代表一个随机过程。在本例中,名为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!”,您可以使用一个观测值说明这一证据:

greetingToday.observe("Hello, world!")```
接下来,您可以根据问候语是“Hello, world!”算出今天是晴天的概率:

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

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

greetingToday.unobserve()```
现在,如果发出如下查询:

println(VariableElimination.probability(sunnyToday, true))`
您将得到和指定证据之前一样的答案0.2。

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

val sunnyTomorrow = If(sunnyToday, Flip(0.8), Flip(0.05))
val greetingTomorrow = If(sunnyTomorrow,
     Select(0.6 -> "Hello, world!", 0.4 -> "Howdy, universe!"),
     Select(0.2 -> "Hello, world!", 0.8 -> "Oh no, not again"))```
您可以计算在有无关于今天问候语的证据情况下,明天的问候语为“Hello, world!”的概率:

println(VariableElimination.probability(greetingTomorrow, "Hello, world!"))
// prints 0.27999999999999997

greetingToday.observe("Hello, world!")
println(VariableElimination.probability(greetingTomorrow, "Hello, world!"))
// prints 0.3485714285714286`
可以看到,在观察到今天的问候语是“Hello, world!”时,明天的问候语是“Hello, world!”的概率增大,为什么?因为今天的问候语是“Hello, world!”,今天就更有可能是晴天,明天是晴天的可能性也就更大,最终使明天的问候语更可能是“Hello, world!”。正如在第1章中所看到的,这是推断过去更好预测未来的一个例子,Figaro负责所有的计算。

2.2.4 理解模型的构建方法

现在,您已经看到了创建模型、指定证据和查询、运行推理得到答案的所有步骤,接下来我们更仔细地观察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)也是模型的一部分,所以它们显示为图中的节点。

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

2.2.5 理解重复的元素:何时相同,何时不同

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

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

class Greeting {
  var string = "Hello, world!"
}
val greetingToday = new Greeting
val greetingTomorrow = new Greeting
greetingTomorrow.string = "Howdy, universe!"```
尽管定义完全相同,greetingToday 和 greetingTomorrow是Greeting类的两个不同实例。因此,greetingTomorrow.string和greetingToday.string可能取不同值,后者仍然等于“Hello, world!”。同样,Figaro构造函数(如Select)创建对应元素类的新实例。所以greetingToday和greetingTomorrow是Select元素的两个不同实例,因此在一次运行中可能取不同的值。

另一方面,注意Scala变量sunnyToday也出现了两次,一次在greetingToday的定义中,另一次在sunnyTomorrow中。但是本身是sunnyToday值的元素在图中仅出现一次。为什么?因为sunnyToday是一个Scala变量,而不是Figaro元素定义。当Scala变量在一段代码中出现超过一次时,它是相同的变量,所以使用相同的值。在我们的模型中,这是有意义的;它表示同一天的天气,用于greetingToday和sunnyTomorrow的定义中,所以在模型的任何随机执行中都取相同的值。

常规代码中也会发生相同的事情。如果编写如下代码:

val greetingToday = new Greeting
val anotherGreetingToday = greetingToday
anotherGreetingToday.string = "Howdy, universe!"`
anotherGreetingToday和greetingToday是相同的Scala变量,所以运行上述代码之后,greetingToday的值也是“Howdy, universe!”,同样,如果同一个Scala变量代表程序中出现多次的一个元素,它在每次运行中也取相同的值。

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

时间: 2024-09-24 21:49:30

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

《树莓派开发实战(第2版)》——导读

前言 树莓派开发实战(第2版)自从2011年诞生以来,树莓派已经成为基于Linux的低成本电脑和嵌入式计算平台这两个领域中的重要角色.同时,也受到了教育工作者和业余爱好者们的一致好评. 自从本书第1版问世以来,树莓派的销售量已超过了几百万台,同时还出现了许多新型号的树莓派.某些型号,比如B+.A+和B+型树莓派2等,还对该设备的规范进行了改进,出现了具有4核处理器的树莓派2和树莓派计算模块,即可以把树莓派作为一个插件式电路板用作更大系统的零部件. 针对出现的各种新型树莓派以及Raspbian操作

《树莓派开发实战(第2版)》——第2章 网络连接

第2章 网络连接 树莓派开发实战(第2版) 2.0 引言 树莓派在设计之初,便是要连接到互联网的.互联网通信是它的关键功能之一,这给其他各种用途铺平了道路,例如家庭自动化.Web服务.网络监控等. 树莓派既可以使用以太网线缆(这种情况至少要求是B型树莓派)联网,也可以使用USB Wi-Fi无线网卡连接网络.树莓派一旦连接到互联网上面,就意味着你可以通过其他计算机来远程连接树莓派.这对于本身难以接近,或者没有连接键盘.鼠标和显示器的树莓派来说,是非常有用的. 接下来,本章将详细介绍树莓派连接互联网

《树莓派开发实战(第2版)》——1.10 优化性能

1.10 优化性能 面临问题你感觉自己的树莓派运行得太慢了,所以,想通过超频来提速. 解决方案如果你使用的是4核处理器的树莓派2的话,你不会有太慢的感觉.但是,如果你使用的是单核心处理器的老版树莓派的话,感觉就像是老牛拉破车. 为了提高树莓派的运行速度,你可以使用超频方法. 当然,这会使树莓派的耗电量有所增加,同时也会使它变得更热(参考后面的讨论). 这里使用的超频方法称为动态超频,因为它会自动检测树莓派的温度,并且一旦温度过高,时钟频率就会自动下调. 为了给树莓派超频,可以在终端输入下列命令来

《树莓派开发实战(第2版)》——1.2 为什么使用概率编程

1.2 为什么使用概率编程 概率推理是机器学习的基础技术之一.Google.Amazon和Microsoft等公司使用它理解可用数据.概率推理已经用于各种各样的应用程序,如预测股价.推荐电影.诊断计算机和检测网络入侵.许多应用都使用了本书中将要学习的技术. 前一小节中,有两个引人注目的要点. 概率推理可用于预测未来.推断过去,以及从过去的事实中学习更好地预测未来. 概率编程是使用图灵完备编程语言作为表示语言的概率推理. 将上面两个要点结合起来,可以得到如下表示. 事实:概率推理+图灵完备=概率编

《树莓派开发实战(第2版)》——1.3 Figaro简介:一种概率编程语言

1.3 Figaro简介:一种概率编程语言 在本书中,您将使用一种称为Figaro的概率编程系统.(我用莫扎特的歌剧<费加罗的婚礼>中的角色为其命名.我喜爱莫扎特,并在该剧于波士顿的一次演出中饰演巴尔托洛医生.)本书的主要目标是教授概率编程的原则,在本书中学到的技术应该可以在其他概率编程系统上沿用.附录B简单描述了现有的一些系统.但是,本书还有第二个目标--帮助您获得创建使用概率程序的亲身体验,并提供可以立即使用的工具.因此,许多例子都用Figaro代码实现. Figaro是从2009年开始开

《树莓派开发实战(第2版)》——2.1 连接有线网络

2.1 连接有线网络 面临问题 你想通过有线网络将树莓派连接到互联网. 解决方案 首先,如果你的树莓派的型号为A.A+或Zero的话,那么它们自身并没有提供RJ45以太网接口.这种情况下,最好使用USB无线网卡来连接互联网(见2.5节). 如果你的树莓派是B型的,可以将以太网电缆插入RJ45接口,同时将线缆另一端接入家用路由器后方的空闲接口中即可.图2-1展示的是一款老版的树莓派1,它的网络LED就在音频接口旁边.对于树莓派2来说,这些LED位于以太网接口自身内部. 树莓派一旦连接到网络上面,它

《树莓派开发实战(第2版)》——2.5 配置无线网络连接

2.5 配置无线网络连接 面临问题你想通过USB无线网卡将树莓派连接到互联网上. 解决方案如果你的Raspbian是最新版本的话,那么配置Wi-Fi的任务将易如反掌.你只需插入USB Wi-Fi无线网卡,然后在屏幕右上方单击Network图标即可(见图2-3).之后,你会看到一个无线网络清单.你选择网络后,系统将提示你输入Pre Shared Key,即密码. 输入密码,稍等片刻,Network图标就会变为标准的Wi-Fi标志,说明无线网络已经连接成功. 如果你使用的是老版的Raspbian的话

《树莓派开发实战(第2版)》——1.3 选择电源

1.3 选择电源 面临问题 你需要为树莓派选择电源. 解决方案 树莓派对电源的基本电气规格的要求为提供5V稳压直流电. 至于电源在电流大小方面的要求,这要取决于树莓派的具体型号以及其连接的外围设备.你最好采用一款能够轻松驱动树莓派的电源,因此电流一般不宜小于700mA.如果你在同一个卖家那里购买树莓派和电源的话,他通常能够告诉你某款电源是否与你的树莓派相匹配. 如果你打算使用无线上网卡或其他大功率的USB外设的话,那么最好选用能够提供1.5A甚至2A电流的电源. 此外需要注意的是,那些非常廉价的

《树莓派开发实战(第2版)》——2.6 使用控制台线联网

2.6 使用控制台线联网 面临问题 虽然没有网络连接可用,但是,你仍然希望能够从另一台计算机远程访问树莓派. 解决方案 使用控制台线来连接树莓派. 如果你打算以无外设的方式来使用树莓派的话,即不用键盘.鼠标或显示器,那么控制台线将是不二之选.控制台线的外观如图2-5所示,可以从Adafruit(https://www.adafruit.com/ )网站购买. 控制台线的连接方式如下. 1.将红色(5V)导线连接到GPIO接口左边缘上的5V引脚. 2.将黑色(GND)导线连接至上面用到的5V引脚左