不平衡数据的机器学习-一-

76 阅读1小时+

不平衡数据的机器学习(一)

原文:annas-archive.org/md5/d4ae49feed2111c802924e036a1b91f2

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

欢迎大家!机器学习ML)使计算机能够通过算法从数据中学习,以做出明智的决策、自动化任务并提取有价值的见解。一个经常引起注意的特定方面是不平衡数据,其中某些类别可能比其他类别有显著较少的样本。

本书提供了深入了解和导航倾斜数据复杂性的指南。你将获得在机器学习环境中管理不平衡数据集的最佳实践见解。

尽管不平衡数据可能带来挑战,但重要的是要理解,解决这种不平衡的技术并非普遍适用。它们的相关性和必要性取决于各种因素,如领域、数据分布、你正在优化的性能指标以及商业目标。在采用任何技术之前,建立基线是至关重要的。即使你目前没有面临不平衡数据的问题,了解本书中讨论的挑战和解决方案也可能是有益的。熟悉这些技术将为你提供一套全面的工具箱,帮助你为可能尚未遇到的情况做好准备。如果你发现模型性能不佳,尤其是对于代表性不足(少数)类别,书中涵盖的见解和策略可以在指导有效改进方面发挥关键作用。

随着机器学习和人工智能领域的不断扩展,对能够熟练处理各种数据挑战,包括不平衡数据的专业人士的需求将会增加。本书旨在为你提供知识和工具,让你成为那些备受追捧的专家之一。

本书面向的对象

这本全面的书精心设计,以满足各种专业人士的需求,包括以下人员:

  • 机器学习研究人员、机器学习科学家、机器学习工程师和学生:在机器学习和深度学习领域寻求获得宝贵见解和实用知识,以应对数据不平衡带来的挑战的专业人士和学习者

  • 数据科学家和分析师:渴望通过实际、现实世界的解决方案来扩展他们处理倾斜数据知识的数据专家

  • 软件工程师:在处理不平衡数据时,希望有效地将机器学习和深度学习解决方案集成到他们的应用程序中的软件工程师

  • 实用洞察寻求者:来自各个背景的专业人士和爱好者,他们希望通过实际、与行业相关的途径高效地处理机器学习和深度学习中的数据不平衡,从而在各自的岗位上脱颖而出

本书涵盖的内容

第一章, 机器学习中的数据不平衡介绍,探讨了机器学习环境中数据不平衡的问题。本章阐述了不平衡数据的本质,将其与其他数据集类型区分开来。它还全面介绍了机器学习的基本组成部分以及当存在数据不平衡时最相关的模型性能指标。本章探讨了处理不平衡数据所涉及的问题和关注点,解释了它何时可能发生以及为什么有时可能是一个挑战。更重要的是,我们将讨论何时不必担心数据不平衡,或者何时可能不值得担心。此外,它介绍了imbalanced-learn库,提供了处理不平衡数据集复杂性的宝贵见解和一般指南。

第二章, 过采样方法,介绍了过采样的概念,概述了何时使用以及何时不使用,以及增强不平衡数据集的各种技术。它通过使用imbalanced-learn库指导这些技术的实际应用,并比较它们在经典机器学习模型中的性能。本章以这些技术在现实场景中的有效性方面的实用建议结束。

第三章, 欠采样方法,介绍了欠采样作为在标准过采样不可行时平衡数据的有效方法的概念。本章涵盖了从不平衡数据中有效删除示例的策略,处理噪声观察的不同方法,以及处理易于分类实例的程序。我们还将讨论何时应避免对多数类进行欠采样。

第四章, 集成方法,探讨了包括袋装和提升在内的集成技术在增强机器学习模型性能中的应用。此外,它通过结合前几章中介绍的技术来解决不平衡数据集的挑战,其中传统的集成方法可能无效。

第五章, 代价敏感学习,探讨了包括过采样和欠采样在内的采样技术的替代方案。本章强调了代价敏感学习作为一种有效策略来克服不平衡数据集问题的意义。我们还讨论了阈值调整技术,这在数据不平衡的背景下可能非常相关。

第六章, 深度学习中的数据不平衡,介绍了深度学习的核心概念,并探讨了不平衡数据集提出的问题。您将研究各种深度学习应用中典型的不平衡数据挑战类型,并了解它们的影响。

第七章数据级深度学习方法,标志着从传统机器学习到深度学习的转变,探讨了熟悉的数据级采样技术的调整,并在深度学习模型的背景下揭示了改进这些方法的机会。它深入探讨了结合深度学习与过采样和欠采样技术,包括图像和文本的动态采样和数据增强。它强调了深度学习和传统机器学习之间的基本区别,尤其是它们处理数据的性质,而深度学习处理的是如图像、文本、音频和视频这样的非结构化数据。本章还探讨了处理计算机视觉中类别不平衡的技术,以及它们在自然语言处理NLP)问题中的应用。

第八章算法级深度学习技术,在第五章成本敏感学习的基础上扩展了概念,并将其应用于深度学习模型。我们通过使用 PyTorch 深度学习框架对损失函数进行修改来调整深度学习模型,从而提高模型性能并实现更有效的预测。

第九章混合深度学习方法,探讨了连接前两章中数据级和算法级方法的创新技术。本章介绍了图机器学习的概念,并使用一个真实的 Facebook 社交网络数据集提供了关于解决深度学习中数据不平衡的有价值见解和实际应用。我们还将介绍硬挖掘损失的概念,并在此基础上探索一种称为少数类增量校正的专门技术,该技术将硬挖掘与交叉熵损失相结合。

第十章模型校准,从不同的角度解决数据不平衡问题。本章不是关注数据预处理或模型构建,而是强调从训练模型获得的预测分数的后处理。这种后处理对于实时预测和离线模型评估都非常有价值。本章探讨了如何衡量模型的校准,并解释了为什么在处理不平衡数据时这一方面可能是不可或缺的。这尤其重要,因为数据平衡技术往往会导致模型校准不当。

附录生产环境中的机器学习管道,提供了一个构建生产环境中遇到数据不平衡的 ML 管道的基础指南。本附录提供了一个简要的路线图,概述了在哪个顺序和阶段应该整合解决数据不平衡的技术。

📌 技术的使用 - 生产环境提示

在本书的整个过程中,你将遇到“在生产中”提示框,如下所示,突出显示所讨论技术的实际应用:

🚀 OpenAI 在生产中的类别重加权

OpenAI 试图解决图像生成模型 DALL-E 2 训练数据中的偏差问题[1]。DALL-E 2 在来自互联网的大量图像数据集上训练,这些数据可能包含偏差。例如,数据集中可能包含比女性更多的男性图像,或者比其他种族或民族群体更多的图像。

这些片段提供了关于知名公司如何应对数据不平衡以及他们采用了哪些策略来有效应对这些挑战的见解。例如,关于 OpenAI 使用 DALL-E 2 的方法的提示,揭示了在过滤训练数据和无意中放大偏差之间的复杂平衡。这样的例子强调了在处理不平衡数据时既要策略性强又要谨慎的重要性。为了深入了解具体细节并理解这些实现的细节,我们鼓励你关注公司博客或提供的论文链接。这些见解可以提供更清晰的理解,了解如何有效地适应和在不同现实场景中应用技术。

要充分利用这本书

本书假设读者对机器学习、深度学习和 Python 编程有一定的基础知识。对scikit-learnPyTorch的基本了解可能会有所帮助,尽管这些可以在学习过程中掌握。

本书涵盖的软件/硬件操作系统要求
Google ColabWindows、macOS 或 Linux

对于软件要求,你有两种执行本书中提供代码的选项。你可以选择在colab.research.google.com/的 Google Colab 在线运行代码,或者将代码下载到你的本地计算机上并执行。Google Colab 提供了一个无烦恼的选项,因为它预装了所有必要的库,因此你不需要在本地机器上安装任何东西。你只需要一个网络浏览器来访问 Google Colab 和一个 Google 账户。如果你更喜欢本地工作,请确保你已经安装了 Python(3.6 或更高版本),以及指定的库,如 PyTorch、torchvision、NumPy 和 scikit-learn。所需库的列表可以在本书的 GitHub 仓库中找到。这些库与 Windows、macOS 和 Linux 操作系统兼容。现代 GPU 可以加速书中稍后出现的深度学习章节的代码执行;然而,这不是强制性的。

如果你使用的是本书的数字版,我们建议你亲自输入代码或从本书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将有助于避免与代码复制和粘贴相关的任何潜在错误。

关于参考文献,我们使用编号参考文献,如“[6]”,你可以去该章节末尾的参考文献部分下载相应的参考文献(论文/博客/文章),无论是通过链接(如果提到)还是通过谷歌学术搜索(scholar.google.com/)。

每章结束时,你将找到一系列旨在测试你对所涵盖内容的理解程度的问题。我们强烈建议你参与这些问题,以巩固你的学习。所选问题的答案或解决方案可以在本书末尾的评估部分找到。

下载示例代码文件

你可以从 GitHub 下载本书的示例代码文件github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data。如果代码有更新,它将在 GitHub 仓库中更新。

我们还有其他来自我们丰富的图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!

使用的约定

本书使用了多种文本约定。

文本中的代码: 表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个例子:“由于可以向BaggingClassifier提供一个基估计器,让我们使用最大树深为6DecisionTreeClassifier。”

代码块设置如下:

from collections import Counter X, y = make_data(sep=2)print(y.value_counts()) sns.scatterplot(data=X, x="feature_1", y="feature_2")plt.title('Separation: {}'.format(separation))plt.show()

粗体: 表示新术语、重要单词或你在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个例子:“真负率TNR):TNR 衡量的是实际负例被正确识别为负例的比例。”

小贴士或重要注意事项

看起来像这样。

联系我们

我们读者反馈总是受欢迎的。

一般反馈: 如果你对本书的任何方面有疑问,请通过客户关怀@packtpub.com 给我们发邮件,并在邮件主题中提及书名。

勘误: 尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果你在这本书中发现了错误,我们将不胜感激,如果你能向我们报告这个错误。请访问www.packtpub.com/support/err…并填写表格。

盗版: 如果你在互联网上以任何形式遇到我们作品的非法副本,如果你能提供位置地址或网站名称,我们将不胜感激。请通过版权@packt.com 与我们联系,并提供材料的链接。

如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com

分享您的想法

一旦您阅读了《不平衡数据机器学习》,我们很乐意听听您的想法!请点击此处直接进入此书的亚马逊评论页面并分享您的反馈。

您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。

下载此书的免费 PDF 副本

感谢您购买此书!

您喜欢随时随地阅读,但又无法携带您的印刷书籍到处走?

您的电子书购买是否与您选择的设备不兼容?

不要担心,现在,随着每本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何地点、任何设备上阅读。从您最喜欢的技术书籍中搜索、复制和粘贴代码直接到您的应用程序中。

优惠远不止于此,您还可以获得独家折扣、时事通讯和每日免费内容的每日电子邮件访问权限

按照以下简单步骤获取好处:

  1. 扫描下面的二维码或访问以下链接

packt.link/free-ebook/9781801070836

  1. 提交您的购买证明

  2. 就这样!我们将直接将您的免费 PDF 和其他优惠发送到您的电子邮件中

第一章:机器学习中数据不平衡的介绍

机器学习算法已经帮助解决了各种现实世界问题,从疾病预测到在线购物。然而,我们希望用机器学习解决的问题中,许多都涉及不平衡数据集。在本章中,我们将讨论并定义不平衡数据集,解释它们与其他类型数据集的不同之处。我们将通过常见问题和场景的例子来展示不平衡数据的普遍性。我们还将学习机器学习的基础知识,包括损失函数、正则化和特征工程等基本概念。我们还将了解常见的评估指标,特别是那些对于不平衡数据集非常有帮助的指标。然后我们将介绍imbalanced-learn库。

尤其,我们将学习以下主题:

  • 不平衡数据集的介绍

  • 机器学习 101

  • 数据集的类型和拆分

  • 常见的评估指标

  • 处理不平衡数据时的挑战和考虑因素

  • 我们何时会在数据集中出现不平衡?

  • 为什么不平衡数据会成为一个挑战?

  • 何时不必担心数据不平衡

  • imbalanced-learn库的介绍

  • 需要遵循的一般规则

技术要求

在本章中,我们将利用常见的库,如numpyscikit-learn,并介绍imbalanced-learn库。本章的代码和笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data/tree/main/chapter01。您可以通过点击本章笔记本顶部的Open in Colab图标或通过使用笔记本的 GitHub URL 在colab.research.google.com启动来使用 Google Colab 打开 GitHub 笔记本。

不平衡数据集的介绍

机器学习算法从我们所称的数据集的集合中学习。这些数据集包含多个数据样本或点,我们可以在整本书中将这些样本、样本或实例互换使用。

当所有目标类别具有相似数量的示例时,可以说数据集具有平衡分布,如图图 1**.1所示:

图 1.1 – 类别间示例数量几乎相等的平衡分布

不平衡数据集或倾斜数据集是指某些目标类别(也称为标签)的数量超过其他类别的数据集(图 1*.2*)。尽管这通常适用于机器学习中的分类问题(例如,欺诈检测),但在回归问题(例如,房价预测)中也会不可避免地发生:

图 1.2 – 具有五个类别和不同样本数量的不平衡数据集

我们将具有更多实例的类别标记为“多数”或“负”类,将具有较少实例的类别标记为“少数”或“正”类。大多数时候,我们的主要兴趣在于少数类,这就是为什么我们经常将少数类称为“正”类,将多数类称为“负”类:

图片

图 1.3 – 不平衡分类中常用术语的视觉指南

这可以扩展到超过两个类别,这样的分类问题称为多类分类。在这本书的前半部分,我们将只关注二元分类,以使材料更容易理解。将概念扩展到多类分类相对容易。

让我们看看几个不平衡数据集的例子:

  • 欺诈检测是指从多笔交易中检测出欺诈交易。这个问题在金融、医疗和电子商务行业中经常遇到并被广泛使用。

  • 使用机器学习的网络入侵检测涉及分析大量网络流量数据,以检测和防止未经授权的访问和计算机系统的滥用。

  • 癌症检测。癌症并不罕见,但我们仍然可能想使用机器学习来分析医疗数据,以更早地识别潜在的癌症病例并改善治疗效果。

在这本书中,我们希望专注于一般类别不平衡问题,并查看各种解决方案,在这些解决方案中,我们看到类别不平衡正在影响我们模型的性能。一个典型的问题是在模型训练期间模型看到的少数类示例非常少,模型在这些少数类上的表现相当差。

机器学习 101

让我们快速概述一下机器学习及其相关领域:

  • 人工智能是所有与智能相关问题的超集。经典机器学习包括可以通过训练传统经典模型(如决策树或逻辑回归)并预测目标值来解决的问题。它们通常在表格数据上工作,需要大量的特征工程(手动开发特征),并且在文本和图像数据上的效果较差。深度学习在图像、文本、语音和视频数据上往往表现得更好,在这些情况下,通常不需要手动特征工程,神经网络中的各个层自动为我们进行特征工程。

  • 监督学习中,数据集既有输入也有输出(标签),模型在训练期间学习预测输出。每个输入可以表示为一个特征列表。输出或标签可以是有限类别的集合(分类)、实数(回归)或更复杂的东西。分类中的监督学习的一个经典例子是鸢尾花分类。在这种情况下,数据集包括花瓣长度、花瓣宽度、萼片长度和萼片宽度等特征,标签是鸢尾花的种类(setosa、versicolor 或 virginica)。可以在该数据集上训练一个模型,然后使用该模型将新的、未见过的鸢尾花分类为这些种类之一。

  • 无监督学习中,模型要么无法访问标签,要么不使用标签,然后尝试做出一些预测——例如,将数据集中的示例聚类到不同的组中。

  • 强化学习中,模型通过犯错误和优化目标或利润变量来尝试学习。一个例子是训练一个模型来下棋,并根据通过奖励和惩罚获得的反馈调整其策略。

在监督学习中(本书的重点),主要有两种类型的问题:分类和回归。分类问题涉及将数据分类到预定义的类别或标签中,例如“欺诈”或“非欺诈”以及“垃圾邮件”或“非垃圾邮件”。另一方面,回归问题旨在预测一个连续变量,例如房屋的价格。

虽然数据不平衡也可能影响回归问题,但本书将仅专注于分类问题。这种关注是由于几个因素,例如本书的范围有限以及分类中可用的技术已经建立。在某些情况下,甚至可以将回归问题重新构造成分类问题,使得本书中讨论的方法仍然相关。

当涉及到用于分类问题的各种流行模型时,我们有相当多的经典监督机器学习模型类别:

  • 逻辑回归:这是一种用于二元分类问题的监督机器学习算法。它通过拟合逻辑函数到数据,根据一组预测变量(特征)预测二元目标变量的概率,该函数输出介于 0 和 1 之间的值。

  • 支持向量机SVMs):这些是主要用于分类的监督机器学习算法,也可以扩展到回归问题。SVMs 通过找到最优的超平面来最大化分离输入数据中的不同类别,从而成为二元和多类别分类任务的强大工具。

  • K-最近邻KNN):这是一种用于分类和回归分析的监督机器学习算法。它根据训练数据集中的k个最近邻来预测目标变量。k的值决定了在做出预测时考虑的邻居数量,并且可以调整以优化模型性能。

  • 树模型:这是一种用于分类和回归分析的监督机器学习算法。它们通过根据最重要的特征递归地将数据分割成更小的子集来创建决策树,该决策树根据输入特征预测目标变量。

  • 集成模型:这些模型结合多个单个模型以提高预测准确性和减少过拟合(本章后面将解释)。集成技术包括袋装(例如,随机森林)、提升(例如,XGBoost)和堆叠。它们常用于分类以及回归分析。

  • 神经网络:这些模型受人类大脑的启发,由多个层组成,每层有众多神经元,能够学习复杂函数。我们将在第六章中更详细地讨论这些内容,深度学习中的数据不平衡

图 1.4显示了迄今为止我们已审查的各种分类器的决策边界。它显示逻辑回归具有线性决策边界,而基于树的模型,如决策树、随机森林和 XGBoost,通过将示例分割成轴平行的矩形来形成它们的决策边界。另一方面,SVM 将数据转换到不同的空间,以便它可以绘制其非线性决策边界。神经网络具有非线性决策边界:

图片

图 1.4 – 在不平衡数据集上流行机器学习算法的决策边界

接下来,我们将深入探讨模型训练过程背后的原理。

模型训练过程中会发生什么?

在机器学习模型的训练阶段,我们向模型提供一个由示例组成的训练数据集,每个示例都有输入特征和相应的标签。让 X 代表用于训练的特征列表,y 代表训练数据集中的标签列表。模型的目标是学习一个函数 f,使得 f(X) ≈ y。

该模型具有可调整的参数,表示为θ,这些参数在训练过程中进行微调。误差函数,通常称为损失函数,定义为 L(f(X; θ), y)。该误差函数需要通过学习算法最小化,该算法找到这些参数θ的最佳设置。

在分类问题中,我们的典型损失函数是交叉熵损失(也称为对数损失):

CrossEntropyLoss(p) = {− log(p) if y = 1  − log(1 − p) otherwise

这里,p 是当 y = 1 时模型预测的概率。

当模型的预测与目标标签非常接近时,损失函数将趋近于零。然而,当预测与目标有显著偏差时,损失可以变得任意大,这表明模型拟合不良。

随着训练的进行,训练损失持续下降(图 1*.5*):

图 1.5 – 随着训练的进行损失函数的变化率

这引出了模型拟合的概念:

  • 如果一个模型过于简单,无法捕捉数据的复杂性,则称为欠拟合。它在训练数据和新的数据上表现都较差。

  • 一个模型如果能够准确捕捉数据模式而不包含学习噪声,则被认为是合适的。它在训练数据和新的数据上表现良好。

  • 一个过拟合的模型过于复杂,并学会了数据模式中的噪声。它在训练数据上表现良好,但在新的数据上表现较差(图 1*.6*):

图 1.6 – 分类任务中的欠拟合、合适拟合和过拟合模型

接下来,让我们简要了解机器学习中的两个重要概念:

  • 正则化是一组用于防止模型过度拟合训练数据的技巧。一种正则化类型(即 L1 或 L2)将惩罚项添加到损失函数中,这鼓励模型具有更小的权重并降低其复杂性。这有助于防止模型过于接近训练数据,并更好地泛化到未见数据。

  • 特征工程是选择和转换模型输入特征以改进其性能的过程。特征工程包括选择与问题最相关的特征,将它们转换以使其更具信息性,并从现有特征中创建新特征。良好的特征工程可以在模型性能上产生巨大差异,并且通常比算法或超参数的选择更重要。

数据集类型和分割

通常,我们在训练集上训练模型,并在一个独立的未见数据集上测试模型,这个数据集被称为测试集。我们这样做是为了对模型进行公平的评估。如果我们不这样做,而是在整个数据集上训练模型并在同一数据集上评估模型,我们就不知道模型在未见数据上的表现如何,此外,模型很可能会过拟合。

在机器学习中,我们可能会遇到三种类型的数据集:

  • 训练集:模型训练的数据集。

  • 验证集:用于调整模型超参数的数据集。验证集通常被称为开发集。

  • 评估集或测试集:用于评估模型性能的数据集。

当处理小型示例数据集时,通常将 80%的数据分配给训练集,10%分配给验证集,10%分配给测试集。然而,训练集和测试集之间的具体比例并不像确保测试集足够大以提供具有统计意义的评估结果那样重要。在大数据环境中,训练集、验证集和测试集分别以 98%、1%和 1%的比例分割可能是合适的。

通常,人们没有为超参数调整设置专门的验证集,而是将测试集作为评估集。这可能发生在超参数调整不是作为常规训练周期的一部分进行,而是一次性活动时。

交叉验证

交叉验证可能是一个令人困惑的术语,猜测其含义。分解它:交叉+验证,所以它是对扩展(交叉)某种东西的验证。在这里,“某种东西”对我们来说是测试集。

让我们看看什么是交叉验证:

  • 交叉验证是一种技术,用于估计模型在实际应用中的准确性。

  • 它通常用于检测过拟合——即未能泛化数据中的模式,尤其是在数据量可能有限的情况下。

让我们看看不同的交叉验证类型:

  • 保留集(Holdout):在保留集方法中,我们将数据点随机分配到两个集合中,通常分别称为训练集和测试集。然后我们在训练集上训练(构建模型),在测试集上测试(评估其性能)。

  • k 折:它的工作方式如下:

    • 我们随机打乱数据。

    • 我们将所有数据分成k部分,也称为折。我们在k-1 折上训练模型,并在剩余的折上评估它。我们使用我们选择的模型评估指标记录这个模型的性能,然后丢弃这个模型。

    • 我们重复这个过程k次,每次都保留不同的子集进行测试。我们从所有之前的模型中取评估指标值(例如,准确率)的平均值。这个平均值代表了模型的总体性能指标。

k 折交叉验证主要用于数据点有限的情况,比如 100 个点。在执行交叉验证时,使用 5 或 10 折是最常见的。

让我们看看机器学习中的常见评估指标,特别关注与不平衡数据问题相关的指标。

常见评估指标

几种机器学习和深度学习指标被用于评估分类模型的性能。

让我们来看看一些有助于评估我们在测试集上模型性能的有用指标。

混淆矩阵

给定一个试图将示例分类为正类或负类的模型,有四种可能性:

  • 真阳性(TP):这发生在模型正确地将样本预测为正类的一部分时,这是其实际的分类。

  • 假阴性 (FN): 这发生在模型错误地将正类样本分类为负类

  • 真阴性 (TN): 这指的是模型正确地将样本识别为负类,这是其实际分类

  • 假阳性 (FP): 这发生在模型错误地将负类样本预测为正类

表 1.1 展示了模型在做出预测时可能会“混淆”的方式,恰当地称为混淆矩阵。混淆矩阵是许多常见机器学习指标的基础:

预测正样本预测负样本
实际正样本真正例 (TP)假阴性 (FN)
实际负样本假阳性 (FP)真阴性 (TN)

表 1.1 – 混淆矩阵

让我们看看机器学习中一些最常见的指标:

  • 真正例率 (TPR) 衡量模型正确分类的实际正例的比例:

    TPR = 正确分类的正样本  ______________  总正样本 =  TP _ TP + FN

  • 假阳性率 (FPR) 衡量模型错误地将实际负样本识别为正样本的比例:

    FPR = 错误分类的负样本  _______________  总负样本 =  FP _ FP + TN

  • sklearn 库中的 sklearn.metrics.accuracy_score

  • sklearn 库下的 sklearn.metrics.precision_score。精确率 =  TP _ TP + FP。

  • sklearn 库下的 sklearn.metrics.recall_score。召回率 =  TP _ TP + FN。

    表 1.2 总结了精确率和召回率之间的差异:

精确率召回率
定义精确率是可信度的衡量标准召回率是完整性的衡量标准
要问的问题当模型说某件事是正样本时,它有多经常是正确的?在所有正样本中,模型正确识别了多少?
示例(使用电子邮件过滤器)精确率衡量模型标记为垃圾邮件的电子邮件中有多少实际上是垃圾邮件,占所有标记电子邮件的百分比召回率衡量模型捕获的实际垃圾邮件数量,占数据集中所有垃圾邮件的百分比
公式精确率 =  TP _ TP + FP 召回率 =  TP _ TP + FN 

表 1.2 – 精确率与召回率

为什么准确率对于不平衡数据集来说可能是一个糟糕的指标?

假设我们有一个不平衡的数据集,包含 1,000 个示例,其中 100 个标签属于类别 1(少数类)和 900 个属于类别 0(多数类)。

假设我们有一个模型,总是对所有示例预测 0。该模型对少数类的准确率为 900 + 0 _ (900 + 0+ 100 + 0 ) = 90%。

图 1.7 – 一幅漫画显示准确率可能并不总是正确的指标

这将我们带到机器学习中的 精度-召回率权衡。通常,精度和召回率是负相关的,也就是说,当召回率增加时,精度通常会降低。为什么?注意,召回率 = TP _ TP + FN,为了召回率增加,FN 应该减少。这意味着模型需要将更多项目分类为正类。然而,如果模型将更多项目分类为正类,其中一些可能是错误的分类,导致假阳性(FP)的数量增加。随着假阳性数量的增加,定义为 TP _ TP + FP 的精度将降低。用类似的逻辑,你可以论证当召回率降低时,精度通常会提高。

接下来,让我们尝试理解一些基于精度和召回率的指标,这些指标可以帮助衡量在不平衡数据上训练的模型的性能:

  • sklearn 库中的 sklearn.metrics.f1_score

  • sklearn 库中的 sklearn.metrics.fbeta_score

  • sklearn 库中的 sklearn.metrics.balanced_accuracy_score

  • 特异性 (SPE):特异性是衡量模型正确识别负样本能力的指标。在二分类中,它被计算为真阴性预测与负样本总数之比。高特异性表明模型擅长识别负类,而低特异性表明模型倾向于正类。

  • sklearn.metrics.precision_recall_fscore_supportimblearn.metrics.classification_report_imbalanced API。

  • imbalanced-learn 库中的 geometric_mean_score() 定义为“正类示例上的准确率”(召回率或灵敏度或 TPR)和“负类示例上的准确率”(特异性或 TNR)的几何平均值。因此,即使一个类别被另一个类别大量超过,该指标仍然能代表模型的总体性能。

  • imblearn.metrics.classification_report_imbalanced

表 1.3 展示了与混淆矩阵相关的指标及其公式作为扩展:

预测为正预测为负
实际上 真阳性 (TP)假阴性 (FN)召回率 = 灵敏度 = 真阳性率 (TPR) = TP _ TP + FN
实际上 假阳性 (FP)真阴性 (TN)特异性 = TN _ TN + FP
精度 = TP/(TP+FP) 假阳性率 (FPR) = FP/(FP+TN)准确率 = TP + TN ________________________ TP + TN + FP + FN F1 分数 = 2 * 精度 * 召回率 ________________________ 精度 + 召回率

表 1.3 – 带有各种指标及其定义的混淆矩阵

ROC

接收者操作特征,通常称为 ROC 曲线,是显示各种阈值值下 y 轴上的 TPRx 轴上的 FPR 的图表:

  • ROC 曲线本质上表示了y轴上正确预测的阳性实例的比例,与x轴上错误预测的阴性实例的比例形成对比。

  • 在分类任务中,阈值是一个用于确定示例类别的截止值。例如,如果一个模型将一个示例分类为“阳性”,则可能设置 0.5 的阈值来决定该实例是否应被标记为属于“阳性”或“阴性”类别。ROC 曲线可以用来识别模型的最佳阈值。这个主题将在第五章成本敏感学习中详细讨论。

  • 要创建 ROC 曲线,我们计算模型预测概率的许多不同阈值值的 TPR 和 FPR。对于每个阈值,相应的 TPR 值被绘制在y轴上,FPR 值被绘制在x轴上,形成一个点。通过连接这些点,我们生成 ROC 曲线(图 1*.8*):

图 1.8 – ROC 曲线作为 TPR 与 FPR 的图表(虚线表示没有技能的模型)

ROC 曲线的一些特性如下:

  • ROC 曲线下的面积AUC)(也称为AUC-ROC)具有特定的用途:它提供了一个单一的数值,表示模型在所有可能的分类阈值上的性能:

    • AUC-ROC 表示类别的可分离程度。这意味着 AUC-ROC 越高,模型区分类别和预测阳性类别示例为阳性以及阴性类别示例为阴性的能力就越强。AUC 接近 0 的差劲模型实际上将阳性类别预测为阴性类别,反之亦然。

    • 随机分类器的 AUC-ROC 为 0.5,并且是连接 ROC 曲线上的点(0,0)和(1,0)的对角线。

    • AUC-ROC 具有概率解释:AUC 为 0.9 表示模型将随机选择的阳性类别示例分配的分数高于阴性类别示例的概率为 90%。也就是说,AUC-ROC 可以表示如下:

