Python-流式数据的机器学习-全-

Python 流式数据的机器学习(全)

原文:annas-archive.org/md5/8b1f81528aade41a4df56df5f294e177

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

流数据是数据科学和机器学习领域需要关注的新顶级技术。随着商业需求变得更加苛刻,许多用例需要实时分析和实时机器学习。本书将使你能够跟上流数据分析的步伐,并强烈关注将机器学习和其他分析适应流数据的情况。

你将首先了解流和实时机器学习的架构。然后,你将查看如 River 等流数据的最新框架。

你将了解流数据的各种工业用例,例如在线异常检测。然后,你将深入了解挑战以及如何减轻这些挑战。接着,你将学习最佳实践,这将帮助你利用流数据生成实时洞察。

完成本书后,你将自信地使用流数据在你的机器学习模型中。

本书面向的对象

对于有机器学习基础、注重实践和技术、并希望通过现代技术的实际案例学习如何将机器学习应用于流数据的数据科学家和机器学习工程师,本书将大有裨益。你需要了解基本的 Python 和机器学习概念,但不需要流数据方面的先验知识。

本书涵盖的内容

第一章**,流数据简介,解释了什么是流数据以及它与批量数据的区别。本章还解释了我们应预期遇到的挑战以及使用流数据的优势。

第二章**,流和实时机器学习架构,描述了可以用来设置流的多种架构,以及如何利用它们。

第三章**,流数据上的数据分析,探讨了流数据上的数据分析,包括实时洞察、实时描述性统计、实时可视化以及基本警报系统。

第四章**,使用 River 进行在线学习,涵盖了在线学习的核心概念,并介绍了 River 库,这是流数据的基本组成部分。

第五章**,在线异常检测,涵盖了在线异常检测,解释了它的用途,并提供了一个涉及构建用于检测流数据中异常的程序的实际案例。

第六章**,在线分类,涵盖了在线分类,解释了它的用途,并提供了一个涉及构建用于对流数据进行分类的程序的实际案例。

第七章**,在线回归,涵盖了在线回归,它的用途,并提供了一个涉及构建用于检测流数据中回归的程序的实际用例。

第八章**,强化学习,向您介绍强化学习。我们将探讨一些关键算法,并使用 Python 探索一些实际用例。

第九章**,漂移和漂移检测,专注于帮助我们理解在线学习中的漂移,并学习如何构建检测漂移的解决方案。

第十章**,特征转换和缩放,展示了如何构建一个与实时和流数据一起工作的特征转换管道。

第十一章**,灾难性遗忘,探讨了什么是灾难性遗忘,并展示了我们如何通过示例用例来处理它。

第十二章**,结论和最佳实践,作为本书的回顾,并综合了本书中探讨的所有概念,以便我们按需复习和重访。

为了充分利用这本书

为了跟随这本书,您可以使用 Google Colab、Kaggle Notebooks 或带有 Python 3 的您自己的本地 Jupyter Notebook 环境。此外,还需要一个(免费)AWS 账户来完成少数几个练习。

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

下载示例代码文件

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

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

下载彩色图像

我们还提供了一份包含本书中使用的截图和图表的彩色图像 PDF 文件。您可以从这里下载:packt.link/6rZ0m

使用的约定

在这本书中使用了多种文本约定。

文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“这里没有 predict_many 函数,因此有必要通过重复使用 predict_one 进行循环。”

代码块设置如下:

def self_made_decision_tree(observation): 
    if observation.can_speak: 
        if not observation.has_feathers: 
            return 'human'     
    return 'not human'  
for i,row in data.iterrows(): 
    print(self_made_decision_tree(row)) 

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

from sklearn.datasets import make_blobs 
X,y=make_blobs(shuffle=True,centers=2,n_samples=2000) 

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“从管理面板中选择系统信息。”

小贴士或重要注意事项

看起来像这样。

联系我们

我们始终欢迎读者的反馈。

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

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

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

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

分享您的想法

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

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

第一部分:流数据简介和核心概念

在本书的引言部分,我们将被介绍到围绕流数据的基礎概念和原理。我们将探索可以用于实现机器学习流数据的各种架构。最后,我们将学习如何对流数据进行数据分析,以及各种其他功能。

本节包括以下章节:

  • 第一章**,流数据简介

  • 第二章**,流和实时机器学习的架构

  • 第三章**,流数据上的数据分析

第一章:第一章:流式数据简介

流式分析是数据科学中的新兴热门话题之一。它提出了一种替代框架,即更标准的批量处理,在这种框架中,我们不再处理固定时间处理的整个数据集,而是直接在接收时处理每个数据点。

这种新的范式对数据工程有重要的影响,因为它需要更加稳健的,尤其是,更快的数据摄取管道。它还要求在数据分析和机器学习方面进行重大变革。

直到最近,机器学习和数据分析的方法和算法主要是为了在整个数据集上工作。现在,随着流式处理成为热门话题,越来越常见的是看到整个数据集不再存在的用例。当连续的数据流被摄取到数据存储源时,没有自然的时间点重新启动分析批量作业。

流式分析流式机器学习模型是专门为与流式数据源一起工作而设计的模型。解决方案的一部分,例如,在于更新。流式分析和机器学习需要不断更新,因为新的数据正在被接收。在更新时,你可能还希望忘记很久以前的数据。

从批量分析转向流式分析引入的这些问题需要不同的分析方法和机器学习方法。本书将为你打下在连续流接收的数据上进行数据分析和机器学习的起点。

在第一章中,你将更深入地了解流式数据和批量数据之间的区别。你将看到一些示例用例,展示为什么与流式数据工作比转换回批量数据更重要。你还将开始使用第一个 Python 示例,以了解你在这本书中将进行的工作类型。

在后面的章节中,你将看到更多关于架构的背景概念,然后,你将进入多个数据科学和数据分析用例,了解它们如何适应新的流式处理范式。

在本章中,你将发现以下主题:

  • 数据科学简史

  • 与流式数据一起工作

  • 实时数据格式和 Python 中导入示例数据集

技术要求

你可以在以下链接的 GitHub 上找到这本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下步骤:

  1. 前往仓库链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

图 1.1 – GitHub 界面示例

图 1.1 – GitHub 界面示例

图 1.1 – GitHub 界面示例

当你下载 ZIP 文件时,你可以在本地环境中解压它,然后你将能够通过你偏好的 Python 编辑器访问代码。

设置 Python 环境

要跟随这本书,你可以下载存储库中的代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda (www.anaconda.com/products/individual),它包含 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含 Spyder 和 VSCode,用于编辑脚本和程序。

如果你安装 Python 或相关程序遇到困难,你可以查看 Google Colab (colab.research.google.com/) 或 Kaggle Notebooks (www.kaggle.com/code),这两个都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码将一般使用 Colab 和 Kaggle Notebooks,Python 版本为 3.7.13,你可以设置自己的环境来模拟这种情况。

数据科学简史

在过去几年里,新技术领域迅速占领了世界的许多部分。机器学习、人工智能和数据科学是进入我们日常生活的新领域,无论是在我们的个人生活中还是在我们的专业生活中。

数据科学家今天所研究的主题并不新颖。该领域的绝对基础是数学和统计学,这两个领域已经存在了几个世纪。例如,最小二乘回归首次发表于 1805 年。随着时间的推移,数学家和统计学家一直在努力寻找其他方法和模型。

在以下时间线上,你可以看到最近技术繁荣是如何发生的。在 1600 年和 1700 年,非常聪明的人已经为我们今天在统计学和数学中做的事情奠定了基础。然而,直到计算能力的发明和普及,这个领域才变得繁荣起来。

![Figure 1.2 – A timeline of the history of data]

![img/B18335_01_02.jpg]

图 1.2 – 数据历史的时间线

个人电脑和互联网的普及是数据科学今天受欢迎的重要原因。几乎每个人都有一台性能足够强大的电脑,可以进行相当复杂的机器学习。这极大地帮助了计算机素养,而且,在线文档的可用性也是学习的一个大助推器。

大数据工具如 HadoopSpark 的可用性也是数据科学普及的重要部分,因为它们允许从业者处理比以往任何时候都能想象的更大的数据集。

最后,云计算正在让世界各地的数据科学家以低廉的价格访问非常强大的硬件。特别是对于大数据工具,所需的硬件仍然定价得让大多数学生无法购买用于训练目的。云计算为许多人提供了这些用例的访问权限。

在这本书中,你将学习如何处理流式数据。记住数据科学简短的历史很重要,因为流式数据是那些因需要困难的硬件和设置要求而受到不利影响的技术之一。流式数据目前在许多领域迅速获得人气,并有可能在接下来的时期成为一大热门。现在让我们更深入地了解流式数据的定义。

处理流式数据

流式数据是流式传输的数据。你可能从可以流式传输视频的在线视频服务中了解到流式传输这个术语。当你这样做时,视频流式传输服务会继续向你发送视频的下一部分,同时你已经在观看视频的第一部分。

在处理流式数据时,概念是相同的。数据格式不一定是视频,可以是任何对你的用例有用的数据类型。最直观的例子之一是工业生产线,其中你有来自传感器的连续测量。只要你的生产线不停歇,你就会继续生成测量数据。我们将查看以下数据流过程的概述:

图 1.3 – 数据流过程

图 1.3 – 数据流过程

重要的概念是你有一个需要实时处理的数据连续流。你不能等到生产线停止后再进行分析,因为你需要立即检测潜在的问题。

流式数据与批量数据

流式数据通常不是新数据科学家倾向于开始使用的第一个用例。通常首先介绍的问题是批量用例。批量数据与流式数据相反,因为它在阶段中工作:你收集一系列数据,然后处理一系列数据。

如果你将流式数据比作在线流视频,那么你可以将批量数据比作先下载整个视频,然后在下载完成后观看。从分析的角度来看,这意味着当数据生成过程完成时,你会得到一系列数据的分析,而不是在出现问题时才进行分析。

对于某些用例来说,这并不是问题。然而,你可以理解,在那些快速分析可以产生影响的用例中,流式可以提供巨大的附加价值。它也在数据以流式方式摄取的用例中具有附加价值,这变得越来越普遍。在实践中,许多本可以通过流式处理获得附加价值的用例仍然使用批处理方法解决,仅仅因为这些方法更为人所熟知且更广泛。

下面的概述显示了批处理过程:

图 1.4 – 批处理过程

图 1.4 – 批处理过程

流式数据的优点

现在让我们看看使用流式分析而不是其他方法的一些优点。

数据生成过程是实时的

建立流式数据分析而不是批处理系统的第一个优点是许多数据生成过程实际上是实时的。你将在后面的例子中看到一些用例,但一般来说,数据收集很少是批量进行的。

尽管我们大多数人习惯于在实时数据生成系统周围构建批处理系统,但直接构建流式分析通常更有意义。

当然,批处理分析和流式分析可以共存。然而,向流式分析服务中添加批处理处理通常比向为批处理设计的系统中添加流式功能要容易得多。从流式开始是最有意义的。

实时洞察有价值

在设计数据科学解决方案时,流式处理并不总是首先考虑的。然而,当解决方案或工具是实时构建的,实时功能很少不被欣赏。

当今许多分析解决方案都是实时构建的,工具也是可用的。在许多问题中,实时信息将在某个时候被使用。也许它不会从一开始就被使用,但当异常发生时,你会发现立即进行数据分析而不是等到下一个小时或第二天早上,这将带来巨大的竞争优势。

流式分析成功实施的例子

让我们谈谈一些成功实施实时分析的公司例子。第一个例子是壳牌。他们能够在加油站对他们的安全摄像头实施实时分析。一个自动化的实时机器学习流程能够检测到人们是否在吸烟。

另一个例子是连接式运动设备中传感器数据的运用。通过实时测量心率和其他关键绩效指标(KPIs),它们能够在你的身体出现任何问题时提醒你。

当然,像 Facebook 和 Twitter 这样的大型玩家也在实时分析大量数据,例如在检测假新闻或不良内容时。流数据分析有许多成功的用例,同时,流数据也带来了一些常见的挑战。现在让我们来看看它们。

流数据的挑战

流数据分析目前不如批量数据分析普及。尽管这种情况正在慢慢改变,但在处理流数据时了解挑战所在是很有好处的。

流数据分析的知识

流数据分析不如普及的一个简单原因是知识和技能问题。在学校的课程中通常不会教授设置流数据分析,而且绝对不是作为首选方法来教授。互联网上可用的资源也较少,以帮助人们入门。由于机器学习和批量处理分析的资源更多,而且批量方法不适用于流数据,因此人们倾向于从数据科学的批量应用开始。

理解架构

在处理流数据时遇到的第二个困难是架构问题。尽管一些数据科学从业者对架构、数据工程和 DevOps 有所了解,但这并不总是如此。为了设置流数据分析的验证或最小可行产品MVP),所有这些技能都是必需的。对于批量处理,通常只需要使用脚本即可。

架构困难是流数据固有的,因为它需要与实时进程合作,这些进程会将单独收集的记录发送到实时更新的分析处理过程。如果没有能够处理这种需求的架构,那么开始流数据分析就没有太多意义。

财务障碍

与流数据合作时的另一个挑战是财务方面。尽管从长远来看,使用流数据不一定更昂贵,但建立启动所需的基础设施可能会更昂贵。在本地开发者的 PC 上为 MVP 工作不太可能成功,因为数据需要实时处理。

运行时问题的风险

实时进程也有更大的运行时问题风险。在构建软件时,错误和故障是不可避免的。如果你正在进行日常批量处理,你可能能够修复进程,重新运行失败的批量,并解决问题。

如果流工具出现故障,可能会丢失数据。由于数据应在实时摄入,因此在你进程暂停期间产生的数据可能无法恢复。如果你的进程非常重要,你需要日夜进行广泛的监控,并在将解决方案推送到生产之前进行更多的质量检查。当然,这在批量过程中也很重要,但在流数据中更为重要。

小型分析(较少的可用方法)

流式分析的最后一个挑战是,常见的方法通常首先是为批量数据开发的。目前有许多针对实时和流式数据分析的解决方案,但仍然没有批量数据那么多。

此外,由于流式分析必须非常快速地完成以尊重实时交付,流式用例往往最终会采用较少有趣的分析方法,并停留在描述性或基本分析的基本水平。

如何开始使用流式数据

对于公司来说,开始使用流式数据的第一个步骤通常是实施简单的应用程序,这些应用程序收集实时数据并使实时数据实时可用。常见的起始用例包括日志数据、网站访问数据或传感器数据。

下一个步骤通常是在实时数据源之上构建报告工具。你可以考虑那些实时更新的 KPI 仪表板,或者基于业务规则的高或低阈值值的小型简单警报工具。

当这样的系统建立起来后,这将为替换那些业务规则或在其之上添加新规则提供途径。你可以考虑包括用于异常检测的实时机器学习在内的更高级分析工具。

最复杂的步骤是在你的实时机器学习和流程之间添加自动反馈循环。毕竟,如果有可能自动化并改进决策,就没有理由仅仅停留在分析业务洞察上。

流式数据的常见用例

让我们看看一些最常用的流式数据用例,以便你能更好地了解可以从流式技术中受益的用例。这将会涵盖三个相对容易接触到的用例,但当然,还有很多更多。

传感器数据和异常检测

流式数据的常见用例之一是传感器数据的分析。传感器数据可以出现在多种用例中,例如工业生产线和物联网用例。当公司决定收集传感器数据时,通常会在实时进行处理。

对于生产线来说,实时检测异常具有很大的价值。当异常发生过多时,生产线可以关闭,或者问题可以在大量次品交付之前得到解决。

可以在这里找到一个关于监测艺术品湿度流式分析的示例:azure.github.io/iot-workshop-asset-tracking/step-003-anomaly-detection/

财务和回归预测

财务数据是流式数据的另一个很好的用例。例如,在股票交易的世界里,时机很重要。你越快能够检测到股市的上涨或下跌趋势,交易者(或算法)就越快能够通过买卖股票来做出反应并赚钱。

在以下由 K.S Umadevi 等人(2018)撰写的论文中描述了一个很好的例子:ieeexplore.ieee.org/document/8554561

网站和分类的点击流

网站或应用是实时洞察的第三个常见用例。如果你能够实时跟踪和分析你的访客,你可以在网站上为他们提供个性化的体验。通过提出与网站访客匹配的产品或服务,你可以增加你的在线销售额。

由 Ramanna Hanamanthrao 和 S Thejaswini(2017)撰写的以下论文为将此技术应用于点击流数据提供了一个很好的用例:ieeexplore.ieee.org/abstract/document/8256978

流与大数据

理解你可能会遇到的不同流定义非常重要。一个需要区分的是流与大数据。一些定义将流主要考虑在大数据(Hadoop/Spark)环境中,而其他定义则不是。

流解决方案通常具有大量数据,大数据解决方案可能是合适的选择。然而,结合精心选择的硬件架构的其他技术也可能能够实时进行数据分析,因此可以在不使用大数据技术的情况下构建流解决方案。

流数据与实时推理

模型的实时推理通常通过 API 构建,并使其可用。由于我们将流定义为无批次的实时数据分析,这种实时预测可以被认为是流。你将在后面的章节中了解更多关于实时架构的内容。

实时数据格式和 Python 中导入示例数据集

为了完成这一章节,让我们看看如何在实践中表示流数据。毕竟,在构建分析时,我们经常需要实现测试用例和示例数据集。

在 Python 中表示流数据的最简单方法就是创建一个包含数据的可迭代对象,并构建你的分析函数以与可迭代对象一起工作。

以下代码使用 pandas 创建一个 DataFrame。有两个列,温度和 pH:

代码块 1-1

import pandas as pd
data_batch = pd.DataFrame({
'temperature': [10, 11, 10, 11, 12, 11, 10, 9, 10, 11, 12, 11, 9, 12, 11],
    ‹pH›: [5, 5.5, 6, 5, 4.5, 5, 4.5, 5, 4.5, 5, 4, 4.5, 5, 4.5, 6]
})
print(data_batch)

当显示 DataFrame 时,它将如下所示。pH 大约在 4.5/5,但有时会更高。温度通常在 10 或 11 左右。

图 1.5 – 结果 DataFrame

图 1.5 – 结果 DataFrame

这个数据集是一个批量数据集;毕竟,你同时拥有所有行(观测值)。现在,让我们看看如何通过使其可迭代来将这个数据集转换为流数据集。

你可以通过迭代数据的行来实现这一点。在这样做的时候,你设置一个代码结构,允许你逐个添加更多构建块到这个代码中。当你的开发完成时,你将能够在实时流上使用你的代码,而不是在 DataFrame 迭代上。

以下代码遍历 DataFrame 的行并将行转换为 JSON 格式。这是一种在不同系统之间通信的非常常见的格式。观测的 JSON 包含温度值和 pH 值。它们如下打印出来:

代码块 1-2

data_iterable = data_batch.iterrows()
for i,new_datapoint in data_iterable:
  print(new_datapoint.to_json())

在运行此代码后,你应该获得如下所示的打印输出:

图 1.6 – 最终的打印输出

图 1.6 – 最终的打印输出

让我们现在定义一个超级简单的流式数据分析示例。以下代码块中定义的函数将在温度低于 10 时打印警报:

代码块 1-3

def super_simple_alert(datapoint):
  if datapoint[‹temperature›] < 10:
    print('this is a real time alert. temp too low')

你现在可以通过在每一个数据点上调用警报测试,简单地将在你的模拟流式过程中添加此警报。你可以使用以下代码来完成此操作:

代码块 1-4

data_iterable = data_batch.iterrows()
for i,new_datapoint in data_iterable:
  print(new_datapoint.to_json())
  super_simple_alert(new_datapoint)

当执行此代码时,你会注意到当温度低于 10 时,将会立即发出警报:

图 1.7 – 带有温度警报的最终打印输出

图 1.7 – 带有温度警报的最终打印输出

此警报仅适用于温度,但你很容易添加相同类型的 pH 警报。以下代码显示了如何做到这一点。警报函数可以更新以包括第二个业务规则,如下所示:

代码块 1-5

def super_simple_alert(datapoint):
  if datapoint[‹temperature›] < 10:
    print('this is a real time alert. temp too low')
  if datapoint[‹pH›] > 5.5:
    print('this is a real time alert. pH too high')

执行函数的方式仍然完全相同:

代码块 1-6

data_iterable = data_batch.iterrows()
for i,new_datapoint in data_iterable:
  print(new_datapoint.to_json())
  super_simple_alert(new_datapoint)

在执行示例流式数据的过程中,你将看到几个警报被触发,如下所示:

![图 1.8 – 带有温度和 pH 警报的最终打印输出图片 B18335_01_08.jpg

图 1.8 – 带有温度和 pH 警报的最终打印输出

在流式数据中,你必须在没有看到完整数据的情况下,仅基于过去接收到的那些数据点做出决定。这意味着需要采用不同的方法来重新开发与批量处理算法类似的算法。

在整本书中,你将发现适用于流式数据的方法。正如你可能理解的,困难之处在于,统计方法通常是为了使用所有数据来计算事物而开发的。

摘要

在本关于流式数据和流式分析的开篇章节中,你首先看到了一些关于流式数据是什么的定义,以及它与批量数据处理是如何相对立的。在流式数据中,你需要处理连续的数据流,而更传统的(批量)数据科学解决方案需要适应,以便与这种更新、要求更高的数据处理方法一起工作。

您已经看到了许多示例用例,现在您应该明白,对于企业和高级技术用例来说,在实时而不是等待固定时刻进行数据科学和数据分析可以带来更大的价值。实时洞察可以改变游戏规则,而自主机器学习解决方案通常需要实时决策能力。

您已经看到了一个例子,其中创建了一个数据流并开发了一个简单的实时警报系统。在下一章中,您将更深入地了解多种流处理解决方案。在实践中,数据科学家和分析师通常不会负责实施流数据摄取,但他们将受到这些系统限制的约束。因此,了解流和实时架构非常重要:这将是下一章的目标。

进一步阅读

第二章:第二章:流式和实时机器学习的架构

流式架构是实时机器学习和流式分析解决方案的一个基本组成部分。即使你有一个可以实时处理数据、更新并立即响应的模型或其他分析工具,如果没有支持你解决方案的架构,这将是徒劳的。

第一个重要的考虑因素是确保你的模型和分析可以在每个数据点上运行;需要有一个更新函数和/或一个预测函数,可以更新系统接收到的每个新观测值上的解决方案。

对于实时和流式架构,另一个重要的考虑因素是数据导入:如何确保数据可以基于每个观测值接收,而不是更传统的以每日数据库更新为例的批量方法。

此外,了解如何使不同的软件系统进行通信也很重要。例如,数据必须从你的数据生成过程非常快速地流动,可能经过一个数据存储解决方案、一个数据质量工具或一个安全层,然后被你的分析程序接收。分析程序将完成其工作并将结果发送回源端,或者可能将处理过的数据点转发到可视化解决方案、警报系统或类似系统。

在本章中,你将了解流式和实时机器学习的架构介绍。本书的核心重点将始终集中在管道的分析和机器学习部分。本章的目标是给你足够的元素来想象和实现初步的工作架构,而一些高度专业化的性能、可用性和安全性部分将被省略。

本章涵盖了以下主题:

  1. 将你的分析定义为函数

  2. 理解微服务架构

  3. 通过 API 在服务之间进行通信

  4. 揭秘 HTTP 协议

  5. 在 AWS 上构建简单的 API

  6. 实时流的大数据工具

  7. 实时调用大数据环境

技术要求

你可以在以下链接的 GitHub 上找到这本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下步骤:

  1. 前往仓库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件时,你需要在本地环境中解压缩它,然后你将能够通过你偏好的 Python 编辑器访问代码。

Python 环境

要跟随这本书的内容,你可以从仓库中下载代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda (www.anaconda.com/products/individual),它包含 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含 Spyder 和 VSCode,用于编辑脚本和程序。

如果你在机器上安装 Python 或相关程序时遇到困难,你可以查看 Google Colab (colab.research.google.com/) 或 Kaggle Notebooks (www.kaggle.com/code),这两个平台都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码通常使用 Colab 和 Kaggle Notebooks,Python 版本为 3.7.13,你可以设置自己的环境来模拟这种情况。

将你的分析定义为函数

为了开始学习架构,让我们从零开始构建一个想法,使用必要的不同构建块来制作一个最小的工作产品。

对于这一点,你需要首先了解你想要执行的类型实时分析。

现在,让我们继续使用上一章的相同示例:一个实时业务规则,当我们的生产线温度或酸度超出可接受范围时打印警报。

在上一章中,这个警报被编码如下:

代码块 2-1

def super_simple_alert(datapoint):
  if datapoint['temperature'] < 10:
    print('this is a real time alert. temp too low')
  if datapoint['pH'] > 5.5:
    print('this is a real time alert. pH too high')

在上一章中,你通过遍历 DataFrame 来测试这段代码。实际上,你将始终需要有一个架构的想法,这样你的代码才能从数据生成过程中实时接收数据。这个构建块将在本章中介绍。

在下面的示意图中,你将看到我们流式解决方案的高级架构图:

图 2.1 – 流式解决方案的高级架构图

图 2.1 – 流式解决方案的高级架构图

在这个示意图中,你可以清楚地看到编写代码将为你提供解决方案的一些关键组件。然而,你需要围绕这些组件构建一个架构,使解决方案变得生动。示例实现中仍缺少较暗的部分。

虽然本书的目标不是提供关于架构的全面深入课程,但你在这里会发现一些工具和构建块,这将使你能够交付一个 MVP 实时用例。为了使你的构建块整洁有序,你需要为你的解决方案选择一个架构结构。微服务是一种架构模式,它将允许你构建干净、小巧的构建块,并使它们相互通信。

理解微服务架构

在处理架构时理解微服务的概念非常重要。尽管有其他方法来架构软件项目,但微服务因其良好的原因而非常受欢迎。它们帮助团队保持灵活和高效,并有助于保持软件的灵活性和清晰结构。

微服务的理念体现在其名称中:软件被表示为许多独立操作的小服务。在查看整体架构时,每个微服务都在一个小小的、黑盒中,具有明确定义的输入和输出。会放置一些流程来在正确的时间调用正确的黑盒。

微服务架构是松散耦合的。这意味着不同微服务之间没有固定的通信。相反,每个微服务可以被任何其他服务或代码调用,也可以不被调用。

如果需要对某个微服务进行更改,更改的范围相对局部,因此不会影响其他微服务。由于输入和输出是预定义的,这也帮助保持程序的基础结构有序,而不会以任何方式固定。

为了允许不同的微服务进行通信,一个常用的解决方案是使用应用程序编程接口API)。现在让我们深入了解这些内容。

通过 API 在服务之间进行通信

微服务架构中的一个重要组成部分是 API 的使用。API 是一个允许您将两个微服务(或其他代码片段)连接起来的部分。

API 与网站非常相似。就像网站一样,API 是在类似网站的链接或 IP 地址后面构建的。当您访问一个网站时,网站的服务器会向您发送代表网站的代码。然后您的互联网浏览器解释此代码并显示网页。

当您调用 API 时,API 将接收您的请求。请求触发服务器上您的代码运行,并生成一个发送回您的响应。如果出现问题(可能是您的请求不符合预期或发生错误),您可能不会收到任何响应,或者收到如“请求未授权”或“内部服务器错误”之类的错误代码。

下一个图显示了涵盖此内容的流程图。计算机或用户发送 HTTP 请求,API 服务器根据在 API 服务器上运行的代码发送响应:

图 2.2 – 流式解决方案的高级架构图

图 2.2 – 流式解决方案的高级架构图

您可以使用许多不同的工具调用 API。有时,您甚至可以使用您的互联网浏览器,否则,像 cURL 这样的工具可以在命令行上完成这项工作。您可以使用 Postman 或 Insomnia 等工具通过用户界面调用 API。所有通信都遵循固定的规则和实践,这些规则和实践共同构成了 HTTP 协议,我们将在下一节中探讨。

揭秘 HTTP 协议

服务(或网站)之间的交互使用 HTTP 协议。当与 API 和构建通信的微服务一起工作时,了解 HTTP 协议的基本知识非常重要。

最重要的是要知道如何发送和格式化请求和响应。

GET 请求

最简单的 HTTP 请求是GET请求。当您需要从服务器或服务中获取某些内容时,您会使用它。例如,当访问一个网站时,您的浏览器会向网站的 IP 地址发送GET请求以获取网站的布局代码。

使用以下代码可以从 Python 发送GET请求:

代码块 2-2

import requests
import json
response = requests.get('http://www.google.com')
print(response.status_code)
print(response.text)

