[译]基于模型的机器学习 - 2.5 诊断问题
当一个机器学习系统不工作时,通常有三个可能的原因:坏数据、坏模型或坏推断。以下是这三个类别下各自一些常见的问题成因:
- 坏数据(Bad data):数据项被错误地录入、存储或加载;数据项不完整或标注错误;数据值噪声太大而无用;数据有偏或不能代表系统将被使用的方式;这是任务的错误数据;数据不足以做出准确预测。
- 坏模型(Bad model):一个或多个建模假设是错的——即与实际生成数据的过程不一致;模型做了太多简化假设;给定可用数据量,模型包含的假设不足以做出准确预测。
- 坏推断(Bad inference):推断代码含有 bug;消息传递调度不好;推断尚未收敛;存在数值问题(例如舍入、溢出);近似推断算法不够准确。
在我们的情形中,我们可以相当有信心地认为数据是好的,因为我们已经仔细地检查和可视化过它。所以看起来问题很可能出在模型或推断上。我们将从检查推断算法——即循环置信传播——是否正确工作开始。
检查推断算法
为了看出推断是否正确工作,我们需要能够把推断问题所引起的任何毛病,与我们的模型不匹配数据所引起的任何毛病区分开来。为了实现这种区分,我们可以生成一个新的合成数据集,它保证与模型完全匹配。如果我们用这个数据集也得到糟糕的结果,那就表明存在推断问题。我们将通过从模型所指定的联合分布中采样来创建这个合成数据集,这保证了数据与模型一致(关于采样是什么,可回顾第 1 章)。我们可以通过运行模型所指定的数据生成过程来产生样本——这个过程称为祖先采样(ancestral sampling),如算法 2.3 所定义(另见 Bishop [2006])。
算法 2.3:祖先采样
输入:因子图
输出:图中每个变量的采样值
for each 该顺序中的变量 $v$ do
如果 $v$ 有父变量,取回它们的采样值(由于排序的缘故,这些值必然已经存在)。
从其父因子函数中,以取回的父值为条件,为 $v$ 采样一个值。
end
看着图 2.19 的因子图,我们从上到下(从祖先到后代)沿着箭头运行祖先采样,通过在给定图中各变量父节点的条件下,为每个变量采样一个值。如果一个变量是某个确定性因子的子变量,那么我们只需从它父变量的取值来计算它的值即可。
于是,从顶端开始,我们从 Bernoulli(0.5) 分布为 skill 数组的每个元素采样一个值——换句话说,我们以 50% 的概率选 true,否则选 false。对于某道题的 relevantSkills 数组元素,我们只需抽出 skill 数组中与该题相关的、已采样的值。随后把这些值 AND 在一起,得到 hasSkills。图 2.21 给出了 skill 和 hasSkills 数组的一组 22 个样本示例。要得到一个多行的数据集,我们只需对每一行重复整个采样过程。注意,对每一行,凡是需要相同技能(即同一颜色)的题目,hasSkills 总是相同的。
(a) 采样得到的 skill 数组
(b) 由采样 skill 数组计算出的 hasSkills 数组
(c) 采样得到的 isCorrect 数组
图 2.21:用祖先采样创建的合成数据集。首先采样 skill 数组,然后由它计算出 hasSkills 数组。接着在给定 hasSkills 数组的条件下采样 isCorrect 数组,其效果是使它成为 hasSkills 的一个带噪版本。
我们模型中祖先采样的最后一步,需要在给定 isCorrect 的父元素 hasSkills 的条件下,采样它的每个元素。当 hasSkills 为 true 时,我们从 Bernoulli(0.9) 采样;当 hasSkills 为 false 时,我们从 Bernoulli(0.2) 采样(依照表 2.1)。执行这一步的结果给出了图 2.21c 的 isCorrect 样本。注意,这些样本最终看起来像是 hasSkills 样本的带噪版本——大约每十个白色方块中就有一个被翻转为彩色,而大约每五个彩色方块中就有一个被翻转为白色。
我们现在有了一整个采样得到的数据集,可以在它上面运行我们的推断算法,来测试它是否正确工作。推断出的技能概率展示在图 2.22 中,紧挨着我们所采样的实际技能。与真实数据不同,这里的结果相当令人信服:推断出的技能看起来与实际采样的技能非常相似。所以,当我们在一个完全符合我们模型的数据集上运行推断时,结果是好的。这表明推断算法工作良好,问题必然出在我们的模型与真实数据不匹配上。
(a) 推断出的技能
(b) 采样得到的技能
图 2.22:从一个样本数据集推断出的技能,与该数据集实际采样的技能并列展示。推断出的技能接近实际技能,表明推断算法工作良好。
一个重要而微妙的点是:即使数据与模型完美匹配,推断出的技能也只是接近、而非完全等同于采样得到的技能。这是因为,即便给定了测试中的所有答案,技能上仍然残留着一些不确定性。例如,在个体不具备技能 1(Core)或技能 2(OOP)的情形下,技能 7(C#)的后验概率是不确定的。这说得通,因为 C# 技能只与前两项技能组合在一起被考察——如果一个人不具备它们,那么无论他是否懂 C#,都会答错相关题目。所以在这种情况下,推断算法对这个人是否具备 C# 技能感到不确定是正确的。我们可以利用这个信息来改进测试,例如添加一些直接单独考察 C# 技能的题目。
弄清模型出了什么问题
我们已经确定我们的模型假设与数据不匹配——现在我们需要识别是哪个(些)假设出了错。我们可以再次使用采样来做到这一点,但这次不是采样 skill 数组,而是把它设为真实(自评)值。如果我们随后采样 isCorrect 数组,它将向我们展示:如果人们拥有这些技能,模型预期他们会答错哪些题。通过把这与我们数据集中实际的 isCorrect 数组作比较,我们就能看出模型的假设在何处与现实不符。图 2.23 表明,实际的 isCorrect 数据看起来与采样数据相当不同。
(a) 给定真实(自评)技能所采样的 isCorrect 数组
(b) 我们数据集中观测到的 isCorrect 数组
图 2.23:建模问题可以通过比较来诊断:(a) 在给定自评技能的条件下从模型采样得到的 isCorrect 数据,与 (b) 观测到的 isCorrect 数据(显示志愿者实际答错了哪些题)。
最大的差异似乎在于:给定志愿者陈述的技能,他们答对的题目比我们模型所预测的要多得多。这表明他们能够猜对一道题的答案,其频率远高于我们模型所假设的五分之一。仔细想想,这说得通——即使某人不具备回答一道题的技能,他也可能凭借常识或聪明的猜测排除掉一些选项。
我们可以进一步研究这一点,方法是:计算在给定自评技能的条件下,模型预测志愿者答对每道题的比例,然后把它与他们实际答对的比例作比较(图 2.24)。
图 2.24:对于测试中的 48 道题,模型在给定自评技能下预测答对每道题的人数比例(蓝色),与实际答对的比例(红色)的对比。
对于少数几道题,答对的人数比例与模型预测的一致——但对于大多数题目,实际比例都高于预测比例。这表明有些题比其他题更容易猜,而且它们被猜对的频率可以高于五分之一。所以我们需要更改我们的假设(以及我们的模型),以允许不同题目有不同的猜测概率。我们可以把第四条假设修改如下:
- 如果一位应试者不具备一道题所需的全部技能,他将
随机挑选一个答案猜一个答案,其中猜对的概率对大多数题目约为 20%,但对非常容易猜的题目可高达约 60%。
这条假设意味着,我们不再对所有题目使用固定的猜测概率,而是需要扩展我们的模型,为每道题学习一个不同的猜测概率。
本页引入概念回顾
祖先采样(ancestral sampling):一种从概率模型产生样本的过程:先用没有父节点的变量的先验分布对它们进行采样,然后在这些采样值的条件下采样它们的子变量,再类似地采样这些子变量的子变量,如此类推,直到图中所有变量都被赋值为止。
自我评估 2.5
以下练习将帮助你巩固本节所学的概念。在做题时,回顾正文或上面的概念小结可能会有所帮助。
- 列一份机器学习系统问题成因的检查清单(数据问题、模型问题或推断问题)。按你认为最可能发生的顺序对成因排序。那么,如果你将来在处理一个机器学习问题,这份检查清单在诊断问题的根本原因时可能会很有用。
- 编写一个程序来实现本节所述的技能模型中的祖先采样,并用它制作一个合成数据集。把这个数据集可视化,例如用你在上一个自我评估中开发的可视化。检查你的样本看起来是否与图 2.21 的样本相似。
- 试着更改我们在模型中选定的几个概率值,例如具备某项技能的先验概率,或猜对答案的概率。再次运行你的采样程序,看看合成数据集如何变化。你可以设想重复这一过程,直到在模型假设下合成数据尽可能像真实数据为止。这样做会相当低效,所以我们转而把这些概率值作为推断算法的一部分来学习,正如我们将在下一节看到的那样。
参考文献
[Bishop, 2006] Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Springer.
下一节:学习猜测概率
- 本文作者:BeanHsiang
- 本文链接:https://beanhsiang.github.io/post/2026-07-10-mbml-learning-skills_diagnosing_the_problem/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。