P(score(𝑥+ ) > score(𝑥− ))

在这里,𝑥+ 表示阳性(少数)类别,而𝑥− 表示阴性(多数)类别。

  • 在评估模型性能的背景下,使用一个反映模型将在现实世界场景中遇到的数据分布的测试集至关重要。这在考虑如 ROC 曲线等指标时尤其相关,因为 ROC 曲线在测试数据类别失衡变化时保持一致[2]。之所以如此,是因为这两个比率都是独立于测试数据类别分布的,因为它们仅基于每个类别的正确和错误分类实例计算,而不是每个类别的实例总数。这不同于训练数据中失衡的变化,这可能会对模型性能产生不利影响,并反映在 ROC 曲线上。

现在,让我们看看使用 ROC 曲线评估失衡数据集时可能遇到的一些问题:

  • ROC 曲线不区分各种类别——也就是说,它不会强调一个类别比另一个类别更重要。这对于失衡数据集来说可能是一个问题,因为在失衡数据集中,通常检测少数类别比检测多数类别更重要。正因为如此,它可能无法很好地反映少数类别。例如,我们可能希望召回率比精确率更好。

  • 虽然 ROC 曲线可以用于比较模型在全范围内 FPR 的性能,但对于需要非常低 FPR 的特定应用,如金融交易或银行应用中的欺诈检测,它们可能不那么相关。FPR 需要非常低的原因是,此类应用通常需要有限的手动干预。可以手动检查的交易数量可能低至所有数据的 1%甚至 0.1%,这意味着 FPR 不能高于 0.001。在这些情况下,ROC 曲线上的 FPR 等于 0.001 右侧的所有内容都变得无关紧要[3]。为了进一步理解这一点,让我们考虑一个例子:

    • 假设对于一个测试集,我们总共有 10,000 个示例,其中只有 100 个正类示例,占所有示例的 1%。因此,任何高于 1%的 FPR——即 0.01——都会引发过多的警报,无法由调查人员手动处理。

    • 在 ROC 曲线的左侧远端的表现对于大多数现实世界问题至关重要,这些问题通常由大量负实例主导。因此,对于需要保持非常低 FPR 的应用,ROC 曲线的大部分内容都变得无关紧要。

精确率-召回率曲线

与 ROC 曲线类似,精确率-召回率PR)曲线在不同阈值下绘制了一对指标。但与绘制 TPR 和 FPR 的 ROC 曲线不同,PR 曲线绘制的是精确率和召回率。为了展示这两条曲线之间的差异,让我们假设我们比较了两个模型——模型 1 和模型 2——在特定手工制作的失衡数据集上的性能:

  • 在*图 1.9(a)*中,两个模型的 ROC 曲线似乎接近左上角(点(0, 1)),这可能会让你得出结论,两个模型都表现良好。然而,这在不平衡数据集的背景下可能会误导。

  • 当我们将注意力转向*图 1.9(b)*中的 PR 曲线时,故事发生了变化。模型 2 接近图表的理想右上角(点(1, 1)),这表明在考虑精度和召回率时,其性能远优于模型 1。

  • PR 曲线显示模型 2 相对于模型 1 有优势。

ROC 曲线和 PR 曲线之间的这种差异也强调了使用多个指标进行模型评估的重要性,尤其是在处理不平衡数据时:

图片

图 1.9 – 与 ROC 曲线相比,PR 曲线可以显示模型之间的明显差异

让我们尝试详细理解这些观察结果。虽然 ROC 曲线显示两种模型性能之间的差异很小,但 PR 曲线显示了更大的差距。原因在于 ROC 曲线使用 FPR,即 FP/(FP+TN)。通常,对于不平衡数据集,TN 非常高,因此即使 FP 有相当大的变化,FPR 的整体值也会被 TN 所掩盖。因此,ROC 变化不大。

哪个分类器更优越的结论可能会随着测试集中类别的分布而改变。在数据集倾斜的情况下,PR 曲线可以比 ROC 曲线更清楚地显示出模型表现不佳,如图所示。

sklearn中的average_precision_score

ROC 曲线与 PR 曲线之间的关系

ROC 曲线和 PR 曲线之间的主要区别在于,ROC 评估模型在“计算”正负类别方面的表现,而 PR 仅关注正类别。因此,当处理平衡数据集的情况,并且你关心正负类别时,ROC AUC 表现得非常好。相比之下,当处理不平衡情况时,PR AUC 更为合适。然而,重要的是要记住,PR AUC 仅评估模型“计算”正类的能力。因为 PR 曲线对正类(少数类)更敏感,所以本书的前半部分我们将使用 PR 曲线。

我们可以在x轴上以精度重新想象 PR 曲线,在y轴上则是 TPR,也称为召回率。这两条曲线的关键区别在于,虽然 ROC 曲线使用 FPR,PR 曲线则使用精度。

如前所述,当处理不平衡数据集时,FPR(假正率)往往非常低。这种低 FPR 值的特性在诸如欺诈检测等某些应用中至关重要,因为这些应用中手动调查的能力本质上有限。因此,这种观点可能会改变对分类器性能的认识。如图 1.9 所示,当使用平均精度(0.69 比 0.90)而不是 AUC-ROC(0.97 和 0.95)进行比较时,两个模型的性能似乎也出现了反转。

让我们总结一下:

  • AUC-ROC 是在 y 轴上以 TPR 为基准,x 轴上以 FPR 为基准的曲线下的面积。

  • AUC-PR 是在 y 轴上以精确率为基准,x 轴上以召回率为基准的曲线下的面积。

由于 TPR 等于召回率,这两个图表仅在比较召回率时有所不同——要么是精确率,要么是 FPR。此外,这两个图表相对于彼此旋转了 90 度:

AUC-ROCAUC-PR
一般公式AUC(TPR, FPR)AUC(精确率,召回率)
扩展公式AUC(  TP _ TP + FN ,  FP _ FP + TN )AUC(  TP _ TP + FP ,  TP _ TP + FN )
对应关系AUC(召回率,FPR)AUC(精确率,召回率)

表 1.4 – 比较 ROC 和 PR 曲线

在接下来的几节中,我们将探讨导致数据集不平衡的情况,这些不平衡可能带来的挑战,以及数据不平衡可能不是问题的情况。

处理不平衡数据时的挑战和考虑因素

在某些情况下,直接使用数据用于机器学习而不担心数据不平衡,可能会得到适合特定业务场景的可用的结果。然而,在某些情况下,需要更多的努力来管理不平衡数据的影响。

广泛的说法,声称你必须始终或永远调整不平衡类别,往往具有误导性。事实是,解决类别不平衡的需要取决于数据的特定特征、手头的问题以及可接受解决方案的定义。因此,处理类别不平衡的方法应根据这些因素量身定制。

我们在什么情况下会遇到数据集的不平衡?

在本节中,我们将探讨导致数据集不平衡的各种情况和原因,例如罕见事件的发生或数据收集过程的偏差:

  • 问题固有的:有时,我们需要解决的任务涉及检测数据集中的异常值——例如,患有某种疾病的患者或一组交易中的欺诈案例。在这种情况下,数据集固有不平衡,因为目标事件本身就很罕见。

  • 在构建机器学习解决方案时数据收集的高成本:对于某些类别,收集数据可能成本过高。例如,由于需要专业的医疗测试、防护设备,以及在高度紧张的医疗环境中获取知情同意的伦理和物流挑战,收集 COVID-19 患者的数据成本很高。

  • 某些类别的噪声标签:这可能在数据收集过程中,某些类别的标签中引入了大量噪声时发生。

  • 标签错误:标签错误也可能导致数据不平衡。例如,如果一些样本被错误地标记为负例,而实际上它们是正例,这可能导致数据集中的不平衡。此外,如果一个类别本身就很罕见,人类标注者可能会存在偏见,并忽略那些存在的罕见类别的少数示例。

  • 采样偏差:数据收集方法有时会在数据集中引入偏差。例如,如果一项调查在特定的地理区域或特定的人群中进行,那么得到的数据集可能无法代表整个群体。

  • 数据清洗:在数据清洗或过滤过程中,由于数据不完整或缺失,某些类别或样本可能会被移除。这可能导致剩余数据集中的不平衡。

为什么不平衡数据会是一个挑战?

让我们深入探讨不平衡数据对模型预测的困难及其对模型性能的影响:

  • 度量指标如准确率的失败:正如我们之前讨论的,在数据不平衡的背景下(一个 99%不平衡的数据集仍然可以达到 99%的准确率),传统的度量指标如准确率可能会产生误导。阈值不变的度量指标,如 PR 曲线或 ROC 曲线,试图揭示模型在广泛阈值范围内的性能。真正的挑战在于混淆矩阵中“真正负例”单元格的不成比例影响。那些较少关注“真正负例”的度量指标,如精确率、召回率或 F1 分数,更适合评估模型性能。需要注意的是,这些度量指标有一个隐藏的超参数——分类阈值——不应被忽视,而应在实际应用中进行优化(参考第五章成本敏感学习,了解更多关于阈值调整的信息)。

  • 不平衡数据对模型的损失函数可能构成挑战:这可能是因为损失函数通常设计为最小化预测输出与训练数据真实标签之间的错误。当数据不平衡时,一个类别的实例比另一个类别的实例多,模型可能会偏向多数类。我们将在第五章成本敏感学习第八章算法级深度学习技术中更详细地讨论解决这个问题。

  • 不同类别的误分类成本不同:通常,误分类正例的成本可能比误分类负例的成本更高。我们可能有的假阳性比假阴性更昂贵。例如,通常将患有癌症的患者误诊为健康(假阴性)的成本将远高于将健康患者误诊为患有癌症(假阳性)。为什么?因为第二种情况下,通过进行一些额外的测试来重新验证测试结果的成本要低得多,而不是在第一种情况下晚得多地检测到它。这被称为误分类成本,对于多数类和少数类可能不同,这使得不平衡数据集变得复杂。我们将在第五章成本敏感学习中进一步讨论这个问题。

  • 计算资源受限:在金融、医疗和零售等行业,处理大数据是一个常见的挑战。在这些大型数据集上进行训练不仅耗时,而且由于所需的计算能力,成本也很高。在这种情况下,对多数类进行下采样或欠采样变得至关重要,这将在第三章欠采样方法中讨论。此外,为少数类获取更多样本还可以进一步增加数据集大小和计算成本。内存限制也可能限制可以处理的数据量。

  • 少数类样本变化不足,无法充分代表其分布:通常,少数类样本的绝对数量问题并不像少数类样本的变化那样严重。数据集可能看起来很大,但样本中可能没有很多变化或种类,足以代表少数类的分布。这可能导致模型无法正确学习分类边界,从而导致模型性能不佳(图 1.10)。这种情况在计算机视觉问题中经常发生,例如目标检测,我们可能只有少数几个特定类别的样本。在这种情况下,数据增强技术(在第7 章数据级深度学习方法中讨论)可以显著帮助:

图片

图 1.10 – 不同分布的少数类示例对决策边界的影响 – 十字表示多数类,圆圈表示少数类

  • 未校准模型的性能不佳:不平衡数据对未校准模型来说可能是一个挑战。未校准模型是指那些不输出良好校准概率的模型,这意味着预测概率可能不会反映预测类别的真实可能性:

    • 当处理不平衡数据时,未校准的模型可能会特别容易产生偏向多数类的预测,因为它们可能无法有效地区分少数类和多数类。这可能导致模型在少数类上的性能不佳,模型可能会产生过于自信的预测或过于保守的预测。

    • 例如,在不平衡数据上训练的未校准模型可能会错误地将属于少数类的实例分类为多数类示例,通常具有很高的置信度。这是因为模型可能没有学会根据数据的不平衡调整其预测,也可能对少数类示例没有很好的理解。

    • 为了应对这一挑战,使用能够输出反映预测类别真实可能性的良好校准模型[4]非常重要。这可以通过如 Platt 缩放或等调回归等技术实现,这些技术可以将未校准模型的预测概率校准,以产生更准确和可靠的概率。模型校准将在第十章模型校准中详细讨论。

  • 由于未调整阈值导致的模型性能不佳:在使用不平衡数据集训练的模型进行预测时,使用智能阈值选择非常重要。当模型概率超过 0.5 时简单地预测 1 可能并不总是最佳方法。相反,我们应该考虑其他可能更有效的阈值。这可以通过检查模型的 PR 曲线来实现,而不是仅仅依赖于默认概率阈值 0.5 的成功率。阈值调整对于在自然或人工平衡数据集上训练的模型来说可能非常重要。我们将在第五章成本敏感学习中详细讨论阈值调整。

接下来,让我们看看何时我们不应该对数据不平衡采取任何措施。

何时不必担心数据不平衡

类不平衡不一定总是对性能产生负面影响,有时使用特定于不平衡的方法可能会使结果变得更糟[5]。因此,在应用任何专门技术之前,准确评估任务是否真正受到类不平衡的影响至关重要。一种策略可以简单到设置一个不考虑类不平衡的基线模型,并观察模型在各种类别上使用各种性能指标的性能。

让我们探讨数据不平衡可能不是问题,且不需要纠正措施的场景:

  • 当不平衡程度较小时:如果数据集中的不平衡相对较小,少数类到多数类的比例只有轻微倾斜(比如说 4:5 或 2:3),对模型性能的影响可能最小。在这种情况下,模型可能仍然表现良好,无需任何特殊技术来处理不平衡。

  • 当目标是预测多数类:在某些情况下,重点可能在于准确预测多数类,而少数类可能不是特别感兴趣。例如,在线广告定位时,重点可以放在针对可能点击广告的用户(多数类)以最大化点击率和即时收入,而对可能觉得广告烦人的用户(少数类)的关注较少。

  • 当两类误分类的成本几乎相等时:在某些应用中,将正类样本误分类的成本并不高(即假阴性)。例如,将电子邮件分类为垃圾邮件或非垃圾邮件。偶尔错过一封垃圾邮件并将其误分类为非垃圾邮件是完全正常的。在这种情况下,误分类对性能指标的影响可能可以忽略不计,不平衡可能不是问题。

  • 当数据集足够大时:即使少数类到多数类样本的比例非常低,例如 1:100,并且如果数据集足够大,两个类别中都有大量样本,数据不平衡对模型性能的影响可能会降低。随着数据集的增大,模型可能能够更有效地学习少数类中的模式。然而,仍然建议将基线模型的性能与考虑数据不平衡的模型的性能进行比较。例如,将基线模型与具有阈值调整、过采样和欠采样的模型(第二章过采样方法,和第三章欠采样方法),以及基于算法的技术(第五章成本敏感学习)进行比较。

在下一节中,我们将熟悉一个在处理不平衡数据时非常有用的库。我们将在一个不平衡的玩具数据集上训练一个模型,并查看一些指标来评估训练模型的性能。

不平衡-learn 库简介

imbalanced-learn(导入为 imblearn)是一个 Python 包,提供了处理数据不平衡的几种技术。在这本书的前半部分,我们将大量依赖这个库。让我们安装 imbalanced-learn 库:

pip3 install imbalanced-learn==0.11.0

我们可以使用 imbalanced-learn 为我们的分析创建一个合成数据集:

from sklearn.datasets import make_classification
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
def make_data(sep):
    X, y = make_classification(n_samples=50000,
        n_features=2, n_redundant=0,
        n_clusters_per_class=1, weights=[0.995],
        class_sep=sep, random_state=1)
    X = pd.DataFrame(X, columns=['feature_1', 'feature_2'])
    y = pd.Series(y)
    return X, y

让我们分析生成的数据集:

from collections import Counter
X, y = make_data(sep=2)
print(y.value_counts())
sns.scatterplot(data=X, x="feature_1", y="feature_2", hue=y)
plt.title('Separation: {}'.format(separation))
plt.show()

这是输出:

0     49498
1       502

图 1.11 – 具有两个特征的 2 类数据集

让我们将这个数据集分成训练集和测试集:

From sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify = \
    y, test_size=0.2, random_state=42)
print('train data: ', Counter(y_train))
print('test data: ', Counter(y_test))

这是输出:

train data:  Counter({0: 39598, 1: 402})
test data:  Counter({0: 9900, 1: 100})

注意 sklearntrain_test_split API 中 stratify 的使用。指定 stratify=y 确保我们在训练集和测试集中保持多数和少数类别的相同比例。让我们更详细地了解分层。

分层抽样是一种根据某些共享特征将数据集分成各种子组(称为“层”)的方法。在处理不平衡数据集时,它非常有价值,因为它确保训练集和测试集具有与原始数据集相同的类别标签比例。

在不平衡数据集中,少数类占总数据的很小一部分。如果我们不进行分层进行简单的随机分割,少数类可能无法在训练集中得到充分代表,甚至可能完全被排除在测试集之外,这可能导致性能不佳和不可靠的评估指标。

通过分层抽样,每个类别在整体数据集中的比例在训练集和测试集中都得到保留,确保了代表性的抽样,并为模型从少数类中学习提供了更好的机会。这导致了一个更稳健的模型和更可靠的模型性能评估。

scikit-learn 的分层 API

scikit-learn 的 API,例如 RepeatedStratifiedKFoldStratifiedKFold,使用分层概念通过交叉验证评估模型性能,尤其是在处理不平衡数据集时。

现在,让我们在训练数据上训练一个逻辑回归模型:

from sklearn.linear_model import LogisticRegression
lr = LogisticRegression(random_state=0, max_iter=2000)
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)

让我们从 sklearn 库中获取报告指标:

from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))

这会输出以下内容:

          precision     recall      f1-score    support
0         0.99          1.00        1.00        9900
1         0.94          0.17        0.29        100
accuracy                                0.99      10000
macro avg       0.97        0.58        0.64      10000
weighted avg    0.99        0.99        0.99      10000

让我们从 imblearn 获取报告指标:

from imblearn.metrics import classification_report_imbalanced
print(classification_report_imbalanced(y_test, y_pred))

这会输出更多列:

图 1.12 – imbalanced-learn 的分类报告输出

你注意到这里与 sklearn API 相比有额外的指标吗?我们得到了三个额外的指标:spe 用于特异性,geo 用于几何平均值,iba 用于指数平衡准确率。

imblearn.metrics模块有几个这样的函数,可以帮助处理不平衡数据集。除了classification_report_imbalanced()之外,它还提供了sensitivity_specificity_support()geometric_mean_score()sensitivity_score()specificity_score()等 API。

需要遵循的一般规则

通常,任何机器学习流程的第一步应该是将数据分为训练集、测试集和验证集。我们应该避免在数据分割之后应用任何处理不平衡的技术。我们应该首先将数据分为训练集、测试集和验证集,然后对训练数据进行任何必要的调整。在分割数据之前应用如过采样(见第二章过采样方法)等技术可能会导致数据泄露、过拟合和过度乐观[6]。

我们应该确保验证数据与测试数据非常相似。验证数据和测试数据都应该代表模型将用于预测的真实世界场景。避免对验证集应用任何采样技术或修改。唯一的要求是包含来自所有类别的足够数量的样本。

让我们稍微讨论一下使用无监督学习算法。异常检测离群值检测是一类可以用于处理不平衡数据问题的算法。异常或离群值是显著偏离其他数据的点。这些异常通常对应于不平衡数据集中的少数类,使得无监督方法可能很有用。

这些问题的常用术语是单类分类。当正例(少数类)稀疏或收集它们在训练之前不可行时,这种技术特别有益。模型仅针对被认为是“正常”或多数类的数据进行训练。然后它将新实例分类为“正常”或“异常”,有效地识别可能属于少数类的实例。这对于二元不平衡分类问题特别有用,其中多数类被认为是“正常”,而少数类被认为是异常。

然而,它确实有一个缺点:在训练期间丢弃了离群值或正例[7],这可能导致潜在的有价值信息的丢失。

总结来说,虽然像单类分类这样的无监督方法为处理类别不平衡提供了一个替代方案,但本书的讨论将始终集中在监督学习算法上。尽管如此,我们建议您在认为适当的时候探索和实验这些解决方案。

摘要

让我们总结一下到目前为止学到的内容。不平衡数据是机器学习中常见的问题,其中一个类别的实例数量显著多于另一个类别。不平衡数据集可能源于各种情况,包括罕见事件发生、高数据收集成本、标签噪声、标签错误、采样偏差和数据清洗。这可能会对机器学习模型构成挑战,因为它们可能会偏向多数类。

可以使用几种技术来处理不平衡数据,例如过采样、欠采样和成本敏感学习。最佳技术取决于具体问题和数据。

在某些情况下,数据不平衡可能不是问题。当数据集足够大时,数据不平衡对模型性能的影响可能会降低。然而,仍然建议比较基线模型性能与使用解决数据不平衡的技术(如阈值调整、基于数据的技术(过采样和欠采样)以及基于算法的技术)构建的模型性能。

传统的性能指标,如准确率,在不平衡数据集中可能会失效。一些更有用的不平衡数据集指标是 ROC 曲线、PR 曲线、精确率、召回率和 F1 分数。虽然 ROC 曲线适用于平衡数据集,但 PR 曲线在某一类比另一类更重要时,更适合不平衡数据集。

imbalanced-learn库是一个 Python 包,提供了处理数据不平衡的几种技术。

有一些一般规则需要遵循,例如在应用任何处理数据不平衡的技术之前将数据分为训练/测试/验证集,确保验证数据与测试数据非常相似,并且测试数据代表模型将最终进行预测的数据,以及避免对验证集和测试集应用任何采样技术或修改。

一类分类或异常检测是另一种可以用于处理无监督不平衡数据问题的技术。在这本书中,我们将只关注监督学习算法的讨论。