此代码使用 Python 中的requests库向谷歌主页发送GET请求。从技术上讲,这与在您的互联网浏览器中访问谷歌主页的过程相同。您将获得所有代码,这些代码是您的网络浏览器显示谷歌主页所需的。尽管你们中的许多人非常熟悉浏览器中谷歌主页的外观,但在这段代码响应中它却不太容易辨认。重要的是要理解,这实际上是完全相同的东西,只是格式不同。

POST 请求

POST请求是您将非常经常遇到的另一种请求。它允许您在请求中发送一些数据。这在分析 API 中尤其必要,因为分析很可能会在这个数据上发生。通过在POST请求的主体中添加数据,您确保您的分析代码收到了您的数据。

Python 中的语法将类似于以下代码块。目前,这段代码不起作用,因为您还没有构建一个能够处理这些数据的服务器。但是,请记住,POST请求允许您将数据点发送到 API,目的是获取响应:

代码块 2-3

import requests
import json
data = {'temperature': 10, 'pH': 5.5}
response = requests.post('http://www.example.com',data=data)
print(response.status_code)
print(response.text)

系统间通信的 JSON 格式

服务器之间交互的最常见格式是JavaScript 对象表示法JSON)格式。它是一种数据类型,在 Python 中非常类似于字典格式。实际上,它是一个被括号包围的键值对象。

一个 JSON 有效载荷的示例如下:

代码块 2-4

{
     'name': 'your name',
     'address': 'your address',
     'age': 'your age'
}

这种数据格式相对容易理解,并且非常常用。因此,了解它是如何工作的非常重要。您将在本章后面的内容中看到它的使用。

RESTful API

虽然这本书不涉及 API 开发,但了解一些指导方针和最佳实践将是有用的。最常用的 API 结构是表示状态转移REST)API。

REST API 的工作方式与其他 API 类似,但它遵循一组特定的风格规则,使其成为可识别的 REST API,也称为 RESTful API。

REST API 中有六个指导约束:

  • 客户端-服务器架构

  • 无状态

  • 缓存性

  • 分层系统

  • 按需代码(可选)

  • 统一接口

如果你想在这个主题上更进一步,本章末尾提供了一些额外的阅读资源。现在我们已经了解了 HTTP 协议,让我们在 Amazon Web ServicesAWS)上构建一个 API。

在 AWS 上构建简单的 API

为了做一些实际的事情,让我们在 AWS 上构建一个非常简单的 API。这将帮助你理解不同的服务如何相互通信。它也可以作为测试本书中其他示例的良好测试环境。

你将使用 AWS 框架的以下组件。

AWS 中的 API Gateway

这是一个为你处理 API 请求的 AWS 服务。你指定你期望接收的请求类型,并指定在接收到请求时应采取的操作。当你使用 API Gateway 构建 API 时,这将自动生成一个 IP 地址或链接,你可以将你的 API 请求发送到那里。

AWS 中的 Lambda

Lambda 是一个无服务器代码执行环境。这意味着你可以编写 Python 代码,将其连接到 API Gateway,而无需考虑如何设置服务器、防火墙等。这对于解耦系统来说很棒,而且对于许多实时系统来说足够快。

本地机器上的数据生成过程

作为最后一个组件,你将在 Python 中构建一个独立的数据生成过程。你可以在笔记本中执行此代码。每次生成新的数据点时,代码将调用 API 并与分析服务通信,如果需要,则发送警报。

以下图示展示了该架构的概览:

图 2.3 – AWS 的详细架构图

图 2.3 – AWS 的详细架构图

实现示例

为了实现示例,我们将使用以下分步说明。如果你已经有了 AWS 账户,你可以跳过 步骤 0

步骤 0 – 在 AWS 上创建账户

如果你还没有 AWS 账户,创建一个账户很容易。你需要用信用卡设置它,但我们在这里使用的所有服务都有免费层。只要你测试结束后关闭资源,你不太可能产生任何费用。然而,要小心,因为错误是会发生的,如果你在 AWS 上使用大量资源,你最终可能会付费。

要设置账户,你可以简单地遵循 aws.amazon.com 上的步骤。

步骤 1 – 设置 Lambda 函数

在接收到 POST 请求后,必须调用 Lambda 函数来执行我们的警报并发送响应。

服务 菜单中选择 Lambda 并点击 创建函数。你将看到以下屏幕:

图 2.4 – 创建 Lambda 函数

图 2.4 – 创建 Lambda 函数

确保选择 Python 并给你的函数起一个合适的名字。

当你完成函数的创建后,就是时候编写代码了。你可以使用以下代码:

代码块 2-5

import json
def super_simple_alert(datapoint):    
    answer = ''
    if datapoint['temperature'] < 10:
        answer += 'temp too low ' 
    if datapoint['pH'] > 5.5:
        answer += 'pH too high '
    if answer == '':
        answer = 'all good'
    return answer
def lambda_handler(event, context):
    answer = super_simple_alert(event)
    return {
        'statusCode': 200,
        'body': json.dumps({'status': answer}),
    }

此代码有两个函数。super_simple_alert函数接受一个datapoint并返回一个答案(一个字符串格式的警报)。lambda_handler函数是处理传入 API 调用的代码。事件包含datapoint,因此事件被传递到super_simple_alert函数以分析是否应该启动警报。这存储在answer变量中。最后,lambda_handler函数返回一个包含状态码200和包含答案的 Python 字典。

窗口现在应该看起来如下:

