本节书摘来异步社区《概率编程实战》一书中的第2章,第2.3节,作者:【美】avi pfeffer(艾维·费弗),更多章节内容可以访问云栖社区“异步社区”公众号查看。
现在正是积累figaro元素知识的时机。我将首先介绍模型的基本构件——原子元素。原子这一名称意味着,它们不依赖于任何其他元素,完全是独立的。在此我不提供完整的原子元素列表,只介绍最常见的例子。
原子元素根据值的类型分为离散元素和连续元素。离散原子元素取boolean和integer等类型的值,而连续原子元素通常使用double类型的值。从技术上说,离散意味着值之间有很清晰的分隔。例如,整数1和2有很清晰的分隔,其中没有任何整数。而连续意味着值处于一个没有分隔的连续域中,如实数。在任何两个实数之间都有更多的实数。离散和连续元素之间的差异造成了概率定义的差异,第4章中将做介绍。
有些人认为离散就意味着有限。这是错误的。例如,整数有无穷多个,但是它们是清晰分隔的,所以是离散值。
关键定义
原子元素——不依赖于任何其他元素的独立元素。
复合元素——由其他元素构成的元素。
离散元素——值类型清晰分隔的元素。
连续元素——值类型没有分隔的元素。
让我们来看一些离散原子元素的例子:flip、select和binomial。
flip
您已经看到了离散原子元素flip。flip包含在com.cra.figaro.language包中,该包中有许多最常用的元素。我建议始终在程序开始处导入该包的所有内容。一般来说,flip取一个参数p,表示元素值为真的概率。p应该是0和1(包含)之间的数值。该元素值为假的概率是1-p。例如:
val greeting = select(0.6 -> "hello, world!", 0.3 -> "howdy, universe!", 0.1
println(variableelimination.probability(greeting, "howdy, universe!"))
// prints 0.30000000000000004<code>`</code>
注意,在select中,概率累加起来不一定等于1。如果它们加起来不等于1,概率将被规格化——加起来等于1,同时保持概率之间的比例。在下面的例子中,每个概率都等于前一个例子中的两倍,因此加总起来等于2。规格化之后恢复成前一个例子中的概率,因此得到相同的结果。
binomial是一个有用的离散元素。想象一下一周有7天,每天都有一个“晴天”元素flip(0.2)。现在您想要一个元素,其值为一周中放晴的天数。这可以用元素binomial(7, 0.2)实现。这个元素的值是总共尝试7次,每次尝试为true的概率为0.2的情况下,尝试结果为true的次数。可以这样使用它:
import com.cra.figaro.library.atomic.continuous.normal
val temperature = normal(40, 100)<code>`</code>
均值为40,方差为100,意味着标准差为10。现在,假定您想要用这一元素进行推理。figaro的变量消除算法只适用于可能取值个数有限的元素。特别是,它不能用于连续元素。所以,需要一个不同的算法。您将使用称作重要性抽样的算法,这是一种很适合于连续元素的近似算法。算法的运行方法如下:
import com.cra.figaro.library.atomic.continuous.uniform
val temperature = uniform(10, 70)
importance.probability(temperature, greaterthan50 _)
// prints something like 0.3334<code>`</code>
uniform元素取两个参数:最小值和最大值。从最小值到最大值的所有值概率密度相同。在前一个例子中,最小值为10,最大值为70,所以范围的大小为60。您的查询预测是该值是否在50~70——区间大小为20。所以,预测的概率为20/60或者1/3,可以看到,重要性抽样得到的结果与此接近。
最后说明一下:这个元素的官方名称是连续均匀分布(continuous uniform)。在<code>com.cra.figaro.library.atomic.discrete</code>包中还可以找到离散均匀分布。正如您的预期,离散均匀分布明确列出一组值,其中的每个值出现可能性相同。
好了,现在您已经了解了构件,下面我们来看看如何组合它们,创建更大的模型。