在下一章中,我们将探讨通过应用过采样技术来处理数据不平衡问题的一种常见方法。

问题

  1. 训练模型时选择损失函数如何影响不平衡数据集上模型的性能?

  2. 你能解释为什么 PR 曲线在处理高度倾斜的数据集时比 ROC 曲线更有信息量吗?

  3. 使用准确率作为不平衡数据集上模型性能的指标可能会出现哪些潜在问题?

  4. “类别不平衡”的概念如何影响机器学习中的特征工程过程?

  5. 在不平衡数据集的背景下,k 折交叉验证中“k”的选择如何影响模型的性能?你将如何解决这个问题?

  6. 测试数据中类别的分布如何影响 PR 曲线,为什么?ROC 曲线又是如何?

  7. 在不平衡数据集的背景下,高 AUC-ROC 但低 AUC-PR 意味着什么?

  8. “采样偏差”的概念如何有助于解决机器学习中不平衡数据集的挑战?

  9. “标签错误”的概念如何有助于解决机器学习中不平衡数据集的挑战?

  10. 在哪些现实世界场景中,处理不平衡数据集是问题固有的部分?

  11. 使用 fetch_dataset API 然后计算 MCC、准确率、精确率、召回率和 F1 分数的值。看看 MCC 值是否可以成为该数据集的有用指标。

参考文献

  1. V. García, R. A. Mollineda, 和 J. S. Sánchez, 平衡精度指数:偏斜类别分布的性能度量, 见《模式识别与图像分析》,第 5524 卷,H. Araujo, A. M. Mendonça, A. J. Pinho, 和 M. I. Torres 编著. 柏林,海德堡:Springer Berlin Heidelberg,2009,第 441–448 页. 访问时间:2023 年 3 月 18 日. [在线]. 可在 link.springer.com/10.1007/978-3-642-02172-5_57 获取。

  2. T. Fawcett, ROC 分析的介绍, 模式识别信函,第 27 卷,第 8 期,第 861–874 页,2006 年 6 月,doi: 10.1016/j.patrec.2005.10.010.

  3. Y.-A. Le Borgne, W. Siblini, B. Lebichot, 和 G. Bontempi, 可复现的机器学习用于信用卡欺诈检测 - 实用手册. 自由布鲁塞尔大学,2022. [在线]. 可在 github.com/Fraud-Detection-Handbook/fraud-detection-handbook 获取。

  4. W. Siblini, J. Fréry, L. He-Guelton, F. Oblé, 和 Y.-Q. Wang, 通过校准掌握你的指标,第 12080 卷,2020,第 457–469 页. doi: 10.1007/978-3-030-44584-3_36.

  5. Xu-Ying Liu, Jianxin Wu, 和 Zhi-Hua Zhou, 探索性下采样用于类别不平衡学习, IEEE Trans. Syst., Man, Cybern. B, 第 39 卷,第 2 期,第 539–550 页,2009 年 4 月,doi: 10.1109/TSMCB.2008.2007853.

  6. M. S. Santos, J. P. Soares, P. H. Abreu, H. Araujo, 和 J. Santos, 不平衡数据集的交叉验证:避免过度乐观和过度拟合方法 [研究前沿],IEEE Comput. Intell. Mag., 第 13 卷,第 4 期,第 59–76 页,2018 年 11 月,doi: 10.1109/MCI.2018.2866730.

  7. A. Fernández, S. García, M. Galar, R. Prati, B. Krawczyk, 和 F. Herrera, 从不平衡数据集中学习. Springer 国际出版社,2018

第二章:过采样方法

在机器学习中,我们往往没有足够的少数类样本。一个可能的解决方案可能是收集此类更多的样本。例如,在检测患者是否患有癌症的问题中,如果我们没有足够的癌症类样本,我们可以等待一段时间来收集更多样本。然而,这种策略并不总是可行或明智,并且可能耗时。在这种情况下,我们可以通过使用各种技术来增强我们的数据。其中一种技术就是过采样。

在本章中,我们将介绍过采样的概念,讨论何时使用它,以及执行它的各种技术。我们还将通过imbalanced-learn库的 API 演示如何利用这些技术,并使用一些经典的机器学习模型比较它们的性能。最后,我们将总结一些实际建议,说明哪些技术在特定现实世界条件下效果最佳。

在本章中,我们将涵盖以下主题:

  • 随机过采样

  • SMOTE

  • SMOTE 变体

  • ADASYN

  • 各种过采样方法的模型性能比较

  • 使用各种过采样技术的指南

  • 多类分类中的过采样

技术要求

在本章中,我们将使用如numpyscikit-learnimbalanced-learn等常用库。本章的代码和笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data/tree/master/chapter02。您只需点击本章笔记本顶部的在 Colab 中打开图标,或通过colab.research.google.com使用笔记本的 GitHub URL 启动,即可启动 GitHub 笔记本。

什么是过采样?

采样涉及从更大的观察集中选择观察子集。在本章中,我们最初将关注具有两个类别的二分类问题:正类和负类。少数类的实例数量显著少于多数类。在本章的后面部分,我们将探讨多类分类问题。在本章的结尾,我们将探讨多类分类问题的过采样。

过采样是一种数据平衡技术,它为少数类生成更多的样本。然而,这可以很容易地扩展到适用于任何有多个类别且存在不平衡的类别。图 2.1显示了在应用过采样技术之前,少数类和多数类的样本是不平衡的(a)以及之后平衡的情况(b):

图 2.1 – 过采样后少数类样本数量的增加

你可能会问:“为什么需要过采样?”这是必要的,以便我们给模型足够的少数类样本来从中学习。如果我们提供的少数类实例太少,模型可能会选择忽略这些少数类示例,而只关注多数类示例。这反过来又会导致模型无法很好地学习决策边界。

让我们使用sklearn库的make_classification API 生成一个 1:99 比例的两类不平衡数据集,该 API 为每个类别创建一个正态分布的点集。这将生成两个类的不平衡数据集:一个是有标签 1 的少数类,另一个是有标签 0 的多数类。在本章中,我们将应用各种过采样技术来平衡这个数据集:

from collections import Counter
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=10000, n_features=2,\
    n_redundant=0, n_classes=2, flip_y=0, n_clusters_per_class=2,\
    class_sep=0.79, weights=[0.99], random_state=81)

这段代码生成了 100 个类别 1 的例子和 9,900 个类别 0 的例子,不平衡比率为 1:99。通过绘制数据集,我们可以看到例子是如何分布的:

图 2.2 – 不平衡比率为 1:99 的数据集

在本节中,我们了解了过采样的必要性。我们还生成了一个合成的非平衡二分类数据集,以展示各种过采样技术的应用。

随机过采样

平衡数据集中不平衡的最简单策略是随机选择少数类的样本并重复或复制它们。这也被称为随机过采样****带替换

为了增加少数类观察的数量,我们可以复制少数类数据观察结果足够多次以平衡两个类。这听起来太简单了吗?是的,但它是有效的。通过增加少数类样本的数量,随机过采样减少了向多数类的偏差。这有助于模型更有效地学习少数类的模式和特征。

我们将使用来自imbalanced-learn库的随机过采样。RandomOverSampler类的fit_resample API 重新采样原始数据集并使其平衡。sampling_strategy参数用于指定各种类的新比率。例如,我们可以将sampling_strategy=1.0指定为两个类具有相同数量的例子。

有多种方式可以指定sampling_strategy,例如浮点值、字符串值或dict – 例如,{0: 50, 1: 50}:

from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(sampling_strategy=1.0, random_state=42)
X_res, y_res = ros.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

这里是输出:

Resampled dataset shape Counter({0: 9900, 1: 9900})

因此,我们从 1:99 的比例变为了 1:1,这正是我们期望的sampling_strategy=1.0的结果。

让我们绘制过采样后的数据集:

图 2.3 – 使用 RandomOverSampler(RandomOverSampler)过采样后的数据集(标签 1 的示例由于重叠而未改变)

应用随机过采样后,标签为 1 的示例会相互重叠,给人一种没有变化的感觉。反复重复相同的数据点可能导致模型记住特定的数据点,而无法推广到新的、未见过的示例。RandomOverSampler中的shrinkage参数让我们可以通过一个小量扰动或移动每个点。

shrinkage参数的值必须大于或等于 0,可以是floatdict类型。如果使用float数据类型,相同的收缩因子将用于所有类别。如果使用dict数据类型,收缩因子将针对每个类别具体指定。

图 2.4 中,我们可以观察到shrinkage=0.2的随机过采样的影响:

图片

图 2.4 – 应用随机过采样(shrinkage=0.2)的结果

在本章的结尾,我们将比较随机过采样与其他多种过采样技术在多个模型和数据集上的性能。这将为我们提供关于它们在实际应用中的有效性的见解。

🚀 Grab 在生产中使用随机过采样

Grab 是一家东南亚的打车和食品配送服务公司,开发了一个用于存储和检索图像和地图数据的图像收集平台[1]。该平台的一个关键特性是能够自动检测和模糊街景图像中的个人身份信息PII),如人脸和车牌。这对于维护用户隐私至关重要。用于此目的的数据集存在显著的不平衡,负样本(没有 PII 的图像)远多于正样本(有 PII 的图像)。手动标注不可行,所以他们转向机器学习来解决此问题。

为了解决数据不平衡,Grab 采用了随机过采样技术来增加正样本的数量,从而提高了他们的机器学习模型的性能。

随机过采样的问题

随机过采样往往会导致模型过拟合,因为生成的合成观察值会重复,模型会一次又一次地看到相同的观察值。收缩试图在某种程度上处理这个问题,但可能很难找到一个合适的收缩值,而且收缩并不关心生成的合成样本是否与多数类样本重叠,这可能导致其他问题。

在上一节中,我们学习了应用过采样平衡数据集和减少对多数类偏差的最基本和实用的技术。很多时候,随机过采样本身可能会极大地提高我们模型的表现,以至于我们可能甚至不需要应用更高级的技术。在生产环境中,在准备引入更多复杂性之前,保持事情简单明了也是有利的。正如他们所说,“过早优化是万恶之源”,所以我们从简单的事情开始,只要它能提高我们模型的表现。

在随后的章节中,我们将探讨一些替代技术,例如 SMOTE 和 ADASYN,它们采用不同的过采样方法,并缓解了与随机过采样技术相关的一些问题。

SMOTE

随机过采样的主要问题是它会重复少数类的观察结果。这通常会导致过拟合。合成少数过采样技术SMOTE)[2]通过使用称为插值的技术来解决这种重复问题。

插值涉及在已知数据点的范围内创建新的数据点。将插值想象成类似于生物学中的繁殖过程。在繁殖中,两个个体结合在一起产生一个具有两者特征的新个体。同样,在插值中,我们从数据集中选择两个观察结果,并通过选择两个选定点之间线上的随机点来创建一个新的观察结果。

我们通过插值合成示例来过采样少数类。这防止了少数样本的重复,同时生成与已知点相似的新的合成观察结果。图 2.5展示了 SMOTE 是如何工作的:

图 2.5 – SMOTE 的工作原理

在这里,我们可以看到以下内容:

  • 大多数类和少数类样本被绘制出来(左侧)

  • 通过在连接少数样本和两个最近邻多数类样本的线上的随机点生成合成样本(右侧)

SMOTE 最初是为连续输入设计的。为了保持解释的简单性,我们将从连续输入开始,稍后再讨论其他类型的输入。

首先,我们将检查 SMOTE 的功能,并探讨与此技术相关的任何潜在缺点。

SMOTE 是如何工作的

SMOTE 算法的工作原理如下:

  1. 它只考虑少数类的样本。

  2. 它在少数样本上训练 KNN。k的典型值是 5。

  3. 对于每个少数样本,从该点到其 KNN 示例之间画一条线。

  4. 对于这样的线段,随机选择线段上的一个点来创建一个新的合成示例。

让我们使用imbalanced-learn库的 API 来应用 SMOTE:

from imblearn.over_sampling import SMOTE
sm = SMOTE(random_state=0)
X_res, y_res = sm.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

这里是输出:

Resampled dataset shape Counter({0: 9900, 1: 9900})

过采样后的数据集看起来像这样:

图 2.6 – 使用 SMOTE 进行过采样

🚀 微软生产中的过采样技术

在微软的一个实际应用[3]中,机器学习被用于预测现场事件LSIs),以实现事件的早期检测和升级,这对于工程团队来说至关重要。每天都会产生大量的事件,其中大部分最初是低严重性问题。由于资源有限,工程团队调查所有事件是不切实际的,这可能导致在事件对客户产生重大影响之前,缓解关键问题的潜在延迟。

为了解决这个问题,微软采用了机器学习来预测哪些 LSIs 可能会升级为严重问题,目标是进行主动识别和早期解决。挑战在于训练集中的数据不平衡:在约 40,000 个事件中,不到 2%升级为高严重性。微软使用了两种不同的过采样技术——袋分类(在第第四章集成方法)和 SMOTE,这些技术在提高模型性能方面最为有效。他们使用了两步流程来平衡类别:首先,使用SMOTE进行过采样,然后使用RandomUnderSampler(在第第三章欠采样方法)进行欠采样。该流程自动选择了两步的最优采样比率,并且当与欠采样结合时,SMOTE 表现更佳。所得到的端到端自动化模型被设计为通用型,使其适用于微软内部或外部的不同团队,前提是可用历史事件进行学习。LSI 洞察工具使用了这个模型,并被各个工程团队采用。

接下来,我们将探讨使用 SMOTE 的局限性。

SMOTE 的问题

SMOTE 有其陷阱——例如,它可能会向已经嘈杂的数据集中添加噪声。它也可能导致以下类重叠问题:

  • SMOTE 在生成少数类样本时没有考虑多数类的分布,这可能会增加类别之间的重叠。在图 2.7 中,我们绘制了应用 SMOTE 前后的二分类不平衡数据集。我们可以看到应用 SMOTE 后两个类别之间有很多重叠:

图片

图 2.7 – 应用 SMOTE 前(左)和后(右)的二分类数据集(右图中两个类别的重叠部分)

  • 另一种情况可能是你拥有大量数据,运行 SMOTE 可能会增加你管道的运行时间。

问题 1 可以通过使用 SMOTE 变体 Borderline-SMOTE(将在下一节中讨论)来解决。

在本节中,我们学习了 SMOTE,它使用最近邻技术来生成少数类的合成样本。有时,SMOTE 可能比随机过采样表现得更好,因为它利用了与其他少数类样本的邻近性来生成新的样本。

SMOTE 变体

现在,让我们看看一些 SMOTE 的变体,例如 Borderline-SMOTE、SMOTE-NC 和 SMOTEN。这些变体将 SMOTE 算法应用于特定类型的样本,并且不一定总是适用。

Borderline-SMOTE

Borderline-SMOTE [4]是 SMOTE 的一种变体,它从靠近分类边界的少数类样本中生成合成样本,该边界将多数类与少数类分开。

为什么要考虑分类边界的样本?

理念是靠近分类边界的例子比远离决策边界的例子更容易误分类。在边界附近产生更多的这种少数样本将有助于模型更好地学习少数类。直观上,远离分类边界的点可能不会使模型成为一个更好的分类器。

这里是 Borderline-SMOTE 的逐步算法:

  1. 我们在整个数据集上运行 KNN 算法。

  2. 然后,我们将少数类点分为三类:

    • 噪声点是所有邻居都是多数类的少数类例子。这些点被埋在多数类邻居中。它们可能是异常值,可以安全地被忽略作为“噪声”。

    • 安全点比多数类邻居有更多的少数类邻居。这样的观察结果包含的信息不多,可以安全地忽略。

    • 危险点比少数类邻居有更多的多数类邻居。这表明这样的观察结果位于或接近两类之间的边界。

  3. 然后,我们仅在少数类例子上训练 KNN 模型。

  4. 最后,我们将 SMOTE 算法应用于危险点。请注意,这些危险点的邻居可能被标记为危险,也可能不是。

图 2.8所示,Borderline-SMOTE 在生成合成数据时专注于危险类点:

图 2.8 – Borderline-SMOTE 算法仅使用危险点来生成合成样本。危险点比少数类邻居有更多的多数类邻居

图 2.9展示了 Borderline-SMOTE 如何专注于靠近分类边界的少数类样本,该边界将多数类和少数类分开:

图 2.9 – Borderline-SMOTE 的说明

这里,我们可以看到以下情况:

a) 多数类和少数类样本的图

b) 使用靠近分类边界的邻居生成的合成样本

让我们看看如何使用imbalanced-learn库中的 Borderline-SMOTE 来执行数据过采样:

print("Before: ", sorted(Counter(y).items()))
from imblearn.over_sampling import BorderlineSMOTE
X_resampled, y_resampled = BorderlineSMOTE().fit_resample(X, y)
print("After: ", sorted(Counter(y_resampled).items()))

这里是输出:

Before: [(0, 9900), (1, 100)]
After:  [(0, 9900), (1, 9900)]

你能猜出只关注两类决策边界上的数据点的问题吗?

由于这种技术如此侧重于边界上非常少数的点,少数类簇内的点根本就没有被采样:

图 2.10 – 利用危险点(多数类邻居多于少数类)的 Borderline-SMOTE 算法生成合成样本

在本节中,我们学习了边界 SMOTE,它通过关注接近多数和少数类别分类边界的样本来生成合成少数类别样本,这反过来又可能有助于提高模型的判别能力。

🚀 亚马逊生产中的过采样技术

在实际应用中,亚马逊使用机器学习优化产品的包装类型,旨在减少浪费同时确保产品安全[5]。在他们的训练数据集中,包含了数百万种产品和包装组合,亚马逊面临显著的类别不平衡,其中只有 1%的示例代表不适合的产品-包装配对(少数类别)。

为了解决这种不平衡,亚马逊使用了各种过采样技术:

  • 边界 SMOTE 过采样,导致 PR-AUC 提高了 4%-7%,但训练时间增加了 25%-35%。

  • 随机过采样和随机欠采样的混合,其中它们随机过采样少数类,并欠采样多数类。这导致了 PR-AUC 提高了 6%-10%,但训练时间增加了高达 25%。

表现最好的技术是两阶段学习与随机欠采样(在第第七章,*数据级深度学习方法)*中讨论),它将 PR-AUC 提高了 18%-24%,而没有增加训练时间。

他们提到,处理数据集不平衡的技术效果既与领域相关,也与数据集特定。这个现实世界的例子强调了过采样技术在解决类别不平衡问题上的有效性。

接下来,我们将学习另一种过采样技术,称为 ADASYN,它通过对边界附近和其他低密度区域的示例进行过采样,而不会完全忽略不在边界上的数据点。

ADASYN

虽然 SMOTE 不区分少数类别样本的密度分布,自适应合成采样ADASYN)[6]则专注于难以分类的少数类别样本,因为它们位于低密度区域。ADASYN 根据分类观察的难度,使用少数类别的加权分布。这样,从更难样本中生成更多的合成数据:

图 2.11 – ADASYN 工作原理的说明

在这里,我们可以看到以下内容:

  • a) 绘制了多数类和少数类样本

  • b) 根据硬度因子(稍后解释)生成合成样本

虽然 SMOTE 使用少数类中的所有样本进行均匀过采样,但在 ADASYN 中,更难分类的观察结果被更频繁地使用。

这两种技术之间的另一个区别是,与 SMOTE 不同,ADASYN 在训练 KNN 时也使用多数类观测值。然后,它根据有多少多数观测值是其邻居来决定样本的硬度。

ADASYN 的工作原理

ADASYN 遵循一个简单的算法。以下是 ADASYN 的逐步工作原理:

  1. 首先,它在整个数据集上训练一个 KNN。

  2. 对于少数类的每个观测值,我们找到硬度因子。这个因子告诉我们分类该数据点有多困难。硬度因子,用 r 表示,是多数类邻居数与邻居总数的比率。在这里,r = M / K,其中 M 是多数类邻居的数量,K 是最近邻的总数。

  3. 对于每个少数类观测值,我们通过在少数类观测值及其邻居(邻居可以是多数类或少数类)之间画线来生成与硬度因子成比例的合成样本。数据点分类越困难,为其创建的合成样本就越多。

让我们看看如何使用来自imbalanced-learn库的 ADASYN API 进行数据的过采样:

from imblearn.over_sampling import ADASYN
X_resampled, y_resampled = ADASYN().fit_resample(X, y)
print(sorted(Counter(y_resampled).items()))

这里是输出:

[(0, 9900), (1, 9900)]

图 2.12 – ADASYN 优先考虑较难分类的样本,并在 KNN 中包含多数类示例以评估样本硬度

图 2.13 – 总结各种过采样技术的记忆辅助图

在本节中,我们学习了关于 ADASYN 的内容。接下来,让我们看看当我们的数据包含分类特征时,我们该如何处理这些情况。

分类特征和 SMOTE 变体(SMOTE-NC 和 SMOTEN)

如果你的数据包含分类特征呢?分类特征可以取有限或固定数量的可能值,这与计算机科学中的枚举(enums)类似。这些可能是无序的分类特征,例如头发颜色、种族等,或者是有序的分类特征,例如低、中、高:

图 2.14 – 带有示例的分类数据和其类型

  • 对于有序特征,我们可以通过 sklearn 的OrdinalEncoder对其进行编码,它将类别分配给值 0、1、2 等。

  • 对于名义特征,我们之前学到的所有 SMOTE 变体都不会起作用。然而,RandomOverSampler也可以处理名义特征:

    from imblearn.over_sampling import RandomOverSampler
    X_cat_mix = np.array([["abc", 1], ["def", 2],\
        ["ghi", 3]], dtype=object)
    y_cat_mix = np.array([0, 0, 1])
    print('X_cat_mix:', X_cat_mix, '\n y_cat_mix: ', y_cat_mix)
    X_resampled, y_resampled = RandomOverSampler().fit_resample(\
        X_cat_mix, y_cat_mix)
    print('X_resampled:', X_resampled, '\n y_resampled: ',\
        y_resampled)
    

    这里是输出:

    X_cat_mix: [['abc' 1]
     ['def' 2]
     ['ghi' 3]]
     y_cat_mix:  [0 0 1]
    X_resampled: [['abc' 1]
     ['def' 2]
     ['ghi' 3]
     ['ghi' 3]]
     y_resampled:  [0 0 1 1]
    

然而,默认情况下,SMOTE 仅在连续数据上工作,不能直接用于分类数据。为什么? 这是因为 SMOTE 通过生成连接少数类两个不同数据点的线上的随机点(也称为插值)来工作。如果我们的数据是分类的,并且有“是”和“否”这样的值,我们首先需要将这些值转换为数字。即使我们这样做,比如“是”映射到 1,“否”映射到 0,SMOTE 的插值可能会产生一个 0.3 的新点,这并不映射到任何真实类别。

此外,由于RandomOverSampler中的shrinkage参数仅设计用于连续值,因此我们无法在分类数据中使用该参数。

然而,SMOTE 的两个变体可以处理分类特征:

  • 使用imbalanced-learn对数据进行过采样。数据集的第一项是分类的,第二项是连续的:

    from imblearn.over_sampling import SMOTENC
    X_cat_mix = np.array([["small", 1],\
        ["medium", 2],\
        ["large", 3],\
        ["large", 4],\
        ["large", 5]], dtype=object)
    y_cat_mix = np.array([0, 0, 1, 0, 1])
    print('X_cat_mix:', X_cat_mix, '\n y_cat_mix: ', y_cat_mix)
    X_resampled, y_resampled = SMOTENC(
        categorical_features=[0], k_neighbors=1, random_state=1
    ).fit_resample(X_cat_mix, y_cat_mix)
    print('X_resampled:', X_resampled, '\ny_resampled: ', \
        y_resampled)
    

    这里是输出:

    X_cat_mix: [['small' 1]
               ['medium' 2]
               ['large' 3]
               ['large' 4]
               ['large' 5]]
    y_cat_mix: [0 0 1 0 1]
    X_resampled: [['small' 1.0]
                 ['medium' 2.0]
                 ['large' 3.0]
                 ['large' 4.0]
                 ['large' 5.0]
                 ['large' 3.005630378122263]]
    y_resampled:  [0 0 1 0 1 1]
    
  • 用于名义数据的合成少数过采样技术SMOTEN)用于名义分类数据。SMOTEN 对所有特征执行与 SMOTE-NC 类似的多数投票。它将所有特征视为名义分类,新样本的特征值通过选择最近邻中最频繁的类别来决定。用于计算最近邻的距离度量称为值距离度量VDM)。VDM 通过考虑与每个值关联的类别标签分布来计算两个属性值之间的距离。基于这样的想法,如果两个属性值的类别标签分布相似,则这两个属性值更相似。这样,VDM 可以捕捉分类属性及其对应类别标签之间的潜在关系。

    让我们看看使用 SMOTEN 的一些示例代码:

    from imblearn.over_sampling import SMOTEN
    X_original = np.array([["abc"], \
                           ["def"], \
                           ["ghi"], \
                           ["ghi"], \
                           ["ghi"]], dtype=object)
    y_original = np.array([0, 0, 1, 1, 1])
    print('X_original:', X_original, '\ny_original: ', y_original)
    X_resampled, y_resampled = \
        SMOTEN(k_neighbors=1).fit_resample(X_original, y_original)
    print('X_resampled:', X_resampled, '\ny_resampled:', \
        y_resampled)
    

    这里是输出:

    X_original: [['abc']
                 ['def']
                 ['ghi']
                 ['ghi']
                 ['ghi']]
    y_original:  [0 0 1 1 1]
    X_resampled: [['abc']
                  ['def']
                  ['ghi']
                  ['ghi']
                  ['ghi']
                  ['abc']]
    y_resampled:  [0 0 1 1 1 0]
    

表 2.1中,我们可以看到 SMOTE、SMOTEN 和 SMOTENC,以及每种技术的一些示例,以展示它们之间的差异:

