[译]基于模型的机器学习 - 2.2 试验模型
构建好一个模型之后,首先要做的就是用一些简单的示例数据来试验它,检查它的行为是否合理。假设有一位候选人懂 C# 但不懂 SQL——我们会预期他答对第一题,而答错另外两题。那么让我们针对这种情况试验一下模型,看看对于这样的答题模式它会推断出哪些技能。为方便起见,我们用 isCorrect 来指代数组 [isCorrect1, isCorrect2, isCorrect3],因此我们要考虑的是 isCorrect 为 [true, false, false] 的情形。
我们想在给定这一组特定的 isCorrect 取值的条件下,推断候选人具备 csharp 和 sql 技能的概率。这是一个推断查询的例子,它由我们想要推断的变量(csharp、sql)以及我们所条件化的变量(isCorrect)连同它们的观测值共同定义。由于这个例子相当小,我们可以用手工方式求出这个推断查询的答案。
手动进行推断
作为开始,我们先来看看在只给定第一题答案的情况下,如何推断 csharp 技能的概率。舍去其它变量后,就得到图 2.6 所示的简化因子图。
图 2.6:只针对第一题和 csharp 技能的因子图。
这张因子图现在看起来就和第 1.3 节中谋杀之谜的那张一模一样,而且和谋杀之谜一样,我们可以应用贝叶斯定理来求解它:
$$ P(\text{csharp} \mid \textcolor{red}{\text{isCorrect1} = \text{true}}) \propto \textcolor{green}{P(\text{isCorrect1} = \text{true} \mid \text{csharp})}\,\textcolor{blue}{P(\text{csharp})}. \quad (2.5) $$在式 (2.5) 中,我们给其中一些项标注了颜色,以便在计算过程中跟踪它们。代入 csharp 为 true 和 false 时的数值,得到:
这两个数加起来是 0.55。为了得到概率,我们需要把两个数都缩放到加起来等于 1(即除以 0.55),这样就得到:
$$ \begin{aligned} P(\text{csharp} = \text{true} \mid \textcolor{red}{\text{isCorrect1} = \text{true}}) = \frac{0.45}{0.55} &\simeq 0.818 \\ P(\text{csharp} = \text{false} \mid \textcolor{red}{\text{isCorrect1} = \text{true}}) = \frac{0.10}{0.55} &\simeq 0.182 \end{aligned} \quad (2.7) $$所以,仅根据第一题的答案,具备 csharp 技能的概率为 $81.8\%$。
对全部三道题手动执行推断是一项更为复杂的计算。如果你想探究这个计算过程,请阅读下面的深入探讨小节;如果不想,可以跳过它,直接去看我们如何把这个推断计算自动化。
(深入探讨) 在这个可选小节中,我们通过对联合分布求边缘(即在除我们关心的变量之外的所有变量上求和),在三题模型中手动进行推断。你可以放心跳过本节。
正如我们在第 1 章中所见,我们可以通过对联合分布求边缘(在除我们关心的变量之外的所有变量上求和),同时固定任何观测变量的取值,来执行推断。对于三题因子图,我们在第 2.1 节的式 (2.4) 中写下了它的联合概率分布。这里再写一遍:
$$ \begin{aligned} P(&\text{csharp}, \text{sql}, \text{hasSkills}, \text{isCorrect}) = \\ & \text{Bernoulli}(\text{csharp}; 0.5)\,\text{Bernoulli}(\text{sql}; 0.5) \\ & \text{AddNoise}(\text{isCorrect1} \mid \text{csharp})\,\text{AddNoise}(\text{isCorrect2} \mid \text{sql}) \\ & \text{And}(\text{hasSkills} \mid \text{csharp}, \text{sql})\,\text{AddNoise}(\text{isCorrect3} \mid \text{hasSkills}). \end{aligned} \quad (2.8) $$在开始这个推断计算之前,我们需要说明如何计算分布之积。假设对于某个变量 $x$,我们知道:
$$ P(x) \propto \text{Bernoulli}(x; 0.8)\,\text{Bernoulli}(x; 0.4). \quad (2.9) $$这看起来可能有点奇怪,因为我们通常只把一个分布与一个变量关联起来,但正如我们将看到的,在执行推断时,分布之积会频繁出现。对 $x$ 的两个取值分别求这个表达式的值,得到:
$$ P(x) \propto \begin{cases} 0.8 \times 0.4 = 0.32 & \text{如果 } x = \text{true} \\ 0.2 \times 0.6 = 0.12 & \text{如果 } x = \text{false}. \end{cases} \quad (2.10) $$由于我们知道 $P(x=\text{true})$ 和 $P(x=\text{false})$ 必须加起来等于 1,我们可以把这些值都除以 $0.32+0.12=0.44$,得到:
$$ P(x) = \begin{cases} 0.727 & \text{如果 } x = \text{true} \\ 0.273 & \text{如果 } x = \text{false} \end{cases} = \text{Bernoulli}(x; 0.727). \quad (2.11) $$这个计算你可能会觉得很熟悉——它和我们在第 1 章中所做的推断计算非常相似。
一般来说,如果想把两个伯努利分布相乘,我们可以使用如下规则:
$$ \text{Bernoulli}(x; a)\,\text{Bernoulli}(x; b) \propto \text{Bernoulli}\left(x;\ \frac{ab}{ab + (1-a)(1-b)}\right). \quad (2.12) $$比如说,如果第二个分布是均匀的($b=0.5$),那么乘积的结果就是 $\text{Bernoulli}(x; a)$。换句话说,分布 $\text{Bernoulli}(x; a)$ 乘以一个均匀分布后保持不变。一般而言,任何分布乘以均匀分布都保持不变。
有了相乘分布的能力,我们现在就可以计算示例候选人具备 csharp 技能的概率了。我们想要计算的确切概率是 $P(\text{csharp} \mid \text{isCorrect}=[\text{T}, \text{F}, \text{F}])$,其中我们把 true 简写为 T,false 简写为 F。和之前一样,我们可以通过对联合分布求边缘并固定观测值来计算它:
正如我们在第 1 章中所见,我们使用正比符号 $\propto$,因为我们并不关心右端的缩放,只关心它在 csharp 为 true 时的值与为 false 时的值之比。
现在我们代入 (2.8) 中联合概率的完整表达式,并固定所有观测变量的取值。我们可以忽略那些 $\text{Bernoulli}(0.5)$ 项,因为正如我们刚学到的,一个分布乘以均匀分布后保持不变。于是 (2.13) 的右端变为:
$$ \begin{aligned} \propto \sum_{\text{sql}} \sum_{\text{hasSkills}} & \text{AddNoise}(\text{isCorrect1}=\text{T} \mid \text{csharp}) \\ & \text{AddNoise}(\text{isCorrect2}=\text{F} \mid \text{sql}) \\ & \text{And}(\text{hasSkills} \mid \text{csharp}, \text{sql})\,\text{AddNoise}(\text{isCorrect3}=\text{F} \mid \text{hasSkills}). \end{aligned} \quad (2.14) $$每个求和 $\sum$ 内部那些不涉及被求和变量的项,都可以移到求和之外,因为对每个被求和的项而言它们的值都相同。你也可以把这想成是把求和号往右移:
$$ \begin{aligned} \propto\ & \text{AddNoise}(\text{isCorrect1}=\text{T} \mid \text{csharp}) \\ & \sum_{\text{sql}} \text{AddNoise}(\text{isCorrect2}=\text{F} \mid \text{sql}) \\ & \sum_{\text{hasSkills}} \text{And}(\text{hasSkills} \mid \text{csharp}, \text{sql})\,\text{AddNoise}(\text{isCorrect3}=\text{F} \mid \text{hasSkills}). \end{aligned} \quad (2.15) $$如果你看这里的第一项,会发现它只是 csharp 的函数,因为 isCorrect1 被观测为 true。当 csharp 为 true 时,这一项的值为 0.9;当 csharp 为 false 时,这一项的值为 0.2。由于我们只关心这两个数的相对大小,我们可以用一个伯努利项来替换这一项,其中 true 的概率为 $\frac{0.9}{0.9+0.2} = 0.818$,因而 false 的概率为 $1-0.818=0.182$。注意这保留了 true/false 之比 $0.818/0.182 = 0.9/0.2$。
类似地,第二个 AddNoise 项在 sql 为 true 时值为 0.1,为 false 时值为 0.8,所以可以用一个伯努利项替换,其中 true 的概率为 $\frac{0.1}{0.1+0.8} = 0.111$。最后那个 AddNoise 项也可以同样替换,于是得到:
对于确定性的 And 因子,我们需要考虑该因子不为零的四种情形(我们在专栏 2.1 中见过),并在每种情形下代入 sql 与 hasSkills 的 $\text{Bernoulli}(0.111)$ 分布:
表 2.3:对 (2.16) 中最后三项的求值。表中每一行对应 And 因子为 1(而非 0)的四种情形之一。前三列给出 csharp、sql 和 hasSkills 的取值,这正是 AND 的真值表。接下来两列给出 sql 与 hasSkills 对应的伯努利分布取值,最后一列把它们相乘。
看表 2.3,我们可以发现:当 csharp 为 true 时,要么 sql 和 hasSkills 都为 false(概率为 0.790),要么两者都为 true(概率为 0.012)。二者之和为 0.802。当 csharp 为 false 时,对应的和为 $0.790+0.099=0.889$。所以我们可以用一个参数为 $\frac{0.802}{0.802+0.889} = 0.474$ 的伯努利项来替换最后三项:
现在我们有了一个伯努利分布之积,所以可以用 (2.12) 把它们相乘。当 csharp 为 true 时,这个乘积的值为 $0.818\times0.474 = 0.388$;当 csharp 为 false 时,值为 $(1-0.818)\times(1-0.474) = 0.096$。因此,这两个分布的乘积是一个参数为 $\frac{0.388}{0.388+0.096}$ 的伯努利分布:
于是我们算出,候选人具备 csharp 技能的后验概率为 80.2%。如果我们对 sql 技能做类似的计算,会发现其后验概率为 3.4%。综合来看,这些概率表明候选人很可能懂 C# 但不太可能懂 SQL,考虑到候选人只答对了 C# 那道题,这看起来是一个非常合理的推断。
通过在图上传递消息进行推断
手动进行推断计算耗时很长,而且容易出错。作为替代,我们可以用一种消息传递算法机械地完成同样的计算。它的工作方式是沿着因子图的边传递消息,其中每条消息都是一个关于该边所连接变量的概率分布。我们将看到,使用消息传递算法能让我们自动完成推断计算——这正是基于模型方法的一大优势!
为了理解消息传递如何工作,我们再来看看式 (2.6):
$$ \begin{aligned} P(\text{csharp} = \text{true} \mid \textcolor{red}{\text{isCorrect1} = \text{true}}) &\propto \textcolor{green}{0.9} \times \textcolor{blue}{0.5} = 0.45 \\ P(\text{csharp} = \text{false} \mid \textcolor{red}{\text{isCorrect1} = \text{true}}) &\propto \textcolor{green}{0.2} \times \textcolor{blue}{0.5} = 0.10. \end{aligned} \quad (2.19) $$图 2.7 把这些标了颜色的项表现为在图中从一个节点传递到另一个节点的消息。例如,csharp 先验的因子节点把这个先验分布作为一条消息发送给 csharp 节点(蓝色)。被观测的 isCorrect1 节点向上发送一条消息,它是位于观测值处的点质量(红色)。AddNoise 因子使用贝叶斯定理对这条消息进行变换,并输出它自己的向上消息(绿色)。这些消息中的每一条,都可以根据发送它的那个节点处所掌握的信息计算得到。
此时到达 csharp 节点的两条消息,提供了我们计算 csharp 变量后验分布所需的全部信息,正如我们在式 (2.6) 中所见。这种用消息传递来计算后验分布的方法称为置信传播(belief propagation)[Pearl, 1982; Pearl, 1988; Lauritzen and Spiegelhalter, 1988]。
如果你想了解如何用置信传播在我们的三题模型中计算后验分布,请阅读下一小节;否则,你可以直接跳到结果部分。
(深入探讨) 在这个可选小节中,我们展示如何用置信传播在我们的三题模型中执行推断。你可以放心跳过本节。
让我们用消息传递来重做 csharp 技能的推断计算——我们先针对这个例子描述消息传递的过程,之后再来看它的一般形式。手动计算的第一步是固定观测变量的取值。使用消息传递时,这对应于每个观测变量发出一条消息,它是位于观测值处的点质量分布。在我们的例子中,如果某个 isCorrect 变量被观测为 false,它就发送点质量 $\text{Bernoulli}(0)$;如果被观测为 true,就发送点质量 $\text{Bernoulli}(1)$。这意味着所发送的三条消息如图 2.8 所示。
图 2.8:从被观测的变量节点发出的消息,这些节点被画成阴影并标注了它们的观测值。任何一条边上的消息都是该边所连接变量上的一个分布。例如,左边的 $\text{Bern}(1)$ 是 $\text{Bernoulli}(\text{isCorrect1}; 1)$ 的简写。
这些点质量消息随后到达 AddNoise 因子节点。每个因子节点处的输出消息可以分别按如下方式计算:
- 从第一个
AddNoise因子向上传给csharp的消息,可以通过把 $\text{AddNoise}(\text{isCorrect1}=\text{T} \mid \text{csharp})$ 写成关于csharp的伯努利分布来计算。正如我们在上一小节所见,这个伯努利分布的参数是 $p=\frac{0.9}{0.9+0.2}=0.818$,所以向上的消息是 $\text{Bernoulli}(0.818)$。 - 从第二个
AddNoise因子向上传给sql的消息,可以通过把 $\text{AddNoise}(\text{isCorrect2}=\text{F} \mid \text{sql})$ 写成关于sql的伯努利分布来计算。这个伯努利分布的参数是 $p=\frac{0.1}{0.1+0.8}=0.111$,所以向上的消息是 $\text{Bernoulli}(0.111)$。 - 从第三个
AddNoise因子向上传给hasSkills的消息与第二条消息相同,因为它是对同一个因子、用相同的输入消息计算得到的。因此,第三条向上的消息也是 $\text{Bernoulli}(0.111)$。
图 2.9:从 AddNoise 因子节点发出的输出消息。
注意,这三条消息恰好就是我们在 (2.16) 中见到的那三个伯努利分布。我们没有在整个联合分布上做计算,而是把计算分解成了因子图各节点处简单、可重复的消息计算。
从 $\text{Bernoulli}(0.5)$ 先验因子向下传的消息,就是这些先验分布本身:
任何变量节点的输出消息,都是该节点所连的其它各边上收到的输入消息之积。对于 sql 变量节点,现在我们在两条边上都有了输入消息,这意味着我们可以计算指向 And 因子的输出消息。这条消息是 $\text{Bernoulli}(0.111)$,因为向上的消息乘以向下的均匀消息 $\text{Bernoulli}(0.5)$ 后保持不变。hasSkills 变量节点则更简单:由于只有一条输入消息,其输出消息就是它的副本。
图 2.11:从 sql 和 hasSkills 变量节点发出的消息。
最后,我们可以计算从 And 因子传给 csharp 变量的输出消息。计算方式是把各输入消息与因子函数相乘,并对除了要发送到的那个变量之外的所有变量求和(因此我们对 sql 和 hasSkills 求和):
这个求和给出消息 $\text{Bernoulli}(0.474)$,正如我们在式 (2.17) 中所见。
图 2.12:传向 csharp 变量节点的最后一条消息。
现在我们在 csharp 变量节点处集齐了全部三条输入消息,这意味着我们已经准备好计算它的后验边缘分布了。方法是把这三条消息相乘——这正是我们在式 (2.17) 中所做的计算,因此得到相同的结果 $\text{Bernoulli}(0.802)$,即 80.2%。
要计算 sql 的边缘分布,我们可以复用刚才算出的大部分消息,因此只需再计算两条额外的消息(如图 2.13 所示)。第一条消息是从 csharp 传给 And 因子的,它是 $\text{Bernoulli}(0.818)$ 与均匀分布 $\text{Bernoulli}(0.5)$ 之积,所以结果也是 $\text{Bernoulli}(0.818)$。
第二条消息是从 And 因子传给 sql 的。同样,我们通过把各输入消息与因子函数相乘,并对除了要发送到的那个变量之外的所有变量求和(因此我们对 csharp 和 hasSkills 求和)来计算它:
这个求和给出消息 $\text{Bernoulli}(0.221)$,因此我们计算出的这两条新消息就是图 2.13 所示的那两条。
图 2.13:计算 sql 变量边缘分布所需的额外消息。
把这条消息与来自 AddNoise 因子的向上消息一起乘入 sql,得到 $\text{Bernoulli}(0.111)\times\text{Bernoulli}(0.221) \propto \text{Bernoulli}(0.034)$,即 3.4%,与之前的结果相同。注意,我们再次忽略了来自先验的均匀消息 $\text{Bernoulli}(0.5)$,因为乘以一个均匀分布不会产生任何影响。
我们刚刚看到的消息传递过程,源自应用一般化的置信传播算法。在置信传播中,消息按三种方式之一计算,取决于该消息是来自因子节点、被观测的变量节点,还是未观测的变量节点。该算法总结于算法 2.1 中——这个算法的完整推导可参见 Bishop [2006]。针对因子图的置信传播在 Kschischang et al. [2001] 中也有讨论。
算法 2.1:置信传播(Belief Propagation)
输入: 因子图,以及要为其计算边缘分布的目标变量列表。
输出: 各目标变量的边缘分布。
重复:
- 对因子图中的每个节点:
直到 各目标变量都在其所有边上收到了输入消息。
在每个目标变量节点处,把所有输入消息相乘,作为边缘分布计算出来。
用置信传播来试验模型
置信传播算法让我们能够针对给定的因子图,完全自动地进行推断计算。这意味着,回答一个推断查询的整个过程可以完全自动化,无需编写任何代码,也无需做任何手工计算!
利用置信传播,我们可以对每一种可能的答对/答错组合,自动推断技能的边缘分布,从而全面地试验我们的模型。这样做的结果如表 2.4 所示。
表 2.4:对 isCorrect 所有可能配置下 csharp 和 sql 变量的后验概率。和之前一样,蓝色条形直观地表示了推断出的概率。
审视这张表,我们可以看到结果看起来是合理的——当候选人答对第一题时,具备 csharp 技能的概率通常更高;类似地,当候选人答对第二题时,具备 sql 技能的概率通常更高。此外,当第三题答对而非答错时,两个概率都更高。
有意思的是,当只有第一题答对时,具备 sql 技能的概率实际上比候选人所有题都答错时还要低(表 2.4 的第一、二行)。这是说得通的,因为答对第一题意味着候选人很可能具备 csharp 技能,这就使得“答错第三题”的解释更有可能是他们不具备 sql 技能。这是基于模型的机器学习所能实现的那种微妙推理的一个例子,也正是它相对于更简单的方法可能具备的优势。举例来说,如果我们仅仅把某人答对的、需要某项特定技能的题目数量作为该技能的指标,我们就会忽略来自其它题目的潜在有用信息。相比之下,借助一个合适的模型,我们利用了这样一个事实:答对一道 csharp 题目实际上可以降低具备 sql 技能的概率。
本页引入概念回顾
推断查询(inference query):定义了要在某个概率模型上进行的推断计算的查询。它由两部分组成:一组我们已知其取值的变量(连同这些取值),以及另一组我们希望为其推断后验分布的变量。举例来说,一个推断查询可以是:我们已知变量 weapon 取值为 revolver,并希望推断变量 murderer 的后验分布。
分布之积(product of distributions):一种把两个(或更多)概率分布相乘、然后将结果归一化使其总和为 1,从而得到一个新概率分布的运算。这个运算不应与把两个不同的随机变量相乘相混淆(后者可能通过模型中的一个确定性因子来实现)。相反,分布之积涉及的是同一个随机变量上的两个分布。在推断过程中,分布之积被频繁用来把关于某个特定变量、来自不同来源的多条不确定信息组合起来。
消息传递算法(message passing algorithm):一种通过在图模型(例如因子图)的边上传递消息来进行推断计算的算法。这些消息是关于边所连接变量的概率分布。置信传播是一种常用的消息传递算法。
置信传播(belief propagation):一种用于计算因子图中各变量后验边缘分布的消息传递算法。置信传播使用两种不同的消息计算方式,一种用于从因子到变量的消息,另一种用于从变量到因子的消息。观测变量发送点质量消息。参见算法 2.1。
自我评估 2.2
以下练习将帮助你巩固本节所学的概念。在做题时,回顾正文或上面的概念小结可能会有所帮助。
- 计算下列各对伯努利分布的乘积: $$\text{Bernoulli}(x; 0.3) \times \text{Bernoulli}(x; 0.9)$$ $$\text{Bernoulli}(x; 0.5) \times \text{Bernoulli}(x; 0.2)$$ $$\text{Bernoulli}(x; 0.5) \times \text{Bernoulli}(x; 0.3)$$ $$\text{Bernoulli}(x; 1.0) \times \text{Bernoulli}(x; 0.2)$$ $$\text{Bernoulli}(x; 1.0) \times \text{Bernoulli}(x; 0.3)$$ 为什么我们无法计算 $\text{Bernoulli}(x; 1.0) \times \text{Bernoulli}(x; 0.0)$?
- 编写一个程序(或制作一个电子表格),打印出从两个参数不同($a$ 和 $b$)的伯努利分布中抽取的成对样本。然后过滤程序的输出,只显示那些取值相同的样本对(即两个样本都为 true 或都为 false 的情形)。打印出这些样本中为 true 的比例。这个过程对应于把两个伯努利分布相乘,因此得到的比例应当接近式 (2.12) 给出的值。用你的程序(近似地)验证你对上一题的答案。当 $a=0.0$ 且 $b=1.0$ 时,你的程序会做什么?
- 手动计算
sql技能的后验概率,就像我们在第 2.2 节中对csharp技能所做的那样,并证明它等于 3.4%。 - 在 Infer.NET 中构建这个模型,并重现表 2.4 中的结果。关于如何构造条件概率表的示例,可参考 Infer.NET 文档中的湿草地/洒水器/下雨示例。你还需要使用 Infer.NET 的
&运算符来表示And因子。这个练习演示了在给定模型定义的情况下,推断计算如何能够完全自动地执行。
参考文献
[Pearl, 1982] Pearl, J. (1982). Reverend Bayes on Inference Engines: A Distributed Hierarchical Approach. In Proceedings of the Second AAAI Conference on Artificial Intelligence, AAAI'82, pages 133–136. AAAI Press.
[Pearl, 1988] Pearl, J. (1988). Probabilistic Reasoning in Intelligent Systems. Morgan Kaufmann, San Francisco.
[Lauritzen and Spiegelhalter, 1988] Lauritzen, S. L. and Spiegelhalter, D. J. (1988). Local Computations with Probabilities on Graphical Structures and Their Application to Expert Systems. Journal of the Royal Statistical Society, Series B, 50(2):157–224.
[Bishop, 2006] Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Springer.
[Kschischang et al., 2001] Kschischang, F. R., Frey, B. J., and Loeliger, H. (2001). Factor graphs and the sum-product algorithm. IEEE Transactions on Information Theory, 47(2):498–519.
下一节:有环性
- 本文作者:BeanHsiang
- 本文链接:https://beanhsiang.github.io/post/2026-06-06-mbml-learning-skills_testing_out_the_model/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。