《树莓派开发实战(第2版)》——2.3 使用基本构件:原子元素

简介:

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

2.3 使用基本构件:原子元素

现在正是积累Figaro元素知识的时机。我将首先介绍模型的基本构件——原子元素。原子这一名称意味着,它们不依赖于任何其他元素,完全是独立的。在此我不提供完整的原子元素列表,只介绍最常见的例子。

原子元素根据值的类型分为离散元素和连续元素。离散原子元素取Boolean和Integer等类型的值,而连续原子元素通常使用Double类型的值。从技术上说,离散意味着值之间有很清晰的分隔。例如,整数1和2有很清晰的分隔,其中没有任何整数。而连续意味着值处于一个没有分隔的连续域中,如实数。在任何两个实数之间都有更多的实数。离散和连续元素之间的差异造成了概率定义的差异,第4章中将做介绍。

有些人认为离散就意味着有限。这是错误的。例如,整数有无穷多个,但是它们是清晰分隔的,所以是离散值。
关键定义
原子元素——不依赖于任何其他元素的独立元素。

复合元素——由其他元素构成的元素。

离散元素——值类型清晰分隔的元素。

连续元素——值类型没有分隔的元素。

2.3.1 离散原子元素

让我们来看一些离散原子元素的例子:Flip、Select和Binomial。

Flip

您已经看到了离散原子元素Flip。Flip包含在com.cra.figaro.language包中,该包中有许多最常用的元素。我建议始终在程序开始处导入该包的所有内容。一般来说,Flip取一个参数p,表示元素值为真的概率。p应该是0和1(包含)之间的数值。该元素值为假的概率是1-p。例如:

import com.cra.figaro.language._
val sunnyToday = Flip(0.2)
println(VariableElimination.probability(sunnyToday, true))
// prints 0.2

println(VariableElimination.probability(sunnyToday, false))
// prints 0.8```
Flip(0.2)的正式类型是AtomicFlip,是Element[Boolean]的子类。这使其区别于后面将会看到的CompoundFlip。

Select

您已经在Hello World程序中看到了Select元素。下面是一个例子:``Select(0.6 -> "Hello, world!", 0.3 -> "Howdy, universe!", 0.1 -> "Oh no, not again")``。图2-5展示了这个元素的构成。在圆括号中是一些子句。每个子句由一个概率、一个右箭头和一个可能结果组成。子句的数量是可变的,您可以使用任意个子句。图中有3个子句。因为所有结果的类型都是String,所以这个元素是Element[String]。同样,其正式类型是AtomicSelect[String]——Element[String]的子类。

<div style="text-align: center"><img src="https://yqfile.alicdn.com/ec5c7ba92444922cb4fcc84437b693b8d8d9276f.png" width="" height="">
</div>

很自然,Select元素对应于一个过程,其中每个可能的结果按照对应的概率而选择。下面是其工作方式:

val greeting = Select(0.6 -> "Hello, world!", 0.3 -> "Howdy, universe!", 0.1

 -> "Oh no, not again")

println(VariableElimination.probability(greeting, "Howdy, universe!"))
// prints 0.30000000000000004`
注意,在Select中,概率累加起来不一定等于1。如果它们加起来不等于1,概率将被规格化——加起来等于1,同时保持概率之间的比例。在下面的例子中,每个概率都等于前一个例子中的两倍,因此加总起来等于2。规格化之后恢复成前一个例子中的概率,因此得到相同的结果。

val greeting = Select(1.2 -> "Hello, world!", 0.6 -> "Howdy, universe!", 0.2
     -> "Oh no, not again")
println(VariableElimination.probability(greeting, "Howdy, universe!"))
//prints 0.30000000000000004
Binomial

Binomial是一个有用的离散元素。想象一下一周有7天,每天都有一个“晴天”元素Flip(0.2)。现在您想要一个元素,其值为一周中放晴的天数。这可以用元素Binomial(7, 0.2)实现。这个元素的值是总共尝试7次,每次尝试为true的概率为0.2的情况下,尝试结果为true的次数。可以这样使用它:

import com.cra.figaro.library.atomic.discrete.Binomial
val numSunnyDaysInWeek = Binomial(7, 0.2)
   println(VariableElimination.probability(numSunnyDaysInWeek, 3))
//prints 0.114688```
一般来说,Binomial取两个参数:尝试次数和每次尝试得出结果true的概率。Binomial的定义假定所有尝试是独立的,第一次尝试为真不会改变第二次尝试得出true的概率。

####2.3.2 连续原子元素
本节介绍两个连续原子元素的常见例子——Normal和Uniform。第4章详细说明连续元素。连续概率分布与离散分布略有不同,指定的不是每个值的概率,而是每个值的概率密度,概率密度描述的是以该值为中心的每个区间的概率。您仍然可以将概率密度视为和常规概率类似的概念,表明某个值与其他值可能性的对比。因为本章是Figaro的教程,第4章解释概率模型,我将把进一步的讨论推迟到那个时候。请放心,在后面这一点将会更加清晰。

Normal

正态分布是您可能熟悉的一种连续概率分布。正态分布还有其他一些名称,包括钟形曲线和高斯分布。图2-6展示了正态分布的概率密度函数。(如果非要吹毛求疵,可能称之为“单变量正态分布”更为合适,因为它定义了单一实数变量上的概率,您还可以在多个变量上定义多变量正态分布,但是我们不是那样的人,所以就称之为正态分布。)这个函数有一个均值——中心点(图中为1),以及一个标准差——函数沿中心点分布的程度(图中为0.5)。大约有68%的情况下,从正态分布生成一个值将得到与均值相差一个标准差的区间内的值。在统计和概率推理中,正态分布通常用均值和方差描述,后者是标准差的平方。图中的标准差为0.5,方差为0.25。因此,这个特定正态分布的标准规格描述是Normal(1,0.25)。