SMOTE 类型支持的特性示例数据
SMOTE仅数值特征:[2.3, 4.5, 1.2],标签:0 特征:[3.4, 2.2, 5.1],标签:1
SMOTEN分类(名义或有序)特征:[‘green’, ‘square’],标签:0 特征:[‘red’, ‘circle’],标签:1
SMOTENC数值或分类(名义或有序)特征:[2.3, ‘green’, ‘small’, ‘square’],标签:0 特征:[3.4, ‘red’, ‘large’, ‘circle’],标签:1

表 2.1 – SMOTE 及其一些常见变体与示例数据

总结来说,当我们有分类和连续数据类型的混合时,应使用 SMOTENC,而 SMOTEN 只能用于所有列都是分类的情况。你可能对各种过采样方法在模型性能方面的比较感到好奇。我们将在下一节探讨这个话题。

各种过采样方法的模型性能比较

让我们检查一些流行的模型在不同过采样技术下的表现。我们将使用两个数据集进行比较:一个是合成数据集,另一个是真实世界数据集。我们将使用逻辑回归和随机森林模型评估四种过采样技术以及无采样的性能。

你可以在本书的 GitHub 仓库中找到所有相关代码。在图 2.15图 2.16中,我们可以看到两个数据集上两种模型的平均精确度得分值:

图片

图 2.15 – 在合成数据集上各种过采样技术的性能比较

图片

图 2.16 – 在 thyroid_sick 数据集上各种过采样技术的性能比较

根据这些图表,我们可以得出一些有用的结论:

  • 过采样的有效性:总的来说,使用过采样技术似乎比不使用任何采样(NoSampling)提高了平均精确度得分。

  • 算法敏感性:过采样技术的有效性取决于所使用的机器学习算法。例如,随机森林似乎比逻辑回归从过采样技术中受益更多,尤其是在合成数据上。

  • thyroid_sick数据集但在合成数据中显示了变化。

  • thyroid_sick数据

  • 对于随机森林,Borderline-SMOTE 在合成数据上具有最高的平均精确度得分。

  • thyroid_sick数据.* 没有明显的胜者:没有一种过采样技术在所有条件下都优于其他技术。技术的选择可能取决于所使用的特定算法和数据集。

请注意,这里使用的模型没有使用最佳超参数进行调整。

调整随机森林和逻辑回归模型的超参数可以进一步提高模型性能。

通常情况下,没有一种技术总是比其他技术表现得更好。这里有几个变量在起作用,即“模型”和“数据”。大多数时候,唯一知道的方法是尝试这些技术中的一系列,并找到最适合我们模型和数据的那个。你可能会对如何从众多过采样选项中进行选择感到好奇。

使用各种过采样技术的指南

现在,让我们回顾一下如何导航我们讨论过的各种过采样技术以及这些技术如何彼此不同的一些指南:

  1. 不应用任何采样技术来训练模型。这将是我们具有基线性能的模型。我们应用的任何过采样技术都预期会提高这种性能。

  2. 从随机过采样开始,并添加一些收缩。我们可能需要调整一些收缩的值,看看模型性能是否有所改善。

  3. 当我们具有分类特征时,我们有几种选择:

    1. 首先将所有分类特征转换为数值特征,使用独热编码、标签编码、特征哈希或其他特征转换技术。

    2. (仅适用于名义分类特征)直接在数据上使用 SMOTENC 和 SMOTEN。

  4. 应用各种过采样技术——随机过采样、SMOTE、Borderline-SMOTE 和 ADASYN——并在适用于您问题的指标上衡量模型的性能,例如平均精度得分、ROC-AUC、精确率、召回率、F1 分数等。

  5. 由于过采样改变了训练数据集的分布,而测试集或现实世界并非如此,使用过采样可能会产生潜在的偏差预测。使用过采样后,根据应用重新校准模型的概率得分可能至关重要。模型的重新校准纠正了由于改变类别分布而引入的任何偏差,确保在部署时做出更可靠的决策。同样,调整分类阈值对于准确解释模型至关重要,尤其是在不平衡数据集的情况下。有关重新校准和阈值调整的更多详细信息,请参阅第十章《模型校准》和第五章《成本敏感学习》。

避免过采样的时机

第一章《机器学习中的数据不平衡介绍》中,我们讨论了数据不平衡可能不是问题的场景。在您选择过采样技术之前,应该重新审视这些考虑因素。尽管存在批评,但过采样的适用性应该根据具体情况评估。以下是一些在选择应用过采样技术时需要考虑的额外技术因素:

  • 计算成本:过采样增加了数据集的大小,导致处理时间和硬件资源方面的计算需求更高。

  • 数据质量:如果少数类数据有噪声或许多异常值,过采样可能会引入更多噪声,降低模型可靠性。

  • 分类器限制:在系统约束场景中,例如极低延迟或处理遗留系统时,使用强大的分类器(复杂且更准确的模型)可能不可行。在这种情况下,我们可能只能使用弱分类器。弱分类器更简单、精度较低,但需要较少的计算资源,并且具有更低的运行时延迟。在这种情况下,过采样可能是有益的[7]。对于强大的分类器,过采样可能带来递减的回报,有时优化决策阈值可能是一个更简单、资源消耗更少的替代方案。

在决定是否使用过采样方法来解决不平衡数据集时,考虑以下因素。

表 2.2 总结了各种过采样技术的关键思想、优点和缺点。这可以帮助你更好地评估选择哪种过采样方法:

SMOTEBorderline-SMOTEADASYNSMOTE-NCSMOTEN
核心思想在连接少数类样本最近邻的线上选择随机点。在多数类和少数类之间的边界上选择少数样本。对这样的样本在边界上执行 SMOTE。根据密度分布自动决定要生成的少数类样本数量。在密度分布低的地方生成更多点。它对分类特征进行多数投票。
优点通常减少假阴性。创建的合成样本不是已知数据的简单复制。它关注不同类别的密度分布。它适用于分类数据。
缺点可能会发生重叠的类别,并可能向数据中引入更多噪声。这可能不适合高维数据或多类别分类问题。它不关心少数类样本的分布。它关注类别之间重叠的区域。它可能过分关注异常值,导致模型性能不佳。与 SMOTE 相同。

表 2.2 – 总结本章讨论的各种过采样技术

在本节中,我们讨论了如何应用本章中学习到的各种过采样技术以及使用它们的优缺点。接下来,我们将探讨如何将各种过采样方法扩展到多类别分类问题。

多类别分类中的过采样

在多类别分类问题中,我们有超过两个类别或标签需要预测,因此可能存在多个类别不平衡。这给问题增加了更多复杂性。然而,我们也可以将相同的技巧应用于多类别分类问题。imbalanced-learn 库提供了在几乎所有支持的方法中处理多类别分类的选项。我们可以通过使用 sampling_strategy 参数选择各种采样策略。对于多类别分类,我们可以在 SMOTE API 中的 sampling_strategy 参数传递一些固定的字符串值(称为内置策略)。我们还可以传递一个包含以下内容的字典:

  • 以类别标签作为键

  • 以该类别的样本数量作为值

在使用参数作为字符串时,以下是一些内置的 sampling_strategy 样本策略:

  • minority 策略仅重新采样少数类。

  • not minority 策略重新采样除了少数类以外的所有类别。在多类别不平衡的情况下,这可能是有帮助的,因为我们有超过两个类别,并且多个类别不平衡,但我们不想触及少数类。

  • not majority策略重新采样所有类别,除了多数类别。

  • all策略重新采样所有类别。

  • auto策略与not``majority策略相同。

以下代码展示了使用各种采样策略进行多类分类时 SMOTE 的使用方法。

首先,让我们创建一个包含 100 个样本的数据集,其中三个类别的权重分别为 0.1、0.4 和 0.5:

X, y = make_classification(n_classes=3, class_sep=2, \
    weights=[0.1, 0.4, 0.5], n_clusters_per_class=1, \
    n_samples=100, random_state=10)
print('Original dataset shape %s' % Counter(y))

这里是输出:

Original dataset shape Counter({2: 50, 1: 40, 0: 10})

如预期,我们的数据集包含三个类别,分别为类别 0、1 和 2,其比例为 10:40:50。

现在,让我们应用带有“少数”采样策略的 SMOTE。这将增加最少样本数的类别:

over_sampler = SMOTE(sampling_strategy='minority')
X_res, y_res = over_sampler.fit_resample(X, y)
print('Resampled dataset shape using minority strategy: %s'% \
    Counter(y_res))

这里是输出:

Resampled dataset shape using minority strategy: Counter({0: 50, 2: 50, 1: 40})

由于类别 0 之前样本数最少,因此“少数”采样策略只对类别 0 进行了过采样,使得样本数等于多数类别的样本数。

在下面的代码中,我们使用字典进行过采样。在这里,对于sampling_strategy字典中的每个类别标签(0、1 或 2)作为key,我们都有每个目标类别的期望样本数作为value

print('Original dataset shape %s' % Counter(y))
over_sampler = SMOTE(sampling_strategy={
                             0 : 40,
                             1 : 40,
                             2 : 50})
X_res, y_res = over_sampler.fit_resample(X, y)
print('Resampled dataset shape using dict strategy: %s\n'% \
    Counter(y_res))

这里是输出:

Original dataset shape Counter({2: 50, 1: 40, 0: 10})
Resampled dataset shape using dict strategy:
         Counter({2: 50, 0: 40, 1: 40})

小贴士

请注意,当在sampling_strategy中使用dict时,每个类别的期望样本数应大于或等于原始样本数。否则,fit_resampleAPI 将抛出异常。

在本节中,我们看到了如何将过采样策略扩展到处理具有两个以上类别的数据不平衡情况。大多数情况下,“auto”采样策略就足够好了,并且会平衡所有类别。

摘要

在本章中,我们介绍了处理不平衡数据集的各种过采样技术,并使用 Python 的imbalanced-learn库(也称为imblearn)进行了应用。我们还通过从头实现一些技术来了解这些技术的内部工作原理。虽然过采样可能会潜在地使模型对数据进行过拟合,但它通常比缺点多,具体取决于数据和模型。

我们将它们应用于一些合成和公开可用的数据集,并对其性能和有效性进行了基准测试。我们看到了不同的过采样技术如何可能导致模型性能在各个尺度上变化,因此尝试几种不同的过采样技术以决定最适合我们数据的方法变得至关重要。

如果你被发现与深度学习模型相关的过采样方法所吸引,我们邀请你查看第七章数据级深度学习方法,我们将讨论深度学习领域内的数据级技术。

在下一章中,我们将介绍各种欠采样技术。

练习

  1. 探索imbalanced-learn库中的 SMOTE 的两个变体,即 KMeans-SMOTE 和 SVM-SMOTE,本章未讨论。使用逻辑回归和随机森林模型比较它们与 vanilla SMOTE、Borderline-SMOTE 和 ADASYN 的性能。

  2. 对于一个有两个类别的分类问题,假设少数类与多数类的比例是 1:20。我们应该如何平衡这个数据集?我们应该在测试或评估时间应用平衡技术吗?请提供你的答案的理由。

  3. 假设我们正在尝试构建一个模型,用于估计一个人是否能获得银行贷款。在我们拥有的 5,000 个观测值中,只有 500 人的贷款获得了批准。为了平衡数据集,我们将批准的人的数据进行复制,然后将其分为训练集、测试集和验证集。使用这种方法是否存在任何问题?

  4. 数据归一化有助于处理数据不平衡。这是真的吗?为什么或为什么不?

  5. 在这里探索imbalanced-learn库中可用的各种过采样 API:imbalanced-learn.org/stable/references/over_sampling.html。请注意每个 API 的各种参数。

参考文献

  1. 《Grab 的图像中保护个人数据》(2021 年),engineering.grab.com/protecting-personal-data-in-grabs-imagery

  2. N. V. Chawla,K. W. Bowyer,L. O. Hall,和 W. P. Kegelmeyer,SMOTE:合成少数类过采样技术,jair,第 16 卷,第 321-357 页,2002 年 6 月,doi: 10.1613/jair.953。

  3. 《实时网站事件升级预测》(2023 年),medium.com/data-science-at-microsoft/live-site-incident-escalation-forecast-566763a2178

  4. H. Han,W.-Y. Wang,和 B.-H. Mao,Borderline-SMOTE:不平衡数据集学习中的新过采样方法,在《智能计算进展》中,D.-S. Huang,X.-P. Zhang,和 G.-B. Huang,编,在《计算机科学讲座笔记》第 3644 卷。柏林,海德堡:Springer Berlin Heidelberg,2005 年,第 878-887 页。doi: 10.1007/11538059_91。

  5. P. Meiyappan 和 M. Bales,立场文件:使用多模态深度学习减少亚马逊的包装浪费,(2021 年),文章:www.amazon.science/latest-news/deep-learning-machine-learning-computer-vision-applications-reducing-amazon-package-waste,论文:www.amazon.science/publications/position-paper-reducing-amazons-packaging-wasteusing-multimodal-deep-learning

  6. Haibo He, Yang Bai, E. A. Garcia 和 Shutao Li,ADASYN:用于不平衡学习的自适应合成采样方法,载于 2008 年 IEEE 国际神经网络联合会议(IEEE 计算智能世界大会),中国香港:IEEE,2008 年 6 月,第 1322–1328 页。doi: 10.1109/IJCNN.2008.4633969。

  7. Y. Elor 和 H. Averbuch-Elor, SMOTE 是否必要?,arXiv,2022 年 5 月 11 日。访问时间:2023 年 2 月 19 日。[在线]。可在arxiv.org/abs/2201.08528获取。

第三章:欠采样方法

有时候,你拥有如此多的数据,通过过采样添加更多数据只会使事情变得更糟。别担心,我们也有针对这些情况的策略。这被称为欠采样或降采样。在本章中,你将了解欠采样的概念,包括何时使用它以及执行它的各种技术。你还将看到如何通过imbalanced-learn库的 API 使用这些技术,并将它们的性能与一些经典的机器学习模型进行比较。

本章将涵盖以下主题:

  • 介绍欠采样

  • 在多数类中何时避免欠采样

  • 均匀地移除示例

  • 移除噪声观察的策略

  • 移除简单观察的策略

到本章结束时,你将掌握各种用于不平衡数据集的欠采样技术,并能够自信地使用imbalanced-learn库来构建更好的机器学习模型。

技术要求

本章将使用常见的库,如matplotlibseabornpandasnumpyscikit-learnimbalanced-learn。本章的代码和笔记本可以在 GitHub 上找到,网址为github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data/tree/master/chapter03。要运行笔记本,有两种选择:你可以点击章节笔记本顶部的在 Colab 中打开图标,或者你可以直接从colab.research.google.com使用笔记本的 GitHub URL 启动它。

介绍欠采样

两个家庭,同样有尊严,

在公平的维罗纳,我们设景之处,

从古老的仇恨到新的叛乱,

在这里,市民的血使市民的手变得不洁。

——莎士比亚《罗密欧与朱丽叶》的开篇诗句

让我们看看一个受莎士比亚戏剧《罗密欧与朱丽叶》启发的场景。想象一个有两个敌对社区(即,蒙塔古家族和凯普莱特家族)的城镇。他们世世代代都是敌人。蒙塔古家族在城镇中属于少数派,而凯普莱特家族是多数派。蒙塔古家族非常富有和强大。凯普莱特家族并不那么富裕。这给城镇带来了复杂的情况。由于这种竞争,城镇里经常发生暴乱。有一天,蒙塔古家族赢得了国王的青睐,并密谋消灭一些凯普莱特家族成员以减少他们的数量。想法是,如果城镇中的凯普莱特家族成员减少,蒙塔古家族将不再是少数派。国王同意了这个计划,因为他希望在执行后实现和平。我们将在这个章节中使用这个故事来说明各种欠采样算法。

有时候,仅仅对少数类别进行过采样是不够的。过采样可能会导致过拟合和更长的训练时间。为了解决这些问题,并从不同的角度处理类别不平衡问题,人们想到了过采样的对立面——即欠采样。在文献中,这也常被称为降采样负降采样,以表示负类别(即多数类别)正在被欠采样。

欠采样技术减少了多数类别中的样本数量。这种方法与过采样相比有两个明显的优势:

  • 数据规模得到控制:即使数据不平衡不是问题,处理从太字节到拍字节不等的大量数据集通常需要数据降维以进行实际训练。数据量本身就可以使训练在时间和计算成本上变得不切实际。云服务提供商如亚马逊网络服务、微软 Azure 和谷歌云除了存储费用外,还会对计算单元收费,使得大规模训练变得昂贵。鉴于你很可能只使用可用训练数据的一小部分,因此关于保留哪些数据以及丢弃哪些数据要有战略性地考虑。欠采样不仅是一种平衡类别的手段,而且是一种有效的成本效益策略,可以将训练时间从几天减少到几小时。

  • 过拟合的可能性更小:通过使用欠采样技术,可以减少多数类别的实例数量,使模型更多地关注少数类别的实例。这反过来又提高了模型在两个类别之间泛化的能力。因此,模型不太可能对多数类别过拟合,并且更好地准备处理新的、未见过的数据,从而降低过拟合的可能性。我们将在本章中讨论各种欠采样方法。

图 3.1以图形方式展示了欠采样的基本思想。

图片

图 3.1 – 欠采样的基本思想展示:(a)具有两个类别的失衡数据,(b)欠采样后的数据

在*图 3.1(a)中,我们展示了包含来自圆形类别的许多数据点的原始数据。在图 3.1(b)*中,我们展示了从圆形类别中移除一些数据点后的重采样数据。

当何时避免对多数类别进行欠采样

欠采样并非万能良药,并不总是有效。它取决于所考虑的数据集和模型:

  • 所有类别的训练数据都太少:如果数据集本身已经很小,对多数类别进行欠采样可能会导致信息损失很大。在这种情况下,建议尝试收集更多数据或探索其他技术,例如对少数类别进行过采样以平衡类别分布。

  • 多数类同等重要或比少数类更重要:在特定场景下,例如在第一章“机器学习中的数据不平衡介绍”中提到的垃圾邮件过滤示例,保持识别多数类实例的高准确性至关重要。在这种情况下,对多数类进行下采样可能会降低模型准确分类多数类实例的能力,导致更高的误报率。相反,可以考虑其他方法,如成本敏感学习或调整决策阈值(这两种方法都在第五章“成本敏感学习”中讨论过)。

  • 当下采样损害模型性能或导致模型过拟合时:对多数类进行下采样可能会降低整体模型性能,因为它丢弃了可能有价值的信息。一些下采样方法会丢弃决策边界附近的示例,这也会改变决策边界。此外,通过减少多数类的大小,下采样可能导致欠拟合,即模型变得过于简单,无法捕捉有限训练数据中的潜在趋势,在新未见过的数据上表现不佳。在使用下采样技术时,如果模型记住了减少后的数据集,也存在过拟合的风险。在这种情况下,探索其他技术,如集成方法(第四章“集成方法”中讨论过)、结合过采样和下采样的混合方法(在本章末尾讨论),或使用不太容易过拟合的不同算法可能更好。

🚀 Meta、Microsoft 和 Uber 在生产中的下采样技术

在广告点击预测等任务中,主要挑战是处理大规模且不平衡的数据集。例如,Facebook 单日广告可能包含数亿个实例,平均点击率(CTR)仅为 0.1%。为了解决这个问题,Meta 在论文《从预测 Facebook 广告点击中汲取的实用经验》[1]中详细介绍了两种专门的技巧。第一种是均匀下采样,它均匀地减少了训练数据量,并表明使用仅 10%的数据只会导致模型性能降低 1%。第二种是负样本下采样,它专门针对负样本(“无点击”)示例,并使用最优的下采样率为 0.025。

类似地,微软和优步在应对这些挑战方面有非常相似的方法。为了估计必应搜索中赞助广告的点击率[2],微软对非点击案例使用 50%的负样本下采样率,有效地将训练时间减半,同时保持相似的性能指标。优步外卖也采用负样本下采样来减少训练数据,以便训练预测是否向客户发送有关新餐厅推送通知的模型[3]。此外,他们在构建模型最终版本时移除了最不重要的特征。

让我们看看分类下采样方法的一种方式。

固定方法与清洗方法

根据从多数类中移除数据点的方式,下采样方法可以分为两类:固定方法和清洗方法。

固定方法中,多数类的示例数量减少到固定的数量。通常,我们会将多数类的样本数量减少到少数类的规模。例如,如果多数类有 1 亿个样本,少数类有 1000 万个样本,应用固定方法后,你将只剩下 1000 万个两个类的样本。这类方法包括随机下采样和基于实例硬度的下采样。

清洗方法中,基于某些预先确定的准则减少多数类的样本数量,与示例的绝对数量无关。一旦满足这个准则,算法就不关心多数类或少数类的规模。

表 3.1 以表格形式总结了两种方法之间的关键差异:

固定 下采样方法清洗 下采样方法
关键思想选择特定数量的多数类实例进行移除识别并移除噪声、冗余或误分类的多数类实例,旨在改善类之间的决策边界
实例之间的关系不考虑实例之间的关系评估实例之间的关系
性能和实现难度实现更快且更容易有时,可能比固定下采样方法有更好的模型性能和泛化能力
示例随机下采样基于实例硬度的下采样托梅克链接邻域清洗规则

表 3.1 – 固定方法与清洗方法下采样

让我们使用sklearnmake_classification API 创建一个不平衡的数据集。我们将在本章中应用各种下采样技术来平衡这个数据集:

from collections import Counter
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=10000, n_features=2,
    n_redundant=0, n_classes=2, flip_y=0,
    n_clusters_per_class=2, class_sep=0.79,
    weights=[0.99], random_state=81)

图 3.2 展示了数据集在二维图上的样子。完整的笔记本代码请参考本章的 GitHub 仓库。

图 3.2 – 绘制不平衡率为 1:99 的数据集

模型校准和阈值调整

在应用欠采样技术后,你可能需要重新校准模型的概率分数。为什么?因为欠采样改变了原始类别的分布,模型的置信度估计存在偏差[4],可能不再准确反映现实场景中每个类别的真实可能性。未能重新校准可能导致模型部署时做出误导性或次优的决策。因此,重新校准模型的概率分数确保模型不仅能够正确分类实例,而且以与实际类别分布一致的方式估计概率,从而提高其可靠性。对于更深入理解此过程,特别是如何校准模型分数以考虑下采样的影响,请参阅第十章模型校准

在不平衡数据集的背景下,阈值调整技术可以是欠采样方法的临界补充。无论我们最终是否应用任何采样技术,调整阈值以确定正确的类别标签对于正确解释模型性能至关重要。对于更深入理解各种阈值调整技术,你可以参阅第五章成本敏感学习

欠采样方法

让我们看看第二种对欠采样算法进行分类的方法。国王可以通过几种方式消除一些凯普莱特家族成员:

  • 他可以从整个城镇均匀地消除凯普莱特家族,从而从城镇的所有地区移除一些凯普莱特家族成员

  • 或者,国王可以移除住在蒙太古家族房屋附近的凯普莱特家族

  • 最后,他可以移除住在远离蒙太古家族房屋的凯普莱特家族成员

这些是在欠采样技术中使用的三种主要方法。我们要么均匀地移除多数样本,要么移除靠近少数样本的多数样本,要么移除远离少数样本的多数样本。我们也可以通过移除一些靠近的和一些远离的样本来结合后两种方法。以下图表给出了这些方法的分类:

图片

图 3.3 – 欠采样技术分类

以下图表说明了两种标准之间的差异。在*图 3.4(a)中,我们展示了原始数据集。在图 3.4(b)*中,我们展示了移除靠近决策边界例子后的相同数据集。注意靠近类别边界的例子是如何被移除的。

大多数类别的例子远离少数类别可能无法有效地帮助模型建立决策边界。因此,这样的远离决策边界的多数类别例子可以被移除。在*图 3.4(c)*中,我们展示了移除远离边界例子后的数据集。远离决策边界的例子可以被认为是易于分类的例子。

图 3.4 – 两种下采样一般方法的差异

在讨论了各种分类下采样技术的方法之后,我们现在更详细地看看它们。

均匀移除示例

从数据中均匀移除多数类示例主要有两种方法。第一种是随机移除示例,另一种则涉及使用聚类技术。让我们详细讨论这两种方法。

随机下采样

国王可能首先想到的技术是随机挑选凯普莱特并从城镇中移除他们。这是一个简单的方法。它可能有效,国王可能能够带来城镇的和平。但是,国王可能会通过挑选一些有影响力的凯普莱特造成不可预见的损害。然而,这是一个很好的开始讨论的地方。这种技术可以被认为与随机过采样是近亲。在随机下采样RUS)中,正如其名所示,我们随机从多数类中提取观察值,直到类别平衡。这种技术不可避免地导致数据损失,可能会损害数据的潜在结构,因此有时表现不佳。

图 3.5 – 解释 RUS 方法主要思想的漫画

下面的代码示例展示了如何使用 RUS:

from imblearn.under_sampling import RandomUnderSampler
rus = RandomUnderSampler(sampling_strategy=1.0, random_state=42)
X_res, y_res = rus.fit_resample(X, y)

可以使用sampling_strategy值来指定少数类和多数类所需的比率,默认情况下,它们将被设置为数量相等。图 3.6显示了RandomUnderSampler技术的应用,其中右侧的图显示大多数负类样本被丢弃:

图 3.6 – 使用 RandomUnderSampler 在降采样前后绘制数据集

接下来,我们转向一种更智能的技术,它会在多数类示例之间形成小组。

ClusterCentroids

国王可能采用的第二种技术来实现均匀下采样,是将凯普莱特人口根据地理位置分成小组。然后,每组保留一个凯普莱特,并从该组中移除其他凯普莱特。这种下采样方法被称为ClusterCentroids方法。如果少数类中有N个项目,我们就从多数类的点中创建N个簇。例如,这可以使用 K-means 算法完成。K-means 是一种聚类算法,它将附近的点分组到不同的簇中,并为每个组分配质心。

