原文:
zh.annas-archive.org/md5/c21b4d7a66ecf910d571f88f5162d065
译者:飞龙
前言
人工智能 (AI) 正在迅速在各行各业找到实际应用,物联网 (IoT) 是其中之一。开发人员正在寻找方法使 IoT 设备更智能,从而使用户的生活更轻松。通过这本 AI 菜谱书,您将学习如何利用 IoT 数据实现智能分析,获取洞察,预测结果并做出知情决策,同时涵盖促进各种 IoT 应用中的分析和学习的高级 AI 技术。
本书采用基于配方的方法,将引导您完成数据收集、数据分析、建模、统计和监控以及部署等基本过程。您将使用智能家居、工业 IoT 和智能设备的真实数据集来训练和评估简单和复杂的模型,并使用训练过的模型进行预测。后续章节将指导您面对实施机器学习、深度学习和其他 AI 技术(如自然语言处理 (NLP)、计算机视觉和嵌入式机器学习)时面临的主要挑战,以构建智能 IoT 系统。除此之外,您还将学习如何轻松部署模型并提高其性能。
通过阅读本书,您将能够打包和部署端到端的 AI 应用程序,并将最佳实践解决方案应用于常见的 IoT 问题。
本书适合的读者
如果您是 IoT 从业者,希望整合 AI 技术来构建智能 IoT 解决方案,但又不想深究太多 AI 理论,那么本 AI IoT 书籍非常适合您。数据科学家和 AI 开发人员希望构建以 IoT 为重点的 AI 解决方案,也会发现本书非常有用。理解本 AI 书籍中涵盖的概念,需要掌握 Python 编程语言和基本的 IoT 概念。
本书涵盖的内容
第一章,设置 IoT 和 AI 环境,将专注于为成功设置正确的环境。您将学习如何选择满足 AI 需求的设备,无论该模型是否需要在边缘或云中运行。您还将学习如何与设备内的模块、其他设备或云安全通信。最后,您将设置一种方式将数据导入云中,然后设置 Spark 和 AI 工具来分析数据、训练模型和扩展运行机器学习模型。
第二章,处理数据,讨论了确保任何格式数据可以被数据科学家有效使用的基础知识。
第三章,物联网的机器学习,将讨论使用诸如逻辑回归和决策树等机器学习模型来解决常见的 IoT 问题,如分类医疗结果、检测不安全的驾驶员和分类化学读数。
第四章,预测性维护的深度学习,将专注于各种分类技术,以使物联网设备成为智能设备。
第五章,异常检测,将解释当警报检测不能分类特定问题时,如何导致问题的发现,以及如果设备表现异常,您可能希望派出维修工人检查设备。
第六章,计算机视觉,将讨论在云端实现计算机视觉以及在诸如 NVIDIA Jetson Nano 之类的边缘设备上实现计算机视觉。
第七章,自助点餐亭的 NLP 和机器人,将讨论使用 NLP 和机器人使用户能够在餐厅点餐亭上与用户进行交互。
第八章,微控制器和管道优化,将讨论如何利用强化学习与智能交通信号交汇处,做出减少交通等待时间、促进交通流畅的交通灯决策。
第九章,边缘部署,将讨论将预训练的机器学习模型应用于边缘设备的各种方式。本章将详细讨论物联网边缘。部署是 AI 管道的重要组成部分。本章还将讨论如何使用 TensorFlow.js 和 ONNX 将机器学习模型部署到 Web 应用程序和移动应用程序。
为了从本书中获得最大收益
读者应具备软件开发的基本理解。本书使用 Python、C、Java 语言。了解如何在这些语言中安装库和包,以及基本的编程概念如数组和循环将有所帮助。以下是几个有助于您复习不同语言基础的网站:
为了从本书中获得最大收益,对机器学习原理的基本理解将是有益的。本书使用的硬件是现成的传感器和常见的物联网开发套件,可以从 Adafruit.com 和 Amazon.com 等网站购买。大多数代码在不同设备之间是可移植的。用 Python 编写的设备代码可以轻松移植到各种微处理器,如树莓派、Nvidia Jetson、Lotte Panda,有时甚至是个人电脑。用 C 语言编写的代码可以移植到多种微控制器,如 ESP32、ESP8266 和 Arduino。用 Java 编写的代码可以移植到任何 Android 设备,如平板电脑或手机。
本书在一些实验中使用了 Databricks。Databricks 有一个免费版本可在community.cloud.databricks.com
获取。
如果您使用本书的数字版本,我们建议您自行输入代码或通过 GitHub 存储库(下一节中提供的链接)访问代码。这样做将有助于避免与复制粘贴代码相关的任何潜在错误。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件,链接为github.com/PacktPublishing/Artificial-Intelligence-for-IoT-Cookbook
。如果代码有更新,将在现有的 GitHub 存储库中进行更新。
我们还有来自我们丰富书籍和视频目录的其他代码包,可以在**github.com/PacktPublishing/
**上获取。看看吧!
下载彩色图像
我们还提供一份 PDF 文件,其中包含本书使用的截图/图表的彩色图像。您可以在这里下载它:static.packt-cdn.com/downloads/9781838981983_ColorImages.pdf
。
使用的约定
本书中使用了多种文本约定。
CodeInText
:表示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄。以下是一个例子:“这将给您一个运行中容器的列表。然后,打开/data
文件夹。”
代码块设置如下:
import numpy as np
import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms, models
from torch.utils.data.sampler import SubsetRandomSampler
任何命令行输入或输出如下所示:
cd jetson-inference
mkdir build
cd build
粗体:表示一个新术语、一个重要词或屏幕上看到的词。例如,菜单或对话框中的单词会以这种方式出现在文本中。这里是一个例子:“点击新项目瓦片。然后,填写创建新项目向导。”
警告或重要说明会显示为这样。
提示和技巧会显示为这样。
部分
在本书中,您将会发现几个经常出现的标题(准备工作、如何做、它是如何工作的、更多信息和另请参阅)。
为了清晰地说明如何完成一个配方,请按照以下部分操作:
准备工作
本节告诉您在配方中可以期待什么,并描述设置任何软件或配方所需的任何初步设置的方法。
如何做…
本节包含完成该配方所需的步骤。
它是如何工作的…
本节通常包括对上一节中发生的事情的详细解释。
更多信息…
本节包含有关配方的附加信息,以使您更加了解配方。
另请参阅
本节为配方提供了有用信息到其他有用信息的链接。
联系我们
我们的读者的反馈总是受欢迎的。
总体反馈:如果您对本书的任何方面有疑问,请在您的邮件主题中提及书名,并发送电子邮件至 customercare@packtpub.com
。
勘误:尽管我们已尽一切努力确保内容的准确性,但错误是不可避免的。如果您发现本书中的错误,请向我们报告。请访问 www.packtpub.com/support/errata,选择您的书籍,点击勘误提交表单链接并输入详细信息。
盗版:如果您在互联网上发现我们作品的任何形式的非法副本,我们将不胜感激您提供地址或网站名称。请发送邮件至 copyright@packt.com
并附上材料链接。
如果您有兴趣成为作者:如果您在某个专题上拥有专业知识,并且有兴趣撰写或贡献书籍,请访问 authors.packtpub.com。
评论
请留下您的评论。一旦您阅读并使用了本书,为什么不在您购买它的网站上留下评论呢?潜在的读者可以看到并使用您的客观意见来做购买决策,我们在 Packt 可以了解您对我们产品的看法,我们的作者可以看到您对他们书籍的反馈。谢谢!
要了解有关 Packt 的更多信息,请访问 packt.com。
第一章:设置 IoT 和 AI 环境
物联网(IoT)和人工智能(AI)正在对人们的生活产生重大影响。像医疗这样的行业正在通过可穿戴传感器的革命性进展来监测病人出院后的情况。在工业设备上使用的机器学习(ML)通过异常检测、预测性维护和指导性行动等技术,实现了更好的监控和更少的停机时间。
构建能够提供结果的 IoT 设备依赖于收集正确信息。本书提供支持端到端 IoT/ML 生命周期的配方。下一章提供了确保设备配备正确传感器和数据最佳化以支持 ML 结果的配方。工具如解释性因子分析和数据收集设计被广泛使用。
本章将涵盖以下主题:
-
Choosing a device
-
设置 Databricks
将涵盖以下配方:
-
设置 IoT Hub
-
设置 IoT Edge 设备
-
将 ML 模块部署到 Edge 设备
-
设置 Kafka
-
在 Databricks 上安装 ML 库
Choosing a device
在开始经典的食谱格式化烹饪书之前,我们将从涵盖几个基础主题开始。选择合适的硬件为 AI 铺平道路。在 IoT 中工作意味着要处理各种限制。在云端使用 ML 通常是一种经济高效的解决方案,只要数据量不大。图像、视频和音频数据通常会拖慢网络速度。更糟的是,如果使用蜂窝网络,成本可能会非常高昂。谚语在硬件上没有钱指的是大多数 IoT 赚钱来自销售服务,而不是生产昂贵设备。
Dev kits
通常,公司会让电气工程师设计他们的设备,这是一种经济高效的选择。定制板不会有额外的组件,比如不必要的蓝牙或额外的 USB 端口。然而,在板设计时预测 ML 模型的 CPU 和 RAM 需求是困难的。入门套件可以是在了解硬件需求之前使用的有用工具。以下板块是市场上被广泛采用的:
-
Manifold 2-C 与 NVIDIA TX2
-
i.MX 系列
-
LattePanda
-
树莓派类
-
Arduino
-
ESP8266
它们通常被用作功能的标尺。例如,树莓派类设备在自定义视觉应用方面可能会有些吃力,但在音频或通用 ML 应用方面表现良好。许多数据科学家的一个决定性因素是编程语言。ESP8266 和 Arduino 需要用低级语言如 C 或 C++进行编程,而像树莓派类及以上的设备可以使用任何语言。
不同的设备具有不同的价格和功能。类似树莓派或更高级别的设备可以处理边缘 ML 运行,减少云成本但增加设备成本。决定是对设备收取一次性价格还是订阅模式可能有助于确定需要的设备类型。
Manifold 2-C with NVIDIA TX2
NVIDIA Jetson 是运行复杂 ML 模型(如边缘实时视频)的最佳选择之一。NVIDIA Jetson 配备内置的 NVIDIA GPU。该产品的 Manifold 版本设计用于安装在 DJI 无人机上,并执行图像识别或自主飞行等任务。运行 NVIDIA Jetson 的唯一不利之处是其使用的 ARM64 架构。ARM64 在 TensorFlow 上表现不佳,尽管其他库如 PyTorch 在 ARM64 上运行良好。Manifold 的零售价为$500,这使其成为高价位选择,但在边缘实时 ML 方面通常是必要的:
价格 | 典型模型 | 使用案例 |
---|---|---|
$500 | 强化学习,计算机视觉 | 自主飞行无人机,机器人技术 |
i.MX 系列
i.MX 系列芯片是开源的,具有强大的 RAM 和 CPU 能力。开放设计有助于工程师轻松构建板卡。i.MX 系列使用 Freescale 半导体。Freescale 半导体保证 10 到 15 年的生产周期,这意味着板卡设计在未来数年内将保持稳定。i.MX 6 的成本可在$200 到$300 之间,并且可以轻松处理 CPU 密集型任务,例如实时流视频中的目标识别:
价格 | 典型模型 | 使用案例 |
---|---|---|
$200+ | 计算机视觉,自然语言处理 | 情感分析,人脸识别,物体识别,语音识别 |
LattePanda
单板计算机(SBCs)如 LattePanda 能够运行大量传感器工作负载。这些设备通常可以运行 Windows 或 Linux。像 i.MX 系列一样,它们能够在设备上运行目标识别;然而,识别对象的帧率可能较慢:
价格 | 典型模型 | 使用案例 |
---|---|---|
$100+ | 人脸检测,语音识别,高速边缘模型 | 启用音频的信息亭,高频率心脏监测 |
树莓派类型
树莓派是物联网的标准入门套件。以$35 的价格标签,它们为成本提供了很多功能:它们可以在边缘上通过容器运行 ML。它们具有 Linux 或 IoT Core 操作系统,可以轻松插拔组件,并有开发者社区构建类似的平台工具。尽管树莓派类型设备能够处理大多数 ML 任务,但在某些更具挑战性的任务(如视频识别)上可能存在性能问题:
价格 | 典型模型 | 使用案例 |
---|---|---|
$35 | 决策树,人工神经网络,异常检测 | 智能家居,工业物联网 |
Arduino
在$15 处,Arduino 是一种经济实惠的解决方案。Arduino 得到了大型社区的支持,使用 Arduino 语言,一组 C/C++函数。如果需要在 Arduino 设备上运行 ML 模型,则可以将建立在诸如 PyTorch 等流行框架上的 ML 模型打包到嵌入式学习库(ELL)中。ELL 允许在设备上部署 ML 模型,而无需大型操作系统的开销。由于 Arduino 的内存和计算能力有限,使用 ELL 或 TensorFlow Lite 移植 ML 模型可能具有挑战性:
价格 | 典型模型 | 使用案例 |
---|---|---|
$15 | 线性回归 | 传感器读数分类 |
ESP8266
在低于$5 的情况下,诸如 ESP8266 和更小的设备代表了一类设备,它们接收数据并将其传输到云端进行 ML 评估。除了价格低廉外,它们通常也是低功耗设备,因此可以通过太阳能、网络电源或长寿命电池供电:
价格 | 典型模型 | 使用案例 |
---|---|---|
$5 或更低 | 仅在云中 | 仅在云中 |
设置 Databricks
在单台计算机上处理大量数据是不可能的。这就是像 Spark(由 Databricks 制作)这样的分布式系统的用武之地。Spark 允许您在许多计算机上并行处理大量工作负载。
Spark 的开发是为了帮助解决Netflix Prize,该奖项为开发最佳推荐引擎的团队提供了 100 万美元的奖金。Spark 使用分布式计算来处理大型和复杂的数据集。还有分布式 Python 等效库,如 Koalas,它是 pandas 的分布式等效版本。Spark 还支持需要大量计算和内存的分析和特征工程,如图理论问题。Spark 有两种模式:用于训练大型数据集的批处理模式和用于几乎实时评分数据的流处理模式。
IoT 数据通常是庞大且不平衡的。设备可能有 10 年的数据表明它在正常条件下运行,只有少数记录表明需要立即关闭以防止损坏。Databricks 在 IoT 中的价值是双重的。首先是处理数据和训练模型。在 TB 和 PB 级别上处理数据可能会超出单个机器的能力。Databricks 通过其可扩展性来解决这个问题。其次是其流处理能力。ML 模型可以在云中几乎实时地运行。然后可以将消息推送回设备。
设置 Databricks 非常简单。您可以访问您的云服务提供商,在门户中注册帐户或注册免费社区版。如果您要将产品投入生产,则应该选择 Azure、AWS 或 Google Cloud。
IoT 和 ML 从根本上讲是一个大数据问题。设备可能在数年间发送遥测数据,直到发送表明设备存在问题的遥测数据为止。从数据管理的角度来看,搜索数百万或数十亿条记录以找到所需的少数记录可能是具有挑战性的。因此,优化的数据存储是关键。
存储数据
如今有一些工具可以帮助处理大量数据变得更加容易。但需要记住几件事情。有一些优化的数据存储方式可以使处理大数据集更加容易。
处理数据时,来自 IoT 设备的大型数据集类型可能对许多公司来说成本过高。例如,将数据存储在 Delta Lake 中可以比通过 JSON 访问数据提供 340 倍的性能提升。接下来的三节将介绍三种可以将数据分析工作从数周缩短到数小时的存储方法。
Parquet
Parquet 是大数据中最常见的文件格式之一。Parquet 的列式存储格式使其能够存储高度压缩的数据。其优势在于占用硬盘空间少,网络带宽消耗小,非常适合加载到 DataFrame 中。Parquet 在 Spark 中的数据导入速度比 JSON 快 34 倍。
Avro
Avro 格式是 IoT 中常用的存储格式。虽然它没有 Parquet 那样的高压缩比,但由于使用了行级数据存储模式,存储数据的计算成本较低。Avro 是流数据(如 IoT Hub 或 Kafka)的常见格式。
Delta Lake
Delta Lake 是由 Databricks 在 2019 年发布的开源项目。它将文件存储在 Parquet 中。此外,它还能够跟踪数据的检入,使数据科学家能够查看特定时间点的数据。这在尝试确定特定 ML 模型精度下降原因时非常有用。它还保存有关数据的元数据,使其在分析工作负载中比标准 Parquet 提供 10 倍的性能提升。
虽然在选择设备和设置 Databricks 时都需要考虑,但本章的其余部分将采用模块化、基于配方的格式。
设置 IoT Hub
开发 IoT 解决方案可能会很复杂。需要处理的问题包括 ML、边缘部署、安全性、监控设备状态以及云中遥测数据的摄取等。云服务提供商(如 Azure)提供了一个现成的解决方案,其中可能包括数据存储和云到设备消息等组件。
在本配方中,我们将在 Azure 中为 IoT Edge 设备设置 IoT Hub,该设备将在边缘上进行 ML 计算。
准备工作
在使用 IoT Hub 之前,您需要拥有一个设备和一个 Azure 订阅。如果您还没有订阅,可以使用免费试用订阅。您还需要某种类型的设备。
如何做…
要设置 IoT Hub,首先需要一个资源组。资源组类似于 Windows 或 macOS 上的文件夹。它们允许您将特定项目的所有资源放置在同一位置。资源组图标位于 Azure 门户左侧面板的收藏夹菜单中:
需要执行以下操作:
-
选择 创建资源。然后,向导将指导您完成创建资源组的步骤。
-
然后,点击顶部的 + 图标创建 IoT Hub 实例。
-
在搜索框中键入
IoT Hub
。然后,向导将指导您如何设置 IoT Hub。
在规模页面上需要注意的一点是,您需要选择 S1 或更高的定价层。S1 层使您可以与设备进行双向通信,并且还能使用高级功能,例如设备双和将 ML 模型推送到边缘设备的能力。
工作原理…
IoT Hub 是专为 IoT 开发的平台。解决影响 IoT 的问题,例如不可靠的通信,通过诸如高级消息队列协议(AMQP)和消息队列遥测传输(MQTT)等机制进行处理。IoT Hub 拥有丰富的工具生态系统,可帮助 IoT 开发人员,如设备双生,云到设备消息,设备安全中心,Kubernetes 集成以及边缘模块市场。
设置 IoT Edge 设备
在本示例中,我们将设置一个能够与 IoT Hub 通信并接收新的 ML 容器的 IoT Edge 设备,以便在设备上执行 ML 评估。
IoT Edge 设备比传统 IoT 设备有优势。主要优势在于它们能够通过空中更新(OTA)进行更新。通过使用容器,可以轻松部署模型,而无需担心设备砖化的问题。
准备工作
在创建 IoT Edge 设备之前,请确保您的设备受 IoT Edge 支持。某些设备架构(例如 ARM64)不受支持。接下来,请确保您在前一个示例中的 IoT Hub 实例正在运行。必须在设备上安装 IoT Edge 运行时。为了本教程的目的,我们将假设用户有一个 Raspberry Pi Class 设备。
如何做…
要设置 IoT Edge 设备,您需要同时设置云端和设备端。IoT 设备需要在云端有一个位置来发送其信息。本示例有两个部分。第一部分是在 IoT Hub 中配置 IoT Edge 设备。第二部分是配置设备与云端通信。
配置 IoT Edge 设备(云端)
步骤如下:
-
在 IoT Hub 刀片中,选择 IoT Edge。
-
点击“+ 添加 IoT Edge 设备”按钮。这将带您进入添加 IoT Edge 设备向导。
-
给您的设备一个独特的设备 ID,并选择保存。
-
屏幕中央将显示一个新设备。点击该设备并复制其主连接字符串:
接下来的部分解释了如何使设备与云端通信。为此,您将需要设备连接字符串。设备连接字符串可以在设备属性部分找到。点击您想要获取连接字符串的设备,并复制连接字符串。
配置 IoT Edge 设备(设备端):
首先要做的是安装 Moby。Moby 是 Docker 的精简版本。Docker 允许您将 Edge 模块推送到设备上。这些模型可以是从传感器收集数据的模块,也可以是 ML 模块。步骤如下:
- 下载并安装 Moby 引擎到设备上:
curl -L https://aka.ms/moby-engine-armhf-latest -o moby_engine.deb && sudo dpkg -i ./moby_engine.deb
- 下载并安装 Moby CLI:
curl -L https://aka.ms/moby-cli-armhf-latest -o moby_cli.deb && sudo dpkg -i ./moby_cli.deb
- 修复安装问题:
sudo apt-get install -f
- 安装 IoT Edge 安全管理器:
curl -L https://aka.ms/libiothsm-std-linux-armhf-latest -o libiothsm-std.deb && sudo dpkg -i ./libiothsm-std.deb
- 安装安全守护程序:
curl -L https://aka.ms/iotedged-linux-armhf-latest -o iotedge.deb && sudo dpkg -i ./iotedge.deb
- 修复安装问题:
sudo apt-get install -f
- 编辑 Edge 设备的配置文件。如果您的设备上尚未安装
nano
,可能需要先安装它。nano
是基于命令行的文本编辑器,可通过 SSH 进行工作:
sudo nano /etc/iotedge/config.yaml
- 在
nano
文本编辑器中查找设备连接字符串。然后,将您从 IoT Hub 门户复制的设备连接字符串粘贴到"<ADD DEVICE CONNECTION STRING HERE>"
部分:
provisioning:
source: "manual"
device_connection_string: "<ADD DEVICE CONNECTION STRING HERE>"
接下来,您需要保存并退出 nano
。为此,请按下 Ctrl + X。终端将显示保存确认消息。按 Y 确认并保存。然后,是时候重新启动设备上的服务以应用更改了。
- 使用以下命令重新启动 Edge 服务:
sudo systemctl restart iotedge
工作原理如下…
在此教程中,我们创建了一个在云中具有特定密钥的设备。这是安全措施的一部分,每个设备都有自己的唯一密钥。如果设备被 compromise,可以关闭它。
然后我们将 IoT Edge SDK 添加到设备上,并将其连接到云端。在这一点上,设备已完全连接到云端,并准备好接收 ML 模型并将其遥测发送到云端。下一步是将 Edge 模块部署到设备上。这些 Edge 模块是 docker 化的容器,可以访问设备上的传感器,并将遥测发送到云端或运行经过训练的模型。
将 ML 模块部署到 Edge 设备:
Docker 是 IoT 设备部署的主要方法。Docker 允许您在本地创建和测试容器,并将其部署到边缘设备上。Docker 文件可以根据不同的芯片架构(如 x86 和 ARM)进行特别脚本化部署。在此教程中,我们将演示如何从云端创建一个带有 ML 库的 IoT Edge 模块。
准备工作:
要创建 IoT Edge 模块,首先安装 Visual Studio Code。安装并运行 Visual Studio Code 后,安装 Azure IoT Edge 扩展。可以通过在 Visual Studio Code 侧边栏中找到扩展图标()来完成。在扩展搜索栏中搜索
azure iot edge
并安装扩展:
安装完扩展程序后,Visual Studio Code 现在有一个向导,可以创建 IoT Edge 部署。通过少量修改,可以配置它以部署 ML 模型。
如何操作…
此配方的步骤如下:
- 在 Visual Studio Code 中,按下Ctrl + Shift + P以打开命令窗口,并找到 Azure IoT Edge: New IoT Edge Solution:
-
选择代码的位置。
-
输入解决方案名称。
-
选择一种语言。为了本书的目的,我们将使用 Python 作为我们的语言。
-
创建一个模块名称。
-
选择本地端口以在本地运行代码。
工作原理…
完成向导后,您应该在 Visual Studio Code 的资源管理器中看到类似以下内容:
让我们探索为您创建的内容。项目的主入口点是 main.py
。main.py
中有一个示例,可以帮助加快开发时间。要部署 main.py
,您将使用 deployment.template.json
文件。右键单击 deployment.template.json
将弹出一个菜单,其中包含创建部署清单的选项。在 modules
文件夹中,有一个包含三个 Docker 文件的示例模块,用于 ARM32、AMD64 和调试模式下的 AMD64。这些是当前支持的芯片架构。Dockerfile.arm32v7
是支持树莓派 v3 的架构。
为了确保构建 ARM32 容器而不是 AMD64 容器,请进入 module.json
文件并删除任何其他 Docker 文件的引用。例如,以下内容有三个 Docker 引用:
platforms": {
"amd64": "./Dockerfile.amd64",
"amd64.debug": "./Dockerfile.amd64.debug",
"arm32v7": "./Dockerfile.arm32v7"
}
删除两个未使用的 Docker 引用后,新文件现在应如下所示:
platforms": {
"arm32v7": "./Dockerfile.arm32v7"
}
还有更多…
要安装 TensorFlow(这是一个 ML 库)、Keras(这是一个在 TensorFlow 之上的抽象层,使编程更加简单)和 h5py
(这是一个序列化层,允许您序列化和反序列化 TensorFlow 模型),请进入目标 Docker 容器,然后进入 requirements.txt
文件,并插入以下内容来安装库:
tensorflow
keras
h5py
设置 Kafka
Kafka 是一个开源项目,在规模上非常经济实惠,可以以毫秒级延迟执行 ML 模型,并具有多主题发布/订阅模型。有几种设置 Kafka 的方式。它是一个开源项目,因此您可以下载 Kafka 项目并在本地运行 Zookeeper 和 Kafka。Kafka 的母公司 Confluent 提供了一个付费服务,提供许多额外的功能,如仪表板和 KSQL。它们在 Azure、AWS 和 Google Cloud 中作为托管服务提供,您也可以将 Kafka 作为 Docker 容器来进行开发使用。
使用 Kafka 的一个缺点是需要大量额外的开销来使其成为一个良好的物联网项目。例如,Kafka 默认情况下不安全。安全性通过一系列插件处理,包括设备端通过 x.509 证书以及云端通过轻量级目录访问协议(LDAP)、Ranger 或 Kerberos 插件。部署 ML 模型也并非易事。任何 ML 库都需要转换为 Java 编译器可用的形式。TensorFlow 有适用于 Java 的 TensorFlow,但许多 ML 库在 Java 中不可用。
准备就绪
在本示例中,我们将在docker-compose
中使用 Confluent Kafka。您需要在计算机上安装 Git、Docker 和docker-compose
才能运行此食谱。要将 ML 模型添加到 Kafka 流中,您需要使用在 Java 上运行的平台,例如 H2O 或 TensorFlow。
如何实现…
此食谱的步骤如下:
- 克隆存储库:
git clone https://github.com/confluentinc/cp-all-in-on
- 运行
docker-compose
:
docker-compose up -d --build
Confluent Kafka 带有许多容器。等待大约 10 分钟使容器完成启动后,打开浏览器,访问localhost:9091
以查看 Kafka 控制中心。
工作原理…
Kafka 使用日志来记录来自最终用户的数据流入主题。这些主题可以被数据的消费者读取。使 Kafka 在物联网社区中成为流行工具的原因是其先进的特性。多个流可以合并,并且流可以转换为基于键/值的表格,其中最新的流更新表格。但更重要的是,对于本书的目的,ML 算法可以在毫秒级延迟的流数据上运行。本示例展示了如何将数据推送到 Kafka,然后创建一个 Java 项目以实时处理数据。
这还不是全部…
将数据流入 Kafka 相当简单。有生产者发送设备到云的消息和消费者接收云到设备的消息。在以下示例中,我们将实现一个生产者:
- 下载示例项目:
git clone https://github.com/Microshak/KafkaWeatherStreamer.git
cd KafkaWeatherStreamer
- 安装必要的依赖:
pip install -r requirements.txt
- 运行
weather.py
文件:
python3 weather.py
现在您应该能够查看 Kafka 控制中心,并看到数据正在流入。Kafka Streams API 是一个实时平台,可以以毫秒级延迟执行 ML 计算。Streams API 具有 KTables 和 KStreams 的概念。KStreams 是流入 Kafka 的各种主题的数据流。KTables 是流转换成表格,其中数据每次有与其主键关联的新记录时更新。这允许将多个流类似于数据库中的表进行联接,使 Kafka 能够不仅处理单个设备,还能够从多个源获取设备数据并将流组合在一起。
要使用 Streams API,您必须首先在计算机上安装 Java 和 Maven。此外,您还需要安装用于 Java 开发的集成开发环境,例如 IntelliJ。安装完所有先决条件后,运行 Maven 原型以生成启动 Kafka Streams API 项目所需的代码:
mvn archetype:generate \
-DarchetypeGroupId=org.apache.kafka \
-DarchetypeArtifactId=streams-quickstart-java \
-DarchetypeVersion=2.2.0 \
-DgroupId=streams.examples \
-DartifactId=streams.examples \
-Dversion=0.1 \
-Dpackage=myapps
在 IntelliJ 中打开新创建的项目,您就可以开始编写针对 Kafka Streams API 的代码了。Confluent,Kafka 的制造商的座右铭是:“It’s just Java”。他们的意思是,一旦您进入 Streams API,您可以编写 Java 代码来做任何您想做的事情。这可能是向网站发送 WebSocket 信息或运行 ML 模型。如果可以用 Java 做到,那么在 KStreams 事件循环中也可以做到。有一些框架,如deeplearning4j
,可以接受在 Python 中训练的 Keras 模型,并在 Java 中运行它们。
在 Databricks 上安装 ML 库
Databricks 是一个统一的大数据和分析平台。它非常适合训练 ML 模型并处理 IoT 中常见的大规模数据。有一些扩展如 Delta Lake 允许研究人员查看数据在特定时间段的存在方式,以便在模型漂移时进行分析。还有像 MLflow 这样的工具,允许数据科学家比较多个模型之间的差异。在这个示例中,我们将在 Databricks 上安装各种 ML 包,如 TensorFlow、PyTorch 和 GraphFrames。大多数 ML 包可以通过 PyPI 安装。例如,用于安装 TensorFlow 的格式可用于 OpenAI Gym、Sonnet、Keras 和 MXNet 等各种 ML 框架。Databricks 中有一些在 Python 中不可用的工具。对于这些工具,我们使用 GraphX 和 GraphFrame 探索的模式,通过 Java 扩展安装包。
准备工作
在我们开始之前,了解各组件如何互相配合是很重要的。让我们从工作空间开始。工作空间区域是您可以通过 Databricks 笔记本与数据科学家和工程师共享结果的地方。笔记本可以与 Databricks 中的文件系统进行互操作,以存储 Parquet 或 Delta Lake 文件。工作空间部分还存储诸如 Python 库和 JAR 文件等文件。在工作空间部分,您可以创建文件夹来存储共享文件。我通常创建一个packages
文件夹来存储 Python 和 JAR 文件。在安装 Python 包之前,让我们首先通过转到集群部分来检查集群是什么。
在您的 Databricks 实例中,转到“Clusters”菜单。您可以创建一个集群或使用已创建的集群。对于集群,您可以指定所需的计算量。Spark 可以处理大型数据集,也可以用于 ML 优化工作负载的 GPU。一些集群预装了诸如 Conda 等 ML 工具,其他允许您安装自己的库。
如何做…
传统的机器学习笔记本可能存在与安装的机器学习包不同版本的问题。Databricks 通过允许用户设置预安装包资源来规避这些问题。在这个示例中,我们将把各种机器学习包安装到 Databricks 中。这些包可以分配给所有新的集群或特定的集群。这使得数据科学家可以灵活地使用新版本的机器学习包,同时支持他们开发的旧版机器学习模型。我们将在三个部分中介绍这个示例。
导入 TensorFlow
或许最简单的导入 Python 库(如 TensorFlow)的方法是使用 PyPI。只需访问 pypi.org/
并搜索 TensorFlow。这将为您提供所需的信息,并能查看不同的版本。安装步骤如下:
-
前往
pypi.org/
并搜索 TensorFlow。 -
复制您想要的名称和版本号,格式为
tensorflow==1.14.0
。 -
在 Databricks 的 Workspace 选项卡中,右键单击任何位置,然后从下拉菜单中选择创建,然后选择库:
- 在创建库页面上,选择 PyPI 作为库源:
-
复制库的名称和版本号,然后粘贴到“包”部分。
-
点击创建。
如果您已经创建了集群,可以将 TensorFlow 附加到它上面。您也可以在所有集群上安装 TensorFlow。
安装 PyTorch
PyTorch 是一个流行的原生 Python 编写的机器学习库,内置支持 GPU。安装 PyTorch 非常类似于安装 TensorFlow。您可以在创建 | 库菜单中通过 PyPI 安装它。在 PyPI 导入库菜单中,输入当前版本的 PyPI (torch==1.1.0.post2
)。安装步骤如下:
-
前往
pypi.org/
并搜索 PyTorch。 -
复制您想要的名称和版本号,格式为
torch==1.1.0.post2
。 -
在 Databricks 的 Workspace 选项卡中,右键单击任何位置,然后从下拉菜单中选择创建,然后选择库。
-
选择 PyPI 作为库源。
-
复制库的名称和版本号,然后粘贴到“包”部分。
-
点击创建。
如果您已经创建了集群,可以将 PyTorch 附加到它上面。您也可以在所有集群上安装 PyTorch。
安装 GraphX 和 GraphFrames
Spark 有一些分布式库,在数据科学中无法找到。GraphFrames 就是其中之一。在图论中,您可以执行诸如寻找最短路径、网络流、同源性、中心度和影响力等操作。因为 GraphFrames 是建立在 Java 库 GraphX 上的,所以您需要安装 Java 库,然后使用 Python 封装器,您需要 pip
安装访问 Java JAR 文件的 Python 库。安装步骤如下:
-
从
spark-packages.org/package/graphframes/graphframes
下载一个 JAR 文件。你需要找到一个与你集群中运行的 Spark 版本匹配的版本。 -
在 Databricks 的 Workspace 标签页中,右键点击任意位置,从下拉菜单中选择 Create,然后选择 Library。
-
将 JAR 文件拖放到名为 Drop JAR here 的空间中。
-
点击 Create。
-
然后,导入另一个库。
-
在 Databricks 的 Workspace 标签页中,右键点击任意位置,从下拉菜单中选择 Create,然后选择 Library。
-
选择 PyPI 作为库源,并在 Package 部分输入
graphframes
。 -
点击 Create。
为了测试你的安装,你可以在这里下载一个示例笔记本和数据文件:github.com/Microshak/Databricks/tree/master/Graph
。
工作原理如下…
Databricks 专为数据工程师和数据科学家设计,支持多版本软件和多语言。通过允许用户分别为每个集群配置不同版本的 ML 包来实现此目的。TensorFlow 隐式安装在流处理集群上。另一个集群安装了流行的 Conda 环境。最后,测试环境没有安装 TensorFlow。
第二章:处理数据
用于收集数据的技术通常决定了可以使用的模型类型。如果地震仪每小时只报告一次地震活动的当前读数,那就毫无意义。数据的高保真度不足以预测地震。在 IoT 项目中,数据科学家的工作并不是在收集数据之后开始,而是需要参与设备的建造。当设备建成时,数据科学家需要确定设备是否发出适合机器学习的数据。接下来,数据科学家帮助电气工程师确定传感器是否放置在正确的位置,传感器之间是否存在相关性,最后,数据科学家需要以高效的方式存储数据以进行分析。通过这样做,我们避免了 IoT 的第一个主要陷阱,即收集和存储最终对机器学习无用的数据。
本章将检查存储、收集和分析数据,以确保有足够的数据进行有效和高效的机器学习。我们将首先看看数据的存储和访问方式。然后,我们将查看数据收集设计,以确保设备上的数据适合机器学习。
本章将涵盖以下教程:
-
使用 Delta Lake 存储分析数据
-
数据收集设计
-
窗口化
-
探索因子分析
-
在 Mongo/hot path 存储中实现分析查询
-
将 IoT 数据导入 Spark
使用 Delta Lake 存储分析数据
如今,有许多处理分析数据的选择。您可以将其存储在数据湖、Delta Lake 或 NoSQL 数据库中。本教程涵盖数据存储和检索,并使用 Delta Lake。Delta Lake 提供了处理数据的最快方式和最有效的存储数据方式。它还允许您查看过去任意时间点存在的数据。
准备就绪
虽然 Delta Lake 是一个开源项目,但在 Delta Lake 中存储文件的最简单方法是通过 Databricks。Databricks 的设置在 第一章 中讨论过,《设置 IoT 和 AI 环境》。本教程假设您已经设置和运行了 Databricks。
如何执行…
将文件导入 Delta Lake 很容易。数据可以通过文件或流导入。本教程的步骤如下:
-
在 Databricks 中,点击“数据”按钮打开数据面板,然后点击“添加数据”按钮,将文件拖入上传区域。
-
在 Notebook 中点击“创建表”。为您生成的代码将从这里开始:
# File location and type
file_location = "/FileStore/tables/soilmoisture_dataset.csv"
file_type = "csv"
# CSV options
infer_schema = "false"
first_row_is_header = "false"
delimiter = ","
df = spark.read.format(file_type) \
.option("inferSchema", infer_schema) \
.option("header", first_row_is_header) \
.option("sep", delimiter) \
.load(file_location)
display(df)
- 查看数据,并在准备好保存到 Delta Lake 时,取消注释最后一行:
# df.write.format("parquet").saveAsTable(permanent_table_name)
- 然后,将
"parquet"
更改为"delta"
:
df.write.format("delta").saveAsTable(permanent_table_name)
- 从这里查询数据:
%sql
SELECT * FROM soilmoisture
- 或者,您可以优化 Delta Lake 保存文件的方式,使查询速度更快:
%sql
OPTIMIZE soilmoisture ZORDER BY (deviceid)
Delta Lake 数据可以进行更新、过滤和聚合。此外,它可以轻松转换为 Spark 或 Koalas DataFrame。
工作原理…
Delta Lake 建立在 Parquet 之上。利用列压缩和元数据存储,可以使数据检索速度比标准 Parquet 快 10 倍。除了更快的性能外,Delta Lake 的数据版本控制允许数据科学家查看数据在特定时间点的情况,使数据科学家在模型漂移时进行根本原因分析。
数据采集设计
机器学习和物联网中最重要的因素是数据采集设计。如果收集的数据是垃圾**数据,那么上面无法进行机器学习。假设您正在查看泵的振动(如下图所示),以确定泵是否存在机械或滚珠轴承问题,以便在机器受到严重损坏之前进行预防性维护:
重要的是,以 100 Hz 的实时数据存储在云中是成本高昂的。为了降低成本,工程师通常以 1 分钟的频率发送数据。低频传感器数据通常不能准确表示所查看的问题。下图显示了仅每分钟采样一次时数据的外观:
这里,我们看到与以 1 分钟间隔收集的数据叠加的振动计数据。数据有一定的用处,但不准确,因为它未显示数据发生的真实幅度。使用平均值更糟糕。下图显示了振动计在 1 分钟内的平均读数:
对于 1 分钟窗口内的平均读数取平均值是更糟糕的解决方案,因为在泵出现问题时平均值不会改变。下图显示了振动计在 1 分钟内的标准读数:
使用标准偏差技术与均值比较显示是否存在泵问题。这比平均技术更精确的解决方案。
使用 1 分钟窗口内的最小和最大值可以提供情况的最佳表示。下图显示了读数的样子:
因为物联网设备可能在出现问题之前正常工作多年,并且在云中传送高频数据是成本高昂的,因此使用其他测量方法来确定设备是否需要维护。可以使用最小/最大、标准偏差或峰值等技术来触发云到设备的消息,告知设备以更高的频率发送数据。高频诊断数据可以使用 Blob 存储来存储大文件。
IoT 的一个挑战之一是在海量数据中找到有意义的数据。在本章中,我们将展示挖掘有价值数据的技术。
准备工作
为了准备好数据收集设计,您需要一个以高速率流式传输数据的设备。在《第一章》中,设置 IoT 和 AI 环境,我们讨论了将设备数据流入 IoT Hub 的过程。通常在生产中,设备数据以 15 秒或 1 分钟的间隔发送。但是在数据收集设计中,一个设备以 10 Hz 的高速率发送数据,即每秒 10 次。一旦数据流入,您可以将其拉入 Databricks 进行实时分析。
如何做…
在我们的 Databricks 笔记本中,我们将使用方差、Z-尖峰和最小/最大值技术分析 IoT 数据。
方差
方差是数据与均值的变化程度的度量。在接下来的代码中,我们使用 Koalas,即 pandas
的分布式克隆,来执行基本的数据工程任务,如确定方差。以下代码使用滚动窗口上的标准偏差来显示数据尖峰问题:
import databricks.koalas as ks
df = ks.DataFrame(pump_data)
print("variance: " + str(df.var()))
minuite['time'] = pd.to_datetime(minuite['time'])
minuite.set_index('time')
minuite['sample'] = minuite['sample'].rolling(window=600,center=False).std()
在 IoT 产品线上使用占空比,直到收集到足够的数据用于机器学习。它们通常是简单的措施,例如设备是否过热或者振动过多。
我们还可以查看高低值,例如最大值,以确定传感器是否输出适当的读数。以下代码显示了我们数据集的最大读数:
max = DF.agg({"averageRating": "max"}).collect()[0]
Z-尖峰
尖峰可以帮助确定是否存在问题,方法是查看读数变化的快速程度。例如,户外 IoT 设备在南极和死亡谷直射阳光下的工作温度可能不同。找出设备是否存在问题的一种方法是查看温度变化的速度。Z-尖峰是典型的基于时间的异常检测方法。它的使用原因是它仅查看该设备的读数,并且可以提供独立于环境因素的数值。
Z-尖峰查看尖峰与标准偏差的差异。它使用统计 z 检验来确定尖峰是否大于人群中 99.5% 的尖峰。
最小/最大值
最小值和最大值可以显示系统上的最大压力值。以下代码展示了如何获取一个 1 分钟窗口的最小值和最大值:
minute['max'] = minute['sample'].rolling(window=600,center=False).max()
minute['sample'] = minute['sample'].rolling(window=600,center=False).min()
最小值和最大值可以突出异常值。这在确定异常时非常有用。
窗口操作
有三种主要的窗口函数:滚动窗口、跳跃窗口和滑动窗口。Spark 和 Stream Analytics 都可以进行窗口操作。窗口操作允许您查看聚合函数,如平均值、计数和总和。它还允许您查看最小值和最大值。窗口操作是一种特征工程技术,有助于使数据更易管理。在本章中,我们将介绍几种窗口操作的工具和窗口的方式。
准备工作
要准备好,您还需要一个将数据流式传输到 IoT Hub 的设备。该流需要被 Azure 的流分析、Spark 或 Databricks 摄取。
如何实现…
使用 Databricks 笔记本或流分析工作空间执行配方。窗口化可以将大数据集的静态转化为机器学习模型的有意义特征。
滚动
滚动窗口函数将数据流分组为时间段(如下图所示)。滚动窗口意味着窗口不会重复或将数据从一个段落传递到下一个段落:
流分析
在流分析中,使用滚动窗口每 10 秒计算事件发生的一种方法如下:
SELECT EventTime, Count(*) AS Count
FROM DeviceStream TIMESTAMP BY CreatedAt
GROUP by EventTime, TumbelingWindow(minuites, 10)
Spark
在 Spark 中,要每 10 分钟计算事件发生的次数,可以按以下步骤进行:
from pyspark.sql.functions import *
windowedDF = eventsDF.groupBy(window("eventTime", "10 minute")).count()
跳跃
跳跃窗口是重叠的滚动窗口。它们允许您设置特定的命令和条件,例如每 5 分钟,给我过去 10 分钟内传感器读数的计数。要使跳跃窗口与滚动窗口相同,需要将跳跃大小设置为窗口大小,如下图所示:
流分析
下面的流分析示例显示了在 10 分钟窗口内的消息计数。每 5 分钟进行一次计数:
SELECT EventTime, Count(*) AS Count
FROM DeviceStream TIMESTAMP BY CreatedAt
GROUP by EventTime, HopingWindow(minuites, 10, 5)
Spark
在 PySpark 中,可以通过窗口函数来完成这一操作。下面的示例显示了一个窗口化的 Spark DataFrame,在 10 分钟时间段内每 5 分钟生成一个新的 DataFrame 条目:
from pyspark.sql.functions import *
windowedDF = eventsDF.groupBy(window("eventTime", "10 minute", "5 minute")).count()
滑动
滑动窗口在事件发生时产生输出。下图说明了这个概念:
流分析
在流分析示例中,通过使用滑动窗口,只有在 10 分钟窗口内的消息数超过 100 条时才会收到结果。与查看确切时间窗口并显示该窗口的消息不同,在滑动窗口中,我们将在每个输入消息上收到一条消息。另一个用途是显示滚动平均值:
SELECT EventTime, Count(*) AS Count
FROM DeviceStream TIMESTAMP BY CreatedAt
GROUP by EventTime,
SlidingWindow(minutes, 10)
WHERE COUNT(*) > 100
它的工作原理…
使用窗口化,IoT 数据可以显示诸如频率、总和、标准差和百分位分布等因素,时间段内的窗口化可以用于丰富数据的特征工程或将数据转换为聚合数据集。例如,窗口化可以显示工厂生产了多少设备或显示传感器读数中的调制。
探索性因子分析
垃圾数据是困扰物联网的主要问题之一。在收集数据之前,数据通常没有经过验证。常常存在与错误的传感器放置或数据看起来随机的问题,因为它不适合所使用的数据类型的适当度量。例如,由于中心极限定理,振动计可能显示数据集围绕平均值集中,而实际上数据显示了一个数量级的大增加。为了应对这些问题,对设备数据进行探索性因子分析至关重要。
在这个示例中,我们将探讨几种因子分析技术。在 Databricks 笔记本中使用聚合数据和原始遥测数据来执行此分析。
准备工作
您需要在 Databricks 的表中拥有数据,我们在使用 Delta Lake 存储数据进行分析示例中实现了这一点。一旦数据存储在 Spark 数据表中,即可进行因子分析。
如何做…
本示例由两个部分组成。第一部分是对数据进行视觉检查。视觉检查可以揭示软件缺陷,了解设备行为以及确定设备数据模式。第二部分则关注相关性和协方差。这些技术通常用于确定传感器是否冗余。
视觉探索
Spark 允许您查看基本的图表而无需太多代码。使用笔记本段落顶部的魔术符号,您可以轻松地从 Python 切换到 Scala 或 SQL。关于使用 Databricks 内置的图表系统的一个警告是,它只查看前 10,000 条记录。对于大型数据集,可以使用其他图表库。步骤如下:
- 使用
%sql
魔法在 Databricks 中查询数据,如下所示:
%sql
select * from Telemetry
- 选择返回数据网格底部的图表图标。这将显示图表构建器 UI,如下屏幕截图所示:
- 选择最能代表数据的图表类型。有些图表更适合变量比较,而其他图表则可以帮助揭示趋势。
以下部分将审查何时以及为什么使用不同类型的图表。
图表类型
不同类型的图表阐明了数据的不同方面,比如比较、组成、关系和分布。关系图表用于测试假设或查看一个因素如何影响其他因素。组成显示数据集的百分比分布。它通常用于显示各因素之间的比较情况。饼图是一种简单的组成图表。分布图表用于显示人群的分布情况。它们通常用于确定数据是否随机,是否具有较大的扩展或是否已归一化。比较图表用于将一个值与其他值进行比较。
条形和柱形图
条形图和柱形图用于比较项目之间的差异。条形图由于页面布局的关系可以有很多项。柱形图和条形图还可以显示随时间变化的情况。以下图表是条形图和柱形图的一个例子:
散点图
散点图可以显示两个变量之间的关系。它还可以显示趋势线。以下是散点图的一个例子:
气泡图
当你想要展示三个变量之间的关系时,可以使用气泡图。这可以用来显示异常行为。以下是气泡图的一个例子:
折线图
这些图表显示随时间变化的情况,并可用于显示设备数据在一天内的变化。如果设备具有季节性数据,则可能需要将时间作为算法的一部分或使用去季节性算法。以下是折线图的一个例子:
面积图
面积图类似于折线图,但用于显示一个部分的体积与另一个部分的比较。以下是面积图的一个例子:
分位数图
通过将数据分成分段(分位数)来帮助确定人口形状。常见的分位数是 25%,50%和 75%,或 33%和 66%,或 5%和 95%(一般百分比是四分位数)。了解数据是否符合预期参数是理解设备是否存在问题的重要因素。以下是分位数图的一个例子:
冗余传感器
物联网的一个挑战是确定传感器放置的位置以及需要多少个传感器。以泵为例:判断泵轴承是否损坏的一种方法是使用麦克风听高音尖叫声。另一种方法是使用参数来判断是否振动更多。还有一种方法是测量电流是否波动。没有一种确定泵轴承是否损坏的正确方法;然而,实施这三种技术可能成本过高且冗余。评估不同传感器之间的相关性的常见方法是使用热图。在下面的代码中,我们使用热图来找出传输冗余信息的传感器:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# load the sample training data
train = pd.read_csv('/dbfs/FileStore/tables/Bike_train.csv')
for i in range(50):
a = np.random.normal(5,i+1,10)
b.append(a)
c = np.array(b)
cm =np.corrcoef(c)
plt.imshow(cm,interpolation='nearest')
plt.colorbar()
#heat map
plt.figure(figsize=(17,11))
sns.heatmap(train.iloc[:,1:30].corr(), cmap= 'viridis', annot=True)
display(plt.show())
下面的屏幕截图显示了热图:
在上面的例子中,我们可以看到count
和registered
具有非常高的相关性,因为这两个数字接近 1。同样,我们可以看到temp
和atemp
之间有很高的相关性。如果在不去除相关数据的情况下使用这些数据,可能会给在数据集上训练的机器学习模型带来加权影响。
当设备数据很少时,仍然进行方差分析、分布和偏差分析可能很有价值。因为它的进入门槛低于机器学习,所以可以在机器生命周期的早期阶段部署。进行统计分析有助于确保设备设置的数据正确,不重复或虚假,并可用于机器学习。
交叉制表提供频率分布表。这可以用于确定两个不同传感器是否计算相同的情况。以下是显示交叉制表表格的代码:
display(DF.stat.crosstab("titleType", "genres"))
样本协方差和相关性
协方差测量两个传感器相对于彼此的联合变化性。正数表明传感器报告相同的数据。负数表明传感器之间存在反向关系。可以使用 Spark DataFrame 中的 DataFrame stat.cov
函数计算两个传感器的协方差:
df.stat.cov('averageRating', 'numVotes')
工作原理…
在生产后修改物理设备可能很昂贵。本文展示了如何检查原型设备,以确保其产生的数据不会毫无意义。使用数据分析工具如 Databricks 进行初步数据分析,可以避免物联网和人工智能中常见的问题,如传感器放置不当、通信过度或不足以及无法用于机器学习的数据。执行标准的机器学习任务,如预测维护、异常检测或剩余有用寿命,取决于良好的数据。
更多内容…
您可以通过创建一个过滤小部件来进一步探索数据。例如,您可以使用如下所示的CREATE WIDGET DROPDOWN
查询:
%sql
CREATE WIDGET DROPDOWN tytleType DEFAULT "movie" CHOICES SELECT DISTINCT titleType FROM imdbTitles
创建一个小部件允许您创建一个数据查询,可以轻松分段,如下代码所示:
%sql
select * from imdbTitles where titleType = getArgument("tytleType")
其他小部件类型,如文本、组合框和多选框,也是可用的。
在 Mongo/hot 路径存储中实现分析查询
在物联网架构中,存在热路径数据和冷路径数据。热路径数据可以立即访问。这通常存储在 NoSQL 或时间序列数据库中。例如,可以使用时间序列数据库如 InfluxDB 来计算每小时每台设备的重置次数。这可以用于辅助特征工程。热数据的另一个用途是精确分析。如果一个设备在现场故障,可以查询诸如 MongoDB 之类的数据库,仅获取该设备在过去一个月内生成的数据。
冷路径数据通常用于批处理,如机器学习和月度报告。冷路径数据主要存储在 Blob、S3 存储或 HDFS 兼容数据存储中。将热路径与冷路径分开通常涉及成本和可扩展性因素。IoT 数据通常属于大数据范畴。如果数据科学家从 NoSQL 数据库中查询多年的数据,使用它的 Web 应用程序可能会崩溃。对于存储在磁盘上的冷路径数据,这种情况并不适用。另一方面,如果数据科学家需要从数十亿条记录中查询几百条记录,那么 NoSQL 数据库就是合适的选择。
本文重点是处理热数据。本文主要关注从 MongoDB 提取 IoT 数据。首先,我们从一个设备中提取数据,然后我们将其在多个设备间进行聚合。
准备工作
流分析可以将 IoT 数据传输到 MongoDB。要做到这一点,请启动 MongoDB。可以通过 Azure Kubernetes Service 或使用 Atlas MongoDB 云提供商来完成此操作。一旦有了数据库,您可以使用函数应用程序在 IoT Hub 和 MongoDB 之间移动数据。
如何做…
Mongo 具有一系列与 SQL 可比较的过滤选项。以下代码显示了如何连接到 Mongo 的本地版本,并查询所有库存状态为A
的产品:
df = spark.read.format("mongo").option("uri",
"mongodb://127.0.0.1/products.inventory").load()
pipeline = "{'deviceid':'8ea23889-3677-4ebe-80b6-3fee6e38a42c'}"
df = spark.read.format("mongo").option("pipeline", pipeline).load()
df.show()
下一个示例展示了如何执行复杂的过滤操作,接着进行分组操作。最终输出将显示状态为A
的项目计数:
pipeline = "[ { '$match': { 'status': 'A' } }, { '$group': { '_id': '$item', 'total': { '$sum': '$qty' } } } ]"
df = spark.read.format("mongo").option("pipeline", pipeline).load()
df.show()
工作原理…
Mongo 将索引数据存储在多台计算机或分区上。这使得以毫秒为单位的延迟时间内检索特定数据成为可能。NoSQL 数据库可以为数据提供快速查找。在本文中,我们讨论了如何从 MongoDB 查询数据到 Databricks。
将 IoT 数据导入 Spark
要将 Spark 连接到 IoT Hub,首先创建一个消费者组。消费者组是指示消费者已达到的日志当前位置的指针。可以在同一数据日志的多个消费者之间存在多个消费者。消费者组是并行和可分布的,使您能够编写即使在大规模的情况下也能保持稳定的程序。
准备工作
对于本文,进入 Azure IoT Hub 门户,单击内置终端菜单选项。然后,通过输入一些文本来添加一个消费者组。在仍在该屏幕上的情况下,复制事件中心兼容端点连接字符串。
如何做…
本文的步骤如下:
- 在 Databricks 中,启动一个新的笔记本,并输入连接到 IoT Hub 所需的信息。然后,输入以下代码:
import datetime as dt
import json
ehConf = {}
ehConf['eventhubs.connectionString'] = ["The connection string you copies"]
ehConf['eventhubs.consumerGroup'] = "[The consumer group you created]"
startingEventPosition = {
"offset": -1,
"seqNo": -1, #not in use
"enqueuedTime": None, #not in use
"isInclusive": True
}
endingEventPosition = {
"offset": None, #not in use
"seqNo": -1, #not in use
"enqueuedTime": endTime,
"isInclusive": True
}
ehConf["eventhubs.recieverTimeout"] = 100
- 将数据放入 Spark DataFrame 中:
df = spark \
.readStream \
.format("eventhubs") \
.options(**ehConf) \
.load()
- 下一步是对数据应用结构,以便您可以使用结构化流处理:
from pyspark.sql.types import *
Schema = StructType([StructField("deviceEndSessionTime", StringType()), StructField("sensor1", StringType()),
StructField("sensor2", StringType()),
StructField("deviceId", LongType()),
])
- 最后一步是将架构应用到 DataFrame 中。这样可以使您像处理表格一样处理数据:
from pyspark.sql.functions import *
rawData = df. \
selectExpr("cast(Body as string) as json"). \
select(from_json("json", Schema).alias("data")). \
select("data.*")
工作原理…
在这个示例中,我们连接到了 IoT Hub 并将数据放入了一个 DataFrame 中。稍后,我们为该数据框架添加了结构,使我们能够像查询数据库表一样查询数据。
在接下来的几章中,我们将讨论如何创建模型。在使用冷路径数据创建模型之后,您可以通过将这些训练好的模型推送到 Databricks 结构化流中,实现几乎实时的机器学习。
第三章:IoT 的机器学习
机器学习已经极大地改变了制造商在物联网(IoT)中的应用。今天,有许多行业有特定的物联网需求。例如,医疗物联网(IoMT)拥有像家中可穿戴的门诊心脏监测器这样的设备。这些设备通常需要在网络上传输大量数据或在边缘进行大量的计算以处理与心脏相关的事件。另一个例子是农业物联网(AIoT)设备,通常放置在没有 Wi-Fi 或蜂窝网络的地方。处方或模型被推送到这些半连接设备上。这些设备中的许多需要在边缘做出决策。当使用 LoRAWAN 或电视白色空间等技术最终建立连接时,模型被下载到设备上。
在本章中,我们将讨论使用逻辑回归和决策树等机器学习模型来解决常见的物联网问题,如分类医疗结果、检测不安全驾驶员和分类化学读数。我们还将探讨处理受限设备的技术,并研究使用无监督学习获取对原型等数据较少设备的见解的技术。
本章包含以下内容:
-
使用异常检测分析化学传感器
-
使用 IoMT 的逻辑回归
-
使用决策树对化学传感器进行分类
-
使用 XGBoost 进行简单的预测性维护
-
检测不安全的驾驶员
-
受限设备上的人脸检测
使用异常检测分析化学传感器
准确的预测模型需要大量的设备在现场失败,以便它们有足够的故障数据用于预测。对于一些精心制作的工业设备来说,这种规模的故障可能需要多年时间。异常检测可以识别不像其他设备的设备。它还可以用于筛选成千上万条类似消息,并确定不同于其他消息的消息。
机器学习中的异常检测可以是无监督的,监督的,或半监督的。通常,它从使用无监督机器学习算法将数据聚类为行为模式或群组开始。这将数据呈现为桶。当机器被检查时,一些桶识别出行为,而一些桶则识别出设备存在问题。设备可能在静止状态、使用状态、冷态或需要调查的状态中表现出不同的行为模式。
此配方假定使用的数据集中对数据了解不多。异常检测过程被用作发现过程的一部分,并且通常与原型一起使用。
准备工作
异常检测是最容易实现的机器学习模型之一。在这个示例中,我们将使用从化学传感器获取的数据集,检测中性、香蕉或葡萄酒。为了准备好,您需要导入 numpy
、sklearn
和 matplotlib
库。
如何做…
完成这个示例需要遵循以下步骤:
- 导入所需的库:
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
- 将数据文件上传到 DataFrame:
df = spark.read.format("csv" \
.option("inferSchema", True) \
.option("header", True) \
.option("sep", "\t") \
.load("/FileStore/tables/HT_Sensor_metadata.dat")
- 查看数据集以查看数据分组是否与簇的数量相关:
pdf = df.toPandas()
y_pred = KMeans(n_clusters=3,
random_state=2).fit_predict(pdf[['dt','t0']])
plt.scatter(pdf['t0'],pdf['dt'], c=y_pred)
display(plt.show())
输出如下:
上述图表显示了三组不同的数据。紧密聚集的簇代表具有明确定义边界的数据。如果我们将簇的数量调整为 10
,可能会更好地分离不同的群组。这些簇段帮助我们识别数据的不同段落。这反过来可能帮助我们确定原型的最佳传感器放置或在机器学习模型中进行特征工程。
工作原理…
在这个示例中,我们使用 numpy
进行数据操作,sklearn
进行机器学习算法,matplotlib
查看结果。接下来,我们将制表符分隔的文件转换为 Spark 数据框架。在这一步中,我们将数据转换为 pandas DataFrame。然后我们使用三个簇运行 k-means 算法,输出图表。
K-means 是一种将数据分组成簇的算法。K-means 是一种流行的聚类算法,用于在没有标签的情况下检查数据。K-means 首先随机初始化簇的质心。在我们的例子中,它有三个簇的质心。然后将这些质心分配给最近的数据点。接下来,它将每个质心移动到其相应簇的中间位置。它重复这些步骤直到达到适当的数据点分割。
还有更多…
在图表中,您可能已经注意到异常值。在查看原型时,这些异常值非常重要。异常值可以代表机器内部的电力波动、传感器安置不当或其他问题。以下示例显示了我们数据的简单标准差计算。从这里,我们能够看到两个超出均值三个标准差的值:
from numpy import mean
from numpy import std
data_mean, data_std = mean(pdf['dt']), std(pdf['dt'])
cut_off = data_std * 3
lower, upper = data_mean - cut_off, data_mean + cut_off
outliers = [x for x in pdf['dt'] if x < lower or x > upper]
print('Identified outliers: %d' % len(outliers))
print(outliers)
使用 IoMT 的逻辑回归
在这个示例中,我们将讨论使用逻辑回归来对乳腺 X 光数据进行分类。最近,IoMT 大大扩展。许多设备被患者佩戴,当他们离开医生时提供家庭医疗监测解决方案,同时其他设备则在医院内,为医生提供运行的医疗测试的额外反馈。在许多情况下,机器学习算法能够发现医生可能忽略的疾病和问题,或为他们提供额外的建议。在这个示例中,我们将使用乳腺癌数据集,并确定乳腺 X 光记录是恶性还是良性。
准备就绪
数据集与 Databricks 笔记本一起,可以在 GitHub 仓库中找到。 数据集笨重。 它有高度相关的坏列,这是说一些传感器是重复的,还有未使用的列和多余的数据。 为了便于阅读,在 GitHub 仓库中将有两个笔记本。 第一个笔记本执行所有数据操作,并将数据放入数据表中。 第二个笔记本进行机器学习。 我们将重点介绍数据操作笔记本。 在配方结束时,我们将讨论另外两个笔记本,以展示 MLflow 的示例。
在本配方中,您还需要一个 MLflow 工作区。 要设置 MLflow 工作区,您需要进入 Databricks 并为此实验创建工作区。 我们将在那里写入我们实验的结果。
如何实现…
按照以下步骤完成本配方:
- 导入所需的库:
import pandas as pd
from sklearn import neighbors, metrics
from sklearn.metrics import roc_auc_score, classification_report,\
precision_recall_fscore_support,confusion_matrix,precision_score, \
roc_curve,precision_recall_fscore_support as score
from sklearn.model_selection import train_test_split
import statsmodels.api as sm
import statsmodels.formula.api as smf
- 导入数据:
df = spark.sql("select * from BreastCancer")
pdf = df.toPandas()
- 分割数据:
X = pdf
y = pdf['diagnosis']
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.3, random_state=40)
- 创建公式:
cols = pdf.columns.drop('diagnosis')
formula = 'diagnosis ~ ' + ' + '.join(cols)
- 训练模型:
model = smf.glm(formula=formula, data=X_train,
family=sm.families.Binomial())
logistic_fit = model.fit()
- 测试我们的模型:
predictions = logistic_fit.predict(X_test)
predictions_nominal = [ "M" if x < 0.5 else "B" for x in \
predictions]
- 评估模型:
print(classification_report(y_test, predictions_nominal, digits=3))
输出显示了恶性 (M
) 和良性 (B
) 的 精确度
、召回率
和 f1-score
:
- 评估混淆矩阵:
cfm = confusion_matrix(y_test, predictions_nominal)
precision,recall,fscore,support=score(y_test, predictions_nominal,
average='macro')
print('Confusion Matrix: \n', cfm, '\n')
输出如下:
结果显示,在我们的测试集中共有 171 条记录,其中 112 条是真负样本,49 条是真正样本,这意味着在 171 条记录中,它能够正确识别出 161 条记录。 其中 10 条预测是错误的: 5 条假负样本和 5 条假正样本。
它是如何工作的…
在本配方中,我们使用了逻辑回归。 逻辑回归是一种可用于传统统计学以及机器学习的技术。 由于其简单性和强大性,许多数据科学家将逻辑回归作为他们的第一个模型,并将其用作超越的基准。 逻辑回归是一个二元分类器,意味着它可以将某些东西分类为 true
或 false
。 在我们的情况下,分类是良性或恶性。
首先,我们导入 koalas
进行数据操作,以及 sklearn
用于我们的模型和分析。 接下来,我们从我们的数据表中导入数据并将其放入 Pandas DataFrame 中。 然后,我们将数据分割为测试和训练数据集。 接下来,我们创建一个描述模型所使用的数据列的公式。 接下来,我们向模型提供公式、训练数据集以及它将使用的算法。 然后,我们输出一个可以用来评估新数据的模型。 现在我们创建一个名为 predictions_nominal
的 DataFrame,我们可以用它来与我们的测试结果数据集进行比较。 分类报告给出了 精确度
、召回率
和 f1-score
:
-
精确度: 正确报告的正预测与预期的正预测之比
-
召回率: 正确报告的正预测与总体人口的比率
-
F 分数: 精度和召回率的混合分数
接下来,我们可以查看模型的结果,并确定其如何准确预测真实值。我们将检查的一些因素如下:
-
**真阴性:**测试集中实际为负的预测负例
-
假阳性:训练模型在训练集中预测为正但实际不是
-
假阴性:测试集中实际上是正例但被预测为负例的假负例
-
真阳性:模型实际上正确获取的数量
还有更多…
我们将记录 MLflow 中的结果以与其他算法进行比较。我们还将保存其他参数,如使用的主要公式和预测族:
import pickle
import mlflow
with mlflow.start_run():
mlflow.set_experiment("/Shared/experiments/BreastCancer")
mlflow.log_param("formula", formula)
mlflow.log_param("family", "binomial")
mlflow.log_metric("precision", precision)
mlflow.log_metric("recall", recall)
mlflow.log_metric("fscore", fscore)
filename = 'finalized_model.sav'
pickle.dump(model, open(filename, 'wb'))
mlflow.log_artifact(filename)
使用决策树分类化学传感器
在本篇文章中,我们将使用金属氧化物(MOx)传感器的化学传感器数据来确定空气中是否有葡萄酒。这种类型的传感器通常用于确定空气中是否存在食品或化学颗粒。化学传感器可以检测对人类有害或仓库内食品泄漏的气体。
如何实现…
按照以下步骤完成这个配方:
- 导入库:
import pandas as pd
import numpy as np
from sklearn import neighbors, metrics
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import LabelEncoder
- 导入数据:
df = spark.sql("select * from ChemicalSensor")
pdf = df.toPandas()
- 编码数值:
label_encoder = LabelEncoder()
integer_encoded = \
label_encoder.fit_transform(pdf['classification'])
onehot_encoder = OneHotEncoder(sparse=False)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
onehot_encoded = onehot_encoder.fit_transform(integer_encoded)
- 测试/训练分割数据:
X = pdf[feature_cols]
y = onehot_encoded
X_train, X_test, y_train, y_test = \
train_test_split(X, y, test_size=0.2, random_state=5)
- 训练和预测:
clf = DecisionTreeClassifier()
clf = clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
- 评估准确性:
print("Accuracy:",metrics.accuracy_score(y_test, y_pred))
print("AUC:",roc_auc_score(y_test, y_pred))
工作原理…
和往常一样,我们导入这个项目所需的库。接下来,我们将 Spark 数据表中的数据导入到 Pandas DataFrame 中。独热编码可以将分类值(例如我们的葡萄酒和无葡萄酒示例)更改为机器学习算法更好使用的编码值。在步骤 4中,我们将特征列和我们的独热编码列分割成一个测试和训练集。在步骤 5中,我们创建一个决策树分类器,使用X_train
和y_train
数据来训练模型,然后使用X_test
数据创建一个y_prediction
数据集。换句话说,最后,我们将根据数据集在X_test
集上的预测得到一组名为y_pred
的预测。在步骤 6中,我们评估模型的准确性和曲线下面积(AUC)。
当数据复杂时,决策树分类器被用来使用一系列逻辑规则进行分类。您可以像下图所示使用决策树跟随一组是/否问题:
机器学习算法可以训练决策树模型使用数值数据,如下图所示:
机器学习算法训练模型以准确选择给定可用数据的最佳路径。
还有更多…
sklearn
决策树分类器有两个超参数可以调整:准则和最大深度。通常会更改超参数以提高准确性。准则可以是基尼系数或信息熵。这两个准则评估子节点中的不纯度。接下来是最大深度。决策树的最大深度会影响欠拟合和过拟合。
欠拟合与过拟合
欠拟合的模型不准确,无法有效地表示它们所训练的数据。
过拟合的模型无法从训练数据中进行泛化。它会错过与训练集相似的数据,因为它只适用于与其训练时完全相同的数据。
使用 XGBoost 进行简单预测维护
每个设备都有寿命终止或需要定期维护。预测维护是物联网中最常用的机器学习算法之一。下一章将深入探讨预测维护,重点关注序列数据及其如何随季节性变化的情况。本配方将从分类的简单视角来看待预测维护。
在此配方中,我们将使用 NASA 涡轮风扇引擎退化模拟 数据集。我们将看到三种分类情况。绿色表示引擎不需要维护;黄色表示引擎在接下来的 14 个维护周期内需要维护;红色表示引擎在下一个周期内需要维护。作为算法,我们将使用极限梯度提升(XGBoost)。近年来,XGBoost 因其在 Kaggle 竞赛中的表现优异而变得流行。
准备工作
要做好准备,您需要 NASA 涡轮风扇引擎退化模拟 数据集。此数据以及一个 Spark 笔记本可以在本书的伴随 GitHub 仓库或 NASA 网站上找到。接下来,您需要确保在 Databricks 中安装 XGBoost 作为库。
如何实现…
此配方的步骤如下:
- 导入库:
import pandas as pd
import numpy as np
from pyspark.sql.types import *
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score
import pickle
import mlflow
- 导入数据:
file_location = "/FileStore/tables/train_FD001.txt"
file_type = "csv"
schema = StructType([
StructField("engine_id", IntegerType()),
StructField("cycle", IntegerType()),
StructField("setting1", DoubleType()),
StructField("setting2", DoubleType()),
StructField("setting3", DoubleType()),
StructField("s1", DoubleType()),
StructField("s2", DoubleType()),
StructField("s3", DoubleType()),
StructField("s4", DoubleType()),
StructField("s5", DoubleType()),
StructField("s6", DoubleType()),
StructField("s7", DoubleType()),
StructField("s8", DoubleType()),
StructField("s9", DoubleType()),
StructField("s10", DoubleType()),
StructField("s11", DoubleType()),
StructField("s12", DoubleType()),
StructField("s13", DoubleType()),
StructField("s14", DoubleType()),
StructField("s15", DoubleType()),
StructField("s16", DoubleType()),
StructField("s17", IntegerType()),
StructField("s18", IntegerType()),
StructField("s19", DoubleType()),
StructField("s20", DoubleType()),
StructField("s21", DoubleType())
])
df = spark.read.option("delimiter"," ").csv(file_location,
schema=schema,
header=False)
- 在数据上创建表视图:
df.createOrReplaceTempView("raw_engine")
- 转换数据:
%sql
drop table if exists engine;
create table engine as
(select e.*, CASE WHEN mc - e.cycle = 1 THEN 1 ELSE
CASE WHEN mc - e.cycle < 14 THEN 2 ELSE
0 END END as label
from raw_engine e
join (select max(cycle) mc, engine_id from raw_engine group by engine_id) m
on e.engine_id = m.engine_id)
- 测试、训练和拆分数据:
new_input = spark.sql("select * from engine").toPandas()
training_df, test_df = train_test_split(new_input)
- 准备模型:
dtrain = xgb.DMatrix(training_df[['setting1','setting2','setting3', 's1', 's2', 's3',
's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14',
's15', 's16','s17', 's18', 's19', 's20', 's21']], label=training_df["label"])
param = {'max_depth': 2, 'eta': 1, 'silent': 1, 'objective': 'multi:softmax'}
param['nthread'] = 4
param['eval_metric'] = 'auc'
param['num_class'] = 3
- 训练模型:
num_round = 10
bst = xgb.train(param, dtrain, num_round)
- 评估模型:
dtest = xgb.DMatrix(test_df[['setting1', 'setting2', 'setting3',
's1', 's2', 's3', 's4', 's5', 's6',
's7', 's8', 's9', 's10', 's11',
's12', 's13', 's14', 's15', 's16',
's17', 's18', 's19', 's20', 's21']])
ypred = bst.predict(dtest)
pre_score = precision_score(test_df["label"], ypred,
average='micro')
print("xgb_pre_score:",pre_score)
- 存储结果:
with mlflow.start_run():
mlflow.set_experiment("/Shared/experiments/\
Predictive_Maintenance")
mlflow.log_param("type", 'XGBoost')
mlflow.log_metric("precision_score", pre_score)
filename = 'bst.sav'
pickle.dump(bst, open(filename, 'wb'))
mlflow.log_artifact(filename)
它的工作原理…
首先,我们导入pandas
、pyspark
和numpy
进行数据处理,xgboost
作为我们的算法,sklearn
用于评分结果,最后使用mlflow
和pickle
保存这些结果。在步骤 2 中,我们在 Spark 中指定了一个模式。Databricks 的推断模式功能通常会出现模式错误。通常我们需要指定数据类型。在接下来的步骤中,我们创建了数据的临时视图,以便在 Databricks 中使用 SQL 工具。在步骤 4 中,我们在页面顶部使用魔术标签 %sql
将语言切换为 SQL。然后,我们创建了一个名为engine
的表,该表包含引擎数据及一个新列,如果引擎剩余循环大于 14,则给出0
,如果只剩一个循环,则给出1
,如果剩余 14 个循环,则给出2
。然后我们切换回默认的 Python 语言,并将数据拆分为测试和训练数据集。在步骤 6 中,我们指定了模型中的列以及超参数。从这里开始训练模型。然后我们测试我们的模型并打印精确度分数。接下来,我们将结果存储在 MLflow 中。在第四章,预测性维护的深度学习,我们将对此数据集进行其他实验,以查看哪种表现最佳。
XGBoost 有大量可以调整的参数。这些调整参数可以是允许算法使用的线程数,以及帮助提高准确性或防止过拟合和欠拟合的树参数。其中一些包括:
-
learning_rate
: 学习率是算法更新节点的步长。它有助于防止过拟合,但也可能会对训练完成所需的时间产生负面影响。 -
max_depth
: 深度树倾向于过拟合,浅树倾向于欠拟合。 -
predictor
: 这是一个标志,告诉程序在 CPU 或 GPU 上进行计算。GPU 可以显著提高性能,但并非所有计算机都配备 GPU。
还有十几个可以在 XGBoost 中调整的参数。
XGBoost 决策树在内部采用弱学习器或浅树,并使用维度评分系统将它们组合成强学习器。这类似于从医生那里得到不良诊断,然后寻求第二和第三意见。第一个医生可能是错的,但不太可能三个医生都错。
检测不安全的驾驶员
在机器学习中的计算机视觉使我们能够判断道路上是否有事故或者不安全的工作环境,并且可以与复杂系统(如智能销售助手)结合使用。计算机视觉在物联网中开辟了许多可能性。从成本的角度来看,计算机视觉也是最具挑战性的之一。在接下来的两个示例中,我们将讨论两种不同的使用计算机视觉的方式。第一种方法是接收大量从物联网设备生成的图像,并使用高性能分布式 Databricks 格式对其进行预测和分析。在下一个示例中,我们将使用一种在边缘设备上执行机器学习的技术,使用低计算量的算法。
准备工作
准备工作,您将需要 Databricks。在这个示例中,我们将从 Azure Blob Storage 中提取图像。
如何做…
此示例的步骤如下:
- 导入库和配置:
from pyspark.ml.classification import LogisticRegression
from pyspark.ml import Pipeline
from sparkdl import DeepImageFeaturizer
from pyspark.ml.evaluation import \
MulticlassClassificationEvaluator
from pyspark.sql.functions import lit
import pickle
import mlflow
storage_account_name = "Your Storage Account Name"
storage_account_access_key = "Your Key"
- 读取数据:
safe_images = "wasbs://unsafedrivers@"+storage_account_name+\
".blob.core.windows.net/safe/"
safe_df = spark.read.format('image').load(safe_images)\
.withColumn("label", lit(0))
unsafe_images = "wasbs://unsafedrivers@"+storage_account_name+\
".blob.core.windows.net/unsafe/"
unsafe_df = spark.read.format('image').load(unsafe_images)\
.withColumn("label", lit(1))
- 查询数据:
display(unsafe_df)
- 创建测试和训练数据集:
unsafe_train, unsafe_test = unsafe_df.randomSplit([0.6, 0.4])
safe_train, safe_test = safe_df.randomSplit([0.6, 0.4])
train_df = unsafe_train.unionAll(safe_train)
test_df = safe_test.unionAll(unsafe_test)
- 构建管道:
featurizer = DeepImageFeaturizer(inputCol="image",
outputCol="features",
modelName="ResNet50")
lr = LogisticRegression(maxIter=20, regParam=0.05,
elasticNetParam=0.3, labelCol="label")
p = Pipeline(stages=[featurizer, lr])
- 训练模型:
p_model = p.fit(train_df)
- 评估模型:
predictions = p_model.transform(test_df)
predictions.select("filePath", "prediction").show(truncate=False)
df = p_model.transform(test_df)
predictionAndLabels = df.select("prediction", "label")
evaluator = \
MulticlassClassificationEvaluator(metricName="accuracy")
print("Training set accuracy = " + \
str(evaluator.evaluate(predictionAndLabels)))
- 记录结果:
with mlflow.start_run():
mlflow.set_experiment("/Shared/experiments/Workplace Safety")
mlflow.log_param("Model Name", "ResNet50")
# Log a metric; metrics can be updated throughout the run
precision, recall, fscore, support=score(y_test, y_pred,
average='macro')
mlflow.log_metric("Accuracy", \
evaluator.evaluate(predictionAndLabels))
filename = 'finalized_model.sav'
pickle.dump(p_model, open(filename, 'wb'))
# Log an artifact (output file)
mlflow.log_artifact(filename)
它是如何工作的…
首先,我们定义文件的位置。对于这个示例,我们使用 Azure Blob Storage,但是任何存储系统,比如 S3 或 HDFS,也同样适用。用你的 Blob Storage 账户的密钥替换storage_account_name
和storage_account_access_key
字段。从我们的存储账户中读取安全和不安全的图像到一个 Spark 图像 DataFrame 中。在我们的示例中,我们将安全图像放在一个文件夹中,不安全图像放在另一个文件夹中。查询图像 DataFrame 以查看是否成功获取了图像。创建安全和不安全的测试和训练集。然后,将我们的数据集合并成一个训练集和一个测试集。接下来,创建一个机器学习管道。我们使用 ResNet-50 算法作为特征提取器。然后,我们使用逻辑回归作为分类器。将它放入管道中并训练我们的模型。接下来,将我们的管道运行我们的训练 DataFrame,得出一个经过训练的模型。然后,评估我们模型的准确性。最后,将结果存储在 MLflow 中,以便与其他模型进行比较。
有许多图像分类模型已经开发出来,比如 ResNet-50 和 Inception v3。在我们的例子中,我们使用了 ResNet-50,这是一种调整过的卷积神经网络。ResNet-50 是一种强大的用于图像特征提取的机器学习模型。在机器学习中,有无免费午餐定理,它指出没有一个模型会在所有情况下表现最好。因此,数据科学家会测试不同的算法。可以通过改变参数如指标名称来简单地完成这一过程。
我们还使用了 Spark ML 流水线。流水线允许数据科学家声明处理过程的不同步骤,并独立实现它们。在我们的例子中,我们使用 ResNet-50 对图像进行特征化。ResNet-50 输出一个特征向量,可以通过分类器进行分类。在我们的情况下,我们使用逻辑回归,但也可以使用 XGBoost 或其他神经网络。
还有更多…
要将我们的流水线更改为使用 Inception 而不是ResNet50
,我们只需更改模型:
featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features",
modelName="ResNet50")
使用Inception v3
,我们能够在图像集上测试不同模型的准确性:
featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features",
modelName="InceptionV3")
我们可以使用一系列模型,并在 MLflow 中记录结果:
for m in ['InceptionV3', 'Xception','ResNet50', 'VGG19']:
featurizer = DeepImageFeaturizer(inputCol="image",
outputCol="features",
modelName=m)
受限设备上的面部检测
深度神经网络往往优于其他分类技术。然而,在物联网设备上,RAM、计算能力或存储量并不是很大。在受限设备上,RAM 和存储通常以 MB 为单位,而不是 GB,使得传统的分类器不可行。一些云端视频分类服务每个设备的实时视频收费超过 10,000 美元。OpenCV 的 Haar 分类器具有与卷积神经网络相同的基本原理,但计算和存储量只是其一小部分。OpenCV 支持多种语言,并可以在一些受限制的设备上运行。
在本示例中,我们将设置一个 Haar 级联,以便检测人物是否靠近摄像头。这通常用于 Kiosk 和其他交互式智能设备。Haar 级联可以以高速运行,当发现接近设备的人脸时,可以通过云服务或其他设备上的机器学习模型发送该图像。
准备工作
我们需要做的第一件事是安装 OpenCV 框架:
pip install opencv-python
接下来,我们下载模型。模型可以从 OpenCV GitHub 页面或书籍的 GitHub 页面下载。文件是haarcascade_frontalface_default.xml
。
接下来,我们通过导入haarcascade_frontalface_default.xml
文件并创建一个 Python 文件来创建一个新文件夹,以用于代码。最后,如果设备没有连接摄像头,请连接一个。在接下来的示例中,我们将使用 OpenCV 实现 Haar 级联。
如何操作…
此示例的步骤如下:
- 导入库和设置:
import cv2
from time import sleep
debugging = True
classifier = \
cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
video = cv2.VideoCapture(0)
- 初始化摄像头:
while True:
if not video.isOpened():
print('Waiting for Camera.')
sleep(5)
pass
- 捕获和转换图像:
ret, frame = video.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
- 对图像进行分类:
faces = classifier.detectMultiScale(gray,
minNeighbors=5,
minSize=(100, 100)
)
- 调试图像:
if debugging:
# Draw a rectangle around the faces
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
- 检测面部:
if len(faces) > 0:
# Your advanced code here
pass
工作原理…
首先,导入库并设置设置。接下来,我们导入 opencv
和 python
库,同时也导入 time
以便在摄像头未就绪时等待。然后,我们设置一些调试标志,以便在调试时可以视觉化测试输出。接着,我们将 Haar Cascade XML 文件导入分类器。最后,我们打开连接到机器的第一个视频摄像头。在 第 2 步 中,我们等待摄像头就绪。在开发软件时,这通常不是问题,因为系统已经识别了摄像头。然后,我们将此程序设置为自动运行;当系统重新启动时,摄像头可能最多需要一分钟才能可用。我们还启动了一个无限循环来处理摄像头图像。在接下来的步骤中,我们捕获并将图像转换为黑白。接着,我们运行分类器。detectMultiScale
分类器可以检测不同尺寸的人脸。minNeighbors
参数指定在检测到一个人脸之前需要多少个相邻的协作检测。将 minNeighbors
参数设置得太小可能会导致误报。设置得太大可能根本无法检测到人脸。最后,还有人脸需要的最小像素大小。为了调试代码并确保摄像头工作准确,我们添加了一些调试代码,将视频和边界框输出到连接的监视器上。在部署设备上,这会增加相当大的负载。但是在测试中,这可以显示问题并允许进行调优。如果检测到人脸,则可以执行任务,如本地情感分析或将其发送到外部服务(如 Azure Face API)以通过人脸 ID 识别人员。
Haar Cascade 是高效的人脸检测分类器。在幕后,它会取图像的矩形部分,并将其与图像的另一部分进行比较,从而得出具有人脸特征的东西。在我们的示例中,我们使用设备上的摄像头,对其进行转换,然后使用 Haar Cascade 进行分类。
第四章:预测性维护的深度学习
预测性维护是物联网中最受追捧的机器学习解决方案之一。但它也是最难以捉摸的机器学习解决方案之一。机器学习的其他领域可以轻松解决,例如使用 OpenCV 或 Keras 等工具实现计算机视觉,可以在几小时内完成。要成功实施预测性维护,首先需要合适的传感器。第二章中的数据采集设计部分可以帮助确定适当的传感器位置。第二章中的探索性因子分析部分可以帮助确定数据存储的频率。实施预测性维护的最大难题之一是需要有足够数量的设备故障。对于坚固的工业设备来说,这可能需要很长时间。将维修记录与设备遥测数据进行关联也是关键步骤。
尽管挑战艰巨,回报是巨大的。一个正确实施的预测性维护解决方案可以通过确保关键设备在需要时准备就绪来拯救生命。它们还可以增加客户忠诚度,因为它们有助于公司比市场上类似产品少停机。最后,它们可以通过在服务设备之前提供服务技术人员所需的信息来降低成本并提高效率。这可以帮助他们诊断设备,并确保他们在服务设备时携带正确的零件。
在本章中,我们将继续使用 NASA Turbofan 数据集进行预测性维护,并涵盖以下配方:
-
利用特征工程增强数据
-
使用 Keras 进行跌倒检测
-
实施 LSTM 进行设备故障预测
-
部署模型到 Web 服务
利用特征工程增强数据
在改进模型中,最好的时间利用是特征工程。物联网生态系统有许多工具可以简化这一过程。设备可以通过数字孪生、图帧和 GraphX 进行地理连接或层次连接。这些工具可以增加一些特征,如显示与其他故障设备的联系程度。窗口化可以显示当前读数在一段时间内的差异。流式处理工具如 Kafka 可以合并不同的数据流,允许你将来自其他来源的数据进行整合。户外的机器可能会受到高温或潮湿的负面影响,而在气候控制建筑物内的机器则不会。
在这个配方中,我们将通过分析时间序列数据(如变化量、季节性和窗口化)来增强我们的数据。对于数据科学家来说,时间最宝贵的用途之一是进行特征工程。能够将数据切分为有意义的特征可以极大地提高我们模型的准确性。
准备工作
在上一章节的使用 XGBoost 进行预测性维护配方中,我们使用 XGBoost 来预测机器是否需要维护。我们导入了 NASA 的涡轮风扇引擎退化模拟数据集,可以在data.nasa.gov/dataset/Turbofan-engine-degradation-simulation-data-set/vrks-gjie
找到。在本章的其余部分,我们将继续使用该数据集。为了做好准备,您将需要该数据集。
然后,如果您还没有将numpy
、pandas
、matplotlib
和seaborn
导入到 Databricks 中,请立即这样做。
如何做到这一点…
需要遵循此配方的以下步骤:
- 首先,导入所需的库。我们将使用
pyspark.sql
、numpy
和pandas
进行数据操作,使用matplotlib
和seaborn
进行可视化:
from pyspark.sql import functions as F
from pyspark.sql.window import Window
import pandas as pd
import numpy as np
np.random.seed(1385)
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
- 接下来,我们将导入数据并为其应用模式,以便正确使用数据类型。为此,我们通过向导导入数据文件,然后将我们的模式应用于它:
file_location = "/FileStore/tables/train_FD001.txt"
file_type = "csv"
from pyspark.sql.types import *
schema = StructType([
StructField("engine_id", IntegerType()),
StructField("cycle", IntegerType()),
StructField("setting1", DoubleType()),
StructField("setting2", DoubleType()),
StructField("setting3", DoubleType()),
StructField("s1", DoubleType()),
StructField("s2", DoubleType()),
StructField("s3", DoubleType()),
StructField("s4", DoubleType()),
StructField("s5", DoubleType()),
StructField("s6", DoubleType()),
StructField("s7", DoubleType()),
StructField("s8", DoubleType()),
StructField("s9", DoubleType()),
StructField("s10", DoubleType()),
StructField("s11", DoubleType()),
StructField("s12", DoubleType()),
StructField("s13", DoubleType()),
StructField("s14", DoubleType()),
StructField("s15", DoubleType()),
StructField("s16", DoubleType()),
StructField("s17", IntegerType()),
StructField("s18", IntegerType()),
StructField("s19", DoubleType()),
StructField("s20", DoubleType()),
StructField("s21", DoubleType())
])
- 最后,我们将其放入一个 Spark DataFrame 中:
df = spark.read.option("delimiter"," ").csv(file_location,
schema=schema,
header=False)
- 然后我们创建一个临时视图,以便我们可以在其上运行一个 Spark SQL 作业:
df.createOrReplaceTempView("raw_engine")
- 接下来,我们计算剩余使用寿命 (RUL)。使用 SQL 魔法,我们从刚刚创建的
raw_engine
临时视图中创建一个名为engine
的表。然后,我们使用 SQL 来计算 RUL:
%sql
drop table if exists engine;
create table engine as
(select e.*
,mc - e.cycle as rul
, CASE WHEN mc - e.cycle < 14 THEN 1 ELSE 0 END as needs_maintenance
from raw_engine e
join (select max(cycle) mc, engine_id from raw_engine group by engine_id) m
on e.engine_id = m.engine_id)
- 然后,我们将数据导入到一个 Spark DataFrame 中:
df = spark.sql("select * from engine")
- 现在我们计算变化率 (ROC)。在 ROC 计算中,我们查看当前记录与上一记录之间的变化百分比。ROC 计算获取当前周期与前一个周期之间的变化百分比:
my_window = Window.partitionBy('engine_id').orderBy("cycle")
df = df.withColumn("roc_s9",
((F.lag(df.s9).over(my_window)/df.s9) -1)*100)
df = df.withColumn("roc_s20",
((F.lag(df.s20).over(my_window)/df.s20) -1)*100)
df = df.withColumn("roc_s2",
((F.lag(df.s2).over(my_window)/df.s2) -1)*100)
df = df.withColumn("roc_s14",
((F.lag(df.s14).over(my_window)/df.s14) -1)*100)
- 接下来,我们审查静态列。为了做到这一点,我们将 Spark DataFrame 转换为 Pandas,以便查看数据的汇总统计信息,如均值、四分位数和标准差:
pdf = df.toPandas()
pdf.describe().transpose()
这将得到以下输出:
- 现在,我们删除在这个练习中对我们没有价值的列。例如,我们将删除
settings3
和s1
列,因为这些值从不改变:
columns_to_drop = ['s1', 's5', 's10', 's16', 's18', 's19',
'op_setting3', 'setting3']
df = df.drop(*columns_to_drop)
- 接下来,我们将审查值之间的相关性。我们寻找完全相同的列。首先,在 DataFrame 上执行相关性函数。然后,使用
np.zeros_like
掩盖上三角。接下来,设置图表大小。然后,使用diverging_palette
定义自定义颜色映射,接着使用heatmap
函数绘制热力图:
corr = pdf.corr().round(1)
mask = np.zeros_like(corr, dtype=np.bool)
mask[np.triu_indices_from(mask)] = True
f, ax = plt.subplots(figsize=(20, 20))
cmap = sns.diverging_palette(220, 10, as_cmap=True)
sns.heatmap(corr, mask=mask, cmap=cmap, vmin=-1, vmax=1, center=0,
square=True, linewidths=.5, cbar_kws={"shrink": .5},
annot=True)
display(plt.tight_layout())
下面的热力图显示了具有高度相关性的值。数值为1
表示它们完全相关,因此可以从分析中删除:
- 删除相似的列。我们发现
S14
与S9
完全相同,因此我们将删除该列:
columns_to_drop = ['s14']
df = df.drop(*columns_to_drop)
- 现在我们拿到 DataFrame 并将其可视化。使用直方图或分布表显示数据的潜在问题,如异常值、偏斜数据、随机数据和不影响模型的数据:
pdf = df.toPandas()
plt.figure(figsize = (16, 8))
plt.title('Example temperature sensor', fontsize=16)
plt.xlabel('# Cycles', fontsize=16)
plt.ylabel('Degrees', fontsize=16)
plt.xticks(fontsize=16)
plt.yticks(fontsize=16)
pdf.hist(bins=50, figsize=(18,16))
display(plt.show())
以下直方图截图展示了结果:
- 然后我们检查模型的噪声,以确保它不会过度受到波动的影响:
values = pdf[pdf.engine_id==1].values
groups = [5, 6, 7, 8, 9, 10, 11,12,13]
i = 1
plt.figure(figsize=(10,20))
for group in groups:
plt.subplot(len(groups), 1, i)
plt.plot(values[:, group])
plt.title(pdf.columns[group], y=0.5, loc='right')
i += 1
display(plt.show())
以下是输出结果:
- 根据前一步骤,很明显数据是嘈杂的。这可能导致错误的读数。滚动平均可以帮助平滑数据。使用 7 个周期的滚动平均,我们去噪了数据,如下所示:
w = (Window.partitionBy('engine_id').orderBy("cycle")\
.rangeBetween(-7,0))
df = df.withColumn('rolling_average_s2', F.avg("s2").over(w))
df = df.withColumn('rolling_average_s3', F.avg("s3").over(w))
df = df.withColumn('rolling_average_s4', F.avg("s4").over(w))
df = df.withColumn('rolling_average_s7', F.avg("s7").over(w))
df = df.withColumn('rolling_average_s8', F.avg("s8").over(w))
pdf = df.toPandas()
values = pdf[pdf.engine_id==1].values
groups = [5, 25, 6, 26, 8, 27]
i = 1
plt.figure(figsize=(10,20))
for group in groups:
plt.subplot(len(groups), 1, i)
plt.plot(values[:, group])
plt.title(pdf.columns[group], y=0.5, loc='right')
i += 1
display(plt.show())
以下截图展示了 rolling_average_s4
相对于 s4
的图表:
- 由于我们希望其他笔记本能访问这些数据,我们将其保存为一个 ML 准备好的表格:
df.write.mode("overwrite").saveAsTable("engine_ml_ready")
工作原理…
在这个配方中,我们进行了特征工程,使我们的数据更适用于我们的机器学习算法。我们移除了没有变化、高相关性的列,并对数据集进行了去噪。在 步骤 8 中,我们移除了没有变化的列。该方法用多种方式描述数据。审查图表显示许多变量根本不发生变化。接下来,我们使用热力图找到具有相同数据的传感器。最后,我们使用滚动平均值将原始数据集的数据平滑成新数据集。
还有更多…
到目前为止,我们只看过训练数据。但是我们还需要查看测试数据。有一个测试数据集和一个 RUL 数据集。这些数据集将帮助我们测试我们的模型。要导入它们,您需要运行额外的 2 个导入步骤:
- 导入测试数据:依赖于训练集的架构,导入测试集并放入名为
engine_test
的表格中:
# File location and type
file_location = "/FileStore/tables/test_FD001.txt"
df = spark.read.option("delimiter"," ").csv(file_location,
schema=schema,
header=False)
df.write.mode("overwrite").saveAsTable("engine_test")
- 导入 RUL 数据集:接下来是导入剩余寿命数据集并将其保存到一个表格中:
file_location = "/FileStore/tables/RUL_FD001.txt"
RULschema = StructType([StructField("RUL", IntegerType())])
df = spark.read.option("delimiter"," ").csv(file_location,
schema=RULschema,
header=False)
df.write.mode("overwrite").saveAsTable("engine_RUL")
使用 keras 进行摔倒检测
一种预测性维护的策略是查看给定记录的设备故障模式。在这个配方中,我们将分类表现出在设备故障之前发生的模式的数据。
我们将使用 keras
,这是一个非常强大的机器学习库。Keras 简化了 TensorFlow 和 PyTorch 的一些复杂性。对于机器学习初学者来说,Keras 是一个很好的框架,因为它易于入门,并且在 Keras 中学到的概念可以转移到更具表现力的机器学习库,如 TensorFlow 和 PyTorch。
准备工作
本配方扩展了我们在之前配方中进行的预测性维护数据集的特征工程。如果你还没有这样做,你需要将 keras
、tensorflow
、sklearn
、pandas
和 numpy
库导入到你的 Databricks 集群中。
如何操作…
请注意以下步骤:
- 首先,导入所需的库。我们导入
pandas
,pyspark.sql
和numpy
用于数据操作,keras
用于机器学习,以及sklearn
用于评估模型。在评估模型后,我们使用io
,pickle
和mlflow
保存模型和结果,以便与其他模型进行比较:
from pyspark.sql.functions import *
from pyspark.sql.window import Window
import pandas as pd
import numpy as np
import io
import keras
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense, Activation, LeakyReLU, Dropout
import pickle
import mlflow
- 接下来,我们导入训练和测试数据。我们的训练数据将用于训练模型,而测试数据将用于评估模型:
X_train = spark.sql("select rolling_average_s2, rolling_average_s3,
rolling_average_s4, rolling_average_s7,
rolling_average_s8 from \
engine_ml_ready").toPandas()
y_train = spark.sql("select needs_maintenance from \
engine_ml_ready").toPandas()
X_test = spark.sql("select rolling_average_s2, rolling_average_s3,
rolling_average_s4, rolling_average_s7,
rolling_average_s8 from \
engine_test_ml_ready").toPandas()
y_test = spark.sql("select needs_maintenance from \
engine_test_ml_ready").toPandas()
- 现在我们对数据进行缩放。数据集的每个传感器具有不同的比例。例如,
S1
的最大值是518
,而S16
的最大值是0.03
。因此,我们将所有值转换为0
到1
的范围。使每个度量影响模型的方式类似。我们将使用sklearn
库中的MinMaxScaler
函数调整比例:
scaler = MinMaxScaler(feature_range=(0, 1))
X_train.iloc[:,1:6] = scaler.fit_transform(X_train.iloc[:,1:6])
X_test.iloc[:,1:6] = scaler.fit_transform(X_test.iloc[:,1:6])
dim = X_train.shape[1]
- 第一层,即输入层,有 32 个节点。激活函数
LeakyReLU
在给定输入时定义输出节点。为了防止过拟合,在训练时将 25%的隐藏层和可见层丢弃:
model = Sequential()
model.add(Dense(32, input_dim = dim))
model.add(LeakyReLU())
model.add(Dropout(0.25))
- 类似于输入层,隐藏层使用 32 个节点作为输入层,
LeakyReLU
作为输出层。它还使用 25%的 dropout 来防止过拟合:
model.add(Dense(32))
model.add(LeakyReLU())
model.add(Dropout(0.25))
- 最后,我们添加一个输出层。我们给它一层,以便可以得到在
0
到1
之间的输出。sigmoid
作为我们的激活函数,有助于预测输出的概率。我们的优化器rmsprop
和损失函数帮助优化数据模式并减少误差率:
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.compile(optimizer ='rmsprop', loss ='binary_crossentropy',
metrics = ['accuracy'])
- 现在我们训练模型。我们使用
model.fit
函数指定我们的训练和测试数据。批量大小用于设置算法迭代中使用的训练记录数量。5
个 epoch 意味着它将通过数据集 5 次:
model.fit(X_train, y_train, batch_size = 32, epochs = 5,
verbose = 1, validation_data = (X_test, y_test))
- 下一步是评估结果。我们使用训练好的模型和我们的
X_test
数据集来获取预测值y_pred
。然后,我们将预测结果与实际结果进行比较,并查看其准确性:
y_pred = model.predict(X_test)
pre_score = precision_score(y_test,y_pred, average='micro')
print("Neural Network:",pre_score)
- 接下来,我们将结果保存到
mlflow
中。结果将与本书中使用的其他预测维护 ML 算法进行比较:
with mlflow.start_run():
mlflow.set_experiment("/Shared/experiments/Predictive_Maintenance")
mlflow.log_param("model", 'Neural Network')
mlflow.log_param("Inputactivation", 'Leaky ReLU')
mlflow.log_param("Hiddenactivation", 'Leaky ReLU')
mlflow.log_param("optimizer", 'rmsprop')
mlflow.log_param("loss", 'binary_crossentropy')
mlflow.log_metric("precision_score", pre_score)
filename = 'NeuralNet.pickel'
pickle.dump(model, open(filename, 'wb'))
mlflow.log_artifact(filename)
工作原理…
通常神经网络有三个任务:
-
导入数据
-
通过训练识别数据的模式
-
预测新数据的结果
神经网络接收数据,训练自己识别数据的模式,然后用于预测新数据的结果。这个流程使用上一个流程中保存的清理和特征工程后的数据集。X_train
数据集从spark
数据表中取出并转换为 Panda DataFrame。训练数据集X_train
和y_train
用于训练模型。X_test
提供了设备列表,这些设备已经发生了故障,而y_test
提供了这些机器的实时故障。这些数据集用于训练模型和测试结果。
首先,我们有输入层。数据被馈送到我们的 32 个输入神经元中的每一个。神经元通过通道连接。通道被分配一个称为权重的数值。输入被相应的权重乘以,并且它们的总和被发送为输入到隐藏层中的神经元。每个这些神经元与一个称为偏置的数值相关联,该偏置被加到输入总和中。然后,这个值被传递到称为激活函数的阈值函数。激活函数决定神经元是否会被激活。在我们的前两层中,我们使用了 Leaky ReLU 作为我们的激活函数。ReLU或修正线性单元是一种流行的激活函数,因为它解决了梯度消失问题。在这个示例中,我们使用了 Leaky ReLU。Leaky ReLU 解决了 ReLU 存在的问题,即大梯度可能导致神经元永远不会被激活。激活的神经元通过通道将其数据传递到下一层。这种方法允许数据通过网络传播。这称为前向传播。在输出层中,具有最高层的神经元被激活并确定输出。
当我们首次将数据传入网络时,数据通常具有较高的误差程度。我们的误差和优化器函数使用反向传播来更新权重。前向传播和反向传播的循环重复进行,以达到更低的误差率。下图显示了输入、隐藏和输出层如何连接在一起:
还有更多…
在这个示例中,我们将LeakyReLU
作为激活函数,rmsprop
作为优化器,binary_crossentropy
作为损失函数。然后我们将结果保存到mlflow
。我们可以通过尝试不同的组合来调整这个实验的参数,比如神经元的数量或层数。我们也可以改变激活函数,使用 ReLU 或者 TanH。我们也可以将Adam
作为优化器。将这些结果保存到mlflow
可以帮助我们改进模型。
实施 LSTM 以预测设备故障
递归神经网络预测数据序列。在前一个示例中,我们查看了某一时刻并确定是否需要维护。正如我们在第一个示例中看到的那样,涡轮风扇失效数据集具有高度的变异性。在任何时间点读取的数据可能表明需要维护,而下一个时间点可能表明不需要维护。在确定是否派出技术员时,有一个振荡信号可能会导致问题。长短期记忆网络(LSTM)经常与时间序列数据一起使用,例如涡轮风扇失效数据集。
使用 LSTM,我们查看一系列数据,类似于窗口。LSTM 使用有序序列来帮助确定,例如,基于先前数据序列,涡轮风扇引擎是否即将故障。
准备工作
对于这个配方,我们将使用 NASA 的涡轮风扇运行到故障数据集。在这个配方中,我们将使用 Databricks 笔记本。此配方需要安装几个库。对于数据处理,我们需要安装numpy
和pandas
,用于创建 LSTM 模型的keras
,以及用于评估和保存模型结果的sklearn
和mlflow
。
即使在先前的配方中我们添加了窗口处理和预处理数据,但在这个配方中我们将使用原始数据。LSTM 窗口化数据,并且还有大量与这种类型 ML 算法独特的提取和转换。
如何做…
我们将为这个配方执行以下步骤:
- 首先,我们将导入后续需要使用的所有库。我们将导入
pandas
和numpy
进行数据处理,keras
用于 ML 模型,sklearn
用于评估,以及pickel
和mlflow
用于存储结果:
import pandas as pd
import numpy as np
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM, Activation
from sklearn import preprocessing
from sklearn.metrics import confusion_matrix, recall_score, precision_score
import pickle
import mlflow
- 接下来,我们将设置变量。我们将设置 2 个周期期间。此外,我们使用一个序列长度变量。序列长度允许 LSTM 回溯 5 个周期。这类似于第一章中讨论的窗口处理,设置 IoT 和 AI 环境。我们还将获取数据列的列表:
week1 = 7
week2 = 14
sequence_length = 100
sensor_cols = ['s' + str(i) for i in range(1,22)]
sequence_cols = ['setting1', 'setting2', 'setting3', 'cycle_norm']
sequence_cols.extend(sensor_cols)
- 接下来,我们从第三章的IoT 机器学习中的简单预测性维护与 XGBoost配方中导入
spark
数据表的数据。我们还删除label
列,因为我们将重新计算标签。我们将导入三个数据框。train
数据框用于训练模型。test
数据框用于测试模型的准确性,而truth
数据框则是test
数据框的实际故障:
train = spark.sql("select * from engine").toPandas()
train.drop(columns="label" , inplace=True)
test = spark.sql("select * from engine_test2").toPandas()
truth = spark.sql("select * from engine_rul").toPandas()
- 然后,我们生成标签,显示设备是否需要维护。
label1
显示设备在 14 个周期内将会故障,而label2
显示设备将在 7 个周期内故障。首先,我们创建一个 DataFrame,显示每台发动机的最大周期数基于 RUL。接下来,我们使用 RUL DataFrame 在我们的 train DataFrame 中创建一个 RUL 列。通过从当前周期中减去最大寿命来完成此操作。然后,我们放弃我们的max
列。接下来,我们创建一个新列label1
。如果 RUL 小于 14 个周期,则label1
的值为1
。然后将其复制到label2
中,并且如果 RUL 小于 1 周,则添加值2
:
rul = pd.DataFrame(train.groupby('engine_id')['cycle']\
.max()).reset_index()
rul.columns = ['engine_id', 'max']
train = train.merge(rul, on=['engine_id'], how='left')
train['RUL'] = train['max'] - train['cycle']
train.drop('max', axis=1, inplace=True)
train['label1'] = np.where(train['RUL'] <= week2, 1, 0 )
train['label2'] = train['label1']
train.loc[train['RUL'] <= week1, 'label2'] = 2
- 除了为训练数据生成标签外,我们还需要为测试数据做同样的事情。训练数据和测试数据有所不同。训练数据有一个表示机器故障时间的结束日期。训练集没有。相反,我们有一个
truth
数据框,显示了机器实际故障的时间。在我们计算标签之前,需要将test
和truth
数据集结合起来添加标签列:
rul = pd.DataFrame(test.groupby('engine_id')['cycle'].max())\
.reset_index()
rul.columns = ['engine_id', 'max']
truth.columns = ['more']
truth['engine_id'] = truth.index + 1
truth['max'] = rul['max'] + truth['more']
truth.drop('more', axis=1, inplace=True)
test = test.merge(truth, on=['engine_id'], how='left')
test['RUL'] = test['max'] - test['cycle']
test.drop('max', axis=1, inplace=True)
test['label1'] = np.where(test['RUL'] <= week2, 1, 0 )
test['label2'] = test['label1']
test.loc[test['RUL'] <= week1, 'label2'] = 2
- 由于列具有不同的最小值和最大值,我们将对数据进行归一化,以防止一个变量掩盖其他变量。为此,我们将使用
sklearn
库的MinMaxScaler
函数。该函数将值转换为0
到1
之间的范围。在我们的情况下,由于没有很多异常值,这是一个很好的缩放器。我们将对训练集和测试集执行相同的归一化步骤:
train['cycle_norm'] = train['cycle']
cols_normalize = train.columns.difference(['engine_id','cycle','RUL',
'label1','label2'])
min_max_scaler = preprocessing.MinMaxScaler()
norm_train = \
pd.DataFrame(min_max_scaler.fit_transform(train[cols_normalize]),
columns=cols_normalize,
index=train.index)
join = \
train[train.columns.difference(cols_normalize)].join(norm_train)
train = join.reindex(columns = train.columns)
test['cycle_norm'] = test['cycle']
norm_test = \
pd.DataFrame(min_max_scaler.transform(test[cols_normalize]),
columns=cols_normalize,
index=test.index)
test_join = \
test[test.columns.difference(cols_normalize)].join(norm_test)
test = test_join.reindex(columns = test.columns)
test = test.reset_index(drop=True)
- Keras 中的 LSTM 算法要求数据以序列形式呈现。在我们的变量部分中,我们选择将
sequence_length
设为100
。这是在实验过程中可以调整的超参数之一。由于这是对一段时间内数据的顺序观察,序列长度是我们训练模型所需数据序列的长度。关于序列的最佳长度没有明确的规则。但通过实验,小序列的准确性较低已经显而易见。为了生成我们的序列,我们使用函数以符合 LSTM 算法的预期方式返回顺序数据:
def gen_sequence(id_df, seq_length, seq_cols):
data_array = id_df[seq_cols].values
num_elements = data_array.shape[0]
for start, stop in zip(range(0, num_elements-seq_length),
range(seq_length, num_elements)):
yield data_array[start:stop, :]
seq_gen = (list(gen_sequence(train[train['engine_id']==engine_id],
sequence_length, sequence_cols))
for engine_id in train['engine_id'].unique())
seq_array = np.concatenate(list(seq_gen)).astype(np.float32)
- 下一步是构建神经网络。我们构建 LSTM 的第一层。我们从一个序列模型开始。然后给它输入形状和序列的长度。单元告诉我们输出形状的维度,它将传递给下一层。接下来,它返回
true
或false
。然后我们添加Dropout
以增加训练中的随机性,防止过拟合:
nb_features = seq_array.shape[2]
nb_out = label_array.shape[1]
model = Sequential()
model.add(LSTM(input_shape=(sequence_length, nb_features),
units=100, return_sequences=True))
model.add(Dropout(0.25))
- 接着,我们构建网络的隐藏层。与第一层类似,隐藏层是一个 LSTM 层。然而,与将整个序列状态传递给输出不同,它只传递最后节点的值:
model.add(LSTM(units=50, return_sequences=False))
model.add(Dropout(0.25))
- 接着,我们构建网络的输出层。输出层指定输出维度和
activation
函数。通过这样,我们已经建立了神经网络的形状:
model.add(Dense(units=nb_out, activation='sigmoid'))
- 接下来,我们运行
compile
方法来配置模型进行训练。在其中,我们设置我们正在评估的度量标准。在这种情况下,我们正在使用accuracy
作为我们的度量标准。然后,我们定义我们的误差或损失度量。在这个例子中,我们使用binary_crossentropy
作为我们的度量标准。最后,我们指定将减少每次迭代中的错误的优化器:
model.compile(loss='binary_crossentropy', optimizer='adam',
metrics=['accuracy'])
print(model.summary())
- 然后,我们使用
fit
函数来训练模型。我们的epochs
参数意味着数据将通过 10 次。由于随机丢弃,额外的运行将提高准确性。我们使用batch_size
为200
。这意味着模型在更新梯度之前将通过 200 个样本进行训练。接下来,我们使用validation_split
将 95% 的数据用于训练模型,将 5% 用于验证模型。最后,我们使用EarlyStopping
回调函数在模型停止提高准确性时停止训练:
model.fit(seq_array, label_array, epochs=10, batch_size=200,
validation_split=0.05, verbose=1,
callbacks = \
[keras.callbacks.EarlyStopping(monitor='val_loss',
min_delta=0, patience=0,
verbose=0, mode='auto')])
- 接下来,我们根据我们在训练数据上进行的 95%/5% 分割来评估我们的模型。结果显示我们的模型在评估我们保留的 5% 数据时达到了 87% 的准确性:
scores = model.evaluate(seq_array, label_array, verbose=1,
batch_size=200)
print('Accuracy: {}'.format(scores[1]))
- 接下来,我们看一下混淆矩阵,它显示了引擎是否需要维护的正确或错误评估的矩阵:
y_pred = model.predict_classes(seq_array,verbose=1, batch_size=200)
y_true = label_array
print('Confusion matrix\n- x-axis is true labels.\n- y-axis is predicted labels')
cm = confusion_matrix(y_true, y_pred)
cm
我们的混淆矩阵看起来像下面的网格:
实际不需要维护 | 预测需要维护 | |
---|---|---|
实际不需要维护 | 13911 | 220 |
实际需要维护 | 201 | 1299 |
- 然后我们计算精度和召回率。由于数据集不平衡,即不需要维护的值远远超过需要维护的值,精度和召回率是评估此算法的最合适的指标:
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
print( 'precision = ', precision, '\n', 'recall = ', recall)
- 接下来,我们需要转换数据,使得测试数据与训练数据的顺序数据类型相同。为此,我们执行类似于我们对训练数据所做的数据转换步骤:
seq_array_test_last = [test[test['engine_id']==engine_id]\
[sequence_cols].values[-sequence_length:] for engine_id in \
test['engine_id'].unique() if \
len(test[test['engine_id']==engine_id]) >= sequence_length]
seq_array_test_last = \
np.asarray(seq_array_test_last).astype(np.float32)
y_mask = [len(test[test['engine_id']==engine_id]) >= \
sequence_length for engine_id in \
test['engine_id'].unique()]
label_array_test_last = \
test.groupby('engine_id')['label1'].nth(-1)[y_mask].values
label_array_test_last = label_array_test_last.reshape(
label_array_test_last.shape[0],1).astype(np.float32)
- 接下来,我们评估使用训练数据集生成的模型对测试数据集的准确性,以查看模型预测引擎何时需要维护的准确程度:
scores_test = model.evaluate(seq_array_test_last,
label_array_test_last, verbose=2)
print('Accuracy: {}'.format(scores_test[1]))
y_pred_test = model.predict_classes(seq_array_test_last)
y_true_test = label_array_test_last
print('Confusion matrix\n- x-axis is true labels.\n- y-axis is predicted labels')
cm = confusion_matrix(y_true_test, y_pred_test)
print(cm)
pre_score = precision_score(y_true_test, y_pred_test)
recall_test = recall_score(y_true_test, y_pred_test)
f1_test = 2 * (pre_score * recall_test) / (pre_score + recall_test)
print('Precision: ', pre_score, '\n', 'Recall: ', recall_test,
'\n', 'F1-score:', f1_test )
- 现在我们已经有了我们的结果,我们将这些结果与我们的模型一起存储在我们的 MLflow 数据库中:
with mlflow.start_run():
mlflow.set_experiment("/Shared/experiments/Predictive_Maintenance")
mlflow.log_param("type", 'LSTM')
mlflow.log_metric("precision_score", pre_score)
filename = 'model.sav'
pickle.dump(model, open(filename, 'wb'))
mlflow.log_artifact(filename)
工作原理…
LSTM 是一种特殊类型的循环神经网络(RNN)。RNN 是一种神经网络架构,通过保持序列在内存中来处理序列数据。相比之下,典型的前馈神经网络不保持序列信息,不允许灵活的输入和输出。递归神经网络使用递归从一个输出调用到其输入,从而生成一个序列。它传递网络在任何给定时间的状态的副本。在我们的案例中,我们使用了两层 RNN。这一额外的层有助于提高准确性。
LSTM 通过使用门控函数解决了传统 RNN 存在的数据消失问题。数据消失问题是指神经网络过早停止训练但不准确的情况。通过使用丢失数据,我们可以帮助解决这个问题。LSTM 通过使用门控函数来实现这一点。
将模型部署到网络服务
模型的部署可以因设备能力而异。一些带有额外计算能力的设备可以直接运行机器学习模型。而其他设备则需要帮助。在本章中,我们将把模型部署到一个简单的网络服务中。使用现代化的云网络应用或 Kubernetes,这些网络服务可以扩展以满足设备群的需求。在下一章中,我们将展示如何在设备上运行模型。
准备工作
到目前为止,在这本书中,我们已经研究了三种不同的机器学习算法,用于解决 NASA Turbofan 故障预测数据集的预测性维护问题。我们记录了结果到 MLflow。我们可以看到,我们的 XGBoost 笔记本在性能上超过了更复杂的神经网络。以下截图显示了 MLflow 结果集,显示了参数及其相关分数。
从这里我们可以下载我们的模型并将其放入我们的 web 服务中。为此,我们将使用 Python Flask web 服务和 Docker 使服务可移植。在开始之前,使用 pip install
安装 python 的 Flask
包。还要在本地计算机上安装 Docker。Docker 是一个工具,允许您构建复杂的部署。
如何做…
在这个项目中,您需要创建三个文件来测试预测器 web 服务和一个文件来扩展到生产环境。首先创建 app.py
作为我们的 web 服务器,requirements.txt
作为依赖项,以及从 mlflow
下载的 XGBoost 模型。这些文件将允许您测试 web 服务。接下来,要投入生产,您需要将应用程序 docker 化。将文件 docker 化后,可以将其部署到诸如基于云的 web 应用程序或 Kubernetes 服务等服务中。这些服务易于扩展,使新的 IoT 设备接入无缝进行。
- 文件
app.py
是 Flask 应用程序。为了 web 服务,导入Flask
,用于内存中读取模型的os
和pickle
,数据操作的pandas
,以及运行我们的模型的xgboost
:
from flask import Flask, request, jsonify
import os
import pickle
import pandas as pd
import xgboost as xgb
- 接下来是初始化我们的变量。通过将 Flask 应用程序和 XGBoost 模型加载到内存中的函数外部,我们确保它仅在每次 web 服务调用时加载一次,而不是每次。通过这样做,我们极大地提高了 web 服务的速度和效率。我们使用
pickle
来重新加载我们的模型。pickle
可以接受几乎任何 Python 对象并将其写入磁盘。它还可以像我们的情况一样,从磁盘中读取并将其放回内存中:
application = Flask(__name__)
model_filename = os.path.join(os.getcwd(), 'bst.sav')
loaded_model = pickle.load(open(model_filename, "rb"))
- 然后我们创建
@application.route
来提供一个http
端点。POST
方法部分指定它将仅接受 post web 请求。我们还指定 URL 将路由到/predict
。例如,当我们在本地运行时,可以使用http://localhost:8000/precict
URL 来发布我们的 JSON 字符串。然后我们将其转换为pandas
DataFrame,然后 XGBoost 数据矩阵变为调用predict
。然后我们确定它是否大于.5
或小于并返回结果:
@application.route('/predict', methods=['POST'])
def predict():
x_test = pd.DataFrame(request.json)
y_pred = loaded_model.predict(xgb.DMatrix(x_test))
y_pred[y_pred > 0.5] = 1
y_pred[y_pred <= 0.5] = 0
return int(y_pred[0])
- 最后,在任何 Flask 应用程序中执行的最后一件事是调用
application.run
方法。此方法允许我们指定一个主机。在本例中,我们指定了一个特殊的主机0.0.0.0
,告诉 Flask 接受来自其他计算机的请求。接下来,我们指定一个端口。端口可以是任何数字。但是它确实需要与 Dockerfile 中的端口匹配:
if __name__ == '__main__':
application.run(host='0.0.0.0', port=8000)
- 然后我们创建一个要求文件。
requirements.txt
文件将安装项目的所有 python 依赖项。docker 将使用此文件安装依赖项:
flask
pandas
xgboost
pickle-mixin
gunicorn
- 接着,我们创建 Dockerfile。
docker
文件允许将预测器部署到 Web 端点。docker 文件的第一行将从 Docker Hub 拉取官方的 Python 3.7.5 镜像。接下来,我们将本地文件夹复制到名为app
的 docker 容器中的新文件夹中。然后,我们设置工作目录为app
文件夹。接着,我们使用pip install
来安装我们在步骤 5中创建的要求文件中的要求。然后,我们暴露端口8000
。最后,我们运行启动 Gunicorn 服务器的gunicorn
命令:
FROM python:3.7.5
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 8000
CMD ["gunicorn", "-b", "0.0.0.0:8000", "app"]
它的工作原理……
Flask 是一个轻量级的 Web 服务器。我们使用pickle
从磁盘上保存的模型来恢复模型。然后,我们创建一个http
端点进行调用。
这还不是全部……
现代基于云的 Web 应用程序,例如Azure Web Apps,可以自动将新的 Docker 镜像拉入生产环境。还有许多 DevOps 工具可以拉取镜像,并通过各种测试来运行它们,然后使用 Docker 容器实例或诸如 Kubernetes 之类的 Docker 编排工具部署它们。但要让它们这样做,首先必须将它们放入诸如Azure Container Registry或Docker Hub之类的容器注册表中。为此,我们需要执行几个步骤。首先,我们将构建我们的容器。接下来,我们可以运行我们的容器以确保它工作正常。然后,我们登录到我们的容器注册表服务并将容器推送到其中。详细步骤如下:
- 首先,我们构建容器。为此,我们导航到包含 docker 文件的文件夹,并运行 docker build。我们将使用
-t
命令标记它为ch4
。然后,我们使用句点.
指定 docker 文件位于本地文件夹中:
docker build -t ch4 .
- 现在我们已经构建了一个 Docker 镜像,接下来我们将基于该镜像运行容器,使用
docker run
命令。我们将使用-it
交互命令,以便能够查看服务器的任何输出。我们还将使用-p
或port
命令指定将 Docker 容器的内部端口8000
映射到外部端口8000
:
docker run -it -p 8000:8000 ch4
-
然后,我们需要将容器放入能够被我们计算资源访问的地方。为此,首先注册 Docker Registry 服务,如 Docker Hub 或 Azure Container Registry。然后创建一个新的仓库。仓库提供者将为该仓库提供一个路径。
-
接下来是登录到您的容器注册表服务,标记容器并推送容器。请记住用注册表服务提供的注册名或路径替换
[Your container path]
:
docker login
docker tag ch4 [Your container path]:v1
docker push [Your container path]:v1
然后,您可以使用启用了 docker 的云技术将该预测器服务推送到生产环境。然后,您的设备可以将其传感器读数发送到 Web 服务,并通过云到设备消息接收设备是否需要维护的反馈。