🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝
📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】 深度学习【DL】
🖍foreword
✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。
如果你对这个系列感兴趣的话,可以关注订阅哟👋
文章目录
最简单的适当模型
简单模型
快速实施
可以理解
可部署
从模式到模型
我们想忽略特征尺度
我们的预测变量是预测变量的线性组合
我们的数据有时间方面
每个数据点都是模式的组合
ML 编辑器模型
拆分您的数据集
验证集
测试集
相对比例
数据泄露
ML 编辑器数据拆分
评委表现
偏差方差权衡
超越聚合指标
评估您的模型:超越准确性
对比数据和预测
混淆矩阵
ROC 曲线
校准曲线
错误的降维
Top-k方法
k个表现最好的例子
k个表现最差的例子
k个最不确定的例子
Top-k 实现技巧
ML Editor 的 Top-k 方法
其他型号
评估特征重要性
直接来自分类器
黑匣子解释器
结论
在在前面的章节中,我们介绍了如何确定要解决的正确问题、制定解决计划、构建简单的管道、探索数据集以及生成一组初始特征。这些步骤使我们能够收集到足够的信息来开始训练一个合适的模型。这里的适当模型意味着模型非常适合手头的任务,并且很有可能表现良好。
在本章中,我们将首先简要回顾一下选择模型时的一些注意事项。然后,我们将描述分离数据的最佳实践,这将有助于在现实条件下评估您的模型。最后,我们将研究分析建模结果和诊断错误的方法。
最简单的适当模型
现在准备好训练模型后,我们需要决定从哪个模型开始。尝试所有可能的模型,对所有模型进行基准测试,然后根据某些指标在保留的测试集上选择具有最佳结果的模型可能很诱人。
一般来说,这不是最好的方法。它不仅计算量大(有很多模型集,每个模型都有很多参数,所以实际上你只能测试一个次优子集),它还将模型视为预测黑盒,完全忽略了ML 模型编码隐式以他们学习的方式对数据进行假设。
不同的模型对数据做出不同的假设,因此适用于不同的任务。此外,由于 ML 是一个迭代领域,您需要选择可以快速构建和评估的模型。
让我们首先定义如何识别简单模型。然后,我们将介绍一些数据模式示例和适当的模型来利用它们。
简单模型
一个简单的模型应该能够快速实现、易于理解和部署:快速实现因为你的第一个模型可能不会是你的最后一个模型,易于理解因为它可以让你更轻松地调试它,并且易于部署因为这是 ML 的基本要求供电的应用程序。让我们首先探讨我所说的快速实施的意思。
快速实施
如果可能的话,从使用 Keras 或 scikit-learn 等流行库中的模型开始,并在深入研究没有文档且在过去九个月内未更新的实验性 GitHub 存储库之前先暂停。
模型实施后,您需要检查并了解它如何利用您的数据集。为此,您需要一个易于理解的模型。
可以理解
模型可解释性和可解释性描述模型揭示导致其做出预测的原因(例如给定的预测变量组合)的能力。出于多种原因,可解释性可能很有用,例如验证我们的模型没有以不良方式存在偏差或向用户解释他们可以做些什么来改善预测结果。它还使迭代和调试变得更加容易。
如果您可以提取模型做出决策所依赖的特征,您将更清楚地了解要添加、调整或删除哪些特征,或者哪个模型可以做出更好的选择。
不幸的是,即使对于简单的模型,模型的可解释性通常也很复杂,有时对于较大的模型来说也很棘手。在“评估特征重要性”中,我们将看到应对这一挑战的方法,并帮助您确定模型的改进点。除其他事项外,我们将使用黑盒解释器来尝试提供模型预测的解释,而不考虑其内部工作原理。
逻辑回归或决策树等更简单的模型往往更容易解释,因为它们提供了一些特征重要性的度量,这也是它们通常是首先尝试的好模型的另一个原因。
可部署
作为提醒一下,你的模型的最终目标是为使用它的人提供有价值的服务。这意味着当您考虑要训练哪个模型时,您应该始终考虑是否能够部署它。
我们将在第 IV 部分介绍部署,但您应该已经在考虑以下问题:
-
经过训练的模型需要多长时间才能为用户做出预测?在考虑预测延迟时,您不仅应该包括模型输出结果所需的时间,还应该包括用户提交预测请求和接收结果之间的延迟。这包括任何预处理步骤,例如特征生成、任何网络调用,以及发生在模型输出和呈现给用户的数据之间的任何后处理步骤。
-
如果我们考虑我们预期的并发用户数量,这个推理管道对于我们的用例来说是否足够快?
-
训练模型需要多长时间,我们需要多久训练一次?如果训练需要 12 小时,并且您需要每 4 小时重新训练您的模型以保持新鲜,那么不仅您的计算费用会非常昂贵,而且您的模型将始终过时。
我们可以使用如图 5-1 所示的表格来比较模型的简单程度。随着 ML 领域的发展和新工具的构建,今天可能部署复杂或难以解释的模型可能会变得更易于使用,并且需要更新此表。出于这个原因,我建议您根据您的特定问题域构建您自己的版本。
图 5-1。基于简单性的评分模型
即使在简单、可解释和可部署的模型中,仍有许多潜在的候选者。要选择模型,您还应该考虑在第 4 章中确定的模式。
从模式到模型
这我们已经确定的模式和我们生成的特征应该指导我们的模型选择。让我们介绍一些数据模式示例和适当的模型来利用它们。
我们想忽略特征尺度
许多模型将比较小的特征更多地利用大的特征。这在某些情况下可能很好,但在其他情况下则不受欢迎。对于使用优化程序的模型,如在神经网络等梯度下降中,特征尺度的差异有时会导致训练过程不稳定。
如果您想同时使用以年为单位的年龄(从一到一百)和以美元为单位的收入(假设我们的数据达到九位数)作为两个预测变量,您需要确保您的模型能够最大程度地利用预测特征,无论其规模如何。
您可以通过预处理特征以将其比例标准化为零均值和单位方差来确保这一点。如果所有特征都归一化到相同的范围,模型将平等地考虑每个特征(至少在最初是这样)。
另一种解决方案是转向不受特征尺度差异影响的模型。最常见的实际例子是决策树、随机森林和梯度提升决策树。XGBoost是一种梯度提升树的实现,因其鲁棒性和速度而常用于生产中。
我们的预测变量是预测变量的线性组合
有时,有充分的理由相信我们可以仅使用特征的线性组合做出好的预测。在这些情况下,我们应该使用线性模型,例如连续问题的线性回归或逻辑回归或用于分类问题的朴素贝叶斯分类器。
这些模型简单、高效,并且通常可以直接解释它们的权重,从而帮助我们识别重要特征。如果我们认为我们的特征和我们预测的变量之间的关系更复杂,使用一个非线性模型,例如多层神经网络或生成特征交叉(参见“让数据通知特征和模型”的开头)可以提供帮助。
我们的数据有时间方面
如果我们正在处理数据点的时间序列,其中给定时间的值取决于先前的值,我们希望利用显式编码此信息的模型。此类模型的示例包括统计模型,例如自回归综合移动平均线 (ARIMA) 或递归神经网络(RNN)。
每个数据点都是模式的组合
什么时候例如,在解决图像领域的问题时,卷积神经网络 (CNN) 通过学习平移不变滤波器的能力证明是有用的。这意味着无论位置如何,它们都能够提取图像中的局部模式。一旦 CNN 学会了如何检测眼睛,它就可以在图像中的任何地方检测到它,而不仅仅是它在训练集中出现的地方。
卷积滤波器已被证明在包含局部模式的其他领域中很有用,例如语音识别或文本分类,其中 CNN 已成功用于句子分类。例如,请参阅 Yoon Kim 在论文“用于句子分类的卷积神经网络”中的实现。
在考虑使用正确的模型时,还有许多其他要点需要考虑。为了对于大多数经典的 ML 问题,我建议使用scikit-learn 团队提供的这个方便的流程图。它为许多常见用例提供模型建议。
拆分您的数据集
这我们模型的主要目标是为我们的用户将提交的数据提供有效的预测。这意味着我们的模型最终必须在它以前从未见过的数据上表现良好。
当你在一个数据集上训练一个模型时,测量它在同一个数据集上的性能只会告诉你它在对它已经看到的数据做出预测方面有多好。如果您只在数据的子集上训练模型,则可以使用模型未训练的数据来估计它在看不见的数据上的表现。
在图 5-2中,您可以看到根据数据集的一个属性(问题的作者)分成三个独立的集合(训练、验证和测试)的示例。在本章中,我们将介绍这些集合中的每一个的含义,以及如何看待它们。
图 5-2。拆分关于作者的数据,同时将正确比例的问题分配给每个拆分
要考虑的第一个留出集是验证集。
验证集
您可以选择数据的不同部分作为验证集来评估您的模型并在剩余数据上对其进行训练。进行多轮此过程有助于控制由于特定选择的验证集和称为交叉验证。
作为你改变你的数据预处理策略和你使用的模型类型或其超参数,你的模型在验证集上的性能将会改变(理想情况下会提高)。使用验证集允许您调整超参数,就像使用训练集允许模型调整其参数一样。
在使用验证集进行模型调整的多次迭代之后,您的建模管道可以专门定制以在验证数据上表现良好。这违背了验证集的目的,验证集应该是看不见的数据的代理。因此,您应该提供额外的测试集。
测试集
出于这个原因,我们通常会提供第三组称为测试集,一旦我们对迭代感到满意,它就会作为我们在未见数据上的性能的最终基准。虽然使用测试集是最佳实践,但从业者有时会使用验证集作为测试集。这增加了模型偏向验证集的风险,但在仅运行少数实验时可能是合适的。
重要的是要避免使用测试集的性能来告知建模决策,因为该集应该代表我们将在生产中面对的看不见的数据。调整建模方法以在测试集上表现良好存在导致高估模型性能的风险。
至有一个在生产中执行的模型,你训练的数据应该类似于将与你的产品交互的用户产生的数据。理想情况下,您可以从用户那里收到的任何类型的数据都应该在您的数据集中表示。如果不是这种情况,请记住,您的测试集性能仅代表一部分用户的性能。
对于 ML 编辑器,这意味着不符合writers.stackoverflow.com人口统计数据的用户可能不会得到我们推荐的服务。如果我们想解决这个问题,我们应该扩展数据集以包含更能代表这些用户的问题。我们可以从合并来自其他 Stack Exchange 网站的问题开始,以涵盖更广泛的主题,或完全涵盖不同的问答网站。
以这种方式纠正数据集对于副项目来说可能具有挑战性。然而,在构建消费级产品时,有必要在用户接触到模型弱点之前尽早发现它们。许多我们将在第 8 章中介绍的故障模式中的一部分可以通过更具代表性的数据集来避免。
相对比例
在一般来说,您应该最大化模型可用于学习的数据量,同时提供足够大的验证和测试集以提供准确的性能指标。从业者经常使用 70% 的数据进行训练,20% 用于验证,10% 用于测试,但这完全取决于数据的数量。对于非常大的数据集,您可以负担得起使用更大比例的数据进行训练,同时仍然有足够的数据来验证模型。对于较小的数据集,您可能需要使用较小的比例进行训练,以便拥有足够大的验证集来提供准确的性能测量。
现在您知道为什么要拆分数据,以及要考虑哪些拆分,但是您应该如何决定每个拆分中的数据点呢?您使用的拆分方法对建模性能有重大影响,应取决于数据集的特定特征。
数据泄露
这用于分离数据的方法是验证的关键部分。您的目标应该是使您的验证/测试集接近您期望的未见数据。
大多数情况下,训练集、验证集和测试集由随机采样数据点分开。在某些情况下,这会导致数据泄露。当(由于我们的训练程序)模型在训练期间接收到在生产中在真实用户面前使用时无法访问的信息时,就会发生数据泄漏。
应不惜一切代价避免数据泄漏,因为它会导致对我们模型性能的夸大看法。在表现出数据泄漏的数据集上训练的模型能够利用信息做出预测,而当它遇到不同的数据时则不会。这使得模型的任务人为地更容易,但这只是由于信息泄露。该模型的性能在保留数据上看起来很高,但在生产中会差得多。
在图 5-3中,我列出了一些将数据随机拆分成集合会导致数据泄漏的常见原因。数据泄露的潜在原因有很多,接下来我们将探讨两个常见的原因。
为了开始我们的探索,让我们处理图 5-3顶部的示例,时间数据泄漏。然后,我们将继续研究样本污染,该类别包含图 5-3中底部的两个示例。
图 5-3。随机拆分数据往往会导致数据泄露
时间数据泄漏
该模型将在验证和测试集上人为地表现良好,但在生产中会失败,因为它所学到的只是利用未来的信息,而这在现实世界中是不可用的。
一旦意识到这一点,时间数据泄漏通常很容易发现。其他类型的数据泄漏可以让模型访问它在训练期间不应拥有的信息,并通过“污染”其训练数据来人为地提高其性能。它们通常更难被发现。
样品污染
一个数据泄漏的常见来源在于随机性发生的级别。在构建预测学生论文成绩的模型时,我协助的一位数据科学家曾经发现他的模型在保留测试集上的表现接近完美。
在如此艰巨的任务中,应该仔细检查表现如此出色的模型,因为它经常表明存在错误或数据泄漏。有人会说,墨菲定律的 ML 等价物是,您对模型在测试数据上的表现越惊喜,您的管道中出错的可能性就越大。
在这个例子中,因为大多数学生写了多篇论文,随机拆分数据导致同一学生的论文同时出现在训练和测试集中。这使得模型能够获取识别学生的特征,并使用该信息做出准确的预测(该数据集中的学生在所有论文中的成绩往往相似)。
如果我们要部署这个作文分数预测器以备将来使用,它将无法为它以前没有见过的学生预测有用的分数,而只会预测它所训练过的学生的历史分数。这根本没有用。
为了解决这个例子中的数据泄漏,在学生而不是论文级别进行了新的拆分。这意味着每个学生要么只出现在训练集中,要么只出现在验证集中。由于任务变得更加困难,这导致模型准确性下降。然而,由于训练任务现在更接近于生产中的任务,因此这个新模型更有价值。
在常见任务中,样品污染可能会以细微的方式发生。让我们以公寓租赁预订网站为例。该网站包含一个点击预测模型,在给定用户查询和一个项目的情况下,预测用户是否会点击该项目。该模型用于决定向用户显示哪些列表。
为了训练这样的模型,该网站可以使用用户特征的数据集,例如他们之前的预订数量、与呈现给他们的公寓配对以及他们是否点击了它们。此数据通常存储在生产数据库中,可以查询该数据库以生成此类对。如果这个网站的工程师只是简单地查询数据库来构建这样一个数据集,他们很可能会面临数据泄露的情况。你能明白为什么吗?
在图 5-4中,我通过描述对特定用户的预测来勾勒出可能出错的示例。在顶部,您可以看到模型可以在生产中使用的功能来提供点击预测。在这里,没有先前预订的新用户会看到给定的公寓。在底部,您可以看到几天后工程师从数据库中提取数据时的功能状态。
图 5-4。数据泄漏可能由于微妙的原因而发生,例如由于缺乏数据版本控制
请注意 中的差异previous_bookings
,这是由于用户在最初看到列表后发生的活动所致。通过使用数据库快照,有关用户未来操作的信息被泄露到训练集中。我们现在知道用户最终会预订五套公寓!这种泄漏会导致使用底部信息训练的模型对错误的训练数据输出正确的预测。模型在生成的数据集上的准确性会很高,因为它利用了在生产中无法访问的数据。当部署模型时,它的性能会比预期的要差。
ML 编辑器数据拆分
这我们用来训练 ML Editor 的数据集包含 Stack Overflow 上提出的问题及其答案。乍一看,随机拆分似乎就足够了,并且在 scikit-learn 中实现起来非常简单。例如,我们可以编写如下所示的函数:
from sklearn.model_selection import train_test_split
def get_random_train_test_split(posts, test_size=0.3, random_state=40):
"""
Get train/test split from DataFrame
Assumes the DataFrame has one row per question example
:param posts: all posts, with their labels
:param test_size: the proportion to allocate to test
:param random_state: a random seed
"""
return train_test_split(
posts, test_size=test_size, random_state=random_state
)
这种方法可能会导致泄漏;你能认出它吗?
如果我们回想我们的用例,我们知道我们希望我们的模型处理它以前没有见过的问题,只看它们的内容。然而,在问答网站上,许多其他因素可以影响问题是否成功回答。这些因素之一是作者的身份。
如果我们随机拆分数据,则给定的作者可能同时出现在我们的训练和验证集中。如果某些受欢迎的作者具有独特的风格,我们的模型可能会过度适应这种风格,并由于数据泄漏而在我们的验证集上达到人为的高性能。为避免这种情况,确保每个作者仅出现在训练或验证中对我们来说更安全。这与我们之前在学生评分示例中描述的泄漏类型相同。
使用scikit-learn 的GroupShuffleSplit
类并将表示作者唯一 ID 的特征传递给它的拆分方法,我们可以保证给定的作者只出现在其中一个拆分中。
from sklearn.model_selection import GroupShuffleSplit
def get_split_by_author(
posts, author_id_column="OwnerUserId", test_size=0.3, random_state=40
):
"""
Get train/test split
Guarantee every author only appears in one of the splits
:param posts: all posts, with their labels
:param author_id_column: name of the column containing the author_id
:param test_size: the proportion to allocate to test
:param random_state: a random seed
"""
splitter = GroupShuffleSplit(
n_splits=1, test_size=test_size, random_state=random_state
)
splits = splitter.split(posts, groups=posts[author_id_column])
return next(splits)
要查看两种拆分方法之间的比较,请参阅本书 GitHub 存储库中的拆分数据笔记本。
拆分数据集后,可以将模型拟合到训练集。我们已经在“从一个简单的管道开始”中介绍了训练管道的必要部分。在本书 GitHub 存储库中的一个简单模型笔记本的训练中,我展示了一个用于 ML Editor 的端到端训练管道示例。我们将分析该管道的结果。
我们已经涵盖了在拆分数据时要牢记的主要风险,但是一旦我们的数据集被拆分并且我们已经在训练拆分上训练了一个模型,我们应该做什么?在下一节中,我们将讨论评估训练模型的不同实用方法以及如何最好地利用它们。
评委表现
现在我们已经拆分了数据,我们可以训练我们的模型并判断它的表现。大多数模型都经过训练以最小化成本函数,该函数表示模型的预测与真实标签的差距。成本函数的值越小,模型对数据的拟合越好。您最小化哪个函数取决于您的模型和您的问题,但通常最好查看它在训练集和验证集上的值。
这个通常有助于估计我们模型的偏差方差权衡,它衡量我们的模型从数据中学习到有价值的可概括信息的程度,而无需记住我们训练集的细节。
笔记
我是假设熟悉标准分类指标,但这里有一个简短的提醒以防万一。对于分类问题,准确性表示模型正确预测的示例的比例。换句话说,它是真实结果的比例,既是真阳性又是真阴性。在严重不平衡的情况下,高精度可以掩盖较差的模型。如果 99% 的案例是阳性的,那么始终预测阳性类别的模型将具有 99% 的准确率,但可能不是很有用。精确率、召回率和 f1 分数解决了这个限制。精确是预测为正的示例中真阳性的比例。记起是具有正标签的元素中真阳性的比例。这f1 分数是精确率和召回率的调和平均值。
在为了在本书的 GitHub 存储库中训练一个简单的模型笔记本,我们使用 TF-IDF 向量和我们在“ML 编辑器功能”中确定的功能训练了随机森林的第一个版本。
以下是我们训练集和验证集的准确性、精确度、召回率和 f1 分数。
Training accuracy = 0.585, precision = 0.582, recall = 0.585, f1 = 0.581
Validation accuracy = 0.614, precision = 0.615, recall = 0.614, f1 = 0.612
快速查看这些指标可以让我们注意到两件事:
-
由于我们有一个由两个类组成的平衡数据集,因此为每个示例随机选择一个类会给我们大约 50% 的准确率。我们模型的准确率达到 61%,优于随机基线。
-
我们在验证集上的准确性高于训练集。看来我们的模型在看不见的数据上效果很好。
让我们更深入地了解模型的性能。
偏差方差权衡
虚弱的训练集上的表现是高偏差的症状,也称为欠拟合,这意味着模型未能捕获有用的信息:它甚至无法在已经为其指定标签的数据点上表现良好。
在训练集上表现出色但在验证集上表现不佳是高方差的表现,也称为过度拟合,这意味着模型已经找到方法来学习其训练数据的输入/输出映射,但它已经了解到不会泛化到看不见的数据。
欠拟合和过拟合是偏差-方差权衡的两种极端情况,它描述了模型产生的错误类型如何随着其复杂性的增加而变化。随着模型复杂度的增加,方差增加,偏差减少,模型从欠拟合变为过拟合。您可以在图 5-5中看到这一点。
图 5-5。随着复杂性的增加,偏差会减少,但方差也会增加
在我们的例子中,由于我们的验证性能优于我们的训练性能,我们可以看到我们的模型没有过度拟合训练数据。我们可能会增加模型或功能的复杂性以提高性能。解决偏差方差权衡需要在减少偏差(提高模型在训练集上的性能)和减少方差(提高模型在验证集上的性能)(通常会降低训练性能作为副产品)之间找到一个最佳点。
性能指标有助于生成模型性能的总体视角。这有助于猜测模型的表现,但无法提供关于模型究竟在哪些方面成功或失败的直觉。为了改进我们的模型,我们需要更深入地研究。
超越聚合指标
一个性能指标有助于确定模型是否已从数据集中正确学习或是否需要改进。下一步是进一步检查结果,以了解模型失败或成功的方式。这很重要,原因有二:
性能验证
表现指标可能非常具有欺骗性。当处理数据严重不平衡的分类问题时,例如预测出现在不到 1% 患者身上的罕见疾病,任何始终预测患者健康的模型都将达到 99% 的准确率,即使它没有预测能力完全没有力量。存在适用于大多数问题的性能指标(f1 分数对于前一个问题会更好),但关键是要记住它们是聚合指标并且描绘了不完整的情况。要信任模型的性能,您需要在更精细的级别检查结果。
迭代
建筑模型是一个迭代过程,开始迭代循环的最佳方法是确定要改进什么以及如何改进它。性能指标无助于确定模型的问题所在以及管道的哪一部分需要改进。很多时候,我看到数据科学家试图通过简单地尝试许多其他模型或超参数,或者随意构建其他功能来提高模型性能。这种方法相当于蒙住眼睛向墙上投掷飞镖。快速构建成功模型的关键是识别并解决模型失败的具体原因。
评估您的模型:超越准确性
那里有无数种方法可以检查模型的性能,我们不会涵盖所有可能的评估方法。我们将重点关注一些通常有助于梳理表面下可能发生的事情的一些问题。
在调查模型性能时,将自己想象成一名侦探,接下来介绍的每种方法都是揭示线索的不同方式。我们将从涵盖多种技术开始,这些技术将模型的预测与数据进行对比以发现有趣的模式。
对比数据和预测
这深入评估模型的第一步是找到比聚合指标更精细的方法来对比数据和预测。我们想分解聚合性能指标,例如我们数据的不同子集的准确性、精确度或召回率。让我们看看如何应对常见的 ML 分类挑战。
您可以在本书的 GitHub 存储库中的比较数据与预测笔记本中找到所有代码示例。
为了对于分类问题,我通常建议从查看混淆矩阵开始,如图 5-6所示,其行代表每个真实类,列代表我们模型的预测。具有完美预测的模型将有一个混淆矩阵,除了从左上角到右下角的对角线外,到处都是零。实际上,这种情况很少见。让我们来看看为什么混淆矩阵通常非常有用。
混淆矩阵
一个混淆矩阵让我们一眼就能看出我们的模型是否在某些类别上特别成功,而在其他类别上是否挣扎。这对于具有许多不同类或不平衡类的数据集特别有用。
通常,我已经看到具有令人印象深刻的准确性的模型显示一个混淆矩阵,其中一列完全为空,这意味着模型永远不会预测到一个类。这通常发生在稀有类上,有时可能是无害的。但是,如果稀有类别代表重要结果,例如借款人拖欠贷款,混淆矩阵将帮助我们注意到问题。例如,我们可以通过在模型的损失函数中更重地权衡稀有类别来纠正它。
图 5-6的第一行显示我们训练的初始模型在预测低质量问题时表现良好。底行显示该模型难以检测到所有高质量问题。事实上,在所有获得高分的问题中,我们的模型只有一半的时间正确预测了他们的类别。然而,查看右栏,我们可以看到当模型预测一个问题是高质量的时,它的预测往往是准确的。
在处理两个以上类别的问题时,混淆矩阵可能会更有用。例如,我曾经和一位工程师一起工作,他试图对来自为他的最新模型绘制混淆矩阵的语音话语。他立即注意到两个对称的非对角线值异常高。这两个类(每个类代表一个词)混淆了模型及其大部分错误的原因。经过进一步检查,发现混淆模型的词是when和where。为这两个示例收集额外的数据足以帮助模型更好地区分这些发音相似的词。
混淆矩阵允许我们将模型的预测与每个类的真实类进行比较。在调试模型时,我们可能希望比它们的预测更深入地观察并检查模型输出的概率。
图 5-6。我们问题分类任务初始基线的混淆矩阵
ROC 曲线
为了对于二元分类问题,接受者操作特征 (ROC) 曲线也可以提供非常丰富的信息。ROC 曲线将真阳性率 (TPR) 绘制为假阳性率 (FPR) 的函数。
分类中使用的绝大多数模型都会返回给定示例属于特定类别的概率分数。这意味着在推理时,如果模型给出的概率高于某个阈值,我们可以选择将示例归于某个类别。这个通常称为决策阈值。
默认情况下,大多数分类器使用 50% 的概率作为决策阈值,但我们可以根据我们的用例进行更改。通过从 0 到 1 定期改变阈值并测量每个点的 TPR 和 FPR,我们获得了 ROC 曲线。
一旦我们有了模型的预测概率和相关的真实标签,就可以使用 scikit-learn 轻松获得 FPR 和 TPR。然后我们可以生成 ROC 曲线。
from sklearn.metrics import roc_curve
fpr, tpr, thresholds = roc_curve(true_y, predicted_proba_y)
对于如图 5-7中绘制的 ROC 曲线,有两个细节很重要。首先,从左下角到右上角的对角线代表随机猜测。这意味着要击败随机基线,分类器/阈值对应该位于这条线之上。此外,完美模型将由左上角的绿色虚线表示。
图 5-7。初始模型的 ROC 曲线
因为在这两个细节中,分类模型通常使用曲线下面积 (AUC) 来表示性能。AUC 越大,我们的分类器就越接近“完美”模型。随机模型的 AUC 为 0.5,而完美模型的 AUC 为 1。然而,在考虑实际应用时,我们应该选择一个特定阈值,为我们的用例提供最有用的 TPR/FPR 比率.
出于这个原因,我建议在代表我们产品需求的 ROC 曲线中添加垂直或水平线。当构建一个系统,如果认为足够紧急时,将客户请求路由给员工,那么你能负担得起的 FPR 完全取决于你的支持人员的能力和你拥有的用户数量。这意味着甚至不应考虑 FPR 高于该限制的任何模型。
在 ROC 曲线上绘制阈值可以让您拥有比简单地获得最大 AUC 分数更具体的目标。确保你的努力对你的目标有用!
我们的 ML Editor 模型将问题分类为好或坏。在这种情况下,TPR 代表了我们的模型正确判断为好的高质量问题的比例。FPR 是我们的模型声称是好的坏问题的比例。如果我们不帮助我们的用户,我们希望至少保证我们不会伤害他们。这意味着我们不应该使用任何可能会过于频繁地推荐错误问题的模型。因此,我们应该为我们的 FPR 设置一个阈值,例如 10%,并使用我们可以在该阈值下找到的最佳模型。在图 5-8中,您可以在我们的 ROC 曲线上看到此要求;它大大减少了模型可接受的决策阈值空间。
ROC 曲线让我们更细致地了解模型的性能如何随着我们做出或多或少的保守预测而变化。查看模型预测概率的另一种方法是将其分布与真实类别分布进行比较,以查看它是否经过良好校准。
图 5-8。添加代表我们产品需求的 ROC 线
校准曲线
校准图是二元分类任务的另一个信息图,因为它们可以帮助我们了解模型的输出概率是否很好地代表了它的置信度。校准图显示了真实正例的比例作为我们分类器置信度的函数。
例如,在我们的分类器给出的所有数据点中,被分类为阳性的概率高于 80%,这些数据点中有多少实际上是阳性的?完美模型的校准曲线将是一条从左下角到右上角的对角线。
在图 5-9中,我们可以在顶部看到我们的模型在 .2 和.7之间进行了很好的校准,但对于超出该范围的概率则不然。查看下面预测概率的直方图可以看出,我们的模型很少预测超出该范围的概率,这很可能导致前面显示的极端结果。该模型很少对其预测充满信心。
图 5-9。校准曲线:对角线代表完美模型(顶部);预测值的直方图(底部)
对于很多问题,比如预测广告服务中的点击率,当概率接近 0 或 1 时,数据将导致我们的模型出现严重偏差,校准曲线将帮助我们一目了然。
要诊断模型的性能,可视化单个预测可能很有价值。让我们介绍使此可视化过程高效的方法。
错误的降维
我们在“矢量化”和“降维”中描述了用于数据探索的矢量化和降维技术。让我们看看如何使用相同的技术来提高错误分析的效率。
当我们第一次介绍如何使用降维方法可视化数据时,我们按类别对数据集中的每个点进行着色,以观察标签的拓扑结构。在分析模型错误时,我们可以使用不同的配色方案来识别错误。
要识别错误趋势,请根据模型的预测是否正确为每个数据点着色。这将使您能够识别模型表现不佳的相似数据点的类型。一旦确定模型在其中表现不佳的区域,就可视化其中的一些数据点。努力想象examples 是生成这些示例中表示的特征以帮助模型更好地拟合它们的好方法。
为了帮助在困难示例中显示趋势,您还可以使用来自“Clustering”的聚类方法。对数据进行聚类后,测量每个聚类上的模型性能并确定模型性能最差的聚类。检查这些集群中的数据点以帮助您生成更多特征。
降维技术是呈现具有挑战性的示例的一种方法。为此,我们还可以直接使用模型的置信度分数。
Top-k方法
top-k 方法很简单。首先,选择数量可控的示例来可视化我们将调用的 k。对于一个人将可视化结果的个人项目,从十到十五个例子开始。对于您之前找到的每个类或集群,可视化:
-
k个表现最好的例子
-
k个表现最差的例子
-
k个最不确定的例子
可视化这些示例将帮助您识别对您的模型来说容易、难或容易混淆的示例。让我们更详细地探讨每个类别。
k个表现最好的例子
一、陈列您的模型预测正确且最有信心的 k 个示例。在可视化这些示例时,旨在确定它们之间可以解释模型性能的特征值的任何共性。这将帮助您识别模型成功利用的特征。
在可视化成功的示例以识别模型利用的特征之后,绘制不成功的示例以识别它未能拾取的特征。
k个表现最差的例子
展示您的模型预测错误并且最有信心的 k 个示例。从训练数据中的 k 个示例开始,然后进行验证。
就像可视化错误集群一样,可视化模型在训练集中表现最差的 k 个示例可以帮助识别模型失败的数据点的趋势。显示这些数据点可帮助您识别可使模型更容易使用的其他特征。
什么时候例如,在探索 ML 编辑器的初始模型错误时,我发现发布的一些问题得分较低,因为它们不包含实际问题。该模型最初无法预测此类问题的低分,因此我添加了一个功能来计算文本正文中的问号。添加此功能使模型能够对这些“非问题”问题做出准确的预测。
可视化验证数据中的 k 个最差示例可以帮助识别与训练数据显着不同的示例。如果您确实在验证集中发现了太难的示例,请参阅“拆分您的数据集”中的提示以更新您的数据拆分策略。
最后,模型并不总是对或错。他们还可以输出不确定的预测。接下来我会介绍这些。
k个最不确定的例子
可视化k 个最不确定的示例包括显示模型对其预测最不自信的示例。对于本书主要关注的分类模型,不确定示例是指模型为每个类别输出尽可能接近相等概率的示例。
如果模型经过良好校准(请参阅“校准曲线”以了解校准说明),它将为人类贴标签者也不确定的示例输出统一概率。例如,对于猫狗分类器,同时包含狗和猫的图片将属于该类别。
训练集中不确定的例子通常是标签冲突的症状。事实上,如果一个训练集包含两个重复或相似的例子,每个例子都被标记为不同的类别,模型将在训练过程中通过输出每个类别的等概率来最小化它的损失,当出现这个例子时。因此,冲突的标签会导致不确定的预测,您可以使用 top-k 方法尝试找到这些示例。
绘制验证集中前 k 个最不确定的示例可以帮助找到训练数据中的差距。模型不确定但人工标注者清楚的验证示例通常表明该模型尚未在其训练集中接触过此类数据。绘制验证集的前 k 个不确定示例可以帮助识别应该出现在训练集中的数据类型。
Top-k 评估可以直接进行。在下一节中,我将分享一个工作示例。
Top-k 实现技巧
这以下是一个简单的 top-k 实现,适用于 pandas DataFrames。该函数将包含预测概率和标签的 DataFrame 作为输入,并返回上面的每个 top-k。
def get_top_k(df, proba_col, true_label_col, k=5, decision_threshold=0.5):
"""
For binary classification problems
Returns k most correct and incorrect example for each class
Also returns k most unsure examples
:param df: DataFrame containing predictions, and true labels
:param proba_col: column name of predicted probabilities
:param true_label_col: column name of true labels
:param k: number of examples to show for each category
:param decision_threshold: classifier decision boundary to classify as
positive
:return: correct_pos, correct_neg, incorrect_pos, incorrect_neg, unsure
"""
# Get correct and incorrect predictions
correct = df[
(df[proba_col] > decision_threshold) == df[true_label_col]
].copy()
incorrect = df[
(df[proba_col] > decision_threshold) != df[true_label_col]
].copy()
top_correct_positive = correct[correct[true_label_col]].nlargest(
k, proba_col
)
top_correct_negative = correct[~correct[true_label_col]].nsmallest(
k, proba_col
)
top_incorrect_positive = incorrect[incorrect[true_label_col]].nsmallest(
k, proba_col
)
top_incorrect_negative = incorrect[~incorrect[true_label_col]].nlargest(
k, proba_col
)
# Get closest examples to decision threshold
most_uncertain = df.iloc[
(df[proba_col] - decision_threshold).abs().argsort()[:k]
]
return (
top_correct_positive,
top_correct_negative,
top_incorrect_positive,
top_incorrect_negative,
most_uncertain,
)
让我们通过将 top-k 方法用于 ML 编辑器来说明它。
ML Editor 的 Top-k 方法
出色地将 top-k 方法应用于我们训练的第一个分类器。本书的 GitHub 存储库中提供了包含 top-k 方法使用示例的笔记本。
图 5-10显示了我们的第一个 ML Editor 模型的每个类的前两个最正确的示例。两个类别之间差异最大的特征是text_len
,它表示文本的长度。分类器了解到好的问题往往很长,而差的问题往往很短。它在很大程度上依赖于文本长度来区分类别。
图 5-10。Top-k最正确
图 5-11证实了这个假设。我们的分类器预测最有可能回答的未回答问题是最长的,反之亦然。这一观察结果也证实了我们在“评估特征重要性”中的发现,我们在其中看到text_len
的是最重要的特征。
图 5-11。Top-k最不正确
我们已经确定分类器text_len
可以轻松识别已回答和未回答的问题,但此功能还不够,会导致错误分类。我们应该添加更多功能来改进我们的模型。可视化两个以上的示例将有助于识别更多候选特征。
在训练数据和验证数据上使用 top-k 方法有助于确定我们的模型和数据集的局限性。我们已经介绍了它如何帮助确定模型是否具有表示数据的能力、数据集是否足够平衡以及它是否包含足够的代表性示例。
其他型号
许多模型可以使用分类框架进行评估。在对象检测,例如,目标是让模型输出图像中感兴趣对象周围的边界框,准确性是一个常见的指标。由于每个图像都可以有多个表示对象和预测的边界框,因此计算准确度需要一个额外的步骤。一、计算预测和标签之间的重叠(通常使用Jaccard 索引)允许每个预测被标记为正确或不正确。从那里,可以计算精度并使用本章前面的所有方法。
同样,在构建旨在推荐内容,最好的迭代方式通常是在各种类别上测试模型并报告其性能。然后评估变得类似于分类问题,其中每个类别代表一个类别。
对于此类方法可能会很棘手的问题类型,例如生成模型,您仍然可以使用之前的数据探索将数据集分成多个类别,并为每个类别生成性能指标。
当我与一位数据科学家合作构建一个句子简化模型,检查模型在句子长度条件下的性能表明,较长的句子对模型来说更难。这需要进行检查和手动标记,但会导致明确的下一步行动,即用更长的句子扩充训练数据,这有助于显着提高性能。
我们已经介绍了很多通过将模型的预测与标签进行对比来检查模型性能的方法,但我们也可以检查模型本身。如果一个模型表现不佳,尝试解释其预测可能是值得的。
评估特征重要性
直接来自分类器
要验证模型是否正常工作,请可视化模型正在使用或忽略的特征。对于回归或决策树等简单模型,通过查看模型的学习参数可以直接提取特征的重要性。
为了我们在 ML Editor 案例研究中使用的第一个模型是随机森林,我们可以简单地使用 scikit-learn 的 API 来获取所有特征重要性的排名列表。特征重要性代码及其用法可以在本书的 GitHub 存储库中的特征重要性笔记本中找到。
def get_feature_importance(clf, feature_names):
importances = clf.feature_importances_
indices_sorted_by_importance = np.argsort(importances)[::-1]
return list(
zip(
feature_names[indices_sorted_by_importance],
importances[indices_sorted_by_importance],
)
)
如果我们在训练好的模型上使用上面的函数,通过一些简单的列表处理,我们可以获得十个最有用的特征的简单列表:
Top 10 importances:
text_len: 0.0091
are: 0.006
what: 0.0051
writing: 0.0048
can: 0.0043
ve: 0.0041
on: 0.0039
not: 0.0039
story: 0.0039
as: 0.0038
这里有几点需要注意:
-
文本的长度是信息量最大的特征。
-
我们生成的其他特征根本没有出现,其重要性比其他特征低一个数量级以上。该模型无法利用它们来有意义地分离类。
-
其他特征代表非常常见的单词或与写作主题相关的名词。
因为我们的模型和特征很简单,所以这些结果实际上可以为我们提供构建新特征的想法。例如,我们可以添加一个功能来计算常用词和稀有词的使用情况,以查看它们是否可以预测获得高分的答案。
如果特征或模型变得复杂,则生成特征重要性需要使用模型可解释性工具。
黑匣子解释器
当特征变得复杂时,特征重要性会变得更难解释。一些更复杂的模型,如神经网络,甚至可能无法揭示它们学习到的特征重要性。在这种情况下,利用黑盒解释器,它试图独立于模型的内部运作来解释模型的预测。
通常,这些解释器在给定数据点而不是全局上识别模型的预测特征。他们通过更改给定示例的每个特征值并观察模型的预测结果如何变化来做到这一点。LIME和SHAP是两种流行的黑盒解释器。
有关使用它们的端到端示例,请参阅本书 GitHub 存储库中的黑盒解释器笔记本。
图 5-12显示了 LIME 提供的解释,围绕哪些词在决定将此示例问题分类为高质量时最重要。LIME 通过反复从输入问题中删除单词并查看哪些单词使我们的模型更倾向于一个或另一个类来生成这些解释。
图 5-12。解释一个特定的例子
我们可以看到模型正确预测了该问题会获得高分。然而,该模型并不自信,只输出了 52% 的概率。图 5-12的右侧显示了对预测影响最大的词。这些词似乎不应该与高质量的问题特别相关,所以让我们检查更多示例,看看该模型是否利用了更有用的模式。
为了快速了解趋势,我们可以对更大的问题样本使用 LIME。对每个问题运行 LIME 并汇总结果可以让我们了解我们的模型发现哪个词具有整体预测性以做出决策。
在图 5-13中,我们绘制了数据集中 500 个问题中最重要的预测。我们可以看到我们的模型利用常用词的趋势在这个更大的样本中也很明显。似乎该模型很难在利用常用词之外进行泛化。表示罕见词的词袋特征通常具有零值。为了改进这一点,我们可以收集更大的数据集以将我们的模型暴露在更多样化的词汇表中,或者创建不那么稀疏的特征。
图 5-13。解释多个例子
您通常会对您的模型最终使用的预测变量感到惊讶。如果任何特征对模型的预测能力超出您的预期,请尝试在您的训练数据中查找包含这些特征的示例并检查它们。借此机会仔细检查您如何拆分数据集并观察数据泄漏。
例如,在构建模型以根据电子邮件内容自动将电子邮件分类为不同主题时,我指导的一位 ML 工程师曾经发现,最好的预测指标是电子邮件顶部的三字母代码。事实证明,这是数据集的内部代码,几乎完美地映射到标签。该模型完全忽略了电子邮件的内容,并记住了一个预先存在的标签。这是数据泄漏的一个明显例子,只有通过查看特征重要性才能发现。