图 3.7 – 描述 ClusterCentroids 方法主要思想的漫画

在 ClusterCentroids 技术中,我们首先将 K-means 算法应用于所有多数类数据。然后,对于每个簇,我们保留质心并移除该簇内的所有其他示例。值得注意的是,质心甚至可能不是原始数据的一部分,这是该方法的一个重要方面。

图 3.8中,我们展示了 ClusterCentroids 的工作原理。在*图 3.8(a)中,我们从一个不平衡的数据集开始。在图 3.8(b)中,我们计算了三个聚类的质心。这些质心在图中以星号表示。最后,我们在图 3.8(c)*中从数据集中移除了所有大多数类别的样本,除了质心。

图 3.8 – 展示 ClusterCentroids 方法的工作原理

这里是使用 ClusterCentroids 的代码:

from imblearn.under_sampling import ClusterCentroids
cc = ClusterCentroids(random_state=42)
X_res, y_res = cc.fit_resample(X,y)
print('Resampled dataset shape %s' % Counter(y_res))

以下是输出:

Resampled dataset shape Counter({0: 100, 1: 100})

图 3.9展示了 ClusterCentroids 技术的应用,其中右侧的图表显示大多数负类样本被剔除。

图 3.9 – 使用 ClusterCentroids 在降采样前后绘制数据集

需要注意的一点是,ClusterCentroids 可能计算成本较高,因为它默认使用 K-means 算法,这可能会很慢。我们建议探索 ClusterCentroids 方法中的各种参数,例如 estimator,它指定了要使用的聚类方法。例如,K-means 可以被 MiniBatchKMeans 替换,这是 K-means 聚类算法的一个更快变体。

在下一节中,我们将尝试以更战略性的方式消除大多数类别的示例。

移除噪声观测值的策略

国王可能会在剔除任何人之前查看公民的友谊和位置。国王可能会决定剔除那些富有且住在蒙太古家族附近的凯普莱特家族。这可以通过分离争斗的家族来为城市带来和平。让我们看看一些使用我们的数据来实现这一点的策略。

ENN、RENN 和 AllKNN

国王可以根据邻居来剔除凯普莱特家族。例如,如果一个或多个凯普莱特家族最近三个邻居中的一个是蒙太古家族,国王就可以剔除凯普莱特家族。这种技术称为imbalanced-learn库为我们提供了选择我们想要重采样的类别以及样本邻居应该具有的类别排列方式。

我们可以遵循两种不同的标准来排除样本:

  • 我们可以选择排除一个或多个邻居不属于自身相同类别的样本

  • 我们可以决定排除大多数邻居不属于自身相同类别的样本

图 3.10中,我们展示了 ENN 算法的工作原理。在这里,我们移除了具有一个或多个少数邻居的大多数样本。在*图 3.10(a)中,我们展示了原始数据集。在图 3.10(b)*中,我们突出显示了具有一个或多个少数类最近邻的大多数类样本。突出显示的大多数类样本以实心框表示,它们的邻居通过围绕它们创建曲线来表示。

图 3.10 – 展示 ENN 方法的工作原理

这里是使用 ENN 的代码:

from imblearn.under_sampling import EditedNearestNeighbours
enn = EditedNearestNeighbours(
    sampling_strategy='auto', n_neighbors=200, kind_sel='all')
X_res, y_res = enn.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

以下是输出:

Resampled dataset shape Counter({0: 7852, 1: 100})

图 3.11 – 使用 ENN 进行下采样前后的数据集绘图

在这里,n_neighbors是要考虑的邻域大小,用于计算最近邻。

有两种 ENN 变体我们不会深入探讨,但如果你感兴趣,可以探索它们:imblearn.under_sampling.RepeatedEditedNearestNeighbours)和imblearn.under_sampling.AllKNN)。在 RENN [6]中,我们重复在 ENN 中遵循的过程,直到没有更多可以删除的示例或达到最大循环计数。此算法也移除噪声数据。由于算法重复多次,它在移除边界示例方面更强(图 3*.12*)。

图 3.12 – 使用 RENN 进行下采样前后的数据集绘图

AllKNN方法[6]中,我们重复ENN,但邻居的数量从 1 增加到 K。

Tomek 链接

1976 年,伊万·托梅克提出了Tomek 链接的概念[7]。如果两个例子属于两个不同的类别,并且没有第三个点与它们的距离比两个点之间的距离短,则称这两个例子形成 Tomek 链接。Tomek 链接背后的直觉是“如果两个点来自不同的类别,它们不应该彼此最近。”这些点是噪声的一部分,我们可以消除多数成员或两个点以减少噪声。这就像国王决定移除那些最好的朋友是蒙太古家族的凯普莱特家族成员。

我们可以这样使用TomekLinks API:

from imblearn.under_sampling import TomekLinks
tklinks = TomekLinks(sampling_strategy='auto')
X_res, y_res = tklinks.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

下面的输出是:

Resampled dataset shape Counter({0: 9875, 1: 100})

图 3.13 – 使用 TomekLinks 进行下采样前后的数据集绘图

图 3*.14展示了 Tomek 链接算法的工作原理。在图 3**.14(a)中,我们有原始数据集。在图 3**.14(b)中,我们找到并突出显示 Tomek 链接。注意这些链接中的点彼此很近。在图 3**.14(c)中,我们展示了移除属于 Tomek 链接的多数类样本(以圆圈表示)后的数据集。注意在部分(b)中出现的两个圆圈但在部分(c)中缺失。同样,我们在图例的部分(d)*中展示了移除 Tomek 链接中所有点后的数据集。

图 3.14 – 展示 TomekLinks 算法的工作原理

由于其要求计算所有示例之间的成对距离,Tomek 链接是一种资源密集型方法。正如在*《几种平衡机器学习训练数据行为的研究》*[8]中所述,在处理大量数据时,在减少的数据集上执行此过程将更具有计算效率。

在下一个方法中,我们将尝试从少数类示例的角度尝试移除多数类示例。我们能否移除属于多数类的少数类示例的最近邻?

社区清洁规则

除了移除那些一个或多个最近邻是蒙太古家族的凯普莱特家族成员之外,国王还可能决定查看蒙太古家族成员的最近邻,并移除那些可能成为蒙太古家族成员最近邻的凯普莱特家族成员。在邻域清洗规则NCR)[9]中,我们应用一个 ENN 算法,在剩余数据上训练一个 KNN,然后移除所有作为少数样本最近邻的大多数类样本。

下面是使用NeighourhoodCleaningRule的代码:

from imblearn.under_sampling import NeighbourhoodCleaningRule
ncr = NeighbourhoodCleaningRule(
    sampling_strategy='auto', n_neighbors=200, threshold_cleaning=0.5)
X_res, y_res = ncr.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

下面是输出结果:

Resampled dataset shape Counter({0: 6710, 1: 100})

图片

图 3.15 – 使用 NCR 在欠采样前后绘制数据集

实例硬度阈值

国王可能会问一位大臣,“哪些凯普莱特家族成员与蒙太古家族相处得很好?”大臣根据他们对城镇的了解,会给出一个那些凯普莱特家族成员的名单。然后,国王会移除名单上的凯普莱特家族成员。这种使用另一个模型来识别噪声样本的方法被称为实例硬度阈值。在此方法中,我们在数据上训练一个分类模型,例如决策树、随机森林或线性 SVM。

除了预测实例的类别之外,这些分类器还可以返回它们的类别概率。类别概率显示了模型在分类实例时的置信度。使用实例硬度阈值方法[10],我们移除了那些获得低概率估计的大多数类样本(称为“难以分类的实例”)。这些实例由于类别重叠而被认为是“难以分类的”,而类别重叠是实例硬度的主要原因。

imbalanced-learn库提供了一个用于利用InstanceHardnessThreshold的 API,其中我们可以指定用于估计示例硬度的估计器。在这种情况下,我们使用LogisticRegression作为估计器:

from imblearn.under_sampling import InstanceHardnessThreshold
nm = InstanceHardnessThreshold(
    sampling_strategy='auto', estimator=LogisticRegression())
X_res, y_res = nm.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

下面是输出结果:

Resampled dataset shape Counter({0: 100, 1: 100})

图片

图 3.16 – 使用 InstanceHardnessThreshold 在欠采样前后绘制数据集

由于分类模型需要在多数类和少数类之间绘制决策边界,因此那些离少数类样本太远的大多数类样本可能无法帮助模型决定这个决策边界。考虑到这一点,我们将在下一节中探讨移除此类简单多数类样本的方法。

移除简单观察的策略

移除富有的、著名的凯普莱特家族成员的策略的相反做法是移除贫穷的、弱小的凯普莱特家族成员。本节将讨论移除远离少数样本的多数样本的技术。我们不是从两个类别的边界移除样本,而是使用它们来训练模型。这样,我们可以训练一个模型以更好地区分类别。然而,一个缺点是这些算法可能会保留噪声数据点,这些数据点随后可能被用来训练模型,从而可能将噪声引入预测系统。

压缩最近邻

压缩最近邻CNNeighbors)[11] 是一个按照以下方式工作的算法:

  1. 我们将所有少数样本添加到一个集合中,并添加一个随机选择的多数样本。让我们称这个集合为 C

  2. 我们在集合 C 上使用 k = 1 训练 KNN 模型。

  3. 现在,我们为每个剩余的多数样本重复以下四个步骤:

    1. 我们考虑一个多数样本;让我们称它为 e

    2. 我们尝试使用 KNN 预测 e 的类别。

    3. 如果预测的类别与原始类别匹配,我们则移除该样本。直觉上,从 e 中学习的东西很少,因为即使是 1-NN 分类器也能学会它。

    4. 否则,我们将样本添加到我们的集合 C 中,并在 C 上再次训练 1-NN

此方法从多数类中移除了易于分类的样本。

使用 CondensedNearestNeighbour 的代码如下:

from imblearn.under_sampling import CondensedNearestNeighbour
cnn = CondensedNearestNeighbour(random_state=42)
X_res, y_res = cnn.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

下面是输出:

Resampled dataset shape Counter({0: 198, 1: 100})

图 3.17 – 使用 CNNeighbors 在欠采样前后绘制数据集

然而,CNNeighbors 方法可能计算成本较高,因为它使用 KNN 算法评估每个多数类示例。这使得 CNNeighbors 方法不适合大数据应用。

单侧选择

国王可能会决定移除一些富有的许多贫穷的凯普莱特。这样,只有中产阶级的凯普莱特会留在城镇里。在单侧选择 [12] 中,我们就是这样做的。这种方法是 CNNeighbors 和 Tomek 链接的组合。我们首先使用 CNNeighbors 进行重采样。然后,我们从重采样数据中移除 Tomek 链接。这减少了噪声和易于识别的样本。

下面是 OneSidedSelection 的代码。当我们不提供 n_neighbors 参数时,默认值 1 被采用:

from imblearn.under_sampling import OneSidedSelection
oss = OneSidedSelection(random_state=0, n_seeds_S=10)
X_res, y_res = oss.fit_resample(X, y)
print('Resampled dataset shape %s' % Counter(y_res))

下面是输出:

Resampled dataset shape Counter({0: 4276, 1: 100})

图 3.18 – 使用 OneSidedSelection 在欠采样前后绘制数据集

在这里,n_seeds_S 是在方法中使用作为种子的少数类样本数量,它可以显著影响方法的表现。建议调整此参数。

结合欠采样和过采样

你可能会想知道我们是否可以将欠采样技术与过采样技术结合起来以产生更好的结果。答案是肯定的。过采样方法增加了少数类样本的数量,但通常也会增加数据中的噪声。一些欠采样技术可以帮助我们去除噪声,例如,ENN、Tomek 链接、NCR 和实例硬度。我们可以将这些方法与 SMOTE 结合起来以产生良好的结果。SMOTE 与 ENN [13] 和 Tomek 链接 [14] 的组合已经得到了充分的研究。此外,imbalanced-learn 库支持这两个方法:SMOTEENNSMOTETomek

模型性能比较

让我们探讨一些流行的模型在使用我们讨论的各种采样不足技术时的表现。我们为此比较使用了两个数据集:一个是合成数据集,另一个是从imbalanced-learn库中获取的名为thyroid_sick的现实世界数据集。我们将使用逻辑回归和随机森林模型评估 11 种不同的采样不足技术相对于无采样基线的性能。图 3.193.22显示了使用这些各种方法训练的模型的平均精度值。

你可以在本章的 GitHub 仓库中找到该笔记本。

图 3.19 – 使用随机森林在 thyroid_sick 数据集上使用各种方法时的平均精度

图 3.20 – 使用随机森林在合成数据上使用各种方法时的平均精度

图 3.21 – 使用逻辑回归在 thyroid_sick 数据集上使用各种方法时的平均精度

图 3.22 – 使用逻辑回归在合成数据上使用各种方法时的平均精度

这里有一些更多的观察:

  • 采样不足技术的有效性可能会因数据集及其特征而显著不同

  • 没有任何一种技术在所有数据集上都占主导地位,这强调了进行实证测试以选择最适合你特定问题的最佳方法的需求。

那么,哪种方法最适合你的数据?这个问题没有简单的答案。关键在于对这些方法的内部工作原理有一个直觉,并拥有一个可以帮助你测试不同技术的流程。

然而,某些技术可能会很耗时。在我们的测试中,在一个包含一百万个示例的数据集上,CondensedNearestNeighborClusterCentroidsALLKNN等方法比其他方法花费的时间更长。如果你处理大量数据,计划将来进行扩展,或者时间紧迫,你可能想要避免这些方法或调整它们的参数。RandomUnderSamplerInstanceHardnessThreshold等技术更适合快速迭代开发。

这就带我们结束了这一章。

摘要

在本章中,我们讨论了采样不足,这是一种通过减少多数类样本数量来解决数据集类别不平衡的方法。我们回顾了采样不足的优点,例如控制数据大小和减少过拟合的机会。采样不足方法可以分为固定方法,即将多数类样本数量减少到固定大小,和清洗方法,即根据预定的标准减少多数类样本。

我们讨论了各种欠采样技术,包括随机欠采样、基于实例硬度的欠采样、ClusterCentroids、ENN、Tomek links、NCR、实例硬度、CNNeighbors、单侧选择,以及欠采样和过采样技术的组合,如SMOTEENNSMOTETomek

我们通过在几个数据集上对逻辑回归和随机森林模型进行各种欠采样技术的性能比较,以及基准测试它们的性能和有效性,得出了结论。

一旦你确定你的数据集是不平衡的,并且可能从应用欠采样技术中受益,就可以尝试本章中讨论的各种方法。使用适当的指标,如 PR-AUC,来评估它们的有效性,以找到最适合提高模型性能的方法。

在下一章中,我们将讨论各种基于集成的方法。

练习

  1. 探索imbalanced-learn库提供的各种欠采样 API,请参阅imbalanced-learn.org/stable/references/under_sampling.html

  2. 探索通过imblearn.under_sampling.NearMiss API 提供的NearMiss欠采样技术。它属于哪一类方法?将NearMiss方法应用于本章中使用的那个数据集。

  3. 在 UCI 的us_crime数据集上尝试本章中讨论的所有欠采样方法。你可以在imbalanced-learn库的fetch_datasets API 中找到这个数据集。为LogisticRegressionXGBoost模型找到具有最高f1-score指标的欠采样方法。

  4. 你能识别出你自己的欠采样方法吗?(提示:考虑以新的方式结合各种欠采样方法。)