<div style="text-align: center"><img src="https://yqfile.alicdn.com/7e5ee13662e52db255b83705526d485da1ac985e.png" width="" height="">
</div>

Figaro遵循上述约定。它提供一个Normal元素,以均值和方差作为参数。可以这样定义Normal元素:

import com.cra.figaro.library.atomic.continuous.Normal
val temperature = Normal(40, 100)`
均值为40,方差为100,意味着标准差为10。现在,假定您想要用这一元素进行推理。Figaro的变量消除算法只适用于可能取值个数有限的元素。特别是,它不能用于连续元素。所以,需要一个不同的算法。您将使用称作重要性抽样的算法,这是一种很适合于连续元素的近似算法。算法的运行方法如下:

import com.cra.figaro.algorithm.sampling.Importance
def greaterThan50(d: Double) = d > 50
println(Importance.probability(temperature, greaterThan50 _))```
重要性抽样是一种每次产生不同答案的随机算法,这些答案通常应该在真值的附近。您得到的答案应该与0.1567接近,但是很有可能得到稍有不同的答案。

注意,这里的查询和之前略有不同。对于连续元素,取特定值(如50)的概率通常为0。原因是,连续元素中有无穷多个没有分隔的值。该过程得出50而非50.000000000000001或者之间其他值的可能性无限小。所以,通常不要向连续元素发出特定值概率的查询。

相反,您可以查询该值落入某个区间的概率。例子中的查询是预测greaterThan50,这个预测以双精度(Double)类型值作为参数,如果参数大于50则返回true。预测是一个元素值的布尔函数。当您查询某个元素是否满足预测时,询问的是将预测应用到某个元素值返回true的概率。在这个例子中,您的查询计算温度高于50的概率。

Scala注释:


greaterThan50之后的下划线告诉Scala,greaterThan50是一个传递给Importance. probability方法的函数值。如果没有这个下划线,Scala可能认为您试图将该函数应用到0参数上,从而出错。有时候,Scala可能自动理解这一点而无需明确地提供下划线。但有时候它无法做到,而会告诉您提供下划线。
Uniform

我们再来介绍一个同样熟悉的连续元素例子。Uniform元素取指定区间中的值,区间中的每个值可能性相同。您可以这样创建和使用Uniform元素:

import com.cra.figaro.library.atomic.continuous.Uniform
val temperature = Uniform(10, 70)
Importance.probability(temperature, greaterThan50 _)
// prints something like 0.3334`
Uniform元素取两个参数:最小值和最大值。从最小值到最大值的所有值概率密度相同。在前一个例子中,最小值为10,最大值为70,所以范围的大小为60。您的查询预测是该值是否在50~70——区间大小为20。所以,预测的概率为20/60或者1/3,可以看到,重要性抽样得到的结果与此接近。

最后说明一下:这个元素的官方名称是连续均匀分布(continuous uniform)。在com.cra.figaro.library.atomic.discrete包中还可以找到离散均匀分布。正如您的预期,离散均匀分布明确列出一组值,其中的每个值出现可能性相同。

好了,现在您已经了解了构件,下面我们来看看如何组合它们,创建更大的模型。

相关文章
|
1月前
|
Java 容器
Java常用组件、容器与布局
Java常用组件、容器与布局
14 0
|
1月前
|
存储 XML 编译器
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
47 3
|
4月前
|
SQL 数据库 C++
C++ Qt开发:SqlTableModel映射组件应用
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍`SqlTableModule`组件的常用方法及灵活运用。 在多数情况下我们需要使用SQL的方法来维护数据库,但此方式相对较为繁琐对于表格等数据的编辑非常不友好,在`Qt`中提供了`QSqlTableModel`模型类,它为开发者提供了一种直观的方式来与数据库表格进行交互。通过使用该组件可以将数据库与特定的组件进行关联,一旦关联被建立那么用户的所有操作均可以使用函数的方式而无需使用`SQL`语句,该特性有点类
28 1
|
8月前
|
容器
教你快速上手Flex弹性盒布局(容器属性)(三)
教你快速上手Flex弹性盒布局(容器属性)
|
8月前
|
JavaScript 容器
教你快速上手Flex弹性盒布局(容器属性)(一)
教你快速上手Flex弹性盒布局(容器属性)
|
XML Android开发 数据格式
鸿蒙开发(14)---ListContainer列表组件
鸿蒙开发(14)---ListContainer列表组件
278 0
鸿蒙开发(14)---ListContainer列表组件
|
XML 数据格式
鸿蒙开发(15)---PageSlider组件
鸿蒙开发(15)---PageSlider组件
213 0
鸿蒙开发(15)---PageSlider组件
|
XML 前端开发 数据格式
鸿蒙开发(16)---PageSliderIndicator组件
鸿蒙开发(16)---PageSliderIndicator组件
120 0
鸿蒙开发(16)---PageSliderIndicator组件
|
XML Android开发 数据格式
鸿蒙开发(1)---Text组件
鸿蒙开发(1)---Text组件
309 0
鸿蒙开发(1)---Text组件
|
XML Java 数据格式
鸿蒙开发(6)---TabList组件
鸿蒙开发(6)---TabList组件
168 0
鸿蒙开发(6)---TabList组件