![图 2.5 – Lambda 函数窗口

![img/B18335_02_05.jpg]

图 2.5 – Lambda 函数窗口

第 2 步 – 设置 API 网关

作为第一步,让我们设置 API 网关以接收一个POST请求。这个POST请求将包含一个正文,其中包含温度和 pH 值的 JSON,就像在警报示例中一样。

要设置 API 网关,您必须转到API 网关菜单,该菜单可通过服务菜单访问。管理控制台看起来如下:

![图 2.6 – AWS 管理控制台

![img/B18335_02_06.jpg]

图 2.6 – AWS 管理控制台

您最终应该出现在API 网关菜单中,如下所示:

![图 2.7 – API 网关菜单

![img/B18335_02_07.jpg]

图 2.7 – API 网关菜单

当您在API 网关菜单中时,您可以转到创建 API以设置您的第一个 API。

创建 API中,执行以下步骤:

  1. 选择REST API

  2. 选择REST协议。

  3. 将 API 作为一个新的 API 构建。

  4. 创建一个 API 名称,例如,streamingAPI

您将获得一个空的 API 配置菜单,如下所示:

![图 2.8 – 在 API 网关中添加方法

![img/B18335_02_08.jpg]

图 2.8 – 在 API 网关中添加方法

我们想添加一个POST方法,所以转到POST方法。以下菜单将出现以设置POST方法:

![图 2.9 – POST 设置

![img/B18335_02_09.jpg]

图 2.9 – POST 设置

第 3 步 – 部署 API

仍然在 API 网关菜单中,点击test以部署到。您可以使用此阶段的默认设置,但重要的是要记住这里顶部的 URL,以便能够从您的数据生成过程中调用您的 API。您需要设置如下所示的设置:

![图 2.10 – API 的更多详细信息

![img/B18335_02_010.jpg]

图 2.10 – API 的更多详细信息

第 4 步 – 从另一个 Python 环境调用您的 API

现在,您可以从另一个 Python 环境调用您的 API,例如您自己的电脑上的笔记本,或者从 Google Colab 笔记本中调用。

您可以使用以下代码来完成此操作:

代码块 2-6

import requests
import json
data = {'temperature': 8, 'pH': 4}
response = requests.post('YOUR_URL', data = json.dumps(data))
print(json.loads(response.text))

您将获得以下答案:

代码块 2-7

{'statusCode': 200, 'body': '{"status": "temp too low "}'}

现在,您可以想象一个实时数据生成过程将如何简单地在每个新数据点上调用 API,并且警报将立即生成!

更多架构考虑因素

虽然这是构建 API 的第一次尝试,但您应该意识到,当您想要以可靠和安全的方式构建它时,还有更多需要考虑。数据科学和软件工程是不同的工作,学习管理 API 从 A 到 Z 所需的所有技能需要时间。一般来说,这不会是数据科学家的工作。

在本例中没有涵盖的一些事情如下:

  • 性能:扩展、负载均衡和延迟

  • DDoS 攻击

  • 安全性和黑客攻击

  • API 调用的财务方面

  • 依赖云服务提供商与云服务提供商无关

在本章末尾,有一些进一步阅读的资源,您可以查看。

其他 AWS 服务以及具有相同功能的一般服务

当前示例使用了 API Gateway 和 Lambda 函数来构建 API。这种方法的优势在于访问和设置的简便性,这使得它非常适合作为本书中的展示方法。然而,您应该意识到还有许多其他工具和技术可以用于构建 API。

AWS 是最常用的云服务提供商之一,在 AWS 上可以完成的大部分工作也可以在其他云服务提供商的平台上完成。其他大型玩家的例子包括谷歌的 GCP 和微软的 Azure。即使在 AWS 上,也有很多替代方案。

您也可以在本地环境中构建 API。在这样做的时候,您将再次拥有大量的工具和提供商选择。现在您已经看到了如何使用标准的 Python 编程和微服务方法来构建 API,接下来您将看到一些在大数据环境中使用的替代方案。大数据环境通常具有更陡峭的学习曲线,并且通常是为特定用例而设计的,但它们在处理高容量和高速度时可以非常强大且绝对必要。

实时流式大数据工具

有许多大数据工具可以进行实时流式分析。它们可以成为常规实时系统的绝佳替代品,尤其是在数据量庞大且需要高速处理时。

作为提醒,术语大数据通常用于重新组合解决那些无法适应内存的问题的工具。这些问题解决有三个核心特征:体积、多样性和速度。

大数据工具通常以其并行计算中的大量工作而闻名。在编写非优化的常规 Python 代码时,代码通常会逐个传递数据点。大数据解决方案通过在多个服务器上并行处理数据点来解决此问题。这种方法使得大数据工具在数据量很大时速度更快,但在数据量较小时(由于管理不同工作者的开销)速度较慢。

大数据工具通常相对特定;它们只应用于具有大量数据的使用案例。对于每个手头的问题都开始使用大数据工具是没有意义的。

为处理流式数据,已经制作了许多此类解决方案。让我们看看一些常用的工具:

  • Spark Streaming:Spark Streaming 是 Spark 的一个补充,Spark 是当今大数据的主要工具之一。Spark Streaming 可以连接到 Kafka、Flume 和 Amazon Kinesis 等源,从而使得流式数据在 Spark 环境中变得可访问。

  • Apache Kafka:Kafka 是 Apache 管理的开源工具。它是一个用于提供实时数据流的框架。许多公司使用 Kafka 来提供数据管道和流式分析。甚至一些云服务提供商已经将 Kafka 集成到他们的解决方案中。

  • Apache Flume:Apache Flume 是 Apache 管理的另一个开源工具,它也专注于流式数据。Flume 专门用于在大数据环境中处理大量日志数据。

  • Apache Beam:Apache 流式家族中的另一个工具是 Apache Beam。这个工具可以处理批量和流式数据。它最出名的是构建 ETL 和数据处理流程。

  • Apache Storm:Apache Storm 是一个流处理计算框架,允许进行分布式计算。它用于实时处理与 Hadoop 相关的数据流。

  • Apache NiFi:Apache NiFi 是一个专注于 ETL 的工具。它为用户提供自动化和管理系统间数据流的可能性。它可以与 Kafka 一起工作。

  • Google Cloud DataFlow:Google Cloud DataFlow 是由 Google Cloud Platform 提出的一种工具。它专门针对流式使用案例进行开发。它允许用户在完全管理的服务中执行 Apache Beam 流程。

  • Amazon Kinesis:Amazon Kinesis 强烈基于前面讨论过的开源 Apache Kafka。使用 Kinesis 而不是 Kafka 的优势在于,它为你提供了许多管理服务,而如果你直接使用 Kafka,则需要投入更多精力来管理服务。当然,作为回报,你必须使用 AWS 平台来访问它。

  • Azure Stream Analytics:Azure Stream Analytics 是微软云平台 Azure 上提出的主要流式分析服务。它是一个基于 Trill 的实时分析服务。

  • IBM Streams:IBM Streams 是一种在 IBM 云上提出的流式分析工具。就像 Kinesis 一样,它基于开源 Kafka 项目。

实时调用大数据环境

如果你的实时分析服务由大数据或特定流式工具管理,你并不总是可以遵循 API 方法将你的实时流程连接到你的分析流程。

在大多数情况下,你需要查阅你选择工具的文档,并确保你理解如何使连接工作。在这个阶段,你通常需要一位专业配置文件来与你合作,因为这种级别的架构和数据工程通常被认为超出了大多数数据科学家的范围。

微服务系统和大数据系统之间的一般区别在于,在微服务方法中,我们通常认为 API 必须有一个响应,该响应被调用服务所考虑。

在大数据环境中,一个网站等服务向大数据环境发送数据但不需要响应的情况更为常见。你可以想象一个网站将用户的所有交互以 JSON 文件的形式写入一个固定位置。然后,大数据流工具连接到这个数据存储位置,以流式方式读取数据并将其转换为分析、可视化或其他内容。

让我们构建一个最小示例,以展示如何做到这一点:

  1. 首先,创建一个名为example.json的 JSON 文件,在其中只写入以下数据:

代码块 2-8

{'value':'hello'}
  1. 你现在可以编写一个非常短的 Spark Streaming 代码,以流式方式读取这些数据:

    from pyspark.sql import SparkSession
    from pyspark.sql.types import *
    spark = SparkSession \
        .builder \
        .appName("quickexample") \
        .getOrCreate()
    schema = StructType([ StructField("value", StringType(), True) ])
    streamingDF = (
      spark
        .readStream
        .schema(schema)
        .json('example.json')
    )
    display(streamingDF)
    

简而言之,此代码首先创建一个spark会话。一旦会话创建成功,就会为example.json文件定义一个模式。由于它只有一个键(称为value),因此模式相当简短。值的类型是string

你接着看到数据是通过.readStream方法导入的,这实际上为你做了很多流式处理的繁重工作。如果你想进一步扩展这个示例,你可以使用streamingDF库编写各种分析 Spark 函数,你将拥有使用知名大数据工具PySpark的流式分析。

摘要

在本章中,你开始探索架构领域。你已经在 AWS 上构建了自己的 API,并看到了系统间通信的基本基础。你现在应该明白,数据是系统间通信的关键,而良好的系统间通信对于通过分析传递价值至关重要。

这在实时和流分析的情况下尤其如此。如果项目早期没有足够早地识别出架构瓶颈,高速和通常较大的数据量很容易引发问题。

你必须记住的其他主题包括安全性、可用性和合规性。这些主题最好留给那些将其作为全职责任来处理此类数据架构问题的人。

在下一章中,我们将回到本书的核心,因为你会了解到如何在流数据上构建分析用例。

进一步阅读

第三章:第三章:流式数据上的数据分析

现在你已经了解了流式数据的介绍以及流式用例,还了解了流式架构的介绍,现在是时候进入这本书的核心内容了:分析和机器学习。

如你所知,描述性统计和数据分析是机器学习的入门点,但它们也经常被用作独立用例。在本章中,你将首先从传统统计学的角度发现描述性统计。传统统计学的某些部分专注于在只有部分数据可用时对描述性统计进行正确估计。

在流式处理中,你将比在批量数据中更强烈地遇到这些问题。通过持续的数据收集过程,你的描述性统计将在每个新的数据点上持续变化。本章将提出一些解决方案来处理这个问题。

你还将基于这些描述性统计数据构建数据可视化。毕竟,人类的大脑是以这样的方式连接的,视觉呈现比数据矩阵更容易阅读。数据可视化是一个重要的工具,当处理流式数据时,需要考虑一些额外的反思。

本章将以对统计过程控制的简要介绍结束。这个统计学的子领域专注于分析连续的测量流。尽管在过程控制发明时流式分析还不是一件事情,但它成为了这些分析方法的一个新的、重要的用例。

本章涵盖了以下主题:

  • 流式数据描述性统计

  • 样本理论简介

  • 主要描述性统计概述

  • 实时可视化

  • 基本警报系统

技术要求

你可以在以下链接的 GitHub 上找到这本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码样本的最简单方法是以下:

  1. 前往仓库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件时,在你的本地环境中解压缩它,你将通过你偏好的 Python 编辑器访问代码。

Python 环境

要跟随这本书,你可以下载仓库中的代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你检查一下 Anaconda(www.anaconda.com/products/individual),它包含了 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和 VSCode,用于编辑脚本和程序。

如果你安装 Python 或相关程序有困难,你可以查看 Google Colab(colab.research.google.com/)或 Kaggle Notebooks(www.kaggle.com/code),这两个平台都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码通常使用 Colab 和 Kaggle Notebooks,Python 版本为 3.7.13,你可以设置自己的环境来模拟这种情况。

流数据描述性统计

计算描述性统计通常是统计学和数据分析课程中最早涉及的内容之一。描述性统计是数据从业者非常熟悉的一种度量,因为它们允许你用一组小的指标来总结数据集。

为什么流数据上的描述性统计不同?

在常规数据集上,你可以使用几乎任何统计软件,通过已知的公式轻松地获得描述性统计。不幸的是,在流数据集上,这要明显复杂得多。

在流数据上应用描述性统计的问题在于,这些公式是为寻找固定测量值而设计的。在流数据中,你持续接收新的数据,不幸的是,这些数据可能会改变你的值。当你没有变量的全部数据时,你不能确定其值。在下一节中,你将了解样本理论,即处理从数据样本中估计参数的领域。

样本理论简介

在深入研究流数据的描述性统计之前,了解常规数据的描述性统计基础知识非常重要。处理使用数据样本估计描述性统计的领域被称为样本理论

比较总体和样本

在常规统计学中,总体和样本的概念非常重要。在进一步探讨之前,让我们先看看这些定义:

  • 个体:个体是研究中包含的单独对象或人。如果你的研究是观察生产线上的产品并测量产品的特征,那么个体就是产品。如果你正在进行关于网站销售的调查研究,并跟踪每个网站访问者的数据,那么你的个体就是网站访问者。

  • 总体:统计学中的总体通常定义为从中抽取样本的个体集合。总体包含任何理论上符合参与研究的个体。在生产线的例子中,总体是所有产品。在网站例子中,总体是所有网站访问者。

  • 样本:样本被定义为你要在其上执行研究的总体子集。在大多数统计研究中,你使用样本;你并没有世界上所有可能个体的数据,而是一个子集,你希望它足够大。有许多统计工具可以帮助你决定这是否是情况。

总体参数和样本统计量

当在样本上计算描述性统计时,它们被称为样本统计量。样本统计量基于样本,尽管它们通常是总体可靠估计,但它们并不是对总体的完美估计。

对于总体,使用的术语是总体参数。它们是准确的,这里没有测量误差。然而,在大多数情况下,它们是无法测量的,因为你永远不会有足够的时间和金钱来测量总体中的每一个个体。

样本统计量允许你估计总体参数。

抽样分布

抽样分布是统计量的分布。想象一下,一个网站顾客群体平均在你的网站上花费 300 秒(5 分钟)。如果你抽取 100 个随机样本的网站访客,并计算每个样本的均值,你可能会得到 100 个不同的估计值。

这些估计值的分布被称为抽样分布。它将遵循一个正态分布,其中均值应该相对接近总体均值。抽样分布的标准差被称为标准误差。标准误差用于估计样本的稳定性和代表性。

样本量计算和置信水平

在传统统计学中,样本量计算可以用来定义样本中需要有多少个元素,以便样本是可靠的。你需要为你的特定统计量定义一个置信水平和样本量计算公式。它们共同将允许你确定使用样本统计量可靠估计总体参数所需的样本量。

从流中滚动描述性统计

在流式分析中,随着时间的推移,你将拥有越来越多的数据。在接收到新的数据点时,可以重新计算整体统计量。在某个时候,新的数据点与过去的大量数据点相比,影响将非常小。如果流中发生改变,这种改变需要时间才能反映在描述性统计中,因此这通常不是最佳方法。

流式数据描述性统计的一般方法是使用滚动窗口来计算和重新计算描述性统计。一旦你决定了窗口的定义,你就计算窗口中所有观察值的统计量。

例如,可以选择最后 25 个产品的窗口。这样,每次新的产品测量数据进入你的分析应用时,你将计算这个产品以及前 24 个产品的平均值。

在你的窗口中,观察到的数据越多,最后一个观察数据的影响就越小。如果你想要避免误报,这可能是很好的,但如果你需要每个产品都完美无缺,这可能会很危险。在窗口中选择少量个体会使你的描述性统计在存在变化时波动很大。

调整窗口期是在尝试微调你的描述性统计时一个好的练习。通过尝试不同的方法,你可以找到最适合你用例的方法。

指数权重

你还可以使用指数加权作为调整流数据描述性统计的工具。指数加权会给最近观察到的数据赋予指数级的重要性,而对过去观察到的数据赋予较少的重要性。这允许你接受更多的历史观察数据,同时不会影响最近观察数据的重要性。

跟踪收敛性作为额外的关键绩效指标

在跟踪数据流的描述性统计时,报告多个测量时间窗口是一个可能的选择。例如,你可以构建一个仪表板,通知你的客户日平均数,同时你也可以报告最后 1 小时和最后 15 分钟的均值。

通过这样做,例如,你可以给你的客户提供信息,说这一天和这一小时总体上表现良好(日和时平均符合规范),但在最后 15 分钟,你的产品开始出现问题,最后 15 分钟的均值不符合规范。有了这些信息,操作员可以迅速介入,根据他们的需求停止或改变流程,而无需担心当天早些时候的生产。

主要描述性统计概述

现在我们来看看最常用的描述性统计,看看你如何将它们适应到任何数据流的滚动窗口中。当然,正如你在上一章中看到的,流分析可以在多种工具上执行。重要的是要理解使用哪些描述性分析,并有一个可以适应不同流输入工具的基础。

平均值

将要介绍的第一种描述性统计量是平均值。平均值是最常用的中心度量。

解释和使用

与中位数和众数等其他中心度量一起,它的目标是描述变量的分布中心。如果分布是完全对称的,平均值、中位数和众数将是相等的。如果分布是偏斜的,平均值将受到异常值的影响,并朝向偏斜或异常值的方向移动。

公式

样本均值的公式如下:

公式

在这个公式中,n是样本大小,x是样本中变量的值。

代码

您可以使用许多 Python 函数来计算平均值。其中之一是名为meannumpy函数。您可以在以下示例中看到它的用法:

代码块 3-1

values = [10,8,12,11,7,10,8,9,12,11,10]
import numpy as np
np.mean(values)

您应该得到一个结果为9.8181

中位数

中位数是变量或分布中心性的第二个度量。

解释和使用

与平均值一样,中位数用于表示中心。然而,与平均值的不同之处在于,中位数对异常值不敏感,并且对偏斜分布的敏感性要小得多。

一个重要的例子是在研究一个国家人口的工资时。众所周知,工资遵循一个强烈偏斜的分布。大多数人工资在最低工资和平均工资之间。很少人有非常高的收入。当计算平均值时,由于高收入者的推动,它将过高,无法代表整体人口。使用中位数更合理,因为它将更接近地代表很多人。

中位数表示 50%的人将赚取低于这个数额,而 50%的人将赚取高于这个数额。

公式

中位数的公式相对复杂,因为它不使用实际值,而是对所有的值从低到高排序后取中间值。如果值的数量是偶数,则没有中间值,所以它将取两个中间值的平均值:

公式

在这里,x是一个有序列表,括号表示在这个列表上的索引。

代码

您可以这样计算中位数:

代码块 3-2

values = [10,8,12,11,7,10,8,9,12,11,10]
import numpy as np
np.median(values)

这个计算的输出应该是10

众数

众数是描述性统计中常用的第三个中心度量。本节将解释其在 Python 中的使用和实现。

解释和使用

众数表示在数据中出现次数最多的值。如果您有一个连续(数值)变量,那么在计算众数之前,您通常创建一些区间来重新分组您的数据。这样,您可以确保它是代表性的。

公式

找到众数的最简单方法是计算每个组或每个值的出现次数,并取出现次数最高的值作为众数。这适用于分类变量以及数值变量。

代码

您可以使用以下代码在 Python 中找到众数:

代码块 3-3

values = [10,8,12,11,7,10,8,9,12,11,10]
import statistics
statistics.mode(values)

获得的结果应该是10

标准差

您现在将看到一系列用于描述变异性的描述性统计量,从标准差开始。

解释和使用

标准差是常用的变异性度量。变异性度量显示了数据中心周围的分布。例如,均值可以表示你的人口平均工资,但它不能告诉你是否每个人都接近这个值,或者是否每个人都非常远离这个值。变异性度量允许你获得这些信息。

公式

样本标准差可以按以下方式计算:

代码

你可以按以下方式计算样本标准差:

代码块 3-4

values = [10,8,12,11,7,10,8,9,12,11,10]
import numpy as np
np.std(values, ddof=1)

你应该得到一个结果为1.66

方差

方差是另一个变异性度量,它与标准差密切相关。让我们看看它是如何工作的。

解释和使用

方差是标准差的平方。有时使用方差的公式更容易,因为它不涉及取平方根。因此,在某些数学运算中更容易处理。标准差通常更容易用于解释。

公式

方差的公式如下:

代码

你可以使用以下代码来计算样本方差:

代码块 3-5

values = [10,8,12,11,7,10,8,9,12,11,10]
import numpy as np
np.var(values, ddof=1)

获得的结果应该是2.76

四分位数和四分位距

将要介绍的第三个变异性度量是四分位距IQR)。这将结束描述变异性的统计。

解释和使用

IQR 是一个与中位数有关联的度量。如果你还记得,中位数是 50%的值低于它,50%的值高于它的点;它实际上是一个中间点。

同样,可以使用 25/75%的分割而不是 50/50%的分割。在这种情况下,它们被称为四分位数。通过计算第一个四分位数(25%以下,75%以上)和第三个四分位数(75%以下,25%以上),你可以了解你的数据的变异性。第三个四分位数和第一个四分位数之间的差异称为 IQR。

公式

IQR 的公式简单来说就是第三四分位数和第一四分位数的差,如下所示:

代码

你可以使用以下 Python 代码来计算 IQR:

代码块 3-6

values = [10,8,12,11,7,10,8,9,12,11,10]
import scipy.stats
scipy.stats.iqr(values)

你应该找到一个 IQR 为2.5

相关性

相关性是描述多个变量之间关系的描述性统计量。让我们看看它是如何工作的。

解释和使用

现在你已经看到了多个中心性和变异性的度量,你现在将发现一个描述性统计量,它允许你研究两个变量之间的关系。这个描述性统计量的主要形式是相关性。相关性有多种公式和定义,但在这里,你将看到最常见的一种:皮尔逊相关性。

对于强负相关,相关系数将是-1,对于强正相关,相关系数是1,对于无相关,相关系数是0,或者介于两者之间。

公式

这里展示了皮尔逊相关系数的公式:

代码

你可以使用以下代码在 Python 中轻松计算它:

代码块 3-7

values_x = [10,8,12,11,7,10,8,9,12,11,10]
values_y = [12,9,11,11,8,11,9,10,14,10,9]
import numpy as np
np.corrcoef(values_x,values_y)

你应该获得一个相关矩阵,你可以从中读取相关系数是0.77。这表明两个变量之间存在正相关。

现在你已经看到了一些描述数据的数值方法,将会有助于发现一些以更用户友好的方式可视化这些数据的方法。下一节将深入探讨这一点。

实时可视化

在这部分,你将了解如何使用 Plotly 的 Dash 设置一个简单的实时可视化。这个工具是数据科学家的一个很好的仪表板工具,因为它易于学习,除了 Python 环境之外不需要其他东西。

代码有点长,不适合在书中展示,但你可以在 GitHub 仓库中找到 Python 文件(名为ch3-realtimeviz.py)。

在代码中,你可以看到如何构建一个简单的实时图形。代码的一般设置是有一个应用程序。你使用 HTML 类似的构建块在应用程序中定义布局。在这种情况下,布局包含一个 div(一个内容块),其中有一个图形。

该布局的主要组件是使用Interval函数。使用这个函数将使仪表板以给定的频率自动更新。这足够快,可以将其视为实时更新。

回调装饰了其下方编写的函数(update_graph)。通过这种方式装饰,应用程序知道每次更新时(由布局中的Interval触发)都必须调用此函数。update_graph函数返回一个更新后的图形。

打开仪表板

一旦你在本地机器上运行代码,你将看到以下信息:

![图 3.1 – Dash 的输出]

![图片 B18335_03_1.jpg]

图 3.1 – Dash 的输出

此链接将为您提供访问正在实时更新的仪表板的方式。它看起来像这样:

![图 3.2 – Plotly 仪表板]

![图片 B18335_03_2.jpg]

图 3.2 – Plotly 仪表板

比较 Plotly 的 Dash 和其他实时可视化工具

现在有许多其他的数据可视化工具。流行的例子包括 Power BI、QlikView 和 Tableau。Plotly 的 Dash 的好处是,如果你已经在 Python 环境中,它非常容易上手。它是免费的,不需要安装。

如果你想要成为商业智能BI)的专家,值得检查其他工具。其中许多都有实时更新的能力,每个工具的具体文档将指导你如何使用它。

在构建仪表板或数据可视化系统时,考虑您的整体架构也很重要。正如前一章所讨论的,在许多情况下,您将有一个数据生成系统和能够实时管理该系统的架构。就像任何其他分析构建块一样,您需要确保您的仪表板可以连接到您的数据生成过程,或者您可能需要构建一个中间数据存储或数据通信层。

我们现在将进入描述性统计学的下一个用例:构建基本的警报系统。

构建基本警报系统

在本章的前几部分,您已经看到了描述性统计和可视化的介绍。

基本警报系统将在最后一个数据分析用例中介绍。在本部分中,您将了解如何使用基本警报系统对流数据进行操作。为此,您将了解如何利用描述性统计与业务规则相结合,在实时自动生成警报。警报系统的示例方法如下:

  • 极端值警报系统

  • 过程稳定性警报系统

  • 恒定变异性警报系统

  • 统计过程控制和精益六西格玛控制图

极端值警报系统

流数据警报和监控系统中的第一个例子是您在早期章节中看到的用例:编写一个业务规则,一旦观察到的值超出硬编码的边界,就会发送警报。

这个例子在之前的章节中如下编码:

代码块 3-9

import pandas as pd
data_batch = pd.DataFrame({
    'temperature': [10, 11, 10, 11, 12, 11, 10, 9, 10, 11, 12, 11, 9, 12, 11],
    'pH': [5, 5.5, 6, 5, 4.5, 5, 4.5, 5, 4.5, 5, 4, 4.5, 5, 4.5, 6]
})
data_batch

您将看到以下数据被打印出来:

图 3.3 – 数据批次

图 3.3 – 数据批次

现在我们来编写函数并遍历数据,以便在每个数据点上执行该函数:

代码块 3-10

def super_simple_alert(datapoint):
  if datapoint['temperature'] < 10:
    print('this is a real time alert. Temp too low')
  if datapoint['pH'] > 5.5:
    print('this is a real time alert. pH too high')
data_iterable = data_batch.iterrows()
for i,new_datapoint in data_iterable:
  print(new_datapoint.to_json())
  super_simple_alert(new_datapoint)

结果打印输出显示已启动多个警报:

图 3.4 – 您警报系统的打印结果

图 3.4 – 您警报系统的打印结果

这个例子是进入警报和监控系统的一个很好的第一步:流数据的一个常见用例。让我们看看您如何在此基础上添加更多和更复杂的静态逻辑。

过程稳定性警报系统(均值和中位数)

与将业务逻辑应用于单个值相比,在某些情况下,添加平均值的逻辑可能更好。在许多情况下,如果只有一个观察值不符合规范,可能不需要发送警报。然而,当多个产品的平均值超出规范时,您可能有一个需要解决的问题的结构性问题。

您可以这样考虑编写这样的例子:

代码块 3-11

import numpy as np
def super_simple_alert(hist_datapoints):
  print(hist_datapoints)
  if np.mean(hist_datapoints['temperature']) < 10:
    print('this is a real time alert. temp too low')
  if np.mean(hist_datapoints['pH']) > 5.5:
    print('this is a real time alert. pH too high')
data_iterable = data_batch.iterrows()
# create historization for window
hist_temp = []
hist_ph = []
for i,new_datapoint in data_iterable:
  hist_temp.append([new_datapoint['temperature']])
  hist_ph.append([new_datapoint['pH']])
  hist_datapoint = {
      'temperature': hist_temp[-3:],
      'pH': hist_ph[-3:]
  }
  super_simple_alert(hist_datapoint)

在这个例子中,你可以看到对最后 10 个观测值进行了窗口平均计算。这允许你在最后三个观测值的平均值达到硬编码的警报阈值时立即发出警报。你应该观察到以下输出:

![图 3.5 – 改进的打印输出图片 B18335_03_5.jpg

图 3.5 – 改进的打印输出

你可以观察到,使用三个观测值的平均值的事实使得收到警报的可能性大大降低。如果你在窗口中使用更多的观测值,这会进一步降低。微调应取决于业务案例。

恒定可变性的警报系统(标准差和方差)

你可以用同样的方法处理可变性。正如在描述性统计章节中讨论的那样,一个过程通常由中心性和可变性来描述。即使你的平均值在规范范围内,也可能存在很大的可变性;如果可变性很大,这也可能成为你的一个问题。

你可以使用窗口计算的平均值来执行可变性的警报系统。这可以用于仪表板,也可以用于警报系统等。

你可以这样编写代码:

代码块 3-12

import numpy as np
def super_simple_alert(hist_datapoints):
  print(hist_datapoints)
  if np.std(hist_datapoints['temperature']) > 1:
    print('this is a real time alert. temp variations too high')
  if np.std(hist_datapoints['pH']) > 1:
    print('this is a real time alert. pH variations too high')
data_iterable = data_batch.iterrows()
# create historization for window
hist_temp = []
hist_ph = []
for i,new_datapoint in data_iterable:
  hist_temp.append([new_datapoint['temperature']])
  hist_ph.append([new_datapoint['pH']])
  hist_datapoint = {
      'temperature': hist_temp[-3:],
      'pH': hist_ph[-3:]
  }
  super_simple_alert(hist_datapoint)

注意,现在的警报不是基于平均值,而是基于可变性。对于这个例子,你会收到以下输出:

![图 3.6 – 进一步改进的打印输出图片 B18335_03_6.jpg

图 3.6 – 进一步改进的打印输出

基本警报系统使用统计过程控制

如果你想将此类警报系统进一步发展,可以使用统计过程控制的方法。这个统计领域专注于控制一个过程或生产方法。从这个领域衍生出的主要工具被称为控制图

在控制图中,你随时间绘制一个统计量,但你会添加控制限。一个标准的控制图是在其中你绘制样本平均随时间的变化,并基于标准差添加控制限。然后你计数并观察极端值,当发生一定数量的重复事件时,你会触发警报。

你可以在“进一步阅读”部分找到一个链接,了解更多关于控制图和统计过程控制的信息。

摘要

在本章中,你学习了在流数据上进行数据分析的基础知识。你看到,对数据流进行描述性统计与对批量数据进行描述性统计的工作方式不同。可以从批量数据中使用的估计理论,但你需要对数据进行窗口处理以获取更大或更小的历史数据窗口。

窗口设置可以对你的结果产生强烈影响。较大的窗口将考虑更多的数据,并将考虑更早时间的数据。然而,它们对新的数据点将变得不那么敏感。毕竟,窗口越大,一个新数据点的影响就越小。

你还学会了如何使用 Plotly 的 Dash 构建数据可视化。这个工具很棒,因为它非常强大,并且仍然可以从 Python 环境中使用。存在许多其他可视化工具,但最重要的是掌握至少其中之一。本章向您展示了可视化流数据的函数需求,如果需要,您将能够在其他数据可视化工具上重现这一点。

本章的最后部分介绍了统计过程控制。到目前为止,你一直在使用静态规则或描述性统计来构建简单的警报系统。统计过程控制是构建更高级警报系统的一个有趣领域,这些系统相对容易理解且易于实现。

在下一章中,你将开始探索在线机器学习。一旦你对在线机器学习有了基本的了解,你将在后面的章节中看到,你可以用基于机器学习的异常检测模型来替换静态的警报系统决策规则。本章中你看到的数据分析方法是朝着这个方向的重要第一步。

进一步阅读

第二部分:探索数据流的应用案例

本节涵盖了流数据的多种机器学习技术,包括许多在线模型以及强化学习。

本节包括以下章节:

  • 第四章**,使用 River 进行在线学习

  • 第五章**,在线异常检测

  • 第六章**,在线分类

  • 第七章**,在线回归

  • 第八章**,强化学习

第四章:第四章:使用 River 进行在线学习

在本章和接下来的三章中,你将学习如何使用一个名为 River 的在线机器学习库。在线机器学习是机器学习的一个分支,其中模型被设计成能够在接收到任何新的数据点时更新其学习到的模型。

在线机器学习是离线机器学习的对立面,后者是你可能已经熟悉的常规机器学习。一般来说,在机器学习中,模型会尝试学习一个可以执行特定任务的数学规则。这个任务是在基于多个数据点的基础上学习的。这些任务背后的数学基于统计学和算法学。

在本章中,你将了解如何使用在线机器学习,你将发现多种类型的在线机器学习。你将更深入地了解在线和离线机器学习之间的差异。你还将看到如何使用 Python 中的 River 构建在线机器学习模型。

本章涵盖了以下主题:

  • 什么是在线机器学习?

  • River 用于在线学习

  • 与 River 的一个超级简单的示例

  • 与 River 的第二个示例

技术要求

你可以在以下链接在 GitHub 上找到本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下:

  1. 前往存储库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件后,在你的本地环境中解压它,你将能够通过你偏好的 Python 编辑器访问代码。

Python 环境

要跟随本书学习,你可以下载存储库中的代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda (www.anaconda.com/products/individual),它包含了 Jupyter Notebook 和 JupyterLabs,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和 VSCode,用于编辑脚本和程序。

如果你在机器上安装 Python 或相关程序时遇到困难,你可以查看 Google Colab (colab.research.google.com/) 或 Kaggle Notebooks (www.kaggle.com/code),这两个平台都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码将一般使用 Python 3.7.13 版本的 Colab 和 Kaggle 笔记本,你可以设置自己的环境来模拟这种情况。

什么是在线机器学习?

在机器学习中,训练模型最常见的方式是进行单次训练遍历。这种方法的一般步骤如下:

  1. 数据准备。

  2. 创建训练-测试分割。

  3. 进行模型基准测试和超参数调整。

  4. 选择最佳模型。

  5. 将此模型部署到生产环境中。

这种方法被称为离线学习

在流数据的情况下,你通常可以很好地使用这种类型的模型。你可以一次性构建模型,部署它,并用于预测你的输入流。你可能可以跟踪你模型的性能指标,当性能开始变化时,你可以更新或重新训练你的模型,部署新版本,只要它能在生产环境中正常工作。

在线机器学习是机器学习的一个分支,包含的模型工作方式非常不同。它们不会一次性学习完整的数据集,而是通过一系列步骤更新学习到的模型(预测的决策规则)。使用这种方法,你可以自动更新生产中的模型;它继续在新数据上学习。

在线学习与常规学习有何不同?

在线学习系统的构建实践在机器学习问题上与离线机器学习方法的视角不同。在离线学习中,有真实的机会测试模型学到了什么,而对于在线学习系统,这种变化可能随时发生。

对于某些用例,无法使用离线学习。一个例子是预测用例。通常,对于预测,你预测未来的一个值。为此,你使用最新的可用数据来训练和重新训练你的模型。在许多预测应用中,机器学习模型每次需要预测新的预报时都会重新训练。

异常值检测是另一个离线学习可能不太合适的例子。如果你的模型没有整合每个新的数据点,这些数据点就不能作为参考案例来对抗新值。这个问题也可以通过离线学习来解决,但在线学习可能是解决这个用例的更合适方案。

在线学习的优势

在线学习有两个主要优势:

  • 第一个主要优势是在线学习算法可以更新。因此,它们可以通过多次迭代学习。这样,大型数据集不需要一次性通过模型,而可以分多步进行。当数据集很大或计算资源有限时,这是一个很大的优势。

  • 在线学习的第二个优势是,在线模型在更新时可以适应更新的过程:它不太固定。因此,当一个离线模型在数据趋势随时间略微变化时可能会过时,在线模型可以适应这些变化并保持性能。

在线学习的挑战

然而,使用在线学习也存在一些缺点。

首先,这个概念不太普及,并且找到在线学习用例的模型实现和文档可能会有些困难。

其次,更重要的是,在线学习存在模型学习到你不希望它们学习的内容或错误内容的风险。与离线学习相比,你在将模型推送到生产环境之前有更多的控制来验证模型学习的内容,而将在线学习推送到生产环境时,它可能会继续学习并由于学习到的更新而性能下降。

既然你已经理解了在线学习的概念,现在让我们来探索多种在线学习类型。

在线学习类型

虽然在线学习的类型没有明确的定义,但考虑以下三个术语至少是好的:增量学习、自适应学习和强化学习。

增量学习

增量学习方法是那些可以一次更新一个观察值的模型。如前所述,在线机器学习的主要增值之一就是这一点,这是标准离线学习所无法实现的。

自适应学习

然而,仅仅更新模型可能不足以满足之前提到的在线学习的第二个重要增值。如果你想使模型更好地适应更近期的数据,你必须选择一个自适应的在线学习方法。这些方法很好地处理了任何需要模型适应的情况,例如,在人们甚至还没有意识到之前,数据中出现的新的趋势。

强化学习

强化学习并不一定被视为在线学习的子领域。尽管强化学习的方法与之前提到的在线学习方法不同,但它可以用于相同的企业问题。因此,了解强化学习也很重要。它将在后面的章节中更深入地介绍。在接下来的部分,你将看到如何使用 Python 中的 River 包构建在线机器学习模型。

使用 River 进行在线学习

在本节中,你将了解用于在线学习的 River 库。River 是一个专门为在线机器学习制作的 Python 库。其代码库是 cremescikit-multiflow 库的结合结果。River 的目标是成为流数据机器学习的首选库。

在这个例子中,你将看到如何在一个知名数据集上训练在线模型。在这个例子中,你将采取以下步骤:

  1. 导入数据。

  2. 重新分类数据以获得二元分类问题。

  3. 使用在线模型进行二元分类。

  4. 使用训练-测试分割来改进模型评估。

  5. 使用一对一策略对在线多类分类模型进行拟合。

使用 River 训练在线模型

对于这个例子,你将使用著名的鸢尾花数据集。你可以从 UCI 机器学习仓库下载它,但你也可以使用以下代码直接将其下载到 pandas 中。

达到我们目标所需的步骤如下:

  1. 导入数据

  2. 将数据重新分类为二分类问题

  3. 将数据转换为合适的输入格式

  4. 逐个数据点学习模型

  5. 评估模型

我们将按照以下步骤开始:

  1. 我们首先将数据集导入,如下所示:

代码块 4-1

#iris dataset classification example
import pandas as pd
colnames = ['sepal_length','sepal_width','petal_length','petal_width','class']
data = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', names=colnames)
data.head()

数据集看起来如下:

![图 4.1 – 鸢尾花数据集img/B18335_04_001.jpg

图 4.1 – 鸢尾花数据集

鸢尾花数据集非常常用,主要在教程和示例中。该数据集包含三种不同鸢尾花物种的多个观察值,是一种花卉。对于每一朵花,你有植物特定部分的长度和宽度。你可以使用这四个变量来预测鸢尾花的物种。

  1. 对于这个第一个模型,你需要将类别列转换为二进制列,因为你将使用来自 River 的 LogisticRegression 模型,它不支持多类别:

代码块 4-2

data['setosa'] = data['class'] == 'Iris-setosa'
data['setosa']

这将产生以下输出:

![图 4.2 – 布尔数据类型的序列img/B18335_04_002.jpg

图 4.2 – 布尔数据类型的序列

  1. 作为下一步,我们将编写代码来遍历数据以模拟流式数据输入。X 数据应该是字典格式,y 可以是字符串、整数或布尔值。在下面的代码中,你可以看到一个在第一次迭代后停止的循环,因此它打印一个 X 和一个 y

代码块 4-3

# convert to streaming dataset
for i,row in data.sample(1).iterrows():
    X = row[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
    X = X.to_dict()

    y = row['setosa']

    print(X)
    print(y)
    break

你可以看到 X 必须是字典格式,这对于熟悉离线学习的人来说相对不常见。然后,y 可以是布尔值、字符串或整数。它看起来如下所示:

![图 4.3 – 模型的 x 和 y 输入img/B18335_04_003.jpg

图 4.3 – 模型的 x 和 y 输入

  1. 现在,让我们逐个拟合模型。添加 .sample(frac=1) 非常重要,以避免数据按顺序排列。如果你不添加这个,模型将首先接收一个类别的所有数据,然后是其他类别的数据。模型很难处理这种情况,因此应该使用 sample 函数引入随机顺序:

代码块 4-4

!pip install river
from river import linear_model
model =  linear_model.LogisticRegression()
for i,row in data.sample(frac=1).iterrows():
    X = row[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
    X = X.to_dict()
    y = row['setosa']

    model.learn_one(X, y)
  1. 让我们看看如何在训练数据上做出预测。你可以使用 predict_many 在数据框上预测,或者你也可以使用 predict_one

代码块 4-5

preds = model.predict_many(data[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']])
print(preds)

结果看起来如下:

![图 4.4 – 150 个布尔观察值img/B18335_04_004.jpg

图 4.4 – 150 个布尔观察值

  1. 你可以使用 scikit-learn 准确率分数来估计此模型的训练准确率:

代码块 4-6

from sklearn.metrics import accuracy_score
accuracy_score(data['setosa'], preds)

在这种情况下,获得的训练准确率是 1,表明模型已经完美地学习了训练数据。尽管模型在学习过程中已经学会了在它所见到的数据上的完美预测,但不太可能在新的、未见过的数据点上表现同样出色。在下一节中,我们将改进我们的模型评估,以避免高估性能指标。

改进模型评估

在第一个例子中,没有真正的重新学习和更新。

在这个例子中,我们将更新并跟踪学习过程中的准确率。你还将看到如何保持训练集和独立的测试集。你可以使用每个到达的数据点进行学习,然后使用更新后的模型来预测下一个到达的数据点。这更接近于流式使用案例。

达到这一步的步骤如下:

  1. 训练-测试集划分。

  2. 在训练数据上拟合模型。

  3. 查看学习曲线。

  4. 在测试数据上计算性能指标。

我们将按以下步骤开始:

  1. 让我们从对数据进行分层训练-测试集划分开始:

代码块 4-7

# add a stratified train test split
from sklearn.model_selection import train_test_split
train,test = train_test_split(data, stratify =data['setosa'])
  1. 你现在可以重新执行之前的相同学习循环,但这次是在训练数据上。你可以看到一个名为 correct 的列表,用于跟踪学习随时间的变化:

代码块 4-8

from river import linear_model,metrics
model =  linear_model.LogisticRegression()
correct = []
for i,row in train.sample(frac=1).iterrows():
    X = row[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
    X = X.to_dict()

    y = row['setosa']

    model.predict_one(X)
    correct.append(y == model.predict_one(X))

    model.learn_one(X,y)
  1. 让我们绘制 correct 分数随时间的累积总和,以查看模型是否从一开始就学得很好,或者模型在学习曲线的末尾是否有更少的错误:

代码块 4-9

# this model is learning quite stable from the start
import matplotlib.pyplot as plt
import numpy as np
plt.plot(np.cumsum(correct))

你可以看到学习曲线相当线性;准确率在时间上保持大致不变。如果机器学习实际上随着训练而改进,那么预计会看到准确率随时间提高(随着时间的推移,正确响应增多,呈现指数曲线)的情况。你可以在以下图表中查看学习曲线:

![图 4.5 – 学习曲线](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/ml-stm-dt-py/img/Figure_4.5 – The_learning_curve.jpg)

img/B18335_04_005.jpg

图 4.5 – 学习曲线

  1. 最后,让我们计算测试分数上的准确率,以查看模型生成样本外数据的性能如何:

代码块 4-10

# model was not so good on out of sample
accuracy_score(test['setosa'],model.predict_many(test[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]))

这个得分是 0.94,略低于在训练集上获得的 1.0。这告诉我们模型学得相当好。

在接下来的章节中,你将看到更多可以帮助提高模型和准确率的技巧和工具。

使用 one-vs-rest 构建多类分类器

在上一个例子中,你已经看到了如何构建一个二元分类器。为此,你将目标变量重新分类为 setosa-vs-rest。然而,你可能希望构建一个模型,能够同时处理所有三个类别。这可以通过使用 River 的 OneVsRest 分类器来实现。现在让我们看看一个例子:

  1. 你可以从之前的相同训练-测试集划分开始,但现在,你可以对类别进行分层:

代码块 4-11

# add a stratified train test split
from sklearn.model_selection import train_test_split
train,test = train_test_split(data, stratify =data['class'])
  1. 然后你在训练数据上拟合模型。代码几乎相同,只是你在调用LogisticRegression时使用OneVsRestClassifier

代码块 4-12

from river import linear_model,metrics,multiclass
model =  multiclass.OneVsRestClassifier(linear_model.LogisticRegression())
correct = []
for i,row in train.sample(frac=1).iterrows():
    X = row[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]
    X = X.to_dict()

    y = row['class']

    model.predict_one(X)
    correct.append(y == model.predict_one(X))
    model.learn_one(X,y)
  1. 当观察学习随时间的变化时,你可以看到模型在约 40 个观测值之后开始更好地学习。在 40 个观测值之前,它的正确预测比之后少得多:

代码块 4-13

# this model predicts better after 40 observations
import matplotlib.pyplot as plt
import numpy as np
plt.plot(np.cumsum(correct))

图形如下所示。它在前 40 个观测值中有一个较缓的斜率,之后的准确性有所提高:

图 4.6 – 更好的学习曲线

图 4.6 – 更好的学习曲线

  1. 你可以使用predict_many再次查看预测是否准确。当你进行predict操作时,现在你将不会得到True/False,而是得到每个鸢尾花类型的字符串值:

代码块 4-14

model.predict_many(test[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']])

这导致了以下输出:

图 4.7 – 多类目标

图 4.7 – 多类目标

  1. 可以使用以下代码计算模型的测试准确率:

代码块 4-15

# model scores 0.63 on the test data
from sklearn.metrics import accuracy_score
accuracy_score(test['class'],model.predict_many(test[['sepal_length', 'sepal_width', 'petal_length', 'petal_width']]))

模型在测试数据上获得了0.63的准确率。

摘要

在本章中,你已经在理论和实践两方面学习了在线机器学习的基础知识。你看到了不同类型的在线机器学习,包括增量学习、自适应学习和强化学习。

你已经看到了在线机器学习的许多优点和缺点。在其他原因中,如果你需要快速重新学习,你可能几乎不得不参考在线方法。一个缺点是,由于批处理学习仍然是目前的行业标准,因此可用的方法较少。

最后,你已经开始通过一个 Python 示例在著名的鸢尾花数据集上练习和实现在线机器学习。

在下一章中,你将更深入地学习在线机器学习,重点关注异常检测。你将看到机器学习如何被用来替代之前章节中构建的固定规则警报系统。在随后的章节中,你将学习更多关于使用 River 进行在线分类和回归的知识,并通过继续本章中鸢尾花分类模型的例子来学习。

进一步阅读

第五章:第五章:在线异常检测

异常检测是流数据机器学习的一个良好起点。由于流数据提供连续的数据点流,因此监控实时解决方案的应用案例是首先想到的。

在许多领域,监控都是至关重要的。在 IT 解决方案中,系统中的发生情况通常会被持续记录,并且这些日志可以被视为流数据进行分析。

物联网IoT)中,正在收集大量传感器的传感器数据。然后这些数据被实时分析和使用。

在此类用例中,实时和在线异常检测可以通过找到远超出预期测量范围或意外值,从而具有极大的附加价值。及时检测它们可以具有很大的价值。

在本章中,你将首先深入了解异常检测及其在实施时需要考虑的理论问题。然后,你将看到如何使用 Python 中的 River 包实现在线异常检测。

本章涵盖了以下主题:

  • 定义异常检测

  • 异常检测的应用案例

  • 比较异常检测和不平衡分类

  • River 中检测异常的算法

  • 进一步探讨异常检测

技术要求

你可以在以下链接的 GitHub 上找到本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下:

  1. 前往存储库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件时,在你的本地环境中解压它,你将能够通过你偏好的 Python 编辑器访问代码。

Python 环境

要跟随本书,你可以下载存储库中的代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda(www.anaconda.com/products/individual),它包含了 Jupyter Notebooks 和 JupyterLabs,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和 VSCode,用于编辑脚本和程序。

如果你安装 Python 或相关程序时遇到困难,你可以查看 Google Colab(colab.research.google.com/)或 Kaggle Notebooks(www.kaggle.com/code),这两个都允许你在在线笔记本中免费运行 Python 代码,无需任何设置。

定义异常检测

让我们先来理解一下什么是异常检测。也称为离群值检测,异常检测是识别数据集中罕见观测值的过程。这些罕见的观测值被称为离群值异常值

异常检测的目标是构建模型,这些模型可以使用统计方法或机器学习自动检测离群值。这样的模型可以使用多个变量来判断一个观测值是否应该被视为离群值。

离群值是问题吗?

离群值在许多数据集中都会出现。毕竟,如果你考虑一个遵循正态分布的变量,看到远离均值的数值是正常的。让我们考虑一个标准正态分布(均值为 0,标准差为 1):

代码块 5-1

import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as stats
x = np.linspace(-4,4, 100)
plt.plot(x, stats.norm.pdf(x, 0, 1))

你可以看到以下结果图:

图 5.1 – 正态分布

图 5.1 – 正态分布

这个标准正态分布的大多数观测值都围绕 0。然而,在分布的尾部观察到一些观测值是正常的。如果你有一个真正遵循这个分布的变量,并且你的样本量足够大,那么有一些远离中心的观测值并不能真正被认为是坏事。

在下面的代码中,你可以看到从标准正态分布中抽取了 1000 万个观测值的样本:

代码块 5-2

import numpy as np
import matplotlib.pyplot as plt
data = np.random.normal(size=10000000)
plt.hist(data, bins=25)

数据很好地遵循正态曲线。你可以在以下图表中看到这一点:

图 5.2 – 正态分布直方图

图 5.2 – 正态分布直方图

现在,让我们使用以下代码查看这个样本的最高值和最低值:

代码块 5-3

min(data), max(data)

在当前抽取中,观察到了最小值 5.11 和最大值 5.12。现在,这些是离群值吗?答案是复杂的。当然,这两个值完全在正态分布的范围内。另一方面,它们是极端值。

这个例子说明了定义离群值并不总是容易的,需要仔细考虑你的特定用例。我们现在将看到一些异常检测的应用案例。

探索异常检测的应用案例

在介绍一些特定的异常检测算法之前,让我们首先考虑一些经常与异常检测一起使用的用例。

金融机构的欺诈检测

异常检测的一个非常常见的用例是在金融机构中检测欺诈。银行通常拥有大量数据,因为几乎每个人都有一个或多个定期使用的银行账户。所有这些使用都产生了大量可以帮助银行改善其服务和利润的数据。欺诈检测是银行数据科学应用的关键组成部分,与其他许多用例一起。

欺诈检测的一个常见用例是自动检测信用卡欺诈。想象一下,您的卡或卡信息被盗,有人正在欺诈性地使用它们。这会导致欺诈交易,这些交易可以通过机器学习算法自动检测。然后银行可以自动阻止您的卡,并要求您验证是否是您本人,或者是有人在欺诈性地进行这些支付。

这既符合银行的利益,也符合用户的利益,因此是异常检测的一个很好的用例。其他处理信用卡和支付数据的公司也可能使用这些方法。

流模型非常适合欺诈检测。通常会有大量的数据以连续的支付和其他数据流的形式进入。流模型允许您在欺诈情况发生时直接采取行动,而不是等待下一批数据被启动。

如果您想了解更多关于金融机构欺诈检测的信息,您可以查看以下链接:

日志数据中的异常检测

异常检测的第二个用例是日志分析。许多软件应用程序会生成包含程序执行的各种信息的巨大日志。这些日志通常被临时或长期存储以供进一步分析。在某些情况下,这些分析可能是对软件在某个时间点发生的事件的特定信息的手动搜索,但在其他时候,它们可能是自动日志处理程序。

在日志数据中执行异常检测的一个困难是,日志数据通常非常无结构。通常,它们只是文本文件中依次打印的一堆语句。很难理解这些数据。

如果您成功地完成了正确结构化和分类日志数据的挑战,那么您就可以使用机器学习技术来自动检测软件执行中的问题。这允许您立即采取行动。

在这里,使用流分析而不是批量分析同样重要。某些软件是关键任务,而停机通常意味着公司会遇到问题。这些问题可能包括合同问题和收入损失。如果一家公司能够自动检测到错误,这将使他们能够快速修复问题。问题修复得越快,公司遇到的问题就越少。

对于关于日志数据异常检测的更深入用例文献,您可以查看以下链接:

制造和生产线的故障检测

生产线故障检测的一个例子是工业食品生产业务。许多生产线几乎是完全自动化的,这意味着从原料输入到最终产品输出的过程中几乎没有人工干预。这种风险是可能会发生无法接受为最终产品的缺陷。

在生产线上使用传感器数据可以极大地帮助检测生产中的异常。当生产线有一些参数出错时,传感器与流式系统和实时警报系统的结合可以让你立即停止生产有缺陷的产品。这可以节省大量资金,因为生产废品成本很高。

在这里使用流式和实时分析也非常重要。你响应问题的时间越长,产生的浪费就越多,损失的钱也就越多。在制造和生产线上实施实时和流式分析系统可以获得巨大的投资回报。

以下链接将帮助您了解更多关于此用例的信息:

计算机网络中的黑客检测(网络安全)

网络安全的自动威胁检测是异常检测的另一个很好的用例。与其他用例一样,正面事件与负面事件相比非常罕见。然而,这些正面案例的重要性远远超过负面案例。

与以前相比,网络安全问题和泄露对公司的影响要大得多。个人数据可以卖得高价,黑客经常试图窃取这些信息,认为他们可以在电脑背后保持匿名。

威胁和异常检测系统是使用机器学习来检测非正常行为和可能代表入侵的自动化系统。如果公司能够快速应对此类事件,他们可以避免大型公开羞辱运动和可能花费大量金钱的潜在诉讼。

流式和实时系统在这里同样至关重要,因为尽可能减少入侵者行动的时间将大大降低你组织发生任何网络犯罪的风险。

以下两篇文章对这类用例进行了深入探讨:

健康数据中的医疗风险

在过去的几年里,医疗界看到了大量的发明。其中一部分是在个人工具,如智能手表和其他连接的健康设备中,这些设备允许你实时测量自己的健康关键绩效指标。其他用例可以在医院和其他专业医疗保健应用中找到。

当你的健康关键绩效指标出现异常时,立即干预通常至关重要。健康关键绩效指标信号可能在我们人类开始注意到我们的健康状况恶化之前就已经出现。即使是在事件发生后不久,这些信息也能帮助你获得正确的护理,而无需花费大量时间寻找问题的原因。

通常,你的大多数健康指标都会很好,或者至少是可以接受的,直到那个指标告诉你真的出了问题,你需要帮助。在这种情况下,与流式分析而不是批量分析合作很重要。毕竟,如果数据在下个小时或第二天到达,可能对你来说已经太晚了。这也是使用流式分析而不是批量分析的一个强有力的论据。

你可以在这里了解更多信息:

预测性维护和传感器数据

这里将要讨论的最后一个用例是预测性维护的用例。许多公司都有需要预防性维护的关键系统;如果某物损坏,这将会花费大量金钱,甚至更糟。

以航空业为例。如果飞机坠毁,这会损失许多生命。当然,没有公司能预测所有异常,但任何在坠毁前可以检测到的异常都将是一个巨大的胜利。

异常检测可用于许多具有相似问题的行业进行预测性维护;如果你能预测你的关键系统即将失效,你就有足够的时间对需要维护的部分进行维护,从而避免更大的问题。

预测性维护有时可以批量进行,但也可以从流式处理中受益。这完全取决于你从检测到异常到需要干预之间的时间量。

如果你有一个预测性维护模型,可以预测在现在和 30 分钟内飞机发动机的故障,你需要尽快将此数据提供给飞行员。如果你有预测系统告诉你下个月某个部件需要更换,你可能也可以使用批量分析。

要了解更多关于这个用例的信息,你可以查看以下链接:

在下一节中,您将看到异常检测模型与不平衡分类的比较。

比较异常检测和不平衡分类

对于检测正面案例与负面案例,标准的方法族通常是分类。对于上述问题,只要您至少有一些正面和负面案例的历史数据,您就可以使用分类算法。然而,您面临一个非常普遍的问题:只有非常少的观测值是异常的。这是一个通常被称为不平衡数据问题的难题。

不平衡数据的问题

不平衡数据集是指目标类别的发生分布非常不均匀的数据集。一个常见的例子是网站销售:在 1,000 名访问者中,您通常至少有 900 名只是观看和浏览,而可能只有 100 名真正购买了东西。

在不平衡数据上不加区分地使用分类方法容易出错。想象一下,您拟合了一个需要预测每位网站访问者是否会购买东西的分类模型。如果您创建了一个非常糟糕的模型,它只预测每个访问者都不会购买,那么您对 1,000 名访问者中的 900 名的预测仍然是正确的,您的准确率指标将是 90%。

有许多标准方法可以应对这种不平衡数据,包括使用 F1 分数和使用 SMOTE 过采样。

F1 分数

F1 分数是不平衡数据情况下准确率指标的绝佳替代品。准确率是正确预测的数量除以总预测数量。

这是准确率的公式:

公式图片

然而,F1 分数考虑了您模型的精确度和召回率。一个模型的精确度是指预测为正的样本中实际正确的百分比。您模型的召回率显示了您实际上能够检测到的正样本的百分比。

这是精确率的公式:

公式图片

这是召回率的公式:

公式图片

F1 分数将这两个指标结合成一个,使用以下公式:

公式图片

使用这个指标进行评估,您将避免将非常糟糕的模型误认为是好的模型,尤其是在不平衡数据的情况下。

SMOTE 过采样

SMOTE 过采样是第二种你可以用来对抗数据不平衡的方法。这是一种将创建与你的正类数据点非常相似虚假数据点的方法。通过创建一定数量的数据点,你的模型将能够更好地了解正类,并且通过使用原始正类作为来源,你保证新生成数据点不会太偏离。

异常检测与分类

虽然不平衡分类问题有时可以很好地用于异常检测问题,但异常检测被视为机器学习的一个独立类别是有原因的。

主要区别在于理解正类(异常)类的外观的重要性。在分类模型中,你希望有一个模型能够轻松地区分两个(正类和负类)或更多类。为了实现这一点,你希望你的模型学会每个类的外观。模型将寻找描述一个类的变量,以及描述另一个类的其他变量或值。

在异常检测中,你并不真正关心异常类看起来像什么。你更需要的是你的模型学会什么是正常的。只要你的模型对正常类有很好的理解,它就能很好地判断正常与异常。这可以是任何方向和任何意义上的异常。模型不需要之前见过这种类型的异常,只需要知道它不是正常的。

在首次出现异常的情况下,标准分类模型不知道这个观察结果应该被分类到哪个类别。如果你很幸运,它可能进入异常类,但你没有理由相信它会。然而,一个专注于已知与未知之间的异常检测模型能够检测出这种异常是它以前没有见过的,因此将其分类为异常。

在下一节中,你将看到 Python 的 River 包中可用的许多异常检测算法。

River 中检测河流异常的算法

在本章中,你将再次使用 River 进行在线机器学习算法。虽然还有其他库,但 River 是成为在线学习(除了强化学习)首选 Python 包的非常有希望的候选人。

你将看到 River 当前(版本 0.9.0)包含的两个在线机器学习算法,如下所示:

  • OneClassSVM:One-Class SVM 离线版本的在线自适应

  • HalfSpaceTrees:Isolation Forests 的在线自适应版本

你还将看到如何使用常量阈值器和分位数阈值器。

River 异常检测中阈值器的使用

让我们先看看阈值器的使用,因为它们将围绕实际的异常检测算法。

异常检测算法通常会返回一个介于 01 之间的分数,以指示模型观察结果异常的程度。分数越接近 1,越可能是异常值,而分数越接近 0,则被认为更正常。

在实践中,你需要决定一个阈值,以确定每个观察结果是否预期为异常。为了将连续的 01 尺度转换为是/否答案,你使用一个阈值器。

常数阈值器

常数阈值器是最直观的方法。你将给出一个常数,将具有连续(01)异常分数的观察结果分为是/否异常,根据其是否高于或低于该常数。

例如,如果你将 0.95 的值指定为你的常数阈值,那么任何异常分数高于该值的观察结果将被视为异常,而任何得分低于该值的数据点则不被视为异常。

分位数阈值器

分位数阈值器稍微复杂一些。而不是常数,你指定一个分位数。你之前在描述性统计章节中已经见过分位数。0.95 的分位数意味着 95% 的观察结果低于此值,而 5% 的观察结果高于此值。

假设你使用了 0.95 的常数阈值,但模型没有检测到高于 0.95 的点。在这种情况下,常数阈值器将不会将任何观察结果分割到异常类别中。0.95 的分位数阈值器仍然会给你提供正好 5% 的观察结果作为异常。

最佳行为将取决于你的用例,但至少你为 River 中的异常检测准备好了两种选项。

异常检测算法 1 – 单类 SVM

让我们现在转到第一个异常检测算法:单类 SVM。你将首先看到单类 SVM 在异常检测中的一般概述。之后,你将看到它是如何在 River 的在线环境中进行适配的,你还将使用 Python 中的单类 SVM 进行一个 Python 用例。

单类 SVM 在异常检测中的通用应用

单类 SVM 是一种基于支持向量机(SVM)分类算法的无监督异常检测算法。

SVMs 是常用的分类或其他监督学习模型。在监督学习中,它们因使用核技巧而闻名,该技巧将输入映射到高维特征空间。通过这个过程,SVMs 能够生成非线性分类。

如前所述,异常检测算法需要理解什么是正常,但它们不需要理解非正常类别。因此,单类 SVM 是对常规 SVM 的改进。在常规的监督 SVM 中,你需要指定类别(目标变量),但在单类 SVM 中,你将所有数据视为单个类别。

基本上,One-Class SVM 只会拟合一个 SVM,它试图拟合一个模型,该模型最好地预测所有变量作为同一目标类。当模型拟合良好时,个体中预测误差最大的人将会有较低的误差。

对于最佳拟合模型的误差分数高的个体,使用与其他个体相同的模型进行预测是困难的。您可以考虑它们可能需要另一个模型,因此假设这些个体不是来自同一数据生成过程。因此,它们很可能是有异常值的。

误差被用作阈值分数来分割个体。误差分数高的个体可以被认为是异常值,而误差分数低的个体可以被认为是正常的。这种分割通常使用之前引入的量数阈值来完成。

River 中的在线 One-Class SVM

River 中的 OneClassSVM 模型在文档中被描述为 One-Class SVM 的随机实现,并且不幸的是,它不会完美地匹配算法的离线定义。如果对于您的用例来说找到精确的结果很重要,您可以考虑尝试在线和离线实现,并查看它们之间的差异有多大。

通常,异常值检测是一个无监督任务,很难完全确定最终答案和模型的精确度。只要您监控结果并认真对待 KPI 选择和业务结果的跟踪,这并不是一个问题。

用例应用

现在我们使用 River 应用 One-Class SVM 的在线训练过程。

对于这个例子,让我们创建自己的数据集,这样我们就可以确定哪些数据应该被认为是异常值或不是:

  1. 让我们创建一个在 01 之间有 1,000 个观测值的均匀分布变量:

代码块 5-4

import numpy as np
normal_data = np.random.rand(1000)
  1. 当前运行的直方图可以准备如下,但它会因随机性而改变:

代码块 5-5

import matplotlib.pyplot as plt
plt.hist(normal_data)

结果图将显示以下直方图:

图 5.3 – 正常数据的绘图

图 5.3 – 正常数据的绘图

  1. 由于我们非常了解这个分布,我们知道可以期待什么:任何介于 01 之间的数据点是正常的,而任何在 01 之外的数据点都是异常值。现在让我们向数据中添加 1% 的异常值。让我们制作 0.5% 的易于检测的异常值(在 23 之间和 -1-2 之间的随机整数),这非常远离我们的正常分布。让我们也将 0.5% 的异常值设置得稍微难以检测(在 0-1 之间和 12 之间)。

这样我们就可以挑战模型并查看其表现如何:

代码块 5-6

hard_to_detect = list(np.random.uniform(1,2,size=int(0.005*1000))) + \
                  list(np.random.uniform(0,-1,size=int(0.005*1000)))
easy_to_detect = list(np.random.uniform(2,3,size=int(0.005*1000))) + \
                  list(np.random.uniform(-1,-2,size=int(0.005*1000)))
  1. 让我们把所有这些数据放在一起,编写代码以流式传输的方式将其传递给模型,如下所示:

代码块 5-7

total_data = list(normal_data) + hard_to_detect + easy_to_detect
import random
random.shuffle(total_data)
for datapoint in total_data:
  pass
  1. 现在,唯一剩下要做的就是将模型添加到循环中:

代码块 5-8

# Anomaly percentage for the quantile thresholder
expected_percentage_anomaly = 20/1020
expected_percentage_normal = 1 - expected_percentage_anomaly
  1. 在这里,你可以拟合模型:

代码块 5-9

!pip install river
from river import anomaly
model = anomaly.QuantileThresholder(
    anomaly.OneClassSVM(),
    q=expected_percentage_normal
    )
for datapoint in total_data:
    model = model.learn_one({'x': datapoint})

当运行此代码时,您现在已经在我们的合成数据点上训练了一个在线单类 SVM!

  1. 让我们尝试了解它的工作效果。在以下代码中,您可以看到如何获取每个个体的分数以及分配给类别的操作:

代码块 5-10

scores = []
for datapoint in total_data:
    scores.append(model.score_one({'x': datapoint}))
  1. 正如我们所知,实际结果,我们现在可以比较答案是否正确。您可以使用以下代码来完成此操作:

代码块 5-11

import pandas as pd
results = pd.DataFrame({'data': total_data , 'score': scores})
results['actual_outlier'] = (results['data'] > 1 ) | (results ['data'] < 0)
# there are 20 actual outliers
results['actual_outlier'].value_counts()

结果如下所示:

![图 5.4 – 代码块 5-11 的结果图片

图 5.4 – 代码块 5-11 的结果

  1. 以下代码块将计算算法检测到的值的计数:

代码块 5-12

# the algo detected 22 outliers
results['score'].value_counts()

下图显示检测到了 22 个异常值:

![图 5.5 – 代码块 5-12 的结果图片

图 5.5 – 代码块 5-12 的结果

  1. 我们现在应该计算检测到的异常值中有多少是真正的异常值,有多少不是真正的异常值。这将在以下代码块中完成:

代码块 5-13

# in the 22 detected otuliuers, 10 are actual outliers, but 12 are not actually outliers
results.groupby('actual_outlier')['score'].sum()

结果是,在检测到的 22 个异常值中,有 10 个是真正的异常值,但 12 个实际上不是异常值。这可以在以下图中看到:

![图 5.6 – 代码块 5-13 的结果图片

图 5.6 – 代码块 5-13 的结果

获得的结果还不错:至少一些异常值被正确检测到,这可以是一个好的最小可行产品,以开始自动化特定用例的异常检测。让我们看看我们是否能用不同的异常检测算法打败它!

异常检测算法 2 – 半空间树

您在这里将看到的第二个主要异常检测算法是 Isolation Forests 的在线替代品,这是一种常用且性能良好的异常检测算法。

异常检测中 Isolation Forests 的一般使用

Isolation Forests 的工作方式与大多数异常检测算法略有不同。正如本章所述,许多模型通过首先理解正常数据点,然后决定一个数据点是否相对类似于其他正常点来进行异常检测。如果不是,则被视为异常值。

Isolation Forests 是一项伟大的发明,因为它们的工作方式相反。它们试图模拟所有非正常的事物,并试图将这些点从其余部分隔离出来。

为了隔离观察值,Isolation Forest 将随机选择特征,然后在最小值和最大值之间分割特征。隔离样本所需的分割次数被认为是观察值的隔离分数的良好描述。

如果它容易隔离(隔离路径短,相当于有很少的分割来隔离该点),那么它可能是一个相对孤立的数据点,我们可以将其归类为异常值。

它与 River 的变化如何?

在 River 中,模型必须在线训练,他们不得不做一些调整才能使其工作。一些调整被做出来的事实是为什么在 River 中把模型称为HalfSpaceTrees的原因。

作为需要记住的一点,异常值必须在数据集中分散分布,以便模型能够良好工作。此外,模型需要所有值都在01之间。

在异常检测用例中应用半空间树

我们将按以下方式实现:

  1. 让我们现在将半空间树应用于相同的单变量用例,看看会发生什么:

代码块 5-14

from river import anomaly
model2 = anomaly.QuantileThresholder(
    anomaly.HalfSpaceTrees(),
    q=expected_percentage_normal
    )
for datapoint in total_data:
    model2 = model2.learn_one({'x': datapoint})
scores2 = []
for datapoint in total_data:
    scores2.append(model2.score_one({'x': datapoint}))

import pandas as pd
results2 = pd.DataFrame({'data': total_data, 'score': scores2})
results2['actual_outlier'] = (results2 ['data'] > 1 ) | (results2['data'] < 0)
# there are 20 actual outliers
results2['actual_outlier'].value_counts()

该代码块的结果可以在以下图中看到。看起来有 20 个实际异常值:

图 5.7 – 代码块 5-14 的结果

图 5.7 – 代码块 5-14 的结果

  1. 您现在可以使用以下代码计算模型检测到的异常值数量:

代码块 5-15

# the algo detected 29 outliers
results2['score'].value_counts()

看起来算法检测到了 29 个异常值。这可以在以下图中看到:

图 5.8 – 代码块 5-15 的结果

图 5.8 – 代码块 5-15 的结果

  1. 我们现在将计算这 29 个检测到的异常值中有多少实际上是异常值,以查看我们的模型是否有效:

代码块 5-16

# the 29 detected outliers are not actually outliers
results2.groupby('actual_outlier')['score'].sum()

结果显示,我们检测到的 29 个异常值实际上并不是异常值,这表明这个模型不适合这项任务。这实际上并没有什么问题。毕竟,这正是进行模型基准测试的精确原因:

图 5.9 – 代码块 5-16 的结果

图 5.9 – 代码块 5-16 的结果

如您所见,这个模型在当前用例中的性能较差。总之,One-Class SVM 在我们的 1000 次均匀分布在区间01的样本中识别异常方面表现更好。

进一步进行异常检测

要进一步探索异常检测用例,您可以尝试使用不同的数据集,甚至尝试您自己的用例数据集。如您在示例中看到的,数据点作为字典输入。在当前示例中,您使用了单变量数据点:字典中只有一个条目。

在实践中,您通常会遇到多元问题,您的输入会有多个变量。模型可能能够在这样的用例中更好地拟合。

摘要

在本章中,您已经学习了异常检测如何在流和非流环境中工作。这类机器学习模型会收集关于某个情况的一组变量,并使用这些信息来检测特定数据点或观察结果是否可能与其他数据不同。

您已经对这些用例的概述有了了解。其中一些包括 IT 系统的监控或制造业的生产线传感器数据。当数据点与其他数据点差异太大时,异常检测具有很大的附加价值。

你通过实现一个模型基准测试完成了这一章节,在这个基准测试中,你测试了 River 库中的两个在线异常检测模型。你看到其中一个模型能够检测到部分异常,而另一个模型的表现则非常糟糕。这不仅让你了解了异常检测,还让你接触到了模型基准测试和模型评估。

在下一章中,你将看到更多关于这些主题的内容。你将致力于在线分类模型,你将再次看到如何实现模型基准测试和指标,但这次是为了分类而不是异常检测。正如你在本章中看到的,分类有时也可以用于异常检测,这使得这两个用例相互关联。

进一步阅读

第六章:第六章:在线分类

在前两章中,你被介绍了一些基本的分类概念。你首先看到了一个用例,其中 River 中的在线分类模型被用来构建一个可以根据植物的一些特征识别鸢尾花种类的模型。这个鸢尾花数据集是世界上最好的数据集之一,也是分类的非常常见的起点。

之后,你看到了异常检测。我们讨论了在我们可以将异常标记为一类,将非异常标记为另一类的情况下,分类模型可以用于异常检测。特定的异常检测模型通常在任务上表现更好,因为它们努力理解只有非异常。分类模型将努力理解每个类别。

在本章中,你将更深入地学习分类。本章将从定义什么是分类以及它可以用于什么目的开始。然后,你将看到一些分类模型,你将学习它们在线和离线版本之间的区别。你还将使用 River 包在 Python 中实现多个示例。最终,这将导致对稍后将要介绍的使用案例进行模型基准测试研究。

本章将涵盖以下主题:

  • 定义分类

  • 识别分类的使用案例

  • River 中的分类算法

技术要求

你可以在以下链接的 GitHub 上找到本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码样本的最简单方法是以下:

  1. 前往仓库链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件时,在你的本地环境中解压缩它,你将能够通过你偏好的 Python 编辑器访问代码。

Python 环境

为了跟随本书的内容,你可以从仓库下载代码,并使用你偏好的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda(www.anaconda.com/products/individual),它包含 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含 Spyder 和 VSCode,用于编辑脚本和程序。

如果你安装 Python 或相关程序有困难,你可以查看 Google Colab(colab.research.google.com/)或 Kaggle 笔记本(www.kaggle.com/code),这两个都允许你在在线笔记本中免费运行 Python 代码,无需任何设置。

定义分类

在本章中,你将了解分类。分类是一种监督机器学习任务,其中构建了一个模型,该模型将观察结果分配到某个类别。

每个人都倾向于知道的简单类型的分类模型是决策树。让我们考虑一个决策树如何用于分类的超级简单例子。

想象一下,我们有一个包含关于五个人类和五个动物观察结果的数据集。目标是使用这些数据构建一个决策树,该树可以用于任何新的、未见过的动物或人类。

数据可以按以下方式导入:

代码块 6-1

import pandas as pd
# example to classify human vs animal
#dataset with one variable
can_speak = [True,True,True,True,True,True,True,False,False,False]
has_feathers = [False,False,False,False,False,True,True,False,False,False]
is_human = [True,True,True,True,True,False,False,False,False,False]
data = pd.DataFrame({'can_speak': can_speak, 'has_feathers': has_feathers, 'is_human': is_human})
data

数据如下所示:

图 6.1 – 数据

图 6.1 – 数据

现在,为了构建决策树,你通常会使用机器学习,因为手动构建树要低效得多。然而,在这个例子中,让我们构建一个简单的决策树,它的工作方式如下所示:

图 6.2 – 示例决策树

图 6.2 – 示例决策树

当然,这是一个模型,所以它只是对真相的部分表示。它对于当前 10 个观察结果的数据集工作得相当好,但如果有更多的数据点,你会遇到各种异常,因此你需要更多的变量。

你可以用 Python 编写以下代码来为“人类”与“非人类”分类模型编码:

代码块 6-2

def self_made_decision_tree(observation):
    if observation.can_speak:
        if not observation.has_feathers:
            return 'human'
    return 'not human'
for i,row in data.iterrows():
    print(self_made_decision_tree(row))

结果如下:

图 6.3 – 预测结果

图 6.3 – 预测结果

这种方法的背后的一般思想是,任何使用数据生成决策规则以将观察结果分配到特定类别的机器学习模型都是分类模型。在下一节中,我们将探讨一些分类用例,以更好地了解它在实际应用中的用途。

识别分类用例

分类用例非常广泛;它是许多项目中非常常用的方法。尽管如此,让我们看看一些例子,以更好地了解可以从中受益的不同类型的用例。

用例 1 – 电子邮件垃圾邮件分类

基于分类的第一个用例通常是电子邮件中的垃圾邮件检测。垃圾邮件已经存在很长时间了。向人们发送虚假电子邮件以窃取金钱的商业模式是一个大问题,而且收到许多垃圾邮件会负面影响你的电子邮件体验。

电子邮件服务提供商在自动检测垃圾邮件并将其发送到你的垃圾邮件/垃圾箱方面已经走了很长的路。如今,这一切都是自动完成的,并且高度依赖于机器学习。

如果你将此与我们超级小的分类示例进行比较,你可以想象决策树(或任何其他模型)可以接收关于每封接收到的电子邮件的几种信息类型,并使用这些信息来决定该电子邮件是否应该被分类为垃圾邮件。这必须实时完成,因为没有人愿意等待垃圾邮件检测服务最终发送他们的电子邮件。

你可以在以下资源中了解更多关于此用例的信息:

用例 2 – 手机摄像头中的面部检测

分类决策的第二个例子是在你想要解锁手机时进行面部检测。你的手机必须在一瞬间做出决定,看它看到的脸是不是所有者的脸。

这个决策是一个分类决策,因为它最终归结为一个是/否的决策:它是所有者,或者它不是所有者。这个决策通常由机器学习来完成,因为规则会非常复杂,难以用if/else语句写下来。如今,机器学习算法在处理此类用例方面相对较好。

对于此用例的其他更详细示例,你可以查看以下链接:

用例 3 – 在线营销广告选择

最后要添加到前两个用例中的例子是在线营销广告选择。如今,许多网站都会显示个性化的广告。这意味着你将看到与你作为客户相匹配的广告。

个性化广告系统并不是发明广告;它们必须做出决定,在多个可用的广告中选择一个最适合你的。因此,这是一个分类问题,因为它必须在多个选择之间做出决定。

正如你所理解的,页面加载必须快速,因此广告选择也必须在瞬间完成。实时响应对于模型提供任何价值至关重要。

以下链接更深入地讨论了此用例:

在下一节中,你将看到进行分类的更实际的一面,因为你将发现 River Python 库中的几个分类算法。

River 中分类算法概述

River 在线机器学习包中有大量的在线分类模型。

以下是一些相关的选择:

  • LogisticRegression

  • Perceptron

  • AdaptiveRandomForestClassifier

  • ALMAClassifier

  • PAClassifier

分类算法 1 – 逻辑回归

逻辑回归是最基本的统计分类模型之一。它模型化了一个具有两个类别(1 或 0)的因变量(目标变量),并且可以使用多个自变量进行预测。

模型将每个自变量组合为对数几率;你可以将其视为线性回归中的系数,只是它们是每个变量的对数几率。模型中的分割基于逻辑函数。

你可以按照以下简化示意图来理解这个想法:

![图 6.4 – 逻辑曲线]

![图片 B18335_06_4.jpg]

图 6.4 – 逻辑曲线

River 中的逻辑回归

对于在线逻辑回归,你可以在 River 的linear_model部分使用LogisticRegression类。现在让我们看看一个例子:

  1. 首先,你可以通过使用 sklearn 内置的make_blobs函数来创建一个分类数据集,该函数用于创建分类数据集。你可以使用以下代码来完成这个任务:

代码块 6-3

from sklearn.datasets import make_blobs
X,y=make_blobs(shuffle=True,centers=2,n_samples=2000)
  1. 为了了解这个数据集的样子,制作一个图表是很重要的。你可以使用以下matplotlib代码来完成这个任务:

代码块 6-4

import matplotlib.pyplot as plt
plt.scatter(X[:,0], X[:,1], c=y)

你应该得到以下图表,或者类似的东西:

![图 6.5 – 数据]

![图片 B18335_06_5.jpg]

图 6.5 – 数据

  1. 为了确保你的模型评估是公平的,在数据中制作一个训练-测试分割是很重要的。你可以使用 sklearn 的train_test_split来完成这个任务,如下所示:

代码块 6-5

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
  1. 现在让我们继续讨论逻辑回归模型的应用。以下代码展示了如何逐个数据点拟合模型。请注意,你应该使用输入数据x的 JSON 转换,因为这是 River 所要求的:

代码块 6-6

!pip install river
from river import linear_model
model=linear_model.LogisticRegression()
for x_i,y_i in zip(X_train,y_train):  
    x_json = {'val1': x_i[0], 'val2': x_i[1]}
    print(x_json, y_i)
    model.learn_one(x_json,y_i)

打印的数据将看起来像这样:

![图 6.6 – 代码块 6-6 的输出]

![图片 B18335_06_6.jpg]

图 6.6 – 代码块 6-6 的输出

  1. 你也可以逐个进行预测,或者使用predict_many在测试集上一次性进行所有预测。结果不会有任何区别。在以下代码中,使用了predict_many

代码块 6-7

import pandas as pd
preds = model.predict_many(pd.DataFrame(X_test,columns=['val1', 'val2']))
  1. 为了得到这个预测的质量指标,让我们使用scikit-learn的准确度评分。正如你可以在以下代码块中看到的那样,模型在 blob 数据示例上获得了 100%的准确率。必须指出的是,这个 blob 数据示例是一个简单的预测任务,因为数据可以被一条直线完美地分开,正如之前显示的图表中所示:

代码块 6-8

from sklearn.metrics import accuracy_score
accuracy_score(y_test, preds)

这应该会产生以下输出:

![图 6.7 – 代码块 6-8 的输出]

![图片 B18335_06_7.jpg]

图 6.7 – 代码块 6-8 的输出

分类算法 2 – 感知器

感知器是另一个用于分类问题的监督学习算法。它接受输入,将它们乘以权重,并将这些乘积通过激活函数。输出是得到的分类。以下图表显示了一个示例:

![Figure 6.8 – 感知器的示意图]

![img/B18335_06_8.jpg]

图 6.8 – 感知器的示意图

River 中的感知器

与逻辑回归一样,感知器是一个常用的离线模型,River 已经将其改造成在线模型。在 River 中,感知器被实现为逻辑回归的一个特例。

你可以使用感知器就像逻辑回归一样。你可以使用与之前案例相同的代码示例,如下所示:

代码块 6-9

# make data
from sklearn.datasets import make_blobs
X,y=make_blobs(shuffle=True,centers=2,n_samples=2000)
# train test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
# build the model
from river import linear_model
model=linear_model.Perceptron()
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val1': x_i[0], 'val2': x_i[1]}
    model.learn_one(x_json,y_i)
# predict on the test set
import pandas as pd
preds = model.predict_many(pd.DataFrame(X_test,columns=['val1', 'val2']))
# compute accuracy
from sklearn.metrics import accuracy_score
accuracy_score(y_test, preds) 

结果是 1.0,不出所料,这与逻辑回归的结果相同。

分类算法 3 – AdaptiveRandomForestClassifier

在介绍中,你已经看到了决策树背后的基本思想。随机森林是一个改进决策树的集成模型。

随机森林背后的思想是,通过构建大量略微不同的决策树来减少单个决策树的误差。在大量决策树中最常见的预测被保留为最终预测。

决策树通过在每个略微不同的数据集上拟合它们而略有不同,这些数据集是通过重采样观测值创建的。还有一个用于创建决策树分割的变量子集。

River 中的随机森林

对于在线学习,数据需要逐个拟合到随机森林中,这并不是一件容易的事情。River 的实现基于随机森林的两个关键元素,即重采样和变量子集。他们还为每个单个决策树添加了漂移检测:

  1. 让我们使用一个替代的数据创建函数,它创建的数据比 blob 更难分离。这个来自 sklearn 的函数叫做 make_classification

代码块 6-10

# make data
from sklearn.datasets import make_classification
X,y=make_classification(shuffle=True,n_samples=2000)
pd.DataFrame(X).head()

数据如下所示:

![Figure 6.9 – 新数据]

![img/B18335_06_9.jpg]

图 6.9 – 新数据

  1. 默认情况下,生成了总共 20 个变量,其中一些被自动赋予更高的相关性,而一些则大部分无关。让我们像之前一样进行训练-测试分割:

代码块 6-11

# train test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
  1. 使用这个训练-测试分割,我们可以继续构建模型:

代码块 6-12

from river import ensemble
model = ensemble.AdaptiveRandomForestClassifier()
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
  1. 现在模型已经拟合,我们可以在测试集上进行预测。这里没有 predict_many 函数,所以需要通过重复使用 predict_one 来进行循环:

代码块 6-13

# predict on the test set
import pandas as pd
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
  1. 作为最后一步,让我们计算这个模型的准确率:

代码块 6-14

# compute accuracy
from sklearn.metrics import accuracy_score
accuracy_score(y_test, preds)
  1. 结果是 0.86。当然,数据集更难预测,所以这不是一个坏分数。作为额外的指标,我们可以查看分类报告以获取更多信息:

代码块 6-15

# classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, preds))

结果显示在下图中:

图 6.10 – 代码块 6-15 的输出

图 6.10 – 代码块 6-15 的输出

在这个分类报告中,你可以看到精确度、召回率和正负样本的分数都相对均衡。这表明分类器没有不平衡,这在依赖于准确度分数时非常重要。

分类算法 4 – ALMAClassifier

现在你已经看到了一些适应在线学习的常用机器学习分类模型,是时候看看一些更具体的模型了。这些模型中的第一个是 ALMA 分类器。

近似大间隔算法ALMA)分类器是支持向量机SVMs)的增量实现,这是一种常用的机器学习分类模型。

你在前一章中看到了 SVMs 的适应:单类 SVM 通常用于异常检测。对于分类,你会使用常规(双类)SVM。

River 中的 ALMAClassifier

让我们看看 ALMAClassifier 与自适应随机森林如何比较,通过在相同的数据上执行它:

  1. 我们首先应用之前已经定义的相同代码:

代码块 6-16

# make data
from sklearn.datasets import make_classification
X,y=make_classification(shuffle=True,n_samples=2000)
# train test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
from river import linear_model
model = linear_model.ALMAClassifier()
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i) 
# predict on the test set
import pandas as pd
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
# compute accuracy
from sklearn.metrics import accuracy_score
accuracy_score(y_test, preds)
  1. 结果是0.77,不如随机森林。让我们也检查一下分类报告,看看那里是否有什么变化:

代码块 6-17

# classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, preds))
  1. 结果显示在下图中:

图 6.11 – 代码块 6-17 的输出

图 6.11 – 代码块 6-17 的输出

这里有一些轻微的变化,但没有什么看起来太令人震惊。总的来说,随机森林在这个数据上整体表现更好。

分类算法 5 – PAClassifier

被动-攻击PA)分类器是一个在线机器学习模型,它与任何现有的离线模型都不相关。它基于在每一步更新模型的想法,从而解决以下问题:

分类器的更新是通过解决一个约束优化问题来完成的:我们希望新的分类器尽可能接近当前的分类器,同时在最近的例子上至少实现一个单位的间隔。

这段话摘自以下关于 PA 算法的论文,这也是一个有趣的进一步阅读的参考文献:jmlr.csail.mit.edu/papers/volume7/crammer06a/crammer06a.pdf

“被动-攻击”这个名字来源于这样的想法,即从每个新的数据点学习得太快的算法被认为是过于激进。PA 不那么激进。

River 中的 PAClassifier

让我们看看 PA 分类器在执行与之前两个模型相同任务时的表现:

代码块 6-18

# make data
from sklearn.datasets import make_classification
X,y=make_classification(shuffle=True,n_samples=2000)
# train test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
from river import linear_model
model = linear_model.PAClassifier()
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
# predict on the test set
import pandas as pd
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
# compute accuracy
from sklearn.metrics import accuracy_score
accuracy_score(y_test, preds)

获得的分数是0.85。接下来的部分总结了我们所获得的所有分数。

评估基准结果

这为我们留下了过去三个模型的以下准确率分数:

表 6.1 – 包含结果的表格

表 6.1 – 包含结果的表格

最佳结果由自适应随机森林获得,PAClassifier 排名第二。ALMAClassifier 的表现较差,得分为0.77

摘要

在本章中,你首先看到了分类及其用例的概述。你了解了它与异常检测的不同之处,但有时它仍然可以应用于异常检测用例。

你已经了解了五种在线分类模型,其中一些主要是离线模型的改编,而其他一些则是专门设计用于在线工作的。这两种类型都存在,在为最终模型做出选择之前,拥有评估模型性能的工具是很重要的。

你在 Python 中执行的模型基准测试是为了找到在测试集上模型准确率最高的模型。你已经看到了基准测试模型之间的明显差异,这是模型基准测试重要性的一个很好的展示。

在接下来的章节中,你将进行相同类型的模型基准测试练习,但这次你将专注于回归用例,其目标与分类的目标在本质上不同。这涉及到对误差测量和基准测试的一些变化,但从高层次来看,也与你在本章中使用的分类基准测试用例有很多共同之处。

进一步阅读

)

)

)

)

第七章:第七章: 在线回归

在前几章中查看在线异常检测和在线分类之后,还有一个大型在线机器学习类别尚未探讨。回归是适用于目标变量为数值型用例的监督机器学习模型系列。

在异常检测和分类中,你已经看到了如何构建模型来预测分类目标(是/否和鸢尾花种类),但你还没有看到如何处理数值型目标。处理数值数据需要具有不同的方法,不仅在模型训练和定义的深层,也在我们使用指标时。

想象你是一名天气预报员,试图预测明天的温度(摄氏度)。也许你期待一个晴朗的日子,并且你有一个用来预测 25 摄氏度温度的模型。想象一下,第二天你发现天气很冷,只有 18 度;你显然预测错了。

现在,假设你预测了 24 度。在分类用例中,你可能倾向于说 25 度不是 24 度,所以结果是错误的。然而,24 度的结果比 18 度的结果更不错误

在回归中,单个预测可能或多或少是错误的。在实践中,你很少会完全正确。在分类中,你可能对或错,所以这是不同的。这引入了新的指标和模型基准测试过程的变化。

在本章中,你将首先深入了解回归模型,重点关注 River 中的在线回归模型。之后,你将着手进行一个回归模型基准测试。

本章涵盖了以下主题:

  • 定义回归

  • 回归用例

  • River 中回归算法概述

技术要求

你可以在 GitHub 上找到这本书的所有代码,链接如下:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码样本的最简单方法是以下步骤:

  1. 前往存储库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当你下载 ZIP 文件时,在你的本地环境中解压缩它,你将能够通过你首选的 Python 编辑器访问代码。

Python 环境

要跟随本书,你可以下载存储库中的代码,并使用你首选的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda(www.anaconda.com/products/individual),它包含 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含 Spyder 和 VS Code,用于编辑脚本和程序。

如果你安装 Python 或相关程序有困难,你可以查看 Google Colab([colab.research.google.com/](https://colab.research.google.com/))或 Kaggle Notebooks(https://www.kaggle.com/code),这两个都允许你在在线笔记本中免费运行 Python 代码,无需任何设置。

定义回归

在本章中,你将了解回归。回归是一种监督机器学习任务,其中构建了一个模型,根据数值或分类自变量预测或估计数值目标变量。

最简单的回归模型是线性回归。让我们考虑一个线性回归如何用于回归的超级简单例子。

想象一下,我们有一个包含 10 个人观察结果的数据集。基于他们每周的学习小时数,我们必须估计他们的平均成绩(在 1 到 10 的范围内)。当然,这是一个高度简化的问题。

数据看起来如下:

代码块 7-1

import pandas as pd
nb_hrs_studies = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
avg_grade = [5.5, 5.8, 6.8, 7.2, 7.4, 7.8, 8.2, 8.8, 9.3, 9.4]
data = pd.DataFrame({'nb_hrs_studies': nb_hrs_studies, 'avg_grade': avg_grade})
data

你将获得以下数据框:

图 7.1 – 数据集

图 7.1 – 数据集

让我们绘制数据,看看如何将其转化为回归问题:

代码块 7-2

import matplotlib.pyplot as plt
plt.scatter(data['nb_hrs_studies'], data['avg_grade'])
plt.xlabel('nb_hrs_studies')
plt.ylabel('avg_grades')

这将产生以下输出:

图 7.2 – 数据的散点图

图 7.2 – 数据的散点图

现在,线性回归的目标是拟合通过这些点的最佳直线(或超平面),并能够预测任何nb_hrs_studies的估计avg_grades。其他回归模型各自有其构建预测函数的特定方式,但最终目标相同:创建最佳拟合公式,使用一个或多个自变量预测数值目标变量。

在下一节中,你将了解一些回归可以用到的示例用例。

回归的用例

回归的使用案例非常多:它是许多项目中非常常用的方法。尽管如此,让我们看看一些例子,以便更好地了解可以从回归模型中受益的不同类型的用例。

用例 1 – 预测

回归算法的一个非常常见的用例是预测。在预测中,目标是预测随时间测量的变量的未来值。这样的变量被称为时间序列。尽管存在许多特定于时间序列建模的方法,但回归模型在获得良好的未来预测性能方面也是强有力的竞争者。

在一些预测用例中,实时响应非常重要。一个例子是股票交易,其中股票价格的数据点以巨大的速度到达,预测必须立即调整,以使用最佳可能的信息进行股票交易。甚至存在自动化的股票交易算法,它们需要快速反应,以便在交易中尽可能获得最大利润。

要进一步了解这个主题,你可以从检查以下链接开始:

用例 2 – 预测制造中的故障产品数量

实际应用中实时和流式回归模型的第二个例子是在制造中应用预测性维护模型。例如,你可以在生产线每小时故障产品数量的实时预测中使用。这将也是一个回归模型,因为结果是一个数值而不是分类变量。

生产线可以使用这种预测来构建实时警报系统,例如,一旦预测到故障产品的阈值达到。实时数据集成对此非常重要,因为生产出错误的产品会造成大量资源的浪费。

以下两个资源将允许你阅读更多关于此用例的信息:

现在我们已经探讨了回归的一些用例,让我们开始了解我们用于回归的各种算法。

River 中回归算法概述

River 在线机器学习包中提供了大量在线回归模型。

以下是一些相关的选择:

  • LinearRegression

  • HoeffdingAdaptiveTreeRegressor

  • SGTRegressor

  • SRPRegressor

回归算法 1 – LinearRegression

线性回归是最基本的回归模型之一。简单线性回归是通过数据点拟合一条直线的回归模型。以下图表说明了这一点:

图 7.3 – 散点图中的线性模型

图 7.3 – 散点图中的线性模型

这条橙色线是以下公式的结果:

在这里,y 代表 avg_grades,而 x 代表 nb_hrs_studies。在拟合模型时,ab 系数是估计值。此公式中的 b 系数被称为截距。它表示当 x 等于 0y 的值。a 系数代表线的斜率。对于 x 的每一步增加,a 表示添加到 y 中的量。

这是一种线性回归的版本,但还有一种称为多元线性回归的版本,其中存在多个x变量。在这种情况下,模型不表示一条线,而是一个超平面,为每个额外的x变量添加一个斜率系数。

River 中的线性回归

现在让我们继续使用 Python 中的 River ML 构建一个在线线性回归的示例:

  1. 如果你记得前面的例子,我们使用了scikit-learn中的make_classification函数。对于回归问题,也可以使用make_regression

代码块 7-3

from sklearn.datasets import make_regression
X,y = make_regression(n_samples=1000,n_features=5,n_informative=5,noise=100)
  1. 为了更好地了解make_regression函数的结果,让我们检查这个数据集的X。你可以使用以下代码来快速了解数据:

代码块 7-4

pd.DataFrame(X).describe()

describe()方法将输出一个包含变量描述性统计的数据框,如下所示:

图 7.4 – 描述性统计

图 7.4 – 描述性统计

X数据中有五个列,有 1,000 个观测值。

  1. 现在,为了查看y变量,也称为目标变量,我们可以按照以下方式制作直方图:

代码块 7-5

pd.Series(y).hist()

结果直方图可以在以下图中看到:

图 7.5 – 结果直方图

图 7.5 – 结果直方图

这里可以进行更多的探索性数据分析,但这超出了本书的范围。

  1. 现在让我们继续创建训练集和测试集,以创建一个公平的模型验证方法。在下面的代码中,你可以看到如何从scikit-learn创建train_test_split函数来创建训练-测试分割:

代码块 7-6

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
  1. 你可以使用以下代码在 River 中创建线性回归:

代码块 7-7

!pip install river
from river.linear_model import LinearRegression
model = LinearRegression()
  1. 此模型接下来需要拟合到训练数据。我们使用与书中前面看到的相同的循环。这个循环遍历单个数据点(Xy),并将X值转换为字典,这是 River 所要求的。然后,模型通过learn_one方法逐个数据点更新:

代码块 7-8

# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
  1. 一旦模型从训练数据中学习完毕,它需要在测试集上进行评估。这可以通过遍历测试数据并对每个数据点的X值进行预测来完成。y值存储在一个列表中,以便与测试数据集的实际y值进行比较:

代码块 7-9

# predict on the test set
import pandas as pd
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
  1. 我们现在可以计算这个回归模型的我们选择的度量,例如,r2分数。这可以通过以下代码完成:

代码块 7-10

# compute accuracy
from sklearn.metrics import r2_score
r2_score(y_test, preds)

获得的结果是0.478

在下一节中,我们将找出是否有其他模型在此任务上表现更佳。

回归算法 2 – HoeffdingAdaptiveTreeRegressor

我们将要介绍的第二个在线回归模型是一个针对在线回归的更具体的模型。与 LinearRegression 模型一样,许多其他模型只是本质上离线模型的在线自适应版本,许多其他模型是专门为在线模型开发的。HoeffdingAdaptiveTreeRegressor 就是其中之一。

Hoeffding 自适应树回归器HATR)是一个基于 Hoeffding 自适应树分类器HATC)的回归模型。HATC 是一个基于树的模型,它使用 自适应窗口ADWIN)方法来监控树的不同分支的性能。当分支的时间到期时,HATC 方法会用新的分支替换旧的分支。这是通过观察新分支比旧分支有更好的性能来确定的。HATC 也在 River 中可用。

HATR 回归版本基于 HATC 方法,并在每个决策节点使用 ADWIN 概念漂移检测器。这允许该方法检测潜在数据变化,这被称为 漂移。漂移检测将在下一章中更详细地介绍。

River 中的 HoeffdingAdaptiveTreeRegressor

我们将按照以下示例进行检查:

  1. 让我们从在之前模型中使用的数据上拟合模型开始:

代码块 7-11

from river.tree import HoeffdingAdaptiveTreeRegressor
model = HoeffdingAdaptiveTreeRegressor(seed=42)
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
# predict on the test set
import pandas as pd
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
# compute accuracy
from sklearn.metrics import r2_score
r2_score(y_test, preds)
  1. 这个模型获得的 r2 分数略低于线性回归:0.437。让我们看看我们是否能做些什么来让它工作得更好。让我们编写一个网格搜索来看看是否有一组超参数可以帮助提高模型。

为了这个,让我们将模型编写为一个函数,该函数接受超参数的值并返回 r2 分数:

代码块 7-12

def evaluate_HATR(grace_period, leaf_prediction, model_selector_decay):
    # model pipeline
    model = (
        HoeffdingAdaptiveTreeRegressor(
            grace_period=grace_period,
            leaf_prediction=leaf_prediction,
            model_selector_decay=model_selector_decay,
            seed=42)
    )
    # fit the model
    for x_i,y_i in zip(X_train,y_train):
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        model.learn_one(x_json,y_i)
    # predict on the test set
    preds = []
    for x_i in X_test:
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        preds.append(model.predict_one(x_json))
    # compute accuracy
    return r2_score(y_test, preds)
  1. 让我们指定以下要调整的超参数:

代码块 7-13

grace_periods=[0,5,10,]
leaf_predictions=['mean','adaptive']
model_selector_decays=[ 0.3, 0.8,  0.95]
  1. 我们将按以下方式遍历数据:

代码块 7-14

results = []
i = 0
for grace_period in grace_periods:
    for leaf_prediction in leaf_predictions:
        for model_selector_decay in model_selector_decays:
            print(i)
            i = i+1
            results.append([grace_period, leaf_prediction, model_selector_decay,evaluate_HATR(grace_period, leaf_prediction, model_selector_decay)])
  1. 结果可以按以下方式获得:

代码块 7-15

pd.DataFrame(results, columns=['grace_period', 'leaf_prediction', 'model_selector_decay', 'r2_score' ]).sort_values('r2_score', ascending=False)

获得的结果有些令人失望,因为测试的所有值都无法生成更好的结果。不幸的是,这是数据科学的一部分,因为并非所有模型在每个用例上都表现良好。

图 7.6 – 代码块 7-15 的输出结果

图 7.6 – 代码块 7-15 的输出结果

让我们继续到下一个模型,看看它是否更适合。

回归算法 3 – SGTRegressor

SGTRegressor 是一个用于回归的随机梯度树。它是一个基于决策树的模型,可以学习新数据。它是一个增量决策树,通过最小化损失函数来最小化均方误差。

River 中的 SGTRegressor

我们将使用以下示例来检查:

  1. 让我们测试这个模型是否可以提高这个回归任务的表现:

代码块 7-16

from river.tree import SGTRegressor
# model pipeline
model = SGTRegressor()
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
# predict on the test set
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
# compute accuracy
r2_score(y_test, preds)
  1. 结果比之前的模型更差,因为它只有 0.07。让我们再次看看是否可以通过超参数调整来优化它:

代码块 7-17

from river.tree import SGTRegressor
def evaluate_SGT(delta, lambda_value, grace_period):
    # model pipeline 
    model = SGTRegressor(delta=delta,
                        lambda_value=lambda_value,
                        grace_period=grace_period,)
    # fit the model
    for x_i,y_i in zip(X_train,y_train):
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        model.learn_one(x_json,y_i)
    # predict on the test set
    preds = []
    for x_i in X_test:
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        preds.append(model.predict_one(x_json))
    # compute accuracy
    return r2_score(y_test, preds)
  1. 对于这次试验,我们将优化 grace_periodlambda_valuedelta 超参数:

代码块 7-18

grace_periods=[0,10,25]
lambda_values=[0.5, 0.8, 1.]
deltas=[0.0001, 0.001, 0.01, 0.1]
  1. 您可以使用以下代码运行优化循环:

代码块 7-19

results = []
i = 0
for grace_period in grace_periods:
    for lambda_value in lambda_values:
        for delta in deltas:
            print(i)
            i = i+1
            result = evaluate_SGT(delta, lambda_value, grace_period)
            print(result)
            results.append([delta, lambda_value, grace_period,result])
  1. 以下行代码可以显示最佳结果:

代码块 7-20

pd.DataFrame(results, columns=['delta', 'lambda_value', 'grace_period', 'r2_score' ]).sort_values('r2_score', ascending=False)

结果如下所示:

图 7.7 – 代码块 7-20 的输出结果

图 7.7 – 代码块 7-20 的输出结果

结果比未调整的 SGTRegressor 更好,但比前两个模型差得多。该模型可以进一步优化,但似乎不是当前数据的最佳选择。

回归算法 4 – SRPRegressor

SRPRegressor,或流随机补丁回归器,是一种集成方法,它在对输入数据的子集上训练一组基学习器。这些子集被称为补丁,它们是特征和观测值的子集。这与前一章中看到的随机森林方法相同。

River 中的 SRPRegressor

我们将使用以下示例来验证这一点:

  1. 在这个例子中,我们将使用线性回归作为基学习器,因为这个模型与其他在本章测试过的模型相比表现最好:

代码块 7-21

from river.ensemble import SRPRegressor
# model pipeline 
base_model = LinearRegression()
model = SRPRegressor(
    model=base_model,
    n_models=3,
    seed=42
)
# fit the model
for x_i,y_i in zip(X_train,y_train):
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    model.learn_one(x_json,y_i)
# predict on the test set
preds = []
for x_i in X_test:
    x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
    preds.append(model.predict_one(x_json))
# compute accuracy
r2_score(y_test, preds)
  1. 最终得分是 0.34。让我们尝试调整使用的模型数量,看看这能否提高性能:

代码块 7-22

def evaluate_SRP(n_models):
    # model pipeline 
    base_model = LinearRegression()
    model = SRPRegressor(
        model=base_model,
        n_models=n_models,
        seed=42
    )
    # fit the model
    for x_i,y_i in zip(X_train,y_train):
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        model.learn_one(x_json,y_i)
    # predict on the test set
    preds = []
    for x_i in X_test:
        x_json = {'val'+str(i): x for i,x in enumerate(x_i)}
        preds.append(model.predict_one(x_json))
    # compute accuracy
    return r2_score(y_test, preds)
  1. 您可以使用以下代码执行调整循环:

代码块 7-23

results = []
for n_models in range(1, 50):
    results.append([n_models, evaluate_SRP(n_models)])
  1. 以下行显示了每个 n_models 值的结果:

代码块 7-24

pd.DataFrame(results,columns=['n_models', 'r2_score']).sort_values('r2_score', ascending=False)

结果如下所示:

图 7.8 – 代码块 7-24 的输出结果

图 7.8 – 代码块 7-24 的输出结果

显然,在 12 个模型的结果中找到了一个甜点,性能达到 0.457。与得分为 0.478 的简单 LinearRegression 模型相比,这是一个更差的结果。这表明 LinearRegression 模型是本数据集中测试的四个模型中得分最高的。

当然,这个结果与 make_regression 函数背后的数据生成过程密切相关。如果 make_regression 函数添加了诸如时间趋势之类的任何内容,自适应模型可能比简单的线性模型表现更好。

摘要

在本章中,您已经了解了回归建模的基础。您已经了解到分类和异常检测模型之间有一些相似之处,但也存在一些基本差异。

在回归中,主要区别在于目标变量是数值的,而在分类中它们是分类的。这引入了指标上的差异,也影响了模型定义和模型工作的方式。

你已经看到了几个传统的、离线回归模型以及它们如何适应在线训练方式。你也看到了一些专门为在线训练和流式传输设计的在线回归模型。

如前几章所述,你已经看到了如何使用训练-测试集来实现建模基准。机器学习领域仍在不断发展,新的和更好的模型定期发布。这为从业者提出了掌握技能以评估模型的需求。

精通模型评估通常比知道最大的模型列表更重要。你需要了解大量的模型才能开始建模,但正是评估能够让你避免将错误或过拟合的模型推入生产。

尽管这在机器学习中通常是正确的,但下一章将介绍一类对这一点的看法根本不同的模型。强化学习是在线机器学习的一个类别,其重点在于模型更新。在线模型也有能力学习进入系统的每一份数据,但强化学习更加关注几乎自主的学习。这将是下一章的主题。

进一步阅读

第八章:第八章:强化学习

强化学习范式与标准机器学习以及我们在前面章节中介绍过的在线机器学习方法非常不同。尽管对于许多用例来说,强化学习并不总是比“常规”学习更好,但它是一个解决再学习和模型适应的强大工具。

在强化学习中,我们给予模型很大的决策权来进行其再学习和更新决策过程的规则。而不是让模型做出预测并硬编码采取该预测的动作,模型将直接决定采取的动作。

对于自动化机器学习管道,其中动作被有效自动化,这可以是一个很好的选择。当然,这必须辅以不同类型的日志记录、监控等。对于我们需要预测而不是动作的情况,强化学习可能不适用。

尽管在合适的使用场景中非常强大,但强化学习目前在常规机器学习方面并不是一个标准的选择。在未来,强化学习可能会在更多的使用场景中变得非常流行。

在本章中,你将首先全面了解强化学习背后的不同概念。然后,你将看到 Python 中强化学习的实现。

本章涵盖以下主题:

  • 定义强化学习

  • 强化学习模型的主要步骤

  • 探索 Q 学习

  • 深度 Q 学习

  • 使用强化学习处理流数据

  • 强化学习的用例

  • 在 Python 中实现强化学习

技术要求

你可以在 GitHub 上找到本书的所有代码,链接如下:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下:

  1. 前往仓库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 zip

当你下载 ZIP 文件时,你需要在本地环境中解压缩它,然后你将能够通过你首选的 Python 编辑器访问代码。

Python 环境

为了跟随本书的内容,你可以下载仓库中的代码,并使用你首选的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你检查 Anaconda(www.anaconda.com/products/individual),它自带 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和 VSCode,用于编辑脚本和程序。

如果你安装 Python 或相关程序有困难,你可以查看 Google Colab (colab.research.google.com/) 或 Kaggle 笔记本 (www.kaggle.com/code),这两个都允许你免费在线运行 Python 代码,无需任何设置。

注意

书中的代码通常使用 Colab 和 Kaggle 笔记本,Python 版本为 3.7.13,你可以设置自己的环境来模拟这种情况。

定义强化学习

强化学习是机器学习的一个子领域,专注于创建能够做出决策的机器学习模型。有时,这些模型并不被称为模型,而是被称为智能代理。

从远处看,你可能会认为强化学习与机器学习非常接近。我们可以说,它们都是人工智能内部的方法,试图提供智能的黑盒,这些黑盒能够像人类一样学习特定的任务——通常表现得更好。

然而,如果我们更仔细地观察,我们开始看到重要的差异。在前面的章节中,你已经看到了机器学习模型,如异常检测、分类和回归。所有这些模型都使用多个变量,并且能够根据这些变量对目标变量进行实时预测。

你已经看到了许多指标,这些指标允许我们数据科学家决定一个模型是否有所作为。在线模型也能够通过重新学习和持续考虑自己的错误指标来适应变化的数据。

强化学习不仅限于这些。RL 模型不仅做出预测,还采取行动。你可以这样说,离线模型在重新学习时不会从错误中吸取任何自主性,在线模型会立即考虑到错误,而强化学习模型被设计成会犯错误并从中学习。

在线模型可以适应它们的错误,就像强化学习一样。然而,当你构建在线模型的第一版时,你确实期望它在开始时就有可接受的表现,并且你会在一些历史数据上对其进行训练。它可以在数据漂移或其他变化的情况下进行适应。

另一方面,强化学习模型一开始是完全无知的。它会尝试采取行动,犯一些错误,然后纯粹出于偶然,在某个时刻,它也会做出一些好的决策。在这个时候,强化学习模型将获得奖励并开始记住这些。

比较在线和离线强化学习

强化学习通常是在线学习:智能代理通过重复采取行动并获得对良好预测的奖励来学习。这可以无限期地继续,至少在决策反馈持续输入模型的情况下。

然而,强化学习也可以是离线的。在这种情况下,模型会在一段时间内学习,然后在某个时刻,反馈循环被切断,这样模型(决策规则)在那之后保持不变。

通常,当使用强化学习时,是因为我们对持续再学习感兴趣。因此,在线变体是最常见的。

强化学习中反馈循环的更详细概述

现在,让我们更深入地探讨强化学习的细节。首先,了解一个通用强化学习模型的反馈循环是如何工作的非常重要。以下图示显示了模型通过反馈循环学习的逻辑。

![图 8.1 – 强化学习中的反馈循环]

![图片 B18335_08_1.jpg]

图 8.1 – 强化学习中的反馈循环

在这个图示中,你可以观察到以下元素:

  • 强化学习代理:我们这个持续学习和做决策的模型。

  • 环境:一个固定的环境,代理可以在其中做出特定的决策集。

  • 动作:每当代理做出决策时,这将会改变环境。

  • 奖励:如果决策产生好的结果,那么将给予代理奖励。

  • 状态:代理做出决策所需的环境信息。

作为简化的例子,想象一下代理是一个学习行走的婴儿。在每一个时间点,婴儿都在尝试可能使他们行走的事情。更具体地说,他们正在激活身体中的几个肌肉。

在做这件事的时候,宝宝正在观察他们是否在行走。同时,当他们的父母看到他们接近正确行走时,会为他们欢呼。这是一种奖励,传达给宝宝的信息是他们在以正确的方式学习。

然后,宝宝将再次尝试通过几乎使用相同的肌肉,但有一点变化来行走。如果效果更好,他们会将其视为积极的事情,并继续以这种方式移动。

现在我们来讨论所有这些步骤中剩余的必要步骤。

强化学习模型的主体步骤

代理的动作是它可以做出的决策。这是一个有限的决策集。正如你将理解的,代理只是一段代码,所以它所有的决策都需要编程控制其自身的行为。

如果把它比作一个电脑游戏,那么你就能理解作为玩家,你可以执行的动作是受限于你在游戏控制台上的按键。所有的组合加在一起仍然允许一个非常广泛的选项,但它们在某种程度上是有限的。

对于我们人类宝宝学习行走来说,也是如此。他们只能控制自己的身体,因此他们无法执行超出这个范围的动作。这为人类提供了大量的可能性,但仍然是一个固定的动作集。

做出决策

现在,随着您的强化智能体接收有关其环境(状态)的信息,它需要将此信息转换为决策。这与需要将独立变量映射到目标变量的机器学习模型中的相同概念。

在这种强化学习的情况下,这种决策映射通常被称为策略。策略通常会通过估计预期奖励来决定最佳行动,然后执行预期奖励最高的行动。

更新决策规则

强化学习这一大图景描述的最后一部分是策略的更新:基本上,就是学习本身。有许多模型,它们都有自己的特定之处,但无论如何,让我们尽量获得一个一般性的概念。

到目前为止,您已经看到智能体从一组固定的行动中选择一个行动。智能体已经估计出哪个最有可能最大化奖励。在执行此任务后,模型将收到一定的奖励。这将用于改变策略,具体取决于您使用的强化学习方法的精确方法。

在下一节中,您将通过探索 Q 学习算法来更详细地了解这一点。

探索 Q 学习

尽管强化学习有许多变体,但前面的解释应该已经为您提供了一个关于大多数强化模型工作原理的良好概述。现在是时候深入探讨强化学习的一个特定模型:Q 学习。

Q 学习是一种所谓的无模型强化学习算法。无模型强化学习算法可以被视为纯试错算法:它们对环境没有先验概念,只是尝试行动并学习其行动是否产生正确的结果。

相反,基于模型的算法使用不同的理论方法。它们不仅基于行动学习结果,还试图通过某种形式的模型来理解其环境。一旦智能体学会了环境如何运作,它就可以采取根据这种知识优化奖励的行动。

尽管基于模型的方案可能看起来更直观,但无模型的方法,如 Q 学习,实际上相当不错。

Q 学习的目标

Q 学习算法的目标是找到一个策略,该策略从当前状态开始,通过一系列连续步骤获得最大化的预期奖励。

用常规语言来说,这意味着 Q 学习会查看当前状态(其环境的变量),然后利用这些信息在未来采取最佳步骤。该模型不会查看过去发生的事情,只关注未来。

模型使用 Q 值作为状态-动作组合质量的计算:也就是说,对于每个状态,都有一个潜在动作的列表。每个潜在状态和潜在动作的组合称为状态-动作组合。Q 值表示当状态是给定的时,这个动作的质量。

在强化学习过程的开始,Q 的值以某种方式初始化(随机或固定),然后在每次收到奖励时更新。智能体根据 Q 值处理模型,当奖励(对动作的反馈)开始到来时,那些 Q 值会改变。智能体仍然继续遵循 Q 值,但随着它们的更新,智能体的行为也会改变。

该算法的核心是 Bellman 方程:一个用于 Q 值的更新规则,它使用旧的和新的 Q 值的加权平均值。因此,在发生大量学习的情况下,旧信息会在某个时刻被遗忘。这避免了陷入以前的行为中。

Bellman 方程的公式如下:

公式 _08_001

Q 学习算法的参数

在这个 Bellman 方程中,有几个重要的参数你可以调整。让我们简要地介绍一下:

  • 学习率是机器学习算法中一个非常常用的超参数。它通常定义了优化器的步长,大步长可能会让你在优化空间中移动得更快,但过大的步长也可能导致问题进入狭窄的最优解。

  • 折扣因子是一个在金融和经济中经常使用的概念。在强化学习中,它表示模型需要以多快的速度优先考虑短期或长期奖励。

在对 Q 学习的概述之后,下一节将介绍这种方法的更复杂版本,称为深度 Q 学习。

深度 Q 学习

现在你已经看到了强化学习的基础以及最基本的强化学习模型 Q 学习,现在是时候转向一个性能更好、更常用的模型,即深度 Q 学习了。

深度 Q 学习是 Q 学习的一个变体,其中 Q 值不仅仅是状态和动作组合的预期 Q 值列表,这些值由 Bellman 方程更新。相反,在深度 Q 学习中,这种估计是通过一个(深度)神经网络完成的。

如果你不太熟悉,神经网络是一类机器学习模型,在性能方面处于最前沿。神经网络在人工智能、机器学习和数据科学等许多用例中得到了广泛应用。深度神经网络是允许许多数据科学用例的技术,例如自然语言处理NLP)、计算机视觉等等。

神经网络背后的想法是通过一个节点网络(称为神经元)传递输入数据点,每个神经元执行一个非常简单的操作。由于有许多这样的简单操作正在进行,并且之间应用了权重,这意味着神经网络是一种强大的学习算法,可以将输入数据映射到目标变量。

下面的示例展示了神经网络的标准表示。模型可以像你想要的那样简单或复杂。你可以拥有大量的隐藏层,并且可以为每个隐藏层添加尽可能多的节点。每条箭头代表一个系数,需要被估计。因此,必须记住,估计此类模型需要大量的数据。

这里展示了神经网络的示例示意图:

![Figure 8.2 – Neural network architecture

![img/B18335_08_2.jpg]

图 8.2 – 神经网络架构

对于强化学习,这必须在 Q 学习范式中应用。本质上,深度学习模型只是比标准 Q 学习方法更好地估计 Q 值的一种方式(或者至少这是它所追求的)。

你可以将类比看作如下。在标准 Q 学习中,对于新奖励的存储和更新机制相对简单,你可以将其视为如下所示的表格:

![Figure 8.3 – Example table format

![img/B18335_08_3.jpg]

图 8.3 – 示例表格格式

在深度 Q 学习中,输入和输出过程大多相同,但状态被转录为一系列变量,这些变量被输入到神经网络中。然后,神经网络输出每个动作的估计 Q 值。

下面的图表显示了如何将状态作为输入添加到神经网络中。

![Figure 8.4 – Adding the state as input to the neural network

![img/B18335_08_4.jpg]

图 8.4 – 将状态作为输入添加到神经网络

现在你已经理解了强化学习背后的理论,下一节将更加应用性,因为它将展示一些强化学习在流数据上的示例用例。

使用强化学习处理流数据

如前几章所讨论的,在流数据上构建模型的挑战在于找到能够增量学习并且能够在模型漂移或数据漂移的情况下适应的模型。

强化学习是能够很好地应对这两个挑战的潜在候选者。毕竟,强化学习有一个反馈循环,允许它在犯了很多错误时改变策略。因此,它能够在变化的情况下自我适应。

强化学习可以看作是在线学习的一个子案例。同时,强化学习的第二个特性是它专注于学习动作,而常规在线模型则专注于做出准确的预测。

这两个领域的分割在实际中体现在用例类型和应用领域上,但许多流用例有可能从强化学习中受益,并且它是一个值得掌握的优秀工具集。

如果你想要更深入和更多的例子,你可以查看以下有洞察力的文章:www.researchgate.net/publication/337581742_Machine_learning_for_streaming_data_state_of_the_art_challenges_and_opportunities

在下一节中,我们将探讨几个关键用例,在这些用例中,强化学习证明是至关重要的。

强化学习的应用案例

强化学习的应用案例几乎和在线学习一样多。与标准离线和在线模型相比,它是一种较少使用的科技,但随着过去几年机器学习领域的变化,它仍然是一个可能在未来几年变得巨大的优秀候选者。

让我们看看一些用例,以更好地了解哪些类型的用例可能适合强化学习。在示例类型中,有一些是更传统的强化学习用例,还有一些是更具体的流数据用例。

应用案例一 - 交易系统

作为强化学习的一个第一个应用案例,让我们来谈谈股票市场交易。股票市场的用例已经在回归章节的预测用例中讨论过了。强化学习是它的一个替代解决方案。

在回归中,在线模型被用来构建预测工具。使用这些预测工具,股票交易者可以预测特定股票在不久的将来价格的发展趋势,并利用这些预测来决定买入或卖出股票。

使用强化学习,这个用例的开发会有所不同。智能代理会学习如何做决策,而不是预测价格。例如,你可以给代理三个动作:卖出、买入或持有(持有意味着什么都不做/忽略)。

代理会接收到关于其环境的信息,这可能包括过去的股票价格、宏观经济信息等等。这些信息将与策略一起使用,这个策略决定何时买入、卖出或持有。

通过长时间训练这个代理,并且使用包括所有类型市场场景的大量数据,代理可以很好地学习如何交易市场。这样你就可以获得一个盈利的“交易机器人”,无需太多干预就能赚钱。如果成功,这显然比回归模型有优势,因为它们只预测价格而不采取任何行动。

关于这个主题的更多信息,你可以从查看以下链接开始:

用例二 - 社交网络排名系统

强化学习的第二个用例是社交网络帖子排名。背后发生的一般想法是创建大量帖子,并根据用户的偏好向每个特定用户展示最相关的帖子。

有许多机器学习方法可以用于此,强化学习就是其中之一。基本上,模型将最终对要向用户展示的帖子做出决策,因此这种方式实际上是在采取行动。

此操作还会生成反馈。如果用户喜欢、评论、分享、点击、暂停或以其他方式与帖子互动,代理将获得奖励,并学会这种类型的帖子确实对用户感兴趣。

通过试错,代理可以向每个用户发布不同类型的帖子,并学习哪些决策是好的,哪些是不好的。

实时响应在这里非常重要,以及从错误中快速学习。如果用户收到大量不相关的帖子,这将损害他们的用户体验,模型应尽快学会其预测是不正确的。因此,在线学习或强化学习非常适合此类用例。

关于此类用例的更多信息,您可以在以下链接中找到一些资料:

用例三 - 自动驾驶汽车

强化学习还被提议用于自动驾驶汽车的用例。正如您可能知道的,自动驾驶汽车在过去几年中越来越受到关注。目标是创建可以取代人类驾驶员行为的机器学习或人工智能模型。

很容易理解,该模型的关键部分将是采取行动:加速、减速、刹车、转弯等等。如果能够构建一个足够好的强化学习模型来获得所有这些技能,它将是构建自动驾驶汽车的理想候选者。

自动驾驶汽车需要响应大量关于环境的数据流。例如,它们需要在多个摄像头拍摄的视频流中连续检测汽车、道路、路标等等,以及其他可能的传感器。

在这种情况下,实时响应至关重要。在道路上,实时重新训练模型可能会更成问题,因为您希望确保模型在行驶过程中不是在应用试错方法。

更多信息可以在以下链接中找到:

应用案例四 – 聊天机器人

另一个非常不同但同样非常高级的机器学习用例是聊天机器人的开发。智能聊天机器人仍然很少见,但我们预计在不久的将来聊天机器人将变得更加智能。

聊天机器人需要能够对用户提供的信息做出响应,同时处理用户提供的信息。因此,聊天机器人正在执行一种行动:回复人类。

强化学习与其他自然语言处理领域的技术的结合可以解决此类问题。通过让聊天机器人与用户交谈,人类用户可以通过例如对互动有用性的评估等形式给予奖励。这个奖励可以帮助强化学习智能体调整其策略,并在未来的互动中使回复更加合适。

聊天机器人需要能够实时响应,因为没有人愿意等待聊天机器人的回答。学习可以是在线或离线进行的,但强化学习无疑是合适的替代方案之一。

你可以在此处了解更多关于这个用例的信息:

应用案例五 – 学习游戏

作为强化学习的最终用例示例,让我们来谈谈学习游戏的应用。这可能对商业的价值不大,但仍然是一个有趣的强化学习用例。

在过去的几年里,强化学习智能体已经学会了玩许多游戏,包括象棋和国际象棋。在每一步都有明确的移动集合,通过玩许多模拟(或真实)游戏,模型可以学习哪种策略(采取步骤的决策规则)是最好的。

最后,智能体拥有如此强大的策略,它通常能击败世界上最好的游戏玩家。

你可以在以下链接中找到更多此类示例:

现在我们已经探讨了强化学习的一些应用案例,接下来我们将使用 Python 来实现它。

在 Python 中实现强化学习

现在我们来举一个例子,其中使用流数据来进行 Q-Learning。我们将使用的是股票价格的模拟数据:

  1. 数据是在以下代码块中生成的。

首先生成的值列表是一个代表股票价格的 30,000 个连续值的列表。数据生成过程从 0 开始,在每一个时间步,都会添加一个随机值。随机正态值以 0 为中心,这表明价格会根据标准差 1 的步长上升或下降。

这个过程通常被称为随机游走,它可以上升或下降很多。之后,这些值被标准化,再次处于正态分布内。

代码块 8-1

import numpy as np
import matplotlib.pyplot as plt
import random
starting = 0
values = [starting]
for i in range(30000):
    values.append(values[-1] + np.random.normal())
values = (values - np.mean(values)) / np.std(values)
plt.plot(values)

结果图可以在以下内容中看到:

图 8.5 – 前一个代码块的结果图

图 8.5 – 前一个代码块的结果图

  1. 现在,对于强化问题,需要有限数量的状态。当然,如果我们考虑股票价格,我们可以收集到无限多位小数。数据被四舍五入到 1 位小数,以限制可能的状态数据点的数量:

代码块 8-2

rounded_values = []
for value in values:
    rounded_values.append(round(value, 1))
plt.plot(rounded_values)

结果图如下所示:

图 8.6 – 前一个代码块生成的图表

图 8.6 – 前一个代码块生成的图表

  1. 我们现在可以将状态的潜在值设置为过去发生过的所有值。我们还可以启动一个策略。

如本章的理论部分所示,策略代表了强化学习代理的规则。在某些情况下,有一个非常具体的规则集,但在 Q 学习中,只有状态和行动组合的 Q 值(质量)。

在我们的例子中,让我们考虑一个只能同时进行两项操作的股票交易机器人 t。要么在时间 t 买入并在 t+1 卖出,要么在时间 t 卖出并在 t+1 关闭卖出头寸。不深入股票交易,重要的是要理解以下内容:

  • 当代理购买时,它应该这样做是因为它预计股市会上涨。

  • 当代理打开卖出订单时,它应该这样做是因为它预计股市会下跌。

作为信息,我们的股票交易员将非常有限。状态中的唯一数据点是时间 t 的价格。这里的目的是不是要创建一个伟大的模型,而是要展示在股票交易示例上构建强化学习代理的原则。在现实中,你需要状态中的更多信息来决定你的行动:

代码块 8-3

states = set(rounded_values)
import pandas as pd
policy = pd.DataFrame(0, index=states, columns=['buy', 'sell'])
  1. 此后定义的函数是根据 Q 表获取行动(卖出或买入)的方法。将 Q 表完全称为策略并不完全正确,但它确实使它更容易理解。

所选的行动是针对给定状态(状态是当前股票价值)的最高 Q 值:

代码块 8-4

def find_action(policy, current_value):

    if policy.loc[current_value,:].sum() == 0:
        return random.choice([ 'buy', 'sell'])
    return policy.columns[policy.loc[current_value,:].argmax()]
  1. 同时,还需要定义一个更新规则。在这个例子中,更新规则基于之前解释过的贝尔曼方程。然而,请注意,智能体相当简单,折扣部分并不真正相关。折扣有助于使智能体更倾向于短期收益而非长期收益。当前智能体总是在一个时间步长内获得收益,因此折扣没有额外的价值。在真实的股票交易机器人中,这一点非常重要:如果你可以在一年内将其翻倍,你不会把你的钱投在一个需要 20 年才能翻倍的股票上:

代码块 8-5

def update_policy(reward, current_state_value, action):

    LEARNING_RATE = 0.1
    MAX_REWARD = 10
    DISCOUNT_FACTOR = 0.05

    return LEARNING_RATE * (reward + DISCOUNT_FACTOR * MAX_REWARD - policy.loc[current_state_value,action])
  1. 现在我们来执行模型。我们首先将past_state设置为 0,将past_action设置为buy。总奖励初始化为 0,并实例化一个奖励累加列表。

然后代码将循环遍历四舍五入的值。这是一个复制数据流的过程。如果数据一个接一个地到达,智能体将以完全相同的方式学习。其本质是在每个学习步骤更新 Q 表。

在每个迭代中,模型将执行最佳行动,这里的最佳行动是基于 Q 值表(策略)的 Q 值。模型还将收到时间步长 t-1 的奖励,因为这被定义为股票交易机器人的唯一选项。这些奖励将被用来更新 Q 表,以便下一轮可以拥有更新的信息:

代码块 8-6

past_state_value = 0
past_action = 'buy'
total_reward = 0.
rewards = []
for i, current_state_value in enumerate(rounded_values):

    # do the action
    action = find_action(policy, current_state_value)

    # also compute reward from previous action and update state
    if past_action == 'buy':
        reward = current_state_value - past_state_value

    if past_action == 'sell':
        reward = past_state_value - current_state_value

    total_reward = total_reward + float(reward)

    policy.loc[current_state_value, action] = policy.loc[current_state_value, action] + update_policy(reward, current_state_value,action)

    #print(policy)
    rewards.append(total_reward)

    past_action = action
    past_state_value = current_state_value
  1. 在下面的图表中,你可以看到模型是如何获得奖励的。一开始,总奖励长时间为负,然后在最后变为正。请注意,我们是在基于假设的输入数据上学习,这些数据代表的是随机游走。如果我们想要一个真正的智能股票交易机器人,我们需要给它更多、更好的数据:

代码块 8-7

plt.plot(rewards)

生成的图表如下所示:

图 8.7 – 上一代码块生成的图表

图 8.7 – 上一代码块生成的图表

  1. 下面的图表显示了 Q 值与策略的热力图。表格顶部的值是在股价低时首选的行动,而表格底部的值是在股价高时首选的行动。浅黄色表示高质量的行动,黑色表示低质量的行动:

代码块 8-8

import seaborn as sns
sns.heatmap(policy.sort_index())

生成的热力图如下所示:

图 8.8 – 上一代码块生成的热力图

图 8.8 – 上一代码块生成的热力图

有趣的是,模型似乎开始学习股票交易的基本规则:低价买入,高价卖出。这可以通过高价卖出时更多的黄色和低价买入时更多的黄色来观察。显然,这个规则在模拟的随机游走数据中也是成立的。

要学习更高级的规则,代理需要拥有更多状态数据,因此 Q 表也会变得非常庞大。你可以添加的一个例子是价格的历史滚动,这样代理就能知道你是处于上升趋势还是下降趋势。你也可以添加宏观经济因素、情绪估计或任何其他数据。

你还可以使动作结构更加高级。而不仅仅是有一天买卖交易,如果有一个模型可以在代理决定的时候随时买卖其投资组合中的任何股票,那就更有趣了。

当然,你还需要提供足够的数据,以便模型能够对所有这些场景进行估计。你考虑的场景越多,代理学习如何正确行为所需的时间就越长。

摘要

在本章中,你首先了解了强化学习的底层基础。你看到强化学习模型专注于采取行动,而不是做出预测。

你也看到了两种广泛使用的强化学习算法。这始于 Q-learning,它是强化学习的基础算法,以及其更强大的改进版本,深度 Q-learning。

强化学习通常用于更高级的应用场景,如聊天机器人或自动驾驶汽车,但也非常适合用于数值数据流。通过一个用例,你看到了如何将强化学习应用于金融的流数据。

通过本章,你已到达发现最相关的在线学习机器学习模型的终点。在接下来的章节中,你将发现许多额外的工具,这些工具在在线学习中需要考虑,而在传统机器学习中没有真正的对应物。你将首先深入了解所有类型的数据和模型漂移,然后了解如何处理由于灾难性遗忘而完全走偏的模型。

进一步阅读

第三部分:关于流数据的先进概念和最佳实践

本部分将涵盖流数据的一些高级主题,包括漂移和特征转换。我们将通过示例来了解这些概念。最后,我们将总结上一章所学的内容,作为对所有主题的总结。

本节包括以下章节:

  • 第九章**,漂移和漂移检测

  • 第十章**,特征转换和缩放

  • 第十一章**,灾难性遗忘

  • 第十二章**,结论和最佳实践

第九章:第九章:漂移和漂移检测

在前面的章节中,你已经发现了许多构建机器学习ML)模型的方法,这些模型以在线方式工作。它们能够从单个观察值更新其学习到的决策规则,而不是像大多数机器学习模型那样需要完全重新训练。

这之所以很棒,一个原因是流式处理,因为这些模型将允许你持续工作和学习。然而,我们可以争论说,传统的机器学习模型也可以对单个观察值进行预测。即使是批量学习和离线模型也可以一次预测一个新观察值。为了更深入地了解在线机器学习的附加值,本章将深入探讨漂移和漂移检测。

为了更深入地理解这些概念,本章将从漂移的定义开始进行详细描述。然后,你将看到不同类型的漂移,包括概念漂移、数据漂移和重新训练策略问题。

之后,你将接触到多种检测数据漂移和概念漂移的方法。你还将看到多种对抗漂移的方法,并介绍模型可解释性和重新训练策略。

现在,让我们通过深入探讨漂移的定义来从基础知识开始。

本章将涵盖以下主题:

  • 定义漂移

  • 介绍模型可解释性

  • 测量漂移

  • 在 Python 中测量漂移

  • 对抗漂移

技术要求

你可以在以下链接的 GitHub 上找到这本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,以下是最简单下载笔记本和代码样本的方法:

  1. 前往存储库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 zip

当你下载 ZIP 文件时,你需要在本地环境中解压缩它,然后你将通过你首选的 Python 编辑器访问代码。

Python 环境

要跟随这本书,你可以下载存储库中的代码,并使用你首选的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda(www.anaconda.com/products/individual),它包含了 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和Visual Studio CodeVS Code)用于编辑脚本和程序。

如果您在您的机器上安装 Python 或相关程序有困难,您可以查看Google ColabatoryGoogle Colab)(colab.research.google.com/)或 Kaggle Notebooks(www.kaggle.com/code),这两个都允许您在在线笔记本中免费运行 Python 代码,无需任何设置。

注意

书中的代码通常将使用 Colab 和 Kaggle Notebooks,Python 版本为 3.7.13,您可以根据需要设置自己的环境来模拟这种情况。

定义漂移

模型随着时间的推移往往开始表现得更差,这是一个众所周知且普遍观察到的问题。无论您的指标是准确率、R2 分数、F1 分数还是其他任何指标,如果您将模型投入生产而不更新它们,您都会看到性能随着时间的推移缓慢但稳定地下降。

根据您的用例,这种下降可能会迅速或缓慢地变得有问题。一些用例需要连续、近乎完美的预测。在某些用例中——例如,对于对生活有直接影响的专业机器学习模型——如果您观察到 1%的下降,您可能会感到非常震惊。在其他用例中,机器学习更多地被用作自动化而不是预测,商业伙伴可能甚至不会注意到 5%的下降。

是否会影响您不是这里的问题。可以肯定的是,总的来说,您会看到您的模型在下降。本章的目标是确保找出为什么模型性能在下降,您如何衡量它,以及如果您认为它对您的用例来说问题太大,您可以做什么。

在下一节中,我们将首先介绍您可能在流用例中遇到的三种不同类型的漂移。

三种漂移类型

在流数据中,通常考虑两种漂移原因:概念漂移和数据漂移。在本部分中,您将首先发现概念和数据漂移,但您也会看到重新训练策略如何影响模型偏离数据而不是相反。

概念漂移

在概念漂移中,我们试图通过我们建模的概念变化来解释模型性能的下降。这意味着目标变量的统计属性正在变化,因此模型不再适合我们的用例。

概念变化的一个简化例子是试图预测某个巧克力棒热巧克力销售的模型。也许这个模型在一段时间内是完美的,但某个时候,一个竞争品牌在该地区安装了。潜在的需求过程已经改变,这在逻辑上没有被包括在模型中,因为当模型构建时竞争并不相关。

当概念发生变化时,模型需要更新以考虑最新的过程,因为模型对于用例来说不再足够合适。以下示意图展示了概念漂移情况下出现的问题:

图 9.1 – 概念漂移

图 9.1 – 概念漂移

现在你已经了解了概念漂移背后的理论,下一节将介绍数据漂移——第二种重要的漂移类型。

数据漂移

当我们谈论数据漂移时,我们谈论的是独立变量统计属性的变化。这主要与我们处理数据样本(可能只是基于我们可用的数据)相关,但后来我们开始意识到这个样本不再代表我们现在接收到的数据。

例如,测量机器的变化,新的测量设备可能比旧材料给出略微不同的测量结果。想象一下我们更换了温度计,新的温度计测量的温度比旧的高约 0.5 度。你可以理解模型无法考虑这种类型的信息,并且当模型将温度设定得高于实际时,将会做出错误的预测。

以下示意图展示了数据漂移情况下出现的问题:

图 9.2 – 数据漂移

图 9.2 – 数据漂移

在讨论了漂移的两个重要原因之后,下一节将介绍模型退化和误指定——第三个与漂移相关的问题。

模型退化和误指定

虽然在文献中通常不认为模型问题是漂移的问题,但我认为将模型问题作为漂移和性能下降背后的一个问题也很重要。在现实情况下,人类是不完美的,会犯错误。理论上,我们应该期望模型被很好地指定,因此不应成为任何问题的原因。

然而,在实践中,模型的重新训练可能被错误地自动化,从而引入了小问题,这些问题随着时间的推移逐渐累积成大问题,这可能是模型退化性能下降的重要原因。

以下示意图展示了由于任何原因(如误指定或重新训练问题)导致的模型问题出现的问题:

图 9.3 – 模型引起的错误

图 9.3 – 模型引起的错误

在了解了流模型中漂移的三个潜在原因之后,下一节将解释如何使用模型可解释性作为对抗漂移的解决方案。

引入模型可解释性

当模型以在线方式学习时,它们会反复重新学习。这个重新学习的过程是自动发生的,通常人类用户无法持续关注模型。此外,这也会违背进行机器学习的主要目标,因为目标是让机器或模型接管,而不是持续的人类干预。

当模型学习或重新学习时,数据科学家通常面临程序化模型构建接口。想象一下,一个随机森林,其中数百个决策树同时作用来预测新观察的目标变量。即使打印出并查看所有这些决策也是一个巨大的任务。

模型可解释性是机器学习最近进展中的一个重要话题。通过将黑盒模型应用于数据科学用例,正在发生一些重大错误。一个例子是,当自动驾驶汽车在包含过多白人的有偏样本上训练时,汽车被测量出与黑人发生更多事故,仅仅是因为数据科学错误。了解你模型中发生的事情可以产生救命的影响。

在考虑模型中的漂移时,了解你模型中发生的事情也很重要。你部署的第一个模型可能非常接近你的预期。之后,模型将从遇到的每个数据点重新学习。如果数据中存在偏差,或者如果偏差是由于过拟合或欠拟合(当模型在自主运行时发生)而产生的,那么在某个时候,你可能会错过那些趋势。

你需要确保设置一个初始的方法来确保模型的可解释性,并继续研究这个话题。在当前章节中,我们将重点关注数据漂移和概念漂移,但请记住,模型误设也可能是一个导致准确性下降的巨大贡献者。这将在第十一章中更深入地讨论。第十一章。

现在,让我们继续探讨一些衡量漂移的方法。

衡量漂移

对于漂移,有两个重要的事情需要考虑。首先,我们应该能够衡量漂移,因为我们无法对抗我们不知道的东西。其次,一旦我们意识到漂移,我们应该定义正确的策略来对抗它。让我们首先讨论漂移的测量方法。

衡量数据漂移

如前所述,数据漂移意味着测量值随着时间的推移而缓慢变化,而基本概念保持不变。为了衡量这一点,描述性统计可以非常有用。既然你在前面的章节中已经看到了很多描述性统计,我们就不重复介绍其背后的理论了。

要应用描述性统计来衡量数据漂移,我们可以简单地设置一些描述性统计指标,并随着时间的推移跟踪它们。对于每个变量,你可以设置以下内容:

  • 中心性测量(均值、中位数、众数)

  • 变化的测量(标准差、方差、四分位距IQR

  • 变量之间的事件相关性

此外,跟踪特定时间尺度的漂移也是必要的。如果你预计在非常长的时间段内会有漂移,你可以按月甚至按年计算这些描述性统计,但为了更快地检测,可以是每周、每天,甚至每小时或更频繁。

这些指标随时间的变化比较将允许你检测数据中的变化,这可能是你模型中漂移的常见原因。

测量概念漂移

在测量概念漂移时,最好的做法是设置一个详尽的模型性能随时间变化的跟踪。你使用的性能指标将取决于你的用例和所使用的模型类型,但可能包括回归的 R2 分数、准确度、验证的 F1 分数,甚至更多。

当你在时间上测量模型性能时,如果没有进行模型更新,你很可能会看到性能下降。对于在线模型,它们会在每个数据点上重新学习,理论上这应该是一个较小的问题。当你看到你的性能下降时,这表明你的系统中某个地方出了问题。

如果你已经在测量数据漂移,这将是一个很好的起点,因为数据漂移很可能会导致模型性能下降。如果数据漂移没有发生,你系统中可能存在概念漂移。

重要的是要记住,在实践中,测量模型漂移和数据漂移是紧密相连的:很难将性能下降归因于一个特定的根本原因。目标应该是保持模型性能高,如果出现问题,找到解决方案。同时测量性能、个别统计数据以及更多指标将使你的策略对抗漂移更加强大。

现在我们来看一些 Python 示例,说明如何检测建模中的漂移。

在 Python 中测量漂移

在测量漂移时,首先要确保你的模型以某种方式记录日志或结果。在下面的例子中,你将使用一个数据集,其中每个预测都被记录下来,这样我们就有每个预测的输入变量、预测、真实值以及预测与真实值之间的绝对差异作为误差的指标。

记录模型的行为是进行漂移检测的绝对前提条件。让我们从一些基本的测量开始,这些测量可以帮助你使用 Python 检测漂移。

测量漂移的基本直观方法

在本节中,你将发现一种直观的测量漂移的方法。以下是我们将遵循的步骤:

  1. 要开始测量我们记录的结果数据中的漂移,我们首先将数据导入为pandas DataFrame。这在上面的代码块中完成:

代码块 9-1

import pandas as pd
data = pd.read_excel('chapter9datafile.xlsx')
data

你将获得一个看起来像下面这样的表格:

![图 9.4 – 数据]

![img/B18335_09_4.jpg]

图 9.4 – 数据

  1. 现在你有了漂移检测数据,让我们通过在日期上执行groupby操作并查看平均错误来查看错误随时间的发展,如下所示:

代码块 9-2

data.groupby('Day')['Error'].mean()

你将获得以下结果:

![图 9.5 – 结果

![img/B18335_09_5.jpg]

图 9.5 – 结果

你可以清楚地看到错误随着时间的推移而强烈增加,因此我们可以相当肯定地认为我们这里有一个模型漂移的问题。当然,现在还没有定义这个问题是由数据问题还是概念问题引起的。

  1. 让我们通过分析目标变量来查看目标是否在时间上经历了大的变化。以下代码对每天的地面实况值进行平均,以查看目标变量是否出现了明显的漂移:

代码块 9-3

data.groupby('Day')['Ground Truth'].mean()

结果看起来是这样的:

![图 9.6 – 结果(继续

![img/B18335_09_6.jpg]

图 9.6 – 结果(继续)

我们确实看到在这个时期平均上有一个相当重要的变化。

  1. 让我们进一步检查,并为每个独立变量也进行这种分析。以下代码对每天三个独立变量进行平均,以查看是否存在任何明显的偏移:

代码块 9-4

data.groupby('Day')[['X1', 'X2', 'X3']].mean()

你将获得以下结果:

![图 9.7 – 分组结果

![img/B18335_09_7.jpg]

图 9.7 – 分组结果

我们看到第三个解释变量X3发生了非常明显的变化。这很可能就是我们的模型发生偏移的原因。

使用稳健的工具测量漂移

如果你正在处理小型用例,并且不想与大型外部平台集成,那么前面的例子真的很好。然而,如果你在一个资源受限的公司工作,从头开始为常见用例开发代码可能不可行或不值得。

漂移检测是目前相当受欢迎的一个用例,因此越来越多的稳健解决方案被公之于众——无论是付费程序、云程序还是开源解决方案。

接下来,我将介绍一些有用的解决方案,如果你对在外部平台上进行模型性能跟踪和漂移检测用例感兴趣,你可以考虑这些解决方案。由于这些平台属于公司,有时是付费的,我不想在这里深入探讨,但如果你对此感兴趣,提供一些指导是有好处的。

Soda SQL

一个值得关注的解决方案是 Soda SQL。这是一个专门针对数据质量的工具,因此它不一定针对 ML 用例进行了优化。然而,数据质量问题几乎必然会导致有问题的模型,所以我发现讨论这个解决方案很有价值。

您可以在此找到完整信息:docs.soda.io/。Soda SQL 是一个真正面向数据工程工具,所以在这里我不会过多深入细节,但我确实建议您在使用案例中记住它。

MLflow with whylogs

一个更多面向生产中 ML 模型的工具是whylogs开源 Python 库,它与 WhyLabs 应用程序(whylabsapp.com)集成。如果您在 WhyLabs 注册了一个账户,您可以使用他们的应用程序编程接口API)并将您的模型日志直接发送到他们的数据库,在那里它将被分析并可供您访问。

Neptune

Neptune(neptune.ai)也在提供类似的工具。Neptune 的目标也是提供一个机器学习操作MLOps)平台,您可以从基本上任何 Python(或其他)模型环境中发送您的日志数据。之后,您可以在他们的网络平台上访问性能,并且漂移检测的所有繁重工作都从您的肩上卸下。

您现在已经看到了一些用于测量和检测漂移的理论方法,以及一些启动平台,如果您没有能力完成这项工作,它们会为您做这类工作。尽管如此,我们还没有讨论到同样重要的一点,那就是对抗漂移。

对抗漂移

如引言中所述,模型漂移是必然发生的。它可能发生得非常缓慢,也可能发生得很快,但如果我们不尝试积极对抗它,这实际上是无法避免的。

在接下来的章节中,您将意识到在线学习,这本书已经广泛介绍了,实际上是一种对抗漂移的高效方法。尽管我们还没有明确定义漂移,但您现在将理解在线学习在这里有很大的附加值。

我们现在将回顾两种对抗漂移的方法,具体取决于您正在做的工作类型,如下所述:

  • 离线学习的重新训练

  • 在线学习

让我们从最传统的案例开始,即通过实施针对模型衰减的重新训练策略进行的离线学习。

针对漂移的离线学习重新训练策略

离线学习仍然是 ML 中最常用的方法。在离线学习中,模型只训练一次,然后仅用于预测。以下示意图描述了离线学习过程:

图 9.8 – 离线学习示意图

图 9.8 – 离线学习示意图

要更新模型,通常需要重新训练整个模型并将新版本部署到您的生产环境中。这如图图 9.9所示。

这种方法的优势在于模型构建者对其模型有完全控制。没有风险模型会学习到新的——错误——过程。然而,这需要付出代价,即当数据或概念发生漂移时,模型不会更新。因此,它的优势和劣势与在线学习相反。

对抗漂移的在线学习

正如你在本书中看到的,在线学习是离线学习的替代方案,并允许模型在新的数据点到达时随时更新。以下图表说明了重新训练策略是如何工作的:

![Figure 9.9 – Schematic diagram of online learning]

![img/B18335_09_9.jpg]

图 9.9 – 在线学习的示意图

使用在线学习,模型在更新方面具有一定的自主性,理论上将更接近数据:应该发生更少的漂移。然而,这需要模型构建者对理论模型没有完全控制。学习可能会走向错误的方向,模型可能会学习到不想要的决策规则。

优势与离线学习相反,是否选择在线或离线学习将真正取决于业务案例。

摘要

在本章中,你首先被介绍到了模型漂移的潜在基础。你看到,在现实世界的环境中,机器学习模型中模型漂移和模型性能随时间的下降是可以预期的。

性能下降通常可以归因于漂移数据、漂移概念或模型引起的问题。当数据测量随时间变化,但模型背后的基本理论概念保持不变时,就会发生数据漂移。概念漂移捕捉了学习过程的理论基础问题。

模型和模型重新训练相关的问题通常不被视为漂移的标准原因,但它们仍然应该被监控并认真对待。根据你的业务案例,重新学习——尤其是如果缺乏监控——可能会给机器学习系统引入大问题。

通常可以使用描述性统计很好地衡量数据漂移。概念漂移通常更难衡量,但它的存在可以从模型性能的不可解释下降中推断出来。一般来说,这里的重要性不在于将下降的性能归因于特定的原因,而在于使用提供的解决方案之一解决问题。

重新训练策略通常可以用于离线模型。它们是更新模型的一种方式,而不放弃对学习决策规则的控制。正如本书前几章详细介绍的,在线模型是重新训练离线模型的绝佳替代方案。使用在线模型的一个巨大优势是,这些模型专门为重新训练而设计。这些模型允许更大的自主性,只要正确实施数据和模型的监控,它们将在许多业务案例中非常有用。

在下一章中,您将了解如何将特征转换FT)和缩放应用于在线建模案例。FT 和缩放是许多机器学习用例中的标准实践,但由于数据漂移——以及窗口中的偏差——它带来了一些需要考虑的理论难题。

进一步阅读

第十章:第十章:特征转换和扩展

在上一章中,您已经看到了如何在流式和在线机器学习模型中管理漂移和漂移检测。漂移检测,虽然不是机器学习中的主要概念,但在生产中的机器学习是一个非常重要的辅助方面。

尽管许多次要主题在机器学习中都很重要,但一些辅助主题对于在线模型尤为重要。漂移检测尤其重要,因为模型在重新学习时的自主性使得它对开发者或数据科学家来说稍微更黑盒。只要重新训练过程由漂移检测和类似方法正确管理,这就有很大的优势。

在本章中,您将看到另一个对在线机器学习和流式学习有重要影响的次要机器学习主题。特征转换和扩展是在传统、批量机器学习中相对定义良好的实践。它们通常不会带来任何理论上的困难。

在在线机器学习中,扩展和特征转换并不像传统的那样直接。必须将实践适应新数据可能并不完全与原始数据可比的情况。这引发了是否需要在每条新到达的数据上重新拟合特征转换和扩展器的问题,同时也引发了是否这些做法会引入偏差到您已经训练并持续再训练的模型中。

本章涵盖的主题如下:

  • 流式数据数据准备挑战

  • 流式数据的数据扩展

  • 在流式环境中转换特征

技术要求

您可以在以下链接在 GitHub 上找到本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果您还不熟悉 Git 和 GitHub,以下是最简单的下载笔记本和代码示例的方法:

  1. 前往存储库的链接。

  2. 点击绿色的代码按钮。

  3. 选择下载 ZIP

当您下载 ZIP 文件时,在您的本地环境中解压缩它,您将能够通过您首选的 Python 编辑器访问代码。

Python 环境

要跟随本书学习,您可以下载存储库中的代码,并使用您首选的 Python 编辑器执行它。

如果您还不熟悉 Python 环境,我建议您查看 Anaconda (www.anaconda.com/products/individual),它包含 Jupyter Notebook 和 JupyterLabs,这两个都是执行笔记本的绝佳选择。它还包含 Spyder 和 VSCode,用于编辑脚本和程序。

如果你难以在你的机器上安装 Python 或相关程序,你可以查看 Google Colab (colab.research.google.com/) 或 Kaggle Notebooks (www.kaggle.com/code),这两个都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码通常使用带有 Python 版本 3.7.13 的 Colab 和 Kaggle Notebooks,你可以设置自己的环境来模拟这种情况。

流式数据数据准备的挑战

在深入探讨特定算法和解决方案之前,让我们首先讨论一下为什么在处理以流式方式到达的数据时,数据准备可能有所不同。可以识别出多个原因,例如以下内容:

  • 第一个,明显的问题是数据漂移。正如在前一章中详细讨论的那样,由于数据漂移,你的数据趋势和描述性统计可能会随着时间的推移而缓慢变化。如果你的特征工程或数据准备过程过于依赖你的数据遵循某些分布,当数据漂移发生时,你可能会遇到问题。由于前一章已经提出了许多解决方案,因此这个主题将在当前章节中不予考虑。

  • 第二个问题是总体参数未知。在以流式方式观察数据时,你的总体参数估计可能会随着时间的推移而缓慢提高,这是可能的,甚至很可能是。正如在 第三章 中所见,你估计描述性统计的精度会随着你拥有的数据量而提高。当描述性统计估计在提高时,它们随时间变化的事实并不容易修复你的数据准备、特征工程、缩放等公式的公式:

    • 作为这个问题的第一个例子,考虑范围。范围代表你观察到的数据的最大值和最小值。这在数据缩放和其他算法中得到了广泛的应用。现在,想象一下,一批数据中的范围(最小值和最大值)可能与数据的全局范围(全局最小值和全局最大值)不同。毕竟,当新数据到达时,你可能会观察到由于随机抽样的过程而高于或低于你历史数据中观察到的任何值的值。如果你没有正确处理,观察到高于你最大值的值可能会导致缩放时出现问题。

    • 另一个例子是在使用正态分布进行缩放时。你的批次中的标准差和平均值可能与总体标准差和总体平均值不同。这可能导致你的缩放器在一段时间后表现不同,这是一种由你自己的缩放算法引起的数据漂移。显然,这必须避免。

  • 存在许多此类问题的其他情况,包括在分类值中观察到新的类别,这可能导致你的 one-hot 编码器或使用分类变量的模型出现问题。你还可以想象,在数据中出现新的值类型,如 NAs 和 InFs,需要得到妥善管理,而不是让它们引起错误。这在一般情况下是正确的,但在处理流数据时,这往往比处理常规数据造成更多麻烦。

在下一节中,我们将学习缩放是什么以及如何与之合作。

为流数据缩放

在本节的第一个部分,让我们先看看一些用于流式缩放数据的解决方案。在进入解决方案之前,让我们快速回顾一下缩放是什么以及它是如何工作的。

介绍缩放

数值变量可以是任何尺度,这意味着它们可以具有非常高的平均值或很低的平均值,例如。一些机器学习算法根本不受变量尺度的任何影响,而其他机器学习算法则可能受到强烈影响。

缩放是将数值变量缩小范围,并可能缩小其标准差,到一个预指定的范围的做法。这将允许所有机器学习算法在没有问题的前提下从数据中学习。

使用 MinMaxScaler 进行缩放

为了实现这一目标,常用的方法是最小-最大缩放器。最小-最大缩放器将输入变量接受任何范围,并将所有值减少到介于该范围(01)之间,这意味着缩放变量的最小值将是0,缩放变量的最大值将是1。有时,会使用一个替代方案,其中最小值不是0,而是-1

最小-最大缩放的数学公式如下:

使用 StandardScaler 进行缩放

另一个非常常见的缩放方法是标准化。标准化是一种基于统计学的强有力方法,它允许你将任何变量转换回标准正态分布。标准正态分布的平均值为0,标准差为1

标准缩放器的数学公式如下:

缩放变量的值不会在任何特定范围内;缩放变量的新值表示原始值与原始平均值之间的标准差数。一个非常极端的值(想象一下平均值的四到五个标准差之外)将具有四到五个的值,顺便说一下,这个值可以是正的也可以是负的。

选择你的缩放方法

缩放算法的选择取决于用例,通常在机器学习管道中使用不同的缩放方法与不同的算法进行调优是一个好主意。毕竟,缩放方法的选择会影响方法的训练性能。

Min-Max 缩放器众所周知在处理异常值方面有困难。毕竟,一个非常极端的异常值会被设置为最大值,即1。然后,这可能会导致其他值被减少到一个更小的范围。

StandardScaler 处理这个问题的方式更好,因为异常值仍然是异常值,只是在缩放变量中取高值。这同时可能是一个缺点,主要是在你使用需要值在01之间的机器学习算法时。

适应流式上下文缩放

现在我们来看看我们如何将每种方法适应流式数据的情况。我们将从 Min-Max 缩放器开始。

将 MinMaxScaler 适应流式

MinMaxScaler 在固定数据集上工作得很好。它保证了缩放数据的值将在01之间,正如某些机器学习算法所要求的。然而,在流式数据的情况下,这要难得多管理。

当新数据一个接一个地到达(在流中),不可能决定最小值或最大值。毕竟,你不能期望一个值既是最小值又是最大值。当批处理时,也没有保证批最大值高于全局最大值,对于最小值也是如此。

你可以使用训练数据来决定最小值和最大值,但问题是你新的数据可能会高于训练最大值或低于训练最小值。这将导致缩放值超出范围(01)。

解决这个问题的方法是用运行中的最小值和最大值。这意味着你继续更新 MinMaxScaler,以便每次观察到更低的最低值时,你就在 MinMaxScaler 公式中更新最低值,每次观察到更高的最高值时,你就在公式中更新最高值。

这种方法的优点是它保证了你的缩放数据始终在01之间。一个缺点是训练 MinMaxScaler 的第一个值会被缩放得很糟糕。这可以通过使用一些训练数据来初始化 MinMaxScaler 来轻松解决。异常值也可能成为问题,因为一个极端的异常值会强烈影响 MinMaxScaler 的公式,并且在那之后分数会有很大的不同。这可以通过使用如第五章中详细描述的异常值检测方法来解决。

现在我们继续到一个自适应 MinMaxScaler 的 Python 实现:

  1. 为了做到这一点,我们将使用 Python 库River中 MinMaxScaler 的实现。我们将使用以下数据作为这个示例:

代码块 10-1

import numpy as np
data = np.random.randint(0, 100, size=1000)
  1. 可以使用以下代码创建这个数据的直方图:

代码块 10-2

import matplotlib.pyplot as plt
plt.hist(data)

结果直方图看起来如下:

图 10.1 – 代码块 10-2 的结果直方图

图 10.1 – 代码块 10-2 的结果直方图

  1. 现在,为了缩放这些数据,让我们使用来自 River 的 MinMaxScaler 函数。通过遍历数据可以模拟数据以流式方式到达,而使用 learn_one 方法表明数据是逐步更新的:

代码块 10-3

!pip install river
from river import preprocessing
# convert the data to required format
data_stream = [{'x':float(x)} for x in list(data)]
# initialize list for scaled values
data_scaled = []
# initialize scaler
my_scaler = preprocessing.MinMaxScaler()
# streaming
for observation in data_stream:
  # learn (update)
  my_scaler.learn_one(observation)
  # scale the observation
  scaled_obs = my_scaler.transform_one(observation)

  # store the scaled result
  data_scaled.append(scaled_obs['x'])
  1. 现在,将很有趣地看到缩放数据的直方图。它可以创建如下:

代码块 10-4

import matplotlib.pyplot as plt
plt.hist(data_scaled)

直方图如下所示:

图 10.2 – 代码块 10-4 的结果直方图

图 10.2 – 代码块 10-4 的结果直方图

这个直方图清楚地表明我们已经成功将数据缩放到 01 的范围。

现在你已经看到了 MinMaxScaler 的理论和实现,让我们现在看看 StandardScaler,这是该方法的一个常见替代方案。

将标准缩放器适应流式处理

在未来观察更多极端数据时,标准缩放可能遇到的问题,与 Min-Max 缩放中看到的问题并不完全相同。Min-Max 缩放器使用最小值和最大值来计算缩放方法,而标准缩放器使用均值和标准差。

这之所以非常不同,是因为最小值和最大值在某个时间点被超越的可能性相对较高。这将导致缩放值高于 1 或低于 0,这可能会对你的机器学习算法造成真正的问题。

在标准缩放器中,未来发生的任何极端值将影响你对全局均值和标准差的估计,但它们不太可能非常严重地影响它们。毕竟,均值和标准差对少数极端值的观察要敏感得多。

在这个理论考虑的基础上,你可能会得出结论,实际上没有必要更新标准缩放器。然而,无论如何更新它可能更好,因为这是保持你的机器学习方法更新的好方法。这种增加的价值将比使用 Min-Max 缩放器时的影响小,但无论如何,这是一个最佳实践。

你可以使用的一种解决方案是使用 Riverml 包中的 AdaptiveStandardScaler。它使用指数加权的移动平均和方差来确保在不过度强调的情况下考虑数据正态分布的轻微漂移。让我们看看如何使用 AdaptiveStandardScaler 的 Python 示例:

  1. 我们将使用以下数据作为本例:

代码块 10-5

import numpy as np
data = np.random.normal(12, 15, size=1000)
  1. 这组数据遵循正态分布,正如从直方图中所见。你可以按照以下步骤创建直方图:

代码块 10-6

import matplotlib.pyplot as plt
plt.hist(data)

结果直方图如下所示:

图 10.3 – 代码块 10-6 的结果直方图

图 10.3 – 代码块 10-6 的结果直方图

数据明显遵循正态分布,但它不是围绕 0 对齐的,也没有标准化到标准差为 1

  1. 现在,为了扩展这些数据,让我们使用来自 River 的 StandardScaler。同样,我们将遍历数据以模拟流式传输。此外,我们再次使用 learn_one 方法逐步更新数据:

代码块 10-7

from river import preprocessing
# convert the data to required format
data_stream = [{'x':float(x)} for x in list(data)]
# initialize list for scaled values
data_scaled = []
# initialize scaler
my_scaler = preprocessing.StandardScaler()
# streaming
for observation in data_stream:
  # learn (update)
  my_scaler.learn_one(observation)
  # scale the observation
  scaled_obs = my_scaler.transform_one(observation)

  # store the scaled result
  data_scaled.append(scaled_obs['x'])
  1. 为了验证它是否正确工作,让我们使用以下代码重新绘制直方图:

代码块 10-8

plt.hist(data_scaled)

这里展示了直方图:

![图 10.4 – 代码块 10-8 的结果直方图

![img/B18335_10_4.jpg]

图 10.4 – 代码块 10-8 的结果直方图

如您所见,数据明显集中在 0 附近,新的缩放值表示每个数据点与平均值相差多少个标准差。

在下一节中,您将看到如何在流式环境中调整特征转换。

在流式环境中转换特征

数据缩放是机器学习数据预处理的一种方法,但许多其他统计方法也可以用于数据准备。在本章的第二部分,让我们深入了解主成分分析PCA)方法,这是一种在机器学习开始时常用的数据准备方法。

介绍 PCA

PCA 是一种可以用于多种应用的机器学习方法。当处理高度多变量数据时,PCA 可以以解释的方式使用,即您可以使用它来理解并分析多变量数据集。这是数据分析中 PCA 的一个应用。

使用 PCA 的另一种方式是为机器学习准备数据。从高层次的角度来看,PCA 可以被视为缩放的替代方案,它减少了数据变量的数量,使模型更容易拟合。这是与本章最相关的 PCA 应用,这也是它在示例中将如何使用的方式。

PCA 的数学定义

PCA 在多变量数据(或具有多个列的数据)上工作。这些列通常具有业务定义。PCA 的目标是保留数据中的所有信息,但将当前的变量定义转换为具有不同解释的变量。

新变量被称为主成分,它们是以这样的方式找到的:第一个成分包含尽可能多的变化,第二个成分是与第一个成分正交的成分,它解释了尽可能多的变化,同时保持正交。

这里展示了一个示意图:

![图 10.5 – PCA 的示意图概述

![img/B18335_10_5.jpg]

图 10.5 – PCA 的示意图概述

这个例子清楚地展示了原始数据左侧如何转换为右侧的主成分。第一个主成分在信息量方面比任何原始变量都更有价值。当处理数百个变量时,您可以想象您只需要保留有限数量的组件(基于不同的标准和您的用例),这可能使您的机器学习算法更容易从数据中学习。

Python 中的常规 PCA

为了在常规和增量 PCA 之间进行良好的比较,最好让每个人都跟上进度,并先快速举一个常规 PCA 的例子:

  1. 要做到这一点,让我们创建一些模拟样本数据来处理这个例子。我们可以创建如下的小示例数据集:

代码块 10-9

import numpy as np
import pandas as pd
X1 = np.random.normal(5, 1, size=100)
X2 = np.random.normal(5, 0.5, size=100)
data = pd.DataFrame({'X1': X1, 'X2': X1 + X2})
data.head()

数据看起来如下:

![图 10.6 – 结果数据图片

图 10.6 – 结果数据

  1. 你可以按照以下方式绘制此数据:

代码块 10-10

import matplotlib.pyplot as plt
plt.scatter(data['X1'], data['X2'])

散点图显示了一个与早期示意图中草图相当相似的绘图:

![图 10.7 – 代码块 10-10 的结果图像图片

图 10.7 – 代码块 10-10 的结果图像

  1. 现在我们使用常规 PCA 来识别组件并转换数据。以下代码块展示了如何使用scikit-learn来拟合 PCA:

代码块 10-11

from sklearn.decomposition import PCA
my_pca = PCA()
transformed_data = my_pca.fit_transform(data)
transformed_data = pd.DataFrame(transformed_data, columns = ['PC1', 'PC2'])
transformed_data.head()

转换后的数据看起来如下:

![图 10.8 – 转换后的数据图片

图 10.8 – 转换后的数据

  1. 我们可以像处理之前的数据一样绘制它。这可以通过以下代码完成:

代码块 10-12

plt.scatter(transformed_data['PC1'], transformed_data['PC2'])
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()

绘图看起来如下:

![图 10.9 – 转换数据的绘图图片

图 10.9 – 转换数据的绘图

你可以清楚地看到,这非常类似于早期理论介绍中的结果绘图。这个 PCA 成功识别出第一个主成分是解释数据最大部分的组件。第二个组件解释了剩余数据(在第一个组件之后)的最大部分。

增量 PCA 用于流处理

在流处理上下文中,PCA 不能轻易地计算在单个数据点上。毕竟,你可以想象,确定单个数据点的标准差是不可能的,因此,没有可能确定最佳组件。

提出的解决方案是通过批量处理来完成,并且批量计算 PCA 而不是一次性计算。scikit-learn包有一个名为IncrementalPCA的功能,允许你批量拟合 PCA。让我们使用以下代码在之前相同的数据上拟合IncrementalPCA并比较结果。使用IncrementalPCA拟合和转换的代码如下所示:

代码块 10-13

from sklearn.decomposition import IncrementalPCA
my_incremental_pca = IncrementalPCA(batch_size = 10)
transformed_data_2 = my_incremental_pca.fit_transform(data)
transformed_data_2 = pd.DataFrame(transformed_data_2, columns = ['PC1', 'PC2'])
transformed_data_2.head()

使用第二种方法转换的数据看起来如下:

![图 10.10 – 使用增量 PCA 转换的数据图片

图 10.10 – 使用增量 PCA 转换的数据

现在,让我们也绘制这个数据的图,看看这个批处理 PCA 是否成功拟合了真实组件,或者它是否远离原始 PCA:

代码块 10-14

plt.scatter(transformed_data_2['PC1'], transformed_data_2['PC2'])
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()

结果散点图如下所示:

![图 10.11 – 使用增量 PCA 转换数据的散点图图片

图 10.11 – 使用增量 PCA 转换数据的散点图

这个散点图显示 PCA 已经正确拟合。不要被增量 PCA 反转了第一个组件(与前面的图像相比,图像左右镜像)的事实所迷惑。这并不是错误,只是镜像。这个增量 PCA 很好地捕捉了两个组件。

摘要

在本章中,你已经看到了一些常见的数据准备方法被应用于流式和在线数据。对于流式数据,重要的是要能够轻松地重新拟合或重新估计模型。

在本章的第一部分,你已经看到了两种缩放方法。MinMaxScaler 将数据缩放到01的范围,因此需要确保新的数据点不会超出这个范围。StandardScaler 使用基于均值和标准差的统计归一化过程。

本章的第二部分演示了常规 PCA 和一个新的增量版本,称为IncrementalPCA。这种增量方法允许你分批拟合 PCA,这在处理流式数据拟合 PCA 时可能很有帮助。

在本章中,通过缩放和特征转换,以及在前一章中的漂移检测,你已经看到了流式机器学习的许多辅助任务。在接下来的章节中,你将看到机器学习和流式处理的第三个也是最后一个次要主题:灾难性遗忘:在线机器学习中可能发生的一个有影响的问题,会导致模型忘记重要的学习趋势。本章将解释如何检测和避免它。

进一步阅读

第十一章:第十一章:灾难性遗忘

在前两章中,我们开始探讨在线机器学习和处理流数据的许多辅助任务。第九章 讨论了漂移检测和解决方案,第十章 讨论了在流环境中的特征转换和缩放。当前章节介绍了辅助任务列表中的第三个也是最后一个主题,即灾难性遗忘。

灾难性遗忘,也称为灾难性干扰,是机器学习模型在更新后忘记他们所学内容的一种趋势,错误地重新学习正确学习的老旧倾向,因为新数据中学习了新的倾向。

由于在这本书中你已经看到了许多在线模型的例子,你会明白模型的持续更新会带来学习出错的大风险。在关于漂移和漂移检测的章节中已经简要提到,模型学习出错也可以被视为性能退化的真实风险。

然而,漂移通常用于指出独立变量(数据漂移)或独立变量与依赖变量之间的关系(概念漂移)中的漂移。由于灾难性遗忘实际上是模型系数内部的问题,我们实际上不能将灾难性遗忘视为漂移的一部分。

机器学习模型,尤其是在线机器学习模型,通常以相对黑盒的方式使用,这意味着我们关注它们的输出,但并不一定花很多时间查看内部机制。当检测到错误学习到的模式时,这就会成为一个问题。因此,机器学习可解释性也与灾难性遗忘的主题相关,并将被涵盖。

本章将介绍机器学习模型错误更新的问题,我们称之为灾难性遗忘或灾难性推理,以下章节将涵盖:

  • 定义灾难性遗忘

  • 灾难性遗忘的检测

  • 模型可解释性 versus 灾难性遗忘

技术要求

你可以在以下链接的 GitHub 上找到这本书的所有代码:github.com/PacktPublishing/Machine-Learning-for-Streaming-Data-with-Python。如果你还不熟悉 Git 和 GitHub,下载笔记本和代码示例的最简单方法是以下步骤:

  1. 前往存储库的链接。

  2. 点击绿色 代码 按钮。

  3. 选择 下载 ZIP

当你下载 ZIP 文件后,你可以在本地环境中解压它,然后你可以通过你偏好的 Python 编辑器访问代码。

Python 环境

要跟随这本书的内容,你可以下载仓库中的代码,并使用你喜欢的 Python 编辑器执行它。

如果你还不熟悉 Python 环境,我建议你查看 Anaconda(www.anaconda.com/products/individual),它包含了 Jupyter Notebook 和 JupyterLab,这两个都是执行笔记本的绝佳选择。它还包含了 Spyder 和 VS Code,用于编辑脚本和程序。

如果你安装 Python 或相关程序有困难,你可以查看 Google Colab(colab.research.google.com/)或 Kaggle Notebooks(www.kaggle.com/code),这两个都允许你免费在线笔记本中运行 Python 代码,无需任何设置。

注意

书中的代码通常使用 Colab 和 Kaggle Notebooks,Python 版本为 3.7.13,你可以设置自己的环境来模拟这种情况。

引入灾难性遗忘

灾难性遗忘最初被定义为在(深度)神经网络上发生的问题。深度神经网络是一组非常复杂的机器学习模型,由于它们的极端复杂性,能够学习非常复杂的模式。当然,只有在有足够数据的情况下才会是这样。

神经网络已经被研究了几十年。它们过去在数学上很有趣,但由于计算能力的缺乏,实际上无法执行。当前计算能力的进步使得神经网络能够获得它们目前所观察到的普及度。

神经网络的复杂性也使得它们对灾难性遗忘问题很敏感。从高角度来看,神经网络的学习方式是通过多次更新系数,并且在每次更新时,模型应该更好地拟合数据。一个神经网络参数的示意图概述如下:

![图 11.1 – 神经网络中系数数量的示意图

![img/B18335_11_1.jpg]

图 11.1 – 神经网络中系数数量的示意图

在这个示意图中,你可以看到即使是对于一个非常小的神经网络,也有许多系数。节点数量越多,需要估计的参数数量就越大。当与传统统计方法进行比较时,你可以看到进行如此多次遍历的想法相对不同,并且与传统统计学中常见的那些问题不同。

灾难性遗忘就是这样一个问题。它在 1989 年的一项研究中首次被观察到,其中进行了一个实验。这个实验训练神经网络进行加法任务(从 1 + 1 = 2 到 1 + 9 = 10)。测试了一种顺序方法,其中模型首先学习第一个任务,然后在第一个任务掌握后添加一个新任务。

这个实验以及其他实验的结论是,在第一个任务学习之后添加新任务将干扰原始学习模型。他们观察到,新信息需要学习,这种干扰就会越大。最后,他们发现这个问题只发生在顺序学习中。如果你同时学习所有任务,实际上并没有发生再学习,所以遗忘实际上是不会发生的。

对于关于在线学习神经网络中灾难性遗忘的更详细、更科学的资源,我建议查看这里提供的两个链接:

现在我们来看看灾难性遗忘是如何影响在线模型的。

在线模型中的灾难性遗忘

尽管灾难性遗忘最初被识别为神经网络的问题,你可以想象在线机器学习也有持续再学习的相同问题。因此,灾难性遗忘的问题,或者说灾难性推理的问题,也是存在的,并且需要被掌握。

如果模型在每一个新的数据点上更新,预计系数会随时间变化。然而,由于现代机器学习算法非常复杂,具有大量的系数或树,密切关注它们是一项相当困难的任务。

在一个理想的世界里,最有益的目标可能是尽量避免在机器学习中出现任何错误的学习。做到这一点的一种方法是通过密切关注模型性能并实施严格的版本控制系统,以确保即使模型错误地学习任何内容,也不会在生产系统中部署。我们将很快讨论这个话题。

另一种可能的解决方案是与漂移检测方法一起工作,正如你在第九章中看到的。当你密切关注你的模型性能、数据分布以及其他 KPI 和描述性统计时,你应该能够很快地发现问题,这将允许你迅速干预。

作为管理灾难性遗忘的第三个工具,你将在本章中看到更多关于模型可解释性的工具。灾难性遗忘的一个问题是模型过于像一个黑盒。使用模型可解释性领域的工具将帮助你窥视那些黑盒模型。这将允许你基于业务逻辑而不是技术逻辑来检测灾难性遗忘和灾难性推理。将业务逻辑和技术逻辑结合起来,将是一个强大的组合,以预防灾难性遗忘。

检测灾难性遗忘

在本章中,我们将探讨两种不同的方法,您可以使用这些方法来检测灾难性遗忘。第一种方法是实现一个系统,可以在模型学习到新内容后立即检测到问题。为此,我们将分多个步骤实现一个 Python 示例:

  1. 开发一个带有在线学习的模型训练循环。

  2. 向此模型添加直接评估。

  3. 向此模型添加长期评估。

  4. 添加一个系统以避免错误学习时的模型更新。

使用 Python 检测灾难性遗忘

为了处理这个例子,让我们首先实现一个在线回归模型,就像你在本书前面已经看到的那样:

  1. 要做到这一点,我们首先需要生成一些数据。本例中生成数据的代码如下所示:

代码块 11-1

import random
X = [
     1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 
     6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10
]
y = [
     x + random.random() for x in X[:15]] + 
    [x * 2 + random.random() for x in X[15:]
]

如果你看这个代码,你可以看到模式中发生了一个变化。在前 15 个观测值中,y 被定义为 x + random.randint(),这意味着与 x 相同的值,但有一些随机变化。在第 15 个观测值之后,这种变化改变并变为 x * 2 + random.randint,这意味着 x 的两倍加上一些额外的随机变化。这个例子将完美地展示模型如何随着时间更新。

  1. 现在让我们快速绘制这些数据,以便更好地了解这种变化实际上看起来是什么样子。这可以通过以下代码实现:

代码块 11-2

import matplotlib.pyplot as plt
plt.scatter(X, y)

生成的图表如下所示:

图 11.2 – 前一个代码块生成的散点图

图片 B18335_11_2.jpg

图 11.2 – 前一个代码块生成的散点图

第一条线性趋势在 x = 1 到 x = 5 之间明显成立,但从 x = 6 开始,有一个不同且更陡峭的函数继续到 x = 10。

  1. 在本例中,我们将使用 River,因此需要将数据格式化为正确格式。到现在为止,你应该已经掌握了 River 库的数据格式,但如果需要,可以参考以下代码:

代码块 11-3

X_dict = [{'X': x} for x in X]
for X_i, y_i in zip(X_dict, y):
  print(X_i, y_i)

此代码块的结果应该类似于以下内容:

图 11.3 – 前一个代码块生成的输出

图片 B18335_11_3.jpg

图 11.3 – 前一个代码块生成的输出

  1. 现在,让我们将 River 库中的 KNNRegressor 函数添加到这个循环中,并在每个新的数据点处使用 learn_one 方法更新模型。这是通过以下代码完成的:

代码块 11-4

!pip install river
from river.neighbors import KNNRegressor
my_knn = KNNRegressor(window_size=3)
X_dict = [{'X': x} for x in X]
for X_i, y_i in zip(X_dict, y):
  my_knn.learn_one(X_i, y_i)
  1. 我们可以计算这个模型的最终训练误差,以大致了解我们应该期望多少误差。以下代码正是这样做的:

代码块 11-4

preds = []
for X_i in X_dict:
  preds.append(my_knn.predict_one(X_i))
sum_absolute_error = 0
for pred, real in zip(preds, y):
  sum_absolute_error += abs(pred - real)
mean_absolute_error = sum_absolute_error / len(preds)
print(mean_absolute_error)

在当前示例中,这计算出一个平均绝对误差为 10。

  1. 现在,让我们更详细地看看模型的逐步学习质量。我们可以通过使用持续评估来实现这一点。这意味着每次我们学习时,我们都会评估模型:

代码块 11-5

my_knn = KNNRegressor(window_size=3)
X_dict = [{'X': x} for x in X]
step_by_step_error = []
for i in range(len(X_dict)):
  my_knn.learn_one(X_dict[i], y[i])
  abs_error = abs(my_knn.predict_one(X_dict[i]) - y[i])
  step_by_step_error.append(abs_error)
  1. 以下代码将绘制这些误差随时间的变化,以查看模型是如何学习的:

代码块 11-6

plt.plot(step_by_step_error)

以下图是此代码的结果:

![Figure 11.4 – 上述代码块产生的图

![img/B18335_11_4.jpg]

图 11.4 – 上述代码块产生的图

有趣的是,模型似乎每次我们看到 x 的新值时都会获得满分,然后当相同的 x 值再次出现时,我们再次获得满分,但第三次,我们有一个更大的误差!

  1. 与最终误差进行比较将非常棒,这个最终误差不是逐步计算的,而是通过以下代码一次性计算的:

代码块 11-7

preds = []
for X_i in X_dict:
  preds.append(my_knn.predict_one(X_i))
all_errors = []
for pred, real in zip(preds, y):
  all_errors.append(abs(pred - real))
plt.plot(step_by_step_error)
plt.plot(all_errors)
plt.show()

以下代码块的结果如下所示:

![Figure 11.5 – 上述代码块产生的图

![img/B18335_11_5.jpg]

图 11.5 – 上述代码块产生的图

你可以清楚地观察到,当逐步评估模型时,每个数据点的误差似乎并不太大。然而,当最终评估时,你会发现模型实际上已经忘记了最初的数据点!这是一个很好的例子,说明了灾难性遗忘如何在实践中被观察到。

  1. 作为最后一步,让我们在模型循环中添加一个小型评估,帮助你意识到模型已经忘记了你的第一个分数:

代码块 11-8

my_knn = KNNRegressor(window_size=3)
X_dict = [{'X': x} for x in X]
step_by_step_error = []
for i in range(len(X_dict)):
  my_knn.learn_one(X_dict[i], y[i])
  abs_error = abs(my_knn.predict_one(X_dict[i]) - y[i])
  step_by_step_error.append(abs_error)
  all_errors_recomputed = []
  for j in range(i):
    orig_error = step_by_step_error[j]
    after_error = abs(my_knn.predict_one(X_dict[j]) - y[j])
    if after_error > orig_error:
      print(f'At learning step {i}, data point {j} was partly forgotten')

在这个代码块中,制定了一条规则,当误差大于原始误差时立即检测到遗忘。当然,这是一个非常严重的检测机制,你可以想象用其他方法来替代这个方法。例如,这可以是一个百分比变化或一个绝对数值,不能超过。这完全取决于你的业务案例。

既然你已经看到了使用基于模型性能的警报机制来检测灾难性遗忘的方法,那么让我们继续本章的下一部分,你将看到如何使用模型可解释性来检测灾难性遗忘。

模型可解释性与灾难性遗忘

查看模型性能通常是一个跟踪模型的好方法,它肯定会帮助你检测到模型中某个地方出现了错误。通常,这将足够作为一个警报机制,并帮助你管理生产中的模型。

然而,如果你想确切地了解出了什么问题,你需要更深入地挖掘你的模型。仅仅查看性能更像是一种黑盒方法,而我们可以提取诸如树、系数、变量重要性等东西,以了解模型内部实际发生了什么变化。

没有一种适合所有情况的深入模型研究方法。所有模型类别都有自己特定的方法来拟合数据,因此对它们的拟合检查将强烈依赖于模型本身。然而,在本节的剩余部分,我们将探讨机器学习中的两种非常常见的结构:具有系数的线性模型和树。

使用线性系数解释模型

在这个第一个例子中,我们将对一些样本数据建立线性回归,并提取模型的系数来解释它们:

  1. 你可以使用以下代码创建此示例的数据:

代码块 11-9

import pandas as pd
ice_cream_sales = [10, 9, 8, 7, 6, 5, 4, 3, 2 , 1]
degrees_celsius = [30, 25, 20, 19, 18, 17, 15, 13, 10, 5]
price  = [2,2, 3, 3, 4, 4, 5, 5, 6, 6]
data = pd.DataFrame({
    'ice_cream_sales': ice_cream_sales,
    'degrees_celsius': degrees_celsius,
    'price': price
})
data

数据以数据框格式在此显示:

图 11.6 – 前一个代码块生成的图表

图 11.6 – 前一个代码块生成的图表

  1. 让我们创建两个散点图,以更好地了解冰淇淋销量与温度和价格(在这个虚构的例子中)的关系。以下代码显示了如何创建第一个散点图:

代码块 11-10

plt.scatter(data['degrees_celsius'], data['ice_cream_sales'])

这导致以下图表:

图 11.7 – 前一个代码块生成的图表

图 11.7 – 前一个代码块生成的图表

  1. 第二个散点图可以创建如下:

代码块 11-11

plt.scatter(data['price'], data['ice_cream_sales'])

这导致以下图表:

图 11.8 – 前一个代码块生成的图表

图 11.8 – 前一个代码块生成的图表

你可以清楚地看到,当温度较高时,销售额也较高,而当温度较低时,销售额较低。此外,高价与低销量相关,低价与高销量相关。

  1. 这些是冰淇淋销量中的两个逻辑上可解释的因素,但这还不是模型。让我们使用LinearRegression函数来模拟这种直接的线性关系:

代码块 11-12

from sklearn.linear_model import LinearRegression
my_lr = LinearRegression()
my_lr.fit(X = data[['degrees_celsius', 'price']], y = data['ice_cream_sales'])
  1. 我们可以如下评估此模型的(样本内)拟合度:

代码块 11-13

from sklearn.metrics import r2_score
r2_score(data['ice_cream_sales'], my_lr.predict(data[['degrees_celsius', 'price']]))

此模型产生一个训练 R2 分数为 0.98,这意味着模型与训练数据拟合得非常好。

  1. 我们现在处于需要比仅仅查看性能更深入地了解模型的步骤。在线性回归中,我们需要查看系数来解释它们拟合的内容。系数在以下代码中提取:

代码块 11-14

pd.DataFrame({'variable': ['degrees_celsius', 'price'], 'coefficient': my_lr.coef_})

这给出了以下输出:

图 11.9 – 前一个代码块生成的系数

图 11.9 – 前一个代码块生成的系数

你可以这样解释:

  • 在恒定价格下,每增加一度摄氏度将使冰淇淋销量增加 0.15。

  • 在恒定温度下,每增加一欧元的价格将使冰淇淋销量减少 1.3。

使用树状图解释模型

当查看系数对于线性模型来说很棒时,一些模型没有任何系数。这类模型的例子基本上是任何使用树的模型。树有节点,这些节点基于是/否问题进行分割。虽然您不能从树中提取系数,但优势是您可以简单地打印出整个树作为图形!我们将在下一个示例中查看这一点:

  1. 要开始,我们需要在之前使用的数据上拟合一个 DecisionTreeRegressor 函数,使用以下代码:

代码块 11-15

from sklearn.tree import DecisionTreeRegressor
my_dt = DecisionTreeRegressor()
my_dt.fit(X = data[['degrees_celsius', 'price']], y = data['ice_cream_sales'])
  1. 为了得到模型是否拟合的一般概念,让我们在训练集上计算一个 R2 分数,就像我们之前做的那样:

代码块 11-16

r2_score(data['ice_cream_sales'], my_dt.predict(data[['degrees_celsius', 'price']]))

结果是 1.0,这意味着决策树在训练数据上获得了完美的拟合。没有任何保证这将在样本外泛化,但这对于解释模型来说不一定是问题。

  1. 要将树提取为图像,您只需使用这里的代码:

代码块 11-17

import sklearn
plt.figure(figsize=(15,15))
sklearn.tree.plot_tree(my_dt)
plt.show()

这将打印出整个树,并让您深入了解预测是如何做出的:

![图 11.10 – 前一个代码块生成的树状图图片

图 11.10 – 前一个代码块生成的树状图

使用变量重要性解释模型

作为解释模型的第三种和最后一种方法,您可以查看变量重要性。再次强调,这并不是所有机器学习模型都适用的。然而,对于相当复杂的模型来说,查看所有树状图和变量重要性估计通常是一个很好的替代方案。

让我们从之前构建的决策树模型中提取变量重要性。这可以通过以下代码完成:

代码块 11-18

pd.DataFrame({'variable': ['degrees_celsius', 'price'], 'importance': my_dt.feature_importances_})

生成的数据框如下所示:

![图 11.11 – 重要性值图片

图 11.11 – 重要性值

这告诉我们,决策树比价格更使用了摄氏度作为预测变量。

摘要

在本章中,您已经看到了灾难性遗忘如何导致模型性能不佳,尤其是在数据以序列方式到达时。特别是当首先学习一个趋势,然后跟随第二个趋势时,忘记第一个趋势的风险是真实的,需要得到控制。

虽然没有一劳永逸的解决方案来处理这些问题,但有许多事情可以做到,以避免不良模型进入生产系统。您已经看到了如何实现连续评估指标,以及您将能够检测到一些趋势已经被遗忘。

基于性能的指标非常适合检测问题,但无法告诉你模型内部究竟发生了什么具体错误。你已经看到了三种模型解释的方法,这些方法可以帮助你进一步深入到大多数模型中。通过从模型中提取模型学习到的趋势或关系,你可以确定这是否对应于已知的业务逻辑或常识。

在本书的下一章和最后一章中,我们将总结所介绍的不同主题,并考虑在处理在线模型和流数据时需要牢记的一些最佳实践。

进一步阅读

第十二章:第十二章:结论和最佳实践

在本书的各章节中,你已经了解了流数据机器学习的领域,主要使用在线模型。在本章的最后,是对本书前十一章所看到的所有内容的回顾性概述。

本章将涵盖以下内容:

  • 需要牢记的最佳实践

  • 学习旅程的下一步

  • 最佳实践

实践总是与理论不同。在这本书中,你已经看到了很多理论知识。在本章的最后部分,你将看到一些在将理论应用于实际案例时始终需要牢记的最佳实践:

  1. 清洁数据/数据质量

数据质量和数据理解问题在大多数公司中是日常问题。有句著名的话说道:“垃圾输入,垃圾输出”,这意味着当你对垃圾数据进行机器学习时,你的输出也将是无用的。对于任何新业务案例,都需要进行彻底的数据探索,以便你能够识别潜在的问题。数据质量流程通常需要,但尚未实施。尽管数据科学家的工作范围不包括这些,但你仍然可以就这些流程的必要性和附加价值提出建议。

  1. 业务需求优先于技术需求

许多数据科学家有数学、统计学或编程背景,并且通常有运营思维。在业务案例中经常出现的问题是,技术人员开始做技术上最好的事情,并开始偏离业务项目定义的路线。虽然这非常可以理解,但始终牢记公司为何投资于特定技术非常重要,因为 99%的情况下,这是为了投资回报。在技术工作中,重要的是要记住你的技术主题是否有助于公司实现其目标,因为在不利的案例中,你可能会迅速失去资金。

  1. 业务指标是项目成功的关键

帮助公司实现目标往往还不够。你还必须能够向公司的领导证明,你确实在为他们赚钱或实现他们的目标。为了证明这一点,指标是你的最佳朋友。只要你在项目开始时以及整个过程中定义了项目的关键绩效指标,你就可以确保项目持续获得支持,让你能够从事尖端技术的工作。

  1. 保持兼容性,将公司指南作为自己的指南

长期成功的项目最终将根据贵公司的最佳实践进行评估。如果你想确保你的工作对公司有长期的相关性,你需要确保记住公司总体上在做什么,并确保你所做的是与之兼容的。允许与整体架构不兼容就是为自己设置失败。

  1. 技术指标对于避免问题非常有效

当项目变得庞大时,可能难以全面了解生产环境中正在运行的所有内容。同样,当使用业务指标时,你可以设置技术指标,帮助你快速轻松地了解你所拥有的一切以及它们的运行情况。随着项目的扩大,监控将变得更加重要,但实施起来也会更加困难。因此,最佳实践是在尽可能早的时候实施监控。

  1. 不要重复造轮子

许多数据科学家和机器学习工程师是技术爱好者,我们有自己关于如何实现某些功能的想法和愿景。然而,有一点需要注意,那就是避免重复造轮子。当解决方案已经存在,无论是公司内部还是外部,通常很有用,可以调查是否可以重用它们,而不是通过再次开发完全相同的功能来“重复造轮子”。

下一节将指导你在流式分析领域进一步扩展你的知识和技能。

进一步学习

本书的主要重点是为你提供进入流式分析领域的必要工具,并相对快速地转向数据科学在线机器学习算法。

如果你想在流式分析方面更进一步,通常有两个方向可以开始你的旅程:深度优先或广度优先。

深度优先的方法包括比当前书籍所涵盖的更深入地研究在线机器学习。尽管你现在应该已经对在线机器学习有了坚实的基础,但总有更多东西可以学习。以下是一些有用的资源:

广度优先的方法会建议你首先关注辅助领域,以便全面掌握架构主题、数据工程、代码效率等方面。以下资源可能有助于你在这一方向上的探索:

  • 研究云架构并获得最受欢迎的认证:

  • 提高你的编码技能:

    • Codewars、HackerRank 以及其他编程竞赛工具。

    • 开源贡献

  • 学习新的工具和环境:

    • 在 PySpark 中学习机器学习。

    • 在 R 中学习机器学习。

    • 在 Julia 中学习机器学习。

    • Databricks。

    • Dataiku。

    • AWS SageMaker。

两个方向之间的重点有很大的不同。在你旅程的终点,你可能想要考虑你对两个方向的见解。对你来说最相关的方向将始终取决于你的个人目标。

摘要

在本章的最后,你看到了一系列最佳实践,这些实践将帮助你成功且高效地将这一理论应用于实践。最后,你看到了两条你可能想要在继续你的流分析学和在线模型学习之旅时遵循的学习路径。

就这样,我们来到了这本书的结尾。我希望这本书对你有所帮助,并且你在实际应用这些主题时能够取得成功。撰写关于这个主题的文章是一种乐趣,我相信它很快就会受到广泛关注。它将是数据科学和机器学习未来趋势中值得关注的话题之一。当然,你可以随时关注出版商和我,在线获取即将发布的关于流处理、数据科学以及更多相关主题的新材料。

Packt.com

)

订阅我们的在线数字图书馆,全面访问超过 7,000 本书籍和视频,以及领先的工具,帮助您规划个人发展并推进您的职业生涯。更多信息,请访问我们的网站。

第十三章:为什么订阅?

  • 通过来自超过 4,000 位行业专业人士的实用电子书和视频,节省学习时间,更多时间编码

  • 通过为您量身定制的 Skill Plans 提高您的学习效果

  • 每月免费获得一本电子书或视频

  • 完全可搜索,便于快速访问关键信息

  • 复制粘贴、打印和收藏内容

您知道 Packt 为每本书都提供电子书版本,包括 PDF 和 ePub 文件吗?您可以在packt.com升级到电子书版本,并且作为印刷书客户,您有权获得电子书副本的折扣。有关更多信息,请联系我们customercare@packtpub.com

www.packt.com网站上,您还可以阅读一系列免费的技术文章,订阅各种免费通讯,并享受 Packt 书籍和电子书的独家折扣和优惠。

您可能还会喜欢的其他书籍

如果您喜欢这本书,您可能对 Packt 的其他书籍也感兴趣:

Azure Databricks 食谱

Phani Raj, Vinod Jaiswal

ISBN: 9781789809718

  • 从各种 Azure 资源和文件格式中读取和写入数据

  • 使用 Delta Tables 和 Azure Synapse Analytics 构建现代数据仓库

  • 探索作业、阶段和任务,并了解 Spark 懒加载是如何工作的

  • 处理并发事务并在 Delta 表中学习性能优化

  • 学习 Databricks SQL 并在 Databricks SQL 中创建实时仪表板

  • 通过 CI/CD 管道集成 Azure DevOps 进行版本控制、部署和生产化解决方案

  • 了解如何使用 RBAC 和 ACLs 来限制数据访问

  • 为近实时数据分析构建端到端数据处理管道

人工智能基础设施研讨会

Bas Geerdink, Anand N.S., Kunal Gera, Gareth Dwyer

ISBN: 9781800209848

  • 掌握人工智能的基础知识

  • 了解数据存储和架构在 AI 应用中的重要性

  • 使用开源工具构建数据存储和工作流程管理系统

  • 使用 Docker 等工具容器化您的 AI 应用程序

  • 发现常用的数据存储解决方案和适用于 Amazon Web Services (AWS)的 AI 最佳实践

  • 使用 AWS CLI 和 AWS SDK 执行常见的数据任务

Packt 正在寻找像您这样的作者

如果你有兴趣成为 Packt 的作者,请访问authors.packtpub.com并今天申请。我们已与成千上万的开发者和技术专业人士合作,就像你一样,帮助他们将见解分享给全球科技社区。你可以提交一个一般性申请,申请我们正在招募作者的特定热门话题,或者提交你自己的想法。

分享你的想法

现在你已经完成了《使用 Python 进行流数据机器学习》,我们非常想听听你的想法!如果你在亚马逊购买了这本书,请点击此处直接进入该书的亚马逊评论页面,分享你的反馈或在该购买网站上留下评论。

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

你可能还会喜欢的其他书籍

你可能还会喜欢的其他书籍

posted @ 2025-09-03 10:21  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报