参考文献

  1. X. He 等人,"从 Facebook 广告点击预测中获得的实际经验",载于第八届国际在线广告数据挖掘研讨会论文集,纽约纽约,美国:ACM,2014 年 8 月,第 1–9 页。doi: 10.1145/2648584.2648589。

  2. X. Ling, W. Deng, C. Gu, H. Zhou, C. Li, 和 F. Sun,"在 Bing 搜索广告中的点击预测模型集成",载于第 26 届国际万维网大会(WWW '17)配套会议论文集,澳大利亚珀斯:ACM 出版社,2017 年,第 689–698 页。doi: 10.1145/3041021.3054192。

  3. 如何使用机器学习和线性规划优化 Uber 推送通知的时间www.uber.com/blog/how-uber-optimizes-push-notifications-using-ml/.

  4. A. D. Pozzolo, O. Caelen, R. A. Johnson, 和 G. Bontempi,"使用欠采样对不平衡分类进行概率校准",载于 2015 年 IEEE 计算智能系列研讨会,南非开普敦:IEEE,2015 年 12 月,第 159–166 页。doi: 10.1109/SSCI.2015.33。

  5. (介绍 ENN 方法) D. L. Wilson, “使用编辑数据的最近邻规则渐近性质,” IEEE Trans. Syst., Man, Cybern., vol. SMC-2, no. 3, pp. 408–421, Jul. 1972, doi: 10.1109/TSMC.1972.4309137.

  6. (介绍 RENN 和 AllKNN 方法) “编辑最近邻规则的实验,” IEEE Trans. Syst., Man, Cybern., vol. SMC-6, no. 6, pp. 448–452, Jun. 1976, doi: 10.1109/TSMC.1976.4309523.

  7. I. Tomek, “CNN 的两种改进,” IEEE Trans. Syst., Man, Cybern., vol. SMC-6, no. 11, pp. 769–772, Nov. 1976, doi: 10.1109/TSMC.1976.4309452.

  8. G. E. A. P. A. Batista, R. C. Prati, 和 M. C. Monard, “研究平衡机器学习训练数据几种方法的性能,” SIGKDD Explor. Newsl., vol. 6, no. 1, pp. 20–29, Jun. 2004, doi: 10.1145/1007730.1007735.

  9. (介绍邻域清洗规则方法) J. Laurikkala, “通过平衡类分布改进困难小类的识别,” 在 Artificial Intelligence in Medicine, S. Quaglini, P. Barahona, 和 S. Andreassen, Eds.,in Lecture Notes in Computer Science, vol. 2101. Berlin, Heidelberg: Springer Berlin Heidelberg, 2001, pp. 63–66. doi: 10.1007/3-540-48229-6_9.

  10. (介绍实例硬度阈值技术) M. R. Smith, T. Martinez, 和 C. Giraud-Carrier, “数据复杂性的实例级分析,” Mach Learn, vol. 95, no. 2, pp. 225–256, May 2014, doi: 10.1007/s10994-013-5422-z.

  11. P. Hart, “压缩最近邻规则 (corresp.),” IEEE transactions on information theory, vol. 14, no. 3, pp. 515–516, 1968, citeseerx.ist.psu.edu/document?
repid=rep1&type=pdf&doi=7c3771fd6829630cf450af853 df728ecd8da4ab2.

  12. (介绍单侧选择方法) M. Kubat 和 S. Matwin, “解决不平衡训练集的诅咒:单侧选择”。

  13. (应用 SMOTEENN 和 SMOTETomek 方法) Gustavo EAPA Batista, Ronaldo C Prati, 和 Maria Carolina Monard. 研究平衡机器学习训练数据几种方法的性能。ACM SIGKDD explorations newsletter, 6(1):20–29, 2004.

  14. (应用 SMOTETomek 方法) Gustavo EAPA Batista, Ana LC Bazzan, 和 Maria Carolina Monard. 平衡训练数据以实现关键词自动标注:一个案例研究。在 WOB, 10–18. 2003.

第四章:集成方法

想象一下一家大型公司的顶级高管。他们不会独自做出决策。在一天中,他们需要做出许多关键决策。他们是如何做出这些选择的?不是独自一人,而是通过咨询他们的顾问。

假设一位高管咨询了来自不同部门的五位不同顾问,每位顾问根据他们的专业知识、技能和领域知识提出略微不同的解决方案。为了做出最有效的决策,高管结合了五位顾问的见解和意见,创建了一个混合解决方案,其中包含了每个提案的最佳部分。这个场景说明了集成方法的概念,其中多个弱分类器被组合起来创建一个更强、更准确的分类器。通过结合不同的方法,集成方法通常可以实现比依赖单个分类器更好的性能。

我们可以通过结合多个弱分类器的结果来创建一个强大的模型。这些弱分类器,如简化的决策树、神经网络或支持向量机,其表现略好于随机猜测。相比之下,通过集成这些弱分类器创建的强大模型,其表现显著优于随机猜测。弱分类器可以接受不同来源的信息。构建模型集成的两种通用方法:bagging 和 boosting。

传统集成方法的问题在于它们使用假设数据平衡的分类器。因此,它们可能不适合处理不平衡的数据集。因此,我们将流行的机器学习集成方法与我们之前章节中研究的不平衡数据处理技术相结合。我们将在本章讨论这些组合。

本章将涵盖以下主题:

  • 处理不平衡数据的 Bagging 技术

  • 处理不平衡数据的 Boosting 技术

  • 集成集成

  • 模型性能比较

图 4.1 中,我们将本章将要涵盖的各种集成技术进行了分类:

图片

图 4.1 – 集成技术概述

到本章结束时,你将了解如何将诸如 bagging 和 boosting 之类的集成模型应用于数据集中类不平衡的问题。

技术要求

本章的 Python 笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data/tree/master/chapter04。像往常一样,你可以通过点击本章笔记本顶部的在 Colab 中打开图标或通过使用笔记本的 GitHub URL 在colab.research.google.com启动它来打开 GitHub 笔记本。

在本章中,我们将继续使用使用make_classification API 生成的合成数据集,就像我们在前几章中所做的那样。在本章的末尾,我们将对我们在本章中学到的方法在几个真实数据集上进行测试。我们的完整数据集包含 90,000 个示例,不平衡比率为 1:99。以下是训练数据集的外观:

图片

图 4.2 – 不平衡比率为 1:99 的数据集的绘图

我们的失衡数据集已经准备好使用,让我们看看第一种集成方法,称为 Bagging。

处理不平衡数据的 Bagging 技术

想象一个商业高管,他拥有数千份关于重要并购的机密文件。分配给这个案例的分析师没有足够的时间来审查所有文件。每个人都可以从集合中随机选择一些文件并开始审查。稍后,他们可以在会议上结合他们的见解来得出结论。

这种场景是机器学习中一个称为 Bagging 的过程的隐喻[1],它是自助聚合的缩写。在 Bagging 中,就像前一个场景中的分析师一样,我们创建原始数据集的几个子集,在每个子集上训练一个弱学习器,然后聚合它们的预测。

为什么使用弱学习器而不是强学习器?这个理由适用于 Bagging 和 Boosting 方法(本章后面将讨论)。有几个原因:

  • 速度:弱学习器在计算上效率高且成本低。

  • 多样性:弱学习器更有可能犯不同类型的错误,这在结合它们的预测时是有利的。使用强学习器可能导致它们都犯同一种类型的错误,从而导致集成效果较差。

  • 过拟合:作为前一点的推论,错误的多样性有助于降低集成中过拟合的风险。

  • 可解释性:虽然整个集成可能不容易解释,但其个别组件——通常是更简单的模型——更容易理解和解释。

现在,回到 Bagging。算法的第一步称为自助抽样。在这个步骤中,我们通过从主数据中随机选择项目来制作几个子集或较小的数据组。数据的选择有可能会选择同一个项目多次(这个过程称为“有放回的随机抽样”),所以这些较小的组可能有一些共同的项目。然后,我们在这些较小的组上训练我们的分类器。

第二步称为聚合。在预测时,将测试样本传递给每个分类器。之后,我们取平均或多数预测作为真实答案。

图 4.3 所示,数据集首先通过有放回抽样分成三个子集。然后,在每个子集上分别训练单独的分类器。最后,在预测时将分类器的结果合并:

图片

图 4.3 – 展示 bagging 的工作原理

图 4*.4* 以伪代码格式总结了 bagging 算法:

图 4.4 – Bagging 的伪代码

我们将在之前创建的数据集上训练一个来自 sklearn 的 bagging 分类器模型。由于可以向 BaggingClassifier 提供一个基估计器,我们将使用最大树深为 6DecisionTreeClassifier

from sklearn.ensemble import BaggingClassifier
from sklearn.metrics import PrecisionRecallDisplay
clf = BaggingClassifier(\
    estimator=DecisionTreeClassifier(max_depth=6), random_state=0
).fit(X_train, y_train)

让我们绘制决策边界:

plot_decision_boundary(X_train, y_train, clf, 'BaggingClassifier')
plt.show()

您可以参考 GitHub 上相应笔记本中 plot_decision_boundary() 的定义。我们使用 sklearn.inspection 模块中的 DecisionBoundaryDisplay API 来绘制决策边界。

图 4*.5* 展示了在训练数据上的学习决策边界:

图 4.5 – BaggingClassifier 在训练数据上的决策边界

让我们也注意使用此模型在测试集上的基线指标平均精度:

PrecisionRecallDisplay.from_estimator(
    clf, X_test, y_test, ax = plt.gca(),name = "BaggingClassifier")

图 4*.6* 展示了得到的 PR 曲线:

图 4.6 – BaggingClassifier 在测试数据上的精确度-召回率曲线

这里有一些其他指标:

Average Precision Score: 0.969
AUC-ROC Score: 0.999
F2-score: 0.891
Precision: 0.967
Recall: 0.874

在本章中,我们还将考虑 F2 分数(Fbeta 分数,beta=2.0),它按比例结合精确度和召回率,给予召回率更高的权重,而给予精确度较低的权重。

因此,当在不平衡数据集上使用 BaggingClassifier 时,我们可能会遇到什么问题?一个明显的事情可能是,在引导过程中,一些用于训练基分类器的子集可能只有很少的少数类示例,甚至没有。这意味着每个基分类器在少数类上的表现都会很差,而它们的组合表现仍然会很差。

我们可以将下采样技术与 bagging(例如 UnderBagging)或过采样技术与 bagging(例如 OverBagging)结合起来,以获得更好的结果。我们将在下一节讨论这些技术。

UnderBagging

UnderBagging [2] 技术在引导(或子集选择)时使用随机下采样。我们为每个分类器选择少数类示例的整个集合,并用与少数类示例数量相同的多数类示例进行带替换的引导。聚合步骤与 bagging 相同。我们可以选择任何分类器,例如决策树,进行训练。

UnderBagging 有变体,其中可以对少数类进行带替换的重采样,以获得更多样化的集成。

图 4*.7* 中的流程图代表了 UnderBagging 算法在三个数据子集中的主要步骤。它包括创建多个数据子集,对每个子集中的多数类进行随机下采样,在每个子集上训练分类器,最后结合分类器的预测:

图 4.7 – 展示 UnderBagging 算法的工作原理

imbalanced-learn 库为 BalancedBaggingClassifier 提供了实现。默认情况下,此分类器使用决策树作为基分类器,并通过 sampler 参数使用 RandomUnderSampler 作为采样器。图 4.8 展示了训练好的 UnderBagging 模型的决策边界:

图 4.8 – 在训练数据上 UnderBagging 分类器的决策边界

🚀 微软生产环境中的 Bagging 分类器

在微软的一个实际应用中 [3],团队在预测 Live Site Incident 升级(如第二章过采样方法)时面临重大挑战。数据集高度不平衡,使得标准分类器难以表现良好。为了解决这个问题,微软采用了集成方法,特别是来自 imbalanced-learn 库的 BalancedBaggingClassifier。他们使用了 UnderBagging,其中每个引导样本都是随机欠采样的,以获得平衡的类别分布。正如我们刚才讨论的,UnderBagging 使用所有少数类样本和多数类样本的随机选择来训练模型。

在他们的评估中,Bagged 分类提供了最佳结果,并且在经过几个月的跟踪后也证明更加一致。他们能够显著提高对事件升级的预测准确性。

OverBagging

在引导过程中,不是对多数类样本进行随机欠采样,而是对少数类进行过采样(带替换)。这种方法称为 OverBagging [2]。作为一种变体,多数类和少数类的样本都可以进行带替换的重采样,以达到多数类和少数类样本数量相等。

图 4.9 中的流程图展示了 OverBagging 算法在三个数据子集上的主要步骤。它包括创建多个数据子集,对每个子集中的少数类进行随机过采样,在每个子集上训练分类器,并最终结合分类器的预测:

图 4.9 – 展示 OverBagging 算法的工作原理

对于 OverBagging,我们可以在 sampler 参数中使用与 BalancedBaggingClassifier 相同的 RandomOverSampler

我们将看到以下决策边界:

图 4.10 – 在训练数据上 OverBagging 分类器的决策边界

在讨论了各种 Bagging 技术之后,我们将比较这些技术的性能指标。

SMOTEBagging

我们能否在引导过程中使用 SMOTE 代替对少数类样本的随机过采样?答案是肯定的。多数类将进行带替换的引导,而少数类将使用 SMOTE 进行采样,直到达到平衡比率。

SMOTEBagging [2] 的伪代码与 OverBagging 非常相似,关键区别在于使用 SMOTE 算法而不是随机过采样来增强少数类数据。

与过袋装类似,我们可以使用带有 SMOTE 作为 sampler 参数的 BalancedBagging Classifier API 实现 SMOTEBagging

决策边界与过袋装没有很大区别:

图 4.11 – SMOTEBagging 分类器在训练数据上的决策边界

关于随机森林及其与袋装的关系的说明

随机森林 [4] 是另一个基于袋装概念的模型。sklearn 中的 RandomForestClassifierBaggingClassifier 模型之间的区别在于,RandomForestClassifier 在尝试决定决策树中节点分裂的特征时,考虑了特征的一个随机子集,而 BaggingClassifier 则采用所有特征。

表 4.1 突出了随机森林和袋装分类器之间的差异:

RandomForestClassifierBaggingClassifier
基本分类器决策树任何分类器。
自举采样是。
选取特征子集是(在每个节点)否,默认情况下。我们可以使用 max_features 超参数来选取特征子集。
最适合的数据类型任何表格数据,但在大型特征集上表现最佳任何表格数据,但最好在仔细选择基本分类器时使用。
处理缺失值和异常值是,固有取决于基本分类器。

表 4.1 – RandomForestClassifier 与 BaggingClassifier 对比

imbalanced-learn 库提供了 BalancedRandomForestClassifier 类来处理不平衡数据集,其中在训练单个决策树之前,每个自举样本都是欠采样的。作为练习,我们鼓励你了解 BalancedRandomForestClassifier。看看它如何与我们刚才讨论的其他技术相关。还可以尝试各种采样策略,并探索此类提供的参数。

袋装方法的比较性能

让我们使用迄今为止使用的相同数据集比较各种袋装方法的性能。我们将使用决策树作为基线,并在多个性能指标上评估不同的技术。所有技术中每个指标的最高值都以粗体突出显示:

技术F2精确度召回率平均精确度AUC-ROC
SMOTEBagging0.9280.7540.9850.9771.000
过袋装0.8880.6121.0000.9761.000
下袋装0.8750.6090.9810.8850.999
袋装0.8910.9670.8740.9691.000
平衡随机森林0.7560.3870.9930.9090.999
随机森林0.8890.9750.8700.9791.000
决策树0.8930.9600.8780.9300.981

表 4.2 – 各种袋装技术的性能比较

以下是我们可以从表 4.2中得出的结论:

  • 为了最大化 F2 分数,SMOTEBagging表现最佳

  • 对于高精确度,袋装法随机森林表现异常出色

  • 对于高召回率,OverBagging平衡随机森林是强有力的选择

  • 对于所有指标的综合性能,SMOTEBagging袋装法被证明是稳固的选项

总之,尽管如袋装法和随机森林这样的集成方法建立了难以超越的稳健基准,但结合如 SMOTEBagging 这样的不平衡学习策略可以带来显著的收益。

这就结束了我们对袋装技术的讨论。如果袋装法是群众的智慧,那么提升法就是大师级的雕塑家,通过每一笔都精炼着先前的艺术。我们将在下一节尝试理解提升法是如何工作的。

处理不平衡数据的提升技术

想象两个朋友一起做小组学习来解决他们的数学作业。第一个学生在大多数主题上都很强,但在两个主题上较弱:复数和三角形。因此,第一个学生要求第二个学生在这两个主题上花更多的时间。然后,在解决作业的过程中,他们结合了他们的答案。由于第一个学生大多数主题都很熟悉,他们决定在作业问题的答案中给予他的答案更多的权重。这两个学生所做的是提升法的核心思想。

在袋装法中,我们注意到我们可以并行训练所有分类器。这些分类器是在数据的一个子集上训练的,并且在预测时它们都有平等的发言权。

在提升法中,分类器是一个接一个地训练的。虽然每个分类器都从整个数据中学习,但根据数据集中点的分类难度,这些点被分配了不同的权重。分类器也被分配了权重,这些权重告诉我们它们的预测能力。在预测新数据时,使用分类器的加权总和。

提升法首先在全部数据集上训练第一个分类器,每个数据点分配相同的权重。在第二次迭代中,第一次迭代中被误分类的数据点被赋予更多的权重,并使用这些新权重训练第二个分类器。同时,也会根据分类器的整体性能给分类器本身分配权重。这个过程通过多个迭代和不同的分类器继续进行。图 4.12展示了对于双分类数据集的此概念:

图片

图 4.12 – 提升法思想:(左)第一个分类器的决策边界;(中)第二分类器对误分类数据点的权重提升;(右)第二个分类器的决策边界

我们刚才描述的这种提升方法称为AdaBoost。还有一种提升算法的类别称为梯度提升,其主要重点是最小化先前模型的残差(实际值与预测输出值之间的差异),试图纠正先前模型的错误。有几个流行的梯度提升实现,如XGBoostLightGBMCatBoost

在本章中,我们将主要关注 AdaBoost 并将其修改为考虑数据不平衡。然而,将 AdaBoost 与 XGBoost 等替换不应太困难。

AdaBoost

AdaBoost,即自适应提升,是基于决策树的最早提升方法之一。决策树是易于组合在一起的分类器:

图 4.13 – AdaBoost 伪代码

以下代码显示了如何从sklearn库中导入分类器并在数据上训练它:

from sklearn.ensemble import AdaBoostClassifier
clf = AdaBoostClassifier(
    random_state=0, estimator = DecisionTreeClassifier(max_depth=6)
).fit(X_train, y_train)

让我们绘制模型在数据上训练后的决策边界:

plot_decision_boundary(X_train, y_train, clf, 'AdaBoostClassifier')
plt.show()

图 4*.14*显示了模型在训练数据上的决策边界:

图 4.14 – AdaBoostClassifier 在训练数据上的决策边界

我们可以将过采样和欠采样作为提升算法的组成部分,类似于我们对 bagging 算法所做的那样。我们将在下一节讨论这一点。

RUSBoost、SMOTEBoost 和 RAMOBoost

如您所猜测,我们可以将 AdaBoost 与重采样技术相结合。以下是主要思路:在每次提升迭代中,在训练分类器之前,对前一次迭代中的错误示例进行数据采样(通过某些欠采样或过采样变体)。以下是通用伪代码:

  1. 输入:训练数据和一些决策树分类器。

  2. 输出:一个聚合分类器。

  3. 初始化训练数据所有样本的相等权重。

  4. 对每个决策树分类器重复此操作:

    1. 使用数据采样方法重新采样数据:

      1. 如果使用的采样方法是随机欠采样RUS),则该方法称为RUSBoost [5]。

      2. 如果使用的采样方法是 SMOTE,则该方法称为SMOTEBoost [6]。

      3. RAMOBoost(即Boosting 中的排序少数过采样 [7])中,少数类的过采样是基于少数类示例的权重进行的。如果一个示例的权重更高(因为模型在前一次迭代中对该示例的表现不佳),则对其进行更多的过采样,反之亦然。

    2. 在重采样数据上训练分类器,根据先前迭代中样本的权重给予更高的重视。

    3. 通过比较其预测与实际输出,计算给定数据上分类器的错误。

    4. 考虑下一次迭代中所有被错误分类的示例。增加这些错误分类示例的权重。

  5. 将所有决策树分类器组合成一个最终分类器,其中在训练数据上具有较小错误值的分类器在最终预测中具有更大的发言权。

在这个伪代码中,*步骤 4(I)*是我们相对于 AdaBoost 算法所添加的唯一额外步骤。让我们讨论这些技术的优缺点:

  • 在 RUSBoost 中,随着数据的减少,我们倾向于有更快的训练时间。

  • SMOTEBoost 从少数类生成合成样本。因此,它增加了数据的多样性,并可能提高分类器的准确性。然而,它会增加训练时间,并且可能无法扩展到非常大的数据集。

  • RAMOBoost 优先考虑靠近类别边界的样本。这在某些情况下可以提高性能。然而,类似于 SMOTEBoost,这种方法可能会增加训练时间和成本,并可能导致最终模型的过拟合。

imbalanced-learn库提供了RUSBoostClassifier的实现:

from imblearn.ensemble import RUSBoostClassifier
rusboost_clf = RUSBoostClassifier(random_state=0, \
    estimator=DecisionTreeClassifier\
    (max_depth=6)).fit(X_train, y_train)

让我们检查训练模型的决策边界:

plot_decision_boundary(
    X_train, y_train, rusboost_clf, 'RUSBoostClassifier')
plt.show()

结果图如下所示:

图 4.15 – RUSBoostClassifier 在训练数据上的决策边界

imbalanced-learn库尚未实现 RAMOBoost 和 SMOTEBoost(截至版本 0.11.0)。您可以在github.com/dialnd/imbalanced-algorithms的开放源代码存储库中查看参考实现。

我们能否为多数类创建多个子集,从每个子集中训练一个集成,并将这些集成中的所有弱分类器组合成一个最终输出?这种方法将在下一节中探讨,我们将利用集成集成技术。

集成集成

我们能否结合提升和 bagging?如我们之前所看到的,在 bagging 中,我们创建多个数据子集,然后在那些数据集上训练分类器。我们可以将 AdaBoost 视为在 bagging 过程中使用的分类器。过程很简单:首先,我们创建袋子,然后在每个袋子上训练不同的 AdaBoost 分类器。在这里,AdaBoost 本身就是一种集成。因此,这些模型被称为集成 的集成

除了拥有集成集成之外,我们还可以在 bagging 时进行下采样(或过采样)。这使我们能够在单个模型中获得下采样(或过采样)的袋装提升随机下采样(或过采样)的好处。在本节中,我们将讨论一个名为EasyEnsemble的算法,它具有与具有相同数量弱分类器的任何其他算法相似的训练时间。由于随机下采样没有显著的开销,这两个算法的训练时间与其他算法相似。

EasyEnsemble

EasyEnsemble 算法[8]从原始数据集中生成平衡数据集,并在每个平衡数据集上训练不同的 AdaBoost 分类器。随后,它创建一个聚合分类器,该分类器基于 AdaBoost 分类器的多数投票进行预测:

图 4.16 – EasyEnsemble 伪代码

图 4*.17*总结了使用训练数据三个子集的 EasyEnsemble 算法:

图 4.17 – EasyEnsemble 算法解释

我们不仅可以随机下采样多数类示例,还可以随机上采样少数类示例。

imbalanced-learn库提供了使用EasyEnsembleClassifier的 API。EasyEnsembleClassifier API 提供了一个base_estimator参数,可以用来设置任何分类器,默认为AdaBoostClassifier

from imblearn.ensemble import EasyEnsembleClassifier
clf = EasyEnsembleClassifier(n_estimators=70,random_state=42).fit(X,y)

让我们绘制决策边界:

plot_decision_boundary(X, y, clf, 'EasyEnsembleClassifier')
plt.show()

图 4.18 – EasyEnsembleClassifier 在训练数据上的决策边界

默认情况下,EasyEnsemble 使用AdaBoostClassifier作为基线估计器。然而,我们也可以使用任何其他估计器,例如XGBoostClassifier,或者以其他方式调整它,例如通过传递另一个sampling_strategy

这就结束了我们对 EasyEnsemble 的讨论。接下来,我们将比较我们研究过的各种提升方法。

提升方法的比较性能

让我们比较我们讨论过的各种提升方法的性能。我们以决策树作为基线,并使用 RUSBoost、AdaBoost、XGBoost 和 EasyEnsemble,以及两种变体。默认情况下,EasyEnsembleClassifier使用AdaBoostClassifier作为基线估计器。我们在EasyEnsembleClassifier的第二种变体中使用 XGBoost 作为估计器;在第三种变体中,我们使用not majority作为我们的sampling_strategy,并配合 XGBoost 估计器:

技术F2 分数精确度召回率平均精确度AUC-ROC
EasyEnsemble(估计器=XGBoost 和sampling_strategy = not_majority0.8850.9330.8740.9781.000
EasyEnsemble(估计器=XGBoost)0.8440.5201.0000.9780.999
EasyEnsemble0.8440.5191.0000.9400.999
RUSBoost0.8360.5170.9890.9481.000
AdaBoost0.9070.9380.9000.9781.000
XGBoost0.8850.9330.8740.9681.000
决策树0.8930.9600.8780.9300.981

表 4.3 – 各种提升技术的性能比较

下面是从表 4.3中得出的结论:

  • 对于最高的 F2 分数,AdaBoost 是最好的选择。

  • 对于高精确度,纯决策树击败了所有其他技术。

  • 对于完美的召回率,EasyEnsemble(估计器为XGBoost)和 EasyEnsemble 表现完美。

  • 对于整体平衡性能,AdaBoost 和 EasyEnsemble(估计器为XGBoostsampling_strategy=not_majority)是强有力的竞争者。

  • 集成技术,如 RUSBoost 和 EasyEnsemble,专门设计用于处理数据不平衡并提高召回率,与基线模型(如决策树或甚至 AdaBoost)相比。

总体而言,结果表明,尽管像 AdaBoost 和 XGBoost 这样的集成方法提供了难以超越的稳健基准,但利用不平衡学习技术确实可以修改结果分类器的决策边界,这有可能有助于提高召回率。然而,这些技术的有效性在很大程度上取决于所考虑的数据集和性能指标。

通过结束我们通过集成集成之旅,我们为我们的机器学习工具箱添加了另一个强大且动态的工具。

模型性能比较

我们所讨论的技术有效性高度依赖于它们应用到的数据集。在本节中,我们将进行全面的比较分析,比较我们迄今为止所讨论的各种技术,同时以逻辑回归模型作为基准。有关完整实现的全面审查,请参阅 GitHub 上提供的配套笔记本。

分析涵盖了四个不同的数据集,每个数据集都有其独特的特性和挑战:

  • Sep: 0.5 的合成数据:一个类别之间具有适度分离的模拟数据集,作为在简化条件下理解算法性能的基准。

  • Sep: 0.9 的合成数据:另一个合成数据集,但具有更高的分离度,使我们能够检查随着类别区分性的提高,算法的表现如何。

  • 与医疗保健相关的imblearn),因其实际重要性以及医疗数据集中常见的自然类别不平衡而被选中。

  • imblearn也是如此。

我们的主要评估指标是平均精确度,这是一个结合了精确度和召回率的综合度量,从而提供了一个平衡的算法性能视图。

我们想强调,我们正在使用各种集成模型的原始版本进行比较。通过一些额外的努力来调整这些模型的超参数,我们当然可以增强这些实现的性能。我们将这留给你作为练习。

通过在多种数据集上比较这些不同的算法,这项分析旨在以下方面提供一些有价值的见解:

  • 传统技术与专门技术的对比

  • 算法有效性对数据集特征的依赖性

  • 在不同场景下选择一种算法而非另一种算法的实际影响

图 4.19比较了使用平均精确度得分,以逻辑回归模型为基准,在两个合成数据集上各种 bagging 和 boosting 技术的性能:

图片

图 4.19 – 合成数据集上的平均精确度得分

图 4.20显示了两个真实世界数据集上的相似图:

图片

图 4.20 – thyroid_sick 和 abalone_19 数据集上的平均精确度得分

让我们分析每个数据集的结果:

  • Sep 0.5 的合成数据 (图 4.19,左侧):在平均精确度方面,XGBoost 和逻辑回归表现最佳,分别得分为 0.30 和 0.27。有趣的是,专门为不平衡数据设计的集成方法,如 SMOTEBagging 和 OverBagging,表现与常规方法如 Bagging 相当,甚至更差。这表明在更简单的合成设置中,专门的方法并不总是能保证优势。

  • Sep 0.9 的合成数据 (图 4.19,右侧):EasyEnsemble 在这个数据集上以平均精确度 0.64 领先,紧随其后的是逻辑回归和 XGBoost。这种更高的分离似乎使 EasyEnsemble 能够利用其对平衡的关注,从而获得更好的性能。其他集成方法如 UnderBagging 和 OverBagging 表现尚可,但未超越领先者。

  • 甲状腺疾病数据集 (图 4.20,左侧):在一个关注甲状腺疾病的真实世界数据集中,XGBoost 的表现远超其他所有方法,平均精确度为 0.97。其他集成方法如 Bagging、OverBagging 和 SMOTEBagging 也得分很高,表明集成方法特别适用于这个数据集。有趣的是,Boosting 和 RUSBoost 没有跟上步伐,这表明并非所有 Boosting 变体都普遍有效。

  • Abalone 19 数据集 (图 4.20,右侧):对于 Abalone 19 数据集,所有方法的表现相对较差,XGBoost 以平均精确度 0.13 脱颖而出。EasyEnsemble 以 0.09 的得分位居第二,而传统方法如逻辑回归和 Bagging 则落后。这可能表明该数据集对大多数方法来说特别具有挑战性,而专门的不平衡技术只能带来微小的改进。

这里有一些总体见解:

  • 常规方法如 XGBoost 和逻辑回归通常提供强大的基线,难以超越。

  • 专门的不平衡学习技术的有效性可能因数据集及其固有的复杂性而显著不同。

  • 集成方法通常在各种数据集上表现良好,但它们的有效性可能取决于具体情境。

  • 性能指标的选择——在本例中为平均精确度——可以显著影响评估,因此考虑多个指标以获得全面理解至关重要。

我们希望这一章已经展示了您如何将采样技术与集成方法相结合以实现更好的结果,尤其是在处理不平衡数据时。

摘要

机器学习中的集成方法通过结合多个弱分类器的结果来创建强大的分类器,例如使用袋装和提升方法。然而,这些方法假设数据平衡,可能难以处理不平衡数据集。将集成方法与过采样和欠采样等采样方法相结合,导致 UnderBagging、OverBagging 和 SMOTEBagging 等技术,所有这些都可以帮助解决不平衡数据问题。

EasyEnsemble 等集成集成,结合提升和袋装技术,为不平衡数据集创建强大的分类器。

基于集成的不平衡学习技术可以成为您工具箱中的优秀补充。基于 KNN 的,如 SMOTEBoost 和 RAMOBoost 可能较慢。然而,基于随机欠采样和随机过采样的集成成本较低。此外,发现提升方法在处理不平衡数据时有时比袋装方法更有效。我们可以将随机采样技术与提升方法相结合,以获得更好的整体性能。正如我们之前强调的,这是经验性的,我们必须尝试了解什么最适合我们的数据。

在下一章中,我们将学习如何调整模型以考虑数据的不平衡以及模型由于对少数类样本的错误分类而引起的各种成本。

问题

  1. 尝试在abalone_19数据集上使用RUSBoostClassifier,并将性能与其他章节中的技术进行比较。

  2. imbalanced-learn库中的BalancedRandomForestClassifierBalancedBaggingClassifier类之间的区别是什么?

参考文献

  1. L. Breiman, Bagging predictors, Mach Learn, vol. 24, no. 2, pp. 123–140, Aug. 1996, doi: 10.1007/BF00058655, link.springer.com/content/pdf/10.1007/BF00058655.pdf.

  2. (介绍了 OverBagging、UnderBagging 和 SMOTEBagging 的论文)S. Wang 和 X. Yao, Diversity analysis on imbalanced data sets by using ensemble models, in 2009 IEEE Symposium on Computational Intelligence and Data Mining, Nashville, TN, USA: IEEE, Mar. 2009, pp. 324–331. doi: 10.1109/CIDM.2009.4938667, www.cs.bham.ac.uk/~wangsu/documents/papers/CIDMShuo.pdf.

  3. 实时网站事件升级预测 (2023), medium.com/data-science-at-microsoft/live-site-incident-escalation-forecast-566763a2178

  4. L. Breiman, Random Forests, Machine Learning, vol. 45, no. 1, pp. 5–32, 2001, doi: 10.1023/A:1010933404324, link.springer.com/content/pdf/10.1023/A:1010933404324.pdf.

  5. (介绍了 RUSBoost 算法的论文)C. Seiffert, T. M. Khoshgoftaar, J. Van Hulse, 和 A. Napolitano, RUSBoost: 一种缓解类别不平衡的混合方法,IEEE Trans. Syst., Man, Cybern. A, 第 40 卷,第 1 期,第 185–197 页,2010 年 1 月,doi: 10.1109/TSMCA.2009.2029559,www.researchgate.net/profile/Jason-Van-Hulse/publication/224608502_RUSBoost_A_Hybrid_Approach_to_Alleviating_Class_Imbalance/links/0912f50f4bec299a8c000000/RUSBoost-A-Hybrid-Approach-to-Alleviating-Class-Imbalance.pdf.

  6. (介绍了 SMOTEBoost 算法的论文)N. V. Chawla, A. Lazarevic, L. O. Hall, 和 K. W. Bowyer, SMOTEBoost: 在提升中改进少数类的预测,在数据库中的知识发现:PKDD 2003,N. Lavrač, D. Gamberger, L. Todorovski, 和 H. Blockeel 编著,计算机科学讲座笔记,第 2838 卷。柏林,海德堡:Springer Berlin Heidelberg,2003 年,第 107–119 页。doi: 10.1007/978-3-540-39804-2_12,www3.nd.edu/~dial/publications/chawla2003smoteboost.pdf.

  7. (介绍了 RAMOBoost 算法的论文)Sheng Chen, Haibo He, 和 E. A. Garcia, RAMOBoost: 在提升中的排序少数类过采样,IEEE Trans. Neural Netw.,第 21 卷,第 10 期,第 1624–1642 页,2010 年 10 月,doi: 10.1109/TNN.2010.2066988,ieeexplore.ieee.org/abstract/document/5559472.

  8. (介绍了 EasyEnsemble 的论文)Xu-Ying Liu, Jianxin Wu, 和 Zhi-Hua Zhou, 用于类别不平衡学习的探索性欠采样,IEEE Trans. Syst., Man, Cybern. B,第 39 卷,第 2 期,第 539–550 页,2009 年 4 月,doi: 10.1109/TSMCB.2008.2007853,129.211.169.156/publication/tsmcb09.pdf.

第五章:成本敏感学习

到目前为止,我们已经学习了各种采样技术以及如何对数据进行过采样或欠采样。然而,这两种技术都有其独特的问题。例如,过采样可能会因为重复看到精确或非常相似的例子而导致模型过拟合。同样,在欠采样中,我们失去了一些信息(这些信息可能对模型有用),因为我们丢弃了大多数类别的例子来平衡训练数据集。在本章中,我们将考虑之前所学数据级技术的替代方案。

成本敏感学习是一种有效的方法来处理不平衡数据。我们将介绍这项技术,并了解为什么它可能是有用的。这将帮助我们理解成本函数的一些细节以及机器学习模型默认不是设计来处理不平衡数据集的。虽然机器学习模型没有配备处理不平衡数据集的能力,但我们将看到现代库是如何实现这一点的。

我们将涵盖以下主题:

  • 成本敏感 学习(CSL)的概念

  • 实践中的成本理解

  • 逻辑回归的成本敏感学习

  • 决策树的成本敏感学习

  • 使用scikit-learn和 XGBoost 模型进行成本敏感学习

  • MetaCost – 使任何分类模型具有成本敏感性

  • 阈值调整

到本章结束时,您将了解在分类问题中成本的含义,如何调整模型参数以考虑这些成本,以及如何优先考虑少数类别的预测以减轻误分类的成本。我们还将探讨一个通用的元算法,该算法可以使任何算法具有成本敏感性,以及一种后处理技术,用于调整预测阈值。

技术要求

与之前的章节类似,我们将继续使用常见的库,如numpyscikit-learnxgboostimbalanced-learn。本章的代码和笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Machine-Learning-for-Imbalanced-Data/tree/main/chapter05。您可以通过点击本章笔记本顶部的在 Colab 中打开图标或通过使用笔记本的 GitHub URL 在colab.research.google.com启动它来打开这个 GitHub 笔记本。

成本敏感学习(CSL)的概念

成本敏感学习(CSL)是一种技术,其中机器学习模型的成本函数被改变以考虑数据的不平衡。CSL 背后的关键洞察是我们希望我们的模型成本函数反映不同类别的相对重要性。

让我们尝试理解机器学习中的成本函数和各种类型的成本敏感学习(CSL)。

成本和成本函数

成本函数估计了实际结果与模型预测结果之间的差异。例如,逻辑回归模型的成本函数由对数损失函数给出:

LogLoss = − 1 * N * ∑ i=1 N (y_i * log(ˆy_i) + (1 − y_i) * log(1 − ˆy_i))

在这里,N 是观察的总数,y_i 是真实标签(0 或 1),ˆy_i 是从模型预测出的概率值(介于 0 和 1 之间)。

一种类型的成本被称为误分类错误成本 [1] - 即,预测多数类而不是少数类或反之的成本。

实际上,我们可能会遇到其他类型的成本,例如以下这些:

  • 标记数据集的成本

  • 训练或评估模型的成本

  • 训练数据收集的成本

让我们考虑混淆矩阵:

预测为负预测为正
实际为负真阴性假阳性
实际为正假阴性真阳性

表 5.1 – 理解分类错误成本的混淆矩阵

心理学研究表明,损失带来的痛苦是收益的两倍。同样,在机器学习中,“成本”捕捉了模型出错(假阳性与假阴性)的时刻,而不关心它正确的时候(真阳性与真阴性)。这种成本是误分类错误的成本。

并非所有误分类都是相同的。例如,假设我们正在尝试预测患者是否患有癌症。如果我们的模型错误地指示患者患有癌症(假阳性),这可能导致额外的测试。然而,如果我们的模型错误地建议患者无病(假阴性),后果可能更为严重,因为疾病可能会未诊断而进展。因此,假阴性比假阳性更具破坏性。我们的成本函数应该考虑这种差异。

不幸的是,大多数模型默认将多数类和少数类同等对待。然而,现代机器学习框架如 scikit-learn、Keras/TensorFlow 和 PyTorch 提供了一种方法,可以在各种学习算法中不同地权衡各种类。

成本敏感学习类型

成本敏感学习(CSL)有两种主要方法,即加权法和元学习。在加权方法中,我们更新机器学习模型的成本函数,以反映不同类的重要性。在元学习中,我们可以使模型对成本敏感,而无需更改其成本函数。

在元学习技术 MetaCost 中,例如,我们可以改变训练实例的标签,以最小化预期的误分类成本。同样,在阈值调整方法中,我们确定一个概率阈值,以最小化预测的总误分类成本。图 5.1 从高层次上对这些方法进行了分类 [2][3]:

图片

图 5.1 – 成本敏感学习方法的分类

CSL 与重采样的区别

与之前讨论的数据级别技术相比,CSL 的关键区别在于数据级别技术调整不同错误类型的频率,但它们对待所有误分类错误都是一样的。在某些情况下,正如我们之前遇到的,不同类别的观测值被误分类的成本并不相同。例如,在癌症检测中,将患有癌症的患者误分类为健康(假阴性)的成本要高得多,因为如果未检测或未早期治疗,患者风险很高。同样,将欺诈预订误分类为非欺诈可能会比将合法交易误分类为欺诈的成本更高。为什么?因为在后一种情况下,我们只需联系用户并验证交易的合法性即可。通过应用重采样技术,如上采样或下采样,我们隐式地改变了不同类型错误的成本。因此,CSL 和重采样技术最终可以对模型产生等效的影响。

然而,在某些情况下,重采样技术可能存在问题,我们将在下一节讨论。在这种情况下,CSL 可能更实用:

图片

图 5.2 – 漫画再次强调误分类错误的概念

重平衡技术的缺陷

在前面的章节中,我们简要地提到了为什么在某些情况下,我们可能更喜欢不应用任何数据采样技术。这可能是因为以下原因:

  • 我们已经拥有太多的训练数据,处理更多的数据可能相当昂贵,或者由于有更多的训练数据,训练时间可能增加数倍。

  • 有时,由于我们使用的数据集,我们可能无法通过采样或数据重平衡技术获得最佳结果。

  • 另一个需要考虑的因素是,在重新平衡数据集后,我们的模型的预测分数可能变得不准确,需要重新校准。我们将在第十章“模型校准”中介绍这一主题,我们将学习各种模型校准技术。

  • 重平衡技术可能导致模型过拟合或欠拟合问题。特别是当使用过采样时,它们会产生重复或相似的训练示例,这可能导致过拟合。同样,当使用欠采样时,模型可能欠拟合,因为模型没有在欠采样过程中丢弃的数据上进行训练。

接下来,让我们尝试理解成本究竟意味着什么。

实践中理解成本

在为不同类别创建权重时,我们需要了解涉及的各种成本类型。这些成本根据具体情况而变化。让我们讨论一个成本计算的例子,以了解在考虑成本计算时应考虑什么。

让我们以儿童肺炎为例。根据联合国儿童基金会,每 43 秒就有一个孩子死于肺炎[4]。想象一下,我们正在为儿童肺炎开发一个新的测试——我们将如何决定不同错误的成本?

让我们回顾一下表 5.1 中的混淆矩阵。通常,对于真正的负例和真正的正例,不会有额外的成本。但是,使用错误的负例——也就是说,当一个孩子患有肺炎,却预测该孩子健康时——将会有非常高的成本。另一方面,当一个健康的儿童被预测为患有肺炎时,将会有与孩子家庭可能遇到的麻烦相关的成本,但这个成本比前一种情况要低得多。此外,误分类的成本可能因孩子的年龄而异。例如,年幼的孩子比年长的孩子风险更高。因此,如果模型在年幼孩子的案例中出错,我们将对模型进行更多的惩罚。

成本可能因症状的持续时间而异。可以这样考虑:如果我们犯了一个错误,错误地诊断了一个只有一天流感症状的孩子,这并不理想,但也不是灾难性的。然而,如果那个孩子已经忍受了 2 周的流感症状,那将是一个不同的场景。这个错误将给我们带来更大的成本。

虽然我们迄今为止讨论了现实世界的问题,但本章将转向使用合成数据集。这种方法旨在在受控环境中强化概念和方法,从而增强学习过程:

X, y = make_classification(
    n_samples=50000, n_features=2, n_redundant=0, class_sep=2,\
    weights=[0.99], random_state=1, n_clusters_per_class=1)
X_train, X_test, y_train, y_test = train_test_split(X, y,\
    test_size = 0.2, random_state = 0, stratify=y)
print('y_train: ', Counter(y_train))
print('y_test: ', Counter(y_test))
plot_dataset(X_train, y_train)

make_classification函数产生了一些重叠的点,我们清理了这些点。为了简化,我们在这里省略了清理代码。您可以在 GitHub 上的完整笔记本中查阅。

之前的代码产生了以下输出和散点图(图 5*.3*):

y_train:  Counter({0: 39404, 1: 596})
y_test:  Counter({0: 9851, 1: 149})

图 5.3 – 显示训练数据集分布的散点图

我们将深入探讨如何将 CSL 应用于逻辑回归模型。

逻辑回归的成本敏感学习

逻辑回归是一种简单的分类算法。我们通过将特征进行线性组合来训练模型。然后,我们将线性组合的结果传递给 sigmoid 函数,以预测不同类别的类别概率。

sigmoid函数(也称为logit函数)是一种可以将任何实数转换为 0 到 1 之间值的数学工具。这个值可以解释为概率估计:

import numpy as np
def sigmoid(x):
     s = 1/(1+np.exp(-x))
     return s

Sigmoid 函数的图像呈 S 形曲线,看起来是这样的:

图 5.4 – Sigmoid 函数

具有最高预测概率的类别被用作给定样本的预测。

假设我们有一个要分类为垃圾邮件或非垃圾邮件的电子邮件,并且我们的逻辑回归模型输出非垃圾邮件的概率为 0.25,垃圾邮件的概率为 0.75。在这里,具有最高预测概率的类别是“垃圾邮件”(1),因为 0.75 大于 0.25。因此,模型会预测这封邮件是垃圾邮件(图 5*.5*):

图 5.5 – 高类别概率决定二分类的类别

对于二分类,我们只预测一个类别的概率。另一个类别的概率是第一个类别的概率减去 1。

对数逻辑回归模型使用损失函数进行训练。来自具有两个类别的数据集的一个示例的损失函数看起来像这样:

cost = − y * log(classProbability) − (1 − y)* log(1 − classProbability)

对于真正的阳性和真正的阴性,这个损失将非常低。对于误报,y,实际值将是 0;因此,第一个项将是 0,但第二个项将非常高,因为类别概率接近 1,这个项将接近负无穷大(因为,log(0) → − ∞)。由于前面有一个负号,成本将接近正无穷大。对误检情况可以进行类似的分析。成本的一部分可以看作是误报部分,另一部分可以看作是误检部分:

cost = falsePositiveCost + falseNegativeCost

如前所述,我们不希望两种类型的成本同等重要。所以我们只是为各自的成本添加权重,W FP 和 W FN:

cost = W FP * falsePositiveCost + W FN * falseNegativeCost

这是对数逻辑回归中 CSL 的核心。为了得到模型的总体成本,我们取所有数据点的平均成本:

lr = LogisticRegression(random_state=0, max_iter=150).fit(
    X_train, y_train)
plot_decision_boundary(X_train, y_train, lr, 'LogisticRegression')
plt.show()
PrecisionRecallDisplay.from_estimator(
    lr, X_test, y_test, ax = plt.gca(),name = "LogisticRegression")

当所有错误成本相等时,模型的决策边界和模型的精度-召回率PR)曲线将看起来像这样:

图 5.6 – 基线回归模型的决策边界(左)和 PR 曲线(右)

compute_scores(lr, X_test, y_test)

之前的代码输出了以下 F2 分数、精度和召回值:

f2-score: 0.921 precision: 0.926 recall: 0.919

在本章中,我们将使用 F2 分数作为我们的主要指标。F2 分数是什么?在第一章机器学习中数据不平衡的介绍,我们学习了 F-beta 分数。F2 分数是 beta=2 的 F-beta 分数,而 F1 分数是 beta=1 的 F-beta 分数。当召回率比精度更重要时,它很有用——也就是说,误报比误检(重要)的成本更高:

F β =  (1 + β 2) × (precision × recall)  ____________________  (β 2 × precision) + recall  =  (5 × precision × recall)  ________________  (4 × precision) + recall

LogisticRegression来自scikit-learn库提供了一个class_weight参数。当此参数的值设置为“balanced”时,每个类别的权重将自动通过以下公式计算:

weightOfClass = totalNumberOfSamples   ________________________________   numberOfClasses * numberOfSamplesPerClass

例如,在数据集中我们有 100 个示例 – 80 个属于类别 0,20 个属于类别 1。每个类别的权重计算如下:

  • 类别 0 的权重 = 100/(2*80) = 0.625

  • 类别 1 的权重 = 100/(2*20) = 2.5

由于类别 0 的示例数量是类别 1 的四倍,类别 1 的权重是 2.5,是类别 0 权重的四倍,即 0.625。这很有道理,因为我们希望给类别 1 更多的权重,因为它的数量较少。

我们也可以将class_weight作为一个字典来提及:

LogisticRegression(class_weight={0: 0.5, 1:0.5})

让我们尝试在LogisticRegression函数中使用class_weight参数:

lr_weighted = LogisticRegression(class_weight='balanced', \
    random_state=0, max_iter=150).fit(X_train, y_train)
plot_decision_boundary(X_train, y_train, lr_weighted, \
    'LogisticRegression')
plt.show()
PrecisionRecallDisplay.from_estimator(lr_weighted, X_test,\
    y_test, ax = plt.gca(),name = "LogisticRegressionWeighted")

图 5.7 – “平衡”类别加权逻辑回归模型的决策边界(左)和 PR 曲线(右)

让我们计算 F2 分数、精确率和召回率分数:

compute_scores(lr_weighted, X_test, y_test)

“平衡”类别加权逻辑回归模型的分数如下:

f2-score: 0.873 precision: 0.587 recall: 0.993

在分析结果后,我们可以看到一个决策边界,它正确地分类了大多数正类示例。精确率下降,而召回率上升。F2 分数的下降可以归因于召回率和精确率值的变化。模型在召回率方面有所提高,表明其识别所有正类示例的能力得到了增强。然而,这种进步导致精确率同时下降,这表明在负类示例(我们并不特别关心)上犯错的速率增加。

让我们尝试使用网格搜索调整class_weight参数,以优化我们的 F2 分数。我们始终可以尝试优化任何其他目标,例如平均精确率、精确率或召回率等。np.linspace(0.05, 0.95, 20)函数是一个numpy函数,它生成一个介于 0.05 和 0.95 之间的 20 个均匀分布的数字数组:

from sklearn.metrics import make_scorer, fbeta_score
def f2_func(y_true, y_pred):
    f2_score = fbeta_score(y_true, y_pred, beta=2.)
    return f2_score
def f2_scorer():
    return make_scorer(f2_func)
# Define the parameter grid
param_grid = {
    'class_weight': [
        {0: x, 1: 1.0-x} for x in np.linspace(0.05, 0.95, 20)]
}
# Instantiate the grid search model
grid_search =GridSearchCV(
    LogisticRegression(),param_grid,\
    cv=3, scoring=f2_scorer(), n_jobs=-1
)
# Fit the grid search to the data
grid_search.fit(X_train, y_train)
# Get the best parameters
best_params = grid_search.best_params_
best_params

这会产生以下输出:

{'class_weight': {0: 0.14473684210526316, 1: 0.8552631578947368}}

我们的标准指标如下:

f2-score: 0.930 precision: 0.892 recall: 0.940

在引入这些类别权重后,我们的决策边界试图在错误分类正类和负类示例之间取得更好的平衡,如图图 5**.8所示。这导致 F2 分数达到 0.93,提高了精确率值,同时保持适度的召回率:

图 5.8 – 类别加权逻辑回归模型的决策边界(左)和 PR 曲线(右)

🚀 微软在生产中的成本敏感学习

在微软的一个实际应用中,主要目标是提高 Bing 广告的点击率CTR)预测[5]。实现准确的 CTR 预测对于优化用户体验和收入流至关重要。预测准确率仅提高 0.1%,就有可能使利润增加数亿美元。通过严格的测试,一个结合神经网络NNs)和梯度提升决策树GBDTs)的集成模型成为最有效的解决方案。

对于训练数据集,从一个月的日志数据中随机选择了 5600 万个样本,每个样本包含数百个统计特征。为了减少训练成本,非点击案例被降采样了 50%,并分配了 2 的类别权重以保持原始分布。然后使用从随后一周日志中随机抽取的 4000 万个样本的测试数据集评估模型性能。而不是重新校准模型,使用了类别权重来在降采样后保持平均 CTR。

在下一节中,我们将讨论如何使用决策树进行代价敏感学习。

决策树的代价敏感学习

决策树是使用条件决策来预测样本类别的二叉树。每个树节点代表一组与基于特征的连续条件语句相对应的样本。我们根据特征和阈值值将节点分为两个子节点。想象一下有一组学生,他们的身高、体重、年龄、班级和位置。我们可以根据年龄特征和 8 的阈值将这个集合分为两部分。现在,所有年龄小于 8 岁的学生将进入左子节点,而所有年龄大于或等于 8 岁的学生将进入右子节点。

这样,我们可以通过连续选择特征和阈值值来创建树。树的每个叶节点将只包含来自一个类别的节点。

在构建决策树的过程中,经常会遇到一个问题:“应该选择哪个特征和阈值对来分割给定节点的样本集?”答案很简单:我们选择产生最均匀(或同质)数据子集的对。理想情况下,产生的两个结果子集——被称为左右子节点——应该各自主要包含来自单个类别的元素。

节点从不同类别中混合样本的程度被称为节点的不纯度,这可以被视为决策树的损失度量。不纯度越高,样本集的异质性就越大。以下是计算不纯度的两种最常见方法:

  • Gini 系数

  • Entropy

让我们看看 Gini 系数和熵的两个类 c1 和 c2 的公式:

Gini = 1− Proportion c1 2− Proportion c2 2

我们将得到以下结果:

Entropy = − Proportion c1 * log (Proportion c1)

− 比例 c2 * log (比例 c2)

要使用决策树进行 CSL,我们只需将类权重与 Gini 和熵计算中每个类的项相乘。如果两个类的权重是 W1 和 W2,Gini 和熵将如下所示:

Gini = 1 − W1 * 比例 c1² − W2 * 比例 c2²

Entropy = − W1 * 比例 c1 * log(比例 c1)

− W2 * 比例 c2 * log(比例 c2)

现在,模型优先考虑权重较高的类,而不是权重较低的类。如果我们给少数类更多的权重,模型将做出优先考虑具有同质少数类样本的节点的决策。

在本节中,我们了解了一些如何将类权重纳入决策树的损失函数中,以考虑误分类错误。在下一节中,我们将看到scikit-learn如何通过将其集成到模型创建 API 中来简化此过程,从而消除我们手动调整损失函数的需要。

使用 scikit-learn 和 XGBoost 模型进行成本敏感学习

scikit-learn提供了一个class_weight超参数来调整大多数模型中各种类的权重。这个参数可以根据scikit-learn中不同学习算法的不同方式指定。然而,主要思想是这个参数指定了损失计算公式中每个类的权重。例如,这个参数指定了之前提到的逻辑回归中的权重 FP 和权重 FN 的值。

LogisticRegression函数类似,对于DecisionTreeClassifier,我们可以使用DecisionTreeClassifier(class_weight='balanced')DecisionTreeClassifier(class_weight={0: 0.5, 1: 0.5})

关于 SVM,它甚至可以通过为每个类标签指定一个权重值来扩展到多类分类:

svm.SVC(class_weight= {-1: 1.0, 0: 1.0, 1: 1.0})

关于确定class_weight值的通用指导原则是使用多数类与少数类比例的倒数。我们可以通过使用网格搜索算法(使用scikit-learn中的GridSearchCV函数)进行超参数调整来找到更优的class_weight值。

类似地,XGBoost 也有scale_pos_weight参数来控制正负权重的平衡:

XGBClassifier(scale_pos_weight)

scale_pos_weight的默认值是 1。一个推荐的scale_pos_weight值是sum(negative_instances)/sum(positive_instances),这可以计算为float(np.sum(label == 0)) / np.sum(label==1)

XGBoost 有几个其他参数,例如 max_delta_stepmin_child_weight,这些参数可以针对不平衡数据集进行调整。在优化过程中,max_delta_step 决定了更新步长的大小,影响学习速度和稳定性。min_child_weight 通过影响决策树中叶节点的大小来控制过拟合并增强泛化。在处理不平衡数据场景时,调整这些参数可以策略性地提高算法性能。

首先,让我们使用 DecisionTreeClassifier 解决我们的分类问题:

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import PrecisionRecallDisplay
dt_clf = DecisionTreeClassifier(random_state=0, max_depth=6).fit(
    X_train, y_train)
plot_decision_boundary(X,y,dt_clf,'DecisionTreeClassifier')
plt.show()
PrecisionRecallDisplay.from_estimator(
    dt_clf, X_test, y_test, ax = plt.gca(),\
    name = "DecisionTreeClassifier")
print(classification_report_imbalanced(
    y_test,\
    dt_clf.predict(X_test), \
    target_names=['class 0', 'class 1']
    )
)
computescores(dt_clf, X_test, y_test)

输出的决策边界比逻辑回归的更复杂(图 5.9),更好地分离了两个类别,并给出了 F2 分数为 0.932:

图 5.9 – 决策树分类器模型的决策边界(左)和 PR 曲线(右)

我们在 图 5.10 中重现了逻辑回归模型的决策边界和 PR 曲线,以进行比较:

图 5.10 – 用于比较的逻辑回归(左)和 PR 曲线(右)

我们对决策树分类器的标准指标如下:

f2-score: 0.932 precision: 0.892 recall: 0.94

接下来,让我们使用 class_weight='balanced' 参数:

dt_clf_tuned = DecisionTreeClassifier(
    class_weight = 'balanced', random_state=0, max_depth=6
).fit(X_train, y_train)

在使用之前的代码绘制决策边界、PR 曲线和计算分数后,输出如下:

图 5.11 – 决策树分类器模型的决策边界(左)和 PR 曲线(右)

f2-score: 0.934 precision: 0.770 recall: 0.987

调整后的权重提高了 F2 分数和召回值。

流行框架如 scikit-learn 也允许我们指定 sample_weight 作为数据集中每个观察值的权重列表。sample_weightclass_weight 参数可能相当令人困惑,并且它们的目的可能不会从它们的文档中非常清楚地了解何时使用什么。以下表格说明了两者之间的区别:

sample_weightclass_weight
目的用于指定单个示例的权重。当某些示例比其他示例更重要时,无论它们的类别如何,都可能很有用。当某些数据更可信(例如使用内部人工标注器标注)时,它可以获得更高的权重。当您对批处理中的样本没有相同的信心时,这可能很有用。用于纠正类别不平衡。当示例的重要性取决于它们的类别时应该使用。
用法可以在训练和测试中使用。特别适用于在具有 AUC 等指标的不同测试集上比较多个模型时,通常希望平衡测试集:sklearn.metrics.confusion_matrix(…, sample_weight) sklearn.linear_model . LogisticRegression() . score(…,sample_weight)主要在训练期间使用,以指导训练。考虑到某些类别比其他类别更重要,因此会计算误分类错误:sklearn.linear_model . LogisticRegression(``class_weight)
在模型训练期间设置值为 0 的影响模型将不会考虑samples_weight=0的示例(无论示例的类别如何)。模型将不会考虑属于class_weight = 0的类别的任何示例。此外,模型永远不会预测该类别。
应用案例当预测客户流失时,如果失去某些客户会对业务产生更大的影响,因为他们倾向于更频繁地购买或花费更多,我们希望使用sample_weight给这些客户更高的权重。如果我们有一个数据集,其中一个类别明显多于其他类别,使用class_weight可以帮助模型更多地关注代表性不足的类别。

表 5.2 – scikit-learn 库中的 sample_weight 与 class_weight

警告

如果我们使用sample_weightclass_weight,两者都会相乘,我们将看到这两个参数的效果。这两个参数仍然可以一起使用,以平衡类别重要性和个体实例重要性,并实现其预期目的。

使用numpy可以更容易地创建sample_weight所需的权重值列表:sample_weight = np.where(label==1, 80, 20)。然而,scikit-learn有一个名为sklearn.utils.class_weight.compute_sample_weight()的函数,可以用来自动从class_weight估计sample_weight的值。

class_weight也可以是一个字典,包含每个标签的值或平衡值。如果我们将其设置为平衡,类别权重将由n_samples/(n_classes * np.bincount(y))确定。

class_weight返回的值是一个字典:{class_label: weight},对于每个class_label值。

类似地,如果你需要进行多标签分类,你也可以使用sklearn.utils.class_weight.compute_sample_weight

🚀 Airbnb 生产中的成本敏感学习

在 Airbnb 的一个实际应用中[6],要解决的主要问题是提高搜索和可发现性以及他们体验(手工活动)平台的个性化。随着体验数量的增加,有效地对这些体验进行排名以匹配用户偏好并提高预订变得至关重要。

Airbnb 旨在提高其搜索排名,为用户提供最相关和高质量的经历。为了提升其排名模型的品质,他们在目标函数中使用了样本权重(在上一节中讨论过)。

通过使用样本权重(在上一节中讨论过)解决训练数据中的数据不平衡问题。在目标函数中,高质量的经历被赋予更高的权重,而低质量的经历被赋予较低的权重。这样做是为了在搜索排名中推广高质量的经历,并且他们成功地提高了高质量经历的排名,减少了低质量经历,而没有影响整体预订,如 A/B 测试所证实。

Airbnb 迭代地开发和测试其机器学习模型,最终将其集成到其生产系统中,以实时对“体验”进行排名。他们经历了多个阶段,从建立强大的基线到个性化、在线评分以及处理各种业务规则。

在下一节中,我们将了解一种可以将任何模型转换为成本敏感版本的技术,而无需我们知道其损失函数或模型的内部工作原理。

MetaCost – 使任何分类模型成本敏感

MetaCost 首次在 1999 年由 Pedro Domingos [7]发表的一篇论文中提出。MetaCost 作为机器学习算法的包装器,将底层算法转换为自身的成本敏感版本。它将底层算法视为黑盒,与不稳定算法(如下定义)配合最佳。当 MetaCost 首次提出时,CSL 还处于早期阶段。只有少数算法,如决策树,已被转换为它们的成本敏感版本。对于某些模型,创建成本敏感版本变得很容易,而对于其他模型,则是一项非平凡的任务。对于定义模型成本敏感版本变得困难的算法,人们主要依赖于数据采样技术,如过采样或欠采样。这就是 Domingos 提出了一种将大量算法转换为它们的成本敏感版本的方法。MetaCost 可以用于多类分类和所有类型的成本矩阵。

不稳定算法

如果一个算法的初始条件(例如,训练数据或初始权重)的微小变化可以导致模型产生大的变化,则称该算法为不稳定[8]。假设你有一个包含 1,000 个项目的数据集。一个稳定的模型,如K-最近邻KNN),如果你从数据集中移除一个项目,它不会改变太多。然而,如果你用 999 个项目而不是 1,000 个项目来训练一个决策树模型,它可能会完全重构。

让我们深入探讨 MetaCost 算法的机制,如图 5.12 所示:

图片

图 5.12 – MetaCost 算法

MetaCost 通过结合 bagging 的概念与误分类成本矩阵来工作:

  1. 首先,我们创建原始数据的多个 bootstrap 样本。

  2. 对于每个 bootstrap 样本,我们训练给定模型的一个新副本。到目前为止,这个过程与 bagging 相同。你可以在图 5.12 左侧看到前两个步骤。首先,我们从原始数据中创建 bootstrap 样本 S1、S2 和 S3。然后,我们分别在样本(S1、S2 和 S3)上训练模型 L1、L2 和 L3。

  3. 接下来,我们将原始数据 S 送入由 L1、L2 和 L3 组成的集成中。

  4. 我们将来自成本矩阵的误分类成本与集成预测的类别概率相乘,以获得实际成本。这显示在图 5.12 的右侧。

  5. 然后,我们重新标记数据,使得新的类别标签最小化实际成本。

  6. 最后,我们在重新标记的数据上训练了一个新模型的副本。这个模型副本被用作最终模型。

我们可以在 图 5.13 中看到使用 MetaCost 重新标记数据的过程:

图 5.13 – 使用 MetaCost 重新标记数据的过程

图 5.13 的左侧,我们有原始数据。星星是少数类示例,正方形是多数类示例。在这里,所有在椭圆形内的样本都被预测为星星,所有在椭圆形外的样本都被预测为正方形。左侧的椭圆形是根据对所有错误具有相同误分类成本的假设绘制的。在中间,我们根据实际误分类成本绘制的一个拉长的椭圆形创建了一个新的类别边界。请注意,现在所有的星星都被正确分类了。同时,请注意,现在一些正方形被错误地分类为星星。这是预期的,因为星星的误分类成本远高于正方形。在此阶段,MetaCost 将这些被错误分类的正方形重新标记为星星。最后,MetaCost 在重新标记的数据上训练了一个模型。由于容易被误认为是少数类的多数类示例已被重新标记为属于少数类,因此最终的模型不太可能将少数类的实例标记错误。

为了节省空间,我们省略了 MetaCost 算法的实现。您可以在本章的 GitHub 仓库中找到它。

我们将应用该算法到逻辑回归模型中。MetaCost 使用一个代价矩阵,这是一个超参数。代价矩阵中的值对应于混淆矩阵(表 5.1中混淆矩阵的转置)中各项的权重或成本:

C = (TN FN FP TP )

假设我们使用一个对假阳性和假阴性具有相同成本的代价矩阵(即单位矩阵):

C = np.array([[0, 1], [1, 0]])

图 5.14 展示了决策边界和指标,它们与逻辑回归分类器的结果(图 5.10)非常接近:

图 5.14 – 具有单位代价矩阵的逻辑回归模型 MetaCost 变种的决策边界(左侧)和 PR 曲线(右侧)

我们可以根据训练数据的失衡比率估计代价矩阵:

C = np.array([[0, 66], [1, 0]])

图 5.15 展示了输出决策函数和 PR 曲线:

图 5.15 – 具有更优代价矩阵的逻辑回归模型 MetaCost 变种的决策边界(左侧)和 PR 曲线(右侧)

虽然与基线相比 F2 分数有所下降,但召回率确实大幅提高。

MetaCost 算法中的各种步骤,如重新标记整个训练集,可能相当昂贵,这可能会阻止我们在训练数据集很大时使用这种技术。

成本敏感的集成技术

AdaCost [9]、AdaUBoost [10]和 AsymBoost [11]是 AdaBoost 模型的成本敏感修改。AdaCost 在迭代训练过程中最小化误分类成本。AdaUBoost 通过强调少数类来处理不平衡数据集。AsymBoost 专注于减少最昂贵的误分类。它们都在考虑误分类成本的同时调整权重。

这些算法背后的基本原理是,除了对误分类成本大的实例分配高初始权重外,更新权重的规则也应考虑成本。这意味着应该增加昂贵误分类的权重,同时减少正确分类的权重。

在下一节中,我们将了解另一种成本敏感的元学习技术,称为阈值调整。

阈值调整

决策阈值是一个非常重要的概念,需要密切关注。默认情况下,我们有以下情况:

  • 预测概率 >= 0.5 表示类别 1

  • 预测概率 < 0.5 表示类别 0

然而,阈值是一个强大的元参数,我们可以自由调整。表 5.3显示了模型预测与真实标签的预测。

如果我们使用默认的阈值 0.5,准确率为 2/4 = 50%。另一方面,如果我们选择的阈值是 0.80,准确率为 100%。这表明所选阈值的重要性:

预测输出真实输出
0.650
0.750
0.851
0.951

表 5.3 – 一个显示模型预测输出与真实输出(标签)的表格

大多数指标,如准确率、精确率、召回率和 F1 分数,都是阈值相关指标。

另一方面,如 ROC 曲线和 PR 曲线这样的指标是阈值无关的,这意味着这些图表评估的是模型在所有可能的阈值下的性能,而不是单个固定的阈值。

在处理机器学习指标,如 F1 或准确率时,了解阈值值的作用很重要。这些指标默认使用 0.5 的阈值。因此,尤其是对于新手和中级机器学习从业者,会产生一种误解,认为这些指标不可避免地与这个特定的阈值相关联。

然而,这可能导致对模型性能的不准确解释,尤其是在涉及不平衡数据集的场景中。指标的选择和决策阈值的确定是独立的选择,应该这样处理。确定适当的阈值是过程中的一个关键步骤,应该独立于所选的指标来考虑。

此外,仅仅依赖于默认的阈值 0.5 可能会产生误导。阈值应根据项目的具体要求和数据的性质来设置。因此,机器学习从业者理解阈值与所选指标之间的相互作用,以准确评估其模型性能至关重要。

在二元分类中,改变阈值将很容易改变阈值相关的指标,如准确度、F1 分数、TPR 或 FPR。许多研究[12][13]提到了阈值调整的价值,尤其是在训练数据不平衡的情况下。Provost [14]的一篇论文指出,使用未调整输出阈值的模型可能是一个严重的错误。在深度学习领域,Buda 等人[15]表明,使用随机过采样ROS)并结合阈值处理,在由 CIFAR 和 MNIST 创建的不平衡数据集上优于简单的 ROS。无论数据是否不平衡,选择一个最优的阈值可以在模型的性能上产生很大的差异。

许多时候,我们希望找到优化我们的阈值相关指标(例如 F1 分数)的阈值。在这里,找到 F1 分数最大的阈值(图 5.16):

图片

图 5.16 – 一个具有最佳阈值的 PR 曲线,该阈值找到最大的 F1 分数(请参阅本章 GitHub 仓库中的笔记本)

图 5.17 展示了一个图表,说明了修改决策阈值对不平衡数据集的各种分类指标的影响:真正例率TPR或召回率)、真负例率TNR)、假正例率FPR)和精确度。所使用的模型是没有任何类别权重或对少数类敏感性的逻辑回归。有关完整笔记本,请参阅本章的 GitHub 仓库:

图片

图 5.17 – 不同分类指标(TPR、TNR、FPR、精确度、F1 分数和准确度)作为决策阈值的函数的图表

关于这些图表,有以下几点观察:

  • 精确度:如果阈值增加,精确度(  TP _________________  总预测为正的次数)通常会上升。为什么?因为随着阈值的增加,总预测为正的次数会减少,因此,随着分母的减少,精确度增加。同样,相反的情况也是正确的:如果阈值降低,精确度也会降低。

  • 召回率:让我们看看阈值变化对召回率的影响。召回率定义为  TP ____________  总正例数,分母是一个常数。随着阈值的降低,TP 可能会增加,通常会提高召回率。

  • 真正例率TNR):TNR 衡量的是实际负例中被正确识别为负例的比例。在不平衡的数据集中,如果负类是多数类,一个简单或表现不佳的分类器可能因为对所有或大多数实例预测多数类而具有很高的 TNR。在这种情况下,TNR 可能会误导性地很高。

  • 误报率FPR):这是将负实例错误地分类为正实例的比率。在不平衡的数据集中,一个简单的分类器,如果将所有实例都预测为多数类(负类),其 FPR 将接近 0。

通常,我们在选择最佳决策阈值时,需要在 TPR(真正例率)和 TNR(真负例率)之间进行权衡,如图 5**.17**所示。

🚀 Shopify 在生产中的成本敏感学习

在 Shopify 的实际应用中[16],平台面临着为成百万的商家对各种商品进行分类的挑战。准确的产品分类对于增强搜索和发现功能以及向商家提供个性化营销洞察至关重要。鉴于产品数量庞大且种类繁多,手动分类是不可行的。机器学习技术被用于自动化分类过程,以适应不断扩展和多样化的产品范围。所使用的数据集高度不平衡,这主要是由于 Shopify 采用的谷歌产品分类法GPT)的层级结构。GPT 拥有超过 5,500 个类别,这给一个已经具有挑战性的问题增加了复杂性。

为了解决数据不平衡的问题,实施了类权重。通过分配类权重,模型可以对代表性不足的类别中的错误预测施加更高的惩罚,从而有效地缓解这些类别中数据不足的问题。模型经过微调,以在层级精度和召回率之间取得平衡。这种微调是基于特定的业务用例,旨在通过最小化负面交互和摩擦来提升商家体验。对置信阈值进行了手动调整(这表明阈值调整在现实世界中是多么相关!),以确保在“宗教和仪式”等敏感类别中实现最佳性能。平衡了各种指标,如层级准确率、精确率、召回率和 F1 分数,以使模型满足业务需求。该模型现在被多个内部团队和合作伙伴生态系统积极用于开发衍生数据产品。

接下来,我们将探讨调整这些阈值的各种方法。

阈值调整方法

大多数情况下,我们旨在优化特定的业务指标或标准机器学习指标,这要求我们选择一个最大化感兴趣指标的阈值。在文献中,讨论了各种阈值调整方法,例如将阈值设置为观察正例的优先概率,使用 ROC 曲线优化高 TPR 和低 FPR,或使用 PR 曲线最大化 F1 分数或 Fbeta 分数(见图 5**.18):

图 5.18 – 调整阈值的流行方法

我们将逐一讨论这些方法。

我们将继续使用我们之前创建的相同数据集。以下代码块拟合逻辑回归模型并获取测试集的预测概率:

lr = LogisticRegression(max_iter=1000)
lr.fit(X_train, y_train)
y_pred = lr.predict_proba(X_test)
y_pred = y_pred[:, 1]

使用先验阈值进行阈值调整

一个明显的阈值可以使用的是训练数据集中正类概率的值[10]。让我们实现这个想法:

num_pos, num_neg = Counter(y)[1], Counter(y)[0]
prior_threshold = num_pos /(num_pos + num_neg)
print('Prior threshold=%f'% prior_threshold)
# Find the closest threshold from thresholds from ROC curve
fpr, tpr, thresholds = roc_curve(y_test, y_pred)
min_threshold_index = np.absolute( \
    thresholds-prior_threshold).argmin()
print('Best threshold using prior threshold from ROC \
    function=%f'% thresholds[min_threshold_index])
print("TPR at threshold=%f" % tpr[min_threshold_index])
print("FPR at threshold=%f" % fpr[min_threshold_index])
print("TNR at threshold=%f" % (1-fpr[min_threshold_index]))

这将打印以下阈值:

Prior threshold=0.014900
Best threshold using prior threshold from ROC function=0.014232
TPR value at the threshold=0.675676
FPR value at the threshold=0.147990
TNR value at the threshold=0.852010

使用 ROC 曲线进行阈值调整

对于 ROC 曲线,可以使用尤登的 J 统计量[17]来找到最优阈值。尤登的 J 统计量在临床领域有根源,是一个捕获诊断测试性能的单个统计量。在二元分类的背景下,统计量 J 定义为以下:

J = 灵敏度 + 特异性 − 1 = TPR + TNR − 1 = TPR − FPR

其值可以从-1(TPR=0 和 TNR=0 – 即,总是错误的结果)到 1(TPR=1 和 FPR=0 – 即,完美结果)不等。这是在 ROC 分析中选择阈值的一个常见选择,因为它平衡了灵敏度(TPR)和特异性(TNR)。请注意,TNR = 1-FPR。

最大化尤登指数 J 的原因等同于选择最优阈值,因为它本质上是在 ROC 曲线上找到离无歧视线(对角线)最远的点。这意味着它选择一个在 TPR 和 FPR 之间取得平衡的阈值,这通常是我们在分类器中想要的。

“最优”阈值可能严重依赖于我们特定应用中假阳性与假阴性的成本。以下代码块使用尤登指数识别最优分类阈值,该指数是从 ROC 曲线计算得出的:

fpr, tpr, thresholds = roc_curve(y_test, y_pred)
youden_index = tpr - fpr
max_youden_index = np.argmax(youden_index)
best_thresh = thresholds[max_youden_index]
print('Best threshold using Youden index=%f'% best_thresh)
print('Max Youden index value=%f'% youden_index[max_youden_index])
print("TPR value at the threshold=%f" % tpr[max_youden_index])
print("FPR value at the threshold=%f" % fpr[max_youden_index])
print("TNR value at the threshold=%f" % (1-fpr[max_youden_index]))

这将输出以下值:

Best threshold using Youden index=0.098879
Max Youden index value=0.622143
TPR value at the threshold=0.635135
FPR value at the threshold=0.012992
TNR value at the threshold=0.987008

另一种常用于文献中的阈值调整方法,它使用 ROC 曲线,即最大化 TPR(也称为灵敏度)和 TNR(也称为特异性)的几何平均值:

G − mean = √ __________________  灵敏度 * 特异性  = √ _TPR * TNR  = √ ______________  TPR * (1 − FPR)

最大化几何平均值等同于在 TPR 和 TNR 之间找到一个良好的平衡。以下代码块使用 G-mean 指标计算分类的最佳阈值,以及其相应的 TPR、FPR 和 TNR 值。我们从sklearn.metrics导入roc_curve

fpr, tpr, thresholds = roc_curve(y_test, y_pred)
gmean = np.sqrt(tpr*(1-fpr))
max_gmean_index = np.argmax(gmean)
best_thresh = thresholds[max_gmean_index]
print('Best threshold using G-mean=%f'% (best_thresh))
print('Max G-mean value=%f'% (gmean[max_gmean_index]))
print("TPR value at the threshold=%f" % tpr[max_gmean_index])
print("FPR value at the threshold=%f" % fpr[max_gmean_index])
print("TNR value at the threshold=%f" % (1 - fpr[max_youden_index]))

这将输出以下最优值:

Best threshold using G-mean=0.098879
Max G-mean value=0.791760
TPR value at the threshold=0.635135
FPR value at the threshold=0.012992
TNR value at the threshold=0.987008

使用 PR 曲线进行阈值调整

正如我们在第一章,“机器学习中的数据不平衡介绍”中讨论的那样,当正类比负类更重要时,PR 曲线通常比 ROC 曲线更适合不平衡数据集。作为提醒,简单的原因是 PR 曲线忽略了真正的负例,因此,它可以在使用不平衡数据集与平衡数据集相比时,表示模型性能的显著差异。虽然随着数据不平衡的增加,ROC 曲线不会变化太多,但如果两个类别都同样重要,它们可以是一个更好的选择。

我们希望精确率和召回率都达到最大值,理想情况下都是 1。因此,PR 曲线上的优化点是(1,1)。

当我们偏离这个优化点时,精确率和召回率的值都会降低,我们就不那么优化了。

一种捕捉精确率和召回率之间权衡的度量是 F1 分数。F1 分数被定义为精确率和召回率的调和平均值:

F1 = 2 * (Precision * Recall) / (Precision + Recall)

如果我们分析这个方程,我们会看到当精确率和召回率都是 1 时,F1 分数最高(在 1 处达到最大值),这正是我们在 PR 曲线上定义的优化点。

因此,优化最大 F1 分数将确保我们努力最大化精确率和召回率,有效地推动我们向 PR 曲线上的优化点靠近。

最大化 F1 分数是确定最佳阈值的一种常见且有效的方法。以下代码块使用从 PR 曲线派生的 F1 分数度量计算最佳阈值:

from numpy import argmax
from sklearn.metrics import precision_recall_curve
precision, recall, thresholds = precision_recall_curve(y_test, y_pred)
fscore = 2*precision*recall/(precision+recall)
max_fscore_idx = argmax(fscore)
print('max_fscore_idx==%d' % max_fscore_idx)
print('best threshold using PR curve=%f' % thresholds[max_fscore_idx])

这会输出以下优化值:

max_fscore_idx==4950
best threshold using PR curve=0.258727
max(fscore)= 0.661290

让我们绘制具有最佳阈值值的 PR 曲线:

pyplot.plot(recall, precision, marker='.', \
    label='precision vs recall for various thresholds')
result = pyplot.scatter(recall[max_fscore_idx], \
    precision[max_fscore_idx], \
    marker='x', color='red', \
    label='Best threshold with highest\
    f1-score')
plt.legend(handles=result.legend_elements()[0], \
    labels="legend", loc='upper center', \
    bbox_to_anchor=(1, 1))
pyplot.xlabel('recall')
pyplot.ylabel('precision')

PR 曲线看起来是这样的:

图片

图 5.19 – 最佳 F1 分数阈值值的 PR 曲线

一般阈值调整

作为一种一般方法,我们可能需要优化任何度量,如平均精确度、准确度等,或任何其他业务度量。在这种情况下,我们可以编写一个函数来直接优化该度量。让我们以 I. Unal 等人在其研究[18]中定义的联合指数IU)度量为例,其中度量由使 IU(c)最小化的阈值值 c 定义:

IU(c) = (|Sensitivity(c) − ROC _ AUC| + |Specificity(c) − ROC _ AUC|)

在这里,Sensitivity(c)是 c 处的灵敏度,Specificity 是 c 处的特异性,ROC _ AUC 是 ROC 图的曲线下面积AUC)。

让我们实现 IU 度量,如这里定义的,作为一个自定义度量来找到最小化它的最佳阈值:

from sklearn.metrics import f1_score, auc
def custom_metric(y_test, y_pred):
    fpr, tpr, thresholds = roc_curve(y_test, y_pred)
    sensitivity = tpr
    specificity = 1-fpr #same as tnr
    roc_auc = auc(fpr, tpr)
    index_of_union = abs(sensitivity-roc_auc) + \
        abs(specificity-roc_auc)
    return index_of_union
scores = custom_metric(y_test, y_pred)
min_score_idx = np.argmin(scores)
print('max_score_idx=%d' % min_score_idx)
print('best threshold=%f'% thresholds[min_score_idx])
print('minimum IU-value at the best threshold=%f' % \
    scores[min_score_idx])

这会产生以下优化值:

max_score_idx=34
best threshold=0.000042
minimum IU-value at the best threshold=0.112514

这标志着我们对经典建模技术的讨论结束。我们现在准备进入研究深度学习领域中的数据不平衡。我们将探讨从上一章学习的一般技术如何适应以增强我们处理不平衡数据时的深度学习模型。

摘要

在本章中,我们深入探讨了 CSL(组合过采样和欠采样),作为一种替代方案。与将所有误分类错误同等对待的数据级别技术不同,CSL 调整模型的成本函数,以考虑不同类别的显著性。它包括类别加权和元学习技术。

类似于scikit-learn、Keras/TensorFlow 和 PyTorch 这样的库支持成本敏感学习。例如,scikit-learn提供了一个class_weight超参数来调整损失计算中的类别权重。XGBoost 有一个scale_pos_weight参数用于平衡正负权重。MetaCost 通过使用袋装和误分类成本矩阵将任何算法转换为成本敏感版本。此外,阈值调整技术可以通过后处理模型预测来增强 F1 分数、精确度和召回率等指标。

使用各种数据采样和 CSL 技术的实验可以帮助确定最佳方法。我们将在第八章中扩展这些概念,即算法级深度学习技术。这标志着我们对经典机器学习模型的讨论结束,我们已经毕业到深入学习技术。在下一章中,我们将简要介绍深度学习概念,并看看不平衡数据集在深度学习世界中可能成为问题。

问题

  1. 在使用本章中使用的相同数据集的同时,将 CSL 技术应用于scikit-learn中的 SVM 模型。使用class_weightsample_weight参数,类似于我们在本章中用于其他模型的方式。比较此模型与本章中已经遇到的模型的表现。

  2. LightGBM 是另一个类似于 XGBoost 的梯度提升框架。在本章使用的数据集上应用成本敏感学习技术到 LightGBM 模型。使用class_weightsample_weight参数,类似于我们在本章中用于其他模型的方式。比较此模型与本章中已经遇到的模型的表现。

  3. AdaCost [10] 是一种结合了提升和 CSL 的 AdaBoost 变体。它通过利用误分类成本来更新连续提升轮次的训练分布。将scikit-learn中的AdaBoostClassifier扩展以实现 AdaCost 算法。比较 AdaCost 与 MetaCost 在本章使用的数据集上的性能。

  4. 调整 XGBoost 模型的超参数,特别是max_depthmax_delta_stepmin_child_weight,使用本章中使用的数据集。调整后,评估加权 XGBoost 模型是否优于非加权版本。

参考文献

  1. P. Turney, 归纳概念学习中的成本类型, 在第 17 届国际机器学习会议的成本敏感学习研讨会论文集,斯坦福大学,加利福尼亚州(2000 年),第 15–21 页。

  2. C. X. Ling 和 V. S. Sheng, 成本敏感学习和类 不平衡问题.

  3. Sheng, V. S.,& Ling, C. X. (2006). 为了使分类器成本敏感的阈值方法. AAAI’06: 第 21 届全国人工智能会议论文集,第 6 卷,第 476–481 页。

  4. 儿童肺炎统计数据 – 联合国儿童基金会数据:data.unicef.org/topic/child-health/pneumonia/.

  5. X. Ling, W. Deng, C. Gu, H. Zhou, C. Li, 和 F. Sun, 在第 26 届国际万维网会议(WWW '17)的伴随会议论文集中,Bing 搜索广告的点击预测模型集成,珀斯,澳大利亚:ACM 出版社,2017 年,第 689–698 页。doi: 10.1145/3041021.3054192.

  6. 机器学习驱动的 Airbnb 体验搜索排名(2019 年),medium.com/airbnb-engineering/machine-learning-powered-search-ranking-of-airbnb-experiences-110b4b1a0789.

  7. P. Domingos, MetaCost: 一种使分类器成本敏感的通用方法,在知识发现与数据挖掘国际会议论文集中,第 155–164 页,1999 年。

  8. 不稳定的学习者. 在:Sammut, C.,Webb, G.I. (eds) 机器学习与数据挖掘百科全书。Springer,波士顿,马萨诸塞州。doi: doi.org/10.1007/978-1-4899-7687-1_866.

  9. W. Fan, S. J. Stolfo, J. Zhang, 和 P. K. Chan, AdaCost: 误分类 成本敏感的 Boosting.

  10. G. I. Karakoulas 和 J. Shawe-Taylor, 优化不平衡 训练集 的分类器*.

  11. P. Viola 和 M. Jones, 使用非对称 AdaBoost 和 检测级联 进行快速和鲁棒的分类.

  12. J. M. Johnson 和 T. M. Khoshgoftaar, 集成学习者的输出阈值和类不平衡大数据, 在 2021 年 IEEE 第 33 届国际人工智能工具会议(ICTAI)中,华盛顿特区,美国:IEEE,2021 年 11 月,第 1449–1454 页。doi: 10.1109/ICTAI52525.2021.00230.

  13. J. M. Johnson 和 T. M. Khoshgoftaar, 在 2019 年第 18 届 IEEE 国际机器学习与应用会议(ICMLA)中,关于类不平衡大数据的深度学习和阈值处理, 佛罗里达州博卡拉顿,美国,2019 年 12 月,第 755–762 页。doi: 10.1109/ICMLA.2019.00134.

  14. F. Provost, 不平衡数据集的机器学习 入门.

  15. M. Buda, A. Maki, 和 M. A. Mazurowski, 卷积神经网络中类别不平衡问题的系统研究, 神经网络,第 106 卷,第 249–259 页,2018 年 10 月,doi: 10.1016/j.neunet.2018.07.011.

  16. 使用丰富图像和文本数据大规模分类产品(2021 年),shopify.engineering/using-rich-image-text-data-categorize-products.

  17. W. J. Youden, 诊断测试评分指数,癌症,第 3 卷,第 1 期,第 32–35 页,1950 年,doi: 10.1002/1097-0142(1950)3:1<32::AID-CNCR2820030106>3.0.CO;2-3.

  18. I. Unal, 在 ROC 分析中定义最佳切割点值:一种替代方法,计算与数学在医学中的应用,第 2017 卷,第 1–14 页,2017 年,doi: 10.1155/2017/3762651.

  19. X. Ling, W. Deng, C. Gu, H. Zhou, C. Li, 和 F. Sun, 用于 Bing 搜索广告点击预测的模型集成, 在第 26 届国际万维网大会伴随会议——WWW '17 伴随会议论文集中,珀斯,澳大利亚:ACM 出版社,2017 年,第 689–698 页。doi: 10.1145/3041021.3054192.