TowardsDataScience-博客中文翻译-2020-四十二-

TowardsDataScience 博客中文翻译 2020(四十二)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

与 GCP 合作开发数据科学

原文:https://towardsdatascience.com/devops-for-data-science-with-gcp-3e6b5c3dd4f6?source=collection_archive---------17-----------------------

来源:https://pix abay . com/photos/dock-ship-container-port-boat-1277744/

为模型服务部署生产级容器

数据科学团队的职能之一是建立机器学习(ML)模型,为产品和个性化提供预测信号。虽然 DevOps 并不总是被视为数据科学团队的核心职责,但随着这些团队开始更多地负责运行和维护数据产品,它变得越来越重要。数据科学家不再将代码或模型交给工程团队进行部署,而是在生产中拥有系统,这种情况越来越普遍。虽然我之前写过一本关于用 Python 构建可伸缩 ML 管道的,但是我并没有太关注构建数据产品的维护方面。这篇文章的目标是通过一个示例 ML 系统,它涵盖了数据科学 DevOps 的一些方面。

https://www.amazon.com/dp/165206463X

模型服务是指托管其他服务可以调用的端点,以便从机器学习模型中获得预测。这些系统通常是实时预测,其中关于用户或 web 会话的状态被传递到端点。设置端点的一些方法包括使用 web 服务,如 Flask 和 Gunicorn,使用无服务器功能,如 GCP 云功能,以及使用消息协议,如 GCP PubSub。如果您希望建立一个需要以最小的延迟为大量请求提供服务的系统,那么您可能希望使用容器化的方法,而不是无服务器的功能,因为您将对系统的运行时环境有更多的控制,并且成本可以显著降低。在这篇文章中,我们将展示如何在谷歌云平台(GCP)上使用 Flask 和 Docker 结合谷歌 Kubernetes 引擎(GKE)构建一个可扩展的 ML 系统。Kubernetes 是一种越来越受 DevOps 欢迎的技术,也是一种越来越需要数据科学家的技能。

除了使用这些工具来构建一个能够以低延迟扩展到大量请求的数据产品之外,我们还将探讨模型服务的以下开发运维关注点:

  1. 如何部署具有高可用性的 ML 模型?
  2. 如何扩展您的部署以满足需求?
  3. 您如何设置警报和跟踪事件?
  4. 您如何推出系统的新部署?

在这篇文章中,我们将重点关注能够部署和监控 ML 模型的工具,而不是触及其他数据科学问题,如模型训练。在本帖中,我们将探讨以下主题:

  1. 为 GCP 建立发展环境
  2. 为本地模型服务开发 Flask 应用程序
  3. 用 Docker 将应用程序容器化
  4. 将容器发布到 GCP 容器注册中心
  5. 使用 GKE 在一个机器集群上托管服务
  6. 使用负载平衡器分发请求
  7. 使用 Stackdriver 进行分布式日志记录和度量跟踪
  8. 使用带有滚动更新的 GKE 部署新版本的服务
  9. 使用 Stackdriver 为警报设置警报

在本文中,我们将从一个烧瓶应用程序开始,并使用 GCP 将其扩展到生产级应用程序。在此过程中,我们将使用 Gunicorn、Docker、Kubernetes 和 Stackdriver 来解决大规模部署此端点时的 DevOps 问题。这篇文章的完整代码清单可以在 GitHub 上找到。

1.GCP 发展环境

在开始之前,你需要建立一个 GCP 账户,该账户提供 300 美元的免费积分。这些信用提供了足够的资金来启动和运行各种 GCP 工具。您还需要创建一个新的 GCP 项目,如下图所示。我们将创建一个项目,其名称为serving,完整的项目 id 为serving-268422

在 GCP 创建一个新项目。

我推荐使用 Linux 环境来开发 Python 应用程序,因为 Docker 和 Gunicorn 最适合这些环境。如果您安装了以下应用程序并设置了命令行访问,您应该能够理解:

  • python :我们将用于模型服务的语言运行时。
  • pip :我们将安装额外的 GCP python 客户端库。
  • docker :用于封装我们的模型服务端点。
  • gcloud : GCP 命令行工具。

我不会深究安装这些应用程序的细节,因为安装会因您的计算环境而有很大的不同。需要注意的一点是,gcloud 工具会在幕后安装 Python 2.7,因此请确保您的命令行 Python 版本不会受到此安装的影响。设置好glcoud之后,您可以使用以下命令创建一个凭证文件,用于对 GCP 的编程访问:

gcloud config set project serving-268422
gcloud auth login
gcloud init
gcloud iam service-accounts create serving
gcloud projects add-iam-policy-binding serving-268422 --member   
    "serviceAccount:[serving@serving-268422.iam.gserviceaccount.com](mailto:serving@serving-268420.iam.gserviceaccount.com)" 
    --role "roles/owner"
gcloud iam service-accounts keys create serving.json 
    --iam-account [serving@serving-268422.iam.gserviceaccount.com](mailto:serving@serving-268420.iam.gserviceaccount.com)

该脚本将创建一个名为serving的新服务帐户,可以访问您的 GCP 资源。最后一步创建了一个名为serving.json的凭证文件,我们将用它来连接 Stackdriver 之类的服务。接下来,我们将使用 pip 在 Google Cloud 中安装用于日志记录和监控的 Python 库,以及使用 Python 作为端点托管 ML 模型的标准库。

pip install google-cloud-logging
pip install google-cloud-monitoringpip install pandas
pip install scikit-learn
pip install flask 
pip install gunicorn

我们现在已经设置好了构建和监控服务于 ML 模型的 web 服务所需的工具。

2.模特服务用烧瓶

为了演示如何将预测模型作为端点,我们将使用 scikit-learn 训练一个逻辑回归模型,然后使用 Flask 公开该模型。接下来,我们将使用 Gunicorn 使我们的服务成为多线程的,并扩展到更大的请求量。

服务于模型请求的示例 web 应用程序的完整代码如下面的代码片段所示。代码首先直接从 GitHub 获取一个训练数据集,然后用 scikit-learn 构建一个逻辑回归模型。接下来,predict函数用于定义服务模型请求的端点。传入的参数( G1,G2,G3,…,G10 )从 JSON 表示转换成一个只有一行的 Pandas dataframe。这些变量中的每一个都跟踪客户过去是否购买过特定的产品,并且输出是客户购买新产品的倾向。dataframe 被传递给 fitted 模型对象以生成倾向得分,该倾向得分作为 JSON 有效载荷被返回给发出模型服务请求的客户机。关于这段代码的更多细节,请参考我之前关于将模型作为 web 端点的文章。

假设这个文件被命名为serving.py,我们可以通过运行下面的命令来测试这个应用程序:python serving.py。为了测试服务是否正常工作,我们可以使用 Python 中的请求模块:

运行该代码块的输出如下所示。结果显示 web 请求是成功的,并且模型为样本客户提供了 0.0673 的倾向得分。

**>>> print(result)** <Response [200]>**>>> print(result.json())** {'response': '0.06730006696024807', 'success': True}

Flask 使用单个进程来服务 web 请求,这限制了端点可以服务的流量。为了扩展到更多的请求,我们可以将 Flask 与一个 WSGI 服务器(比如 Gunicorn)结合使用。要使用 Gunicorn 运行模型服务,我们只需将文件名传递给 Gunicorn,如下所示。

gunicorn --bind 0.0.0.0 serving:app

运行此命令后,您将在端口 8000 上运行一个服务。为了测试服务是否正常工作,您可以更改上面的请求片段,使用端口 8000 而不是端口 5000。

3.集装箱模型

我们现在有了一个在本地机器上运行的模型服务应用程序,但是我们需要一种分发服务的方法,以便它可以扩展到大量的请求。虽然可以使用云提供商提供硬件来手动分发模型服务,但 Kubernetes 等工具提供了完全托管的方法来设置机器以服务请求。我们将使用 Docker 来封装应用程序,然后使用 GKE 托管服务。

容器化应用程序的 docker 文件如下所示。我们从 Ubuntu 环境开始,首先安装 Python 和 pip。接下来,我们安装所需的库,并复制应用程序代码和凭证文件。最后一个命令运行 Gunicorn,在端口 8000 上公开模型服务应用程序。

下面的命令说明了如何构建容器,在您的机器上查看生成的 Docker 图像,并以交互模式在本地运行容器。在本地测试容器以确保您在应用程序中使用的服务按预期工作是很有用的。要测试应用程序是否工作,请将上面的请求片段更新为使用端口 80。

sudo docker image build -t "model_service" .
sudo docker images
sudo docker run -it -p 80:8000 model_service

4.发布到容器注册表

要在 Kubernetes 中使用我们的容器,我们需要将图像推送到 Docker 注册中心,Kubernetes pods 可以从那里提取图像。GCP 提供了一种叫做容器注册的服务来提供这种功能。要将我们的容器推到注册表中,运行下面要点中显示的命令。

上面代码片段中的第一个命令将凭证文件传递给docker login命令,以便登录到容器注册中心。下一个命令用您的 GCP 帐户 ID 标记图像,最后一个命令将图像推送到注册表。运行这些命令后,您可以浏览到 GCP 控制台的容器注册表部分,验证映像是否已成功推送。

集装箱注册中的模型服务图像。

5.与 GKE 一起部署

我们现在可以启动 Kubernetes 集群,在分布式环境中托管模型服务应用程序。第一步是通过浏览 GCP 控制台中的“Kubernetes Engine”选项卡并单击“create cluster”来设置集群。将model-serving指定为集群名称,选择 2 个节点的池大小,并将 g1-small 实例类型用于节点池。这将创建一个小集群,我们可以使用它进行测试。

与 GKE 一起创造了一个库伯内特集群。

一旦集群启动,我们就可以使用我们的容器创建一个工作负载。要将我们的映像作为应用程序在 GKE 上运行,请执行以下步骤:

  1. 从“Kubernetes 引擎”中点击“工作负载”
  2. 单击“部署”
  3. 点击“图像路径”下的“选择”
  4. 选择带有最新标签的型号 _ 服务图像
  5. 点击“继续”
  6. 分配一个应用程序名称“serving”
  7. 在“集群”下,选择您新创建的集群
  8. 单击“部署”

这个过程可能需要几分钟才能完成。完成后,您可以单击工作负载来浏览管理工作负载的 pod 的状态。该映像应该部署到三个单元,如下所示。

模型在 GKE 应用的三个实例。

6.公开服务

我们现在有了在分布式环境中运行的服务,但是还没有一种与服务交互的方法。我们需要设置一个负载平衡器来将模型服务公开给开放的 web。执行以下步骤为服务设置负载平衡器:

  1. 选择“服务”工作负载
  2. 点击“操作”,然后点击“暴露”
  3. 为“端口”选择 80,为“目标端口”选择 8000
  4. 在“服务类型”下,选择“负载平衡器”
  5. 点击“暴露”

要使用负载平衡器,请单击您的工作负载并浏览到“公开服务”一节。您现在应该看到一个负载平衡器设置了一个公开服务的公共 IP 地址。您可以通过修改请求片段以使用负载平衡器的 IP 来测试服务。

服务工作负载的负载平衡器。

我们现在已经具备了可扩展模型托管服务的许多要素。如果需要,系统现在可以扩展到更多的单元,我们可以增加节点池大小和副本数量,以使用 GKE 处理大流量。此外,如果在系统中检测到故障,新的 pod 将会启动,因此 GKE 为我们的服务提供高可用性。我们在该系统的开发操作中缺少的关键部分是警报和监控。我们需要知道系统何时出现故障或异常,并快速做出响应。

7.使用 Stackdriver 进行监控

GCP 现在提供了 Stackdriver 的托管版本,这是一种提供日志记录、监控、警报和事件跟踪的服务。我们将使用日志功能来记录服务器状态更新,我们将使用监控功能来跟踪系统的请求量。为了实现这个功能,我们将使用 Stackdriver 的 Python 接口

下面的代码片段展示了如何为 Stackdriver 设置一个日志客户端,并向服务记录一条文本消息。要在本地工作时设置日志客户端,您需要提供一个凭证文件,但是在 GCP 的计算实例或 GKE 上工作时,这一步可能不是必需的。

要浏览应用程序的日志,请在 GCP 控制台中搜索“日志”,然后选择“日志查看器”选项卡。选择“Global”作为资源,您应该会看到如下所示的日志消息。在下一节中,我们将使用日志记录来跟踪新的服务器实例何时启动,并记录导致异常的模型请求。

在 Stackdriver 中查看服务日志。

Stackdriver 提供了记录定制指标的能力,比如记录每分钟模型服务请求的数量。要使用定制指标,您需要首先创建一个定制指标,如下面的代码片段所示。该代码首先设置一个监控客户机,为记录定义一个新的度量,然后提供一个样本数据点。一旦设置了新的指标,您就可以在每台服务器上每分钟记录一条记录。

要查看自定义指标,请在 GCP 控制台中搜索“Stakdriver Monitoring API ”,然后选择“Metrics explorer”。通过搜索“custom”并选择“Global”作为资源来选择指标。您应该会看到一个图表,它绘制了一段时间内自定义指标的值。

使用 Stackdriver 监控功能跟踪每分钟的请求数。

在下一节中,我们将使用定制的指标来记录每分钟有多少模型请求被处理。在最后一节中,我们将展示如何使用自定义指标来设置警报。

8.执行滚动更新

现在,我们已经拥有了为模型服务应用程序设置警报和监控所需的所有组件。Flask 应用程序的完整代码清单如下所示。

以下是最初应用程序的主要变化:

  1. 我们为日志记录和监控引入了额外的库
  2. 我们根据 IP 和一个随机值为服务创建一个唯一的 ID
  3. 我们设置了日志记录和监控客户端,并记录服务器启动消息
  4. 我们创建一个计数器变量来跟踪请求计数
  5. 我们定义了一个函数来编写自定义指标
  6. 我们定义一个每秒运行一次的函数,将请求计数作为自定义指标传递给 Stackdriver 并重置计数
  7. 我们用导致失败的 try/except 块和日志请求来包装 Flask 请求
  8. 我们为每个 web 请求更新计数器值

一旦我们在本地测试完脚本,我们将需要重新构建容器,并向集群推送一个新版本。第一步是重新构建容器,并将更新后的映像推送到容器注册表,如下所示:

sudo docker image build -t "model_service" .
sudo docker tag model_service us.gcr.io/serving-268422/model_service
sudo docker push us.gcr.io/serving-268422/model_service**# Output** latest: digest: sha256:**286fe62e19368d37afd792b5bc6196dd5b223eada00e992d32ed6f66a4de939d** size: 3048

更新映像后,push 命令将输出一个摘要,我们可以用它在 GKE 上推出容器的新版本。第一步是选择上面显示的摘要的粗体部分。接下来,选择您的工作负载,单击“ACTIONS”,然后单击“Rolling update”。用新的容器摘要替换“图像”下的摘要,然后按更新。

用滚动更新来更新容器。

使用滚动更新的目的是防止在执行系统更新时服务出现任何停机。下图显示了 GKE 如何在运行旧版本的同时为新的服务版本构建新的 pod。一旦单元被加速旋转以匹配当前的工作负载,旧的单元版本将被删除。

GKE 为执行滚动更新提供了强大的功能,降低了 GCP 上基于容器的数据产品开发运维的复杂性。

9.使用 Stackdriver 进行事件跟踪

我们将在本文中讨论的最后一个 DevOps 问题是设置警报和响应事件。使用 Stackdriver 记录定制指标的一个主要好处是,我们可以为不同的问题设置警报,例如缺少检测到的数据或太多的传入请求。我们将通过执行以下步骤,设置一个当系统每分钟收到 10 个以上请求时触发的警报:

  1. 在 GCP 控制台中搜索“Stakdriver Monitoring API”
  2. 单击“警报”选项卡
  3. 单击“创建策略”
  4. 分配一个名称,“太多请求”
  5. 点击“添加条件”
  6. 在“目标”下,选择自定义指标和“全局”作为资源
  7. 在“配置”下,将阈值设置为 10
  8. 选择持续时间的“最近值”
  9. 点击“添加”
  10. 在“通知”下,单击“添加通知频道”
  11. 选择电子邮件并输入您的电子邮件地址
  12. 点击“添加”,然后点击“保存”。

我们现在设置了一个警报,如果自定义指标值超过每秒 10 个请求,就会触发一个事件。我们现在可以通过向负载平衡器 URL 发送 10 个以上的请求来生成警报。向端点发送过多流量的示例如下所示。

超过警报策略的请求计数。

几分钟后,Stackdriver 将检测到违反了警报策略,并触发一个事件。您将收到为警报设置的每个频道的通知。通过电子邮件发送的通知示例如下所示。

警报策略违规的电子邮件通知

如果触发了警报,您可以浏览 Stackdriver 中的“alerting”选项卡,查看状态更新并提供事件的附加注释。对于这个事件,我们可能会决定更改工作负载的自动缩放设置。对于其他类型的实例,比如停机,我们可能会部署一个新版本,或者为当前版本设置新的副本。

在堆栈驱动程序警报选项卡中查看事件。

Stackdriver 中的警报功能提供了许多有助于数据科学家拥有更多数据管道的功能。除了电子邮件提醒之外,您还可以使用其他渠道(如 Slack、PagerDuty 或 SMS)向团队通知提醒。

结论

在这篇文章中,我们介绍了数据科学团队承担更多将 ML 模型投入生产的 DevOps 责任所必需的许多构建模块。我们在这篇文章中提到的关键特性是滚动更新和提醒。当使用 GKE 时,GCP 提供了开箱即用的功能,这使得数据科学团队能够拥有更多部署预测模型的流程。

借助这些工具,数据科学团队可以构建在出现问题时触发警报的数据产品,调查日志以确定问题的根源,并部署新的模型版本,同时最大限度地减少停机时间。

本·韦伯是 Zynga 的一名杰出的数据科学家。我们正在招聘

ML 和其他半真半假的 DevOps 生命周期的过程和工具

原文:https://towardsdatascience.com/devops-for-ml-and-other-half-truths-processes-and-tools-for-the-ml-life-cycle-b642ece2d6c0?source=collection_archive---------43-----------------------

活动讲座

肯尼·丹尼尔| TMLS2019

关于演讲者

肯尼·丹尼尔是 Algorithmia 的创始人兼首席技术官。他在攻读博士学位期间,看到了大量从未公开的算法,于是萌生了开发 Algorithmia 的想法。

作为回应,他建立了 Algorithmia 云人工智能层,该层已帮助超过 80,000 名开发人员共享、管道化和消费超过 7000 个模型。

通过与数百家实施 ML 的公司合作,他创建了企业 AI 层,帮助世界上最大的组织部署、连接、管理和保护大规模的机器学习操作。Kenny 拥有卡内基梅隆大学和南加州大学的学位,在那里他学习了人工智能和机制设计。

ML 和其他半真半假的 DevOps 生命周期的过程和工具

DevOps —使用亚马逊 EKS、ECS 和 Docker 的无服务器 OCR-NLP 管道

原文:https://towardsdatascience.com/devops-serverless-ocr-nlp-pipeline-using-amazon-eks-ecs-and-docker-9c439ebbce01?source=collection_archive---------41-----------------------

我们如何使用由 Docker 和 Kubernetes 驱动的事件驱动微服务架构,自动扩展光学字符识别管道,每天将数千份 PDF 文档转换为文本

图片由穆罕默德·哈桑拍摄,来自皮克斯拜

在最近的一个项目中,我们被要求创建一个能够将 PDF 文档转换为文本的管道。收到的 PDF 文档通常有 100 页,可以包含打字和手写文本。这些 PDF 文档由用户上传到 SFTP。正常情况下,平均每小时会有 30-40 个文档,但在高峰时期会高达 100 个。由于他们的业务不断增长,客户表示每天需要 OCR 多达一千份文档。这些文件然后被输入到一个 NLP 管道中做进一步的分析。

让我们做一个概念验证——我们的发现

转换 100 页文档的时间— 10 分钟

执行 OCR 的 Python 进程消耗了大约 6GB RAM 和 4 个 CPU。

我们需要找到一种渠道,不仅能满足我们的常规需求,还能在高峰期自动扩展。

最终实施

我们决定使用事件驱动的微服务来构建一个无服务器管道。整个过程细分如下:

  • 以 PDF 格式上传的文档—使用 SFTP 的 AWS Transfer 处理
  • 当上传新的 PDF 文档时触发 S3 事件通知—触发 Lambda 函数
  • Lambda 函数在 Kinesis 流中添加 OCR 事件
  • OCR 微服务被触发-使用 Tesseract 库(每页一个)将 PDF 转换为文本。在 MongoDB 中将文本输出保存为 JSON 文档
  • 在 Kinesis 流中添加 NLP 事件
  • NLP 微服务从 MongoDB 读取 JSON。NLP 的最终结果保存回 MongoDB

作者图片

技术栈

数据接收— AWS SFTP 服务

微服务— 存储在亚马逊弹性容器注册表(ECR)中的 Docker 图像

容器编排—EC2 节点上的亚马逊弹性库本内特服务(亚马逊 EKS)

用于容器的无服务器计算引擎— AWS Fargate

基础设施供应— 地形

消息传递— 亚马逊 Kinesis 流

网络体系结构

作者图片

聚类自动缩放

使用水平和垂直缩放的组合来实现集群自动缩放,如下所示:

垂直缩放—容器

根据我们的计算,我们能够在给定的 EC2 节点上支持 25 个正在运行的容器。我们用 3 个副本容器(minReplicas=3)启动 OCR 微服务,并将最大值设置为 25 (maxReplicas=25)。我们还设置 targetAverageUtilization=15,这意味着如果容器 CPU 利用率超过 15%,即容器正在处理一个文档,那么在给定的 EKS 节点上将一个新容器加速到最大值 25。

作者图片

水平缩放— EKS 节点

如果当前 ELS 节点已满,即 25 个并发运行的容器,则 EKS 会自动提供一个新的 EKS 节点。此后,垂直扩展接管新节点并旋转新容器。

通过这种方式,基础架构能够支持数百个 OCR 和 NLP 流程。高峰需求满足后,等待期开始。在等待期到期后,新部署的 EKS 节点和容器会缩减,以便达到最佳资源分配。

我希望这篇文章有助于启动您的 DevOps 知识。这些主题是由 Datafence Cloud Academy 提供的 DevOps 课程的一部分。

DevOps:下一个级别

原文:https://towardsdatascience.com/devops-trends-8ccbed85e7af?source=collection_archive---------15-----------------------

DevOps 的新领域和 2020 年的发展趋势

自从 Patrick Debois 在 2009 年创造了 DevOps 这个术语以来,十多年过去了。在 IT 界,没有什么是确定的。所有的技术和技巧都在沿着我们无法阻挡的创新趋势继续发展。

我们不能仅仅说:“我厌倦了改变;请让我休息一下。”唯一的选择是为变化做好准备。

在 DevOps 中,由于云和市场的兴起,有一个显著的变化,要求总是更高。

DevOps 理念被大多数公司采用,并为质量和成本节约带来了重要的改进。无论如何,DevOps 场景正在不断发展,并适应新的市场需求。

在这篇文章中,我们将分析 DevOps 的新前沿以及与时俱进的知识。让我们看看这篇文章中有哪些最新的趋势可以遵循。

粘土银行Unsplash 拍摄的照片

DevOps 增长

简而言之,DevOps 是将开发(Dev)和运营(Ops)集成在一起的实践,创建一个合作过程。

根据 IDC 的报告,到 2022 年,DevOps 市场将达到 80 亿美元,而 Grand View Research 的另一份报告称,到 2025 年将达到 130 亿美元。这意味着公司对这一主题的兴趣越来越大。

促成这种变化的另一个驱动因素是云的上升。根据甲骨文预测,到 2025 年,80%的 IT 解决方案将迁移到云。

自从 DevOps 概念被提出以来,DevOps 实践从未停止过发展,并得到了越来越强大的工具的支持。

这应该是阅读下一章的一个推动,并成为这一演变的一部分。

将安全性集成到开发运维流程中

安全性一直很重要,但随着数字服务和敏感数据(生物特征、照片等)存储的使用越来越多。),是至关重要的。

用户开始询问他们的数据在哪里,如何管理这些数据。我们的客户希望我们保护他们的信息安全。我们不能失败。顺便说一下,用户总是要求更高,我们需要支持敏捷流程来响应变化。

我们不能允许这样的情况,我们完成一个项目或一个重要的功能,然后我们需要提交给 SecOps。我们的项目可能会被 SecOps 停止一周,或者更糟,被拒绝。我们不能仅仅重做所有的工作,因为一些“小”的安全细节被开发人员忽略了。

这不仅仅是一个成本问题。更多的是时机问题。营销不会等待你的内部问题。客户将从其他供应商那里购买该功能。这就是为什么,几乎对于关键项目,我们需要将 DevOps 与 SecOps 集成,从一开始就让安全人员参与进来。

从一开始就牢记安全需求,并让安全团队参与进来,有助于防止发布过程中出现糟糕的情况和摩擦。

如果你有一个 NoOps 过程,这个问题就更重要了。NoOps 带来了更多的自动化,您可能会陷入这样一种境地:不受信任的代码在没有正确监督的情况下进入生产环境。

这种情况可以通过在流水线(或装配线,我们将在下面的段落中读到)中包含和自动化安全测试来解决。

我们不想因为安全漏洞而在报纸上结束,对吧?嗯,我们需要一种方法来整合敏捷性和安全性;这得到了 DevSecOps 哲学的支持。

NoOPS

NoOps 表示无操作。无服务器解决方案的普及和新的自动化前沿使 NoOps 梦想成为可能。它的理念是去除所有的平台管理部分,减少开发者和基础设施之间的摩擦。

正如我在《更好的编程》上发表的文章《 DevOps 已死,NoOps 万岁》中所解释的,对 DevOps 的尊重是:

DevOps […]意味着你总是需要一些手工劳动。你仍然有一个人在这个过程的大部分背后。这意味着用老方法工作。

NoOps 的目的是定义一种新的过程,在这种过程中,不需要将开发和运营结合起来,因为大多数任务都是自动化的。好处是上市时间更短,质量更好,维护工作更少。

这很有意思。我们可以同意背后的原则,但我们必须意识到故事的另一面:监控和安全。在一个完全自动化的系统中,一切都由代码驱动,谁来负责这些步骤呢?

这个问题的解决方案还是自动化。您需要对每个部分实现自动化,而不再是由一个人来完成,包括监控和安全。这收敛到 DevSecOps 方向。

从管道到装配线

当 DevOps 在 2009 年诞生时,典型的场景可能是一个单一的 web 应用程序。这是 MVC 模式发生革命性变化的时候,在我看来这是很久以前的事了。

今天的情况有点不同。在最简单的情况下,我们有两个应用程序(前端 SPA 和后端 API)。通常他们会有不同的发展,并由不同的团队管理。很可能,我们会为下一个项目选择微服务架构。这意味着要部署大量的应用程序和依赖关系。

我们的管道方法似乎相当有限。管道模型非常适合遵循线性模型(提交>编译>测试>部署),其中您的项目很容易用简单的流程来表示。让我们看一个例子。

传统的流水线方法

在图中,我们看到了代码,由到达 CI\CD 工具的 Pull 请求批准,构建并测试。如果在“构建到测试”阶段出现错误,问题将在部署之前提交给正确的解决方案。

这种方法可能仍然适用于大多数项目,但是在许多情况下,它可能是有限制的。

这种新方法类似于工业,它被称为“装配流水线

就像在物理装配流水线中,每个部门都做出自己的贡献,为产品添加“一块”。

使用这种方法,所有参与这一过程的人从一开始就参与其中,并且从一开始就有一个清晰的流程。通过这种方式,许多团队可以合作并管理具有多种依赖关系的复杂场景(来自应用程序、安全性等)..).

装配线合作的一个例子

装配线是对复杂场景中 DevOps 碎片化的响应,需要文化协作作为粘合剂将所有部分结合在一起。此外,我们还需要一个“超级工具”来管理所有这些交互。

基于工业的类比,我们称之为装配岛。坏消息是,大多数常见的 CI\CD 工具本身并不支持这种方法,您将需要与一组工具结合在一起。

一切都是一个代码

没有比源代码更好的了。它是可版本化的,可以复制和粘贴,可以通过电子邮件发送,如果你执行两次,你会得到相同的结果。或者说,差不多,这就是我们对好代码的期望。

从历史上看,sysadmin 的事情并不相同。你不能剪切和粘贴一个数据中心,并通过电子邮件发送,对不对?

好消息是你的基础设施可以像源代码一样。

我们已经推出了像 AnsibleTerraform 或其他自动化框架这样的解决方案。我们可以对我们的脚本进行版本控制,并自动化基础架构管理。

此外,像在 Kubernetes 中那样的基础设施的声明性描述对于理解事物如何工作非常有帮助,而不需要阅读大量的文档或者通过对配置文件进行逆向工程来理解服务器配置。

流程开始时的自动化有助于以正确的方式开始,并为我们提供一个可复制的基础架构,这无疑是一个巨大的帮助(只是想一想灾难恢复或简单地创建一个新环境来测试单个功能)。

外卖

DevOps 是一个上升的话题。甚至很多东西都已经写好了;我们需要发展我们的能力以适应趋势。

这一变化中最重要的关键词是:

  • 自动化
  • 装配流水线
  • NoOps

它们中的大多数都汇聚到同一个目标:使用技术来改进质量过程。

这种改变可以通过自动化人工任务、在流程中集成安全性和监控来实现,并使用云来加速这一过程。

这怎么可能呢?

我们需要一个工具或一组工具来支持所有的操作,并协调人们的合作,但像往常一样,改变心态是第一步。

感谢您的阅读。

觉得这篇文章有用?在 Medium 上关注我( Daniele Fontani )并查看我在下面 DevOps 上最受欢迎的文章!不要忘记👏这篇文章分享一下吧!

DevSecOps vs DataOps vs MLOps

原文:https://towardsdatascience.com/devsecops-vs-dataops-vs-mlops-93b49f0282b8?source=collection_archive---------23-----------------------

为您的项目选择正确的工作流程

马文·迈耶在 Unsplash 上的照片

自从敏捷宣言在 2001 开始,许多软件开发方法出现了,每一种都试图改进过程。虽然基本的敏捷宣言仍然指导着工作流,但是今天主要的努力已经扩展到打破广泛领域的孤岛。这是通过将孤立的部门统一到协作团队中来实现的。

起初,开发和运营团队被统一到 DevOps 团队中。今天,越来越清楚的是,DevOps 是不够的。安全性也是一个关键的方面,需要在整个过程中解决,而不是在最后解决。因此,DevSecOps 应运而生,它为开发周期增加了安全性。为了确保数据库操作和机器学习操作顺利运行,还创建了 DataOps 和 MLOps。

本文研究了这四种主要方法——devo PS、DevSecOps、DataOps 和 MLOps——为何时以及如何使用每个工作流提供了指导原则。

什么是 DevSecOps?

DevSecOps 是 DevOps 与安全团队的结合。它旨在确保在开发和操作任务中分担安全责任,并实现“安全即代码”的管理。实现 DevSecOps 通常由已经习惯使用 DevOps 策略的团队承担。

DevSecOps 团队使安全成员能够在开发和部署操作与安全问题之间架起一座桥梁。通过打破团队之间的孤岛,他们可以帮助将安全实践集成到现有的工作流中,减少摩擦,并从一开始就确保产品更加安全。

DevSecOps 与 DevOps 的不同之处在于工具和思维方式。这些实现将测试转移到开发过程的左侧,并专注于教授安全性最佳实践。这个想法是从一开始就防止漏洞进入项目。

例如,DevSecOps 团队经常在集成开发环境(ide)中集成静态应用程序安全测试(SAST)工具。这意味着安全审计和测试甚至在提交代码进行传统测试之前就开始了。同样,团队的关注点可能包括云安全状态管理(CSPM)或环境部署步骤中的合规性审计工具。这有助于在环境上线之前发现错误配置。

数据运营:利用 DevSecOps 原则进行安全数据分析

随着 DevSecOps 从 DevOps 发展而来,其他业务单位也开始结合 DevOps 原则,从战略中分支出来并不断发展。一个例子是 DataOps,它有一个数据分析的基础。

DataOps 采用 DevOps 的实践和价值观,并将其扩展到数据分析工作流和目标。它将重点放在协作和分担责任上,并将其转移到收集、存储、分析、保护和交付数据的工程师和管理员身上。

DataOps 旨在简化现有的大数据流程,同时提高工作负载价值和安全性。它通过将安全性集成到数据的编码、保留和交付中来实现这一点,同时牢记分析和存储工作流之间的依赖关系。这有助于确保更可靠的访问,并且可以提高价值实现时间

数据运营基础设施

实施数据运营需要的不仅仅是心态或工作流程的改变;它还需要基础设施的改造。例如,关注敏捷反馈循环和自动化的新架构模式。

DataOps 还经常要求团队实施专为分析和存储设计的下一代技术。例如,团队通常需要采用冗余的、基于集群的存储来确保数据处理管道的高可用性和可伸缩性。可能还需要配置和部署环境,以确保隔离和遵守数据隐私法规。对于生产和测试或开发环境都是如此。

DataOps 团队可能需要解决的另一个变化是所支持的工作负载的多样性。为了让管道提供敏捷性,解决方案需要集成到单个基础设施中,而不是由任务或团队来分发。这意味着整合大数据分析工具,如 Spark 和 Hadoop ,日志聚合器,如 Sumo Logic 和 Splunk,以及监督工具,如 Prometheus 和吉拉。

DevOps 与 MLOps

MLOps 是 DevOps 的另一个分支。在其中,DevOps 原理和工作流被应用于机器学习操作,例如模型训练和部署。它实现了流水线和自动化,使训练操作和完成的模型集成到软件产品中的流程更加顺畅。

在许多方面,MLOps 也与 DataOps 重叠,因为它也需要数据集的处理、维护和安全性。然而,机器学习工作负载的某些方面需要不同的关注点或实现。这些差异包括:

  • 团队技能 —在 MLOps 中,团队需要吸收 ML 研究人员和数据科学家,他们通常不是经验丰富的软件工程师。这些成员专注于实验、模型开发和数据分析,可能不具备执行应用程序开发、操作或安全任务所需的技能。
  • 开发——与传统的更加线性的开发不同,ML 通常是高度实验性的。团队需要能够操作参数和特性,并经常重新训练模型。这需要更复杂的反馈回路。此外,团队需要能够在不妨碍工作流可重用性的情况下跟踪操作的可重复性。
  • 测试 —在 MLOps 中测试需要在 DevOps 或 DevSecOps 中通常完成的方法之上的额外方法。例如,MLOps 需要测试数据验证、模型验证和模型质量测试。
  • 部署 —根据您正在部署的 ML 模型的类型,您可能需要为正在进行的数据处理和培训建立管道。这需要多步流水线,可以处理再训练步骤以及验证和重新部署过程。没有 MLOps,这是手动完成的,但有了它,这些步骤应该是自动化的。
  • 生产 —生产中的模型可能会面临标准应用程序部署不会面临的挑战,例如与不断发展的数据配置文件相关的问题。这可能导致模型衰退,可靠性降低。MLOps 实现需要包含持续的监控和审计,以确认模型是可用的和准确的。如果精度下降,模型需要被召回并修正。

MLOps 与 DevOps 不同的另一个重要领域是如何构建持续集成/持续开发(CI/CD)管道。在 MLOps 中,CI 组件需要扩展到测试和验证数据模式、数据和模型。CD 组件需要支持培训管道以及最终模型预测服务或应用程序的部署。此外,还有另一个组件,持续测试(CT ),它需要被考虑以实现自动的模型再训练和改进。

结论

DevSecOps 将开发、安全和操作角色统一到一个统一的团队中。工作流程通常是自动化的,反馈循环应该是连续的。这确保团队成员将时间花在关键任务上,并不断改进和保护代码。

DataOps 工作流将协作和自动化等 DevOps 原则用于数据管理工作流。此工作流有助于消除源自数据级别的孤岛。MLOps 工作流也利用了 DevOps 原则,但这里的应用是在机器学习操作中。

选择工作流程是一个关键的组成部分,它需要所有相关方的合作。在实现工作流之前,您应该确保所有团队成员都具备必要的技能,工作流适合您的项目,并且您拥有用于测试、部署和生产的所有必要工具。

使用 Streamlit 的糖尿病预测应用

原文:https://towardsdatascience.com/diabetes-prediction-application-using-streamlit-fed6120124a5?source=collection_archive---------34-----------------------

使用 PIMA 印度糖尿病数据集创建机器学习应用程序

马库斯·温克勒在 Unsplash 上的照片

Streamlit 是一个开源的 Python 库,其速度快得惊人,可以轻松地为机器学习和数据科学构建漂亮的定制网络应用。这是一个非常棒的工具,只需要一些 python 知识就可以创建高度交互式的仪表板。

使用 streamlit 创建应用程序会对最终用户产生影响,因为它有一个良好的用户界面,并支持许多用户友好的小部件。在 streamlit 中创建应用程序也很容易。我们将使用 streamlit 创建一个应用程序,它将预测用户是否患有糖尿病。我们将使用的数据集是 PIMA Indian 糖尿病数据集,它包含 8 个预测变量和 1 个目标变量。

让我们看看数据集中有哪些不同的属性。预测变量被命名为结果,其被编码为 0 和 1,其中 0 代表非糖尿病,1 代表糖尿病。其他属性信息如下所示。

探索数据集

让我们从探索我们将使用的数据集开始。为了探索数据集,我们将使用 jupyter 笔记本通过 pandas 加载数据集并执行探索性数据分析。

import pandas as pd
df = pd.read_csv('Diabetes.csv')
df.head()

糖尿病数据集

可视化不同的属性:

  1. 热图
sns.heatmap(df.corr(),annot=True)

热图显示不同属性之间的关联。

2.配对图

sns.pairplot(df,hue='Outcome')

配对图用于显示糖尿病患者和非糖尿病患者之间的相似性和差异。

类似地,我们可以为 EDA 过程创建更多的图,并探索所有属性的不同特性。

我们将在 jupyter 笔记本中创建逻辑回归模型并保存它,以便在我们的 streamlit 应用程序中调用它进行预测。

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import pickle# Split dataset into training set and test set
X_train, X_test, y_train, y_test = train_test_split(df[['Pregnancies', 'Glucose','BloodPressure','SkinThickness','Insulin','BMI','DiabetesPedigreeFunction','Age']], df['Outcome'], test_size=0.3, random_state=109)#Creating the model
logisticRegr = LogisticRegression(C=1)
logisticRegr.fit(X_train, y_train)
y_pred = logisticRegr.predict(X_test)#Saving the Model
pickle_out = open("logisticRegr.pkl", "wb") 
pickle.dump(logisticRegr, pickle_out) 
pickle_out.close()

现在让我们开始创建应用程序。为了创建应用程序,我们需要用 python 创建一个脚本,为此,我们需要在系统中安装一个代码编辑器。你可以使用任何代码编辑器,但是我个人使用 Atom 是因为它的特性。我们将使用逻辑回归来创建预测模型。让我们从导入所需的库开始。

加载所需的库

import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import seaborn as sns
import pickle

调用我们在上面保存的模型:

pickle_in = open('logisticRegr.pkl', 'rb')
classifier = pickle.load(pickle_in)

为应用程序创建用户界面:

Streamlit 为其界面预定义了 UI。我们将使用不同的小部件来显示信息,并接受用户输入进行预测。

st.sidebar.header('Diabetes Prediction')
select = st.sidebar.selectbox('Select Form', ['Form 1'], key='1')
if not st.sidebar.checkbox("Hide", True, key='1'):
    st.title('Diabetes Prediction(Only for females above 21years of    Age)')
    name = st.text_input("Name:")
    pregnancy = st.number_input("No. of times pregnant:")
    glucose = st.number_input("Plasma Glucose Concentration :")
    bp =  st.number_input("Diastolic blood pressure (mm Hg):")
    skin = st.number_input("Triceps skin fold thickness (mm):")
    insulin = st.number_input("2-Hour serum insulin (mu U/ml):")
    bmi = st.number_input("Body mass index (weight in kg/(height in m)^2):")
    dpf = st.number_input("Diabetes Pedigree Function:")
    age = st.number_input("Age:")submit = st.button('Predict')if submit:
        prediction = classifier.predict([[pregnancy, glucose, bp, skin, insulin, bmi, dpf, age]])
        if prediction == 0:
            st.write('Congratulation',name,'You are not diabetic')
        else:
            st.write(name," we are really sorry to say but it seems like you are Diabetic.")

这将创建应用程序,现在我们需要使用。py”扩展名。例如,如果您需要将应用程序保存为“predict ”,那么您需要将其保存为“predict.py”。为了启动该应用程序,我们将打开 Anaconda 命令提示符并键入下面给出的命令。

streamlit run predict.py

这将启动我们的应用程序,现在我们可以使用不同的用户输入来创建一个人是否患有糖尿病。

应用程序的主页

通过输入不同用户的信息,我们可以产生不同的结果,并看到我们的模型有多好和准确。

这只是 streamlit 所能做的一个例子。您可以探索更多信息,了解 streamlit 为创建网络应用和仪表盘提供的无限功能。创建您的应用程序,并在本文的回复中分享您的经验。

[## 使用 Dora 进行探索性数据分析

自动化探索性数据分析和数据建模

towardsdatascience.com](/exploratory-data-analysis-using-dora-ac596e5a32a6) [## 使用 TA-Lib 对股票进行技术分析

技术分析 python 库

towardsdatascience.com](/technical-analysis-of-stocks-using-ta-lib-305614165051)

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。也可以在我的Github中查看我在这里使用过的代码和数据集。 还有,随意探索 我的简介 阅读我写过的与数据科学相关的不同文章。

诊断广义线性模型

原文:https://towardsdatascience.com/diagnose-the-generalized-linear-models-66ad01128261?source=collection_archive---------8-----------------------

广义线性模型

检查 r 中 GLM 的模型假设和异常值。

内森·安德森Unsplash 上拍照

G 一般化线性模型( GLM )之所以受欢迎,是因为它可以处理多种不同响应变量类型的数据(如 二项式 、泊松 ,或者 多项式 )。

非线性模型相比,例如神经网络基于树的模型,线性模型在预测方面可能没有那么强大。但是解释的简单性使得它仍然具有吸引力,尤其是当我们需要理解每个预测因素是如何影响结果的时候。

GLM 的缺点和优点一样明显。线性关系可能并不总是成立的,它对异常值非常敏感。因此,不进行诊断就安装 GLM 是不明智的。

在这篇文章中,我将简要地谈谈如何诊断一个广义线性模型。实现将用 R 代码展示。

主要有两种类型的诊断方法。一个是异常值检测,另一个是模型假设检验。

残差

在深入诊断之前,我们需要熟悉几种类型的残差,因为我们将在整个帖子中使用它们。在高斯线性模型中,残差的概念非常直接,它基本上描述了预测值(通过拟合的模型)和数据之间的差异。

响应残差

在 GLM 中,它被称为“响应”残差,这只是一种有别于其他类型残差的符号。

响应的方差在 GLM 不再是常数,这导致我们对残差进行一些修改。

如果我们根据估计的标准误差重新调整响应残差,它就变成了皮尔逊残差。

皮尔逊残差

你可能会看到一些人使用估计值的平方根而不是标准误差作为上式的分母。这是因为他们使用的是泊松模型,其中估计方差等于估计均值。所以,不要混淆,它们是一回事。

皮尔逊残差的特征之一是皮尔逊残差平方和近似遵循卡方分布。

皮尔逊残差的平方和近似遵循卡方分布

这一特性启发研究人员使用另一种类型的残差,称为偏差残差,其平方和也遵循卡方分布。

注意:模型的偏差仅描述拟合优度,它专门计算完全(饱和)模型的对数似然性与考虑中的模型(您为拟合而构建的模型)的对数似然性之间的差异。高偏差表明模型拟合不好。

每个数据点都有自己对模型偏差的贡献。如果我们根据拟合值和数据之间的差异给这些个体偏差中的每一个分配一个方向,我们就得到偏差残差。

偏差残差

通常,在 GLMs 的诊断中,偏差残差优于其他类型的残差。

在 R 中,使用“残差”函数实现这些不同类型的残差是很简单的。

# R code
# Suppose your fitted model is modresiduals(mod,type = "response") ## response residuals
residuals(mod,type = "pearson") ## pearson residuals
residuals(mod,type = "deviance") ## deviance residuals

函数‘residuals’的默认性能是偏差残差,所以如果您忘记将‘type’参数传递给函数,也不用担心。

模型诊断

残差与拟合值的关系图是诊断中最重要的图形。该图旨在检查残差和拟合值之间是否存在非线性。

GLMs 和高斯线性模型之间的一个区别在于,GLM 中的拟合值应该是在通过连接函数转换之前的值,然而在高斯模型中,拟合值是预测的响应。

让我们以下面的泊松模型为例。记住泊松回归模型是这样的:

泊松模型

让我们首先绘制残差与估计值(mu_i)的关系图。

# R code
mod_pois = glm(Species ~ ., family=poisson,gala) # gala is the example dataset in package "faraway" plot(residuals(mod_pois) ~ predict(mod_pois,type="response"),xlab=expression(hat(mu)),ylab="Deviance residuals",pch=20,col="red") 

相对于估计值的偏差残差

我们可以看到大部分点都挤在了图的左侧,很难解读。因此,最好不要检查链接函数的转换。

# R code
plot(residuals(mod_pois) ~ predict(mod_pois,type="link"),xlab=expression(hat(eta)),ylab="Deviance residuals",pch=20,col="blue")

相对于\eta 的偏差残差

如果满足模型的假设,我们确实期望图中有恒定的变化,因为偏差残差不应该有已经重新调整的非恒定变化。如果图中有明显的非线性,就会发出警告信号。然而,该图看起来没有任何非线性。

假设剧情出现了明显的非线性,怎么办?

通常,从改变链接函数或转换响应变量开始是不明智的。链接函数的选择通常受限于响应数据的自然特征,例如,泊松模型要求响应变量为正。同样,响应变量的转换将违反响应的假定分布。

有两件事值得一试:

  1. 更改模型类型。例如,利用负二项式模型,而不是泊松模型。
  2. 对预测值做一些转换,比如 log()、sqrt()等等…

离群点检测

GLMs 诊断的另一个重要部分是检测异常值。它可以是定量的或图形的。**

在检测异常值的定量方法中,基本思想是找到那些对模型具有异常大的影响的点或者拟合模型最敏感的点。

在拟合模型上可以使用两个指标:杠杆和库克距离。这是 r 中的代码。**

**# R code
i_n = influence(mod_pois)$hat # calculate the influence of data points
which.max(i_n)**

这产生了,

**## SantaCruz 
##        25**

这意味着数据点“SantaCruz”对拟合泊松模型的影响最大。

如果我们应用库克的距离度量,它将产生相同的结果。

**# R code
c_d = cooks.distance(mod_pois)
which.max(c_d)**

结果是,

**## SantaCruz 
##        25**

我们还可以使用 QQ 图来图形化地检测异常值。与高斯线性模型诊断的一个区别是,在 GLM 诊断中,我们不在 QQ 图中寻找直线,因为残差预计不会呈正态分布。GLM 的 QQ 图的唯一目的是找出数据中的异常值。

**# R code
halfnorm((i_n))**

杠杆作用的 QQ 图

**# R code
halfnorm((c_d))**

库克距离的 QQ 图

这些方法建议进一步调查数据点“25 SantaCruz”。

外卖

  1. 在 GLMs 中运行诊断非常重要。
  2. 两个方面需要研究:模型假设和异常值。
  3. 必要时,对预测变量进行转换,但不对响应变量进行转换。
  4. 移除定量和图形化检测到的异常值。

希望这篇帖子对你有帮助。

参考资料:

远方,Julian J. 用 R 扩展线性模型:广义线性、混合效应和非参数回归模型。CRC 出版社,2016。

** [## 解释广义线性模型

GLMs 允许在响应变量具有非正态误差分布的情况下使用线性模型…

www.datascienceblog.net](https://www.datascienceblog.net/post/machine-learning/interpreting_generalized_linear_models/) [## RPubs

编辑描述

rpubs.com](https://rpubs.com/benhorvath/glm_diagnostics) [## 5.7 模型诊断|预测建模说明

正如第 5.2 节所暗示的,广义线性模型是建立在一些概率假设上的,这些假设是…

bookdown.org](https://bookdown.org/egarpor/PM-UC3M/glm-diagnostics.html)

扎克·杜兰特在 Unsplash 上的照片**

用机器学习诊断心脏病

原文:https://towardsdatascience.com/diagnostic-for-heart-disease-with-machine-learning-81b064a3c1dd?source=collection_archive---------15-----------------------

我们能用核磁共振诊断心脏病吗?你打赌!

目录表

  1. 摘要
  2. 背景
  3. 材料&方法
  4. 结果&结论
  5. 参考文献

1。摘要

根据疾病控制和预防中心(CDC)的数据,心脏病是美国男性、女性和大多数种族和民族人群的头号死因。在美国,每分钟都有一个以上的人死于这种疾病,每年有近 50 万人死于这种疾病,每年造成数十亿美元的损失。⁴因此,这个故事的目的是研究不同的潜在监督机器学习(ML)算法,以创建诊断心脏病模型。

2。背景

为了进行这种分析,使用公开可用的克利夫兰心脏病数据集用 Python 构建了一个 Jupyter notebook ,该数据集有 300 多个独特的实例,共有 76 个属性。⁵ ⁶ ⁷ ⁸ ⁹从这 76 个总属性中,只有 14 个常用于研究至今。此外,该分析中使用的超参数来自 Olson 博士的建议,“将机器学习应用于生物信息学问题的数据驱动建议。”⁰在这个分析中使用的库和编码包是:SciPy,Python,NumPy,IPython ⁴,Matplotlib ⁵,熊猫⁶,Scikit-Learn ⁷,和 Scikit-Image。⁸

克里夫兰 dataset⁵ ⁶ ⁷ ⁸ ⁹的背景

克利夫兰数据库的实验集中在简单地试图区分心脏病的存在和不存在。使用的 14 个属性:

表 1。克利夫兰数据集 14 特征和描述。

olson 博士超参数建议的背景

Randal S. Olson 等人在 2017 年发表的一篇文章为解决机器学习的生物信息学问题提供了有见地的最佳实践建议,“将机器学习应用于生物信息学问题的数据驱动建议”

简要地看一下摘要,他们分析了“13 种最先进的、常用的机器学习算法,这些算法针对一组 165 个公开可用的分类问题,以便为当前的研究人员提供数据驱动的算法建议。”有趣的是,这个数据集并没有包含在他们论文的表 2 的分析中。这使得它成为实验的一个很好的候选,因为它是使用 ML 的生物信息学中的一个二元分类问题。

通过他们的研究,他们能够提供“五种具有超参数的算法的建议,这些算法可以最大限度地提高测试问题的分类器性能,以及将机器学习应用于监督分类问题的一般指南。”这些建议概述如下:

表二。奥尔森博士的超参数建议摘要。⁰

3.材料和方法

导入库

导入和查看数据

表 3。导入后克利夫兰数据集的前 10 行原始数据。

将二进制分类的诊断列值更改为 0 或 1 之间。

*# Change num values > 0 to 1 for a Diagnosis* 
data['diagnosis'] = np.where((data['diagnosis']>0),1,0)

检查有关数据的信息以了解其类型。

data.info() *# view*

有两个包含object值的特性:

  1. 通过荧光镜检查着色的主要血管

  2. 地中海贫血

在这两个特征中,有一个唯一的值?,表示缺少一个值。这是从以下内容中发现的:

data['# Major Vessels colored by Flouroscopy'].unique()data['Thalassemia (norm.,fix. defect, revers. defect)'].unique()

两者分别只有 4 次和 2 次计数。假设实例数量不多,因此这些实例按如下方式删除:

sum(data['# Major Vessels colored by Flouroscopy'].values=='?')droplist=data.loc[data['# Major Vessels colored by Flouroscopy']=='?'].index.tolist()data.drop(droplist,axis=0,inplace=**True**)data['# Major Vessels colored by Flouroscopy']=data['# Major Vessels colored by Flouroscopy'].astype(str).astype(float).astype(int)sum(data['Thalassemia (norm.,fix. defect, revers. defect)'].values=='?')droplist=data.loc[data['Thalassemia (norm.,fix. defect, revers. defect)']=='?'].index.tolist()data.drop(droplist,axis=0,inplace=**True**)data['Thalassemia (norm.,fix. defect, revers. defect)']=data['Thalassemia (norm.,fix. defect, revers. defect)'].astype(str).astype(float).astype(int)

此外,还使用以下方法检查缺失值:

data.isnull().sum()

此时,没有丢失的值。

特征工程

如上所述的一些分类值只有几个唯一值。对这些值使用分类编码是一个很好的实践,这样 ML 算法就不会过度适应唯一的值。将这些转换成二进制允许 ML 算法以较少偏差的方式处理数据,而不会丢失任何信息。

表 4。特征工程后克利夫兰数据集的前 5 行原始数据。

接下来,清理和特征工程后的克利夫兰数据集直方图如图 0 所示。这个数字和心脏病的结果有一些明显的关系。

例如,看左上方的年龄支线图,在 60 岁左右,心脏病患者的数量相对于该年龄没有疾病的人增加了近一倍。另外,看性爱的支线剧情,很明显男性(值= 1)比女性(值=0)患心脏病的次数多。还有更多的关系,但关键是这些关系是存在的,而且是牢固的。因此,最大似然算法应该利用这些来进行诊断。

图 0。清理和特征工程后克利夫兰数据集的直方图。

缩放数据

缩放数据非常重要,这样 ML 算法就不会过度拟合错误的要素。使用MinMaxScaler(),根据 0 和 1 之间的最小值和最大值,对每个特征的值进行缩放。这防止了信息丢失,但是允许 ML 算法用数据正确地训练。

X = data.drop(['diagnosis'], axis= 1)
y= pd.DataFrame(data['diagnosis'])

*#Scale Data*
scaler = MinMaxScaler()
X=MinMaxScaler().fit_transform(X.values)
X = pd.DataFrame(X)
X.columns=(data.drop(['diagnosis'], axis= 1)).columns

表 5。克利夫兰的前 5 行缩放数据。

具有皮尔逊相关系数的特征热图

皮尔逊相关系数值接近 1 表示强相关。因此,当查看热图(图 1)时,与第一列“诊断”最相关的值对于训练 ML 算法将非常重要。其他特征之间接近 1 的值是不理想的,因为该信息已经存在用于训练 ML 算法。这在统计学上叫做多重共线性。看来Major Vessels F. Colored: 0Thalassemia:normal的特征与diagnosis具有最强的相关性,皮尔逊相关系数为 0.24。这不是很大的相关性,因此 ML 模型不太可能具有心脏病诊断所需的准确性,但这并不妨碍诊断的有效模型,因为它是基于受试者工作特征(ROC)曲线的曲线下面积(AUC)。

图一。清理、要素工程和缩放后的克利夫兰数据集的热图。

为培训拆分数据

在删除了 6 个缺失值的实例后,数据被分成 80%的培训(237 人)和 20%的测试(60 人)。这是拆分数据以训练 ML 算法的一般经验法则。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2, random_state= 42) *#withold 20% of data for testing*

机器学习

为了找到一个好的模型,在训练数据集上测试了几种算法。使用算法的不同超参数的敏感性研究用 GridSearchCV 迭代,以便优化每个模型。通过查看训练数据和验证数据结果,最佳模型是具有最高准确性而不会过度拟合的模型。对于这些模型来说,计算机时间似乎不是问题,所以它对模型之间的决策没有什么影响。代码和细节请看 Jupyter 笔记本

使用 GridSearchCV 通过 k-fold 交叉验证评估模型,k-fold = 10,GridSearchCV 迭代不同算法的超参数。测试了几个新的超参数,以及奥尔森博士的分析中没有包括的一个附加算法。新算法是一个神经网络,多层感知器(MLPClassifier)。

表 6。GridSearchCV 使用的超参数和算法摘要。

评估模型

查看混淆矩阵图

图二。答 3。答 4。答,5。答,6。答,7。答,8。答,9。答,10。答,11。答,12。答和 13。答

当涉及到诊断心脏病时,我们希望确保我们没有太多的假阳性(你没有心脏病,但被告知你做了并得到了治疗)或假阴性(你有心脏病,但被告知你没有得到治疗)。因此,选择总体精度最高的模型(精度是混淆矩阵上对角线的总和除以总和)。您可以看到,所有模型的假阳性和假阴性都不到几个,这意味着它们都相当准确,因为测试集有 60 个观察值。

查看变量重要性图

图二。b,3。b,4。b,5。b,8。b 和 9。B

大多数ensemble模型都有一个名为predict_proba的参数,该参数允许通过gini指数或entropy的多数投票,根据模型的概率输出最重要的特征。这 5 个最重要的变量出现在所有这些变量重要性图中:

  • Exercise Max Heart Rate Achieved
  • Major Vessels F. Colored: 0
  • ST depression induced by Exercise relative to Rest
  • Thalassemia: reversable defect
  • Thalassemia: normal

毫不奇怪,在图 1 的热图中,这些相同的变量也显示出与诊断的高度相关性。因此,这些变量变得如此重要是有道理的。

查看接收机工作特性(ROC)曲线

图 14

所有模型的受试者操作特征(ROC)曲线都有Excellent曲线下面积(AUC ),因为它们的值都大于 90%(所有值都接近 95%),这意味着它们都可以作为优秀的诊断。这也表现在它们的高特异性和灵敏度值上。

4.结果和结论

所有模型在微调其超参数后都表现良好,但最佳模型是具有最高总体准确性的模型。

在该分析中,Support Vector Machine (SVM)Multilayer Perceptron Classifier (MLP)并列获得最高的整体精度,91.7%(见下表 7)。然而,SVM的 AUC96.8%MLP的 AUC93.6%高。这意味着SVM在这种情况下是更好的诊断模型,但并不意味着SVM在所有情况下都是最好的模型。所有这一切意味着,对于给定的测试集,这个模型表现最好。在这个测试中 20%的数据(60 个随机个体)中,只有少数几个在所有模型中被误诊。没有一个模型是完美的,但是这些结果是有希望的。如果平均来说,测试集中少于几个人被如此准确和精确地误诊,这是一个好的开始。显然,未来的目标是找到更好的度量标准来训练和测试 ML 算法或更多的数据。

这里的关键是,由于受试者工作特征(ROC)曲线的曲线下面积(AUC)如此之高,心脏病的诊断是可能的。所有值都大于 90%,诊断结果非常好。

表 7。统计结果汇总。

5.参考

  1. Heron,M. 死亡:2017 年的主要原因国家人口动态统计报告;68(6).2019 年 11 月 19 日接入。
  2. 本杰明·EJ、蒙特纳·P、阿朗索·A、比滕考特·MS、卡拉威·CW、卡森·AP 等,《心脏病和中风统计——2019 年更新:美国心脏协会的一份报告》。循环。2019;139(10):e56–528。
  3. Fryar CD,Chen T-C,Li X. 心血管疾病未控制危险因素的患病率:美国,1999–2010。NCHS 数据简报,第 103 号。马里兰州 Hyattsville:国家卫生统计中心;2012.2019 年 5 月 9 日接入。
  4. Dua d .和 Graff c .(2019 年)。http://archive.ics.uci.edu/ml 的 UCI 机器学习库。加州欧文:加州大学信息与计算机科学学院。
  5. 匈牙利心脏病研究所。布达佩斯:安朵斯·雅诺西,医学博士
  6. 瑞士苏黎世大学医院:医学博士威廉·斯坦布伦
  7. 瑞士巴赛尔大学医院:医学博士马蒂亚斯·普菲斯特勒
  8. V.A .医疗中心,长滩和克里夫兰诊所基金会: Robert Detrano,医学博士,哲学博士
  9. Olson,Randal S .等人“将机器学习应用于生物信息学问题的数据驱动建议”。“太平洋生物计算研讨会。太平洋生物计算研讨会 23(2017):192–203。
  10. SciPy。(2019)SciPy 1.0-Python 中科学计算的基本算法。预印本 arXiv:1907.10121
  11. 巨蟒。 a)特拉维斯·芬特。科学计算的 Python,科学计算&工程,9,10–20(2007)b)k . Jarrod mill man 和 Michael Aivazis。面向科学家和工程师的 Python,科学计算&工程,13,9–12(2011)
  12. NumPy。 a)特拉维斯·芬特。美国 NumPy 指南:特雷戈尔出版公司(2006 年)。(b)斯蒂芬·范德沃特、克里斯·科尔伯特和盖尔·瓦洛夸。NumPy 数组:高效数值计算的结构,科学计算&工程,13,22–30(2011)
  13. IPython。 a)费尔南多·佩雷斯和布莱恩·e·格兰杰。IPython:交互式科学计算系统,科学计算工程,9,21–29(2007)
  14. Matplotlib。 J. D. Hunter,“Matplotlib:一个 2D 图形环境”,科学计算&工程,第 9 卷,第 3 期,第 90–95 页,2007 年。
  15. 熊猫。 韦斯·麦金尼。Python 中统计计算的数据结构,第 9 届科学中的 Python 会议录,51–56(2010)
  16. Scikit-Learn。 法比安·佩德雷戈萨、加尔·瓦洛夸、亚历山大·格拉姆福特、文森特·米歇尔、贝特朗·蒂里翁、奥利维耶·格里塞尔、马蒂厄·布隆德尔、彼得·普雷顿霍弗、罗恩·韦斯、文森特·杜伯格、杰克·范德普拉斯、亚历山大·帕索斯、大卫·库尔纳波、马蒂厄·布鲁彻、马蒂厄·佩罗特、爱德华·杜谢斯奈。sci kit-learn:Python 中的机器学习,机器学习研究杂志,12,2825–2830(2011)
  17. Scikit-Image。 斯蒂芬·范德沃特、约翰内斯·l·舍恩伯格、胡安·努涅斯-伊格莱西亚斯、弗朗索瓦·布洛涅、约书亚·d·华纳、尼尔·雅戈、伊曼纽尔·古雅特、托尼·于和 scikit-image 供稿人。sci kit-Image:Python 中的图像处理,PeerJ 2:e453 (2014)

与数据库的对话

原文:https://towardsdatascience.com/dialogues-with-databases-d0eefa754f81?source=collection_archive---------84-----------------------

活动讲座

凯瑟琳·休姆| TMLS2019

在多伦多机器学习峰会上的演讲

关于演讲者

Kathryn Hume 领导加拿大皇家银行机器学习研究实验室 Borealis AI 的产品和业务开发。在加入 Borealis AI 之前,Kathryn 曾在 integrate.ai 和 Fast Forward Labs(被 Cloudera 收购)担任领导职务。她已经帮助 50 多家财富 500 强公司开发了机器学习应用程序,是人工智能道德和负责任部署方面的公认专家。Kathryn 经常在人工智能上发表演讲和写作,其作品在 TED、《环球邮报》和《哈佛商业评论》上发表。她拥有斯坦福大学比较文学博士学位,会说七种语言,并在哈佛商学院、麻省理工学院、斯坦福大学和卡尔加里大学法学院发表过关于人工智能和职业道德的演讲。

与数据库对话 |凯瑟琳·休姆

使用 PyTorch 根据切割、颜色、净度和价格预测钻石价格

原文:https://towardsdatascience.com/diamond-price-prediction-based-on-their-cut-colour-clarity-price-with-pytorch-1e0353d2503b?source=collection_archive---------21-----------------------

PyTorch 中构建简单线性回归模型的指南

资料来源:Opensourceforu.com

你有没有问过自己,钻石是怎么定价的?这篇文章讨论了基于切割、颜色、净度和其他属性的钻石价格预测,还介绍了使用 PyTorch 建立一个简单的线性回归模型。

我的 PyTorch 之旅从“使用 PyTorch 进行深度学习:零到 Gans”课程开始,现在我们已经进入了该课程的第二周,到目前为止,我们已经在 PyTorch 中讲述了线性&逻辑回归的基础知识和实现。PyTorch 的基本功能请参考这篇文章

线性回归简介:

线性回归是机器学习中最简单的算法之一。根据统计,术语回归被定义为输出变量和输入变量之间关系的度量,因此,线性回归假设自变量(输入)和因变量(输出)之间存在线性关系。

更一般地,线性模型通过简单地计算输入要素的加权和以及偏差(也称为截距)项来进行预测。(来源)如下式所示。

资料来源:中央统计局。Brown.edu

我考虑了经典的钻石数据集,它包含了近 54,000 颗钻石的价格和其他属性,该数据集托管在 Kaggle 上。数据集包含 53940 行和 10 个变量。在开始构建模型之前,让我们来看看变量&的定义。

  • 价格以美元为单位
  • 克拉钻石的重量
  • 切割切割质量(一般、良好、非常好、优质、理想)
  • 颜色钻石颜色,从 J(最差)到 D(最好)
  • 净度衡量钻石净度的指标(I1(最差)、SI2、SI1、VS2、VS1、VVS2、VVS1、IF(最佳))
  • x 长度单位为毫米
  • y 宽度单位为毫米
  • z 深度以米为单位
  • 深度:钻石的高度
  • :钻石台面的宽度,以其平均直径的百分比表示

让我们根据以下步骤开始构建线性回归模型

  1. 导入所需的包
  2. 加载数据集
  3. 执行探索性数据分析(EDA)
  4. 为训练准备数据集
  5. 创建线性回归模型
  6. 训练模型以适应数据
  7. 使用训练好的模型进行预测

步骤 1:导入所需的包

步骤 2:加载数据集

为了加载数据集,我们将使用pd.read_csv()函数,它会将数据集转换为数据帧,并使用pd.head()函数查看数据集的前 5 行

删除不必要的列“未命名:0”

数据集有 53940 行和 10 个变量

步骤 3:探索性数据分析(EDA)

看看数据集的简明摘要

数据集中没有空数据,也可以使用data.isnull().any进行检查。给定的数据集有 6 个数值列和 3 个非数值(分类)列

使用pd.describe()获取数据集的描述性统计数据

观察到 x(长度)、y(宽度)和 z(深度)的最小值为零,并且使钻石的长度\宽度\深度为零没有任何意义。

让我们放弃这几行

观察到 x、y 和 z 的最小值是非零值

hist()法绘制数值属性的分布

让我们看看数据集的配对图。配对图允许我们看到变量的分布以及两个变量之间的关系

观察到 x、y 和 z 变量相对于因变量(目标)价格具有良好的相关性,让我们通过使用.corr() &来量化这种相关性,并使用sns.heatmap()方法将其可视化

我们可以得出结论,克拉,x,y & z 特征与 w.r.t 价格变量有很强的相关性,而深度与 w.r.t 价格变量的相关性很弱。因此,我们可以从模型的最终输入特征列表中删除深度特征。

让我们通过使用箱线图来了解目标(价格)变量的分类特征

我使用箱线图来比较给定分类变量类别之间的数据分布、集中趋势(中值)和可变性,这些图也有助于识别异常值。

到目前为止,我已经做了非常初步的数据探索,以了解这些特征及其与目标变量的关系。本文的主要目标是在 PyTorch 中实现线性回归,所以让我们停止数据探索,直接进入本文的核心部分。

在继续构建模型之前,最好将分类数据转换为数值数据,有两种方法可以转换为数值形式 1。标签编码器或整数编码 2。一键编码

一般来说,一键编码做得很好,更多细节请参考此链接

步骤 4:为训练准备数据集

我们需要将熊猫数据框架中的数据转换成 PyTorch 张量用于训练。为此,第一步是将它转换成 NumPy 数组

现在,我们需要为训练和验证创建 PyTorch 数据集和数据加载器。第一步是通过使用torch.tensor函数将输入的&目标数组转换成张量来创建TensorDataset

现在,我们将把数据集分为训练和验证数据集。训练集将用于训练模型,而验证集用于评估模型&它还用于调整超参数(学习率、时期、批量等..)以便更好地概括训练模型。

我们将使用random_split函数将数据集分割成所需百分比的训练&验证

让我们来看一组数据,以验证到目前为止一切正常。

xb 和 yb 的形状表明到目前为止一切都运行良好

步骤 5:创建线性回归模型

我们将通过扩展 PyTorch 的nn.Module类来创建线性回归模型

__init__构造函数方法中,我们使用nn.Linear函数实例化权重和偏差。在 forward 方法中,当我们将一批输入传递给模型时会调用这个方法,它会将输入传递给self.linear。定义的神经网络将如下所示

我们已经定义了模型并实例化了权重和偏差。现在,让我们定义成本函数。

线性回归的常用成本函数(J)是均方误差(MSE)。该函数计算预测值和目标值之间的差值,并对其求平方。成本函数衡量评估模型性能的好坏。

training_step & validation_step该方法分别计算每个训练&验证批次的预测& MSE 损失。该方法返回每个历元的平均验证损失。请参考此链接以了解历元&迭代之间的差异。

让我们通过调用DiamondPriceModel()类创建一个模型对象

步骤 6:训练模型以适应数据

  • 线性回归的主要目标是最小化成本函数。
  • 梯度下降是一种帮助最小化成本函数的优化算法,其主要目的是找出全局最小值。这里,梯度表示函数的变化率,它指向函数最大增长的方向。为了最小化成本函数,我们需要执行成本函数(J)相对于其参数(权重(w) &偏差(b))的梯度。

来源:http://rasbt.github.io/

注意:学习率α是一种控制权重更新量的方法。如果我们选择一个小的学习率,我们的模型训练可能需要很长时间。但是如果选择大的学习率,可能会超调,我们的训练永远不会收敛。具体的学习率取决于我们的数据和我们使用的模型类型,但通常最好在[1e 8,1e 1]范围内探索。

evaluate函数,调用validation_step & validation_epoch_end方法,返回每个历元后的平均验证损失。

fit函数通过在随机梯度下降(梯度下降的一种变体)优化的帮助下更新每一步的参数来执行模型训练。它记录每个时期的确认损失,并返回训练过程的历史。

最初的验证损失非常高,这是人们从随机初始化的模型中可能期望的。目标是使用足够的历元和合适的学习速率来最小化这种损失。这是一个反复试验的过程,以产生最适合我们的模型。

运行该模型 800 个时期,验证损失已经减少到 1276336.0000。现在,让我们用验证数据来测试模型。

步骤 7:根据验证数据测试模型

predict_single函数返回给定输入张量的预测钻石价格值。

测试# 1

测试# 2

通过在当前模型中引入隐藏层可以提高模型性能,并且可以进一步将优化器改进为 Adam 优化器。

代码库:你可以在我的 GitHub repo 找到笔记本。

嗯!!我们刚刚使用 PyTorch 完成了第一个机器学习算法。感谢阅读!!并尝试使用自己的数据集。

快乐学习!编码快乐!

参考资料:

  1. https://www.youtube.com/watch?v=4ZZrP68yXCI&feature = youtu . be
  2. https://github . com/madewithml/basics/blob/master/notebooks/07 _ Linear _ Regression/07 _ PT _ Linear _ Regression . ipynb
  3. https://cs . brown . edu/courses/csci 1951-a/assignments/stats _ 讲义. html
  4. https://jovian.ml/forum/c/pytorch-zero-to-gans/18
  5. https://www.kaggle.com/shivam2503/diamonds

DiCE:酒店取消的多种反事实解释

原文:https://towardsdatascience.com/dice-diverse-counterfactual-explanations-for-hotel-cancellations-762c311b2c64?source=collection_archive---------44-----------------------

DiCE 是更广泛的 InterpretML 库的一部分,非常擅长为特定数据集生成“多样化的反事实解释”。

“多样化的反事实解释”,我们指的是由一个模型做出的决定,这个模型的影响力足以改变结果变量。

我们举个例子。假设一位酒店经理正在分析客户预订,并希望分析哪类客户更有可能取消预订。

来源:Jupyter 笔记本

这是一个客户没有取消酒店预订的例子。

根据这个特例,客户属于细分市场线下 TA/TO ,客户类型为合同,分销渠道为 TA/TO ,所需停车位为 0

然而,一个希望最大限度减少取消预订的酒店经理最好能确定哪些类型的顾客可能会取消预订。这就是骰子的用武之地。

当查看上面的反事实解释时——客户取消了的预订,我们观察到:

  • 提前期在四个示例中明显更长。
  • 一个客户仍然属于线下 TA/TO 细分市场。
  • 四个取消的客户之一仍然是合同客户。
  • 分配渠道所需停车位数量保持不变。

不取消和取消(至少在这种情况下)的最大区别似乎是提前期。从客户预订到实际入住酒店的时间越长,客户取消预订的可能性就越大。

方法学

为了生成上面的骰子解释:

  1. 使用 Tensorflow 的 Keras API 训练神经网络,以便建立分类算法。
  2. 然后从神经网络生成骰子模型。
  3. 使用原始数据集的参数创建“骰子数据集”,即包含连续变量的范围,同时包括分类变量的所有类别。
  4. 然后生成骰子解释实例,以解释结果变量的结果。
  5. 然后产生反事实的例子(按照上面的例子)来解释可能改变结果变量的条件。
  6. 接近度差异度被修改,以检验接近原始输入的数据与显著变化的数据如何影响反事实解释。

关于第 3 点,DiCE 的一个便利特性是能够丢弃旧数据,同时保留模型训练参数。这样的做法符合公平数据原则,规定数据应该 F 可用, A 可访问, I 不可操作, R 可用。

但是,其他法律可以规定客户有权删除其个人数据,或者确实要求在一定期限后删除客户数据。

在这种情况下,DiCE 的优势在于它允许保留对相关数据的模型训练,而不必保留数据本身。

请注意,TensorFlow 1.15 在此实例中与 Python 3.6.9 一起用于运行 DiCE 和相关模型。

神经网络训练及骰子说明实例

以下是数据集的一个示例:

来源:Jupyter 笔记本

数据集中的连续要素定义如下:

# Dataset for training an ML model
d = dice_ml.Data(dataframe=dataset,
                 continuous_features=['LeadTime','RequiredCarParkingSpaces'],
                 outcome_name='IsCanceled')

使用二元交叉熵损失和 adam 优化器在 30 个时期内训练神经网络模型。

from tensorflow.keras.models import Sequential
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.wrappers.scikit_learn import KerasRegressorsess = tf.InteractiveSession()
# Generating train and test data
train, _ = d.split_data(d.normalize_data(d.one_hot_encoded_data))
X_train = train.loc[:, train.columns != 'IsCanceled']
y_train = train.loc[:, train.columns == 'IsCanceled']# Fitting a dense neural network model
ann_model = Sequential()
ann_model.add(Dense(6, input_shape=(X_train.shape[1],), activation=tf.nn.relu))
ann_model.add(Dense(1, activation=tf.nn.sigmoid))
ann_model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
history=ann_model.fit(X_train, y_train, validation_split=0.20, epochs=30, verbose=0, class_weight={0:1,1:2})
history

这是模型损耗的曲线图:

来源:Jupyter 笔记本

这是训练和验证准确性的图表:

来源:Jupyter 笔记本

出于解释目的,该模型现在存储为骰子模型:

>>> m = dice_ml.Model(model=ann_model)
>>> m
<dice_ml.model_interfaces.keras_tensorflow_model.KerasTensorFlowModel at 0x7f22fb2e0da0>

如前所述,这些特征现在以与 DiCE 兼容的特殊格式存储,以便生成 DiCE 解释实例:

new_d = dice_ml.Data(features={
                   'LeadTime':[0, 737],
                   'MarketSegment': ['Complementary', 'Corporate', 'Direct', 'Groups', 'Offline TA/TO', 'Online TA'],
                   'CustomerType': ['Contract', 'Group', 'Transient', 'Transient-Party'],
                   'DistributionChannel':['Corporate', 'Direct', 'TA/TO', 'Undefined'],
                   'RequiredCarParkingSpaces': [0, 8]},
         outcome_name='IsCanceled')

形成骰子解释实例:

>>> exp = dice_ml.Dice(new_d,m)
>>> exp<dice_ml.dice_interfaces.dice_tensorflow1.DiceTensorFlow1 at 0x7f22fb2da630>

不同的反事实解释

既然解释实例已经形成,这可以用来生成反事实的解释。

让我们以具有以下属性的客户为例,这些属性包含在所谓的查询实例中:

query_instance = {'LeadTime': 68,
    'MarketSegment': 'Online TA',
    'CustomerType': 'Transient',
    'DistributionChannel': 'TA/TO',
    'RequiredCarParkingSpaces': 0}

反事实的例子产生如下:

# Generate counterfactual examples
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class="opposite")
# Visualize counterfactual explanation
dice_exp.visualize_as_dataframe()

来源:Jupyter 笔记本

这里,生成了一个原始结果为 1 的案例,即取消。还观察到顾客不取消他们的酒店预订的反例也产生了。

相反,这里有一个查询实例,客户取消了的酒店预订。

query_instance_2 = {'LeadTime': 93,
    'MarketSegment': 'Offline TA/TO',
    'CustomerType': 'Contract',
    'DistributionChannel': 'TA/TO',
    'RequiredCarParkingSpaces': 0}

再次,产生了反事实的例子:

# Generate counterfactual examples
dice_exp_2 = exp.generate_counterfactuals(query_instance_2, total_CFs=4, desired_class="opposite")
# Visualize counterfactual explanation
dice_exp_2.visualize_as_dataframe()

来源:Jupyter 笔记本

这些只是一些如何产生反事实解释的例子。

在这些例子中,产生了四个反事实的例子(total_CFs=4),其中“相反的”类是感兴趣的。

邻近性与多样性

关于反事实的解释,使用骰子可以产生更深刻的见解。

例如,可以考虑对原始输入的解释的接近度以及那些解释的多样性,即对所讨论的解释的建议改变的范围。 DiCE GitHub 自述文件提供了关于这些的更多解释。

以下是 proximity_weight 增加到 1.5 时的反事实解释。

查询实例 1

来源:Jupyter 笔记本

查询实例 2

来源:Jupyter 笔记本

现在,让我们将 diversity_weight 设置为 2.5 ,其中 proximity_weight0 处。

查询实例 1

来源:Jupyter 笔记本

查询实例 2

来源:Jupyter 笔记本

结论

在本文中,您已经看到:

  • 骰子如何被用来产生反事实的解释
  • DiCE 如何在不保留数据本身的情况下保留模型训练
  • 修改神经网络输出以便与骰子兼容
  • 利用邻近性和多样性来产生有根据的反事实解释

非常感谢您的参与,这个例子的相关 GitHub 库可以在这里找到。

免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。

参考

卡普兰疯狂:二分法连续变量

原文:https://towardsdatascience.com/dichotomising-continuous-variables-8cf3fdd5de33?source=collection_archive---------31-----------------------

封面照片:托拜厄斯·范·施奈德

M 任何时候,我都会看到从数据集(如 TGCA 癌症基因组图谱)中选择基因的海报或演示,作者表明该基因的高(或低)表达与更好或更差的存活率相关。通常,会显示 p 值,作者没有提到他们是否调整了其他(已知的)预后因素。然后他们利用这种存活率的差异进行进一步的功能验证。

这没有太大的意义。

因为基因表达是一个连续变量(这里基因表达可以用任何其他连续变量代替),所以需要设定一个阈值来定义“高”与“低”表达,以便绘制 Kaplan Meier 生存曲线。连续变量的二分法是一个的坏习惯,并且伴随着成本。

如果没有预先指定临界值,可以改变阈值,直到找到显著的 p 值:

使用这种所谓的“最佳”分界点(通常是给出最小 P 值的分界点)存在产生虚假显著结果的高风险;两组之间的结果变量的差异将被高估,可能相当大;并且置信区间会太窄。永远不要使用这种策略— 奥特曼,BMJ

通过模拟数据,这个概念变得更加清晰。在该动画中,截止值从~27 开始变得“统计显著”,并在~ 65 左右停止显著,而曲线本身的变化很小:

在这个模拟中,除了定义高和低基因表达的阈值之外,没有任何变化。

如果您仍然想了解连续变量和生存率之间的关系,您可能想了解 Cox 比例风险模型,其中包括影响预后的其他协变量。

如果我们只包括基因表达,Cox 比例风险(请注意,我没有检查比例风险假设,这应该在您对真实数据集进行此操作之前进行)给出的 p 值为 0.0276,HR 为 3.114,95%置信区间为[1.133,8.559]。由于在将其用作 Cox 拟合的输入之前,我已经转换了 log10 尺度的基因表达,3.14 的 HR 将意味着基因表达每增加 log10 单位的风险增加 3.14 倍(正如@jvdesomp 在 Twitter 上指出的)。

此外,使用 Cox 回归模型,如果基因表达增加,我们可以评估相对死亡率(而不是隐藏大部分数据的二分法):

模拟内部

生成虚构数据

如果你对模拟是如何制作的感兴趣,请继续阅读。

开始了一项虚构的研究,随访时间为 96 个月,有 300 名参与者。没有被审查的个体(除了在 96 个月,模拟结束时)。基因表达从对数正态分布中随机取样,其中 rlnorm R 函数的均值为 4,标准差为 1:

模拟了其他几个协变量,这在本文中没有提到。我鼓励你自己看一看它们,看看对 KM 估计和 Cox 模型有什么影响。

考克斯回归

为了研究基因表达对风险比的影响,使用了惩罚样条项(注意,基因表达是从对数正态分布中取样的,因此这里应该转换回 log10 标度):

相对死亡率如生存曲线插图所述绘制(但使用 ggplot 代替 matplot 函数)。

这个模拟的完整代码可以在 GitHub 上获得。

关于作者:

我是 Ruben Van Paemel 博士,我于 2017 年从医学院毕业后开始在根特大学(医学遗传学中心)担任博士研究员,由研究基金会 Flanders 资助。我也是根特大学医院的儿科住院医师。可以在 Twitter 关注我:@ RubenVanPaemel

我从事神经母细胞瘤的研究,这是一种罕见但极具破坏性的肿瘤,最常见于非常年幼的儿童。我们的团队试图了解潜在的基因改变,以提高神经母细胞瘤儿童的诊断、治疗和最终存活率。

参考

封面照片- Unsplash

Kaplan Meier 模拟代码编辑自http://dwoll . de/rex repos/posts/survival km . html # simulated-right-删失事件次数服从威布尔分布

Python 中的字典

原文:https://towardsdatascience.com/dictionaries-in-python-74839d2d852?source=collection_archive---------36-----------------------

理解 Python 中的字典

来源

在 python 中,字典是数据值的无序集合,其中每个元素是一个键/值对。字典有各种各样的应用,包括视频游戏中的映射对象、数据库设置、单元测试等等。在这篇文章中,我们将讨论如何在 python 中定义和执行简单的字典操作。

我们开始吧!

带集合和列表的词典

字典的简单定义是将一个键映射到一个值的数据结构。大多数情况下,字典用于将键映射到多个值。为此,我们需要将字典键映射到容器,比如集合和列表。例如,假设我们有一个包含电影名称和元评论等级的字典:

movie_ratings = {'movie_name': ['Parasite', 'Once Upon a Time in Hollywood', 'Marriage Story'], 'rating': [96, 83, 93]}

在这个字典中,“movie_name”是一个映射到包含三部电影的列表的键。类似地,“rating”是映射到三个电影分级的另一个键。您也可以使用集合来定义类似的字典:

movie_ratings = {'movie_name': {'Parasite', 'Once Upon a Time in Hollywood', 'Marriage Story'}, 'rating': {96, 83, 93}}

其中集合是没有重复的无序集合对象。如果要保持集合中各项的插入顺序,就需要使用列表。如果你不想在你的收藏中有重复的东西,而且顺序也不重要,你可以使用集合。

默认词典

定义带有映射到列表或集合的键的字典的一种简单方法是使用“defaultdict”。让我们使用‘default dict’来构造我们的第一个字典,它使用了列表。首先,让我们导入' defaultdict ':

from collections import defaultdict

让我们将电影分级字典初始化为列表字典:

movie_ratings = defaultdict(list)

python 中默认字典的一个很好的特性是,它们会自动初始化第一个值,这样您就可以专注于插入值。让我们插入三个电影名称:

movie_ratings['movie_name'].append('Parasite')
movie_ratings['movie_name'].append('Once Upon a Time in Hollywood')
movie_ratings['movie_name'].append('Marriage Story')

现在,让我们打印字典:

print(movie_ratings)

接下来让我们插入电影评级:

movie_ratings['rating'].append(96)
movie_ratings['rating'].append(83)
movie_ratings['rating'].append(93)

让我们再打印一次:

print(movie_ratings)

现在让我们添加一个副本。让我们插入“寄生虫”及其评级:

movie_ratings['movie_name'].append('Parasite')
movie_ratings['rating'].append(96)

让我们打印:

print(movie_ratings)

我们看到我们已经成功地插入了一个副本。让我们试着为一套字典做同样的事情。让我们初始化我们的集合字典:

movie_ratings = defaultdict(set)

我们现在将添加我们的电影名称。请注意,对于集合,我们使用“add()”方法,不要与我们用于列表的“append()”方法混淆:

movie_ratings['movie_name'].add('Parasite')
movie_ratings['movie_name'].add('Once Upon a Time in Hollywood')
movie_ratings['movie_name'].add('Marriage Story')print(movie_ratings)

接下来让我们添加我们的评级:

movie_ratings['rating'].add(96)
movie_ratings['rating'].add(83)
movie_ratings['rating'].add(93)print(movie_ratings)

现在让我们尝试添加一个副本。让我们再次添加“寄生虫”及其评级:

movie_ratings['movie_name'].add('Parasite')
movie_ratings['rating'].add(96)print(movie_ratings)

我们可以看到我们的字典没有被修改。这是因为集合不允许重复。我将在这里停下来,但是我鼓励你研究一下 python 中其他一些重要的字典方法。例如,您可以使用。items()”方法迭代键/值对:

for key, value in movie_ratings.items():
    print(key, value)

您可以研究的其他重要方法有。keys()“,”。get()'、和'。setdefault()'。

结论

总之,在这篇文章中,我们讨论了如何在 python 中定义和操作字典。我们讨论了如何用列表和集合定义字典。我们还讨论了如何使用“defaultdict”来初始化这些字典类型,这使我们能够专注于数据插入。我希望这篇文章对你有用。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

Python 中的字典

原文:https://towardsdatascience.com/dictionaries-in-python-84b05bb95629?source=collection_archive---------8-----------------------

玛蒂尔德·德库尔塞尔Unsplash 上拍摄的照片

Python 中最重要的数据结构之一

在这篇文章中,我将谈论字典。这是“Python 中的数据结构”系列的第二篇文章。这个系列的第一部分是关于列表的。

字典是 Python 中使用进行索引的重要数据结构。它们是一个无序的项目序列(键值对),这意味着顺序没有被保留。密钥是不可变的。就像列表一样,字典的值可以保存异构数据即整数、浮点、字符串、NaN、布尔、列表、数组,甚至嵌套字典。

这篇文章将为您提供清晰的理解,并使您能够熟练地使用 Python 字典

本文涵盖了以下主题:

  • 创建字典并添加元素
  • 访问字典元素
  • 删除字典元素
  • 添加/插入新元素
  • 合并/连接字典
  • 修改字典
  • 整理字典
  • 字典理解
  • 创建词典的替代方法
  • 抄字典
  • 重命名现有密钥
  • 嵌套字典
  • 检查字典中是否存在关键字

1)创建字典并添加元素

像列表用方括号([ ])初始化,字典用花括号({ })初始化。当然,空字典的长度为零。

dic_a = {} # An empty dictionarytype(dic_a)
>>> dictlen(dic_a)
>>> 0

一个字典有两个典型特征:每个键都有相应的值。键和值都可以是字符串、浮点、整数、NaN 等类型。向字典中添加元素意味着添加一个键值对。字典由一个或多个键值对组成。

让我们给我们的空字典添加一些元素。下面是这样做的一种方法。这里,'A'是键,'Apple'是它的值。您可以添加任意多的元素。

# Adding the first element
dic_a['A'] = 'Apple'print (dic_a)
>>> {'A': 'Apple'}# Adding the second element
dic_a['B'] = 'Ball'print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball'}

注意: Python 区分大小写,'A''a'充当两个不同的键。

dic_a['a'] = 'apple'print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'a': 'apple'}

一次初始化字典

如果您发现上述逐个添加元素的方法令人厌烦,您还可以通过指定所有的键-值对来立即初始化字典。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

异质词典

到目前为止,您的字典将字符串作为键和值。字典也可以存储混合类型的数据。以下是有效的 Python 字典。

dic_b = {1: 'Ace', 'B': 123, np.nan: 99.9, 'D': np.nan, 'E': np.inf}

不过,您应该为关键字使用有意义的名称,因为它们表示字典的索引。特别要避免使用 floats 和 np.nan 作为键。

2)访问字典元素

创建了字典之后,让我们看看如何访问它们的元素。

访问键和值

您可以分别使用功能dict.keys()dict.values()访问键和值。您还可以使用items()函数以元组的形式访问键和值。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a.keys()
>>> dict_keys(['A', 'B', 'C'])dic_a.values()
>>> dict_values(['Apple', 'Ball', 'Cat'])dic_a.items()
>>> dict_items([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])

或者,您也可以使用“for”循环一次访问/打印一个。

# Printing keys
for key in dic_a.keys():
    print (key, end=' ')
>>> A B C############################## Printing values
for key in dic_a.values():
    print (key, end=' ')
>>> Apple Ball Cat

您可以避免两个“for”循环,并使用items()访问键和值。“for”循环将遍历由items()返回的键值对。这里,keyvalue是任意的变量名。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}for key, value in dic_a.items():
    print (key, value)
>>> A Apple
    B Ball
    C Cat

访问单个元素

无法使用列表式索引来访问词典条目。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a[0]
>>> **---->** 1 dic_a[**0**] **KeyError**: 0

您需要使用键从字典中访问相应的值。

# Accessing the value "Apple"
dic_a['A']
>>> 'Apple'# Accessing the value "Cat"
dic_a['C']
>>> 'Cat'

如果字典中不存在该键,您将得到一个错误。

dic_a['Z']**>>> KeyError** Traceback (most recent call last)
**----> 1** dic_a**['Z']****KeyError**: 'Z'

如果您想在不存在键的情况下避免这样的键错误,您可以使用get()功能。当键不存在时,返回None。您也可以使用自定义消息来返回。

print (dic_a.get('Z'))
>>> None# Custom return message
print (dic_a.get('Z', 'Key does not exist'))
>>> Key does not exist

像在列表中一样访问字典元素

如果您想使用索引来访问字典元素(键或值),您需要首先将它们转换成列表。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}list(dic_a.keys())[0]
>>> 'A'list(dic_a.keys())[-1]
>>> 'C'list(dic_a.values())[0]
>>> 'Apple'list(dic_a.values())[-1]
>>> 'Cat'

3)删除字典元素

从字典中删除元素意味着一起删除一个键值对。

使用 del

您可以使用del关键字和您想要删除其值的键来删除字典元素。删除是原位的,这意味着您不需要在删除后重新分配字典的值。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}# Deleting the key-value pair of 'A': 'Apple'
del dic_a['A']print (dic_a)
>>> {'B': 'Ball', 'C': 'Cat'}# Deleting the key-value pair of 'C': 'Cat'
del dic_a['C']print (dic_a)
>>> {'B': 'Ball'}

使用 pop()

您还可以使用“pop()”函数来删除元素。它返回被弹出(删除)的值,字典被就地修改

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a.pop('A')
>>> 'Apple' print (dic_a)
# {'B': 'Ball', 'C': 'Cat'}

在上述两种方法中,如果要删除的键在字典中不存在,您将得到一个 KeyError。在“pop()”的情况下,如果键不存在,您可以指定要显示的错误消息。

key_to_delete = 'E'
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a.pop(key_to_delete, f'Key {key_to_delete} does not exist.')
>>> 'Key E does not exist.'

删除多个元素

没有直接的方法,但是你可以使用如下所示的“for”循环。

to_delete = ['A', 'C']
dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}for key in to_delete:
    del dic_a[key]

print (dic_a)
>>> {'B': 'Ball'}

4)添加/插入新元素

您可以一次添加一个元素到一个已经存在的字典中,如下所示。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a['D'] = 'Dog'print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}dic_a['E'] = 'Egg'print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

如果您添加的密钥已经存在,现有值将被覆盖

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a['A'] = 'Adam' # key 'A' already exists with the value 'Apple'print (dic_a)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}

使用更新( )

您还可以使用update()函数添加一个新的键-值对,方法是将该对作为参数传递。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_a.update({'D': 'Dog'})print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

update()函数还允许您同时向现有字典添加多个键值对。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = {'D':'Dog', 'E':'Egg'}dic_a.update(dic_b)print(dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog', 'E': 'Egg'}

5)合并/连接字典

从 Python 3.5 开始,可以使用解包操作符(**)合并两个或多个字典。

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}dic_merged = {**dic_a, **dic_b}print (dic_merged)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

如果您不想创建一个新的字典,而只想将dic_b添加到现有的dic_a中,您可以简单地更新第一个字典,如前面所示。

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}dic_a.update(dic_b)print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

串联时如何处理重复键?

Python 字典的一个特点是它们不能有重复的键,也就是说,一个键不能出现两次。那么,如果你把两个或更多有一个或多个公共关键字的字典连接起来,会发生什么呢?

答案是最后合并的字典中的键值对(按合并的顺序)会存活下来。在下面的例子中,键'A'存在于所有三个字典中,因此,最终的字典采用最后一个合并的字典的值(dic_c)。

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'A': 'Apricot'}
dic_c = {'A': 'Adam', 'E': 'Egg'}dic_merged = {**dic_a, **dic_b, **dic_c}print (dic_merged)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat', 'E': 'Egg'}

一句警告

我刚刚说过字典不能有重复的键。严格地说,您可以定义一个具有重复键的字典,但是,当您打印它时,将只打印最后一个重复键。如下所示,只返回唯一的键,对于重复的键(此处为“A”),只返回最后一个值。

dic_a = {'A': 'Apple', 'B': 'Ball', 'A': 'Apricot', 'A': 'Assault'}print (dic_a)
>>> {'A': 'Assault', 'B': 'Ball'}

Python 3.9+中更简单的方法

从 Python 3.9 开始,可以使用|操作符连接两个或更多的字典。

dic_a = {'A': 'Apple', 'B': 'Ball'}
dic_b = {'C': 'Cat', 'D': 'Dog'}dic_c = dic_a | dic_b
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}# Concatenating more than 2 dictionaries
dic_d = dic_a | dic_b | dic_c

6)修改字典

如果想把'A'的值从'Apple'改成'Apricot',可以用一个简单的赋值。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_a['A'] = 'Apricot'print (dic_a)
>>> {'A': 'Apricot', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

7)整理字典

字典中不维护顺序。使用sorted()功能,您可以使用关键字或值对字典进行排序。

使用关键字排序

如果键是字符串(字母),它们将按字母顺序排序。在字典中,我们有两个主要元素:键和值。因此,在根据键进行排序时,我们使用第一个元素,即键,因此,lambda 函数中使用的索引是“[0]”。你可以阅读这篇文章了解更多关于 lambda 函数的信息。

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}sorted(dic_a.items(), key=lambda x: x[0])
>>> [('A', 40), ('B', 100), ('C', 10), ('D', 90)]

分类不到位。如下所示,如果您现在打印字典,它仍然是无序的,和最初初始化时一样。排序后,您必须重新分配它。

# The dictionary remains unordered if you print it
print (dic_a)
>>> {'B': 100, 'C': 10, 'D': 90, 'A': 40}

如果您想以相反的顺序排序,请指定关键字reverse=True

sorted(dic_a.items(), key=lambda x: x[0], reverse=True)
>>> [('D', 90), ('C', 10), ('B', 100), ('A', 40)]

使用值排序

要根据字典的值对其进行排序,需要在 lambda 函数中使用索引“[1]”。

dic_a = {'B': 100, 'C': 10, 'D': 90, 'A': 40}sorted(dic_a.items(), key=lambda x: x[1])
>>> [('C', 10), ('A', 40), ('D', 90), ('B', 100)]

8)词典理解

这是动态创建字典的一个非常有用的方法。假设您想要创建一个字典,其中键是一个整数,值是它的平方。字典的理解应该是这样的。

dic_c = {i: i**2 for i in range(5)}print (dic_c)
>>> {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

如果你希望你的键是字符串,你可以使用“f-strings”。

dic_c = {f'{i}': i**2 for i in range(5)}print (dic_c)
>>> {'0': 0, '1': 1, '2': 4, '3': 9, '4': 16}

9)创建词典的替代方法

从列表创建字典

假设你有两个列表,你想用它们创建一个字典。最简单的方法是使用dict()构造函数。

names = ['Sam', 'Adam', 'Tom', 'Harry']
marks = [90, 85, 55, 70]dic_grades = dict(zip(names, marks))print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

您还可以将这两个列表压缩在一起,并使用前面所示的“字典理解”创建字典。

dic_grades = {k:v for k, v in zip(names, marks)}print (dic_grades)
>>> {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}

传递键值对

您还可以将逗号分隔的键值对列表传递给dict()构造,它将返回一个字典。

dic_a = dict([('A', 'Apple'), ('B', 'Ball'), ('C', 'Cat')])print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

如果您的键是字符串,您甚至可以使用更简单的初始化,只使用变量作为键。

dic_a = dict(A='Apple', B='Ball', C='Cat')print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

10)复制字典

我将用一个简单的例子来解释这一点。字典的复制机制涉及到更多的微妙之处,我建议读者参考这篇堆栈溢出帖子,了解详细的解释。

参考分配

当您简单地将一个现有字典(父字典)重新分配给一个新字典时,两者都指向同一个对象(“引用分配】)。

考虑下面的例子,您将dic_a重新分配给dic_b

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}dic_b = dic_a # Simple reassignment **(Reference assignment)**

现在,如果您修改dic_b(例如添加一个新元素),您会注意到这一变化也会反映在dic_a中。

dic_b['D'] = 'Dog'print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}

浅拷贝

使用copy()函数创建一个浅层副本。在浅层拷贝中,两个字典作为两个独立的对象,它们的内容仍然共享相同的引用。如果在新字典(浅层拷贝)中添加一个新的键值对,它将不会出现在父字典中。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()dic_b['D'] = 'Dog'# New, shallow copy, has the new key-value pair
print (dic_b)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}# The parent dictionary does not have the new key-value pair
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

现在,父字典(" dic_a ")中的内容是否会改变取决于值的类型。例如,在下面的例子中,内容是简单的不可变的字符串。因此,改变“dic_b”中给定键的值(本例中为'A')不会改变“dic_a”中键'A'的值。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}
dic_b = dic_a.copy()# Replace an existing key with a new value in the shallow copy
dic_b['A'] = 'Adam'print (dic_b)
>>> {'A': 'Adam', 'B': 'Ball', 'C': 'Cat'}# Strings are immutable so 'Apple' doesn't change to 'Adam' in dic_a
print (dic_a)
>>> {'A': 'Apple', 'B': 'Ball', 'C': 'Cat'}

但是,如果“dic_a”中关键字'A'的值是一个列表,那么在“dic_b”中改变它的值将反映“dic_a”(父字典)中的变化,因为列表是可变的。

dic_a = {'A': ['Apple'], 'B': 'Ball', 'C': 'Cat'}# Make a shallow copy
dic_b = dic_a.copy()dic_b['A'][0] = 'Adam'print (dic_b)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Coal'}# Lists are mutable so the changes get reflected in dic_a too
print (dic_a)
>>> {'A': ['Adam'], 'B': 'Ball', 'C': 'Cat'}

11)重命名现有密钥

假设您想将键“Adam”替换为“Alex”。您可以使用pop函数,因为它删除传递的键(这里是“亚当”)并返回删除的值(这里是 85)。所以你一箭双雕。使用返回的(已删除的)值将该值分配给新键(此处为“Alex”)。可能有更复杂的情况,其中键是一个元组。这种情况超出了本文的范围。

dic_a = {'Sam': 90, 'Adam': 85, 'Tom': 55, 'Harry': 70}dic_a['Alex'] = dic_a.pop('Adam')print (dic_a)
>>> {'Sam': 90, 'Tom': 55, 'Harry': 70, 'Alex': 85}

12)嵌套字典

嵌套字典在一个字典中有一个或多个字典。下面是具有两层嵌套的嵌套字典的最简单的例子。这里,外部字典(第 1 层)只有一个键值对。然而,该值现在是字典本身。

dic_a = {'A': {'B': 'Ball'}}dic_a['A']
>>> {'B': 'Ball'}type(dic_a['A'])
>>> dict

如果想要进一步访问内部字典(第 2 层)的键-值对,现在需要使用dic_a['A']作为字典。

dic_a['A']['B']
>>> 'Ball'

三层字典

让我们添加一个额外的嵌套字典层。现在,dic_a['A']本身就是一个嵌套字典,不像上面最简单的嵌套字典。

dic_a = {'A': {'B': {'C': 'Cat'}}}# Layer 1
dic_a['A']
>>> {'B': {'C': 'Cat'}}# Layer 2
dic_a['A']['B']
>>> {'C': 'Cat'}# Layer 3
dic_a['A']['B']['C']
>>> 'Cat'

13)检查字典中是否存在关键字

您可以使用in操作符来查找特定的键是否存在于字典中。

dic_a = {'A': 'Apple', 'B': 'Ball', 'C': 'Cat', 'D': 'Dog'}'A' in dic_a
# True'E' in dic_a
# False

在上面的代码中,不需要使用“in dic_a.keys()”,因为“in dic_a”已经在键中查找了。

这让我想到了这篇文章的结尾。您可以在这里访问本系列的第一部分“Python 中的数据结构”

字典作为 If-Else 的替代

原文:https://towardsdatascience.com/dictionary-as-an-alternative-to-if-else-76fe57a1e4af?source=collection_archive---------5-----------------------

使用字典创建一个更清晰的 If-Else 函数代码

动机

您可能经常使用 Python 的字典。但是,您是否已经释放了字典的全部容量来创建更高效的代码呢?如果你不知道如何创建一个有序的字典,将多个字典组合成一个映射,创建一个只读字典,你可以在这里找到更多的。

本文将关注如何使用 Python 的字典作为 if-else 语句的替代。

图片来自 PixabayGerd Altmann

削减 If-Else 语句,增加默认值

假设我们有杂货店商品的价格表:

price_list = {
'fish': 8,
'beef': 7,
'broccoli': 3,
}

我们想打印商品的价格,但是预期不是每个商品都在price_list.中,所以我们决定创建一个函数:

def find_price(item):
    if item in price_list:
        return 'The price for {} is {}'.format(item, price_list[item])
    else:
        return 'The price for {} is not available'.format(item)>>> find_price('fish')
'The price for fish is 8'>>> find_price('cauliflower')
'The price for cauliflower is not available'

聪明。if-else 语句做了我们希望它做的事情:当项目不可用时返回另一个值。但是我们查询字典两次,并使用两条语句返回几乎相同的内容。我们能做得更好吗?如果条目不在列表中,有没有办法返回一个默认值?幸运的是,Python 的字典方法有一种方法可以做到这一点,叫做get()

def find_price(item):
    return 'The price for {} is {}'.format(item, price_list.get(
        item, 'not available'))

.get()查找一个键,用不存在的键返回默认值。代码看起来确实更短了,但是它的表现是否像我们想要的那样呢?

>>> find_price('fish')
'The price for fish is 8'>>> find_price('cauliflower')
'The price for cauliflower is not available'

整洁!

但是我可以用 Dict 来实现不涉及字典的功能吗?

好问题。让我们来处理一个完全不涉及字典的例子。

假设您想创建一个函数,返回两个数之间的运算值。这就是你得出的结论:

def operations(operator, x, y):
    if operator == 'add':
        return x + y
    elif operator == 'sub':
        return x - y
    elif operator == 'mul':
        return x * y
    elif operator == 'div':
        return x / y>>> operations('mul', 2, 8)
16

你可以利用字典和get()方法得到一个更优雅的代码:

def operations(operator, x, y):
    return {
        'add': lambda: x+y,
        'sub': lambda: x-y,
        'mul': lambda: x*y,
        'div': lambda: x/y,
    }.get(operator, lambda: 'Not a valid operation')()

操作符的名字变成了键,lambda有效地将函数压缩成字典的值。get()找不到键时返回默认值。让我们检查一下我们的功能:

>>> operations('mul', 2, 8)
16
>>> operations('unknown', 2, 8)
'Not a valid operation'

太好了!它像我们想要的那样工作。

那么我应该把 If-Else 语句切换到字典吗?

替代方案使代码看起来更干净。但是如果我们想使用字典,我们应该考虑这个开关的缺点。每次我们调用operations(),它都会创建一个临时字典和 lambdas,让操作码循环。这将降低代码的性能。

因此,如果我们想使用字典,我们应该考虑在函数调用之前创建一次字典。这个行为防止代码在我们每次查找时重新创建字典。这种技术当然不会在所有情况下都适用,但是在您的工具箱中有另一种技术可供选择将是有益的。

this Github repo 中,您可以随意使用本文的代码。

我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedInTwitter 与我联系。

如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:

[## 高效 Python 代码的计时

如何比较列表、集合和其他方法的性能

towardsdatascience.com](/timing-the-performance-to-choose-the-right-python-object-for-your-data-science-project-670db6f11b8e) [## 使用 Python 最大化您的生产力

你创建了一个待办事项清单来提高效率,但最终却把时间浪费在了不重要的任务上。如果你能创造…

towardsdatascience.com](/maximize-your-productivity-with-python-6110004b45f7) [## 如何使用 Lambda 获得高效的 Python 代码

lambda 对元组进行排序并为您创造性的数据科学想法创建复杂函数的技巧

towardsdatascience.com](/how-to-use-lambda-for-efficient-python-code-ff950dc8d259) [## cy thon——Python 函数的加速工具

当调整你的算法得到小的改进时,你可能想用 Cython 获得额外的速度,一个…

towardsdatascience.com](/cython-a-speed-up-tool-for-your-python-function-9bab64364bfd) [## 通过专门的字典实现提高您的效率

创建包含有序和只读条目的字典,为不存在的键返回默认值,等等

medium.com](https://medium.com/better-programming/boost-your-efficiency-with-specialized-dictionary-implementations-7799ec97d14f)

参考

巴德丹。“Python 的把戏。”2017

Python 中的词典理解

原文:https://towardsdatascience.com/dictionary-comprehensions-in-python-912a453da512?source=collection_archive---------21-----------------------

如何使用字典理解在 Python 中创建字典

天一马Unsplash 上拍照

创建字典

假设我们想用 python 从另一个可迭代对象或序列(比如一个列表)创建一个字典。例如,我们有一个数字列表,我们想创建一个字典来计算每个元素在列表中出现的次数。因此,我们可以让字典的键成为列表中的不同元素(或数字),它们对应的值等于特定元素(或数字)在列表中出现的次数。

我们可以使用 for 循环来创建这个字典,如下所示:

num_list = [1,1,7,3,5,3,2,9,5,1,3,2,2,2,2,2,9]count_dict = {}for num in num_list:
    count_dict[num] = num_list.count(num)print(count_dict)
# {1: 3, 7: 1, 3: 3, 5: 2, 2: 6, 9: 2}

注意,我们必须先创建一个空字典, count_dict 。然后,当我们使用 for 循环遍历 num_list 时,我们在 count_dict 中创建了 key:value 对。在遍历 num_list 时,该键将等于我们当前所在的数字,其对应的值等于 num_list 中该数字的计数。

count()是一个列表方法,它返回我们传入的值在列表中出现的次数。

词典释义

就像我们在 python 中有列表理解一样,我们也有字典理解。我们可以回忆一下,列表理解允许我们以一种非常简洁的方式从其他序列中创建列表。

这里有一个完整的列表理解教程

[## Python 中的列表理解

用 python 创建列表的更优雅、更简洁的方式

towardsdatascience.com](/list-comprehensions-in-python-28d54c9286ca)

嗯,我们也可以使用 python 中的字典理解来以非常简洁的方式创建字典。

例如,我们可以使用字典理解来创建上面的字典,如下所示:

num_list = [1,1,7,3,5,3,2,9,5,1,3,2,2,2,2,2,9]count_dict = {num:num_list.count(num) for num in num_list}print(count_dict)
# {1: 3, 7: 1, 3: 3, 5: 2, 2: 6, 9: 2}

就是这样!首先,我们使用花括号来确定我们想要创建一个字典(与使用括号的列表相反)。然后,当我们在 num_list 上循环时,我们创建如下的键:值对:num:num _ list . count(num),其中键是数字(或 num),它的值是那个数字在 num_list 中的计数。

有条件的词典理解

就像在 list comprehensions 中一样,我们可以在 for 循环后使用 if 语句将条件添加到字典理解中。

{ key:key 的值,iterable 或 sequence if 中的值}

例如,如果我们只想在字典中查找偶数,我们可以使用下面的代码:

num_list = [1,1,7,3,5,3,2,9,5,1,3,2,2,2,2,2,9]count_dict = {num:num_list.count(num) for num in num_list if num_list.count(num)%2==0}print(count_dict)
# {5: 2, 2: 6, 9: 2}

正如我们所看到的,我们在字典理解中的 for 循环之后添加了一个 if 语句。如果当前数字的计数是偶数,我们将相应的键:值对添加到字典中。否则,我们不会将键:值对添加到字典中。

[## 成为更好的 Python 程序员的三个概念

了解 Python 中的和**运算符,args 和**kwargs 以及更多内容

towardsdatascience.com](/three-concepts-to-become-a-better-python-programmer-b5808b7abedc)

额外收获——对字典进行排序

我们还可以使用 sorted()函数根据计数对字典进行排序,如下所示:

num_list = [1,1,7,3,5,3,2,9,5,1,3,2,2,2,2,2,9]count_dict = {num:num_list.count(num) for num in num_list}sorted_dict = dict(sorted(count_dict.items(), key=lambda x:x[1]))print(sorted_dict)
# {7: 1, 5: 2, 9: 2, 1: 3, 3: 3, 2: 6}

有关上述代码的完整解释,请查看 python 中的对象排序教程:

[## Python 排序的终极指南

如何在 python 中对列表、元组、字符串和字典进行排序

levelup.gitconnected.com](https://levelup.gitconnected.com/the-ultimate-guide-to-sorting-in-python-d07349fb96d5)

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。

[## 通过我的推荐链接加入媒体——卢艾·马塔尔卡

阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…

lmatalka90.medium.com](https://lmatalka90.medium.com/membership)

结论

在这个简短的教程中,我们首先回顾了如何使用一个 iterable 对象(比如一个 list)创建一个带有 for 循环的字典,其中我们必须先创建一个空字典。然后,我们学习了如何使用字典理解来以更简洁和 pythonic 化的方式创建同一个字典,而不必先创建一个空字典。然后,我们看到了如何使用 if 语句将条件添加到字典理解中。最后,我们使用 sorted()函数根据列表中数字的计数值对字典进行了排序。

Python 字典详解:Python 编程

原文:https://towardsdatascience.com/dictionary-in-details-python-programming-7048cf999966?source=collection_archive---------36-----------------------

举例学习在编程问题求解中使用字典

来源:费尔南多·埃尔南德斯

Dictionary 是 Python 编程语言中的一种复合数据类型。在某种程度上,它类似于列表。列表是元素的集合。字典是键、值对的集合。借助字典,许多编程问题的解决方案可以变得更简单、更简洁。在这里我要解释一下字典里所有重要的方法和一些例题的解题。

让我们定义一个简单的字典。

d = {'a': 1, 'b':5, 'c': 3, 'd': 2, 'e': 8, 'f': 6}
  1. 在此字典中添加新元素:
d['s'] = 12
print(d)

字典 d 现在是这样的:

{'a': 1, 'b': 5, 'c': 3, 'd': 2, 'f': 6, 's': 12}

2.从字典 d 中删除元素 e。

del d['e']

3.获取键 a 的值。

d['a']
#Output: 1

4.“a”的值看起来太小了。将元素 a 的值更新为 10。

d['a'] = 10

5.将元素 s 的值加 3。

d['s'] += d['s'] + 3

6.检查字典 d 的长度。如果它小于 9,则向它添加三个以上的元素。

if len(d) < 8:
    d.update({'t': 21, 'h': 9, 'p':14})
print(d)
'''
Output:
{'a': 10, 'b': 5, 'c': 3, 'd': 2, 'f': 6, 's': 12, 't': 21, 'h': 9, 'p': 14}
'''

7.列出所有钥匙的清单。

d.keys()##Output looks like this:dict_keys([‘a’, ‘b’, ‘c’, ‘d’, ‘f’, ‘s’, ‘t’, ‘h’, ‘p’])

8.列出所有的值。

d.values()
##Output looks like this:
dict_values([10, 5, 3, 2, 6, 27, 21, 9, 14])

9.找出哪个字母的价值最大。

max = 0
max_key = 'a'
for k, v in d.items():
    if v > max:
        max_key = k
        max = d[max_key]
print(max_key)

答案出来是‘t’。

10.按升序对字典 d 的键进行排序。

sorted(d, key=lambda x: d[x])
#Output:
['d', 'c', 'b', 'f', 'h', 'p', 't', 's']

11.找出每个单词在下列句子中出现的次数。

sentences = "I love my country. My country is the best in the world. We have the best athletes in the world."

让我们制作一本字典,其中的关键字是这些句子中的单词,值是这些单词出现的频率。

以下是解决这个问题的步骤:

a.初始化一个空字典' sen_map '

b.把句子都变成小写

c.重复句子中的每个单词

d.检查 sen_map 中是否存在一个单词

e.如果不是,则添加值为 1 的单词

f.否则,将该单词的值更新 1

sen_map = {}  
sentences = sentences.lower() 
for i in sentences.split(): 
    if i not in sen_map:   
        sen_map[i] = 1  
    sen_map[i] += 1  
sen_map'''Output
{'i': 2,  'love': 2,  'my': 3,  'country.': 2,  'country': 2,  'is': 2,  'the': 5,  'best': 3,  'in': 3,  'world.': 3,  'we': 2,  'have': 2,  'athletes': 2}'''

字典可以像列表一样嵌套。这里有一个例子:

Gold_medals = {'USA': {'Wrestling': 3, 'Long Jump': 3, 'Basketball': 5},
              'China': {'Wrestling': 1, 'Long Jump': 5, 'Basketball': 3},
              'England': {'Wrestling': 2, 'Long Jump': 7, 'Basketball': 0}}

12.美国在跳远比赛中获得了多少枚金牌?

Gold_medals['USA']['Long Jump']

正如我们在上面的字典中看到的,输出是 3。

字典中组织信息的另一种方式是列表。

这里有一个例子:

students = [{'Name': 'John Smith', 'Age': 12, 'Score': 90},
           {'Name': 'Laila Jones', 'Age': 11, 'Score': 82},
           {'Name': 'Omar Martinez', 'Age': 10, 'Score': 70},
           {'Name': 'Romana Raha', 'Age': 13, 'Score': 78},]

13.返回班上得分最高的学生的姓名。

让我们一步一步解决这个问题。首先按降序对“学生”进行排序。

sorted(students, key=lambda x: x['Score'], reverse=True)
#Output:
'''[{'Name': 'John Smith', 'Age': 12, 'Score': 90},  {'Name': 'Laila Jones', 'Age': 11, 'Score': 82},  {'Name': 'Romana Raha', 'Age': 13, 'Score': 78},  {'Name': 'Omar Martinez', 'Age': 10, 'Score': 70}]'''

获取列表“学生”中的第一个词典。

sorted(students, key=lambda x: x['Score'], reverse=True)[0]'''Output
{'Name': 'John Smith', 'Age': 12, 'Score': 90}'''

最后,得到学生的名字。

sorted(students, key=lambda x: x['Score'], reverse=True)[0]['Name']

这一行代码将返回学生的姓名。也就是‘约翰·史密斯’。

我试图先解释字典的方法,然后给出一些例子来说明如何使用字典。我希望这有所帮助。

下面是字典的视频教程:

Python 3.9 中的字典联合运算符和新的字符串方法

原文:https://towardsdatascience.com/dictionary-union-operators-and-new-string-methods-in-python-3-9-4688b261a417?source=collection_archive---------49-----------------------

可以简化您的数据处理代码的新功能—它们是什么?它们改进了什么?

卡南·哈斯马多夫在 Unsplash 上的照片

Python 3.9 已经积累了一份长长的改进列表,其中有一些非常重要的变化,比如一种新型的解析器。与 LL(1)解析器相比,新的 PEG 解析器占用了更多的内存,但是速度更快,应该能够更好地处理某些情况。虽然,根据发布文档,我们可能从 Python 3.10 开始才能看到真正的效果。Python 3.9 还扩展了一些内置和标准库集合类型的使用,作为通用类型,而不必求助于typing模块。如果您使用类型检查器、linters 或 ide,这尤其有用。

也许,在处理文本数据的日常工作中最方便的两个特性是添加到dict中的联合操作符和新的string方法。

dict的联合运算符

dict引入了合并 |更新 |=两个联合运算符。

因此,如果您想基于您已经拥有的两个字典创建一个新的dict,您可以执行以下操作:

>>> d1 = {'a': 1}
>>> d2 = {'b': 2}
>>> d3 = d1 | d2
>>> d3
{'a': 1, 'b': 2}

如果您想用这两个字典中的一个来扩充另一个,您可以这样做:

>>> d1 = {'a': 1}
>>> d2 = {'b': 2}
>>> d1 |= d2
>>> d1
{'a': 1, 'b': 2}

在 Python 中已经有几种合并字典的方法。这可以使用[dict.update()](https://docs.python.org/3/library/stdtypes.html#dict.update)来完成:

>>> d1 = {'a': 1}
>>> d2 = {'b': 2}
>>> d1.update(d2)
>>> d1
{'a': 1, 'b': 2}

然而,dict.update()修改了您正在就地更新的dict。如果你想有一个新的字典,你必须先复制一个现有的字典:

>>> d1 = {'a': 1}
>>> d2 = {'b': 2}
>>> d3 = d1.copy()
>>> d3
{'a': 1}
>>> d3.update(d2)
>>> d3
{'a': 1, 'b': 2}

除此之外,在 Python 3.9 之前,还有 3 种方法可以实现这一点。合并两个字典也可以通过[{**d1, **d2}](https://www.python.org/dev/peps/pep-0448/)构造来实现:

>>> d1 = {'a': 1}
>>> d2  = {'b': 2}
>>> {**d1, **d2}
{'a': 1, 'b': 2}

但是,如果您使用的是dict的子类,例如defaultdict,这个构造将把新字典的类型解析为dict:

>>> d1
defaultdict(None, {0: 'a'})
>>> d2
defaultdict(None, {1: 'b'})
>>> {**d1, **d2}
{0: 'a', 1: 'b'}

工会运营商不会出现这种情况:

>>> d1
defaultdict(None, {0: 'a'})
>>> d2
defaultdict(None, {1: 'b'})
>>> d1 | d2
defaultdict(None, {0: 'a', 1: 'b'})

另一种将两个字典合并成一个新字典的方法是 dict(d1, **d2):

>>> d1 = {'a': 1}
>>> d2  = {'b': 2}
>>> dict(d1, **d2)
{'a': 1, 'b': 2}

然而,这只适用于所有键都是类型string的字典:

>>> d1 = {'a': 1}
>>> d2 = {2: 'b'}
>>> dict(d1, **d2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

第三种选择是使用 [collections.ChainMap](https://www.python.org/dev/peps/pep-0584/#id15)。这可能比前两种方法更不直接,而且不幸的是,如果您更新链图对象,会修改底层字典:

>>> d1 = {'a': 1, 'b': 2}
>>> d2 = {'b': 3}
>>> from collections import ChainMap
>>> d3 = ChainMap(d1, d2)
>>> d3
ChainMap({'a': 1, 'b': 2}, {'b': 3})
>>> d3['b'] = 4
>>> d3
ChainMap({'a': 1, 'b': 4}, {'b': 3})
>>> d1
{'a': 1, 'b': 4}
>>> d2
{'b': 3}

用于dict的新的合并和更新 union 操作符解决了这些问题,看起来没有那么麻烦。但是,它们可能会与按位“或”和其他一些运算符混淆。

移除前缀和后缀的字符串方法

正如 PEP-616 文档所建议的,几乎任何时候你使用str.startswith()str.endswith()方法,你都可以简化代码如下。

以前,如果您正在规范化一个列表,例如,电影标题,并从无序列表中删除任何额外的标记,如破折号,您可以这样做:

>>> normalized_titles = []
>>> my_prefixed_string = '- Shark Bait'
>>> if my_prefixed_string.startswith('- '):
...     normalized_titles.append(my_prefixed_string.replace('- ', ''))
>>> print(normalized_titles)
['Shark Bait']

然而,使用str.replace()可能会产生不必要的副作用,例如,如果破折号是电影标题的一部分,而您实际上更愿意保留它,如下例所示。在 Python 3.9 中,您可以安全地这样做:

>>> normalized_titles = []
>>> my_prefixed_string = '- Niko - The Flight Before Christmas'
>>> if my_prefixed_string.startswith('- '):
...     normalized_titles.append(my_prefixed_string.removeprefix('- '))
>>> print(normalized_titles)
['Niko - The Flight Before Christmas']

PEP 文档指出,用户报告期望来自另一对内置方法[str.lstrip](https://docs.python.org/3/library/stdtypes.html#str.lstrip)[str.rstrip](https://docs.python.org/3/library/stdtypes.html#str.rstrip)的这种行为,并且通常必须实现他们自己的方法来实现这种行为。这些新方法应该以更健壮和有效的方式提供该功能。用户将不必关心空字符串的情况或使用str.replace()

另一个典型的用例如下:

>>> my_affixed_string = '"Some title"'
>>> if my_affixed_string.startswith('"'):
...     my_affixed_string = my_affixed_string[1:]
...
>>> if my_affixed_string.endswith('"'):
...     my_affixed_string = my_affixed_string[:-1]
...
>>> my_affixed_string
'Some title'

在 Python 3.9 中,这可以简化为:

>>> my_affixed_string = '"Some title"'
>>> my_affixed_string.removeprefix('"').removesuffix('"')
'Some title'

bytesbytearraycollections.UserString也宣布了这些方法。

Python 3.9 还有一系列有趣的模块改进和优化——一定要看看这些改进和优化,找出对您的应用程序最有用的。享受新功能!

如果您对 Python 感兴趣,请随意查看我关于并发性和并行性的文章。

30 天的自行车旅行让我更健康了吗?

原文:https://towardsdatascience.com/did-a-30-day-bicycle-tour-make-me-more-fit-639ae92cfd27?source=collection_archive---------60-----------------------

如果是的话,这能被测量吗?

TLDR:是的,它让我更健康。向下滚动以查看动画和导出的方程式。

上个月,我完成了奥特罗阿之旅——一次穿越新西兰的 3000 公里自行车打包之旅。完成的截止日期是 30 天。我从 2 月 21 日开始,到 3 月 23 日结束——29 天 23 小时 45 分钟后。于是我结束了 30 天的旅行,只剩下 15 分钟。

我知道这次旅行会是一次挑战。虽然我总体上很健康,也进行了一些自行车打包旅行,包括在 GDMBT 上的 2 周,但在过去几年里我的体重增加了一些,没有时间进行必要的训练(建议是 3000 公里,我可能完成了 200 公里?).

当我在头几天挣扎的时候,来自巡回赛老兵的建议通常是“别担心。你会变得更健康。训练成这样!”从主观上来说,随着巡演的进行,事情似乎变得稍微容易了一些。然而,实际上骑行 3000 km 后变得更健美了吗?我的身体在巡演结束时比开始时更好吗?如果是的话,我能测量吗?

重要统计数据

  • 32 岁
  • 生来就是男性
  • 身高 182 cm
  • 中度但可控的哮喘
  • 约 55 静息心率(午夜)
  • 骑着 2017 款铝制 Salsa Timberjack,配有 29er 车轮,13.5 kg。当然,我有很多装备,所以我们假设总共 25 公斤?

数据收集

在游览期间,我戴了一只佳明菲尼克斯 5 手表。这记录了我每隔几秒钟的距离、高度和心率。每天结束时,收集的数据会被同步到 Garmin 的服务器上。后来,我能够以 XML 格式下载这些数据。在这 30 天里,我总共检索了 187,216 个数据点。

数据概述

下面是基本的距离和高程数据。有一些差异需要注意。在第四天,我的变速器坏了,我被困在等待一个新的。然后,大约从第 20 天开始,我生病了。四天后,在第 24 天的另一个休息日达到了高潮。

适合度的度量

我的中学体育老师对健康有一个很好的定义。用他的话来说,想象两个同样身高体重的跑步者在 5k 比赛。在整个比赛中,没有一个运动员领先于另一个。然后他们在同一时间结束比赛。一名跑步者的平均心率为 150,另一名为 155。谁是更健康的跑步者?

答案当然是心率较低的跑步者。如果你的心脏在同样的输出量下工作得更少,你就更健康。骑自行车也是如此。事实上,如果我变得更健康,我会预计我的心率会在旅行过程中下降。那是真的吗?

看看我在旅游过程中的平均日心率,它确实似乎在下降。但是我的心率下降是因为我变得更健康了还是因为我变得更懒了?要回答这个问题,我需要将我的心率与我的努力程度进行比较。

清理数据

Garmin XML 文件提供了四个数据点:时间、距离、海拔和心率。这些是某个时间点的快照。为了让这些数据有用,我需要速度和坡度(比如上坡/下坡)。为此,我计算了时间间隔。例如,速度是距离随时间的变化。

处理心率时,还有一个额外的重要考虑因素。人类的心脏实际上需要时间来适应身体当前的努力水平。因此,计算出的时间间隔必须足够长,以捕捉这种调整。不过,不会太久的。间隔太长会有模糊数据的风险。通过实验,我发现 90 秒的间隔效果最好(或者至少给了我最好的 r !).

我做过的其他事情:

  • 删除了所有速度为 30 公里/小时的区间<5km/hr or >(要么是我站着不动,要么是 GPS 变得古怪)
  • 放弃所有大于 1 米/秒的爬升。同样,任何超过 1 米/秒的速度都远远超过了我的身体极限,这是因为古怪的 GPS 读数
  • 取消所有下降间隔。重力——而不是肌肉——是下降的主要力量。它们对这个分析没有用。

天真的第一遍

作为第一步,当我比较心率和速度时,我得到一个向下倾斜的趋势。这是为什么呢?为什么我走得越快心率越慢?

事实证明,当我慢下来的时候,很有可能我实际上是在爬山,并且更加努力地工作。事实上,速度与成绩的关系图揭示了两者之间的密切关系。

在热图上包括心率表明心率在分布的顶端附近最高。这是我快速上坡的时候。这是有道理的。

恒定输出模型

那么,在旅行的过程中,我变得更健康了吗?对于相同的输出,我的心跳变慢了吗?如果我将速度和坡度都保持在 8-12 公里/小时和 3-5%的坡度,我会发现一个明显的下降趋势。根据下面的动画估计,我的心跳在第 30 天比第一天慢了 20 bpm!

调整和多变量模型

动画很好,但我能做得更好吗?我可以使用我所有的数据,而不仅仅是 8-12 公里/小时,3-5%的坡度范围吗?我能得到精确的数字吗?我当然可以。我可以用一个多元模型

*heartrate = a*speed + b*grade + c*day*

通过 sklearn 的 线性回归 实现,对于所有 89238 个区间,我得到 r 为 0.27 。使用计算出的系数可得出以下等式…

*heartrate = .77*speed + 344.49*grade - 0.50*day + 116.61*

我还能调整什么?您可能会从动画中注意到,前三天似乎是数据中的异常值。造成这种情况的一个因素可能是我在头三天换了不同的轮胎。当我在第四天修好我的换档器时,我也认为我所用的轮胎,enduro 轮胎,太重了,所以我更换了它们。移除前三天的数据后,r 在处大致相同。26 新的等式如下

*heartrate = .83*speed + 365.81*grade - 0.19*day + 116.61*

查看“天”的系数,我看到-0.19。在旅程中的每一天,同样强度的运动,我的心率会下降 0.2 次/分钟。这意味着总共提高了 6 BPM!这不像动画中暗示的那样戏剧化(~20BPM),但更精确,仍然是一个好结果!

开放数据

我的代码和原始数据可以在 Github 上获得。感谢阅读!

原载于*。***

脸书预言家让时间序列建模太容易了吗?

原文:https://towardsdatascience.com/did-facebook-prophet-make-time-series-modeling-too-easy-7d22a2bbf5dd?source=collection_archive---------47-----------------------

顶点工程的困境

图片由 kues1Adobe Stock

我最近选择库存需求预测作为我完成的一个为期 10 个月的数据科学项目的顶点项目(在熨斗学校)。我很高兴探索这个话题,因为我觉得它可以应用到我职业兴趣的几个不同领域。然而,当我开始应用脸书先知的项目时,我不禁觉得这太简单了。

我不禁想知道我是否做错了什么,选择了一个基本上为我做了工作的包。

我是不是作弊了?这个项目会让我更像一个分析师而不是数据科学家吗?

幸运的是,当我的导师向我保证数据科学家的主要目的是解决商业问题时,我的恐惧被平息了,我成功地做到了。我们使用不同的机器学习工具来解决这些问题。因此,如果一个工具碰巧有效地解决了这个问题,这是一件好事——它不必很复杂。我实际上节省了时间(在工作中,这相当于时间和金钱)。干得好,

经过进一步的研究,我发现脸书设计了这个软件包,以便分析师可以进行时间序列预测。除了数据科学家。时间序列建模可能是行业中一个特定的专业领域,因此并不是每个数据科学家都熟悉它。

脸书使数据科学家和分析师能够像有经验的时间序列建模者一样,高效、大规模地制作时间序列模型。我在这里确认(不是说他们需要我的确认)他们确实成功了。

他们称这是他们的大规模预测分析方法,他们说这种方法充分利用了人工和自动化任务。

摘自脸书先知的白皮书

我的没有超参数调整的基线模型能够准确预测测试数据,只有 8.65%的 MAPE(平均绝对百分比误差)。然后,当通过按照 Prophet 文档的指示执行网格搜索来调整两个超参数(变点和季节性先验尺度)时,我能够将模型改进为仅 6.47%的 MAPE。这在产品(库存)需求预测中被认为是极好的。

作者图片

他们的文档是我见过的最好的文档之一;同时为 R 和 Python 提供了一个组织良好的关于大多数模型使用的教程。为了更深入的探究,他们甚至包括了到他们白皮书的链接,该白皮书解释了模型背后的计算,并展示了更多潜在的使用方法。

总的来说,脸书似乎已经成功地创建了一个开源的时间序列建模包。这使得新的和经验丰富的数据科学家以及用 Python 和/或 R 编写代码的分析师现在可以有效地大规模建模和预测时间序列数据。

高效地解决业务问题总是一件好事。

你试过脸书先知的时间序列建模包吗?你觉得怎么样?我期待在评论中听到你的回应。

感谢您的阅读!我希望你喜欢。

参考资料:

我捅了马蜂窝了吗?

原文:https://towardsdatascience.com/did-i-kick-the-hornets-nest-7d26860a2638?source=collection_archive---------27-----------------------

回应对我的《厌恶模糊》一文的评论

继我的文章 “什么是暧昧厌恶?” 发表后,我收到了几个脾气暴躁的人顽强地为自己的选择行为辩护的评论。暗示数学人可以表现得不理性,类似于踢马蜂窝。

我欠这些评论一个回应,所以让我在这里谈谈主要的味道:

  • 原装* 安全摄像头?

点评:不能说不理智就此罢休!
回应:我绝对同意。我不会就此罢休的。请放心,这只是一个很长的系列文章中的一篇。被关在家里让我幻想了 5 年的实验/行为经济学和神经经济学(那些茂盛的研究生院草坪!)并激励我重新拾起那些话题。这篇文章并不是要把所有的 5 年都塞进一篇文章里——后面还有很多呢!

评论:称我的选择为“非理性”可不太好。我被冒犯了。
回应:“非理性”这个词在这里并不是一种诋毁……理性在经济学中有一个正式的定义,A & D 选择偏离了它。事实上,知道“非理性”和“次优”不是一回事,你会松一口气——非理性有时比理性更好。如果这个想法让你热血沸腾,你可能会爱上实验/行为经济学领域!这几乎是整个领域的内容,它为你提供了几十年的美味研究来咀嚼。此外,这篇文章仅仅展示了你失去理性的许多方式之一。还有好多好多!

点评:你忘记了风险厌恶和预期效用理论
回应:不,那些是不可能忘记的。但是你是对的,我应该在发表这篇文章之前写下他们。我的过失。别走开,我已经有一半的草稿了。当你在愤怒地搜索的同时,要小心避免混淆风险和模糊风险的新手错误。风险厌恶并不能解释模糊厌恶,但是在我润色这篇文章解释为什么会这样的时候,你必须对我有耐心。

点评:人们选择一个& D 来对冲他们的赌注。回应:没有,请再查一下游戏规则。一个早期的打字错误让它听起来像是你花 100 美元得到了两次机会(你只有一次机会!)但这在大部分投票结果出来之前就已经解决了。我们还是把 A & D 看做最喜欢的选择。(在允许两次出手的情况下,你很聪明地注意到了对冲机会,但对冲不足以解释结果。B & C 不受欢迎的问题仍然存在,因为 B & C 也提供了对冲机会。如果这种行为仅仅是为了对冲,为什么会有差异呢?)

评论:这是两人博弈,所以纳什均衡…回应:不,这是单人博弈。你不是在和我玩游戏,因为我不是想为自己赢得或保留这 100 美元,我是想在我想象的客厅里玩一个小游戏。但不用担心,我们可以在未来的话题上一起掀起一场博弈论风暴。

评论:你想骗我。回应:哎呀,我绝对不会。我很好。❤

评论:不,说真的,你在骗我。回应:你确实提出了一个非常相关的方法论观点。

在实验经济学中,实验者永远不要欺骗参与者,这一点非常重要。如果我们说桶里有三种颜色的高尔夫球,那就是桶里的东西。如果我们说这些球的颜色是在游戏开始时决定的,我们就不会在你身上做换手的戏法。如果你没有赢,我们也不会保留我们“存”的任何钱,所以我们不会和你玩。

我们不是试图诱导异常行为,而是试图揭示现有的行为。因此,欺骗任何人去做他们在野外类似情况下不会做的事情,这违背了我们的最大利益。(参与者事后可能会对他们的选择感到惊讶,这是另一回事。)

在实验室中,我们竭尽全力解释(口头和在同意书上)参与者看到的就是参与者得到的。他们选择什么取决于他们自己。回报是真实的,没有欺骗。因为实验经济学是我接受培训的传统,所以我也不想欺骗你。但我并不惊讶你认为我是。

我们的传统与那些信任被其他领域污染的参与者斗争,在这些领域,欺骗是乐趣的一部分。(我没有指责任何人,没有,根本没有,我不敢指责任何,咳咳,社会心理学实验室对参与者撒谎。)最后,实验经济学实验室最终会花费大量预算来修复这种破碎的信任(例如,进行几轮练习,如果参与者赢了,就把真钱放到他们手里)。

所以,很公平。

备注:【在此插入关于方差减少的备注。】
回应:诶,不是,没有分布就没有定义方差。你必须去贝叶斯继续这个思路,这让我…

评论:【此处插入贝叶斯评论。】
回应:贝叶斯哥们,你是不是在这里挣扎着攀越我们的语言障碍?将行为经济学家所谓的风险与不确定性映射到你的行话上:
客观风险涉及带有“客观”先验的概率计算。如果“客观”这个词激怒了你们当中那些在认识论上吹毛求疵的人,那就用“填鸭式”这个词来代替它。这些先验要么是由某人口述给你的(负责的决策者,给你布置家庭作业的教授,传统等等)。)或者也许他们是每个人都同意的先验知识(例如,公平对待硬币,没有争吵地前进)。
主观风险涉及你必须选择的先验概率计算。含糊不清包括感觉自己无力首先提出先验/假设。在特定情况下,区分模糊性和主观风险的警戒线因人而异。在这篇文章中,“经济学家”的角色在埃尔斯伯格的环境中从来不会面临模糊厌恶,因为他们被训练以一种跳过模糊直接面对主观风险的方式思考。这并不是因为他们对模糊厌恶免疫,而是因为他们不认为埃尔斯伯格的环境是模糊的。

经济学家被教导应用无差别原则。如果我们有幸有教授让我们练习,我们许多人会近乎本能地学会练习。感谢 雅各布·谭 为我写了一个 解释 什么的,这样我就不用说了。

也许你会发现,从决策者在被迫提供先验信息时感到的一种痛苦的角度来看待模糊厌恶现象是有用的。我不是在说挑选插入共轭先验的参数的苦恼,我是在说提出覆盖整个设置的假设。双主修贝叶斯统计和行为经济学的学生可能会通过预先启发的难度,从量化歧义子类的尝试中挤出一篇论文…

点评:只是一个思想实验;如果真的涉及到金钱,人们就不会选择 A & D。回应:伟大的本能,但不是,我的本科导师和最喜欢的教授是约翰·李斯特。各方面都很可爱,但在一个问题上是个暴君:不切实际的实验设置。这是有道理的,因为他是在经济学中率先使用现场实验的人。约翰的声音还活在我的脑海里,“如果是思想实验,不算,拿去实验室。但是你还没完!如果是实验室实验,不算,带去实地(真实世界)。始终在现实环境中研究有真实回报的选择行为。”多亏了像约翰这样的实地实验者,现代决策理论的大部分支柱——包括这一个——已经在现实世界中用真钱完全复制了。(在你问之前,是的,他们确实想到要改变奖励金额。)

现在是完全不同的东西…

感谢阅读!如果你在这里玩得开心,并且对人工智能感兴趣,这里有一个初学者友好的介绍供你娱乐:

在这里欣赏整个课程播放列表:bit.ly/machinefriend

与凯西·科兹尔科夫联系

让我们做朋友吧!你可以在 TwitterYouTubeLinkedIn 上找到我。有兴趣让我在你的活动上发言吗?用这个表格联系。

利物浦值得赢得英超冠军吗?

原文:https://towardsdatascience.com/did-liverpool-deserve-to-win-the-premier-league-eda7a4e1b9ca?source=collection_archive---------32-----------------------

使用 Python、R 和预期目标的英超赛季回顾

肖恩·唐纳利(@ 肖恩尼斯)在 Unsplash 上的照片

我转念一想,见日光之下,跑得快的不一定能赛跑,强壮的不一定能打仗,智慧的不一定能吃饭,聪明的不一定能发财,有才能的不一定能得宠。但是时间和机遇会降临到他们身上。

传道书 9:11

我期待圣诞礼物

如果一支足球队大规模超水平发挥,以绝对优势赢得联赛冠军,会发生什么?我们怎么知道他们是靠运气赢的,还是他们只是比其他人好得多?

体育分析行业在过去 10 年中开发的评估足球表现的指标之一是传说中的预期目标(xG)。这个想法是,进球只是可能发生的结果之一(射门击中横梁,守门员扑救,射门偏出,犯规,等等)。).现在我不想过多地讨论 xG 的定义(FbRef 有很好的解释和计算方法这里是,因为对于什么是 xG,或者如何计算 xG 有很多不同的观点。

为了简单起见,让我们把 xG 看作是一个球员射门(或射门机会)有多好的指标。有 1 比 1 机会对抗门将的球员?xG 高。一名中场球员在 40 码外射门越过人墙。低 xG。处罚?xG 高。在角旗附近拍摄的照片?低 xG。

几年来,职业足球俱乐部一直在使用 xG 来跟踪表现,而不是简单地通过查看最终结果来得出结论。它慢慢地走向媒体来帮助为辩论提供信息——尽管在与某些专家对话时可能会尽量避免。

现在我们已经建立了 xG,这个项目的目标(不是双关语)是确定球队在 2019/20 英超赛季的表现如何,相对于他们的最终位置——具体来说,利物浦是否应该成为赢家。为此我们需要一些东西:

  1. 本赛季英超联赛的 xG 数据
  2. 一个足球结果概率模型(主场胜利、平局、客场胜利)
  3. 花式图表

Scrapy

首先,让我们得到 xG 数据。FbRef 是一个很棒的开源网站,它们由 StatsBomb 的模型提供支持(该模型被认为是业界最好的模型之一)。在上面的链接中有两种获取 xG 数据的方法,第一种是下面的方法,它使用 Python 中的 Scrapy。在xg_spider.py中:

现在在终端中键入以下内容:

scrapy crawl expectedGoals --output exG.csv

让我们清理 R 中的数据(我知道我们可以用 Python 来做这些,但是我已经准备好了一个 Scrapy scraper,并且我的大部分模型已经在 R 中了):

install.packages("tidyr")prem1920<-read.csv("exG.csv")#get rid of empty rows
prem1920<-prem1920[rowSums(is.na(prem1920)) == 0,] #separate delimiter by hyphen into two columns
tidyr::separate(prem1920, score, into = c("homeScore", "awayScore")) 

Et voila,你现在有了一个带英超 2019/20 xG 数据的 csv!

或者……您可以轻松地使用 Microsoft Excel 复制并粘贴这些表格,然后手动将数据整理成 csv 格式。

足球模型

好吧,这就是开始有趣的地方。足球比分可以用泊松分布来近似。我不想赘述,因为网上有大量的文献,如果你想查看描述近似过程的 Python 或 R 代码,这是一个很好的网站

无论如何,我们可以使用内置泊松包中的 R 来计算一场足球比赛的正确得分,给定两队的得分率。打开一个名为FootballMatchResult.R的文件:

这个简单的泊松模型实际上并不像你希望的那样符合数据,因为低分和抽牌概率比模型更有可能,但是…出于时间的考虑,让我们改天再讨论这个问题。

使用这个模型,我们现在可以推导出一场比赛中球队的期望得分(xPts)。例如,在第一场比赛(利物浦对诺维奇城)中,利物浦的得分为 1.9,诺维奇城的得分为 1.0——现在假设一个球队赢了 3 分,平了 1 分,我们可以通过键入以下内容得出比赛结果概率

FootballMatchResult(1.9,1.0)

到 R 控制台,它会以向量的形式输出主场赢、平局和客场的概率:

[1] 0.5840664 0.2210540 0.1948797

因此,如果我们想计算利物浦的 xPts,我们将第一个条目乘以 3,然后加上中间的条目。诺里奇城也是如此,但在他们的例子中,将最后一个条目乘以 3,再加上中间的一个。

排行榜不会说谎

让我们将每场比赛从prem1920数据框计算到 xPts 表中,看看各队在比赛中根据 xG 表现如何。我们将总结三个属性,并将它们整理成一个排名表。这些属性是:期望得分(xPts)、期望进球得分(xG_For)和期望进球得分(xG_Against)。这是代码

随着 RStudio 的魔力,开火

xPts<-ExpectedPointsTable(prem1920)

进了候机楼。我们现在能够导出这个 xPts 表:

2019/20 英超 xPts 表

与实际的最终排名相比:

2019/20 英超决赛表

它回家了

好吧,我们从哪里开始?让我们首先回答这个问题,利物浦今年是否值得赢得联赛冠军。对于强大的红军来说,可悲的是,根据 xPts 表,答案是响亮的。根据 xPts,他们甚至没有排在第二,根据 xG,切尔西应该排在他们前面!

曼城今年的表现绝对让所有人大吃一惊,在对阵 93.1 xG 的比赛中打入 102 球,并让他们的失球数量几乎达到了预期(35 球对 35.2 球)。他们的 xPts 也相当准确,81.89,而实际得分是 81。领先第二名超过 10 分钟!佩普瓜迪奥拉可以认为他的球队今年非常不走运——但是嘿,他们推翻了欧洲冠军联赛的禁令,所以摇摆和回旋。

排名前四的球队是,嗯,排名前四的球队——除了曼联可能应该获得第四名而不是第三名。然而,xPts 的排名显示他们的防守在联赛中仅次于同城对手,所以也许大卫·德·赫亚和哈里·马奎尔并没有那么差

尽管莱斯特城在赛季的最后一天错过了一个冠军联赛席位,但 xPts 表表明他们度过了伟大的一年,并最终完成了他们应该完成的任务。不幸的是,热刺就不一样了。他们获得了第六名,但是 xPts 的排名表显示他们甚至不是一个顶级球队!何塞·穆里尼奥似乎有很多事情要做来改善他的球队(以及他自己的声誉)…

运气好不如运气好

现在我们讨论垫底的队伍。沃特福德肯定感到特别不公平——他们为了远离倒数三名已经做得够多了。根据 xPts 排名表,伯恩茅斯是另一支感觉今年应该保持领先的球队。

诺维奇城的表现可能也需要更多,尽管可能不足以在英超联赛中再生存一年。如果我是阿斯顿维拉、水晶宫或西汉姆招聘团队的教练或工作人员,我会回头看看,如果夏休期间没有任何变化,我会有点担心我的球队下赛季的前景。

然而,他们都不应该像纽卡斯尔联队那样担心。虽然他们非常符合他们的预期目标数字,但他们以最低的预期 xG 在 xPts 表中垫底。不知何故,史蒂文·布鲁斯的球队设法比他们应得的多得了 8 分,这足以将他们拉到安全地带。这正说明了保持领先和被降级之间的差距有多小!

书中还有最后一个技巧。我们可以基于 xPts 表使用 R 来模拟 1000 个季节(代码不包括在内,因为我发誓我家里的灯肯定已经开始闪烁,因为需要用我的样板代码来运行模拟人生)。这是基于 1000 个赛季模拟的最终位置热图(V1 =第一名,V2 =第二名,以此类推)。颜色越粉红,团队在特定位置完成的次数就越多:

英超模拟位置热图

曼城基本上巩固了第一的位置,几乎没有挑战者(利物浦和切尔西最有可能击败他们)。看起来在温格的中年时期,曼联变成了阿森纳,而阿森纳变成了……21 世纪初的热刺。在积分榜的另一端,纽卡斯尔联队牢牢地固定在垫底的位置,很少能在最后的 4 或 5 名之外。

真的让你觉得…

警告

虽然 xG 已经成为分析行业预测团队绩效的标准指标之一,但现在讨论它的局限性可能是明智的——以及这个项目的局限性。

首先,有几种定义 xG 的方法——似乎没有一家公司在计算 xG 的最佳方法上达成一致。一些分析师可能会根据机会的角度增加 xG 的权重,而一些分析师可能会比其他人更多地惩罚长镜头,等等。这样想吧——每个人都知道目标是什么。黄牌或角球也是如此。它们都是定义明确、切实可见的统计数据。但是什么是 xG 呢?在这被标准化之前,它将继续是一个问题。

接下来,每个模型都有局限性,我们天真的泊松足球模型也不例外。它假设比赛中的两个进球是独立的——例如,如果一个进球得分了,它不会影响结果。事实上,这只是无稽之谈-如果你是一名球员,如果你的比赛中有进球,你会改变方法。因此,一个在 1-0 落后的情况下追逐比赛的球队将会和一个有两个进球缓冲的球队表现不同。为了使模型更加有用,需要对模型中的依赖性进行一些计算。只要记住经典的足球陈词滥调- 进球改变比赛

最后,xG 真的是一个团队表现的重要指标吗?在其他限制中,该指标没有考虑到单个玩家(或他们的相对技能)。在一天结束时,大多数 xG 模型都是建立在成千上万次射门和机会的数据库上的——最终的输出将是一个平均值,而不是特定于玩家的。出于论证的目的,只要认为一个前锋,一般来说,在某些位置上应该比一个后卫有更好的射门率。那么可能会有一些球员比其他人更擅长把握某些类型的机会。这是指标开始分解的地方,理解这一点是充分利用 xG 的关键。

那么…利物浦值得赢吗?谁在乎呢,它还是发生了。到最后,不管发生了什么,唯一重要的还是结果。

其他文章

[## 运动中的随机性

为什么赢得超级碗的球队比英超多?

towardsdatascience.com](/randomness-in-sport-6e60c6132838) [## 创造机会

英国博彩业繁荣简史,从贝叶斯到必发

towardsdatascience.com](/making-up-the-odds-73fbc509ee7f)

锁定报告卡:新加坡

原文:https://towardsdatascience.com/did-singapore-obey-lockdown-rules-2aef5e55ad2c?source=collection_archive---------78-----------------------

艺术:陈卓琳

谷歌的社区流动性数据集揭示了新加坡“断路”措施对行为、服从和习惯的影响

为了遏制新冠肺炎病毒的传播,新加坡在 4 月份实行了为期两个月的局部封锁。政府“断路器”倡议的规则包括强制关闭几家零售企业,禁止公共集会,以及为老年人提供居家咨询。

从 2 月到 5 月,谷歌的社区移动数据集跟踪了不同地方的访问量相对于基线的变化——一月份的五周时间。谷歌使用相同的数据(来自选择分享谷歌位置历史的用户)计算这些变化,这些数据用于在谷歌地图中显示地点的流行时间

对数据集的分析显示了高水平的遵守,在断路器启动的当天,大多数户外场地的移动数据急剧下降。

断路器的直接影响

下图显示了对 4 月 7 日开始的断路器措施的即时反应。花在工作场所的时间几乎减少了三倍,花在家里的时间几乎增加了一倍。

蓝线(工作)和绿线(家庭)在 4 月和 5 月的波峰和波谷与周末和公共假日相对应,似乎不能解释任何异常行为。

杂货店/药店访问量的激增(黄线)与 5 月初断路器措施开始前的恐慌性购买新闻报道相对应。

3 月 17 日,邻国马来西亚进入封锁状态的消息也引发了更早的恐慌性购买事件。

公园和停车场

以下图表显示了前往五类地方的旅行数据:

杂货店&药房 —杂货店、食品仓库、农贸市场、特色食品店、药店和药房。

公园 —地方公园、国家公园、公共海滩、码头、狗公园、广场和公共花园。

公交车站 —地铁、公交、火车站等公共交通枢纽。

零售&娱乐 —餐馆、咖啡馆、购物中心、主题公园、博物馆、图书馆和电影院。

工作场所 —工作场所。

杂货店和药店受到的影响最小(与基线相差约 20%),因为它们仍被允许经营。而在 4 月 7 日之后,对零售、交通和工作场所的访问大幅下降,比基线下降了 60%以上。

参观公园的游客没有经历同样的急剧下降,因为这是断路器期间允许的“自由”之一。然而,政府将公园内的持续集会视为新冠肺炎聚集的潜在来源,并开始关闭停车场和公园内的特定区域,以确保符合社会距离措施。这导致公园的使用率慢慢下降,并最终在两周后降至-60 以下。

地区#家庭冠军

在这两个月里,新加坡可以称得上是呆在家里最多的地区。从全球来看,新加坡位列第三,仅次于巴拿马和斯里兰卡。

一个好的家庭记录并不一定意味着在新冠肺炎战场上的类似结果。没有实施封锁的台湾,其居民在家的时间只增加了 3%。它将新冠肺炎的死亡人数保持在一位数(相比之下,新加坡为 23 人)。

在家工作:共享的全球体验

我们最后的图表显示了 132 个不同国家的交通和工作场所使用的变化。除了台湾、莫桑比克和巴布亚新几内亚,样本中的所有其他国家的工作场所访问和交通使用都有所下降。

走向

随着新加坡开始放松封锁措施,下个月重新访问该数据集的更新版本将会很有意思。一个有趣的问题是,流动性——以及总体生活——是否会回到基线水平。

信用

文/数据分析— Samuel He | samuelhe@ntu.edu.sg

编辑:珀尔·李| pearllpy@gmail.com

艺术—谭卓琳|【instagram.com/fakejolart/】T2

数据集—谷歌有限责任公司“谷歌新冠肺炎社区移动报告”
https://www.google.com/covid19/mobility/访问:【2020 年 5 月 30 日>。

探索更多

我为可能想探索其他国家类似趋势的读者建立了一个交互式 tableau 仪表盘

谷歌对这些数据的说法

每天的变化与一周中该天的基线值进行比较:

基线是 2020 年 1 月 3 日至 2 月 6 日这 5 周期间,一周中相应一天的中值。

数据集显示几个月的趋势,最近的数据代表大约 2-3 天前的数据,这是生成数据集所需的时间。

计算中包括哪些数据取决于用户设置、连接性以及是否符合我们的隐私阈值。如果没有达到隐私阈值(当某个地方没有忙到足以确保匿名时),我们不会显示一天的变化。因此,您可能会遇到某些地点和日期的空字段。

我们包括对社会距离努力以及获得基本服务有用的类别。

我们根据选择加入谷歌账户位置历史的用户的数据来计算这些见解,因此这些数据代表了我们的用户样本。与所有样本一样,这可能代表也可能不代表更广泛人群的确切行为。

识别内存消耗高的变量

原文:https://towardsdatascience.com/did-you-know-how-to-identify-variables-in-your-python-code-which-are-high-on-memory-consumption-787bef949dbd?source=collection_archive---------52-----------------------

优化 Python 代码

附身摄影Unsplash 上拍照

有时候,在执行 Python 脚本时,我们会遇到内存错误。这些错误主要是由于一些变量内存消耗高

在本教程中,我们将重点关注剖析 Python 代码以优化内存消耗内存分析是一个过程,使用它我们可以剖析我们的代码并识别导致内存错误的变量。

1。)内存分析器

  • 内存分析器是一个 python 包,它评估函数中编写的每一行 Python 代码,并相应地检查内部内存的使用情况。
  • 我们可以使用 pip 或者 conda 包管理器 来安装这个包。如果使用 Anaconda 发行版,我会推荐使用 conda install ,因为它会自动解决依赖和环境问题。
**#### Install Packages** conda install memory_profiler
  • 安装完成后,可以使用以下代码行在 iPython 环境中加载评测器:
**#### Loading Memory Profiler Package** %load_ext memory_profiler
  • 加载后,使用以下语法对任何预定义的函数进行内存分析:
**#### Memory Profiling a Function** %mprun -f function_name_only Call_to_function_with_arguments

:要使用内存评测器评测一个函数,将其作为一个模块导入,这意味着将其写入一个单独的 。py 文件,然后像其他包一样导入到主程序中。

2.)工作示例

对于本教程,我使用了与内存分析器的 PyPi 网页相同的功能。它创建两个有大量元素的列表变量,然后删除其中一个(参考下面代码中的函数定义)。然后我们保存并导入这个函数到主程序中进行分析。

**#### Function Definition (Saved as example.py file)**
def my_func(): 
    a = [1] * (10 ** 6) 
    b = [2] * (2 * 10 ** 7) 
    del b 
    return a**#### Loading the line profiler within ipython/jupyter environment** %load_ext memory_profiler
from example import my_func**#### Profiling the function using line_profiler**
%mprun -f my_func my_func()**#### Memory Profile Output**
Line #    Mem usage    Increment   Line Contents
================================================
     1     50.8 MiB     50.8 MiB   def my_func():
     2     58.4 MiB      7.6 MiB    a = [1] * (10 ** 6)
     3    211.0 MiB    152.6 MiB    b = [2] * (2 * 10 ** 7)
     4     58.4 MiB      0.0 MiB    del b
     5     58.4 MiB      0.0 MiB    return a

说明

从内存分析器的输出中,可以看到输出表有四列:

  • 第 1 列(行号) —代码行号
  • 第 2 列(内存使用) —函数使用的总内存,直到行号
  • 第 3 列(增量) —程序特定行使用的内存
  • 第 4 列(内容) —实际代码内容

注意:从程序中去掉 变量“b”后内存消耗明显下降。

从表中可以清楚地看出, 变量“b”的创建导致了内存使用量的突然激增。在实时场景中,一旦我们意识到这样的发现,我们总是可以寻找其他方法来编写代码,使内存消耗保持在可接受的范围内。

结束语

在这个简短的教程中,我们学习了 Python 代码的内存分析。要了解 Python 代码的时间分析,请参考教程。

我希望这个你知道吗系列的教程信息丰富,足智多谋。

我们将在未来的教程中尝试引入更多有趣的话题。在此之前:

快乐学习!!!!

如何让您的 Python 代码运行得更快—第 1 期

原文:https://towardsdatascience.com/did-you-know-how-to-make-your-python-code-run-faster-1st-installment-f317359159a1?source=collection_archive---------20-----------------------

优化系统硬件的利用率

蒂姆·高在 Unsplash 上拍摄的照片

的上一篇教程中,我们向您介绍了 line_profiler ,这是一个可以帮助您对代码进行时间分析的包。现在是向前迈出一步的时候了。

在本教程中,我们将学习使用 Python 实现多线程和多处理方法。这些方法指导操作系统优化利用系统硬件,从而提高代码执行效率。

让我们开始吧…

多线程

引用 Wiki —在计算机架构中,多线程是中央处理器(CPU)(或多核处理器中的单核)提供多个线程并发执行的能力,由操作系统支持。

给定并发性,可以启动多个进程的并行执行,并实现更快的运行时。不去探究技术细节(谷歌和阅读 GIL ),要记住的一件事是多线程在执行基于 I/O 的任务(如下载图像和文件)时效率更高。另一方面,多处理更适合基于 CPU 的计算密集型任务。

Python 中的多线程实现

为了实现多线程,我们将使用 Python 的标准库,线程。默认情况下,标准 Python 安装附带了这个库,因此可以在我们的代码中直接导入。

为了演示多线程的有效性,我们将从 Unsplash 下载 5 幅图像。让我们观察顺序下载这些图像时的执行时间:

**#### Importing requests library**
import requests**#### Defining the function** 
def down_img(name,link):
    data = requests.get(link).content
    name = f"/home/isud/DidYouKnow/Tutorial 5/{name}.jpg"
    with open(name, "wb") as file:
        file.write(data)**#### 5 images downloaded sequentially**
%%timeit -n1 -r1
images = ['[https://images.unsplash.com/photo-1531458999205-f31f14fa217b'](https://images.unsplash.com/photo-1531458999205-f31f14fa217b'),
          '[https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa'](https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa'),
          '[https://images.unsplash.com/photo-1531404610614-68f9e73e35db'](https://images.unsplash.com/photo-1531404610614-68f9e73e35db'),
          '[https://images.unsplash.com/photo-1523489405193-3884f5ca475f'](https://images.unsplash.com/photo-1523489405193-3884f5ca475f'),
          '[https://images.unsplash.com/photo-1565098735462-5db3412ac4cb'](https://images.unsplash.com/photo-1565098735462-5db3412ac4cb')]
for i,link in enumerate(images):
    down_img(i,link)**#### %%timeit results**
51.4 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

可以看出,完整下载 5 个图像花费了 51.4 秒。此外,只有在前一次下载结束后,新的下载才会开始。现在让我们看看多线程如何提高代码性能。

**#### Importing necessary library** import threading
import requests**#### Defining the function** def down_img(name,link):
    data = requests.get(link).content
    name = f"/home/isud/DidYouKnow/Tutorial 5/{name}.jpg"
    with open(name, "wb") as file:
        file.write(data)**#### Images downloaded in parallel threads** %%timeit -n1 -r1
threads = []
images = ['[https://images.unsplash.com/photo-1531458999205-f31f14fa217b'](https://images.unsplash.com/photo-1531458999205-f31f14fa217b'),
          '[https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa'](https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa'),
          '[https://images.unsplash.com/photo-1531404610614-68f9e73e35db'](https://images.unsplash.com/photo-1531404610614-68f9e73e35db'),
          '[https://images.unsplash.com/photo-1523489405193-3884f5ca475f'](https://images.unsplash.com/photo-1523489405193-3884f5ca475f'),
          '[https://images.unsplash.com/photo-1565098735462-5db3412ac4cb'](https://images.unsplash.com/photo-1565098735462-5db3412ac4cb')]for i,link in enumerate(images):
    **t = threading.Thread(target=down_img, args=(i,link))**
    **t.start()**
    **threads.append(t)****for thread in threads:
    thread.join()****#### %%timeit results** 25.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

代码解释—定义图像下载循环:

  • 步骤 1(线程初始化) — Python 在单线程中运行完整的代码(姑且称之为主线程)。在这个例子中,通过从线程库中调用线程函数,我们启动并行线程并为它们分配一个要执行的目标进程(在这个例子中为 down_image )。被调用函数所需的所有参数都应该作为一个序列对象(在本例中为 tuple)传递。每次调用线程函数都会启动一个新线程(姑且称之为并行线程)。
  • 步骤 2(线程开始) —调用线程的 开始 方法将指示 Python 开始线程执行。假定for 循环的正在主线程中执行,并且函数调用在并行线程中执行,则在图像下载进行的同时,for 循环将继续执行。
  • 步骤 3(线程连接) —每个新线程都被捕获到一个名为 threads 的列表中。然后通过调用连接方法将并行线程连接到主线程。

为什么加盟是必要的?

直到第 2 步,我们所有的线程(主线程和并行线程)都处于并行执行状态。在这种情况下,主线程的执行可以在并行线程之前完成。为了避免这种情况,将并行线程连接到主线程是不可避免的。这将确保主线程的执行仅在并行线程完成之后完成。下图解释了这两种情况:

无连接和有连接的螺纹

可以看出,下载图像的执行时间缩短了近 50%(大约 25.6 秒)。).上述示例展示了多线程如何有助于 I/O 操作并提高下载/上传过程的效率。

多重处理

与在单个进程中执行多个线程的多线程不同,多线程为每个任务启动一个新的并行进程。如上所述,它为 CPU 密集型任务(需要大量计算的任务)提供了相当大的运行时间改进。

在 Python 中实现多重处理

多重处理是另一个支持 Python 多重处理特性的标准库。为了理解它的功能,我们将多次调用一个计算密集型函数。用于演示的函数计算 1 到 1000 万的平方。该功能并行执行 8 次。我们来观察一下这个函数在正常情况下的表现。

**#### Importing time library**
import time**#### Defining the function** 
def demo_func(num):
    for i in range(num):
        a = i**2**#### Calling demo function sequentially**
%%timeit -n1 -r1
for i in range(8):
    demo_func(10000000)**#### %%timeit results**
21.2 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

演示函数的顺序执行总共花费了 21.2 秒。现在,让我们检查一下在多处理设置中完成后的性能提升。

**#### Importing time library**
import time**#### Defining the function** 
def demo_func(num):
    for i in range(num):
        a = i**2**#### Multi-processing demo function** 
%%timeit -n1 -r1
processes = []
lop_size = [10000000,10000000,10000000,10000000,10000000,10000000,10000000, 10000000]
**p = multiprocessing.Pool()**
**p.map(demo_func,lop_size)**
**p.close()
p.join()****#### %%timeit results**
11.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

在多处理框架下,的执行时间下降了 50%,为 11.6 秒。在顺序处理中,一次使用一个单 CPU 内核,而在多处理中,并行使用所有系统内核。CPU 使用情况屏幕截图显示了同样的情况:

顺序执行与并行执行

上图中的每条线代表一个 CPU 内核。请注意,在顺序执行中,每个函数调用都会触发一个内核,而在并行执行中,所有内核都会被同时触发。

代码解释

  • 步骤 1(池创建)池方法创建可以并行利用的进程池。在没有任何参数的情况下,创建的进程数量等于系统上 CPU 内核的数量。我有一个四核系统(4 核),这意味着在执行 8 个函数调用时,前 4 个调用将并行运行,随后是后 4 个调用。请注意,您还可以在池中定义自定义的进程数量(比内核数量多),但是超过这个数量,它将开始消耗您的系统内存并可能降低性能
  • 步骤 2(池映射) —这是指示您的流程执行一个特定函数(第一个参数)以及要传递给它的一系列参数(第二个参数)
  • 步骤 3(池关闭)—关闭方法指示 Python 解释器,我们已经提交了我们想要提交到池中的所有内容,并且将来不会再有输入提供给池。
  • 第 4 步(池连接)——与线程的情况一样,连接方法确保只有当所有并行进程完成时,代码执行才会完成。

从上面的场景中,我们可以看到多线程是如何成为对抗低效代码性能的强大武器的。

结束语

在本教程中,我们将重点放在通过优化利用系统硬件来提高代码性能上。在下一期文章中,我们将展示提高代码性能的具体编程实践。

我希望这篇教程是有启发性的,并且你学到了一些新的东西。

会在以后的教程中尝试并带来更多有趣的话题。在此之前:

快乐学习!!!!

如何让您的 Python 代码运行得更快—第 2 部分

原文:https://towardsdatascience.com/did-you-know-how-to-make-your-python-code-run-faster-2nd-installment-f105516cbd8a?source=collection_archive---------49-----------------------

高效的编程实践

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

在前两个教程中,我们谈到了测量 python 代码执行时间的和优化硬件利用率以提高代码执行速度的。在本教程中,我们将再次致力于提高我们的代码运行时间,但是,通过使用一些编程最佳实践,如:

  • 记忆化
  • …向量化…

记忆化

a .)学校

我相信每个读这篇文章的人都会记得他们的小学时代。我们的数学老师总是记住从 2 到 20 的乘法表。想过为什么吗?很简单,这些表格构成了数学计算的基础,将它们储存在你的记忆中可以帮助你更快地计算。

b .)回到未来

记忆难懂的概念的旧概念通过名称记忆化被构建到现代世界编程概念中。**

引用维基百科,记忆化被定义为优化技术,主要用于通过存储昂贵的函数调用的结果和在相同的输入再次出现时返回缓存的结果来加速计算机程序。**

c .)用 Python 实现

为了用 Python 演示这个概念,让我们首先定义一个昂贵的函数。为了仿真一个昂贵的函数,这在计算上很耗时,我们将从时间包中调用睡眠函数,并人为增加函数*的运行时间。*******

下面是创建该函数的 Python 代码。这个函数使用一个数值参数作为输入,休眠 5 秒钟,然后返回数值的平方作为输出。

*****USUAL APPROACH TO DEFINE A FUNCTION****#### Import time package**
import time**#### Defining expensive function - The Usual Way**
def expensive_function(n):
    time.sleep(5)
    return n**2***

d .)用常用方法运行时间

使用上面使用的方法,每次我们调用函数时,函数将首先休眠 5 秒钟,然后返回实际结果。让我们通过连续调用 3 次来对这个函数进行时间分析。**

*****### Calling the expensive function 3 times and timing** 
%%timeit -n1 -r1
expensive_function(9)
expensive_function(9)
expensive_function(9)**### Time profile output** 15 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)***

假设,我们调用了该函数 3 次,记录的总执行时间为 15 秒

e.)使用记忆减少运行时间**

*****MEMOIZATION APPROACH TO DEFINE FUNCTIONS
### Importing package** import time**### Re-defining the expensive function** def expensive_function(n):
    **argument_dict** = {}
    if n in argument_dict:
        return argument_dict[n]
    else:
        argument_dict[n] = n**2
        time.sleep(5)
        return n**2**### Testing the run time** %%timeit -n1 -r1
expensive_function(9)
expensive_function(9)
expensive_function(9)**### Time Profile output**
5.01 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)***

解释

  • 由于记忆化提倡存储昂贵函数的结果,我们使用 Python 的字典来完成这项工作。注意字典" argument_dict "的定义**
  • 函数定义略有变化。现在,在休眠 5 秒钟并返回结果之前,该函数检查与传递的参数相关联的结果是否已经存在于字典中
  • 只有在内存(本例中为字典)中没有与传递的参数相关的结果时,才会调用 sleep 函数
  • 查看测试结果,可以注意到测试代码的执行时间(5.01 秒)比过去的执行时间(15 秒)减少了三分之一****

…向量化…

每当我们想到在代码中实现迭代操作时,我们想到的第一个编程结构就是循环。当处理较小的数据集时,循环可以做得很好,但是随着数据集大小的增加,性能会下降。编程思维的一个小小的改变可以帮助我们克服这个挑战。**

考虑一个场景,我们需要对包含一百万行Pandas 数据帧的一列执行一些操作。如果以传统方式处理,将会一个接一个地循环一百万条记录。**

另一方面,建议对整个列只执行一次该操作(而不是逐个记录)。这样,你就避免了执行一百万次同样的操作。下面的例子展示了我们如何从一些常见的编程场景中消除循环:

a)简单的数学运算

让我们考虑一个简单的场景,其中我们有一个包含 100k 直角三角形的底边垂直长度的熊猫数据帧,任务是计算它们的斜边。计算公式如下:**

来源

让我们使用 pandas apply 方法(一个函数在一个系列对象上的迭代应用)来捕获执行该操作时的运行时间:

*****#### Importing libraries** import pandas as pd
import random**#### Creating Pandas with random values of base and perpendicular** base = [random.randint(1,100) for _ in range(100000)]
perpend = [random.randint(1,100) for _ in range(100000)]
triangle = pd.DataFrame(list(zip(base, perpend)),columns=["Base", "Perpendicular"])**#### Calculating hypotenuse** %%timeit
triangle["Hypotenuse"] = triangle.apply(lambda row: (row["Base"]**2 + row["Perpendicular"]**2) **(1/2), axis=1)**#### Performance** 3.43 s ± 52.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)***

使用 apply(熊猫循环最有效的形式之一)循环 100k 行花费了我们大约 3.43 秒。让我们看看矢量化如何帮助我们提高这段代码的性能。**

*****#### Importing libraries** import pandas as pd
import random**#### Creating Pandas with random values of base and perpendicular** base = [random.randint(1,100) for _ in range(100000)]
perpend = [random.randint(1,100) for _ in range(100000)]
triangle = pd.DataFrame(list(zip(base, perpend)),columns=["Base", "Perpendicular"])**#### Calculating hypotenuse** 
%%timeit
triangle["Hypotenuse"] = **(triangle["Base"]**2 + triangle["Perpendicular"]**2) **(1/2)****#### Performance** 5.81 ms ± 274 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)***

解释

  • 循环,在这个版本的代码中被删除了
  • 合并了对完整 pandas 系列对象(基本和垂直)操作的单个代码语句(以粗体突出显示)****
  • 与之前的 3.42 秒相比,整个操作在 5.81 毫秒内完成。这种改善难道不是实质性的吗?

b)涉及条件的操作

矢量化可以派上用场的另一个常见场景是,当循环被有条件地执行时。为了证明这一点,我们创建了一些虚拟数据,其中包含 2019 年我们家每小时的电力消耗信息(36524 条记录)。鉴于费用在一天的不同时间是不同的,我们想计算每小时的电费。费用表如下:*

编写简单循环时的性能评估:

*****#### Importing libraries** import pandas as pd**#### Importing Dataframe** df = pd.read_csv("/home/isud/DidYouKnow/Tutorial 4/electricity.csv")**#### Calculating Hourly Electricity Cost** %%timeit
df["cost"] = df[["Hour","Usage"]].apply(lambda x: x["Usage"]*5 if x["Hour"]<7 else (x["Usage"]*10 if x["Hour"] <19 else x["Usage"]*15),axis = 1)**#### Performance** 417 ms ± 19.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)***

使用熊猫最有效的循环方法(应用方法),我们可以在 417 毫秒内完成我们的目标。让我们评估使用矢量化执行相同操作时的性能:**

*****#### Importing libraries** import pandas as pd**#### Importing Dataframe** df = pd.read_csv("/home/isud/DidYouKnow/Tutorial 4/electricity.csv")**#### Calculating Hourly Electricity Cost** %%timeit
**less_seven = df["Hour"].isin(range(1,7))
less_nineteen = df["Hour"].isin(range(7,19))
rest = df["Hour"].isin(range(19,24))** df.loc[less_seven, "Cost"] = df.loc[less_seven, "Usage"] * 5 
df.loc[less_nineteen, "Cost"] = df.loc[less_nineteen, "Usage"] * 10 
df.loc[rest, "Cost"] = df.loc[rest, "Usage"] * 15**#### Performance** 7.68 ms ± 47 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)***

解释

  • 我们没有遍历每一行,而是为一天中不同的时间创建了逻辑向量,即 less_sevenless _ 19rest**
  • 然后,这些逻辑向量用于成本构成的一次性(矢量化)计算。
  • 使用上述方法,我们可以将代码运行时间从的 417 毫秒减少到的 7.68 毫秒。**

结束语

在过去的 2 个教程中,我们已经尝试用编写高效代码的方法来武装你。尝试这些方法,让我们知道它们是否有帮助。

会在以后的教程中尝试并带来更多有趣的话题。在此之前:

快乐学习!!!!

你在 Spark SQL 中知道这个吗?

原文:https://towardsdatascience.com/did-you-know-this-in-spark-sql-a7398bfcc41e?source=collection_archive---------16-----------------------

入门

Spark SQL 中值得了解的 8 个不明显的特性。

Spark SQL 的 DataFrame API 是用户友好的,因为它允许用高级术语表达甚至非常复杂的转换。尤其是现在的 Spark 3.0,已经相当丰富和成熟了。然而,在某些情况下,您可能会发现它的行为出乎意料,或者至少不是很直观。这可能会令人沮丧,尤其是当你发现你的生产流水线产生了你没有预料到的结果。

在本文中,我们将介绍 Spark 的一些乍一看并不明显的特性,了解这些特性可以避免愚蠢的错误。在一些例子中,我们还会看到一些很好的优化技巧,根据您的转换,这些技巧会变得很方便。对于代码示例,我们将在 Spark 3.0 中使用 Python API。

1.array_sort 和 sort_array 有什么区别?

这两个函数都可以用来对数组进行排序,但是在用法和空值处理上有所不同。虽然 array_sort 只能按升序对数据进行排序,但是 sort_array 采用了第二个参数,在这个参数中,您可以指定数据应该按降序还是升序排序。 array_sort 会将空元素放在数组的末尾,当按降序排序时 sort_array 也会这样做。但是当以升序(默认)使用 sort_array 时,空元素将被放在开头。

l = [(1, [2, None, 3, 1])]df = spark.createDataFrame(l, ['id', 'my_arr'])(
    df
    .withColumn('my_arr_v2', array_sort('my_arr'))
    .withColumn('my_arr_v3', sort_array('my_arr'))
    .withColumn('my_arr_v4', sort_array('my_arr', asc=False))
    .withColumn('my_arr_v5', reverse(array_sort('my_arr')))
).show()+---+----------+----------+-----------+----------+-----------+
| id|    my_arr| my_arr_v2|  my_arr_v3| my_arr_v4|  my_arr_v5|
+---+----------+----------+-----------+----------+-----------+
|  1|[2,, 3, 1]|[1, 2, 3,]|[, 1, 2, 3]|[3, 2, 1,]|[, 3, 2, 1]|
+---+----------+----------+-----------+----------+-----------+

关于如何使用函数 array_sort 还有一个选项,即直接在 SQL 中使用(或者作为 SQL 表达式作为 expr() 函数的参数),其中它接受第二个参数,该参数是一个比较器函数(从 Spark 3.0 开始支持)。使用此函数,您可以定义如何比较元素来创建订单。这实际上带来了非常强大的灵活性,例如,您可以对结构数组进行排序,并定义应该按照哪个结构字段进行排序。让我们看看这个例子,在这个例子中,我们通过第二个 struct 字段进行显式排序:

schema = StructType([
    StructField('arr', ArrayType(StructType([
        StructField('f1', LongType()), 
        StructField('f2', StringType())
    ])))
])
l = [(1, [(4, 'b'), (1, 'c'), (2, 'a')])]
df = spark.createDataFrame(l, schema=schema)(
    df
    .withColumn('arr_v1', array_sort('arr'))
    .withColumn('arr_v2', expr(
        "array_sort(arr, (left, right) -> case when left.f2 < right.f2 then -1 when left.f2 > right.f2 then 1 else 0 end)"))
).show(truncate=False)

在这里,您可以看到 SQL 中表示的比较函数采用两个参数,它们是数组的元素,它定义了应该如何比较它们(即根据第二个字段 f2 )。

2. concat 函数不允许空值

concat 函数可以用于连接字符串,也可以用于连接数组。不太明显的一点是,该函数不允许空值,这意味着如果任何参数为空,那么输出也将为空。例如,当连接两个数组时,如果一个数组为空,我们很容易丢失另一个数组的数据,除非我们显式地处理它,例如,使用 coalesce :

from pyspark.sql.types import *
from pyspark.sql.functions import concat, coalesce, arrayschema = StructType([
    StructField('id', LongType()),
    StructField('arr_1', ArrayType(StringType())),
    StructField('arr_2', ArrayType(StringType()))
])l = [(1, ['a', 'b', 'c'], None)]
df = spark.createDataFrame(l, schema=schema)(
    df
    .withColumn('combined_v1', concat('arr_1', 'arr_2'))
    .withColumn('combined_v2', concat(coalesce('arr_1'), array(), coalesce('arr_2', array())))
).show()+---+---------+-----+-----------+-----------+
| id|    arr_1|arr_2|combined_v1|combined_v2|
+---+---------+-----+-----------+-----------+
|  1|[a, b, c]| null|       null|  [a, b, c]|
+---+---------+-----+-----------+-----------+

3.collect_list 不是一个确定性函数

聚合函数 collect_list 可用于在按某个键分组后创建元素数组,但它不是确定性的,因为结果数组中元素的顺序取决于行的顺序,而行的顺序在洗牌后可能不是确定性的。

了解优化器对非确定性函数的处理非常小心也是有好处的,例如,优化器不会对其进行筛选,如以下查询所示:

(
  df.groupBy('user_id')
  .agg(collect_list('question_id'))
  .filter(col('user_id').isNotNull())
).explain()

从计划中可以看到,过滤器是最后一个转换,因此 Spark 将首先计算聚合,然后过滤掉一些组(这里我们过滤掉 group,其中 user_id 为 null)。然而,如果数据首先被过滤器减少,然后被聚集,这将更有效,这确实会发生在确定性函数中,例如计数:

(
  df.groupBy('user_id')
  .agg(count('*'))
  .filter(col('user_id').isNotNull())
).explain()

这里的过滤器被推得更靠近源,因为聚合函数计数是确定性的。

除了 collect_list 之外,还有其他非确定性函数,例如 collect_setfirstlastinput_file_namespark_partition_idrand 等等。

4.对窗口进行排序会改变框架

有多种聚合和分析函数可以在所谓的窗口中调用,定义如下:

w = Window().partitionBy(key)

该窗口也可以通过调用 orderBy(key) 进行排序,并且可以通过 rowsBetweenrangeBetween 指定一个帧。这个框架决定了在窗口中调用哪个行的函数。一些功能也需要对窗口进行排序(例如 row_count ),但是对于一些功能,排序是可选的。关键是排序可以改变可能不直观的框架。考虑带有 sum 函数的示例:

from pyspark.sql import Window
from pyspark.sql.functions import suml = [
  (1, 10, '2020-11-01'), 
  (1, 30, '2020-11-02'), 
  (1, 50, '2020-11-03')
]df = spark.createDataFrame(l,['user_id', 'price', 'purchase_date'])w1 = Window().partitionBy('user_id')
w2 = Window().partitionBy('user_id').orderBy('purchase_date')(
  df
  .withColumn('total_expenses', sum('price').over(w1))
  .withColumn('cumulative_expenses', sum('price').over(w2))
).show()+-------+-----+-------------+--------------+-------------------+
|user_id|price|purchase_date|total_expenses|cumulative_expenses|
+-------+-----+-------------+--------------+-------------------+
|      1|   10|   2020-11-01|            90|                 10|
|      1|   30|   2020-11-02|            90|                 40|
|      1|   50|   2020-11-03|            90|                 90|
+-------+-----+-------------+--------------+-------------------+

正如你所看到的,排序窗口将改变帧从开始到当前行,所以求和将产生一个累积和而不是总和。然而,如果我们不使用排序,默认框架将是整个窗口,求和将产生总和。

5.写入表会使缓存失效

不完全是这样,但是如果您的缓存数据是基于某人刚刚向其追加了数据(或者覆盖了数据)的这个表,那么一旦您调用另一个操作,数据将被扫描并再次缓存。让我们看看这个例子:

df = spark.table(tableA)
df.cache()
df.count()  # now the data is placed in cache# someone writes to tableA now:
dx.write.mode('append').option('path', path).saveAsTable(tableA)# now df is no longer cached, but it will be again after calling some action on itdf.count()  # the data is now placed to memory again but its content was changed

因此,这里意想不到的事情是,如果有人同时追加表,对缓存的数据帧调用相同的计算可能会导致不同的结果。

6.为什么调用 show()会运行多个作业?

在 Spark 中,有两种类型的操作、转换和动作,前者是懒惰的,而后者将物化查询并运行作业。 show() 函数是一个动作,所以它运行一个作业,然而令人困惑的是有时它运行更多的作业。为什么会这样?一个典型的例子是这个查询:

(
  spark.table(table_name).filter(col('user_id') == xxx)
).show()

现在,根据数据的属性,情况可能如下图所示,这是 Spark UI 的一个屏幕截图,我们可以看到 Spark 在返回结果之前运行了五个作业:

还要注意,这些作业中的任务数量是不同的。第一个作业(作业 id = 10)只有一个任务!下一个作业运行了 4 个任务,然后是 20、100 个任务,最后是一个有 75 个任务的作业。顺便说一下,执行这个任务的集群有 32 个可用内核,因此 32 个任务可以并行运行。

在多个作业中执行查询的想法是为了避免处理所有输入数据。默认情况下, show 函数只返回 20 行(这可以通过传递 n 参数来改变),所以也许我们可以只处理数据的一个分区来返回 20 行。这就是为什么 Spark 首先运行一个只有一个任务的作业,只处理数据的一个分区,希望找到输出所需的 20 行。如果 Spark 没有找到这 20 行,它将启动另一个作业来处理另外四个分区(这就是为什么第二个作业有四个任务),这样情况会重复,Spark 在每个进一步的作业中都会增加要处理的分区数量,直到它找到所需的 20 行或所有分区都被处理。

这是一个有趣的优化,特别是当您的数据集非常大(包含许多分区)并且您的查询不是非常有选择性时,所以 Spark 实际上只能处理前几个分区来找到 20 行。另一方面,如果您的查询具有很强的选择性,例如您要在一个非常大的数据集中查找一行(甚至可能不存在),那么使用 collect 函数可能会更有用,该函数将从一开始就充分利用集群的潜力,并在一个作业中处理所有数据,因为最终无论如何都必须处理所有分区(如果记录不存在)。

7.如何确保用户定义的函数只执行一次?

众所周知,如果用户定义函数(UDF)不是必需的,最好避免使用,因为它们会带来一些性能损失(损失有多大,取决于 UDF 是用 scala/java 还是 python 实现的)。但不太明显的是,如果使用 UDF,它可能会比预期执行更多次,因此开销会变得更大。然而,这是可以避免的,因此总的惩罚将会减轻。让我们看一个简单的例子:

@udf('long')
def add_one(x):
    return x + 1(
    spark.range(10)
    .withColumn('increased', add_one(col('id')))
    .filter(col('increased') > 5)
).explain()

在本例中,我们创建了一个简单的 UDF,用于向 DataFrame 添加一个新列,然后基于该列进行筛选。如果我们通过调用 explain 来检查查询计划,我们将看到:

如您所见,BatchEvalPython 操作符在计划中出现了两次,这意味着 Spark 将执行 UDF 两次。显然,这不是最佳的执行计划,尤其是当 UDF 成为瓶颈时,这是常有的事。幸运的是,有一个很好的技巧可以让 Spark 只调用 UDF 一次,那就是让函数变得不确定(参见文档):

add_one = add_one.asNondeterministic()(
    spark.range(10)
    .withColumn('increased', add_one(col('id')))
    .filter(col('increased') > 5)
).explain()

现在,检查查询计划发现 UDF 只被调用了一次:

这是因为 Spark 现在认为函数是不确定的,所以调用它两次是不安全的,因为每次调用它都会返回不同的结果。同样值得理解的是,通过这样做,我们对优化器施加了一些约束,优化器现在将以与其他非确定性表达式类似的方式处理它,例如,过滤器将不会像我们在上面的 collect_list 函数中看到的那样被推送。

8.UDF 可以破坏你的数据分发

不是字面意思。但让我解释一下我这么说的意思。假设有这样一种情况,您希望连接两个分桶的表,并且还需要在其中一列上调用 UDF。分桶将允许您在不混乱的情况下进行连接,但是您需要以正确的顺序调用转换。考虑以下查询:

(
  dfA.join(dfB, 'user_id')
  .withColumn('increased', add_one('comments'))
).explain()

这里,我们将两个在 user_id 列上分桶的表连接到相同数量的桶,并在 dfA 的其中一列上应用 UDF ( add_one )。该计划如下所示:

在这里你可以看到一切都很好,因为计划没有交换操作符,执行将是无洗牌的,这正是我们所需要的,这是因为 Spark 知道数据的分布,可以用它来连接。

另一方面,让我们看看如果先应用 UDF,然后再执行连接会发生什么:

(
  dfA
  .withColumn('increased', add_one('comments'))
  .join(dfB, 'user_id')
).explain()

现在情况发生了变化,我们在计划中有两个交换操作符,这意味着 Spark 现在将在连接之前洗牌。这是因为调用 UDF 删除了关于数据分布的信息,Spark 现在不知道数据实际上分布得很好,它将不得不洗牌以确保分区是正确的。因此,调用 UDF 并没有真正破坏分布,但它删除了关于它的信息,所以 Spark 将不得不假设数据是随机分布的。

结论

在本文中,我们讨论了一些 Spark 特性的例子,这些特性可能不太明显,或者在编写查询时很容易忘记。其中一些使用不当会导致代码中的错误,例如,如果您忘记了对窗口排序将改变您的框架,或者如果一些输入参数为空,一些函数将产生空值(如 concat 函数)。在一些例子中,我们还看到了一些简单的优化技巧,比如使 UDF 不确定可以避免执行两次,或者如果表是分桶的(或者根据一些特定的分区进行分布),在连接后调用 UDF 可以避免混乱。

我们还看到,使用 SQL 函数有时比 DSL 函数更灵活,一个特别的例子是 array_sort ,它在 SQL 中允许您指定比较器函数来实现自定义排序逻辑。

使用 Python 的 SQLite 简介

原文:https://towardsdatascience.com/did-you-know-you-can-get-rid-of-excel-sheets-csv-files-when-working-with-python-3cf8164a57c4?source=collection_archive---------39-----------------------

电子表格的免费替代品

米利安·耶西耶在 Unsplash 上拍摄的照片

在信息报告领域,我们主要依靠 Excel 表格CSV 文件来满足我们的数据存储需求。随着 ExcelCSV 文件数量的增加,这些文件的管理成为一个真正的挑战。对于这样的场景,拥有数据库软件似乎是一个合适的解决方案。但是,由于成本和上线时间等因素,该解决方案被搁置一旁。

在本教程中,我们将向您介绍 SQLite3。 它是一个 Python 模块,创建了一个免费的、磁盘上的关系数据库管理系统。

1.)关于 SQLite DB 和 SQLite3 模块的简要说明

SQLite 是一个独立的无服务器 C 应用程序库,它提供了一个磁盘关系数据库管理系统(RDBMS) 。无服务器使得 SQLite 成为原型开发、移动应用和游戏开发的完美候选。

SQLite3 是一个 Python 模块,在标准 Python 安装中默认可用。它提供了一个使用 SQLite 数据库的接口,并确保数据库管理成为标准 Python 代码的一部分。

2.)如何创建数据库?

创建一个 SQLite 数据库就像执行一个 SQLite3 下面的代码块演示了同样的情况。

**import** sqlite3 **as** db
con = db.connect(**"Folder_Location/sample.db"**)

如果文件夹位置中已经存在具有所需名称的数据库,库将建立新的连接。否则,它创建一个新的数据库。

要查看该数据库、其相关表以及这些表中的数据,可以使用基于第三方 GUI 的应用程序SQLitebrowser下面是应用程序界面截图:

屏幕截图— Sqlite 浏览器(图片由作者提供)

3.)PEP 249 和光标

在我们学习使用 SQLite3 执行 SQL 查询之前,让我们先来看看 PEP 249游标

PEP 249 是设计数据库接口的 Python 标准指南(像 SQLite3 模块)。它确保使用 Python 代码执行 SQL 查询的方法在数据库之间保持一致,并且支持可移植性。PEP 249 推荐使用游标来执行 SQL 查询。

光标是对象的,其方法定义为支持 SQL 查询执行。我们可以使用连接对象光标方法创建这个类的一个实例。下面的代码块演示了光标对象的创建。

### Previous Python Statements
**import** sqlite3 **as** db
con = db.connect(**"Folder_Location/sample.db"**)### Creating a cursor
cur = con.cursor()

4.)游标方法和 SQL 查询

创建游标对象后,我们可以使用它的方法来执行 SQL 查询。游标对象支持多种方法来执行 SQL 查询。其中一个清单如下:

光标方法(图片由作者提供)

对于本教程,我们将主要使用执行方法。关于 Sqlite3 模块的详细文档可以在 Python 官网 : 获得

表格创建

我们可以将 create table SQL 查询作为字符串传递给 execute 方法来创建新表。我们可以保持表格结构与我们想要存储在数据库中的 Excel 表CSV 文件相同。

#### Creating table using cursor
**cur.execute**('''CREATE TABLE IF NOT EXISTS **dept** 
(id text PRIMARY KEY, dept_name text NOT NULL,
dept_head text);''')

加载数据

为了将数据加载到数据库中,我们将使用 Pandas 库的内置函数。下面的代码块假设导入的 Excel 文件与 SQLite 数据库表中的列名相同。

#### Using Pandas builtin function to push the excel data
#### Importing pandas
import pandas as pd#### Importing CSV file as a dataframe
df = pd.read_csv("file location/name.csv")#### Pushing the dataframe in to the SQLite database
df.to_sql(table_name, con, if_exists = "append", index = False)

描述 : —我们使用了‘to _ SQL’data frame 方法的 4 个不同参数:

  • Table_name —我们正在推送数据的表
  • Con —连接到数据库时初始化的连接对象
  • If_exists —该参数检查 SQLite 数据库中是否存在表格。如果该表存在,数据框中的数据将追加到现有表中。另一个选项是用“代替作为参数实参。它将用数据框中的新数据替换历史数据。
  • 索引-这将确保数据框索引不会被添加到表中。

使用上面的函数,可以很容易地将 Excel / CSV 数据导入到他们的 SQLite 数据库中。如果情况允许插入手动记录,我们可以使用下面的 insert 语句:

#### Insert statement to insert a specific value in a database table
**cur.execute**('''INSERT INTO **dept**(id, dept_name,dept_head) VALUES (1, "Analytics", "Department Head");''')

在上面的语句中,我们将数据插入到部门表中。

  • 表名后提供的列列表是可选的
  • 在列列表后,使用关键字值,然后是值列表
  • 值列表中值的数量应该与列列表中列的数量相同
  • 值列表中的元素数应该与表中的列数相同

选择数据

一旦数据在 SQLite 表中可用,将它用于您的报告或分析目的是一个人将执行的关键活动。使用选择查询来完成工作

#### Selecting data from the table
**for** rows **in** cur.execute(**'''select * from dept'''**):
    print(rows)

注意:在上面的例子中,光标对象被视为一个 iterable 对象,它循环以获取所有选中的记录。或者,可以使用获取一个、获取多个或获取所有方法来选择特定数量的记录。

删除记录

通常,在后期阶段,我们会发现表中的数据记录包含一些错误的值。使用标准的删除查询,我们可以删除特定的记录。

#### Deleting data in table
cur.execute('''**delete from dept where id in (3,4)**''')
con.commit()

注意:我们应该在每次插入、删除或更新操作之后执行一个提交方法它确保数据更改成功地反映在数据库中。

结束语

在创建应用程序原型设计概念证明处理多个 CSV 文件时,我们大多数人都希望有一个服务器免费和轻量级版本的关系数据库。有了 SQLite 和它的 Python 接口的知识,我希望我们现在可以跨越这个鸿沟。

我希望这篇教程是有启发性的,并且你学到了一些新的东西。

我会在未来的教程中尝试引入更多有趣的话题。在此之前:

快乐学习!!!!

您知道吗——Python 技巧

原文:https://towardsdatascience.com/did-you-know-you-can-measure-the-execution-time-of-python-codes-14c3b422d438?source=collection_archive---------9-----------------------

测量 Python 代码的执行时间

UnsplashNeONBRAND 拍摄的照片

当我们开始精通一门编程语言时,我们渴望不仅交付最终目标,而且使我们的程序更高效。

你知道吗系列的这个教程中,我们将学习一些 Ipython 的魔法命令,它们可以帮助我们对 python 代码进行时间分析。

注意,出于本教程的目的,建议使用 Anaconda 发行版。

1.)分析一行代码

要检查一行 python 代码的执行时间,可以使用 %timeit 。下面是一个简单的例子来说明它是如何工作的:

#### Simple usage of magics command %timeit
**%timeit [num for num in range(20)]**#### Output
**1.08 µs ± 43 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)**

关键注意事项:

  • 在要评测的代码行之前使用 %timeit
  • 返回每次运行内循环r次数或运行和n次数计算的代码运行时间的平均值和标准差。在上面的例子中,列表理解被评估了 7 次,每次运行有一百万次循环(默认行为)。这平均花费了 1.08 微秒,标准偏差为 43 纳秒。
  • 你可以在调用魔术命令时自定义运行和循环的次数。下面的例子供参考:
#### Customizing number of runs and loops in %timeit magic command
**%timeit -r5 -n100 [num for num in range(20)]**1.01 µs ± 5.75 ns per loop (mean ± std. dev. of 5 runs, 100 loops each)

使用命令选项 -r 和-n,分别表示运行次数和循环次数,我们将时间曲线操作定制为 5 次运行和每次运行中的 100 次循环。

2.)剖析多行代码

本节向前迈进了一步,解释了如何分析一个完整的代码块。通过对%下面有一个示例演示供参考:

#### Time profiling the code block using %%timeit
**%%timeit -r5 -n1000
for i in range(10):
    n = i**2
    m = i**3
    o = abs(i)**#### Output
**10.5 µs ± 226 ns per loop (mean ± std. dev. of 5 runs, 1000 loops each)**

可以观察到循环的平均执行时间为 10.5 微秒。注意,相同的命令选项 -r 和-n 分别用于控制运行计数和每次运行中的循环数。

3.2)对代码块中的每一行代码进行时间剖析

到目前为止,在分析单行代码或完整代码块时,我们只查看了汇总统计数据。如果我们想评估代码块中每一行代码的性能,该怎么办?线路侧写师包来救援!!!

Line_profiler 包可用于对任何功能进行逐行剖析。要使用 line_profiler 软件包,请执行以下步骤:

  • 安装包line profiler包可以通过简单调用 pip 或 conda install 来安装。如果使用 Python 的 anaconda 发行版,建议使用 conda install
#### Installing line_profiler package
**conda install line_profiler**
  • 加载扩展 —安装后,您可以使用 IPython 加载 line_profiler IPython 扩展,该扩展作为该软件包的一部分提供:
#### Loading line_profiler's Ipython extension
**%load_ext line_profiler**
  • 对函数进行时间分析——一旦加载,使用以下语法对任何预定义的函数进行时间分析
%lprun -f **function_name_only** **function_call_with_arguments**

语法细节:

  • 对 line_profiler 的调用以关键字 %lprun 开始,后跟命令选项-f
  • 命令选项后面是函数名,然后是函数调用

在本练习中,我们将定义一个函数,接受身高(单位为米)和体重(单位为磅)列表作为输入,并将它们分别转换为厘米和千克。

**#### Defining the function**
def conversion(ht_mtrs, wt_lbs ):
    ht_cms = [ht*100 for ht in ht_mtrs]
    wt_kgs = [wt*.4535 for wt in wt_lbs]**#### Defining the height and weight lists:**
ht = [5,5,4,7,6]
wt = [108, 120, 110, 98]**#### Profiling the function using line_profiler** %lprun -f conversion conversion(ht,wt)---------------------------------------------------------------
**#### Output
Total time: 1.46e-05 s****File: <ipython-input-13-41e195af43a9>****Function: conversion at line 2****Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     2       1        105.0    105.0     71.9      ht_cms = [ht*100 for ht in ht_mtrs]
     3       1         41.0     41.0     28.1      wt_kgs = [wt*.4535 for wt in wt_lbs]**

输出详细信息:

  • 完整的时间曲线以 14.6 微秒为单位完成(参考输出的第一行)

生成的表格有 6 列:

  • 第 1 列(行号)-代码的行号(注意,第 1 行被故意从输出中省略,因为它只是函数定义语句)
  • 第 2 列(命中数)—线路被调用的次数
  • 第 3 列(时间)—花费在代码行上的时间单位数(每个时间单位为 14.6 微秒)
  • 第 4 列(每次命中)-第 3 列除以第 2 列
  • 第 5 列(%Time) —在花费的总时间中,在特定代码行上花费的时间百分比是多少
  • 第 6 列(内容)-代码行的内容

人们可以清楚地注意到,从米到厘米的高度转换几乎花费了总时间的 72%。

结束语

有了我们可以支配的每行代码的执行时间,我们就可以部署策略来提高代码的效率。在接下来的 3 个教程中,我们将分享一些最佳实践来帮助你提高代码的效率。

我希望这个【T42 你知道吗】系列的教程是有益的,你学到了一些新的东西。

会在以后的教程中尝试并带来更多有趣的话题。在此之前:

快乐学习!!!!

Pandas 中应用()和转换()的区别

原文:https://towardsdatascience.com/difference-between-apply-and-transform-in-pandas-242e5cf32705?source=collection_archive---------4-----------------------

一些最有用的熊猫把戏

照片由翻滚 926 上的退溅

Pandas 是一个令人惊叹的库,它包含大量用于操作数据的内置函数。在寻找应用自定义函数时,您可能会对以下两种选择感到困惑:

  • apply(*func, axis=0*):沿数据帧的一个轴调用函数*func*。它返回沿给定轴应用*func*的结果。
  • transform(*func, axis=0*):调用 self 上的函数*func*,产生一个带有变换值的数据帧。它返回一个与 自身 长度相同的 DataFrame。

他们采用相同的论点*func*axis。两者都沿着给定数据帧的轴调用*func*。那么区别在哪里呢?你如何选择一个而不是另一个?

在本文中,我们将介绍以下用法,并讨论它们的区别:

  1. 操纵值
  2. 结合groupby()结果

源代码请查看我的 Github repo

如果您不熟悉以下文章,请查看它们:

[## 何时使用 Pandas transform()函数

一些最有用的熊猫把戏

towardsdatascience.com](/when-to-use-pandas-transform-function-df8861aa0dcf) [## 熊猫应用简介,应用地图和地图

一个直观的熊猫教程,介绍如何使用 apply()和 applymap()应用一个函数,以及如何替换值…

towardsdatascience.com](/introduction-to-pandas-apply-applymap-and-map-5d3e044e93ff)

1.操纵值

apply()transform()都可以用来操作数值。让我们借助一些例子来看看它们是如何工作的。

df = pd.DataFrame({'A': [1,2,3], 'B': [10,20,30] })**def plus_10(x):
    return x+10**

对于整个数据帧

apply()transform()都可用于操作整个数据帧。

df.**apply(plus_10)**

df.**transform(plus_10)**

apply()transform()都支持 lambda 表达式,下面是 lambda 的等价表达式:

df.**apply(lambda x: x+10)**df.**transform(lambda x: x+10)**

为单列

apply()transform()都可以用于操作单个列

df['B_ap'] = df['B'].**apply(plus_10)**
# The lambda equivalent
df['B_ap'] = df['B'].**apply(lambda x: x+10)**df['B_tr'] = df['B'].**transform(plus_10)**
# The lambda equivalent
df['B_tr'] = df['B'].**transform(lambda x: x+10)**

有什么区别?

以下是三个主要区别

  • transform()(1)使用函数、字符串函数、函数列表和字典。但是,apply()只允许与函数一起使用。
  • transform()不能产生聚合结果。
  • (3) apply()一次处理多个系列。但是,transform()一次只允许处理单个系列。

让我们借助一些例子来看看它们。

(1) **transform()** 作用于函数,一个字符串函数,一个函数列表,一个字典。但是, **apply()** 只允许有一个功能。

对于transform(),我们可以将任何有效的熊猫字符串函数传递给func

df.transform(**'sqrt'**)

func可以是函数列表,例如 NumPy 中的sqrtexp:

df.transform(**[np.sqrt, np.exp]**)

func可以是轴标签的字典- >函数。例如

df.transform(**{
    'A': np.sqrt,
    'B': np.exp,
}**)

(2) **transform()** 不能产生聚合结果。

我们可以使用apply()来产生聚合结果,例如,总和

df.**apply(lambda x:x.sum())**A     6
B    60
dtype: int64

然而,当我们试图用transform()做同样的事情时,我们将得到一个值错误。我们遇到这个问题是因为transform()的输出必须是与 自身 长度相同的数据帧。

df.**transform(lambda x:x.sum())**

(3) **apply()** 一次处理多个系列的作品。但是, **transform()** 一次只允许处理单个系列。

为了演示这一点,让我们创建一个一次处理 2 个系列的函数。

def subtract_two(x):
    return x['B'] - x['A']

apply()subtract_twoaxis=1完美配合

df.apply(**subtract_two, axis=1**)0     9
1    18
2    27
dtype: int64

然而,当我们试图用transform()做同样的事情时,我们得到了一个值错误。这是因为transform() 一次只允许处理一个系列。

# Getting error when trying the same with transform
df.**transform(subtract_two, axis=1)**

当使用 lambda 表达式时,我们会得到相同的结果

# It is working
df.apply(**lambda x: x['B'] - x['A'], axis=1**)# Getting same error
df.transform(**lambda x: x['B'] - x['A'], axis=1**)

2.结合groupby()

apply()transform()都可以与groupby()配合使用。而且事实上,这是transform()最引人注目的用法之一。更多详情,请查看以下文章中的“结合 **groupby()** 结果”:

[## 何时使用 Pandas transform()函数

一些最有用的熊猫把戏

towardsdatascience.com](/when-to-use-pandas-transform-function-df8861aa0dcf)

以下是与groupby()结合使用时的两个区别

  • (1) transform()返回与输入长度相同的数据帧
  • (2) apply()一次处理多个系列。但是,transform()一次只允许处理单个系列。

让我们创建一个数据框架,并通过一些示例来展示其区别

df = pd.DataFrame({
    'key': ['a','b','c'] * 4,
    'A': np.arange(12),
    'B': [1,2,3] * 4,
})

在上例中,可以通过 将数据分成三组。

(1) **transform()** 返回一个与输入长度相同的序列

为了演示这一点,让我们创建一个函数来产生一个聚合结果。

**# Aggregating the sum of the given Series
def group_sum(x):
    return x.sum()**

对于apply(),它为每组返回一个值,输出形状为(num_of_groups, 1)

gr_data_ap = df.groupby('key')['A']**.apply(group_sum)**
gr_data_ap**key
a     9
b    12
c    15**
Name: A, dtype: int64

对于transform(),它返回一个与给定数据帧长度相同的序列,输出形状为(len(df), 1)

gr_data_tr = df.groupby('key')['A']**.transform(group_sum)**
gr_data_tr**0     9
1    12
2    15
3     9
4    12
5    15
6     9
7    12
8    15**
Name: A, dtype: int64

(2) **apply()** 一次处理多个系列。但是 **transform()** 一次只允许处理单个系列。

这和我们在 1 中提到的区别是一样的。操作值,我们只是不需要在一个groupby()结果上指定参数axis

为了演示这一点,让我们创建一个一次处理 2 个系列的函数。

def subtract_two(x):
    return x['B'] - x['A']

apply()一次处理多个系列。

df.groupby('key')**.apply(subtract_two)****key   
a    0    1
     3   -2
     6   -5
b    1    1
     4   -2
     7   -5
c    2    1
     5   -2
     8   -5**
dtype: int64

然而,当我们用transform()尝试同样的方法时,我们得到了一个键错误

df.groupby('key')**.transform(subtract_two)**

摘要

最后,这里是一个总结

对于操作值,apply()transform()都可以用来操作整个数据帧或任何特定的列。但是有三个不同之处

  1. transform()可以接受一个函数、一个字符串函数、一个函数列表和一个字典。然而,apply()只允许一个函数。
  2. transform()无法产生汇总结果
  3. apply()一次处理多个系列。但是,transform()一次只允许处理单个系列。

用于与groupby()一起工作

  1. transform()返回与输入长度相同的序列
  2. apply()一次处理多个系列。但是,transform()一次只允许处理单个系列。

好了

感谢阅读。

请在我的 Github 上查看笔记本的源代码。

如果你对机器学习的实用方面感兴趣,请继续关注。

你可能会对我的其他一些熊猫文章感兴趣:

更多可以从我的 Github 中找到

概率和统计的区别

原文:https://towardsdatascience.com/difference-between-probability-and-statistics-d69db0ff3f71?source=collection_archive---------37-----------------------

让我们了解概率和统计是如何联系在一起的,哪一个更重要

照片由 Riho KrollUnsplash 上拍摄

正如著名数学家卡尔·皮尔逊所说:

"统计学是科学的语法。"

统计学是几乎所有技术进步的核心,在从生物统计学、金融、经济学到机器学习等广泛领域都有应用。

话虽如此,我们会发现没有任何一本统计学书籍不谈论概率的概念。事实上,它们是密切相关的数学分支,相互交织在一起。

在本文中,我们将了解两者的区别。然后,我们将以一个普遍存在的问题作为结束,这个问题是我们所有人都可能在某个时候面临的——从哪里开始?

概率:

概率是一个前瞻性的过程。它有助于预测下一个输出,因为我们知道数据生成模型的分布是一个随机过程

我们要计算 P(Y |θ);其中 Y 是结果,θ是定义随机过程的参数(可能是掷硬币、掷骰子等)。

有两个学派来解释概率:

1)频数主义者:主要是通过多次重复实验来计算某种结果出现的相对频率

让我们举一个公平掷硬币的例子。投掷硬币 10 次可能会显示 7 个“正面”,转换成 0.7 的概率,但是,如果我们重复这个实验足够长的时间,那么“正面”的频率计数就等于反面的频率计数,表明概率为 0.5。

2)贝叶斯:它主要讲的是

a)在看到新数据之前的信任程度,在术语“先验”中捕获

b)来自观测数据的更新信息,包含在由 L(θ|Y)表示的“可能性”中。

然后,通过取先验与似然性的乘积来更新后验概率:

让我们了解更多关于可能性的知识:由于现实生活中的过程是高度随机和复杂的,我们通常对它们的分布没有一个合理的概念,因此我们需要对θ进行更好的估计,以便能够更好地预测 P(Y/ θ)。

简而言之,我们需要估计最能描述生成给定数据的分布的θ。这就是统计进入图片的地方——计算可能性函数。

统计:

这是一个回顾过去的过程。它从潜在的随机过程中提取模式,以提出一个可能的量化模型来解释数据行为。

请注意,我们不一定知道定义潜在随机过程的所有特征。

统计模型从有限的可能值θ(称为模型空间θ)以及观察到的数据开始,以推断哪个θ会产生该数据。θ可以是定义概率分布的任何参数,如均值或方差。

统计建模也有助于我们量化推断分布或参数的误差。随着从相同的实验中收集更多的数据,估计的参数越来越接近真实的θ。也就是说,估计值周围的误差缩小了,我们对自己的估计更有信心了。这被称为置信区间。

现在,你会想这个错误是从哪里产生的,有多少数据足以推断出真正的参数。

这是因为我们只能研究数据样本,而不是整个人口,因为各种原因,比如数据的可用性,获取数据的成本等等。如果通过重复实验,我们可以使样本空间如此接近总体,我们就可以将样本参数估计的误差降低到 0,从而得到真实的参数。

概率与统计的区别——举例说明

例一:

概率:对于一个药物试验,整个人群以 1:10 的比例分为两组——A 组和 B 组,P(A) = 0.7,P(B) = 0.5,其中 P =药物作用成功的概率。从人群中随机抽取一名患者,成功治疗的概率是多少?

统计:现在,如果我们不知道真实人群的构成,并且观察从人群中随机选择的 1000 名患者中 700 名成功接受药物治疗的患者的数据。关于人口分布我们能推断出什么?

例 2:

概率:如果我们连续抛 3 次无偏的硬币,得到 HHT 的概率是多少?

统计:如果我们观察 3 次连续掷硬币的 HHT 结果,二项分布的参数 p 的估计值是多少?

概率和统计孰先孰后?

统计学致力于收集大量数据,以便能够产生洞察力并推动决策制定。统计学只包括但不限于概率模型的研究。

关于 StackOverflow 的讨论很多,但是,我想得出的结论是,理解概率的基础,足以理解统计学的中心法则,这是一个迭代的过程。

在这个过程中,当我们开始理解统计学和概率模型背后的概念时,我们不再受先学习什么的教育学的限制。

感谢阅读!!!

Python 中 type()和 isinstance()的区别

原文:https://towardsdatascience.com/difference-between-type-and-isinstance-in-python-47fae6fbb068?source=collection_archive---------22-----------------------

Python 初学者

Python 中的实例是什么?

克里斯汀娜·戈塔迪在 Unsplash 上的照片

上次我们谈到了 Python 中的 4 个内省工具,每个初学者都应该使用。如果你还没有读过这篇文章,可以看看下面这篇文章。最先引入的两个函数是type()isinstance()

[## Python 中 4 个易于使用的自省函数

如何在运行时检查对象

towardsdatascience.com](/4-easy-to-use-introspection-functions-in-python-49bd5ee3a2e8)

这里简单回顾一下。

Python 中的每个对象都有一个数据类型,一个内置的或者定制的。可以是整数int,字符串str,T21【NumPy】数组等..也可以是自定义类的对象。

给定某个对象objtype(obj)返回该对象的数据类型。如果对象是dtype的实例,则isintance(obj, dtype)返回True,否则返回False

那么 到底是某个类的一个实例 呢?

子类和继承

重要的事情先来。我们需要理解子类和继承。以下面的代码为例:

我们将类Rectangle(Shape)定义为Shape的子类,将Square(Rectangle)定义为Rectangle的子类。子类 继承其超类的 方法、属性和其他功能。

我们以一种方式定义层次结构,使得类Square的对象继承Rectangle的所有属性和方法。毕竟,正方形是矩形的特例,所有边的长度都相同。

Edvard Alexander lvaagUnsplash 上拍摄的照片

如果我们用length=5创建一个Square对象a,我们可以调用get_area()方法来计算它的面积。注意,这个方法没有在Square类中显式定义,而是在它的超类中指定,例如Rectangle

a = Square(5)
a.get_area() # 25

因此我们说Square(Rectangle) 从它的超类Rectangle继承了 方法get_area()

情况

现在你应该对子类和继承有了基本的了解。但是和type()isinstance()有什么关系呢?考虑以下关于方形物体a的陈述。这两个语句会返回什么?两个True?两个False

type(a) == Rectangle
isinstance(a, Rectangle)

首先,我们肯定地知道type(a)返回Square。并且类别Square不等于类别Rectangle。所以第一条语句返回False

Square == Rectangle # returns False

但是,第二条语句会返回True!因为SquareRectangle子类,所以对象aRectangle实例

子类的实例也是基类的实例。

梁杰森Unsplash 上的照片

外卖

综上所述,isinstance()考虑了继承,但是使用 equality ( ==)语句检查对象的type()却没有。

一个子类不等于它的基类。

感谢阅读!你可以注册我的时事通讯来接收我的新文章的更新。如果您对提高 Python 技能感兴趣,以下文章可能会有所帮助:

[## 我希望我能早点知道的 5 个 Python 特性

超越 lambda、map 和 filter 的 Python 技巧

towardsdatascience.com](/5-python-features-i-wish-i-had-known-earlier-bc16e4a13bf4) [## Python 3.8 中针对 Python 新手的 6 项新特性

请做好准备,因为 Python 2 不再受支持

towardsdatascience.com](/6-new-features-in-python-3-8-for-python-newbies-dc2e7b804acc)

机器学习和常规代码的区别

原文:https://towardsdatascience.com/differences-between-machine-learning-and-regular-code-9edd0e54ea58?source=collection_archive---------43-----------------------

机器会写和人类类似的代码吗?

Unsplash 上由凯·多尔纳拍摄的照片

机器学习(ML)和传统软件工程是生产软件的两种互补方法。学习过的算法和手工制作的算法有什么不同?理解差异有助于更好地发现这两种方法的机会。

下表总结了主要差异。本文的其余部分将更详细地讨论这些差异。

我所说的学习算法是指推荐引擎、欺诈检测器或其他从数据中学习的程序。我只关注实际的预测算法,并从定义中排除 API、参数拟合和其他支持代码。传统编程是指由软件工程师编写的应用程序。

建立有学问的和传统的程序

当然,在 CPU 指令的最基本层面上,学习算法和传统算法之间没有区别。一个习得的算法由循环、if-else 子句和乘法组成,就像人类程序员编写的程序一样。这种差异表现在程序是如何产生的。

在传统的软件开发方法中,软件工程师坐下来,一边喝着拿铁咖啡一边思考任务,将问题分成小块,最后写下解决每个子问题的代码。相反,学习算法是自动搜索的结果。优化方法的任务是找到最小化训练数据上的误差测量的算法。

这是否意味着机器学习和传统编程是开发类似应用程序的两种可选方式?不要!他们擅长不同的问题领域。ML 解决了复杂但狭窄的任务,在这些任务中,收集大量地面真实训练数据不是问题。图像和语音识别是突出的例子。另一方面,传统的软件工程更好地应用于广泛的问题,这些问题不能简单地简化为最大化的适应度函数。具有产品目录、购物车和结账功能的移动电子商务应用程序是传统方法的主要候选。

构造和应用领域的这些差异甚至在程序代码中也能体现出来。

代码的形状

学习算法的代码是严格结构化的。同时,它包括可以在学习期间调整的参数,以实现所需的行为。这类似于人类大脑如何进化来处理语言。儿童大脑的胚胎发育建立了神经元结构,赋予儿童学习任何语言的灵活性。这种灵活性是由于可调节的神经元连接强度和其他因素,当成长中的儿童学会用父母的语言交流时,这些因素会重新配置。

以类似的方式,学习算法的形状遵循预先制定的模板,通常是相对简单的数学运算块的统一链。例如,决策树是将数据分成组的 if-else 子句序列。下图显示了一个示例。同样,人工神经网络是组合输入特征的分层变换链。这种操作的链将输入(例如,图像)转换成期望的输出(例如,图像包含猫的概率)。学习算法通常由一种或几种不同类型的块组成。

银行是否应该接受贷款申请的决策树。树的结构是统一的,由以链的形式组织的 if-else 块组成。if 条件(变量和数字阈值)是可学习的参数。图片作者。

这些构建模块的形状是固定的(就像人脑中语言中心的结构),但它们包含可调参数(就像大脑中的神经连接强度):决策树中的变量和阈值以及神经网络中的连接权重。机器学习中的“学习”是指优化这些参数,使输出在训练数据上尽可能接近地匹配预期目标。

这种严格一致的结构是实现优化所必需的。我们只知道如何有效地优化某些类别的数学结构。因此,只有当程序由这种易于优化的操作块组成时,机器学习才能发现好的解决方案。幸运的是(有点令人惊讶的是),研究人员发现许多问题,尤其是图像和语言处理中的问题,确实符合这种所需的形状。

手工代码并不局限于遵循与 ML 代码相似的高度规则的形状。人类程序员可以编写任何需要的业务逻辑。开发人员倾向于分层组织源代码,每一层都有不同的功能。下图以一个游戏中的一些套路为例。主循环渲染一帧,处理声音效果,轮询游戏控制器等等。这些阶段中的每一个都调用它们自己的专用子程序。渲染包括计算对象可见性、纹理等等。与高度统一的学习程序相比,传统程序的功能更加多样化。

作为传统程序示例的游戏引擎架构。功能是像学习算法一样分层次组织的,但是这里每个分支和层都包含在别处找不到的专用子程序。图片作者。

人类开发人员的目标是编写他们的同伴可以理解的代码。这意味着,例如,将代码分割成小而连贯的功能,在大部分独立的模块之间定义清晰的边界,并尽量减少理解一段代码时需要记住的概念数量。一个好的传统源代码读起来就像是一个其他开发人员可以理解的故事。

然而,习得的算法不一定容易理解。没有人类可以理解的主函数或子程序作为研究的自然起点。相反,人们需要立刻理解抽象数学变换的整个错综复杂的链条。随着问题规模的增长,这项任务很快超出了人类的能力。因此,我们需要可解释的 AI 方法来解释由黑盒 ML 算法做出的决策。

外部依赖性

传统程序定期与外部世界交互并保持状态。它们调用库来执行操作和访问数据库中的数据。

学习到的算法通常是独立的,没有副作用。他们很少在预测之间保持任何状态(尽管随着对像神经图灵机这样的主题越来越感兴趣,这正在慢慢形成链条)。

展望未来

在未来,传统课程和学习课程之间的区别可能会开始变得模糊。通过优化方法的进步或仅仅因为原始计算能力的增长,机器学习可能会获得更大的灵活性。另一方面,可微分编程承诺让手工代码更易于优化和学习。

方差差异

原文:https://towardsdatascience.com/differences-in-variance-an-unappreciated-source-of-insight-in-our-data-f9cb0de520c0?source=collection_archive---------49-----------------------

我们数据中一个不受重视的洞察力来源

数据人喜欢平均值。他们喜欢平均值,他们喜欢中间值。平均而言,哪些国家最幸福?患有罕见疾病的人的平均寿命是多少?一个国家的收入中位数是多少?订婚戒指的平均价格是多少?

均值和中位数很棒,因为它们告诉我们数据的总体情况。如果你的数据是正态分布的(通常不是正态分布的),那么平均值会告诉你大部分你需要知道的东西。因此,我们的大多数统计测试,如 t-test、ANOVA 等,都是为了查看平均值,以及它们在不同组之间的差异。但是还有另一个变量,就在我们面前,却没有得到同样多的爱:方差。它有很多东西可以教给我们!

群体差异的两种方式

这里有两组人,给定一些虚构的测试。我绘制了它们的分布图:

我只是在 R 中生成了一些随机的、虚假的数据,并用 ggplot 包绘制出来(图片由作者提供)

我们看到各组的均值不同。这种差异可以通过 t 检验或回归分析清楚地发现。但是还有另一种方式可以区分不同的群体:

作者图片

这里,组 1 比组 2 具有更宽的分布。它有更大的方差。t-检验或回归不能捕捉到这一点;他们只会告诉你平均而言,这些群体没有什么不同。但是正如你所看到的,这并不意味着这些群体没有什么不同!下面我将向您展示如何测试这一点。

你可能认为你以前没有遇到过这个概念。但是你可能听说过 GINI 系数,对吧?它衡量一个社会的财富不平等。概念大致相同:一些社会可能拥有相同的平均财富,但在一个社会中,财富可能分配不均。一个所有人工资完全相同的社会,其方差终究是 0!

真实数据的含义:性别和智商的例子

对方差差异进行研究的少数领域之一是智商领域。研究普遍发现,虽然男女之间的智商差异很小或不存在,但男性的智商差异始终高于女性。下面我用数据来说明这一点,这些数据是我用一项超过 80,000 名苏格兰青少年的研究的真实方差模拟的(多么大的数据集啊!).我还给出了 R 代码,这样你就可以自己做了:

set.seed(140293)male = rnorm(5000,100,14.9)
female = rnorm(5000,100,14.1)

我们设置了一个特定的种子,这样你就能得到和我一样的随机数,结果也能得到相同的随机生成的数据。然后,我创建了两组智商数据,5000 名男性和 5000 名女性,均以 100 为均值呈正态分布。男性数据的标准偏差为 14.9,女性为 14.1。记住标准差是方差的平方根。

接下来,我们将数据放入一个tible中,并对其进行重新格式化,以便于使用,使用 tidyverse R 包很容易做到这一点:

install.packages('tidyverse')
library(tidyverse)IQ_data = tibble(male, female) %>% 
  pivot_longer(cols = c(male, female), 
               names_to = 'sex',
               values_to = 'IQ') %>%
  mutate(sex = factor(sex))

然后我们做一个频率多边形来比较男性和女性的分布:

IQ_data %>% ggplot( aes(x = IQ, colour = sex) )+
  geom_freqpoly(size = 1)

作者图片

女性分布的峰值高于男性,表明接近 100 分的女性较多。男性分布在尾部略高,表明有更多的男性智商得分极高和极低。但是差别似乎很小。

t.test(IQ ~ sex, IQ_data)

进行 t 检验,平均智商没有性别差异( p = 0.38)。然而,这些组可能在尾部有所不同。我们来调查一下。

尾比

我们可以通过取高于或低于某个阈值的雄性与雌性的比例来计算出一个尾比。我们将所有智商为 130 或更高(一般认为非常聪明)的个体,统计男女数量。对于智商 70 或更低的人(一般认为不聪明),我们也是这样做的。

IQ_data %>% filter(IQ >= 130) %>% group_by(sex) %>% countIQ_data %>% filter(IQ <= 70) %>% group_by(sex) %>% count

运行代码,你会看到在我们的数据集中有 95 个非常聪明的男性和 66 个非常聪明的女性(59%是男性)。这使得男女比例为 1.44:1。
同样,我们的数据中有 104 名非常不聪明的男性和 84 名女性(55%为男性),男女比例为 1.24。因此,尽管男性和女性平均来说没有什么不同,但在最聪明和最不聪明的群体中,男性更多。

更进一步:Levene 的测试

比率都很好,但我们可能想知道差异是否有统计学意义。对此有一些测试,但最常见的是方差相等的 Levene 测试,这是我从 car R 包中得到的:

install.packages('car')
library(car)
leveneTest( IQ ~ sex, IQ_data)

我们得到的 F 值为 5.54,显著性 p 值为 0.02,证实了我们的结果,即男性的智商比女性高。

含义

通过比较方差,我们还能回答哪些问题?

  • 如果一种药物增加了患者的平均健康水平,但也增加了服药者之间的健康差异,这可能表明该药物对一些人有帮助,但对另一些人没有帮助。
  • 也许你有两种产品在许多商店出售。平均而言,每家商店销售的产品数量可能相同,但一种产品的销售数量差异可能更大,这表明一些商店销售很多,而其他商店销售很少。这可能对理解你的客户和在哪里销售你的产品有所暗示。
  • 不影响一个国家人民平均幸福的政策,如果能减少幸福的方差,可能对社会仍然是好的,我们可以称之为“幸福不平等”。
  • 通常,特别低的方差可能是好产品的标志。罗里·萨瑟兰喜欢争辩说,最好的卫星导航是最小化旅行时间差异的卫星导航,不一定是能找到最短平均旅行时间的卫星导航。当你去机场赶飞机时,你不想要一条可能需要 30 分钟到 2 小时的路线,即使它是平均最快的路线。
  • 在调查妇女和少数民族的代表性时。例如,在所有公司中,女性在高层管理职位中的平均比例可能是 50%,有些人会将其解释为实现了性别平等。然而,如果公司之间的这一比例差异非常大,这可能表明许多公司的女性经理远远超过 50%,而其他公司的女性经理可能会少得多。

结论

我们在这里已经看到,不同的组可以有不同的方差和均值,这可以在我们的数据中产生新的见解。根据我的经验,这确实是一个被忽视的领域,所以很酷的结果可能相对容易找到。它也不需要比你已经有的更多的数据,这很好。如果你发现任何有趣的方差差异,请务必在 Twitter 上给我加标签,这样我就可以看到它们了。我很期待你的发现!

不同的色彩空间作为 CNN 的输入

原文:https://towardsdatascience.com/different-colorspaces-as-inputs-to-cnns-406ae62d1bd6?source=collection_archive---------28-----------------------

通过自定义数据生成器找出在 CNN 中使用不同的色彩空间是否会导致更好的结果,并最终创建一个集合模型。

Unsplash 上由鲁维姆·诺加拍摄的照片

我刚刚在另一篇文章中发完一篇牢骚,我忘记了 OpenCV 使用 BGR 格式,CNN 的输出是以 RGB 格式检查的,这浪费了我很多时间,当我有了一个想法,如果将不同色彩空间的图像传递给 CNN 会怎么样,它会如何影响模型?这就是我们要在这篇文章中发现的。

目录

  1. 要求

2.使用的色彩空间

3.代码和结果

  • 导入和加载数据集
  • 创建图像数据生成器
  • 正常 CNN 的结果
  • 迁移学习的结果
  • 创建集合模型

如果你对这个过程不感兴趣,只想知道结果,你可以直接跳到正常的 CNN 部分的结果。

要求

如果你想编码,那么你需要 Tensorflow 和 OpenCV。你可以使用下面显示的代码进行 pip 安装,或者像我一样使用 Google Colab,不需要安装免费的 GPU。

pip install tensorflow
pip install opencv-python

使用的色彩空间

照片由埃菲社Unsplash 拍摄

决定使用哪种色彩空间是一项相当简单的任务。我只是打开了 OpenCV 的文档,并选择了所有可以转换的独特文档。我会把它们列出来,并提供额外的阅读链接,如果有人有兴趣了解更多的话。

代码和结果

在开始之前,我想澄清一下,我的目的不是创造一个非常高科技的 CNN 架构来获得最好的准确性,而是比较所有的色彩空间。

导入和加载数据集

选择的数据集是猫对狗的数据集。首先,我们开始导入所有需要的库。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, Activation, BatchNormalization
import os
import numpy as np
import matplotlib.pyplot as plt
import re
import random
import cv2

下一步是加载数据集并存储所有文件名,然后将这些文件名传递给图像数据生成器,并定义将使用的所有常数。

创建图像数据生成器

创建一个普通的定制数据生成器,带有一个额外的 colorspace 参数,它定义了要转换到哪个 colorspace。cv2.cvtColor用于那个目的。除了 HSV 的色调矩阵被除以 180 之外,所有的图像都被调整到它们需要的尺寸,并通过除以 255 进行归一化。为图像和标签创建 NumPy 数组,并生成它们。

使用 Conv2D、max pooling、batch normalization 和 dropout 层创建基本的 CNN,这些层最终被展平,密集层给出具有 sigmoid 激活函数的输出。该模型是使用 Adam 编译的。如果你想创建更好的 CNN,你可以参考我之前的文章。只要用所需的值来拟合模型,我们就完成了。

正常 CNN 的结果

如果我们观察训练精度和损失,除 HSV 之外的所有色彩空间给出了非常相似的更好的结果。然而,也可以观察到我们的模型是过度拟合的,所以 HSV 不能直接被怀疑。此外,对于验证集,HSV 中的图像可以比它们的对应图像学习得更快,但是对于大量的时期,结果是均匀的,并且选择特定的一个是非常困难的。

迁移学习的结果

如上所述,迁移学习模型非常简单。使用预训练的 MobileNetV2,其层被设置为不可训练的,并且全局平均池层之后是用于输出的密集层。

迁移学习中不可训练层的结果

只有 BGR 和 XYZ 色彩空间给出了好的结果,而 HSV 给出了最差的结果。但是为什么会这样呢?我们使用预先训练好的层,这些层习惯于看到 RGB 风格的输入,XYZ 与 RGB 非常相似,所以只有它们给出了好的结果,而 HSV 是一个圆柱形系统,在相似性方面离 RGB 最远,因此给出了最差的结果。

因此,我们通过改变base_model.trainable = True使整个预训练网络可训练,让我们看看现在会发生什么。

所有可训练层的迁移学习的结果。

即使现在 BGR 和 XYZ 色彩空间从一开始就表现很好,然而,他们被所有其他色彩空间赶上了,除了 HSV,它再次表现最差。除了 HSV 之外,几乎没有太多颜色空间可供选择,所以让我们创建一个集合模型,看看它是否能提高性能。

创建集合模型

如果我们可以实现性能的提高,那么我们也将知道不同的色彩空间得到不同的分类为正确或错误的图像,这将意味着改变色彩空间对模型有一些影响。我们将创建一个非常简单的集合模型,取所有模型的预测概率的平均值,将其转换为整数,并根据真实标签对其进行评估。

accuracy for bgr  : 0.9596773982048035
accuracy for ycrcb  : 0.9536290168762207
accuracy for lab  : 0.9415322542190552
accuracy for luv  : 0.9546371102333069
accuracy for xyz  : 0.9546371102333069
Ensemble accuracy: 0.966

因此,可以说,改变色彩空间可能会也可能不会提高准确性,特别是如果你是随机检查,并且没有分配随机种子来重复结果,因为有很多起伏。然而,如果你想要更精确一点,你可以尝试其他色彩空间,甚至是整体模型。

如果你想玩的话,你可以在这里和这里找到 gist 链接到 colab 文件的链接。

张量流中不同类型的归一化

原文:https://towardsdatascience.com/different-types-of-normalization-in-tensorflow-dac60396efb0?source=collection_archive---------22-----------------------

了解 Tensorflow 中的批次、组、实例、层和权重标准化,以及解释和实现。

来源

当规范化被引入深度学习时,它是下一件大事,并大大提高了性能。对你的模型来说,做得正确是一个至关重要的因素。曾经有过这样的疑问,不管批处理规范化是什么,它是如何提高性能的。还有什么替代品吗?如果你像我一样有这些问题,但从来没有烦恼,只是为了在你的模型中使用它,这篇文章将澄清它。

目录

  • 批量标准化
  • 群体规范化
  • 实例规范化
  • 图层规范化
  • 权重标准化
  • Tensorflow 中的实现

批量标准化

Kaspars UpmanisUnsplash 上拍摄的照片

最广泛使用的技术为表演带来奇迹。它是做什么的?嗯,批量标准化是一种标准化方法,通过小批量标准化网络中的激活。它计算小批量中每个特征的平均值和方差。然后减去平均值,并将该特征除以其最小批量标准偏差。它还有两个额外的可学习参数,激活的平均值和幅度。这些用于避免与零均值和单位标准偏差相关的问题。

所有这些看起来很简单,但是为什么会对社区产生如此大的影响?它是如何做到的?答案还没有完全想出来。有些人说它改善了内部协变量的转移,而有些人不同意。但我们确实知道,它使损失表面更平滑,一层的激活可以独立于其他层进行控制,并防止重量到处乱飞。

既然如此,为什么我们还需要别人呢?当批量较小时,小批量的均值/方差可能远离全局均值/方差。这引入了大量噪声。如果批量大小为 1,则不能应用批量标准化,并且它在 RNNs 中不起作用。

群体规范化

照片由哈德逊·辛慈Unsplash 上拍摄

它计算每个训练示例的通道组的平均值和标准偏差。所以它基本上与批量大小无关。在 ImageNet 数据集上,组规范化与批处理大小为 32 的批处理规范化的性能相当,并且在较小的批处理大小上优于它。当图像分辨率很高并且由于内存限制而不能使用大批量时,组归一化是一种非常有效的技术。

对于图像识别任务,实例规范化和层规范化(我们将在后面讨论)都不如批量规范化,但不是组规范化。层规范化考虑所有通道,而实例规范化只考虑导致其崩溃的单个通道。所有的通道并不同等重要,就像图像的中心到它的边缘,同时也不是完全相互独立的。因此,从技术上来说,组规范化结合了两者的优点,并消除了它们的缺点。

实例规范化

照片由埃里克·沃德Unsplash 拍摄

如前所述,它计算每个训练图像的每个通道的平均值/方差。它用于风格转换应用,也建议作为 GANs 批量标准化的替代。

图层规范化

照片由免费使用声音Unsplash

批次归一化是对各批次维度的输入进行归一化,而图层归一化是对各要素地图的输入进行归一化。再次像组和实例标准化一样,它一次对单个图像起作用,即它的平均值/方差是独立于其他示例计算的。实验结果表明,该算法在 RNNs 上表现良好。

权重标准化

Kelly SikkemaUnsplash 上拍摄的照片

我认为描述它的最好方式是引用它的论文摘要。

通过以这种方式重新参数化权重,我们改进了优化问题的条件,并且我们加速了随机梯度下降的收敛。我们的重新参数化受批处理规范化的启发,但不会在小批处理中的示例之间引入任何依赖关系。这意味着我们的方法也可以成功地应用于循环模型,如 LSTMs,以及噪声敏感的应用,如深度强化学习或生成模型,批量归一化不太适合这些应用。尽管我们的方法简单得多,但它仍然大大提高了整批规范化的速度。此外,我们的方法的计算开销更低,允许在相同的时间内采取更多的优化步骤。

Tensorflow 中的实现

如果我们不能实现它,理解理论又有什么用呢?所以我们来看看如何在 Tensorflow 中实现它们。使用稳定 Tensorflow 只能实现批量规范化。对于其他人,我们需要安装 Tensorflow 附加组件。

pip install -q  --no-deps tensorflow-addons~=0.7

让我们创建一个模型,并添加这些不同的规范化层。

import tensorflow as tf
import tensorflow_addons as tfa**#Batch Normalization** model.add(tf.keras.layers.BatchNormalization())**#Group Normalization** model.add(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'))
model.add(tfa.layers.GroupNormalization(groups=8, axis=3))**#Instance Normalization** model.add(tfa.layers.InstanceNormalization(axis=3, center=True, scale=True, beta_initializer="random_uniform", gamma_initializer="random_uniform"))**#Layer Normalization** model.add(tf.keras.layers.LayerNormalization(axis=1 , center=True , scale=True))**#Weight Normalization** model.add(tfa.layers.WeightNormalization(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu')))

当在组规范化中指定组的数量时,确保其值是当时存在的特征映射数量的完美除数。在上面的代码中是 32,所以它的约数可以用来表示要分成的组的数量。

现在我们知道如何使用它们,为什么不试一试呢?我们将使用具有简单网络架构的 MNIST 数据集。

model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'))
#ADD a normalization layer here
model.add(tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation='relu'))
#ADD a normalization layer here
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(128, activation='relu'))
model.add(tf.keras.layers.Dropout(0.2))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
model.compile(loss=tf.keras.losses.categorical_crossentropy,
optimizer='adam', metrics=['accuracy'])

我用 5 种不同的批量大小,即 128、64、32、16 和 8,尝试了所有的标准化。结果如下所示。

培训结果

测试精度

由于数据集偏差和运气等差异,我不会深入研究这些结果!再训练一次,我们会看到不同的结果。

如果你想更详细地了解这些或者发现更多的标准化技术,你可以参考这篇文章,它对我写这篇文章有很大的帮助。如果您想进一步改善您的网络,您可以阅读以下内容:

[## 超越 Tensorflow 2 中的标准 CNN

使用复杂的架构生成更深层次的模型,并了解不同的层,从而使模型更好。

towardsdatascience.com](/beyond-the-standard-cnn-in-tensorflow-2-a7562d25ca2d)

不同类型的时间序列分解

原文:https://towardsdatascience.com/different-types-of-time-series-decomposition-396c09f92693?source=collection_archive---------4-----------------------

以及为什么你可能用错了。

照片由在 Unsplash 上拍摄

介绍

布拉哈姆·马斯洛写道,“我想,如果你唯一的工具是一把锤子,那么把一切都当成钉子是很诱人的”。

这是有抱负的数据科学家在分析时间序列数据时发现自己所处的情况。Python 的 Statsmodels 库中的 seasonal _ decompose 函数就是锤子,每一个时间序列数据都只是另一个钉子。

分解我们的时间序列是提高预测准确性和创造因果洞察力的重要一步。

seasonal_decompose 函数可以用于时间序列分解,但是还有其他更好的方法。而且,作为数据科学家,我们总是希望使用好的东西。

亚伯拉罕·马斯洛写道,“我想,如果你唯一的工具是一把锤子,那么把一切都当成钉子是很有诱惑力的”。

本文有以下目标:

  1. 解释时间序列分解的重要性。
  2. 解释季节性分解函数的问题。
  3. 介绍时间序列分解的替代方法

为什么要分解我们的时间序列数据?

时间序列分解是指我们将时间序列数据分解为以下四个部分的方法:

  1. 趋势[ T
  2. 循环[ C ]
  3. 季节性
  4. 余数[ R

1)趋势

时间序列的趋势是指时间序列移动的大致方向。时间序列可以有正趋势或负趋势,但也可以没有趋势。

例如,美国(和许多发达经济体)的 GDP 增长率没有趋势,因为经济力量使增长率保持相对稳定。

来自世界银行的数据。

2)循环

时间序列数据的周期是指其以不一致的频率上升和下降的趋势。我们经常使用时间序列的周期部分来讨论经济数据中的商业周期。

3)季节性

时间序列的季节性成分与其周期成分相似,除了一个重要的区别:季节性成分指的是以一致的频率上升和下降的数据。

旅游业对季节性因素非常熟悉。一个国家的旅游业在温暖的月份里收入很高,然后当第一次有下雪的迹象时,它的收入慢慢地向悬崖的边缘移动。

4)余数

剩余部分是去除趋势、周期和季节成分后的时间序列数据。是上述成分无法解释的时间序列数据中的随机波动。

在预测时,使用“季节性调整”时间序列是有利的,这只是一个去除了季节性因素的时间序列。这使得预测者可以专注于预测数据的总体趋势。

使用时间序列分解的第二个原因是为了识别季节成分中任何有趣的行为。然后,我们可以研究为什么我们的数据以这种方式移动。

时间序列分解常用方法的问题是

有趣的是,Statsmodels 知道有比通常的季节性分解函数更好的方法来分解时间序列数据。

他们警告我们(强调是我自己的):

这个[季节性 _ 分解]是一个天真的分解。应首选更复杂的方法— Statsmodels 文档

季节性分解使用经典的分解方法,有两种类型:加法和乘法。

加法分解

加法分解认为时间序列数据是其组成部分之和的函数。因此,

其中 Y 是时间序列数据,T 是趋势周期分量,S 是季节分量,R 是余数。

重新排列给了我们,

乘法分解

乘法分解认为时间序列数据是其组成部分的乘积的函数,而不是总和。因此,

重新排列给了我们,

我们通常可以从其变化中识别出一个加法或乘法时间序列。如果季节分量的大小随时间变化,那么这个序列就是乘法序列。否则,该系列是可加的。

图片来自尼古拉·考伦茨

请注意,从 2011 年开始,在加法时间序列中,季节性成分的大小(序列的最大值与红线之间的差值)相对恒定。

然而,在乘法序列中,季节性成分的大小随着时间的增加而增加。

注意:识别一个数列是加法还是乘法比上面的图片显示的要复杂。通常,时间序列的一个部分可能是相加的,而其他部分是相乘的。

例如,你可以合理地拥有一个时间序列,其中

和,因此,

时间序列分解的经典方法有几个问题:

  1. 它使用双边移动平均线来估计趋势周期。因此,前几个观察值和后几个观察值在趋势周期中是不存在的。
  2. 它假设季节成分在整个系列中是恒定的。虽然这对于短期来说可能是一个准确的假设,但是对于长期来说,这种假设就站不住脚了。例如,航空旅行和其他交通方式的创新为许多经济体的旅游业带来了根本性的变化;因此,假设它的季节变化在历史上一直保持稳定是不正确的。
  3. 趋势线过度平滑了数据。因此,它对剧烈波动没有反应。这导致了大的剩余分量。

幸运的是,有解决上述问题的时间序列分解方法。

传统方法的替代方法

X11 分解

X11 分解为所有观察值创建了一个趋势周期。同样,X11 分解允许季节性成分缓慢变化。

我不知道任何实现 X-11 过程的 Python 库。然而,这可以通过 R 的季节性软件包中的 seas 函数相对容易地完成。您也许可以使用 rpy2 库来复制 Python 中的 R 代码。(一旦我找到解决办法,我将在本周晚些时候更新这篇文章)。

seas(data, x11 = "")

让我们比较 X11 分解和经典分解的结果。

在 R 中:

library(forecast)
library(ggplot2)
autoplot(AirPassengers)

国际航空旅客人数时间序列(千人)

因为季节成分随时间增加,所以我们知道应该使用乘法分解。

mfit <- decompose(x = AirPassengers, type = "multiplicative")
autoplot(mfit)

航空乘客数据的乘法分解

请注意,季节性成分是不变的,余数成分有许多大值,趋势线在数据集的开始和结束处缺少一些观察值。

现在,如果我们使用 X11 分解,

fit <- seas(x = AirPassengers, x11 = "")
autoplot(fit)

X11 分解

您应该注意到三件事:

  1. 季节性因素随着时间的推移而增加,从而反映了自 1950 年以来航空业的根本变化。
  2. 余数部分比我们进行经典分解时的情况要小。这是因为我们的趋势线非常适合原始数据。
  3. 趋势线中没有遗漏的观察值。

STL 分解

X11 的一个问题是它只处理月度和季度数据。

另一个原因是它对异常值不够稳健。

因此,在 1990 年,密歇根大学和贝尔实验室的研究人员发表了“STL:基于黄土的季节趋势分解过程”。

与 X11 方法相比,STL 时间序列分解方法具有以下优势:

  1. 它处理任何类型的季节性。
  2. 用户可以控制季节性成分的变化率。
  3. 它对异常值是鲁棒的。

我们可以用 Python 中的 STL 函数实现 STL。

from statsmodels.tsa.seasonal import STL

我们可以通过向 STL 函数中的趋势季节性参数传递一个整数来改变趋势周期和季节性成分的平滑度。默认情况下,季节参数设置为 7(也建议您使用大于或等于 7 的季节平滑器)。

如果未指定趋势值,则 Statsmodels 会使用大于的最小奇整数来计算趋势值

季节性平滑的选择取决于你。整数越大,季节性因素就越“平滑”。这使得数据中较少的变化归因于季节性因素。因此,您必须确定数据中有多少变化可以合理地归因于季节性因素。

STL 方法的创始人建议使用季节性诊断图,然后用不同的平滑值进行试验,以确定哪个值似乎是正确的。不幸的是,Python 中没有这种实现(但是 R 中有)。

用 Python 实现 STL:

from statsmodels.tsa.seasonal import STL
import matplotlib.pyplot as plt
import pandas as pddf = df[:len(df) - 1] # Removes the last row in the data setcolumns = ['Month', 'Passengers']
df.columns = columns
df.head()

df = df.set_index('Month') # Set the index to datetime object.
df = df.asfreq('MS') # Set frequency# Set robust to True to handle outliersres = STL(df, robust = True).fit() 
res.plot()
plt.show()

季节成分的变化比我们使用 X11 分解时变化更快。因为 STL 分解对异常值也是稳健的,所以它对季节分量的估计可能比 X11 的估计更准确。

请随意在 STL 函数中试验季节性参数。请确保您使用的是奇数整数。

如果你想对数据进行季节性调整,那么从原始数据中减去季节性因素。

也就是说,

df['Seasonally Adjusted'] = df['Passengers'] - res.seasonal
df.head()

结论

现在你知道季节分解的其他方法了。与 Statsmodels 的 seasonal_decompose 函数相比,这些替代方法可以更好地估计时间序列的季节性和趋势周期成分。

您还可以使用这些方法获得更准确的预测,并更好地识别数据集中的有趣模式。

文献学

[1] Hyndman,R.J .,& Athanasopoulos,G. (2018) 预测:原则与实践,第二版,OTexts:澳洲墨尔本。OTexts.com/fpp2.

[2]https://www . stats models . org/stable/generated/stats models . TSA . seasonal . seasonal _ decompose . html

[3]安德鲁·萨特克利夫。(1993)“X11 时间序列分解和抽样误差”,澳大利亚统计局:澳大利亚墨尔本。

[4] Cleveland,R.B .,Cleveland W.S .,McRae J.E .,& Terpenning,I. (1990 年)“短期负荷预测:基于黄土的季节性趋势分解程序”,《官方统计期刊》。

[5] Gardner,Dillon r .(2017)《STL 算法讲解:STL 第二部分》

笔记

  1. 趋势项可以是非线性的。还有,有两种趋势:随机的和确定的。我将在以后的文章中讨论这些。
  2. seasonal_decompose 函数还可以估计单边移动平均值;然而,这导致在序列开始时比使用双边方法时有更多的缺失观测值,而在序列结束时没有缺失观测值。

资料组

https://www.kaggle.com/rakannimer/air-passengers

将数据导入 R 的不同方式

原文:https://towardsdatascience.com/different-ways-of-importing-data-into-r-2d234e8e0dec?source=collection_archive---------32-----------------------

如何在 R 中处理不同类型的数据

卡斯帕·卡米尔·鲁宾在 Unsplash 上拍摄的照片

我在 DataCamp 完成了两门课程,这两门课程向我介绍了将数据导入 r 的概念。我想详细讨论一下我在课程中学到的一些方法。让我们开始吧。

数据可以来自许多来源。一些最常见的是

  • 平面文件— CSV、txt、tsv 等
  • 来自 Excel 的数据
  • 数据库— Postgresql、Mysql 等
  • 统计软件——SAS、SPSS、STATA

平面文件

什么是平面文件?

根据维基百科,平面文件数据库是存储在一个名为平面文件的文件中的数据库。记录遵循统一的格式,并且没有用于索引或识别记录之间关系的结构。文件很简单。平面文件可以是纯文本文件或二进制文件。

下面列出的一些包将帮助您在 r 中处理平面文件。

实用工具

默认情况下,当您加载 r。

  • read.table():主功能。读取表格格式的文件并从中创建数据框。它提供了许多参数来对输入的数据进行分类。
  • read.csv():read . table()的包装函数。用于读取逗号分隔(CSV)文件。
  • read.delim():用于读取制表符分隔文件的包装函数。如果文件中的数字使用句点(。)作为小数。
  • read.csv2() : read.csv()和 read.csv2()是相同的。唯一的区别是,它们是根据您在数字中使用句点还是逗号作为小数点来设置的。
  • read.delim2():当文件中的数字使用逗号(,)作为小数时,使用 read.delim2。

read.csv() 功能的输出

专业包

readr

这个包让我们的生活更轻松。它比 utils 包更快、更方便、更高效。我总是倾向于使用这个。

read_r 支持七种文件格式,具有七种功能:

  • [read_csv()](https://readr.tidyverse.org/reference/read_delim.html):逗号分隔(CSV)文件
  • [read_tsv()](https://readr.tidyverse.org/reference/read_delim.html):制表符分隔的文件
  • [read_delim()](https://readr.tidyverse.org/reference/read_delim.html):通用分隔文件
  • [read_fwf()](https://readr.tidyverse.org/reference/read_fwf.html):定宽文件
  • [read_table()](https://readr.tidyverse.org/reference/read_table.html):表格文件,其中各列由空格分隔。
  • [read_log()](https://readr.tidyverse.org/reference/read_log.html):网络日志文件

read_csv() 功能的输出

Tibbles 的 readr 包工作。根据文档,Tibbles 数据帧,但是它们调整了一些旧的行为以使生活变得更简单。打印输出还显示了 read.csv 输出中缺少的列类。

数据表

作者 Matt Dowle & Arun Srinivasan 的 data.table 的关键指标是速度。这个包主要是关于数据操作的,但是它还有一个非常强大的功能,可以将数据读入 R: fread()。

如果你有很大的文件要导入到 R 中,你可以使用 data.table 包。

fread() 功能的输出

Fread()可以自动处理名称。它还可以推断列类型和字段分隔符,而不必指定这些。它是 read.table()的改进版本,速度非常快,更加方便,并增加了更多功能。

擅长

数据分析中最常用的工具是 Microsoft Excel。excel 文件的典型结构包含带有表格数据的不同工作表。

我们需要浏览这些文件,然后从中导入一些数据。r 提供了两个函数来处理这个问题。

  • **excel_sheets()** : 探索不同的工作表

结果是一个简单的字符向量,它返回 excel 文件中工作表的名称。

  • **read_excel()**:将数据导入 R

read_excel()函数的输出

默认情况下,第一个工作表作为 tibble 导入。我们可以通过使用索引或设置工作表参数来显式指定要导入的工作表。下面的两个调用做同样的工作。

然而,手动加载每一个工作表,然后将它们合并到一个列表中会非常繁琐。幸运的是,您可以使用[**lapply()**](http://www.rdocumentation.org/packages/base/functions/lapply) 来自动执行这个操作。这个函数返回一个相同长度的列表。

XL 连接

由马丁·斯图德开发。它充当了 R 和 Excel 之间的桥梁。它允许用户执行任何活动,如编辑工作表、格式化数据等。在 R 内部的 Excel 上,它可以处理 XLS 和 XLSX 文件。XLConnect 工作在 Java 之上。确保您已经安装了所有的依赖项,如 Java 开发工具包(JDK ),并在 r

在使用软件包之前安装它,下面的命令将为您完成这项工作:

XLConnect 载入工作区

**loadWorkbook()**:该函数将一个 Microsoft Excel 文件加载到 R 中,可以对其进行进一步操作。将 create 参数设置为 True 将确保在文件尚不存在时创建该文件。

loadWorkbook() 函数的结构

这个对象实际上是 R 和 Excel 之间的桥梁。在 R 中构建工作簿后,您可以使用它来获取它所链接的 Excel 文件的信息。一些基本功能是

  • get_Sheets() :蒂耶函数从 excel 文件中以列表形式返回工作表。

getSheets() 的输出

  • readWorksheet() :允许用户从指定的工作表中读取数据,只需在函数的工作表参数中给出工作表的名称。

readWorksheet() 的输出

这个函数最好的部分是你可以指定从哪一行和哪一列开始读取信息。

readWorksheet() 的输出

PS:在对数据集执行任何操作之前,确保数据集被导入到工作目录中。

关系数据库

关系数据库是数据项之间具有预定义关系的集合。这些项目被组织成一组具有列和行的表格。表用于保存关于要在数据库中表示的对象的信息。

开源 : MySQL,PostgreSQL,SQLite

专有 : Oracle 数据库,微软 SQL Server

根据您想要连接的数据库类型,您必须在 r 中使用不同的包。

MySQL: RMySQL

PostgreSQL: RPostgreSQL

Oracle 数据库:ROracle

DBI 为 R 和关系数据库管理系统之间的通信定义了一个接口。这个包中的所有类都是虚拟的,需要通过各种 R/DBMS 实现来扩展。

用更专业的术语来说,DBI 是一个接口,RMySQL 是实现。

像往常一样,让我们先安装包并导入库 DBI。安装 RMySQL 将自动安装 DBI。

第一步是创建到远程 MySQL 数据库的连接。你可以这样做

现在我们已经连接到数据库,可以访问其中的内容了。以下函数帮助我们读取、列出、查询和执行数据库上的其他操作。

  • dbListTables :这个函数让用户列出数据库中的表格。这个函数需要连接对象作为输入,并输出一个带有表名的字符向量。

  • dbReadTable :读取所需的表格,并将结果显示为数据帧。

选择性导入

我们可以用两种方法做到这一点

  • 读取整个表并使用 subsetting 函数对数据进行子集化。
  • **dbGetQuery()**:这个函数发送一个查询,检索结果,然后清除结果集。这里的字符串是一个常见的 SQL 查询。
  • dbFetch() :这个函数有助于从以前执行的查询中获取记录,并允许我们指定每次获取的最大记录数。

注意:dbSendQuery() 将查询发送到数据库,要获取它,我们应该使用 dbFetch()。它的工作与 dbGetQuery()相同。当您想要逐块加载大量记录时,这可能会很有用。

不要忘记断开它。

从互联网上下载文件意味着发送一个 GET 请求并接收你所请求的文件。

读取 CSV、TSV 或文本文件时,我们可以在函数中以如下方式将 URL 指定为字符串。

Excel 文件

r 不知道如何处理直接来自网络的 excel 文件,所以我们需要在导入之前下载它。文件下载后,我们可以使用read_excel功能读取和导入文件。

JSON 数据

JavaScript Object Notation (JSON)是一种非常简单、简洁且结构良好的数据形式。此外,它是人类可读的,也很容易为机器解释和生成。这就是为什么 JSON 被用于与 API(应用程序编程接口)通信。

jsonlite 包

它是一个健壮的、高性能的 JSON 解析器和生成器。

让我们先安装软件包。成功安装后,我们将使用fromJSON函数从 URL 获取数据。

包含 JSON 数据的 r 列表

软件包的另一个有趣的功能是美化和缩小。它们主要用于格式化 JSON 数据。

  • 美化/缩小:美化给 JSON 字符串增加缩进;minify 删除所有缩进/空白。

统计软件包

  • 避风港

这个包用来读取 SAS,STATA,SPSS 数据文件。它通过包装 Evan Miller 的 ReadStat C 库来实现这一点。这个软件包使用起来非常简单。

让我们先安装包并加载库。

  • read_sas:该函数读取 SAS 文件。

同样,对于其他类型的文件,我们可以使用read_stata()read_dta()read_por()read_sav()

  • 国外

由 R 核心团队编写。它在命名和使用上不太一致,但它支持许多外来数据格式,如 Systat、Weka 等。我们还可以将数据导出为各种格式。

让我们安装软件包并加载库。

SAS

这个包的缺点是不能导入. sas7bdat 。它只能导入 SAS 库(。xport)

STATA

这个包裹可以阅读。STATA 版本 5 到 12 的 dta 文件。

convert.factors :将带标签的 STATA 值转换为 R 因子。

convert.dates :将 STATA 日期和时间转换为 Date 和 POSIXct。

缺失类型:

如果为 FALSE,则将所有类型的缺失值转换为 NA。

如果为真,存储属性中值是如何丢失的。

SPSS

use.value.labels :将带标签的 STATA 值转换为 R 因子。

to.data.frame :返回 dataframe 而不是 list。

结论

这是关于将基本数据导入 r 的。加载数据是任何过程的第一步,比如分析、可视化和操作。

有很多方法可以将数据导入 r。

我主要使用 read_excel 和 read_csv。

你用什么?评论下来。

感谢阅读!

使用 pandas 创建、子集化和组合数据框的不同方式

原文:https://towardsdatascience.com/different-ways-to-create-subset-and-combine-dataframes-using-pandas-e7227330a7f1?source=collection_archive---------21-----------------------

熊猫中一些最有用的方法和功能的非常需要的简明指南

介绍

在最近 5 年左右的时间里,python 是新的最热门的编码语言,每个人都在努力学习和研究它。其中一个最大的原因是程序员和数据科学家的庞大社区,他们不断地使用和开发语言和资源,使更多人的生活变得更容易。然而,为了有效地使用任何语言,在进入该语言的广阔世界之前,通常需要了解某些框架。对于 python 来说,有三个这样的框架或者我们称之为库,它们被认为是基石。它们是熊猫、Numpy 和 Matplotlib。在这篇文章中,我们将探讨熊猫的一些有用的方法或功能,以了解熊猫是如何做事情的。

克里斯里德在 Unsplash 上的照片

由于 pandas 具有广泛的功能,我将只介绍一些最重要的功能。在本文中,我们将试图回答以下问题:

  • 什么是包?
  • 如何安装包和调用包?
  • 熊猫是什么?
  • 什么是 dataframe 和 series?
  • 如何用多种方式初始化一个 dataframe?
  • 如何选择/子集化/切片数据帧?
  • 如何组合两个数据帧?

python 新手,想在继续学习之前先学习基础知识吗?你可以看看我写的另一篇文章,这篇文章解释了数据科学的 python 基础。

[## 数据科学的 Python 基础

用 Python 实现数据科学所需的构件

medium.com](https://medium.com/analytics-vidhya/basics-of-python-for-data-science-7c165c76123f)

知道 python 的基础,但不确定所谓的“包”是什么?别担心,我会掩护你的。如果你已经知道什么是软件包,你可以跳到“熊猫数据框架和系列”部分来看看直接覆盖的主题。

什么是包裹?
在大多数现实世界的应用程序中,实际需求需要一个人做大量的编码来解决一个相对常见的问题。例如,机器学习是一个真实世界的应用程序,世界各地的许多人都在使用,但大多数情况下可能有一个非常标准的方法来解决问题。为了节省编码人员和那些想开发这种代码的人的大量时间,所有这种应用程序或代码片段都被编写并在线发布,其中大多数通常是开源的。这些代码的集合被称为包。这个定义是我想用简单的术语让你理解什么是包,它绝不是一个正式的定义。

如何安装包和调用包?
熊猫就是这样一种包装,它无疑是世界上使用最多的包装之一。个人必须先下载这些软件包,然后才能使用它们。使用终端输入 pip 命令可以很容易地做到这一点。一旦下载,这些代码就存在你的电脑里,但不能直接使用。必须做一些被称为“导入”包的事情。简单来说,我们用这个语句告诉计算机“嘿,计算机,我将在这个文件/笔记本中使用这个名字的下载代码”。有了这个,计算机就会明白它必须在下载的文件中查找该包中所有可用的功能。

使用 pip 命令格式化安装包:pip 安装包名
调用包:导入包名作为别名

什么是熊猫?
Pandas 是多个函数和自定义类的集合,称为 dataframes 和 series。它无疑是最常用的软件包之一,世界各地的许多数据科学家都使用它进行分析。这也是大多数数据科学学生学习的第一个包。让我们详细看看使用这个软件包可以做些什么。

注意:我们不会查看 pandas 提供的所有功能,而是查看一些人们在日常工作中经常使用和可能需要的有用功能。

熊猫数据框和系列

在 Pandas 中,主要有两种数据结构,称为 dataframe 和 series。把 dataframes 想象成你常规的 excel 表格,但是在 python 中。基本上,它是一个二维表,其中每一列都有一种数据类型,如果一列中有多个值,很有可能会转换为 object 数据类型。对于 series,它相当于数据帧中的一列信息,有点类似于列表,但它是 pandas 的原生数据类型。

注意:每个包通常都有其对象类型。这可以在尝试打印类型(对象)时找到

让我们看下面的例子来更好地理解它们的区别。

正如我们在上面看到的,series 创建了一系列列表,但实际上创建了 2 个一维值。另一方面,dataframe 根据需要在二维空间中创建了一个表格样式值。

既然我们已经有了基础知识,现在让我们开始深入学习吧。

创建基本数据框架

在进入任何奇特的方法之前,我们应该首先知道如何初始化数据帧和不同的方法。让我们首先看看如何使用不同的方法创建一个简单的 dataframe,其中一列包含两个值。在这样做之前,确保有进口熊猫作为“进口熊猫作为 pd”。请注意,这里我们使用 pd 作为熊猫的别名,大多数社区都使用它。

如上所示,声明或初始化数据帧的基本语法是 pd。DataFrame()和括号中给出的值。因为在括号内只能输入一个变量,所以使用了可以一次保存多个值的数据结构。在上面显示的例子中,列表、元组和集合被用于启动数据帧。如图所示,它们都给出相同或相似的结果。现在让我们看看如何使用字典声明数据帧。

当试图使用简单的字典初始化数据帧时,我们得到如上给出的值错误。我们得到的错误声明该问题是由于字典中的标量值引起的。我们可以通过使用 from_records 方法或使用字典中的值列表来解决这个问题。还要注意,当试图从 dictionary 初始化 dataframe 时,dictionary 中的键被视为单独的列。

注意将值初始化为字典还有什么不同吗?这是本文中我们第一次控制列名。你问还有没有其他方法可以控制列名?是的,我们可以,让我们看看下面的例子。

正如我们在上面看到的,我们可以在 DataFrame 方法中使用 column 关键字初始化列名,语法为 pd。DataFrame(值,列)。我们可以在同一个语句中创建多个列,方法是利用列表列表、元组列表。我们还可以使用列名列表同时为多个列指定名称。

选择或索引数据

既然我们知道了如何从头开始创建或初始化新的数据帧,接下来的事情就是查看特定的数据子集。在 python 中,这在某些情况下被指定为索引或切片。让我们看一下我们将在本节中使用的数据帧。

请注意这里是如何指定索引值的。如果没有给定索引值,索引的顺序将从 0 开始到 9 结束。这将有助于我们更好地理解方法之间的差异。如果您想知道代码的 np.random 部分是做什么的,它会创建随机数并输入到数据帧中。

人们主要使用三种不同的方法来选择数据。它们是:

  • 通信线路(LinesofCommunication)
  • iloc
  • 切片— []

让我们来看看它们,了解它们是如何工作的

通信线路(LinesofCommunication)

loc 方法将使用数据帧和/或序列中的索引信息提取数据。这意味着,对于子集化数据,loc 会查找每一行的索引值,以获取所需的信息。让我们看看下面的例子来更好地理解它。

请注意,当我们将 0 作为 loc 输入传递时,结果输出是对应于索引值 0 的行。这就是从 loc 中提取信息的方式。这种方法的主要优点是,可以仅基于索引值从数据集中检索信息,因此我们可以确定每次提取的内容。

iloc

iloc 方法将使用数据帧和/或序列中的位置信息提取数据。这意味着,对于子集化数据,iloc 不会查找每行的索引值来获取所需的信息,而是根据位置获取所有信息。让我们看看下面的例子来更好地理解它。

注意,这里与 loc 不同,获取的信息来自第一行,对应于 0,因为 python 索引从 0 开始。如果你还记得最初看 df 的时候,指数从 9 开始,到 0 结束。因此,我们现在清楚了,使用 iloc(0)获取第一行,而不考虑索引。

切片— []

python 中的切片是使用方括号— []完成的。有多种方法可以根据需要对数据进行切片。让我们看看如何最有效地利用切片。

让我们首先看看数据帧中的行切片。

在这里,我们可以看到括号中输入的数字对应于行的索引级别信息。上面给出的三个不同的例子应该涵盖了行切片的大部分内容。

现在让我们看看数据帧中的列切片。

我们可以看到,对于按列切片,语法是 df[["col_name "," col_name_2"]],我们将需要关于列名的信息,因为这将非常清楚我们正在提取哪些列。上述方法在某种程度上类似于 loc,因为它试图匹配精确的列名(loc 匹配索引号)来提取信息。使用这种方法,我们还可以添加多个要提取的列,如上面的第二个示例所示。

最后,如果我们必须按照某种条件进行切片,那该怎么办呢?嗯,那些也可以容纳。让我们看一个例子。

正如我们所看到的,切片的语法是 df[condition]。这里的条件不必仅仅是一个条件,也可以是多个条件的叠加或分层。在上面的第一个例子中,我们想要查看 A 列有正值的所有列。类似地,我们可以将多个条件相加,就像上面的第二个例子一样,以获得所需的信息。

这 3 种方法或多或少涵盖了使用 python 可能需要做的大部分切片和/或索引。

组合两个数据帧

我们现在将研究如何以多种方法组合两个不同的数据帧。你问我们为什么一定要这样做?有很多原因可以解释为什么人们会对这样做感兴趣,比如将多个数据源放入一个表中。

有多种方法可以帮助我们做到这一点。它们是:

  • 串联
  • 附加
  • 加入
  • 合并

让我们逐一研究一下。

串联

Concat 是 method 中最强大的方法之一。在某种程度上,我们甚至可以说所有其他方法都是 concat 的派生方法或子方法。让我们首先看一个简单直接的 concat 例子。

它看起来像一个具有默认设置的简单 concat,只是将一个数据帧添加到另一个数据帧之下,而不考虑索引,同时考虑列的名称,即 df2 的列 A 被添加到 df1 的列 A 之下,以此类推。现在让我们探索一些我们可以在 concat 中调整的附加设置。

让我们首先看看如何在 concat 语句中更改轴值,如下所示。

正如我们所看到的,当我们将 axis 的值更改为 1(默认值为 0)时,数据帧的添加是并排进行的,而不是从上到下。此外,现在不是以列名作为添加两个数据帧的指导,而是以索引值作为指导。

现在,让我们尝试使用另一个附加参数 join。join 参数用于指定我们想要的连接类型。如果您不确定什么是连接,那么在继续阅读文章之前快速阅读一下是个好主意。现在让我们看看下面的例子。

正如我们在上面看到的,当我们使用轴值为 1 的 inner join 时,得到的数据帧由具有公共索引的行(如果轴=0,则为公共列)组成,并并排添加两个数据帧(如果轴=0,则为一个在另一个下面)。让我们看一个 axis=0 的例子来理解这一点。

输出与我们预期的一样,其中只显示了公共列,并且数据帧一个接一个地添加。

Ignore_index 是 concat 方法中另一个经常使用的参数。让我们看看它是做什么的。

正如我们在这里看到的,这里的主要变化是索引值不是连续的,与 df1 和 df2 的索引值无关。因此,这是通过忽略原始数据帧的索引,将现有索引值替换为新的顺序索引。

concat 的最后一个参数是 keys。该参数通过输入自定义键名帮助我们跟踪行或列的来源。让我们看一个例子来更好地理解它。

正如我们所看到的,根据值是如何添加的,keys 标记说明了所提到的键以及列和行中的信息。

附加

Append 是 pandas 中的另一种方法,专门用于将数据帧一个接一个地相加。可以说这种方法的功能相当于 concat 方法的子功能。让我们看一个例子。

正如我们从上面看到的,如果我们在 axis=0 的情况下使用 concat,这就是我们将得到的确切输出。然而,因为这个方法是特定于这个操作的,所以 append 方法是 pandas 用户所知道的著名方法之一。让我们看看如何将多个数据帧追加到单个数据帧中。

正如我们在上面看到的,第一个给了我们一个错误。这可以通过使用括号和插入我们想要添加的数据帧的名称来解决。这是因为 append 参数只接受一个用于追加的输入,它可以是一个数据帧,也可以是一组数据帧(在本例中为 list)。concat 中有一个 ignore_index 参数,其工作方式类似于 ignore_index。我们可以看一个例子来更好地理解它。

正如我们所看到的,它忽略了数据帧中的原始索引,并给它们新的顺序索引。

加入

Join 是 pandas 中的另一种方法,专门用于将数据帧添加到另一个旁边。可以说这种方法的功能相当于 concat 方法的子功能。让我们看一个例子。

正如我们所看到的,如果我们在 axis=1 的情况下使用 concat,这就是我们将得到的确切输出。尽管大多数人更喜欢使用 merge 方法而不是 join 方法,但 join 方法是 pandas 用户所知道的著名方法之一。现在让我们来看看对于具有不同索引的数据帧,随着参数“how”值的改变,join 将如何表现。

因此,从上面可以确认,当使用 axis=1 并使用指定的 how 参数时,join 方法的行为类似于 concat。

合并

Merge 类似于 join,只有一个重要的区别。也就是说,在 join 中,数据帧仅基于索引值添加,但在 merge 中,我们可以根据合并的发生来指定列名。所以,说 merge 比 join 更有用、更强大一点都不会错。让我们看一个例子来更好地理解它。

注意我们在 merge 语句中是如何使用参数“on”的。On 是使用 merge 时必须指定的强制参数。只有当两个数据框架中有相同的列和相同的名称时,这种方法才能很好地工作。如果我们想要合并基于不同名称的列的数据帧呢?还是基于多列合并?Merge 自然也包含所有类型的联接,可以使用“how”参数访问这些联接。让我们看一些例子来了解如何使用它们。

正如我们在上面看到的,我们可以将多个列指定为一个列表,并将其作为 on 参数的输入。如果数据帧有不同的列名,我们可以使用 left_on 和 right_on 参数合并它们,而不是使用 on 参数。

我们将关注的最后一个参数是指标。默认情况下,这是 False,但是当我们将它作为 True 传递时,它将创建另一个附加的 column _merge,该 column _ merge 在行级别通知进行了什么类型的合并。

正如我们在上面看到的,它将通知 left_only,如果该行只有来自左边数据帧的信息,它将说 right_only,如果它有关于右边数据帧的信息,最后如果它有两个数据帧的信息,它将显示两者。

结论

在本文中,我们研究了多种情况,包括完成以下任务的多种方法:

  • 以多种方式初始化数据帧
  • 使用 loc、iloc 和切片子集化数据帧
  • 使用 concat、append、join 和 merge 组合多个数据帧

总之,每个人都知道实践使人完美。这句话也适用于技术领域,对吗?为了让你更容易实践我们在本文中讨论的多个概念,我已经创建了一个 Jupiter 笔记本,你可以在这里下载。练习的好时光!!!

如果有任何疑问、建设性的批评和任何反馈,请随时联系我这里的。

从头开始的可微分编程

原文:https://towardsdatascience.com/differentiable-programming-from-scratch-abba0ebebc1c?source=collection_archive---------24-----------------------

去年,我有一个很好的机会,在脸书人工智能研究中心参加了与 Yann Lecun 的谈话。

作为一名数学爱好者,在他的演讲中给我留下深刻印象的是其对深度学习作为可微分编程的重新思考。Yann Lecun 在《脸书邮报》上详述了他的想法:

越来越多的人以数据依赖的方式(用循环和条件)程序化地定义网络,允许它们随着输入数据的变化而动态变化。它真的非常像一个常规程序,除了它是参数化的、自动微分的、可训练/可优化的。

——雅恩·勒村,博览会主任

微分编程的总体目标是计算:

为什么我们需要能够区分一个程序?

即量化程序及其输出对某些参数的敏感性。

在本文中,我们将通过从头开始开发这种令人兴奋的新型编程所需的所有工具来解释什么是可区分编程

大局

让我们从一个简单的玩具示例开始,展示我们如何使用传统的、与数据无关的代码编写一个程序来估计乘坐出租车的持续时间:

在此代码中,我们使用预计算的纽约市平均速度(约 30km/h)来计算出租车行程持续时间。这是编制程序的传统方式,即数据不会影响其参数。

我们使用预定义的参数,这里是由专家估计的平均速度,将这个速度的倒数乘以行程距离,我们得到预期的行驶持续时间。

无论我们运行多少次,它都不会改进。它永远不会从错误中吸取教训。

可区分编程提供的恰恰相反:每次运行都可以用来微调应用程序参数。让我们看看这是如何实现的。

利用反馈

对计算机和人类都适用的一点是,要想进步,你需要反馈。理想情况下,你需要一种方法来量化你的错误。

在计算机世界中,通过在我们的初始代码中引入一个计算相对常见的误差度量的新函数:平方误差可以很容易地做到这一点。

增加了误差计算的代码

一旦你知道了误差,你需要一种方法来知道你需要在哪个方向修改你的参数来减少误差。

让我们分析一个具体的例子。考虑一次持续时间为 12 分钟、距离为 6 公里的旅行。为了用我们的模型精确地预测这个值,模型的正确参数应该是 30 km/h。任何其他值都会导致此行程的错误。

让我们看一下相对于我们的参数,平均速度的平方误差图,以获得一些见解。整个代码很简单:

得到的曲线是:

平方误差总是正的,更重要的是,在正确的平均速度下达到最小值。图片作者。

蓝色曲线显示了行程持续时间相对于速度的演变。更快的行程显然导致更短的行程持续时间。

橙色曲线将误差显示为实际持续时间(此处为 12 分钟)与给定速度的行程持续时间之间的简单差异。对于实际平均速度:30km/h,该误差为零。

绿色曲线是误差的平方。与误差类似,平均速度为 30 公里/小时时,误差为零。

下山

我们现在有了一种方法来量化我们的误差,关于我们程序的输入参数。我们现在需要的是如何减少错误的一些指示。

假设我们的参数,平均速度是 35 公里/小时。平方误差在 2.95 左右。问题是,我们应该降低速度还是提高速度来减少误差?由于我们知道最佳值 30 公里/小时,我们显然知道速度必须降低。但是对于一个真正的问题,我们必须发现它。

再一次,让我们从绘制平均速度为 35 km/h 的平方误差曲线和曲线切线的图表中获得洞察力。

平均速度的误差 wrt,以及平均速度为 35 公里/小时时曲线上的切线。图片由作者提供。

观察切线,很明显我们需要跟随它的斜率来减少误差。在这种情况下,这意味着我们应该降低平均速度。这种方法叫做梯度下降法。

分化

有效地知道如何更新我们的参数的方法是清楚的:我们需要计算我们的误差函数的正切并估计它的斜率。这意味着我们必须对误差函数求导以得到它的梯度。

计算函数的导数有多种方法。现在,我们将只考虑两个:

  • 数值微分,使用基于极限的微分定义
  • 形式微积分,通过推导平均速度的误差公式。

在本文的后面,我们将看到第三个非常通用和健壮的选项。

下面是用于绘制上述曲线的代码:

函数 speed_error_num_diff 使用导数定义来估计相对于误差的误差梯度:

使用导数定义允许简单有效的计算

如您所见,代码很简单,但是不方便的是数值不稳定,并且需要一个额外的参数: delta 。根据误差的绝对值,必须调整增量

另一种方法需要平方误差公式的符号推导:

平均速度误差的形式微分。图片作者。

这两种方法都有效,可用于优化平均速度参数。我们可以绘制梯度下降法的迭代图,并观察到该方法如预期收敛到 30 km/h:

当我们在梯度方向上移动参数时,误差减小。图片作者。

梯度下降通过一个循环和一个指定步长的常数、任意系数来实现:

用于使用梯度下降优化平均速度参数的代码

自动微分

如上所示,这两种方法都有效,但是它们并不总是容易处理的。形式上的一个意味着误差公式的符号推导,一般不像我们例子中那么简单。

有时使用数值法,但由于舍入误差,数值不稳定。

这两种方法都需要人工构建坡度。

幸运的是,有一种方法可以自动构造所有需要的导数:自动微分。

这种方法利用了两个事实:

  • 在任何程序中,我们最终都会进行简单的算术运算(+-*/),并调用一些基本函数,如 sin、cos、exp、log、…
  • 链式法则可以用来区分复杂的数学表达式

为了说明这种方法是如何工作的,我们将开发一个仅支持算术运算的基本实现。提醒一下,下面是四种算术运算的推导规则列表:

(+-*/)运算的派生规则。图片作者。

自动微分有两种主要的实现方式:正向和反向模式。由于前向模式实现起来更简单,我们选择这种模式。

原则是用一个额外的浮点数来增加数字,这个浮点数存储导数。如果数是常数,它的导数是零,而如果数是变量,它对自身的初始导数是 1.0。这一对浮点数被称为对偶数,从数学上来说,构成了一个代数,我们可以在其上执行标准运算。

我们所要做的就是为 DualFloat 创建一个类,重载算术运算符,并使用上面的派生规则:

现在,让我们以多项式 f(x)=3x 为例,手动使用自动微分计算其对 x=4 的导数。

3 是常数,用(3,0)初始化,变量 x 用(4,1)初始化。使用新定义的双数乘法计算,我们得到:

双数计算

而且的确, f(x) 的导数是 6x ,因此当 x = 4f'(x)=24

使用 python 和 DualFloat 类,我们得到:

我们现在有工具使用自动微分重写梯度下降:

请注意,我们不必再为微积分费心了。所有必要的计算都是使用双数秘密进行的。对误差调用 derivative() 自动提供梯度。

可微编程

在这篇文章中,我们已经开发了所有的数学和编程工具来进行差异化编程。如您所见,它基于简单的数学概念,可以很容易地引入任何程序。

注意,重写代码甚至是不必要的,因为存在可以替代 numpy 的库:例如,参见 Jax 。唯一剩下的问题在于识别影响程序性能的参数。

一旦你确定了这些参数并定义了一个误差,你就可以计算你的参数对这个误差的导数。然后,每次使用新数据时,您都可以从梯度的免费计算中受益,以改进您的参数。

使用可微分编程,任何程序都可以受益于可微分函数的所有数学工具,允许它们通过从数据中学习来改进自己。

微分方程作为神经网络层

原文:https://towardsdatascience.com/differential-equations-as-a-neural-network-layer-ac3092632255?source=collection_archive---------4-----------------------

向神经网络模型添加领域知识的第一步

人工神经网络(ANN)的主要思想是使用称为层的相对简单的函数的组合来建立复杂函数的表示。

一个深度神经网络是一个有许多层,或者许多功能组合在一起的网络。

虽然层通常是简单的函数(例如 relu( Wx + b )),但通常它们可以是任何可微分的函数。

该层由参数θ ∈ ℝᵖ.的某个有限向量来指定为了实用,我们需要能够使这一层(或多层)适合数据。这包括定义一个成本或损失函数来衡量模型预测与数据的接近程度。

如果我们的层是可微的,那么我们可以找到这个成本函数∇ C ( θ )的梯度,并使用它以有效的方式找到成本的局部最小值。

这里我考虑微分方程模型。这些系统使用包含当前状态导数的表达式来描述系统(x)的状态随时间的演化。一般来说,它们采取以下形式:

微分方程适合我们的神经网络框架,因为它接受一些参数并产生解作为输出,并且它是可微分的。

因此,我们可以使用微分方程作为神经网络中的一层。这真的很棒,有几个原因:

  • 微分方程是所有物理定律的基本语言。
  • 在物理学和化学之外,微分方程是描述复杂系统行为的重要工具。在我们的神经网络中使用微分方程模型允许这些模型与神经网络方法相结合。
  • 建立有效的神经网络包括为网络选择一个好的底层结构。通常更容易想到的是描述一个函数如何随时间变化,然后写下这个现象的函数。相对简单的速率法则可以变成非常复杂的行为(参见下面的洛伦兹系统!).

为了简单起见,在这篇文章中,我将集中讨论基于单个微分方程层的神经网络。然而,这些层可以很容易地作为一层嵌入到深度学习项目中。深度学习与微分方程形式的领域知识的结合是许多领域的游戏规则改变者。

洛特卡-沃尔泰拉捕食者猎物

为了演示如何将自己的微分方程层构建到神经网络中,我将使用朱莉娅通量微分通量微分方程库。在本文中,我将把代码保持为小片段,以允许开发直觉,但是这些示例的完整代码已经在 Github repo 中发布。

首先,我们将考虑拟合数学生物学中经典模型的参数。Lotka-Volterra 捕食者-食饵模型描述了在简单生态群落中可以观察到的种群振荡。

在哈德逊湾贸易公司 19 世纪的毛皮贸易数据中可以观察到一个著名的例子。

该模型的方程由下式给出

Lotka-Volterra 捕食者-食饵方程

捕食者种群的振荡将会滞后于被捕食者种群的峰值。

现在让我们使用这个模型作为神经网络中的一层,有一组六个参数。两个用于猎物 x₀和捕食者 y₀的初始种群,四个速率参数α,β,δ,γ。

在 Julia 中,我们可以定义这个系统和一组默认参数。

现在,我们将微分方程层定义为输入参数的函数。参数数组将前两个条目作为初始条件。

接下来要做的是生成一些假数据来拟合(添加一些噪声),然后定义一个损失函数。我对我们的数据使用了一个简单的均方误差损失函数,尽管我们当然可以很容易地对损失函数添加一些正则化的参数。

最后,我们定义一个函数来实际执行模型拟合。这是 Julia 生态系统真正受益的地方,因为这个系统可以很容易地使用梯度方法来拟合参数。

正如你所看到的,我使用了 3000 次 Adams 迭代,然后调用 BFGS 来最小化成本。这是一个很好的组合,你可以直接跳到 BFGS,但收敛需要更长的时间。最好是让亚当斯迭代让你在附近,然后结束与 BFGS。

恢复的参数由下式给出:

The parameters are [0.9987532818277052, 0.9809616721237869, 1.5075095360807464, 1.009470157647888, 2.974936372236448, 0.993477599859459] with final loss value 2.1229151208653736The actual parameters should be: [1,1,1.5,1,3,1]

这是我们的数据与模拟时间序列数据的对比图。如你所见,最小化结果与数据非常吻合。

Lotka-Volterra 捕食者-食饵模型的参数使用最小二乘成本函数上的亚当和 BFGS 优化进行拟合。对于该合成数据集,参数恢复得非常接近精确。

当然,对神经网络的真正考验是它如何推广到验证数据。在这种情况下,我们可以很容易地测试系统的未观察到的初始条件,看看我们的神经网络是否捕捉到了动态。

推广到一组新的初始条件。当然,模型概括得很好,因为我们找到了几乎完全正确的参数。

太好了,它起作用了!

斯图尔特-朗道振子模型

现在让我们考虑一下在我的研究领域中经常出现的从动力系统中恢复参数的问题。 Stuart-Landau 方程描述了接近 Hopf 分叉(非线性系统中出现普通振荡)的非线性系统的动力学。我将考虑这种变化,它出现在对耦合振子群体的同步研究中。

使用这个模型的部分动机是由于我对这个模型的熟悉。然而,我还有另一个更深层的动机。Lotka-Volterra 捕食者-食饵模型是一个很好的开始模型,但对于数学生物学来说,这是一个奇怪的模型。这是一个保守系统,很像在物理系统中观察到的(没有摩擦)。这意味着 Lotka-Volterra 系统不是有一个孤立的吸引振荡(称为极限环),而是有一整类同心环。

这有可能扭曲我们对拟合微分方程模型的理解,因为保守系统保留了对初始条件的记忆。对于极限环振荡,我们会随着时间的推移丢失关于初始条件的信息(瞬态动力学衰减,时间箭头)。由于许多初始条件以相同的最终状态结束,这可能使参数恢复的问题更加困难。

该系统的方程由下式给出:

这些是用极坐标写的。因此,R 表示振幅,ψ表示弧度角度。动力学的定性形式为

斯图尔特-朗道动力学导致稳定的极限环振荡。

在 Julia 中,更新我们的捕食者-食饵模型来考虑这种类型的系统是一件简单的事情。

现在我们定义一个 ode 层生成一些数据并创建一个损失函数。

最后,下面的函数创建运行优化来恢复参数。

恢复的参数由下式给出:

The parameters are [0.02293619193779294, 0.08918144968755219, 0.08304049248335474, 6.275224626122101, 0.11589347566722544, 0.9019465183126854] with final loss value 0.7300967336849258The actual parameters are:
[0.0,0.08,0.1, 6.28, 0.1, 1.0]

训练数据的拟合如下所示:

蓝色表示训练数据的 Stuart-Landau 振荡器拟合,绿色表示模型预测。左图显示了相平面中的动态特性。右图显示了作为时间函数绘制的幅度 R(右上)和相位(右下)。

我们可以进一步测试我们的模型,看看它对新的初始值的推广程度。

使用未观察到的初始条件数据生成的验证数据的 Stuart-Landau 振荡器拟合显示为蓝色,模型预测显示为绿色。左图显示了相平面中的动态特性。右图显示了作为时间函数绘制的幅度 R(右上)和相位(右下)。

洛伦兹方程和混沌

现在是有趣的应用程序的时间了。让我们考虑使用混沌系统的时间序列数据进行参数恢复。为此,我们将使用著名的洛伦兹系统。

三维洛伦兹系统。最初用作大气流体动力学(对流)的简单模型。我们将选择给出混沌解的标准参数值(σ=10,ρ=28,β=8/3)。

在 Julia 中定义这个系统很简单:

作为演示,让我们看一下从稍微不同的初始条件值生成的 X 变量的一些时间序列数据。

x 变量作为时间的函数,三个初始条件非常接近。

该图显示了对初始条件的敏感依赖,这是混沌的标志。

通常,我们定义一个依赖于初始条件和参数的微分方程层。

生成一些数据,给时间序列添加一些噪声,并定义我们通常的误差函数。

现在使用 DiffEqFlux 库定义训练函数。

运行优化可以很好地拟合训练数据,当我们对训练样本之外的时间进行积分时,这种拟合可以很好地概括。

相空间绘制 t ∈ [0,30]的真实系统(蓝色)与模型预测(绿色)的关系图。

我们还可以查看适合时间序列图的训练模型。

蓝色显示的(扩展)训练数据的时间序列与绿色显示的拟合模型预测。在模型预测和数据出现分歧的 t ≈ 22 之前,它们一直非常接近。该模型仅针对 t ∈ [0,10]的数据进行训练。因此,该模型能够对大约两倍长度的训练数据进行准确预测。

由于该模型仅在 t ∈ [0,10]上训练,我们可以看到该模型对 t ∈ [10,20]的概括非常好,但在此时间之后很快偏离真实值。

The parameters are [0.9727986684296102, 0.034212291835795355, -0.001627150535904438, 9.99887680497206, 28.00267472723392, 2.666573010932292] with final loss value 1.7015586033442565The actual parameters are:
[1.0,0.0,0.0,10.0,28.0,2.6666666666666665]

所以我们找到了一个非常合适的。

结论

使用 Julia 生态系统(科学机器学习 sciml) 将微分方程模型包括到神经网络中是相对简单的。这使得我们能够通过经典的动态系统模型将整个知识分支纳入到我们的神经网络模型中,用于时序数据。下一步是将这些微分方程层结合到我们的深度学习模型中。

关于动力系统和人工神经网络的接口问题。我们从这里开始的三个可能的概括是:

  • 神经微分方程是一个术语,用来描述使用人工神经网络函数作为动力系统的右端。由于这些系统利用了一般的人工神经网络功能,它们可能在建模时间序列时表现出较差的收敛性。
  • 通用微分方程是一个用于混合数学模型和人工神经网络函数的术语。这里的想法是指定尽可能多的动态,然后让 ANN 填入未建模的动态、控制函数等。这些系统可以表现出更快的收敛速度,并允许我们将神经网络的能力与领域知识相结合来描述高维函数。
  • 混合动力系统用于将微分方程模型(或更一般的某种领域知识)与单独的机器学习方法相结合。存在这些类型的许多变体,但我会区分这个类,它将机器学习模型与领域模型分开。然后可以将它们组合起来或作为输入输入到另一个中。

此外,我应该提到,我们在本文中讨论的所有模型都可以很容易地转移到 GPU 进行训练。只需在 GPU 上声明初始条件。

提醒:本文的所有代码都可以在这里找到

快乐造型!

差分机器学习

原文:https://towardsdatascience.com/differential-machine-learning-f207c158064d?source=collection_archive---------26-----------------------

利用导数的新用途进行不合理的有效函数逼近

[## 差分机器学习

工作底稿

arxiv.org/abs/2005.02347](https://arxiv.org/abs/2005.02347) [## 差分机器学习概述

同伴回购

github.com/differential-machine-leanring-](https://github.com/differential-machine-learning)

为 2020 年 5 月 28 日彭博烧烤研讨会录制的 5 分钟视频概述

Brian Huge 和我刚刚发布了一篇的工作论文,这是在丹斯克银行进行了六个月的人工智能(AI)函数逼近的研究和开发之后。一个主要发现是,当训练标签相对于训练输入的梯度可用时,用于回归(即值的预测,而不是类的预测)的训练机器学习(ML)模型可以得到极大的改善。给定这些差分标签,我们可以编写简单但不合理有效的训练算法,能够以稳定的方式从小数据集以显著的速度和准确度学习精确的函数近似,而不需要额外的正则化或超参数优化,例如通过交叉验证。

在这篇文章中,我们以差分机器学习的名义简要总结了这些算法,强调了主要的直觉和好处,并评论了 TensorFlow 实现代码。所有细节都可以在工作文件、在线附录Colab 笔记本中找到。

在金融衍生品定价近似的背景下,训练集是用蒙特卡罗模型模拟的。每个训练示例都是在一个蒙特卡罗路径上模拟的,其中标签是交易的最终收益,输入是市场的初始状态向量。微分标签是对状态的收益 wrt 的路径梯度,并且用自动伴随微分(AAD) 有效地计算。由于这个原因,差分机器学习在金融中特别有效,尽管它也适用于所有其他可以获得高质量一阶导数 wrt 训练输入的情况。

模型不仅在输入和标签的扩充数据集上训练,而且在差异数据集上训练:

通过最小化值和导数的预测误差的组合成本:

给出了值和导数标签。我们通常通过推理计算预测的值,通过反向传播计算预测的导数。虽然这种方法适用于任意复杂的架构,但为了简单起见,我们在此讨论普通前馈网络。

回想一下普通前馈方程:

前馈方程

其中符号是标准的,并在论文中规定(索引 3 是为了与论文一致)。

本文中的所有代码都摘自演示笔记本,其中还包括注释和实际的实现细节。

标准进口

下面是前馈方程的 TensorFlow (1.x)实现。我们选择显式编写矩阵运算来代替高级 Keras 层,以突出代码中的等式。我们选择了 softplus 激活。ELU 是另一个选择。出于论文中解释的原因,激活必须是连续可微的,排除了例如 RELU 和 SELU。

代码中的前馈方程

用反向传播预测输出 wrt 输入的导数。回想反向传播方程是作为前馈方程的附件推导出来的,或者参见我们的教程了解一下:

反向传播方程

或者在代码中,回想一下 softplus 的导数是 sigmoid:

代码中的反向传播方程

我们再次明确地编写了反向传播方程来代替对 tf.gradients() 的调用。我们选择这样做,首先,再次突出代码中的方程,同时避免在训练期间嵌套反向传播层,如下所示。为了避免疑问,通过调用一次 tf.gradients() 来替换这段代码也是可行的。

接下来,我们在一个网络中结合了前馈和反向传播,我们称之为双网络,这是一个两倍深度的神经网络,能够以两倍的计算成本同时预测值和导数:

孪生网络

前馈和反向传播相结合的孪生网络

孪生网络在两个方面是有益的。训练后,在需要导数预测的应用中,它可以有效地预测给定输入的值和导数。例如,在金融领域,它们是价格对市场状态变量的敏感性,也称为希腊(因为交易员给它们取希腊字母),也对应于对冲比率

双生网络也是差异训练的基本构造。组合成本函数通过孪生网络的推理、预测值和导数来计算。成本函数的梯度通过 twin 网络反向传播来计算,包括由 TensorFlow 作为其优化循环的一部分静默进行的反向传播部分。回想一下神经网络的标准训练循环:

普通训练循环

差分训练循环实际上是相同的,对于成本函数的定义是安全的,现在结合了值和导数的均方误差:

差分训练循环

TensorFlow 在幕后无缝区分 twin 网络,以满足优化需求。网络的一部分本身就是反向传播,这无关紧要。这只是矩阵运算的另一个序列,TensorFlow 没有困难地进行微分。

笔记本的其余部分涉及标准数据准备、训练和测试,以及对金融教科书数据集的应用:Black & Scholes 中的欧式看涨期权和 correlated Bachelier 中的篮子期权。结果证明了差分深度学习的不合理有效性。

一些测试结果展示了差分深度学习的强大功能,可在笔记本电脑上重现

在在线附录中,我们探索了差分机器学习在其他类型的 ML 模型中的应用,如基函数回归和主成分分析(PCA),结果同样显著。

差分训练对不正确的导数施加惩罚,与常规正则化(如 ridge/Tikhonov)倾向于小权重的方式相同。与常规正则化相反,差分 ML 有效地减轻了过拟合,而没有引入偏差。因此,不存在偏倚-方差权衡或通过交叉验证调整超参数的必要性。它只是工作。

差分机器学习更类似于数据扩充,这反过来可以被视为一种更好的正则化形式。数据扩充一直被应用于例如计算机视觉中,并取得了成功。这个想法是从一个单独的图像产生多个标记的图像,例如通过裁剪、缩放、旋转或重新着色。除了以可忽略不计的成本扩展训练集之外,数据扩充教会了 ML 模型重要的不变性。同样,衍生品标签,不仅以非常小的成本增加了训练集中的信息量(只要它们是用 AAD 计算的),而且教会了 ML 模型定价函数的形状

工作论文:【https://arxiv.org/abs/2005.02347
Github repo:github.com/differential-machine-learning
Colab 笔记本:https://Colab . research . Google . com/Github/differential-machine-learning/notebooks/blob/master/differential ml . ipynb

安托万·萨维恩

深度学习中的差分隐私

原文:https://towardsdatascience.com/differential-privacy-in-deep-learning-cf9cc3591d28?source=collection_archive---------14-----------------------

关于如何将差分隐私集成到图像分类的深度学习架构中的实用指南

马太·亨利Unsplash 上拍照

我要感谢阿克谢·库尔卡尼先生在我发表我的第一篇文章的征途上指引我。

介绍

随着我们大量的日常活动被转移到网上,被记录的个人和敏感数据的数量也在增加。数据的激增也导致了机器学习和深度学习形式的数据分析工具的增加,这些工具正在渗透到每个可能的行业。这些技术也用于敏感用户数据,以获得可操作的见解。这些模型的目标是发现整体模式,而不是个人习惯。

凯文·Ku 在 Unsplash 上的照片

深度学习正在发展成为许多自动化程序的行业标准。但它也因学习训练数据集的微小细节而臭名昭著。这加剧了隐私风险,因为模型权重现在编码了更精细的用户细节,这些细节在恶意检查时可能会泄露用户信息。例如,弗雷德里克松等人演示了一种从面部识别系统中恢复图像的模型反转攻击[1]。给定大量可免费获得的数据,可以有把握地假设,确定的对手可以获得从模型权重中提取用户信息所需的必要辅助信息。

什么是差分隐私?

差分隐私是一种为我们提供用户信息隐私的某些数学保证的理论。它旨在减少任何一个人的数据对整体结果的影响。这意味着人们会对一个人的数据做出同样的推断,不管它是否出现在分析的输入中。随着数据分析数量的增加,暴露用户信息的风险也在增加。差分保密计算的结果不受各种隐私攻击的影响。

这里 X 是一个个体。图片作者。

这是通过在计算过程中添加精心调整的噪声(由ε表征)来实现的,使黑客难以识别任何用户。这种噪声的增加也导致计算精度的下降。因此,在准确性和提供的隐私保护之间存在权衡。隐私级别由ε来衡量,并与提供的隐私保护程度成反比。这意味着ε越高,数据的保护程度越低,泄露用户信息的可能性就越大。实现ε差分隐私是一种理想情况,在实际场景中很难实现,因此使用(ε,δ)-差分隐私。通过使用(ε,δ)-差分隐私,该算法是ε-差分隐私,具有概率(1-δ)。因此,δ越接近 0 越好。Delta 通常被设置为训练样本数量的倒数。

渐变操作。作者图片

请注意,我们的目标是保护模型而不是数据本身免受恶意检查。这是通过在模型权重的计算期间添加噪声来实现的,这具有模型正则化的额外优点。具体来说,在深度学习中,我们通过选择差分隐私优化器来集成差分隐私,因为这是大多数计算发生的地方。梯度首先通过获取损失相对于重量的梯度来计算。然后根据 l2_norm_clip 参数对这些梯度进行剪切,并添加噪声,这由 TensorFlow 隐私库中的噪声 _ 乘数参数控制。

我们的目标是通过以差分隐私—随机梯度下降(DP-SGD)优化器的形式应用差分隐私来保持模型权重的隐私性。这里尝试将差分隐私应用于深度神经网络 VGG19 ,其任务是对 CIFAR10 数据集进行图像分类,以了解对模型性能和隐私的影响。如果这样的模型被训练在敏感和私人的图像上,那么这些图像不被泄露将变得非常重要,因为它们可能被恶意使用。

DP-SGD 的实施

TensorFlow Privacy 库用于实现 DP-SGD 优化器和计算 epsilon。超参数不是非常精确地调整以获得基准的最大精度,因为我们只需要它进行比较研究,过度调整可能会扭曲预期的比较。计算ε的函数将'步数'作为输入,计算为(历元训练样本数)/批量步数是模型查看训练数据次数的度量。随着步数的增加,模型看到的训练数据越多,它会通过过度拟合将更好的细节纳入自身,这意味着模型有更高的机会暴露用户信息。*

*num_classes = 10# data loading
x_train, y_train), (x_test, y_test) = cifar10.load_data()
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
y_train = tf.keras.utils.to_categorical(y_train, num_classes)
y_test = tf.keras.utils.to_categorical(y_test, num_classes)# network params
num_classes  = 10
batch_size   = 128
dropout      = 0.5
weight_decay = 0.0001
iterations   = len(x_train) // batch_size# dpsgd params
dpsgd = True
learning_rate = 0.15
noise_multiplier = 1.0
l2_norm_clip = 1.0
epochs = 100
microbatches = batch_sizeif dpsgd:
   optimizer = DPGradientDescentGaussianOptimizer(
   l2_norm_clip=l2_norm_clip,
   noise_multiplier=noise_multiplier,
   num_microbatches=microbatches,
   learning_rate=learning_rate)
   loss = tf.keras.losses.CategoricalCrossentropy(
   reduction = tf.compat.v1.losses.Reduction.NONE)
   # reduction is set to NONE to get loss in a vector form
   print('DPSGD')else:
   optimizer = optimizers.SGD(lr=learning_rate)
   loss = tf.keras.losses.CategoricalCrossentropy()
   print('Vanilla SGD')model.compile(loss=loss, optimizer=optimizer, metrics=['accuracy'])
# where model is the neural network architecture you want to use# augmenting images
datagen = ImageDataGenerator(horizontal_flip=True,
          width_shift_range=0.125, 
          height_shift_range=0.125,        
          fill_mode='constant',cval=0.)datagen.fit(x_train)# start training
model.fit(datagen.flow(x_train,y_train,batch_size=batch_size), steps_per_epoch=iterations,epochs=epochs,validation_data=(x_test, y_test))if dpsgd:
   eps = compute_epsilon(epochs * len(x_train) // batch_size)
   print('Delta = %f, Epsilon = %.2f'(1/(len(x_train)*1.5),eps))
else:
   print('Trained with vanilla non-private SGD optimizer')*

基准使用 Keras 库提供的 SGD,学习率与 DP-SGD 相同。网络参数保持不变,以便进行公平的比较。

注意:由于 DP-SGD 将权重衰减作为一个参数,因此将权重衰减应用于神经网络的每一层都会出现错误。

作者图片

一些观察结果:

  • 使用 DP-SGD 的模型训练比基准慢。
  • 使用 DP-SGD 的模型训练比基准更嘈杂,这可能是由于梯度削波和噪声的添加。
  • 最终,与基准测试相比,具有 DP-SGD 的模型实现了相当不错的性能。

处理不平衡数据

我们试图通过制作不平衡的数据集,然后训练模型来模拟真实世界的场景。这是通过向每个类随机分配用户定义的权重来实现的。我们人为地制造了一个严重的不平衡,导致模型表现不佳。为了克服这种不平衡,我们采用数据级方法,而不是对模型本身进行更改。实例数量的减少导致ε增加。使用合成少数过采样技术(SMOTE)进行过采样后,我们看到精度有所提高。这使我们能够在不改变模型本身的情况下处理不平衡。

每个类的实例数(共 10 个类):
[3000 3500 250 2000 1000 1500 500 50 5000 2500]

*def imbalance(y, weights, x_train, y_train):
    random.shuffle(weights)
    indices = []
    for i in range(10):
       inds = np.where(y==i)[0]
       random.shuffle(inds)
       print(i,int(weights[i]*len(x_train)))
       indices += list(inds[0:int(weights[i]*len(x_train))])
       x_train = x_train[indices]
       y_train = y_train[indices]
    return x_train, y_trainweights = [0.01,0.05,0.1,0.2,0.3,0.4,0.5,0.6,0.7,1]
x_train, y_train = imbalance(y_train,weights,x_train, y_train)# Implementing SMOTE
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
x_train = x_train.reshape(len(x_train),32*32*3) 
# where shape of image is (32,32,3)
x_train, y_train = smote.fit_resample(x_train, y_train)
x_train = x_train.reshape(len(x_train),32,32,3)*

我们注意到:

  • 使用 SMOTE 对数据进行过采样后,模型性能比在不平衡数据集上训练的模型有所提高。
  • 基于原始平衡数据训练的模型取代了基于过采样数据训练的模型的性能。

从实验中,我们观察到数据级方法足以解决不平衡的问题,即使是有差别的私有优化器。

调整 NOISE_MULTIPLIER 参数

张量流隐私中对ε有直接影响的超参数是噪声乘数。

作者图片

噪声= 100.0,ε= 0.18
噪声= 10.0,ε= 0.36
噪声= 1.0,ε= 3.44

我们注意到:

  • 增加噪声会降低ε的值,这意味着更好的隐私保护。
  • 众所周知,隐私保护级别与模型性能成反比
  • 但是我们在实践中没有观察到这种关系,并且增加噪声乘数对性能几乎没有影响。

结论

在调整噪声乘数时发现的与理论的偏离可能归因于模型的深度较大,其中梯度操作无效。从 TensorFlow 隐私源中给出的代码中,我们看到ε的值独立于模型训练,仅取决于我们选择的噪声 _ 乘数批量 _ 大小时期增量值。Epsilon 有很好的理论支持来衡量隐私风险的大小。它考虑了在确定模型查看训练数据的次数时很重要的所有因素,并且直观地感觉到模型查看数据越多,用户信息暴露于模型权重的风险就越大。但它仍然缺乏实际测量模型重量对恶意检查的安全性。这是一个令人担忧的原因,因为现在我们不知道我们的模型权重是否对黑客攻击免疫。现在需要一个度量标准来衡量模型权重的不透明程度,即它们透露的用户信息有多少。

未来的工作

正如结论中所讨论的,首先也是最重要的是提出一个衡量标准来判断模型的隐私性。它甚至可能是一个试图从模型权重中获取用户信息的攻击模型。模型能够正确识别的训练数据点的数量可以量化为隐私风险。一旦一个可靠的度量被形式化,我们就可以着手将不同的私有优化器扩展到计算机视觉和自然语言处理中更复杂的任务,以及其他架构。

参考

  1. 米(meter 的缩写))弗雷德里克松、s·贾和 t·里斯坦帕尔。利用置信度信息和基本对策的模型反转攻击见 CCS,第 1322-1333 页。2015 年 ACM* 。*
  2. Abadi,Martin 等.“深度学习与差异隐私2016 年 ACM SIGSAC 计算机与通信安全会议论文集。2016.
  3. 张量流隐私,https://github.com/tensorflow/privacy

Diffie Hellman 密钥交换

原文:https://towardsdatascience.com/diffie-hellman-key-exchange-f673d617137?source=collection_archive---------24-----------------------

克林特·帕特森在 Unsplash 上拍摄的照片

使互联网成为可能的技术

简而言之,Diffie Hellman 是一种广泛使用的技术,用于将对称加密密钥安全地发送给另一方。在继续之前,让我们首先讨论一下为什么要使用像 Diffie Hellman 这样的东西。当在互联网上以纯文本形式传输数据时,有人很容易使用某种数据包嗅探器(如 WireShark)来捕获数据包。一个恶意的人,可能会偷听你和你女朋友的谈话,或者更糟,窃取密码和信用卡信息。幸运的是,一些非常聪明的人想出了一种为交通信息编码的方法。我们将普通的纯文本转换成难以理解的东西,反之亦然的过程被称为密码术。密码学最基本的例子叫做凯撒密码。

https://commons . wikimedia . org/wiki/File:Caesar _ Shift _ Cipher _ wheel . png

本质上,双方都有一个对称密钥,它指定了什么字符映射到加密文本的什么符号。没有钥匙的人不能阅读这条信息。例如,在前面的图像中,字符“A”将被编码为加密消息中的“T”。然后,接收端的个人可以使用相同的凯撒密码来解码消息。

在计算机网络领域,对称加密算法的问题是,密钥必须不可避免地通过网络发送给另一方,以便他们可以解密传入的消息,并依次加密它们。如果恶意行为者碰巧在那个时间点监听网络,他们可以获得密钥,并将其用于邪恶的目的。

这就是不对称加密发挥作用的地方。不对称加密通过生成公钥和私钥对来工作。公钥只能用于加密消息,而私钥只能用于解密消息。例如,当您进行网上银行业务时,您将您的公钥交给银行,然后用它来加密发送给您的数据。如果一个坏人得到了公钥,他们不会造成任何真正的伤害,因为他们只有加密数据的能力。

今天,使用最广泛的非对称加密算法是 RSA。 RSA 代表Rivest–sha mir–ad leman之后,这些人在 1977 年首次描述了该算法。RSA 算法通过将消息提升到公钥的幂,然后取结果的模来加密消息。为了解密给定的消息,我们将它提升到私钥的幂,然后取结果的模。RSA 依赖于一种称为单向函数的数学概念。假设我们有以下等式:

现在,假设你得到了数字 8 并要求返回 2 。你能做到吗?

为了弄清楚 8 的所有因素,我们可以相对容易地逆向工作。

相反,模(与余数同义)运算是单向函数的一个例子。假设我们有以下等式:

如果让你从推导出 11 ,你会吗?

通过尝试所有不同的可能性(即 3 % 4 = 37 % 4 = 311 % 4 = 3) ,您可能能够获得正确的答案(**11))**,但是当分子非常大时,如 RSA 的情况(即 4096 位长),有很多排列,我指的是给出余数的很多排列鉴于这一特性,黑客将别无选择,只能使用蛮力(尝试各种可能性)来从加密消息和公钥中确定私钥。考虑到今天的密钥长度为 4096 位,传统的计算机要花上几个世纪才能遍历完所有可能的值。

实际上,非对称加密比对称加密慢 3 到 5 个数量级。因此,我们不使用非对称加密来加密实际的有效载荷。相反,我们使用像 Diffie-Hellman 这样的技术安全地向另一方发送对称加密密钥,然后使用所述密钥加密/解密所有进一步的消息。

模运算(RSA) Diffie Hellman

我们已经在高层次上描述了 RSA。现在,让我们看一个具体的例子。假设,鲍勃想给爱丽丝发送一条消息。鲍勃将通过生成新的随机素数 N 和相应的生成器开始**

注意:g 不是随机的,但是我们如何选择它超出了本文的范围。

实际上,N 是一个很大的数字。然而,为了简单起见,我们将使用以下值:

****

两个 g & N 都以纯文本形式在网络上发送。Bob 然后生成一个密钥 a = 2 。接下来,鲍勃将生成器 g 提升到他的密钥 a 的幂,并取结果的模。最终产品 A = 5 被发送给爱丽丝。

****

在另一端,Alice 执行相同的步骤—即,她生成一个密钥 b,将生成器 g 提升到她的密钥 b 的幂,取乘积的模,并将最终结果 B = 3 发送给 Bob。

****

即使恶意的参与者想要窥探他们的流量。他们无法从 A 和 b 中推导出 Bob 或 Alice 的秘密密钥。

在从爱丽丝接收到 B 后,鲍勃将其提升到他的私钥 a 的幂,并取结果的模。

爱丽丝也一样。

****

在这种情况下,爱丽丝和鲍勃最后都得到相同的数字, 9 。然后他们使用 9 作为 AES 等对称加密算法的密钥。

椭圆曲线 Diffie Hellman

试图从椭圆曲线上的一点导出私钥比传统的 RSA(模运算)更难破解。因此,椭圆曲线 Diffie Hellman 可以用较少的比特实现相当的安全级别。

较小的密钥需要较少的计算步骤来加密/解密给定的有效载荷。当从本地机器建立安全连接时,您不会注意到太大的不同。然而,在类似于每秒执行成千上万次密钥交换的中型 web 服务器上,使用椭圆曲线 Diffie Hellman 可以节省大量资源。

我们可以将 Diffie Hellman RSA 密钥交换中所有可能数字的定义域想象成一个圆(由于模函数的性质)。 n 的值越大,圆圈越大,越难猜出正确的数字。

相反,顾名思义,椭圆曲线 Diffie Hellman 密钥交换的所有可能数字的定义域采用椭圆曲线的形式。

前述椭圆曲线的特征在于以下数学方程:

在野外,使用等式( mod n )是很常见的。

在实践中,你希望使用由专业数学家开发的曲线,并检查以确保它们是安全的。

椭圆曲线 Diffie Hellman 的工作方式不是像 RSA 那样进行幂运算,而是将点 G 多次添加到自身。

让我们看一个例子。假设 Bob 发起了与 Alice 的连接。Bob 选择一个生成器 G ( 曲线上的一点)和椭圆曲线方程的参数 abn ,并以明文形式通过网络发送它们。

****

Bob 和 Alice 各自生成一个私钥(数字)。为了简单起见,我们假设鲍勃选择了 b = 9爱丽丝选择了 a = 3 。鲍勃和爱丽丝分别负责计算 bG = 9GaG = 3G

为了计算 xG (其中 x 为任意数),我们使用了点的加法和加倍公式。例如,为了确定 2G,我们使用一个点加倍的公式。

为了取分数的模,我们可以使用模乘逆计算器。

**[## 模乘逆

这个计算器计算给定整数 a 模 m 的模乘逆。理论是下面的…

planetcalc.com](https://planetcalc.com/3311/)**

然后我们将答案乘以 77 % 17 = 9,并取结果的模。

该点的 x 坐标可以计算如下:

****

然后我们用 x2G 来计算 y2G。

为了计算 3G,我们使用增加一个点的公式。

我们从计算斜率开始。

然后我们计算新点的 x 位置。

最后,我们使用 x 坐标的值来计算 y。

****

Bob 通过网络发送 bG = 9G = (7,6) 。同样,爱丽丝发送 aG = 3G = (10,6) 。在事件中,一个恶意的演员正在监听,从椭圆曲线上的点 (7,6)(10,6) 导出 aGbG 的值是不可能的。

一旦 Bob 从 Alice 接收到 aG = (10,6) ,他计算 abG = 9(3G) = 27G = (13,7) 。当 Alice 从 Bob 收到 bG = (7,6) 时,她计算 abG = 3(9G) = 27G = (13,7) 。然后,它们都使用 abG 的 x 坐标作为所有进一步数据传输的对称加密密钥。

挖得足够深

原文:https://towardsdatascience.com/dig-deep-enough-features-engineering-techniques-for-time-series-analysis-in-python-500c96aebade?source=collection_archive---------39-----------------------

以 Python 中时间序列分析的工程技术为特色。

照片由 Unsplash 上的 Dragos Gontariu 拍摄

在我们开始挖掘数据之前,对时间序列有一个概念是非常重要的。
基本上,一个时间序列是一个按连续顺序排列的数据点的序列,在大多数情况下,时间序列是一个变量在不同时间取值的一组观察值。

在这篇文章中,我将重点介绍一些特性工程的基本技术,这些技术可以随着时间的推移增强您的预测。很久没有在时间序列项目中合作了,所以我决定快速更新一下我用来提高预测的技术,并与大家分享。

特征工程

特征工程是使用领域知识来创建相关特征的过程,以便使您的机器学习算法更加准确。特征工程可以显著影响您的建模过程的性能,如果它做得正确,它可以帮助您的模型执行得非常好。

在进行这一步之前,您需要遵循解决数据科学问题的基本流程,即:

  • 明确你的目标;
  • 提取数据;
  • 清洗数据;
  • 探索数据和发现洞察力(探索性数据分析);
  • 特色工程;
  • 定义你的模型;
  • 训练和调整您的模型;
  • 部署模型。

特征工程是区分好模型和坏模型的重要步骤。在接下来的章节中,我们将讨论这些技术在时间序列分析中的应用。

让我们假设我们需要预测某个区域的能源消耗,那么我们得到了这个数据:

数据是不言自明的,它是一个单变量时间序列,我们有一些重复的模式,我们有两列日期和值,日期列被视为一个对象,因此我们需要将其转换为一个日期时间类型这可以使用 Pandas 轻松完成:

现在我们的数据已经准备好了,让我们看看我们可以从这些变量中挖掘的东西,正如我所说的,数据集包含两列,一个与值相关联的日期,所以让我们讨论一些用于设计这类问题的技术。

日期相关功能

日期相关要素是一种特殊类型的分类变量,日期仅提供时间线上的特定点。如果我们有一些领域的知识或者对问题有深刻的理解,约会是可以被恰当地安排的。
既然我们想预测能源消耗,我们来问一些问题...

  • 日期/时间和消费有关系吗?
  • 数据中是否至少有一个趋势?

要回答第一个问题,如果我们的数据与一些个人使用有关,例如,我们知道晚上的能源使用量比白天低;因此,我们可以得出结论,如果我们从日期中提取小时,可能会有一些隐藏的模式,这也回答了第二个问题,因为我们知道在我们的数据中有一个重复的趋势。

提取周末和工作日有助于了解一周中是否有任何变化模式,而月/年也是一种非常有用的方法来查看季节是否有变化。

使用 Python 提取这些特征非常简单:

data['year']=data['Datetime'].dt.year 
data['month']=data['Datetime'].dt.month 
data['day']=data['Datetime'].dt.day
data['dayofweek']=data['Datetime'].dt.dayofweek  
data['dayofweek_name']=data['Datetime'].dt.day_name()
data.head()

从日期变量中提取的特征

我们可以提取更多信息,例如:

  • 四分之一
  • 学期
  • 是一个月的开始
  • 是一年的开始
  • 是月末
  • 是年末

一些日期提取的代码

基于时间的特征

同样,我们可以使用日期的时间戳部分提取更多的特征,例如,我们可以提取小时、分钟甚至秒,因为日期是每小时的,所以我们在这里可以提取的唯一特征是小时。

data['Hour'] = data['Datetime'].dt.hour 

我们可以使用日期列生成很多特性,如果你想探索更多关于基于日期/时间的特性,这里有文档链接

滞后特征

滞后特征基本上是目标变量,但随着一段时间而变化,它用于了解我们的目标值在过去的行为,可能是前一天,一周或一个月。

使用滞后特性有时可能是一把双刃剑,因为使用目标变量非常棘手,如果使用不当,有时会导致过度拟合

data["Lag1"] = data['AEP_MW'].shift(3) # Lag with 3 hours
data["Lag5"] = data['AEP_MW'].shift(5) # Lag with 5 hours
data["Lag8"] = data['AEP_MW'].shift(8) # Lag with 8 hours

滞后特征

滚动窗口功能

滚动窗口功能正在根据过去的值计算一些统计数据:

  • 预测点:进行预测的时间点;
  • 特征推导窗口(FDW): 滚动窗口,相对于预测点;
  • 预测窗口(FW): 我们希望预测的未来值的范围,称为预测距离(FDs)。

我们可以使用这行代码来实现:

data['rollingMean'] = data['AEP_MW'].rolling(window=6).mean()

同样,我们可以生成许多不同的时间序列特征,这些特征可用于预测不同的预测距离:

扩展窗口统计

另一种类型的窗口,包括系列中所有先前的数据。

这可以通过使用 expanding() 函数在 Python 中轻松实现:

data['ExpandingMean'] = data['AEP_MW'].expanding(6).mean()

滚动平均值与膨胀平均值

当我们对问题有了很好的理解时,所有这些特性都是有用的,这将引导我们做出有意义的见解,并避免特性爆炸

对于非时间序列数据,还有一些其他有用的特征工程技术,我想和你们分享一下。

交互功能

还有一些其他类型的特征工程,涉及揭示特征之间的相互作用,一些特征可以组合起来提取一条特定的信息,所以基本上,我们可以对两个变量进行求和、相乘、求差或求商。

一个通用的技巧是查看每一对特征并问自己,“我能以任何可能更有用的方式组合这些信息吗?”

  • T0 变量的总和:假设我们有两个变量,selling_food_amountselling_gadgets_amount,如果我们只关心费用的总额,我们可以对它们求和。
  • 两个特征的区别:假设我们有两个特征expiration_datemanufacturing_date,我们可以创建一个新特征,名为product_validity
  • 两个特征的乘积:我们有一个在某个区域定义最佳房屋的问题,我们有这两个特征
    number_of_houses:该区域 10 公里内的房屋数量,median_houses:这些房屋的平均质量分数,
    重要的是,有许多房屋选择,但前提是它们足够好。为了创建这种交互,我们可以在这两个变量之间创建一个简单的乘积:house_score = number_of_houses x median_houses
  • 两个特征的商:以疫情冠状病毒为例,我们可以通过mortality_rate = numberOfDeaths / numberOfCases生成死亡率特征。

结论

特征工程将你的输入转化为算法可以理解的东西。

生成太多的特征会混淆你的模型并降低性能,我们应该总是选择最有贡献的特征,并删除未使用的和多余的特征,最重要的是不要使用你的目标变量作为指标(这包括非常接近的滞后特征),这可能会给你非常误导的结果。

那都是乡亲们!顺便说一下,这是我的第一篇文章,希望你喜欢。

别忘了..相信数据,不要相信观点。

数据来源:https://www.kaggle.com/robikscube/hourly-energy-consumption

其他来源:

深入了解 YOLO V3 -动手指南第 1 部分

原文:https://towardsdatascience.com/digging-deep-into-yolo-v3-a-hands-on-guide-part-1-78681f2c7e29?source=collection_archive---------4-----------------------

深度学习/实时对象检测

让我们熟悉 YOLO v3 模型的尝试

https://www . good fon . com/wallpaper/zhivotnoe-gry Zun-zveriok-belka-priroda-mokh-voda-vodopoi . html

人工智能领域在过去几年里发生了巨大的变化,产生了许多新技术。计算机视觉就是这样一个领域,它的目的是训练机器阅读和解释视觉世界。

计算机视觉处理许多具有挑战性的用例,从图像分类到人脸识别。我今天要解决的一个挑战就是物体检测

什么是物体检测?

对象检测是一种包含两项任务的技术,即对象分类对象定位。它是一个被训练来检测多类对象的存在和位置的模型。这可以用于静态图像,甚至实时视频。

物体检测是如何工作的?

对象检测定位对象并将其分类为不同的类别,并通过在其周围绘制边界框来对其进行定位。对象检测有许多使用案例。例如,自动驾驶汽车需要能够在驾驶时识别道路上的其他物体。阅读 X 射线的 AI 放射科医生需要能够定位病变(异常组织)。为了解决这些用例,许多最先进的算法被用于检测对象,例如 R-CNN、快速 R-CNN、更快 R-CNN、掩蔽 R-CNN、SSD、YOLO 等。但是我对今天的 YOLO 特别感兴趣。

YOLO(你只看一次)被认为是最强大的对象检测算法之一。由约瑟夫·雷德蒙、桑托什·迪夫瓦拉、罗斯·吉斯克和阿里·法尔哈迪(2015)发明,迄今为止它已经有 4 个不同的版本,YOLO V4 是 2020 年 4 月发布的最新版本,但在这篇文章中,我们将重点关注 YOLOv3,并试图了解围绕它的所有宣传。除了 YOLO v3,我们还将使用最新版本的 Tensorflow,即谷歌在 2019 年 9 月发布的 TF 2.0。

这个帖子是给谁的?

由于被隔离,这些天我有额外的时间,我决定使用 YOLO V3 探索对象检测,并在跳到最新的 YOLO 版本之前了解它的动态。我看到了很多文章,但大多数都解释了这个非常复杂的 YOLO v3 模型的高级架构和直接实现,这让我感到困惑。我在寻找更多的东西,并决定要理解每一行代码以及后台发生的事情。我偶然看到了伊桑·颜佳·李、阿尤什·卡图里亚和拉赫马德·萨德里的精彩文章,这些文章帮助我更好地理解了。还有什么比试着向别人解释更好的方法来确保我真的理解了某事,因此我写了这篇文章。

这篇文章是我对 YOLO V3 的理解,一个复杂的算法,一个用简单的语言表达的天真的尝试。

先决条件

这篇文章假设你是:

熟悉 Python 3

很了解 TensorFlow

对卷积神经网络(CNN)和对象检测有基本的了解。

Streamlit 有一个基本的了解,以及它是用来做什么的。

本教程的关键要点

本教程分为 两个 部分。

第 1 部分 解释了理解 YOLO v3 如何工作的架构和关键概念。

第 2 部分 从理解配置文件到能够创建用户界面以从用户处获取输入图像并在其上执行对象检测,开始着手实施该算法。

让我们首先了解一下 YOLO 是什么。

YOLO——你只能看一次

许多对象检测模型多次接收和处理图像,以便能够检测图像中存在的所有对象。但是 YOLO,顾名思义,只看物体一次。它对整个图像应用单次向前传递,并预测边界框及其类别概率。这使得 YOLO 成为超快速实时对象检测算法。

太多行话了,嗯?让我们一步步来理解。

YOLO V3

Yolo V3 是对前两个 Yolo 版本的改进,前两个版本更强大,但比前两个版本稍慢。该模型具有多尺度检测、更强的特征提取网络和损失函数的一些变化。

网络体系结构

为了在高层次上理解网络架构,我们将整个架构分为两大部分: 特征提取器特征检测器(多尺度检测器)** 。图像首先被提供给特征提取器,该特征提取器提取特征嵌入,然后被传递给网络的特征检测器部分,该特征检测器部分吐出处理后的图像,该图像具有围绕检测到的类别的边界框。

让我们稍微深入一下这些组件。

特征提取器

以前的 YOLO 版本使用 Darknet-19(用 C 和 CUDA 编写的定制神经网络架构)作为特征提取器,顾名思义,它有 19 层。YOLO v2 在 Darknet-19 的基础上又增加了 11 层,使其架构达到 30 层。尽管如此,该算法在检测小对象时仍然面临着挑战,因为对输入图像进行了下采样,丢失了细粒度的特征。

YOLO V3 提出了一个更好的架构,其中使用的特征提取器是 YOLO v2、Darknet-53(在 ImageNet 上训练的网络)和残差网络(ResNet)的混合。该网络使用 53 个卷积层(因此被命名为 Darknet-53),其中该网络由个连续的 3×3 和 1×1 卷积层构建,后跟一个跳过连接(由 ResNet 引入,以帮助激活通过更深的层传播,而不会出现梯度递减)。

53 层暗网再叠加 53 层用于探测头,使得 YOLO v3 总共是一个 106 层全卷积底层架构。从而导致大的架构,尽管与 YOLO v2 相比,使其稍慢,但同时提高了精度。

这张图片是取自 YOLOv3 的 darknet-53 架构:一个增量改进

如果目标是像在 ImageNet 中那样执行分类,那么将添加平均池层、1000 个完全连接的层和 SoftMax 激活函数,如图所示,但是在我们的示例中,我们希望检测类和位置,因此我们将在提取器上附加一个检测头。探测头是多尺度探测头,因此,我们也需要在多个尺度上提取特征。

一旦我们熟悉了高层架构,我们将在下一节更深入地了解这些层是如何工作的。

为了形象化多尺度提取器的样子,我举了一个 416x416 的图像的例子。层的跨度定义为比率,通过该比率对输入进行下采样,,因此在我们的情况下,三个比例将是 52x52、26x26、13x13 ,其中 13x13 将用于较大的对象,26x26 和 52x52 将用于中等和较小的对象。

416x416 图像的多尺度特征提取器

多尺度检测器

YOLO v3 模型的一个重要特征是其多尺度检测器,这意味着对全卷积网络的最终输出的检测是通过在三个不同位置对三个不同大小的特征映射应用 1x1 检测核来完成的。内核的形状是1x1x(B (5+C))。*

完整的网络架构

Ayoosh Kathuria 制作了一个非常精细的图表,完美地解释了 YOLO v3 的完整架构(结合了提取器和检测器)。

图来自https://towards data science . com/yolo-v3-object-detection-53 FB 7d 3 bfe 6 b

如上图所示,我们以 416x416 的图像为例,进行检测的三个比例分别为第 82 层、第 94 层和第 106 层。

对于第一次检测,前 81 层被下采样,使得第 81 层具有 32 的跨距(如前所述,层的跨距被定义为对输入进行下采样的比率),产生我们的大小为 13×13 的第一特征图,并且第一次检测是用 1×1 内核进行的,产生我们的大小为 13×13×255 的检测 3D 张量。

对于第二次检测,在向上采样到 26×26 的维度之前,第 79 层向前经过卷积层。该特征图然后与来自层 61 的特征图深度连接以形成新的特征图,该新的特征图在 1x1 卷积层的帮助下进一步与第 61 层融合。第二检测层在第 94 层,具有大小为 26×26×255 的 3D 张量。

对于最终(第三)检测层,遵循与第二检测相同的过程,其中第 91 层的特征图在与来自第 36 层的特征图深度连接和融合之前经历卷积层。在第 106 层使用大小为 52×52×255 的特征图进行最终检测。

多尺度探测器用于确保小物体也能被探测到,这与 YOLO v2 不同,后者一直受到批评。与先前层连接的上采样层最终保留了有助于检测小对象的细粒度特征。

下一节将详细描述这个内核在我们的模型中的样子。

YOLO v3 的工作

YOLO v3 网络旨在预测每个对象的**(候选对象的感兴趣区域)以及该对象所属类别的 概率

为此,该模型将每个输入图像分成一个由单元组成的 S x S 网格,每个网格预测中心落在网格单元内的对象的 B 边界框和 C 类别概率。该论文指出,每个包围盒可以专门用于检测某一种对象。

边界框【B】与正在使用的 锚点 的数量相关联。每个包围盒都有 5+C 属性,其中‘5’*是指五个包围盒属性(例如:中心坐标(bx,by)、高度(bh)、宽度(bw)和置信度得分) C 是类的数量。*

我们将此图像传递到正向卷积网络的输出是一个三维张量,因为我们正在处理一个 S x S 图像。输出看起来像[ S,S,B(5+C) ]。*

让我们用一个例子来更好地理解这一点。

图片来自https://machine learning space . com/yolo v3-tensor flow-2-part-1/# NMS-unique

在上面的例子中,我们看到我们的输入图像被分成 13×13 个网格单元。现在,让我们来理解只取一个网格单元会发生什么。

由于 YOLO v3 的多尺度检测特征,在三个不同的地方应用三个不同大小的检测核,因此有 3 个盒子(即 B=3 )。YOLO v3 在具有 80 个对象类别或类的 COCO 数据集上被训练,因此 C=80

因此,输出是前面提到的具有维度(13,13,3(80+5))的 3d 张量。*

锚箱

在检测物体的早期,科学家使用滑动窗口的概念,并在每个窗口上运行图像分类算法。很快他们意识到这没有意义,而且效率很低,所以他们转而使用 ConvNets,在一个镜头中运行整个图像。由于 ConvNet 输出特征值的方阵(例如 YOLO 的 13x13 或 26x26),因此“网格”的概念出现了。我们将正方形特征矩阵定义为一个网格,但是当要检测的对象不是正方形时,真正的问题就来了。这些物体可以是任何形状(大部分是矩形)。因此,锚箱开始被使用。

锚定框是预定义的具有纵横比设置的框。甚至在训练之前,通过在整个数据集上运行 K-means 聚类来预先定义这些纵横比。这些锚定框锚定到网格单元并共享同一个质心。YOLO v3 每个检测秤使用 3 个锚箱,总共 9 个锚箱。

非最大抑制

在单次向前传递后,由于质心是相同的,预测的输出可能会有多个相同对象的边界框,但我们只需要一个最适合所有对象的边界框。

为此,我们可以使用一种称为非最大值抑制(NMS)的方法,该方法基本上在这些检测之后清除。我们可以定义一个特定的阈值作为这个 NMS 方法的约束,其中它将忽略置信度低于所述阈值的所有其他边界框,从而消除一些。但是这并不能消除所有的置信度,因此 NMS 的下一步将被执行,即按照降序排列包围盒的所有置信度,并选择得分最高的一个作为最适合该对象的置信度。然后,我们找到所有其他与具有最大置信度的边界框具有高并集交集(IOU)的框,并消除所有这些框。

现在我们已经知道了 YOLO v3 中使用的所有术语和架构,在下一部分 (第 2 部分) 中,我们将深入研究实现。

由于这是我第一次尝试写一篇中型文章,并试图阐明我的想法,我很乐意听到反馈的改进建议。

第二部即将上映……

参考

  1. 约洛夫 3:增量改进https://arxiv.org/pdf/1804.02767.pdf
  2. 伊桑李https://towards data science . com/dive-really-deep-into-yolo-v3-a-初学者指南-9e3d2666280e
  3. Ayoosh Kathuriahttps://towardsdatascience . com/yolo-v3-object-detection-53 FB 7d 3 bfe 6 b
  4. rahmad Sadlihttps://MC . ai/the-初学者-实施指南-yolo-v3-in-tensor flow-2-0-part-1/
  5. https://machine learning mastery . com/object-recognition-with-deep-learning/

使用 CNN 的数字识别器

原文:https://towardsdatascience.com/digit-recognizer-using-cnn-55c65ca7f9e5?source=collection_archive---------17-----------------------

使用 mnist 数据集建立一个简单的卷积神经网络来识别手写数字。

作者图片

数据集:

MNIST(“改进的国家标准和技术研究所”)是计算机视觉事实上的“Hello World”数据集。自 1999 年发布以来,这个经典的手写图像数据集已经成为基准分类算法的基础。随着新的机器学习技术的出现,MNIST 仍然是研究人员和学习者的可靠资源。

数据处理:

import tensorflow as tf
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

数据集包含 60,000 个训练图像和 10000 个测试图像。在这里,我将数据分别分为训练数据集和测试数据集。x_train & x_test 包含灰度代码,而 y_test & y_train 包含代表数字的从 0 到 9 的标签。

当您检查数据集的形状以查看它是否适用于 CNN 时。您可以看到我们将(60000,28,28)作为我们的结果,这意味着我们的数据集中有 60000 张图像,每张图像的大小为 28 * 28 像素。

要使用 Keras API,我们需要一个 4 维数组,但从上面可以看到,我们有一个 3 维 numpy 数组。

x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
input_shape = (28, 28, 1)

因此,这里我们将三维 numpy 数组转换为四维数组,在我们将类型设置为 float 后,除法运算后会有浮点值。

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')

现在到了正常化的部分,我们总是会在我们的神经网络中这样做。方法是将其除以 255(最大 RGB 码减去最小 RGB 码)。

x_train /= 255
x_test /= 255

建立模型:

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Dropout, Flatten, MaxPooling2D
model = Sequential()
model.add(Conv2D(28, kernel_size=(3,3), input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(128, activation=tf.nn.relu))
model.add(Dropout(0.2))
model.add(Dense(10,activation=tf.nn.softmax))

我使用 Keras API 来构建模型,因此我有一个 Tensorflow 背景。我从 Keras 导入顺序模型,并添加 Conv2D、MaxPooling、Flatten、Dropout 和 Dense 层。

丢弃层通过在训练时忽略一些神经元来对抗过度拟合,而展平层在构建完全连接的层之前将 2D 阵列展平为 1D 阵列。

编译和拟合模型:

到目前为止,我们已经创建了一个非优化的空 CNN。然后,我设置了一个具有给定损失函数的优化器,该函数使用一个指标,并通过使用我们的训练数据来拟合模型。据说 ADAM 优化器比其他优化器更好,这就是我使用它的原因。

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(x=x_train,y=y_train, epochs=10)

在这里,我们仅用 10 个历元就获得了相当高的精度。由于数据集不需要很强的计算能力,所以你可以随意调整时段的数量,也可以随意调整优化器、损失函数和指标。

模型评估:

model.evaluate(x_test, y_test)

当评估该模型时,我们看到仅 10 个时期就以非常低的损失给出了 98.59%的准确度。

现在来验证它的预测:

image_index = 2853
plt.imshow(x_test[image_index].reshape(28, 28),cmap='Greys')
predict = x_test[image_index].reshape(28,28)
pred = model.predict(x_test[image_index].reshape(1, 28, 28, 1))
print(pred.argmax())

在这里,我们选择一个图像并运行它来获得预测,然后显示图像和预测,看看它是否准确。

作者图片

这就是你如何建立和实现一个简单的卷积神经网络。您可以将这个概念实现到各种不同类型的分类和其他类似的实现中。用你在什么上实现了这个概念来回应这篇文章。

机器学习中的数字意义

原文:https://towardsdatascience.com/digit-significance-in-machine-learning-dea05dd6b85b?source=collection_archive---------32-----------------------

追逐统计幽灵的终结

唉,机器学习中报告性能指标的标准很少被讨论。由于在这个主题上似乎没有一个明确的、广泛共享的协议,我认为提供我一直倡导的标准可能是有趣的,并且我试图尽可能地遵循这个标准。它来源于一个简单的前提,这个前提从中学开始就被我的科学老师灌输给我了:

科学报道的一般规则是,你写下的每一个数字都应该是“真实的”,不管“真实”的定义是什么。

让我们来看看这对统计量(比如测试性能)意味着什么。当你在科学出版物上写下以下陈述时:

测试准确率为 52.34%。

你说,就你所知,你的模型在从测试分布中提取的看不见的数据上成功的概率在 0.52335 和 0.52345 之间。

这是一个非常强烈的声明。

假设您的测试集由从正确的测试分布中抽取的 N 个样本组成。成功次数为 s 。成功率可以表示为一个二项式变量,它的平均概率 p 由样本均值估计:ps/n

其标准差为:σ=√p(1-p)

p = 0.5 时,其被限制在 0.5 以上。

在正态近似下,估计量的标准差为:δ=σ/√N

精度估计上的这个误差 δ 看起来是这样的,在大约 50%精度的最坏情况下:

换句话说,为了有把握地报告上面例子的 52.34%的准确性,您的测试集的大小应该至少在 3000 万样本的数量级!这种粗略的分析很容易转化为除准确性之外的任何可计数的量,尽管不能转化为像可能性或困惑这样的连续数字。

以下是一些常见机器学习数据集的插图:

在 ImageNet 上可以合理地报告多少位数的准确度?准确率在 80%左右,测试集为 15 万张图片:

√( 0.8 * 0.2/150000)=0.103%

也就是说你可以差点举报 XX。X%的数字,实际上每个人都这样做。

MNIST 怎么样,准确率在 99%以内

√( 0.99 * 0.01/10000)=0.099%

pf first,报 XX 就可以了。X%也是!

然而,最大的警告是,在大多数情况下,性能数据不是孤立地报告的,而是用于在相同的测试集上比较多种方法。在这种情况下,实验的两臂之间的采样差异相互抵消,即使样本量更小,它们之间的差异也可能具有统计学意义。估计图形方差的一个简单方法是进行自举重采样。一个更加严格的测试包括进行成对差异测试或者更一般的方差分析

报告超出其内在精度的数字可能很有诱惑力,因为性能数字往往在基线比较的上下文中更有意义,或者当人们认为测试集是固定的而不是从测试分布中抽取的样本时。当在生产中部署模型时,这是一个导致惊奇的实践,突然固定的测试集假设消失了,伴随着无关紧要的改进。更一般地说,这是一种直接导致过度适应测试集的实践。

那么,在我们的领域中,一个数字为“真”意味着什么呢?嗯,这很复杂。对于工程师来说,很容易认为尺寸不应超过公差。或者对物理学家来说,物理量不应该超过测量误差。对于机器学习实践者来说,我们不仅要应对测试集的采样不确定性,还要应对独立训练运行、不同初始化和训练数据混洗下的模型不确定性。

按照这个标准,很难确定机器学习中哪些数字是“真”的。解药当然是尽可能报告置信区间。置信区间是一种更精细的报告不确定性的方式,可以考虑所有的随机性来源,以及简单方差之外的显著性测试。它们的出现也向你的读者发出信号,表明你已经考虑了你所报告的内容的意义,而不仅仅是你的代码所给出的数字。正如这篇博文所解释的,一个用置信区间表示的数字可以被报告为超过其标称精度,尽管要小心,你现在不得不考虑用多少位数字来报告不确定性。一路都是乌龟。

更少的数字意味着更少的混乱和更好的科学。

避免报告超出统计意义的数字,除非你为它们提供明确的置信区间。这可以被认为是科学上的不当行为,尤其是在缺乏配对显著性检验的情况下,用来论证一个数字比另一个数字更好的时候。仅凭这一点,论文通常会被拒绝。总是怀疑用大量数字报告的数字的准确性是一个健康的习惯。请记住,在最坏的情况下,作为“气味测试”,统计显著性所需的样本数有 3000、300k 和 30M 的经验法则限制它将使你免于追逐统计幽灵。

(感谢为本文早期版本提供宝贵意见的许多同事。)

来自现代数据格式的数字污染

原文:https://towardsdatascience.com/digital-pollution-5a579f8c9bf?source=collection_archive---------53-----------------------

抛弃 JSON 如何减轻我们对环境的影响,并使应用程序更加灵敏和高效。

照片由艾拉·伊万内斯库

当听到数字污染时,你可能会想到一个位于农村的大型服务器农场,托管着废弃的脸书账户,其中有数百张照片,这些照片永远不会再被加载到浏览器中。或者你可能认为垃圾填埋场堆满了仍在工作,但几乎无用的 iPhone 4s。

作为开发者,为了地球和未来的人类,我们应该留下尽可能小的足迹。与废弃的脸书照片不同,我们的足迹可以成倍增长。让我解释一下…

让我们考虑一些看似简单的事情。这是为 Y-combinator 的黑客新闻实现的一个 API。

http://hn.algolia.com/api/v1/search?query=digitalpollution&标签=故事

下面是上面调用 API 的结果:(您不必阅读)

JSON 结果

酷毙了。按下按钮,拿香蕉。发出 URL 请求并获得您想要的故事,所有这些都可以在人类可读、机器可解析的 JSON 中获得。但是等等。

从什么时候开始我们的数据需要人类可读了?当然,在一个 API 的开发过程中拥有它可能是件好事,但是在最终产品中呢?看起来这是对文字和花括号的极大浪费。“[]} }]”是否向读者传达了很多意图或意义?但是当你把人类可读的格式和机器可解析的格式结合起来时,就会发生这种情况。有必要吗?这一切有什么意义?这样我就可以获取这个故事,并以一种漂亮的、人类可读的方式显示在我的应用程序中。我会向我的客户展示这个 JSON 吗?不要!那么,为什么我们要以人类可读的格式传递数据呢?

在我将其归咎于“货邪教编程”之前,也许我们应该进一步分析?人类出于什么原因想要阅读这些“数据”作为一名开发人员,拥有这个伟大的 API 不是很好吗?当然可以。这是一件好事。能够将我的请求结果打印到屏幕上,以确保我得到了“我需要的数据”,这真是太棒了但这不是我需要的数据。这是我需要的包装在文本中的数据,我不需要。这是我需要用文本表示的数据,我必须将它转换成我需要的数据。现在我有了 burn 资源和 CPU 解析和转换这些乱七八糟的东西,所以我终于可以得到我需要的数据了。

如果数据“人类可读”的目的是为了在开发过程中有所帮助,那么为什么不在对人类可读版本的请求中添加一个参数呢?类似于," &格式=可读。"然后,一个在我们知道我们正在读入正确的数据后,我可以删除那个参数,得到实际的数据。

这是为什么污染?

因为这是对 CPU、收音机和调制解调器的不必要的使用。这需要时间、金钱和精力。

不必要的 CPU 使用

为什么我们需要解析数据?如果我们只是收到了我们需要的数据,以约定的格式,我们可以把它放入我们需要的变量中。我们不需要一个 JSON 解码库,里面有处理器的成千上万条 if-then 指令。

无线电和调制解调器的不必要使用

尺寸很重要。与发送 200 字节的信息相比,发送 100 字节的信息会减少蜂窝无线电、家庭调制解调器或服务器调制解调器的能耗。

作为一个快速演示,让我们检查上面的一点 JSON。

"num_comments":0

这让我们知道上面检索到的文章标题没有评论。我可以在我的新闻阅读器应用程序中使用它,显示这篇文章没有评论。我们收到了多少 JSON 格式的数据?

JSON 通常以 UTF-8 格式传输。UTF 8 中的字符是可变长度的,但在这种情况下,它们都属于一个字节的范围。所以上面的“num _ comments”:0等于实际字符数。这里是 16 个字符,所以是 16 个字节。如果有 100 条注释,那么由于多了两个零,它应该是 18 个字节。"00."1000 条评论就是 19 个字节,依此类推。

如果我们不使用 JSON,只是将它作为序列化数据发送,我们可以将它作为无符号整数发送。这将只是 2 个字节,代表从 0 到 65,536 条评论。19 字节对 2 字节。我们节省了 17 个字节,节省了将近 90%。

JSON 版本:

00100010 01101110 01110101 01101101 01011111 01100011 01101111 01101101 01101101 01100101 01101110 01110100 01110011 00100010 00111010 00110000

数据版本:

00000000

传递同样的信息,这是一个非常显著的差异。
下面是一个 swift 示例,展示了类似这样的东西在 Swift 中的表现:

在操场上运行代码,打印结果将是:

**Article(id: 18798989, title: "JSON = fat", comments: 3, likes: 1000)****{length = 16, bytes = 0x0a004a534f4e203d206661740300e803}****Article(id: 18798989, title: "JSON = fat", comments: 3, likes: 1000)**

它用 ID、标题、评论数和赞数来序列化文章标题。结果是 16 个字节。

使用我们之前的 JSON 例子发送给我们的相同数量的数据仅仅是评论数,我们得到了两个更有意义的数字和文章的标题…哦,还有评论数。

这需要多少 JSON?

{ "id": 18798989, "title": "JSON = fat", "comments": 3, "likes": 1000 }

67 字节的 JSON 与 16 字节的紧密序列化数据。所以是的,我给 JSON fat 打了电话。

在这种情况下,我们使用不到 25%的数据来表示相同的信息。当然,一旦我们开始在一篇实际的文章中发送大量的内容,实际的节省将会下降。占用空间的实际文本。许多 API 甚至不包含长文文本。许多是以数字为中心的,这些节省是最显著的。

考虑来自同一黑客新闻 API 的以下请求,该 API 仅返回头版文章的元信息:

http://hn.algolia.com/api/v1/search?tags=front_page

在撰写本文时,这是它返回的 16137 个字节:

巨大的 JSON 回报

16 千字节,而如果我们使用紧密序列化数据,可能是 4 千字节。为了什么?只是为了让开发人员可以浏览一些文本,以验证我们在测试这段代码时是否得到了它,而不考虑这段代码可能会被使用成千上万次。想想如果 CNN 或福克斯新闻频道在他们的应用中使用这种类型的 API。他们可能是。想象一下如果 twitter 有呢?看起来是的。

数据就是金钱

他们可以在数据传输成本上节省多少钱?有人估计 twitter 每天使用12tb。在 AWS 上,这是 12,000 GB,每 GB 约 8 美分,每天约 960 美元或每年 350,400 美元。理论上,紧密的数据序列化可以将它们的传输成本降低到当前使用量的 25%。这可以为他们每年节省 262,800 美元。还不错。

他们也在存储这些数据,可能是序列化的… JSON,这是要花钱的。一年每天 12tb 的新推文,每年 4,380。让我们假设他们只保留了最近 5 年的推文。这相当于 21,000 万亿字节的存储推文数据。刚刚检查了 AWS 上的简单存储。这将花费每月 452,147 美元或每年 5,425,764 美元。紧密的电子监管似乎每年可以为他们节省 4,069,323 美元。也许他们正在压缩?JSON 通常会压缩到 50%左右。所以也许紧密的序列化每年只能节省几百万美元?但是压缩和解压缩会节省 CPU 周期。计算变得复杂,但证据相当简单。公司可以使用紧密的序列化来节省空间、时间和金钱。

这也是一个环境问题。成本和数据节约接近 1:1。环境影响减少更多。减少 75%的数据将比减少 75%的排放或能源使用量还要多。能量消耗在于储存、压缩、发送和接收。通常,发送和接收是通过多个服务器或节点进行的。来自 twitter 的一个字节可能会跳过五个或更多的服务器到达你的设备。考虑这个从随机用户服务器到 twitter 服务器的跟踪路由。

从您的电话,这将包括您和第一个服务器之间或您的电话和您的路由器之间的无线电跳。然后……五个左右收发(10x)之间。减少 75%的数据比减少 75%的能源成本要多得多。每次发送和接收都减少了 75%。在这种情况下,数据减少 4 倍相当于传输的能源成本减少 40 倍。

效率和延迟

较小的数据传输速度更快,到达速度也更快。这使得您的应用程序响应速度更快。紧密序列化的数据不必通过 JSON 解码器用数千条 if-then 语句来解析。这使得你的应用程序反应更快。为了省钱,数据不必在服务器上压缩,这使得你的应用在请求数据时响应更快。纯粹、紧密、序列化的数据让你的应用更快。这使得客户体验更好,您的应用程序更节能。

当我在我的应用程序中这样做时,我发现性能和响应能力都得到了提高。

结论

我喜欢 Maxim Zaks 在他题为停止在生产中使用 JSON 的文章中所说的。“模式的主要好处是它描述了有效负载。当你有一个模式时,你的负载携带的描述是多余的。”API 开发人员花费数天时间编写模式来描述他们的 JSON 格式,这种格式已经在他们的 JSON 格式中描述过了。我们应该停止在生产中使用 JSON。我们应该描述从我们的参数返回的数据格式。

我把这个问题的解决方案称为“紧密序列化”类似的解决方案还有平面映射或协议缓冲区。最终,它只是由提供者描述并被接收者理解的有序数据。它不需要是人类可读的。它可以节省时间、金钱和能源,并使您的应用程序响应更快。

降维方法

原文:https://towardsdatascience.com/dimensionality-reduction-approaches-8547c4c44334?source=collection_archive---------27-----------------------

获取主要变量的方法,以更好地表示数据,提高效率,节省时间。

2019 年 12 月 30 日,普雷纳·辛格,金斯敦。

大数据的全面爆发让我们相信还有更多的东西。当然,虽然大量的训练数据允许机器学习模型学习更多的规则并更好地概括新数据,但不加选择地引入低质量的数据和输入特征可能会引入太多的噪声,同时大大降低训练速度,这也是事实。此外,如果数据集包含大量数据列,那么最好查看这些数据要素中有多少对模型真正有用。

在机器学习中,我们倾向于在一开始添加尽可能多的特征,以抓住有用的指标,获得更准确的结果。然而,随着元素数量的增加,模型的输出将在某个级别后减少。这种现象通常被称为“维数灾难”。

维数灾难之所以存在,是因为随着维数的增加,样本的密度呈指数下降。如果我们继续增加特征而不增加训练样本的数量,特征空间的维度将会扩大,变得越来越稀疏。由于这种稀疏性,机器学习模型找到很可能导致过拟合的正确解决方案变得简单得多。当模型过于接近一组特定的数据并且不能很好地概括时,就会发生过度拟合。

然而,我们能否克服维数灾难,避免过度拟合,特别是当我们有很多特征而训练样本相对较少的时候?一种常见的方法是降低特征空间的维数。降维是通过获得主要特征的集合来考虑降维函数空间的方法。降维可以进一步分为特征的收集和特征的提取。

特征的选择试图挑选原始特征的子集用于机器学习模型。这样,我们可以删除冗余和过时的特征,而不会导致太多的信息损失。特征的提取也称为特征的投影。虽然特征的选择返回原始特征的子集,但是特征的提取通过将数据投影到高维空间中的较小维度的空间来创建新的特征。也可以从该方法中提取信息和非冗余功能。

我们可以一起使用特征的选择和特征的提取。除了减少过度拟合和冗余,维度的减少还导致更好的人类解释和模型简化的更低计算成本。

降维的优势

  1. 它减少了所需的时间和存储空间。
  2. 多重共线性的去除改善了机器学习模型的参数的解释。
  3. 当数据降低到非常低的维度时,如 2D 或 3D,将变得更容易可视化。
  4. 降低空间复杂度。
  5. 更容易理解,因为它消除了噪音,从而提供了一个更简单的解释。
  6. 以减轻“维数灾难”。

我们讨论了降维的好处,并概述了降维要求。我们现在将详细讨论降维的两个关键技术,即

  1. 主成分分析
  2. 线性判别分析

例如,将使用来自 UCI 机器学习知识库的参考文献、葡萄酒数据集。该数据集相当小,具有六个目标类别和十一个维度的特征集(即十一个不同的特征,如固定酸度、pH 值、酒精含量等,以预测葡萄酒的质量)。

可以通过下面的链接下载数据集。https://github . com/psi49/revising _ machine learning/blob/master/wine quality _ red . CSV

PCA 概念是如何工作的?

主成分分析(PCA)是一种无监督的学习算法,因为它忽略了使数据集中的方差最大化的类别标签(所谓的主成分),以找到方向。换句话说,PCA 基本上是数据的汇总。例如,为了获得葡萄酒的质量/类型,我们可以使用葡萄酒的不同特征,例如其 pH 值、酒精含量、葡萄酒的颜色、酸度含量等,然而,这些特征中的许多将是冗余的或虚拟的特征变量(可以从其他特征中导出),因此导致在不必要的特征上训练模型。简而言之,我们可以用更少的特征变量得到葡萄酒的类型,这实际上是 PCA 在盒子里做的。

请注意,PCA 不会选择一组特征并丢弃其他特征,而是推断出一些新的特征,这些新的特征从现有的特征中最好地描述了类别的类型(在我们的例子中是葡萄酒的类型)。

以前,在推导正式定义时,我们提出了一个短语—最大化数据集中的方差。现在问题来了——“方差”这个词和 PCA 有什么关系?记住,我们的主要任务是定义一个特征集来区分不同类型的酒。想象一下,你得到了一组不能区分酒的种类的特征,因此,这些特征是没有用的。这种类型的降维会降低模型的准确性,有时会导致数据拟合不足。因此,主成分分析寻找尽可能显示数据集差异的属性。

PCA 对协方差矩阵的特征向量和特征值起作用,这相当于将那些直的主成分线拟合到数据的方差。为什么?因为 PCA 确定数据集中被称为主成分的方差线,其中第一主成分具有最大方差,第二主成分具有第二最大方差,等等。

现在让我们以葡萄酒数据集为例,使用 sklearn 来实现 PCA,并理解数据是如何从高维度转换到低维度的。

让我们对葡萄酒数据集执行 PCA,并通过可视化表示进行分析:

import numpy as np
import pandas as pd
df=pd.read_csv(‘winequality_red.csv’)
def isQuality(quality):
if quality > 6:
     return 1
if (quality >= 5) and (quality <= 6):
     return 2
else:
   return 0

给定的数据有六种不同的质量类型。为了理解降维概念。葡萄酒的质量分为三个目标等级,如果你愿意,你也可以分为六个目标等级。

让我们用上面的函数转换成三个葡萄酒质量等级。

df[‘isQuality’] = df[‘quality’].apply(isQuality)
print(‘New classes are defined for the quality of wines:\n’,df[‘isQuality’].value_counts())

新的葡萄酒质量等级定义如下:

第二类: 1319

第一类: 217

第 0 类: 63

从数据集中删除 quality 列,因为我们已经创建了一个包含三个葡萄酒质量等级的新列。

df=df.drop(‘quality’, axis=1)

将数据分为要素和目标类。

feature=df.iloc[:,0:11]
target=df[‘isQuality’]
print(feature.shape)
print(target.shape)

特征和目标列的形状。

(1599, 11)

(1599,1)

使用 sklearn 的 train_test_split 函数将数据拆分为 train 和 test。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(feature, target, test_size = 0.2, random_state = 42)

缩放数据集

缩放数据集-可以使用最小-最大归一化来缩放具有平均值零和单位标准差的数据集。

该估计器单独缩放和转换每个特征,使得它在数据集上的给定范围内,例如在 0 和 1 之间。

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train=scaler.fit(X_train).transform(X_train)
X_test=scaler.fit(X_test).transform(X_test)

在 sklearn 中应用内置 PCA 功能的 PCA:

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X_train = pca.fit(X_train).transform(X_train)
X_test= pca.fit(X_test).transform(X_test)

这里,特征是训练数据集,n_component 是我们想要从现有特征集中导出的 PCA 分量的数量。

因此,使用 sklearn,PCA 就像一个黑盒(如上所述),您给定一个缩放的特征集作为 sklearn PCA 的输入,并获得 PCA 分量作为输出,它可以用作数据训练算法的输入。对于 PCA 算法,在我们找到主成分之前,有必要执行数据缩放。

训练算法

在这里,我们将使用一个简单的逻辑回归工具来训练主成分,以解决这个分类问题。为此,请执行以下代码:

from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(random_state = 0)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)# Predicting the test set result using. Predict function under Random Forest Classifier
y_pred = clf.predict(X_test)

评估算法

对于分类问题,用于评估算法的度量是准确度、混淆矩阵、精确度、召回率和 F1 值。执行以下脚本来查找这些值:

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

输出将如下所示:

仅使用原始数据集中 11 个特征中的两个特征集,我们就获得了 84.06%的准确度。我不认为这太糟糕。使用逻辑回归模型的超参数优化或使用 sklearn 库中的一些其他模型,可以进一步提高模型精度。尝试多于 2 个且少于 6 个主成分的训练模型。使用 PCA 将是一个有趣的练习。

这里,我们将绘制两个图表,一个仅考虑 11 列中的两列,另一个绘制主成分,以了解 PCA 如何使高维数据的可视化变得容易。

import seaborn as sns
import matplotlib.pyplot as plt
sns.set(rc={‘figure.figsize’:(12,7)})
sns.scatterplot(x=”free sulfur dioxide”, y=”total sulfur dioxide”, hue=”isQuality”,data=df,palette=”Set1")
plt.show()

sns.scatterplot(pca_df[:,0],pca_df[:,1], hue=”isQuality”,data=df, palette=”Set1")
plt.show()

上图显示了使用两个特征绘制的数据点与使用主成分(基本上是我们数据集中所有特征的总结)即 PCA_1 和 PCA_ 绘制的数据点。这有助于我们识别 PCA 的另一种用途,即我们的数据集及其“质量”类别标签的适当和可分离的可视化。如果你看这个图表,你可以用两个特征变量而不是十三个特征变量来正确地显示整个数据集。虽然它们不能很好地区分葡萄酒质量等级(如二氧化硫和总二氧化硫之间的区别),但有助于以最小的信息损失减少特征集。

到目前为止,我们已经通过一些实际例子对 PCA 有了很好的理解。我们已经看到很少的组件足以训练模型,PCA 也是一个伟大的工具来减少数据维度,并给我们权力来适当地可视化数据。

让我们继续前进,深入研究另一个降维工具“线性判别分析”。我将使用相同的葡萄酒数据集来提供实践经验。

LDA 概念是如何工作的?

线性判别式是一种降维的统计方法,它在各种类别中提供最高可能的区分度,用于机器学习以找到特征的线性组合,这可以以最佳性能分离两个或更多类别的对象。该方法基于判别函数,这些判别函数是基于称为训练集的一组数据来估计的。这些判别函数相对于特征向量是线性的。

LDA 的目标是在假设每一类中的数据由具有相同协方差的高斯概率密度函数描述的情况下,通过线性判别函数来最大化类间方差和最小化类内方差。

理解 LDA 背后的数学是很好的,但我不想压倒任何人。我们将通过保持事情的简单来理解,这样对任何人来说都很容易遵循。

LDA 帮助你找到类簇的边界。这将您的数据点投影到一条直线上,以便尽可能地区分聚类,每个聚类与质心的距离相对较近。

因此,问题出现了——如何识别这些聚类,我们如何得到 LDA 的精简特征集?

基本上,LDA 考虑每个类的数据点的质心。例如,对于 11 个不同的要素,LDA 将使用 11 个不同的要素数据集来查找每个类的质心。基于这一点,它现在定义了一个新的维度,它只不过是一个满足两个要求的轴。

  1. 最大化每个类质心之间的距离。
  2. 最小化每组内的方差(LDA 称之为散点,用 s2 表示)。

因此,本质是(平均值 a —平均值 b)/(平均值 a —平均值 b)

(mean_a — mean_b) =理想的大(S_a — S_b) =理想的小

这里的意思无非是类质心。方差只不过是数据在平面上的分布。因此,如果数据差异很小,则类之间的重叠会更少,并且不同类之间的总体区别会得到保留。

因此,无论新轴的哪个坐标满足这两个条件,它们都会形成新的数据集维度。

现在让我们跳到在 wine 数据集上使用 sklearn 实现 LDA,看看数据是如何从高维度过渡到低维度的。

让我们对葡萄酒数据集执行 LDA 并进行图形分析:

所有必要的库都已经导入,让我们用 sklearn 中内置的 LDA 函数导入 LDA。

from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
lda = LDA(n_components=2)
lda_df = lda.fit(feature, target).transform(feature)
sns.scatterplot(lda_df[:,0],lda_df[:,1], hue=”isQuality”,data=df, palette=”Set1")
plt.show()

注意:我想澄清的是,像(标准化)这样的特征缩放不会改变 LDA 的整体结果,因此是可选的。

在主成分分析图中,您可以可视化整个数据集,正确区分不同的类。而在 LDA 图中,由于类别的不均匀分布,类别不是明显可分的。这是 PCA 和 LDA 的主要区别。

总的来说,PCA 在每类样本较少的情况下表现更好。在这个葡萄酒数据集中,我们有高计数的类别 2 (1319),其次是类别 1 (217),最后是类别 0 (63)。

虽然 LDA 在处理大的多类数据集时效果更好,其中类的可分性是降维时的一个重要因素。

一般的 LDA 方法非常类似于主成分分析,但是除了找到使我们的数据的方差最大化的成分轴(PCA)之外,我们还对使多个类别之间的分离最大化的轴(LDA)感兴趣。

最后,我们来看看如何在 sklearn 内部选择降维方法中 n_components 的个数。简单的方法是使用 explained_variance_ratio_ 并绘制图表,以查看我们需要多少组件来保留新功能集的方差。让我们通过这样做来理解

scaler_df=scaler.fit(df).transform(df)pca = PCA().fit(scaler_df)
plt.figure()
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel(‘Number of n_Components’)
plt.ylabel(‘Variance (%)’) #for each component present in the dataset
plt.title(‘Wine Dataset Explained Variance’)
plt.show()
plt.ylabel(‘Variance (%)’) #for each component present in the dataset
plt.title(‘Wine Dataset Explained Variance’)
plt.show()

这张图告诉我们,选择 8 个成分,我们可以保留葡萄酒数据总方差的 90 %以上。这是有意义的,我们不会使用 100%的方差,因为它表示所有的成分,我们只需要主要的成分。

现在,通过使用 n_components=8,我们可以执行相同的分析并训练任何模型。值得一玩。用于机器学习的高级 Python 库实际上使学习变得非常容易,不像早期我们必须从头开始硬编码。

祝你好运!

降维——主成分分析能提高分类模型的性能吗?

原文:https://towardsdatascience.com/dimensionality-reduction-can-pca-improve-the-performance-of-a-classification-model-d4e34194c544?source=collection_archive---------44-----------------------

利用 PCA——降维技术提高 ML 模型的性能

奥拉夫·阿伦斯·罗特内在 Unsplash 上的照片

什么是 PCA?

主成分分析(PCA)是数据科学中一种常见的特征提取技术,它采用矩阵分解将数据的维度降低到较低的空间。

在现实世界的数据集中,数据中通常有太多的要素。要素数量越多,数据的可视化和处理就越困难。有时大多数特征是相关的,因此是多余的。因此,特征提取开始发挥作用。

关于数据:

本文使用的数据集是来自 UCI 机器学习知识库的电离层数据集。这是一个二元分类问题。有 351 个观察值和 34 个特征。

准备数据集:

  • 导入必要的库并读取数据集
  • 数据集预处理
  • 标准化

(作者代码)

使用全部 34 个特征的逻辑回归 ML 模型:

训练数据有 34 个特征。

  • 在对数据进行预处理后,使用逻辑回归算法对训练数据进行二值分类训练
  • 微调逻辑回归模型以找到最佳参数
  • 计算训练和测试准确度以及 f1 分数。

(图片由作者提供),34 个特征数据集的逻辑回归模型的 C 与 F1 得分图

  • 使用 c=10**0 的 34 个特征训练 LR 模型
  • 计算训练和测试准确度以及 f1 分数

(图片由作者提供),训练测试准确性和 F1 分数,混淆矩阵

通过训练具有 34 个特征的整个“X_train”数据获得的结果,

测试 f1 分数为 0.90,因为在混淆矩阵中观察到 14 个值被错误分类。

(作者代码)

使用主成分分析的特征提取;

为了使用 PCA 技术从数据集中提取特征,首先我们需要找到解释为维度减少的方差百分比。

Notations,
**λ:** eigenvalue **d:** number of dimension of original dataset
**k:** number of dimensions of new feature space

(图片由作者提供),解释的方差百分比与维度数量的关系图

  • 从上面的图中可以看出,对于 15 个维度,解释的方差百分比为 90%。这意味着我们通过将较高的维度(34)投影到较低的空间(15)来保留 90%的方差。

(作者代码)

使用 PCA 的前 15 个特征训练逻辑回归 ML 模型:

现在 PCA 降维后的训练数据有 15 个特征。

  • 在对数据进行预处理后,使用逻辑回归算法对训练数据进行二值分类训练
  • 微调逻辑回归模型以找到最佳参数
  • 计算训练和测试准确度以及 f1 分数。

(图片由作者提供),15 个特征数据集的逻辑回归模型的 C 与 F1 分数的关系图

  • 使用 c=10**0 的 15 个特征训练 LR 模型
  • 计算训练和测试准确度以及 f1 分数

(图片由作者提供),训练测试准确性和 F1 分数,混淆矩阵

通过用 15 个特征训练 PCA 数据获得的结果,

测试 f1 分数为 0.896,因为混淆矩阵中观察到 12 个值被错误分类。

比较上述两种模型的结果:

(图片由作者提供),训练测试准确性和 F1 分数,混淆矩阵

(作者代码)

使用原始数据+来自 PCA 的数据训练 LR 模型:

在连接具有 34 个特征的原始数据和具有 15 个特征的 PCA 数据之后,我们形成了具有 49 个特征的数据集。

  • 在对数据进行预处理后,使用逻辑回归算法对训练数据进行二值分类训练
  • 微调逻辑回归模型以找到最佳参数
  • 计算训练和测试准确度以及 f1 分数。

(图片由作者提供),49 个特征数据集的逻辑回归模型的 C 与 F1 得分图

  • 使用 c=10**0 的 15 个特征训练 LR 模型
  • 计算训练和测试准确度以及 f1 分数

(图片由作者提供),训练测试准确性和 F1 分数,混淆矩阵

(作者代码)

从上述结果得出的结论:

(图片由作者提供),上述三种模型的准确度和 F1 得分结果

从上表中,我们可以观察到,

  • 使用具有 34 个特征的原始预处理数据集训练的 LR 模型,我们得到 90%的 F1 分数。
  • 使用 PCA 仅用提取的 15 个特征训练的 LR 模型,我们得到 89%的 F1 分数。
  • 用上述两个数据的组合训练的 LR 模型,我们得到 92%的 F1 分数。

让我们观察上述 3 个模型的混淆矩阵结果的变化。

(图片由作者提供),上述三种模型的混淆矩阵

因此,我们得出结论,仅使用 PCA 提取的特征,仅使用来自原始数据的 50%数量的特征,我们得到少 1%的 F1 分数。但是,如果我们将两个数据结合起来,我们将改进 2%的指标,以获得 91%的最终 F1 分数。

点击下方获取代码:

[## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/1hH5vJHNgCXohWhT5GXh5jzCasDtVxGHF?usp=sharing)

感谢您的阅读

数据可视化的降维:主成分分析与 TSNE、UMAP 和线性判别分析

原文:https://towardsdatascience.com/dimensionality-reduction-for-data-visualization-pca-vs-tsne-vs-umap-be4aa7b1cb29?source=collection_archive---------1-----------------------

使用 PCA、TSNE、UMAP 和 LDA 在 2D 和 3D 中可视化高维数据集

照片由 Unsplash 上的 Hin Bong Yeung 拍摄

在这个故事中,我们将介绍三种专门用于数据可视化 : PCA、t-SNE、LDA 和 UMAP 的降维技术。我们将使用手语 MNIST 数据集来详细探索它们,而不是深入研究算法背后的数学。

什么是降维?

许多机器学习问题涉及成千上万个特征,拥有如此大量的特征会带来许多问题,最重要的是:

  • 使训练极其缓慢
  • 很难找到好的解决方案

这就是所谓的 维数灾难 简而言之,降维就是将特征的数量减少到最相关的数量的过程。

降低维数确实会丢失一些信息,但是大多数压缩过程都会有一些缺点,即使我们训练得更快,我们也会使系统性能稍差,但这没关系!“有时降低维度可以过滤掉一些存在的噪音和一些不必要的细节”。

大多数降维应用用于:

  • 数据压缩
  • 降噪
  • 数据分类
  • 数据可视化

降维最重要的一个方面,就是 数据可视化。

降维的主要方法

降维的两种主要方法:投影流形学习。

  • 流形学习:**许多降维算法通过对训练实例所在的流形进行建模来工作;这叫做流形学习。它依赖于流形假设或假设,认为大多数真实世界的高维数据集都接近一个低得多的维流形,这种假设在大多数情况下是基于观察或经验,而不是理论或纯逻辑。[4]**

现在,在开始解决用例之前,让我们简要地解释一下这三种技术。

主成分分析

最著名的降维“无监督”算法之一是 PCA(主成分分析)。

其工作原理是识别最接近数据的超平面,然后将数据投影到该超平面上,同时保留数据集中的大部分变化。

主成分

解释训练集中最大方差的轴称为 主成分

与该轴正交的轴称为第二主分量。当我们追求更高的维度时,主成分分析会找到与其他两个分量正交的第三个分量,以此类推,为了可视化的目的,我们总是坚持 2 个或最多 3 个主分量。

来源:Packt_Pub,via Hackernoon

选择正确的超平面非常重要,这样当数据投影到超平面上时,可以获得关于原始数据如何分布的最大信息量。

T-SNE(T-分布式随机邻居嵌入)

(t-SNE)T-分布式随机邻居嵌入于2008 由 ( 劳伦斯·范·德·马腾和杰弗里·辛顿)创建,用于降维,特别适合于高维数据集的可视化。

(t-SNE) 取一个高维数据集,把它还原成一个低维图,保留了很多原始信息。它通过在二维或三维地图中给每个数据点一个位置来做到这一点。这种技术发现数据中的聚类,从而确保嵌入保留了数据中的含义。t-SNE 减少维数,同时试图保持相似的实例接近,不相似的实例分开。[2]

要快速了解这种技术,请参考下面的动画(它摘自西里尔·罗桑特的精彩教程,我强烈推荐看看他的精彩教程。

链接:https://www . oreilly . com/content/an-illustrated-introduction-to-the-t-SNE-algorithm/

来源:西里尔·罗桑特,转自奥雷利

LDA ( 线性判别分析)

模式分类的预处理步骤中,线性判别分析(LDA)是最常用的降维技术。****

目标是将数据集投影到具有良好类别可分性的低维空间上,以避免过拟合并降低计算成本。

一般的方法非常类似于 PCA,而不是找到使我们的数据方差最大化的分量轴, 我们另外感兴趣的是使多个类之间的间隔最大化的轴(LDA)【5】。

LDA 是“受监督的”并计算方向(“线性判别式”),这些方向将代表使多个类之间的间隔最大化的轴。

来源:https://sebastianraschka.com/Articles/2014_python_lda.html

UMAP(均匀流形近似和投影)

( 利兰·麦金尼斯约翰·希利詹姆斯·梅尔维尔)于 2018 年创建的均匀流形逼近与投影、T15,是一种通用的流形学习与降维算法。****

UMAP 是一种 非线性 降维方法,对于可视化聚类或组数据点及其相对邻近度非常有效。

与 TSNE 的显著区别是可伸缩性,它可以直接应用于稀疏矩阵,从而无需应用任何维数缩减,如 s PCA 或截断 SVD(奇异值分解)作为之前的预处理步骤【1】

简而言之,它类似于 t-SNE,但可能具有更高的处理速度,因此,更快,可能更好的可视化。(让我们在下面的教程中找出答案)

用例

现在,我们将浏览上述所有三种技术都将被应用的用例:具体来说,我们将尝试使用这些技术来可视化一个高维数据集:T 何手语-MNIST 数据集:

(手语-MNIST 数据集),截图自 kaggle.com

数据

****

训练数据的大小

唯一标签的数量

注意:有 25 个独特的标签代表不同手语的数量。现在为了更好的可视化(“在单个可视化中观察所有 24 个类是非常困难的”)和更快的计算。我只保留前 10 个标签,省略其余的。

实现绘图功能

数据标准化

实施 PCA

在应用 PCA 之后,与 x 数据的 784 个特征相比,数据的新维度现在只有 3 个特征。

维度的数量已经大幅减少,同时试图尽可能多地保留信息中的“变化”。

— PCA — 2D —

plot_2d(principalComponents[:, 0],principalComponents[:, 1])

作者图片

从 2D 图中,我们可以看到这两个部分肯定包含了一些信息,尤其是特定的数字,但显然不足以将它们区分开来。

— PCA— 3D —

plot_3d(principalComponents[:, 0],principalComponents[:, 1],principalComponents[:, 2])

作者图片

实施 t-SNE

需要注意的一点是, t-SNE 的计算开销非常大,因此在其文档中提到:

“如果特征的数量非常多,强烈建议使用另一种降维方法(例如,针对密集数据的 PCA 或针对稀疏数据的 TruncatedSVD)将维数减少到合理的数量(例如,50)。这将抑制一些噪声,并加快样本之间成对距离的计算。”[2]

因此,我应用了 PCA,选择保留原始数据中的 50 个主成分,以减少对更多处理能力的需求,并且如果我们考虑了原始数据,将需要时间来计算维度缩减。

这三种技术的速度将在以下章节中进一步详细分析和比较。

— t-SNE — 2D —

plot_2d(tsne[:, 0],tsne[:, 1])

作者图片

与 PCA 2d 结果相比,我们可以清楚地看到不同聚类的存在以及它们是如何定位的。

— t-SNE — 3D —

plot_3d(tsne[:, 0],tsne[:, 1],tsne[:, 2])

作者图片

实施 UMAP

UMAP 具有不同的超参数,这些参数会对生成的嵌入产生影响:

  • n_neighbors

该参数控制 UMAP 如何平衡数据中的本地和全局结构。n _ neighbours 的低值迫使 UMAP 关注非常局部的结构,而较高的值将使 UMAP 关注较大的街区。

  • min_dist

该参数控制允许 UMAP 将点打包在一起的紧密程度。较低的值意味着这些点将紧密地聚集在一起,反之亦然。

  • n_components

该参数允许用户确定降维空间的维度。

  • metric

此参数控制如何在输入数据的环境空间中计算距离。

更详细的信息,我建议查阅 UMAP 文档😕/umap-learn . readthedocs . io/en/latest/

UMAP(默认设置)

对于本教程,我选择保留默认设置 n_components ,我为 3d 空间图设置为 3。最好试验不同的超参数设置,以获得算法的最佳效果。

— UMAP— 2D —

plot_2d(reducer.embedding_[:, 0],reducer.embedding_[:, 1])

作者图片

我们可以清楚地看到,与 t-SNE 和 PCA 相比,UMAP 在分离数据点方面做得非常好。然而,从 2d 的角度来看,没有大的聚类充分地分隔符号,在其他部分也有相似的数据点聚集在一起。

— UMAP— 3D —

plot_3d(reducer.embedding_[:, 0],reducer.embedding_[:, 1],reducer.embedding_[:, 2])

作者图片

实施 LDA

— LDA— 2D —

plot_2d(X_LDA[:, 0],X_LDA[:, 1])

作者图片

利用 LDA,我们可以清楚地识别出这九个具有显著分离的集群的存在。如果使用 UMAP 和 SNE 霸王龙,我们几乎看不到聚类的主干,而使用 LDA,我们可以看到聚集在相同聚类区域的数据点的整个聚类。

— LDA— 3D —

plot_3d(X_LDA[:, 0],X_LDA[:, 1],X_LDA[:, 2])

作者图片

降维技术的比较:主成分分析 vs t-SNE vs UMAP vs LDA

作者图片

速度比较

注意:上表是考虑到 Kaggle 上使用 GPU 的内核的计算时间而构建的。

  • PCA 不能很好地区分这些信号。PCA 的主要缺点是受数据中异常值的影响很大。PCA 是一种线性投影,这意味着它不能捕捉非线性依赖关系,它的目标是找到使数据集中的方差最大化的方向(所谓的主成分)。
  • PCA 相比,t-SNE 在可视化集群的不同模式方面做得更好(它试图保持拓扑邻域结构)。相似的标签被聚集在一起,即使有大量的数据点在彼此之上,当然也不足以期望聚类算法表现良好。
  • UMAP 的表现优于 t-SNEPCA ,如果我们观察 2d 和 3d 图,我们可以看到被很好分离的迷你星团。这对于可视化集群或组数据点及其相对接近度非常有效。然而,对于这个用例来说,肯定没有好到期望一个聚类算法来区分模式。 UMAPt-SNE 快得多,后者面临的另一个问题是需要另一种降维方法优先,否则计算时间会更长。**
  • 最后, LDA 在各方面都超过了之前所有的技术。出色的计算时间(第二快)以及证明我们所期望的分离良好的集群。

注意:LDA 比其他技术表现得更好并不奇怪,这正是我们所期望的。与 LDA 不同,PCA、TSNE 和 UMAP 是在不知道真实类别标签的情况下执行的。

总结

我们探索了四种用于数据可视化的降维技术:(PCA、t-SNE、UMAP、LDA)并尝试使用它们在 2d 和 3d 图中可视化高维数据集。

注意:很容易陷入认为一种技术比另一种技术更好的陷阱,最终没有办法将高维数据映射到低维,同时保留整个结构,总有一种技术与另一种技术相比的质量权衡。

再次感谢到达这里,希望这是一个信息丰富的职位!值得你花时间。还有许多其他变体和其他用例,我强烈鼓励您探索这一令人惊叹且发展良好的科学领域。

参考

[1]麦金尼斯和希利(2018 年)。UMAP:一致流形逼近和降维投影。ArXiv 电子印花。

[2]范德马滕,l . j . p . t-分布式随机邻居嵌入

** [## t-SNE

t 分布随机近邻嵌入(t-SNE)是一种降维技术,特别适用于

lvd maten . github . io](https://lvdmaaten.github.io/tsne/)

[3]Kaggle.com. 2020。与 T-SNE 一起观想卡纳达语 MNIST。可在:https://www . ka ggle . com/parulpandey/visualizing-kannada-Mn ist-with-t-SNE获取

[4]通过 Scikit-Learn、Keras & Tensorflow 实践机器学习,作者 Aurelien Geron

[5]https://sebastianraschka.com/Articles/2014_python_lda.html**

降维取证

原文:https://towardsdatascience.com/dimensionality-reduction-forensics-50014430767f?source=collection_archive---------23-----------------------

入门

何时应用哪种降维技术

照片由 Pixabay 上的路缘石拍摄,编辑为 me 😄

介绍

在我上一篇关于 Gap-Statistics 的博文中,我解释了大多数时候数据都有很多特征(数百个甚至有时数千个)。我们认为具有 100 个特征的数据集是 100 维的。所以每个特征代表数据集的一个维度。对于可视化数据来说,这是相当多的维度。我们不能只保留三个特征,不是吗?当然不是!一个例外是,100 个特征中有 98 个高度相关(通过热图找出),所以我们省略了 97 个,只取其中的一个。然后我们会得到一个三维数据集。
但这种可能性极小!

因此,我将介绍两个在处理数据时需要了解的重要主题:

  • 1)维度的诅咒——更深入的解释
  • 2)降维取证

1)维度的诅咒

直观但重要的是要知道,所有模式分析或数据探索任务的复杂性随着维度的增加而增加。

我们称这种复杂性的增加为维度的诅咒。通常一些特征是完全无用的或者高度相关的。因此,可以应用降维技术来去除尽可能多的维度。我们真的想处理尽可能低维度的数据。

在处理高维数据时,我们可以推断出三个问题:

第一期:

如何绘制三维以上的数据?

是的,我们甚至可以在 4D 或 5D 的空间里进行策划,但是升级很快。

从技术上讲,有一些方法可以做到这一点。例如,我们可以改变数据点的大小来可视化第四维,或者我们可以额外引入一些颜色作为第五维。但是你可以看到升级的速度有多快,我们作为人类再也不能从 5D 的情节中读到任何有价值的东西了。所以这基本上是没用的,因为主要的一点是我们想从这些可视化中获取信息。

第二个问题:

特征空间的细分随着维度的增加呈指数上升。

图 1)特征空间

在上图最右边的图中,我们看到,在三维空间中,我们已经有 27 个大小相等的细胞。对于 15 个特征(=15 个维度),我们最终将在特征空间中拥有 1500 万个大小相等的细胞(3^15)。随机森林需要大量的参数来适应这个高维特征空间。如果你知道随机森林是如何工作的,那么你就知道它将特征空间分割成不同的分类区域,如果它是随机森林分类器而不是随机森林回归器的话。但是其他分类方法也需要不可行的更多参数来适应这样的高维空间。

第三期:

这可能是最重要的,但也是最令人惊讶的问题:

维度越高,位于特征空间最外边界的数据点越多。

图 2)边界行为|来自 HTF 第 2.5 节

从上面的图 2 中我们可以看到计算的公式,其中数据位于特征空间中。 D 是尺寸的数量,而 ε 表示边界壳的极限。如果选择 ε 为 0.01,表示有多少个数据点位于特征空间的外侧 1% 。ε = 0.75 意味着有多少数据点位于特征空间的外部 75% 。上面的绿色圆圈代表特征空间,其中 ε 表示它总是从边缘开始向圆圈的中心移动。

因此,回到图 1 我们可以看到,对于 200 的维度,我们有 86% 的数据点位于特征空间的外层 1% 。而在 3D 空间中,3%的数据位于 1%的外壳上。

这太疯狂了,不是吗?

特征空间的稀疏性随着维度的增加而显著增加。我们将需要大量的新数据样本来维持特征空间的恒定密度。但是,除非你使用一些非常奇特的生成模型,如甘的模型,否则你无法轻松地生成新的数据样本。

2)降维取证

在这一节中,我们将介绍四种降维技术。我对它们中的每一个都进行了简要的概述,但是仅仅是为了理解如何执行取证。不是每种维度技术都适合每种数据分布。在选择四种方法中的一种时需要非常小心,因为如果你选择了错误的技术,你会惊讶地发现它并没有像预期的那样起作用。我们需要区分全局和局部降维技术。这是我们将要介绍的四种技术:

    1. PCA(全球)
    1. MDS(全球)
    1. ISOMAP(本地)
  • 4)拉普拉斯特征映射(局部)

有更多的方法,但让我们把重点放在我认为基本上是主要的方法上。最先进的 t-SNE 没有包括在内,因为它会打破这个帖子的限制。

主成分分析

当您不知道需要使用哪种技术时,主成分分析(PCA)总是首选。对于许多数据科学家来说,这是一项繁重的工作,它通常会产生正确的结果,但也有失败的情况。我提到过 PCA 是一种全球性的技术。这意味着 PCA 仅通过矩阵乘法计算,然后将数据投影到新的轴上。

重要提示:投影的每一个轴都是相互正交的。因此,您可以将图 1 中的基本笛卡尔坐标系想象为投影轴。

基本步骤是:

  1. 将数据标准化为平均值 0(第 5 行)
  2. 对数据的协方差矩阵执行特征分解(第 9 行)
  3. 按照特征值的大小降序排列特征向量(第 11–15 行)
  4. 选择最大特征值的个数,将数据投影到特征向量上,将其作为新的轴。(第 19–21 行)

这四个步骤没什么特别的。它实际上只是几行 Python 代码:

92.46+0.00j% explained variance in vector 0
5.30+0.00j% explained variance in vector 1
1.72+0.00j% explained variance in vector 2
0.52+0.00j% explained variance in vector 3

你可以看到全局降维技术有另一个非常有用的特点,这是局部技术所没有的。全局方法揭示了在应用降维之后数据保留了多少差异。虹膜数据集是一个众所周知的尝试降维的数据集。它有四个维度,但只保留一个维度仍然保持数据总方差的 92.46% (从上面的灰色输出框“92.46+0.00j”->可以看出,这是一个复数表示)。这意味着 92.46%的信息仍然只保存在一个维度内。

图 3)我制作的 PCA 示例

左图显示了 PCA 的另一个例子。请注意每个红色数据点是如何投影到新的蓝色轴上的。这将保留红点所有位置信息的 96.62%。

图 4)我制作的 1D 主成分分析投影示例

我使用了具有两个不同聚类的数据集。在减少了从 2D 到 1D 的维度后,即使将它们投射到蓝色箭头上,星团之间仍然保持着距离。

结论:我们只能用 PCA 将数据线性投影到特征向量轴上。请记住这一点。你能想象 2D 数据分布很难线性映射并保留大部分信息吗?

2) MDS

经典的多维标度(MDS)将更简单,因为它非常类似于 PCA。MDS 和 PCA 之间的唯一区别是,MDS 将点之间的距离作为输入。如果欧几里德距离范数用于 MDS,它的行为 100%类似于 PCA 。因此,MDS 也能够测量保留了多少差异。保存的公式是:

图 5)方差的最大保存

请注意,d’(d 撇)是缩减的维度(例如 1D),d 是初始维度(例如 3D)。d* (d 星)是最佳降维。我们可以将所有的目标维度方差相加,然后除以 100,因为它从 100%方差开始。

图 6)弯头方法

当你阅读我上一篇关于差距统计的博文时,你会注意到上面的这个数字是多么的相似。肘方法可用于可视化方差如何随着维度的减少而缩小。x 轴表示维度的减少。例如,在 x=2 时,初始维度减少了两个维度,我们仍然保留了大约 80%的总方差。最大的差距表明我们的最佳缩减结果可能是在只缩减两个维度,因为我们通过缩减三个维度的初始数据丢失了如此多的方差。

当对 MDS 使用不同的距离度量时

  • 欧几里德距离
  • 曼哈顿距离
  • 汉明距离
  • LogFC

很难知道最佳指标是什么。当您确定全局降维技术足以完成您的任务时,您可以尝试许多不同的距离度量,并绘制肘方法图,以查看哪种最小维度可以保持最大的差异。

结论:由于使用欧几里德距离时 MDS 与 PCA 相同,所以可以直接选择 MDS 而非 PCA。然后你很快就能使用不同的测试距离指标。

PCA 和 MDS 的线性取证

图 7)线性取证

让我们看看上面图 7 中的数据分布。它看起来像蜗牛的壳。无论我们如何将数据投射到绿色的主成分线(或 MDS 线)上,我们都永远不能保留太多的方差。上图的右部显示了数据如何理想地投影到绿线上。这并没有给我们带来任何进展。

这同样适用于非常高维度的空间。我们真的无法想象 100 维的特征空间会是什么样子,但同样的原理也适用。有些分布不能用线性降维技术来解释。

因此,当我们看到降维揭示了对潜在数据聚类的零洞察时,如上所述,我们可以肯定 PCA 或 MDS 不是正确的选择。

3) ISOMAP

先说局部降维。我会给你算法,然后我会解释它:

  1. 使用 K 最近邻算法(kNN)定义局部邻域。
  2. 用欧几里得距离计算所有点到另一个点的距离,以得到我们的图在所有点之间的边。
  3. 使用 Dijktra 算法计算所有点到另一个点的最短距离。
  4. 使用此最短距离矩阵作为 MDS 算法的距离度量。

啊,我们亲爱的 MDS。这么有用!kNN 需要一个固定的超参数,即 K 的数量。问题是,我们不能真正知道 K 的最佳值是多少。因此,由于 kNN 的半径变得太大,局部邻域可能包括并不真正属于它的点,因为存在太稀疏的数据点。应用 Dijkstras 的算法并使用这个距离矩阵作为 MDS 的输入很简单,但是困难在于这四个步骤中的第一步。

ISOMAP 的全球取证

图 8)全球取证第 1 部分

让我们考虑一下图 8 最左侧上方底部开放的蛋形数据分布。如果我们能沿着绿线投影数据,那将是最好的降维映射。数据分布的本地结构将被保留。紫色圆圈表示两个本地邻域的例子。

你可以这样想象:

图 9)全球取证第 2 部分

这是最理想的情况。如果数据分布看起来像这样,它将完全工作。但是我已经提到了 ISOMAP 的问题行为。如果你仔细观察图 8 最右边的图,你会发现有两个新的蓝色数据点。这些可能是由于样本噪声造成的。这导致投射到绿线上的致命变化。

结论:由于 kNN,ISOMAP 对噪声样本非常敏感。因为 kNN 对于每个局部邻域的半径是不同的,因为它想要例如在它的局部邻域中找到 5 个点,而不管这些点位于多远或多近。这就产生了问题,并成为图 8 中 A 点到 B 点的桥梁,尽管这些点应该相距很远。

4)拉普拉斯特征映射

我也将从编写 LE 的基本算法开始,然后我将解释它:

  1. 定义局部邻域,并根据所有数据点构建邻接图。
  2. 测量图中各边的权重
  3. 对该图进行特征分解(如 PCA)。
  4. 像 PCA 一样,通过选择最大特征值的数量来执行低维嵌入,并且相对于它们相应的特征向量来映射数据。

邻接图基本上只是表示图中所有邻域点位置的矩阵(亲和矩阵),但是在一个矩阵内。这个亲和矩阵看起来像这样(它是二进制的,但是它也可以用除了二进制热核之外的其他核来计算):

拉普拉斯特征映射的全局取证

图 10)全球取证第 3 部分

W 表示二元亲和矩阵。上面的 W 是没有噪声的数据分布,而下面的 W 包括一些绿色噪声样本点。你可以看到,嘈杂的点想要再次创建一个从 A 到 B 的桥梁,但亲和矩阵阻止了这种情况的发生,因为紫色的局部邻域键在对角线上太强了。噪声对拉普拉斯特征映射没有影响。优化的目的是使局部邻域尽可能靠近(因此邻域的距离尽可能小)。

因此,对于拉普拉斯特征映射会有最小化问题,但是对于 PCA、MDS 和 ISOMAP 会有最大化问题(这三个想要最大化数据的方差信息)。

结论:LE 本质上更适合执行这种流形学习(=学习低维嵌入),因为它对于噪声数据分布更鲁棒。正如我们所知,数据中经常存在噪声。在所有情况下,LE 都优于 ISOMAP。

进一步的调查

最后,让我们考虑一些测试示例:

图 11)进一步调查

  • 在第一个图中,我们可以很容易地拟合一条线或一个邻居通过。
  • 对于第二个地块来说,附近可能会有问题,因此 LE 是安全的选择。
  • 第三个图也将保留足够的方差,即使在拟合直线时也是如此。ISOMAP 和 LE 也没有问题,因为没有样本噪声和容易的局部邻域
  • 最后一个例子用绿色显示地面实况。当我们想从中取样时,有噪声的样本可能会使地面实况向不希望的方向移动。这就是为什么我们真的想使用 LE,因为 ISOMAP 可能会受到噪声的负面影响,从而产生错误的降维。

我希望你发现这篇文章内容丰富!如果你有问题,可以给我发邮件。你可以在我的网站上找到我的邮箱地址。

参考

[1] Trevor Hastie,Robert Tibshirani 和 Jerome Friedman,统计学习的要素:数据挖掘、推理和预测 (2009)

这篇博文基于从弗里德里希·亚历山大大学埃尔兰根-纽伦堡的课程模式分析中获得的知识。我使用了 Christian Riess 博士演讲的部分内容来说明这篇文章的例子。所以所有权利归克里斯汀·里斯博士所有。

数据挖掘中的降维

原文:https://towardsdatascience.com/dimensionality-reduction-in-data-mining-f08c734b3001?source=collection_archive---------16-----------------------

大数据是具有多级变量的大规模数据集,并且增长非常快。体量是大数据最重要的方面。随着数据处理和计算机科学领域最近的技术进步,最近大数据在记录数量和属性方面的爆炸式增长给数据挖掘和数据科学带来了一些挑战。大数据中极大规模的数据形成多维数据集。在大型数据集中有多个维度,这使得分析这些维度或在数据中寻找任何类型的模式的工作变得非常困难。高维数据可以从各种来源获得,这取决于人们对哪种过程感兴趣。自然界中的任何过程都是许多不同变量的结果,其中一些变量是可观察或可测量的,而另一些是不可观察或可测量的。当我们要准确地获得任何类型的模拟数据时,我们就要处理更高维度的数据。

克林特·王茂林在 Unsplash 上拍摄的照片

降维是减少所考虑的随机变量或属性数量的过程。作为数据预处理步骤的一部分,高维数据约简在许多现实应用中是极其重要的。高维降维已经成为数据挖掘应用中的重要任务之一。例如,您可能有一个包含数百个要素(数据库中的列)的数据集。那么,降维就是通过组合或合并来减少数据属性的特征,这样就不会丢失原始数据集的许多重要特征。高维数据的一个主要问题是众所周知的“维数灾难”。如果我们想使用数据进行分析,这就促使我们降低数据的维度。

维度的诅咒

维数灾难指的是当进行分类、组织和分析高维数据时出现的现象,这种现象在低维空间中不会出现,具体来说就是数据稀疏性和数据的“紧密性”问题。

获得的空间与总空间之间的差异(1/3、1/9、1/27)

上面的图表序列显示了当基础维度增加时数据的紧密性问题。随着上面看到的数据空间从一维移动到二维并最终移动到三维,给定的数据填充的数据空间越来越少。为了保持空间的精确表示所需的数据量随着维度成指数增长。

数据稀疏导致相似性降低

当维数增加时,随着稀疏度的增加,两个独立点之间的距离增加。这导致数据点之间的相似性降低,这将导致大多数机器学习和数据挖掘中使用的其他技术出现更多错误。为了进行补偿,我们将不得不输入大量的数据点,但是如果维数更高,这实际上是不可能的,即使有可能,效率也会很低。

为了克服上述高维数据带来的挑战,需要降低计划分析和可视化的数据的维度。

降维技术

基于特征选择特征提取完成维度缩减。特征选择基于从可用测量中省略那些对类别可分性没有贡献的特征。换句话说,冗余和不相关的特征被忽略。另一方面,特征提取考虑整个信息内容,并将有用的信息内容映射到较低维度的特征空间。

人们可以将用于降维的技术区分为线性技术和非线性技术。但是这里将基于特征选择和特征提取的观点来描述这些技术。

特征选择技术

特征选择过程

作为一项独立的任务,特征选择可以是无监督的(例如方差阈值)或有监督的(例如遗传算法)。如果需要,还可以结合使用多种方法。

1)方差阈值

这种技术寻找给定特征的从一个观察到另一个观察的变化,然后如果根据给定的阈值,在每个观察中的变化没有不同,则导致该观察的特征被移除。变化不大的功能并没有增加多少有效信息。在建模过程开始时,使用方差阈值是一种简单且相对安全的降维方法。但是,如果您想要减少维度,这本身是不够的,因为它非常主观,您需要手动调整方差阈值。这种特性选择可以使用 Python 和 r 来实现。

2)相关阈值

这里考虑这些特征,并检查这些特征是否彼此紧密相关。如果是这样的话,那么这两个特性对最终输出的总体影响甚至与我们使用其中一个特性时得到的结果相似。你应该删除哪一个?你首先要计算所有的成对相关。然后,如果一对要素之间的相关性高于给定的阈值,您将移除与其他要素具有较大平均绝对相关性的要素。像前面的技术一样,这也是基于直觉,因此以不忽略有用信息的方式调整阈值的负担将落在用户身上。由于这些原因,具有内置特征选择的算法或像 PCA 这样的算法比这种算法更受欢迎。

3)遗传算法

它们是受进化生物学和自然选择启发的搜索算法,结合变异和交叉来有效地遍历大型解决方案空间。遗传算法用于寻找最佳二进制向量,其中每个比特与一个特征相关联。如果该向量的比特等于 1,则该特征被允许参与分类。如果该位为 0,则相应的特征不参与。在特征选择中,“基因”代表个体特征,“有机体”代表一组候选特征。“群体”中的每一种生物都根据适合度评分进行分级,例如在坚持组中的模型表现。最适合的生物生存并繁殖,如此重复,直到几代后种群收敛于一个解。在这里你可以对遗传算法有进一步的了解。

**[Start]** Generate random population of *n* chromosomes (suitable solutions for the problem)**[Fitness]** Evaluate the fitness *f(x)* of each chromosome *x* in the population**[New population]** Create a new population by repeating following steps until the new population is complete : **[Selection]** Select two parent chromosomes from a population according to their fitness (the better fitness, the bigger chance to be selected) **[Crossover]** With a crossover probability cross over the parents to form a new offspring (children). If no crossover was performed, offspring is an exact copy of parents. **[Mutation]** With a mutation probability mutate new offspring at each locus (position in chromosome). **[Accepting]** Place new offspring in a new population**[Replace]** Use new generated population for a further run of algorithm**[Test]** If the end condition is satisfied, **stop**, and return the best solution in current population**[Loop]** Go to step **2 [Fitness]**

这可以有效地从非常高维的数据集中选择特征,而穷举搜索是不可行的。但在大多数情况下,人们可能会认为这不值得争论,这取决于上下文,因为使用 PCA 或内置选择会简单得多。

4)逐步回归

在统计学中,逐步回归是一种拟合回归模型的方法,其中预测变量的选择是通过自动程序进行的。在每一步中,基于一些预先指定的标准,考虑将一个变量添加到解释变量集或从解释变量集中减去。通常,这采取一系列F-测试t-测试的形式,但是其他技术也是可能的,例如调整的 R 2阿凯克信息准则贝叶斯信息准则马洛斯的 Cp按下这里可以更好的理解逐步回归

这有两种味道:向前和向后。对于向前逐步搜索,您开始时没有任何功能。然后,您将使用每个候选特征训练一个单特征模型,并保留具有最佳性能的版本。您将继续添加特性,一次添加一个,直到您的性能改进停止。向后逐步搜索是相同的过程,只是顺序相反:从模型中的所有特征开始,然后一次删除一个特征,直到性能开始显著下降。

这是一种贪婪算法,并且通常比诸如正则化等监督方法具有更低的性能。

特征提取技术

特征提取过程

特征提取是为了创建一个新的、更小的特征集,该特征集仍然捕获大部分有用的信息。这可以是有监督的(例如 LDA)和无监督的(例如 PCA)方法。

1)线性判别分析(LDA)

LDA 使用来自多个要素的信息创建一个新的轴,并将数据投影到新的轴上,以便最小化方差并最大化类平均值之间的距离。LDA 是一种监督方法,只能用于标记数据。它包含为每个类计算的数据的统计属性。对于单个输入变量(x ),这是每类变量的平均值和方差。对于多个变量,这是在多元高斯上计算的相同属性,即均值和协方差矩阵。LDA 变换也依赖于比例,因此您应该首先归一化您的数据集。

LDA 是有监督的,所以需要有标签的数据。它提供了各种变化(如二次 LDA)来解决特定的障碍。但是正在创造的新特征很难用 LDA 来解释。

2)主成分分析

PCA 是一种降维方法,它可以识别我们数据中的重要关系,根据这些关系转换现有数据,然后量化这些关系的重要性,以便我们可以保留最重要的关系。为了记住这个定义,我们可以把它分成四个步骤:

  1. 我们通过一个协方差矩阵 来识别特征之间的关系。
  2. 通过协方差矩阵的线性变换或特征分解,得到特征向量和特征值
  3. 然后,我们使用特征向量将数据转换成主分量。
  4. 最后,我们使用特征值量化这些关系的重要性,并保留重要的主成分

PCA 创建的新特征是正交的,这意味着它们是不相关的。此外,它们按照“解释方差”的顺序排列第一个主成分(PC1)解释了数据集中最大的方差,PC2 解释了第二大方差,依此类推。您可以通过根据累计解释方差限制要保留的主成分数量来降低维度。PCA 变换也依赖于比例,因此您应该首先归一化您的数据集。PCA 是寻找给定特征之间的线性相关性。这意味着,只有当数据集中有一些线性相关的变量时,这才会有所帮助。

主成分分析

3) t 分布随机邻居嵌入(t-SNE)

t-SNE 是一种非线性降维技术,通常用于可视化高维数据集。t-SNE 的一些主要应用是自然语言处理(NLP)、语音处理等。

t-SNE 通过最小化由原始高维空间中的输入特征的成对概率相似性构成的分布与其在缩减的低维空间中的等价物之间的差异来工作。t-SNE 然后利用 Kullback-Leiber (KL)散度来测量两种不同分布的不相似性。然后使用梯度下降最小化 KL 散度。

这里,低维空间使用 t 分布建模,而高维空间使用高斯分布建模。

4)自动编码器

自动编码器是一系列机器学习算法,可用作降维技术。自动编码器也使用非线性变换将数据从高维投影到低维。自动编码器是被训练来重建其原始输入的神经网络。基本上,自动编码器由两部分组成。

  1. 编码器:获取输入数据并进行压缩,以去除所有可能的噪音和无用信息。编码器级的输出通常被称为瓶颈或潜在空间。
  2. 解码器:将编码后的潜在空间作为输入,并尝试使用其压缩形式(编码后的潜在空间)再现原始自动编码器输入。

自动编码器的结构

在上图中,中间层代表数量较少的神经元中的大量输入特征,因此给出了密集且较小的输入表示。由于这是一种基于神经网络的特征提取解决方案,因此可能需要大量数据进行训练。

我希望你喜欢这篇文章,谢谢你的阅读!

利用 Python 实现高光谱图像的降维

原文:https://towardsdatascience.com/dimensionality-reduction-in-hyperspectral-images-using-python-611b40b6accc?source=collection_archive---------40-----------------------

初学者指南

高光谱图像的降维技术。

美国地质勘探局在 Unsplash 上拍摄的照片

本文详细介绍了高光谱图像降维技术的实现。

目录

  • 高光谱图像简介
  • 什么是降维?
  • 降维需求
  • HSI 上的降维
  • 参考文献

超光谱图像(HSI)介绍

高光谱图像(HSI)提供了广泛的光谱信息,可用于解决各种问题,如作物分析、地质制图、矿产勘探等。由于数据中存在大量的波段,降维已经成为机器学习的一个重要方面。它通常被认为是解决分类和聚类等机器学习问题的预处理步骤。一般来说,任何分类和聚类算法的准确性都会受到维度或特征数量的影响。

降维技术用于减少数据集的维数,从而为分类器以低计算成本生成综合模型铺平了道路。因此,降维在提高高光谱图像像素分类精度方面变得越来越重要。

要更好地了解 HSI,请阅读下面题为“超光谱图像分析—入门”的文章。

[## 超光谱图像分析—入门

使用 Python 进行高光谱图像分析的演练。

towardsdatascience.com](/hyperspectral-image-analysis-getting-started-74758c12f2e9)

什么是降维?

降维是减少数据集的维度或特征的过程。可以使用两种方法来减少数据集的维度。它们是:

  • 功能选择
  • 特征提取

特征选择是选择数据集特征维度的过程,该数据集为机器学习任务(如分类、聚类等)提供模式。这可以通过使用不同的方法来实现,如相关性分析、单变量分析等

特征提取特征提取是通过选择和/或组合现有特征来创建缩减的特征空间,同时仍然准确和完整地描述数据集而不丢失信息,从而发现新特征的过程。

基于准则函数和收敛过程,降维技术也分为凸和非凸两类。一些流行的降维技术包括 PCA、ICA、LDA、GDA、核 PCA、Isomap、局部线性嵌入(LLE)、Hessian LLE 等。

我们将使用主成分分析(PCA)来降低 HSI 的维度。

主成分分析

PCA 是一种线性无监督降维技术,可以描述为数据协方差矩阵的特征分解。它通过创建方差最大化的不相关要素来减少信息损失,从而提高可解释性。

为了理解主成分分析(PCA)背后的数学原理,请使用下面的教程:

[## 主成分分析算法—降维| Coursera

斯坦福大学为“机器学习”课程制作的视频。在本模块中,我们将介绍主要…

www.coursera.org](https://www.coursera.org/lecture/machine-learning/principal-component-analysis-algorithm-ZYIPa)

需要降维

维数灾难指的是在高维空间中分类、分析数据时出现的现象,这种现象在低维空间中不会出现。随着维数或特征在最佳点之后增加,分类性能逐渐降低。

降维技术用于减少数据集的维数,从而为分类器以降低的计算成本生成综合模型铺平了道路。

HSI 上的降维

帕维亚大学数据集用于说明目的。在意大利北部帕维亚上空的一次飞行活动中,安明传感器拍摄到的 HSI 场景。光谱波段的数量为 103 个,HSI 的大小为610340 像素,地物包含 9 个类*。图像中的一些像素不包含信息,并且在分析之前必须被丢弃。

读取 HSI 数据

使用下面的代码从 SciPy 中的' load_mat '函数读取帕维亚大学 HSI 的数据。

可视化数据

让我们来看看帕维亚大学高光谱影像的地面真相。

帕维亚大学 HSI 的基本事实—作者图片

让我们看看帕维亚大学 HSI 的几个乐队,这些乐队是随机挑选的。

Pavia 大学 HSI 样本带的可视化——图片由作者提供

主成分分析

让我们降低帕维亚大学 HSI 的维度。首先,我们要降多少维?

使用解释的方差比图,我们可以选择要减少的最佳维数。图表的 x 轴代表主成分的数量,y 轴代表累计解释方差。我们可以选择具有> 95%累积解释方差的成分数量。在这种情况下,我们选择了 8 个主成分,也就是说,我们正在将帕维亚大学数据的维度减少到 8 个维度。

累积解释方差和组件数量之间的图表-按作者分类的图片

现在,我们知道应该减少多少个恒指数据维度。对 HSI 数据应用主成分分析(PCA)。下面的代码可以满足这个目的。

在减少数据的维度之后,为了将来的目的,添加类别标签。以下代码用于显示降维后的波段。

我们来看看帕维亚大学 HSI 用 PCA 降维后的八个波段。

应用主成分分析后条带的可视化—作者提供的图像

我编写的在 HSI 上执行 PCA 的整个代码被保存为一个 Colab 笔记本。请随意使用。

[## 基于主成分分析的 HSI 数据降维

基于主成分分析的高光谱图像降维。

colab.research.google.com](https://colab.research.google.com/drive/1Lstx55MxHT8bpXlgnaXYloVCmagE_xC7?usp=sharing)

参考

[## 奇异值分解和主成分分析

编辑描述

link.springer.com](https://link.springer.com/chapter/10.1007%2F0-306-47815-3_5) [## sk learn . decomposition . PCA-sci kit-learn 0 . 23 . 1 文档

主成分分析。使用数据的奇异值分解进行线性降维…

scikit-learn.org](https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html)

降维:PCA 与自动编码器

原文:https://towardsdatascience.com/dimensionality-reduction-pca-versus-autoencoders-338fcaf3297d?source=collection_archive---------9-----------------------

主成分分析和自动编码器降维的比较

图片由比利·胡恩Unsplash 上拍摄

降维是一种减少特征空间的技术,以获得稳定且统计上合理的机器学习模型,避免维数灾难。降维主要有两种方法:特征选择特征变换

特征选择方法试图对重要特征进行子集划分,并移除共线或不太重要的特征。你可以在这里读到更多。

特征转换也称为特征提取,试图将高维数据投影到较低的维度。一些特征变换技术有 PCA矩阵分解自动编码器t-SneUMAP 等。

通过这篇博文,我打算深入研究 PCA 和自动编码器。我们将看到这两种技术的优点和缺点,并通过一个有趣的例子来清楚地理解它。该解决方案的完整源代码可以在这里找到。

你也可以在 Youtube 上找到我关于同一主题的视频。

我在 Youtube 上的同主题视频

主成分分析

主成分分析是一种无监督技术,将原始数据向高方差方向投影。这些高方差方向彼此正交,导致投影数据中的相关性非常低或几乎接近 0。这些功能转换是线性的,其方法是:

第一步: 计算 n 维组成的相关矩阵数据。相关矩阵的形状为 n*n。

第二步: 计算这个矩阵的特征向量和特征值

第三步: 取特征值最高的前 k 个特征向量。

第四步: 将原始数据集投影到这 k 个特征向量中,得到 k 维,其中 k ≤ n。

自动编码器

Autoencoder 是一个无监督的人工神经网络,它将数据压缩到较低的维度,然后将输入重构回来。Autoencoder 通过更多地关注重要特征来消除噪声和冗余,从而在较低的维度中找到数据的表示。它基于编码器-解码器架构,其中编码器将高维数据编码为低维数据,解码器获取低维数据并尝试重建原始高维数据。

基本自动编码器的模式:维基百科

深度自动编码器的模式:维基百科

在上图中,X 是输入数据,z 是输入 X 的低维表示,X '是重构的输入数据。根据对激活功能的选择,较高维度到较低维度的映射可以是线性的或非线性的。

比较:PCA 与自动编码器

  1. PCA 是数据的线性变换,而 AE 可以是线性的或非线性的,这取决于激活函数的选择。
  2. PCA 非常快,因为存在可以快速计算它的算法,而 AE 通过梯度下降训练,并且相对较慢。
  3. PCA 将数据投影到彼此正交的维度中,导致投影数据中的相关性非常低或接近零。PCA 得到正交子空间,因为特征向量(数据投影到其上)来自应用于协方差矩阵(对称半正定)的特征分解,并且在这种情况下,特征分解产生正交特征向量。AE 转换数据不能保证这一点,因为它的训练方式仅仅是为了最小化重建损失。
  4. PCA 是输入空间到最大变化方向的简单线性变换,而 AE 是更复杂的技术,可以模拟相对复杂的关系和非线性。
  5. 一个经验法则可能是数据的大小。对于小数据集使用 PCA,对于相对较大的数据集使用 AE。
  6. PCA 超参数是“k ”,即投影数据的正交维数,而对于 AE,它是神经网络的架构。
  7. 具有单层和线性激活的 AE 具有与 PCA 相似的性能。称为深度自动编码器的具有多层和非激活功能的 AE 易于过拟合,并且可以通过规范化和仔细设计来控制。请参考下面两篇博客来了解更多。

[## 在 Keras 中构建自动编码器

在本教程中,我们将回答一些关于自动编码器的常见问题,我们将涵盖代码的例子…

blog.keras.io](https://blog.keras.io/building-autoencoders-in-keras.html) [## 不同类型的自动编码器

阅读时间:30 分钟自动编码器是一种人工神经网络,用于学习有效的数据编码

iq.opengenus.org](https://iq.opengenus.org/types-of-autoencoder/)

图像数据示例:了解 PCA 和自动编码器

让我们以下图为例,用这两种方法进行降维。

图像来源

这幅图像的尺寸是 360 * 460。从另一个角度来看,它是一个包含 360 个数据点和 460 个特征/维度的数据集。

我们将尝试将维数从 460 减少到 10%,即 46 维,首先使用 PCA,然后使用 AE。再来看看重构和其他属性的区别。

使用主成分分析进行降维

pct_reduction = 0.10
reduced_pixel  = int( pct_reduction* original_dimensions[1])#Applying PCA
pca = PCA(n_components=reduced_pixel)
pca.fit(image_matrix)#Transforming the input matrix
X_transformed = pca.transform(image_matrix)
print("Original Input dimesnions {}".format(original_dimensions))
print("New Reduced dimensions {}".format(X_transformed.shape))

输出

Original Input dimesnions (360, 460)
New Reduced dimensions (360, 46)

让我们检查来自 PCA 的新变换特征的相关性。

df_pca = pd.DataFrame(data = X_transformed,columns=list(range(X_transformed.shape[1])))figure = plt.figure(figsize=(10,6))
corrMatrix = df_pca.corr()
sns.heatmap(corrMatrix, annot=False)
plt.show()

变换特征的相关矩阵:PCA

相关矩阵显示新的变换特征彼此不相关,相关度为 0。原因是在 PCA 中将数据投影到正交维度中。

接下来,我们将尝试仅通过来自我们可用的缩减特征空间的信息来重建原始数据。

reconstructed_matrix = pca.inverse_transform(X_transformed)
reconstructed_image_pca = Image.fromarray(np.uint8(reconstructed_matrix))
plt.figure(figsize=(8,12))
plt.imshow(reconstructed_image_pca,cmap = plt.cm.gray)

重建图像:主成分分析

计算重建图像的 RMSE

def my_rmse(np_arr1,np_arr2):
    dim = np_arr1.shape
    tot_loss = 0
    for i in range(dim[0]):
        for j in range(dim[1]):
            tot_loss += math.pow((np_arr1[i,j] - np_arr2[i,j]),2)
    return round(math.sqrt(tot_loss/(dim[0]* dim[1]*1.0)),2)error_pca = my_rmse(image_matrix,reconstructed_matrix)

RMSE 是 11.84(越低越好)。

如果原始图像和重建图像之间没有差异,RMSE 将为 0。如果使用大约 120 个来自 PCA 的维度,则 RMSE 接近于 0。

使用具有线性激活的单层自动编码器进行降维

# Standarise the Data
X_org = image_matrix.copy()
sc = StandardScaler()
X = sc.fit_transform(X_org)# this is the size of our encoded representations
encoding_dim = reduced_pixel # this is our input placeholder
input_img = Input(shape=(img.width,))# "encoded" is the encoded representation of the input
encoded = Dense(encoding_dim, activation='linear')(input_img)# "decoded" is the lossy reconstruction of the input
decoded = Dense(img.width, activation=None)(encoded)# this model maps an input to its reconstruction
autoencoder = Model(input_img, decoded)#Encoder
encoder = Model(input_img, encoded)# create a placeholder for an encoded (32-dimensional) input
encoded_input = Input(shape=(encoding_dim,))# retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]# create the decoder model
decoder = Model(encoded_input, decoder_layer(encoded_input))autoencoder.compile(optimizer='adadelta', loss='mean_squared_error')autoencoder.fit(X, X,
                epochs=500,
                batch_size=16,
                shuffle=True)encoded_imgs = encoder.predict(X)
decoded_imgs = decoder.predict(encoded_imgs)

模型架构/摘要

让我们检查从 AE 出来的新变换特征的相关性。

df_ae = pd.DataFrame(data = encoded_imgs,columns=list(range(encoded_imgs.shape[1])))
figure = plt.figure(figsize=(10,6))
corrMatrix = df_ae.corr()
sns.heatmap(corrMatrix, annot=False)
plt.show()

变换特征的相关矩阵:AE

相关矩阵显示新的变换特征在某种程度上是相关的。皮尔逊相关因子偏离 0 很多。AE 训练的原因仅仅是为了最小化重建损失。

接下来,我们将尝试仅通过我们可用的缩减的特征空间来重建回原始数据。

X_decoded_ae = sc.inverse_transform(decoded_imgs)reconstructed_image_ae = Image.fromarray(np.uint8(X_decoded_ae))
plt.figure(figsize=(8,12))
plt.imshow(reconstructed_image_ae,cmap = plt.cm.gray)

重建图像:声发射

计算重建图像的 RMSE

error_ae = my_rmse(image_matrix,X_decoded_ae)

RMSE 是 12.15。接近 PCA 的 RMSE 11.84。具有单层和线性激活的自动编码器的性能类似于 PCA。

使用具有非线性激活的三层自动编码器进行降维

input_img = Input(shape=(img.width,))
encoded1 = Dense(128, activation='relu')(input_img)
encoded2 = Dense(reduced_pixel, activation='relu')(encoded1)
decoded1 = Dense(128, activation='relu')(encoded2)
decoded2 = Dense(img.width, activation=None)(decoded1)autoencoder = Model(input_img, decoded2)
autoencoder.compile(optimizer='adadelta', loss='mean_squared_error')autoencoder.fit(X,X,
                epochs=500,
                batch_size=16,
                shuffle=True)
# Encoder
encoder = Model(input_img, encoded2)
# Decoder
decoder = Model(input_img, decoded2)encoded_imgs = encoder.predict(X)
decoded_imgs = decoder.predict(X)

模型架构/摘要

接下来,我们将尝试仅通过我们可用的缩减的特征空间来重建回原始数据。

X_decoded_deep_ae = sc.inverse_transform(decoded_imgs)reconstructed_image_deep_ae = Image.fromarray(np.uint8(X_decoded_deep_ae))
plt.figure(figsize=(8,12))
plt.imshow(reconstructed_image_deep_ae,cmap = plt.cm.gray)

计算重建图像的 RMSE

error_dae = my_rmse(image_matrix,X_decoded_deep_ae)

RMSE 是 8.57。相对于 PCA 的增益是 28 %,同时减少了相同数量的维数。

具有非线性激活的额外层的 Autoencoder 能够更好地捕捉图像中的非线性。与 PCA 相比,它能够更好地捕捉复杂的模式以及像素值的突然变化。尽管它伴随着相对较高的培训时间和资源的成本。

结论

通过这篇博文,我们深入研究了 PCA 和自动编码器。我们也看到了这两种技术的优缺点。这些概念在图像数据集上进行了尝试,其中具有额外非线性激活层的自动编码器优于 PCA,尽管代价是更高的训练时间和资源。该解决方案的完整源代码可以在这里找到。

如果你有任何疑问,请联系我。我很想知道你是否面临过高维度的问题,以及你尝试了哪些方法来克服它。

我的 Youtube 频道更多内容:

[## 阿布舍克·蒙戈利

嗨,伙计们,欢迎来到频道。该频道旨在涵盖各种主题,从机器学习,数据科学…

www.youtube.com](https://www.youtube.com/channel/UCg0PxC9ThQrbD9nM_FU1vWA)

关于作者-:

Abhishek Mungoli 是一位经验丰富的数据科学家,拥有 ML 领域的经验和计算机科学背景,跨越多个领域并具有解决问题的思维方式。擅长各种机器学习和零售业特有的优化问题。热衷于大规模实现机器学习模型,并通过博客、讲座、聚会和论文等方式分享知识。

我的动机总是把最困难的事情简化成最简单的版本。我喜欢解决问题、数据科学、产品开发和扩展解决方案。我喜欢在闲暇时间探索新的地方和健身。关注我的 Linkedininsta gram并查看我的以前的帖子。我欢迎反馈和建设性的批评。我的一些博客-********

在 MNIST 数据集上使用 t 分布随机邻居嵌入(t-SNE)进行降维

原文:https://towardsdatascience.com/dimensionality-reduction-using-t-distributed-stochastic-neighbor-embedding-t-sne-on-the-mnist-9d36a3dd4521?source=collection_archive---------13-----------------------

应用 PCA (n_components = 50)和 t-SNE 后 MNIST 数据的 2D 散点图

我们很容易将二维或三维数据可视化,但一旦超出三维,就很难看到高维数据是什么样子了。

今天,我们经常处于这样一种情况,我们需要在具有数千甚至数百万维的数据集上分析和发现模式,这使得可视化有点挑战性。然而,一个绝对可以帮助我们更好地理解数据的工具是降维

在这篇文章中,我将讨论 t-SNE,一种流行的非线性降维技术,以及如何使用 sklearn 在 Python 中实现它。我在这里选择的数据集是流行的 MNIST 数据集。

古玩目录

  1. 什么是 t-SNE,它是如何工作的?
  2. t-SNE 与 PCA 有何不同?
  3. 我们如何改进 t-SNE?
  4. 有哪些局限性?
  5. 接下来我们能做什么?

概观

t-分布式随机邻居嵌入,或 t-SNE,是一种机器学习算法,通常用于在低维空间嵌入高维数据[1]。

简单来说,SNE 霸王龙的方法可以分为两步。第一步是通过构建概率分布 P 来表示高维数据,其中相似点被选取的概率高,而不同点被选取的概率低。第二步是用另一个概率分布 Q 创建一个低维空间,它尽可能地保持 P 的性质。

在步骤 1 中,我们使用条件概率 p 来计算两个数据点之间的相似性。例如,给定 I 的 j 的条件概率表示 x_j 将被 x_i 挑选为其邻居,假设在以x _ I【1】为中心的高斯分布下,邻居与其概率密度成比例地被挑选。在步骤 2 中,我们让 y_iy_j 分别是 x_ix_j,的低维对应物。然后,我们认为 q 是被 y_i 挑选的 y_j 的类似条件概率,并且我们在低维图中采用了学生 t 分布。通过最小化来自 q 的概率分布 P 的kull back–lei bler 散度 来确定低维数据点的位置

关于 t-SNE 的更多技术细节,请查看本文。

我选择了 Kaggle ( link )的 MNIST 数据集作为这里的例子,因为它是一个简单的计算机视觉数据集,包含手写数字(0–9)的 28x28 像素图像。我们可以把每个实例想象成一个嵌入 784 维空间的数据点。

要查看完整的 Python 代码,请查看我的 Kaggle 内核

事不宜迟,让我们进入细节!

探测

请注意,在最初的 Kaggle 竞赛中,目标是使用具有真实标签的训练图像来建立 ML 模型,该模型可以准确预测测试集上的标签。出于我们的目的,我们将只使用训练集。

像往常一样,我们首先检查它的形状:

train.shape
--------------------------------------------------------------------
(42000, 785)

有 42K 个训练实例。785 列是 784 像素值,以及“标签”列。

我们还可以检查标签分布:

label = train["label"]
label.value_counts()
--------------------------------------------------------------------
1    4684
7    4401
3    4351
9    4188
2    4177
6    4137
0    4132
4    4072
8    4063
5    3795
Name: label, dtype: int64

主成分分析

在实现 t-SNE 之前,我们先来试试 PCA,这是一种流行的线性降维方法。

标准化数据后,我们可以使用 PCA 转换数据(将“n_components”指定为 2):

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCAtrain = StandardScaler().fit_transform(train)
pca = PCA(n_components=2)
pca_res = pca.fit_transform(train)

让我们制作一个散点图来直观显示结果:

sns.scatterplot(x = pca_res[:,0], y = pca_res[:,1], hue = label, palette = sns.hls_palette(10), legend = 'full');

应用主成分分析后 MNIST 数据的 2D 散点图

如散点图所示,含有两种成分的五氯苯甲醚不足以提供关于不同标签的有意义的见解和模式。我们知道 PCA 的一个缺点是线性投影不能捕捉非线性相关性。现在让我们试试 SNE 霸王龙。

T-SNE 与sk learn

我们将使用 sklearn.manifold ( 文档)实现 t-SNE:

from sklearn.manifold import TSNEtsne = TSNE(n_components = 2, random_state=0)
tsne_res = tsne.fit_transform(train)

sns.scatterplot(x = tsne_res[:,0], y = tsne_res[:,1], hue = label, palette = sns.hls_palette(10), legend = 'full');

应用 t-SNE 后 MNIST 数据的 2D 散点图

现在我们可以看到,与主成分分析的结果相比,不同的聚类更加可分。以下是对该图的一些观察:

  1. 与“2”和“4”等其他聚类相比,“5”数据点似乎更加分散。
  2. 有几个“5”和“8”数据点类似于“3”
  3. 有两个“7”和“9”的集群,它们彼此相邻。

结合两者的方法

通常建议在应用 t-SNE 之前,使用 PCA 或 TruncatedSVD 将维数减少到合理的数量(例如 50)[2]。

这样做可以降低噪音水平并加快计算速度。

让我们先尝试主成分分析(50 个成分),然后应用 t-SNE。这是散点图:

应用 PCA(50 个成分)和 t-SNE 后 MNIST 数据的 2D 散点图

与之前的散点图相比,我们现在可以更好地分离出 10 个星团。以下是一些观察结果:

  1. 大多数“5”数据点不像以前那样分散,尽管有几个看起来仍然像“3”。
  2. 现在有一簇“7”和一簇“9”。

此外,这种方法的运行时间减少了 60%以上。

更多互动 3D 散点图,请查看这篇文章

限制

以下是 SNE 霸王龙的一些局限性:

  1. 与主成分分析不同,t-SNE 的成本函数是非凸的,这意味着我们有可能陷入局部最小值。
  2. 类似于其他维度缩减技术,压缩维度的含义以及转换后的特征变得更难解释。

后续步骤

以下是我们接下来可以尝试的几件事:

  1. 超参数调整—尝试调整“困惑”并查看其对可视化输出的影响。
  2. 尝试其他一些非线性技术,如均匀流形近似和投影 (UMAP),这是 t-SNE 的推广,它基于黎曼几何。
  3. 根据转换后的数据训练 ML 模型,并将其性能与未经降维的模型进行比较。

摘要

让我们快速回顾一下。

我们在 MNIST 数据集上使用 sklearn 实现了 t-SNE。我们将可视化输出与使用主成分分析的输出进行了比较,最后,我们尝试了一种混合方法,该方法首先应用主成分分析,然后应用 t-SNE。

我希望你喜欢这篇博文,并请分享你的想法:)

看看我关于独立性卡方测试的另一篇文章:

[## Python 中独立性的卡方检验以及 IBM HR 分析数据集的示例

员工流失是否取决于因素“X”?

towardsdatascience.com](/chi-square-test-for-independence-in-python-with-examples-from-the-ibm-hr-analytics-dataset-97b9ec9bb80a)

参考

[1]https://en . Wikipedia . org/wiki/T-distributed _ random _ neighbor _ embedding
【2】https://sci kit-learn . org/stable/modules/generated/sk learn . manifold . tsne . html

PCA 降维:从基本思想到完全推导。

原文:https://towardsdatascience.com/dimensionality-reduction-with-pca-from-basic-ideas-to-full-derivation-37921e13cae7?source=collection_archive---------11-----------------------

在本文中,我们将在一个简单的例子上建立一些关于维数约简和 PCA 的直觉,然后整理其背后的数学,并推导出一般情况下的算法。

主成分分析(PCA)是一种强大的算法,其思想是由卡尔·皮尔逊在 1901 年【1】为一个数据拟合问题提出的。与最小二乘回归不同,它不依赖于哪个变量是独立的,反映了变量之间的普遍关系。主成分分析有许多应用,如噪声过滤、特征提取或高维数据可视化,但最基本的应用是数据降维。在下面的帖子中,我将从这个角度描述 PCA。

内容

在本文中,我们将:

  • 深入了解降维。
  • 2d3d 中几乎不用数学就能建立一些关于 PCA 的直觉。
  • 推导一般情况下的主成分分解。
  • 总结一下 PCA 降维算法。
  • 看看它如何处理图像压缩。

降维的基本思想降维

一般意义上,降维是对原始M-维度数据N-维度子空间的一种表示,其中 N < M 。为了建立一些直觉,让我们从一个简单的例子开始。假设我们的数据是从汽车中的 GPS 传感器获取的坐标。假设它是真实世界的数据,所以它包含一些噪声。

图一。汽车坐标的二维数据,生成为 y=2*x +噪声。

因此,我们手头有二维数据,在这种情况下降低维度意味着找到数据的一维表示。这可以通过将每个点投影到一维子空间上来实现。不一定是正交投影。例如,让我们取由基向量【b】跨越的子空间并使用某种投影矩阵 P 来投影我们的数据。

图 2 我们以某种方式将数据投射到低维空间。低维坐标称为代码。然后,我们可以通过子空间基向量和编码将数据重构回原始维度。

现在,在子空间中,我们可以为每个数据点仅存储一个值——子空间 U 中对应的坐标λ。这些低维坐标称为 代码 。使用这些坐标λ和一个基向量 b 我们可以将数据重建回原始维度,结果称为 重建 。****

你可以在这里提到,重建的数据不同于原始数据,我们可以用原始点和重建点之间的欧几里德距离来衡量这种差异。我们可以看到降维是有一定代价的,我们要付出的代价是信息损失。但它仍然有一些意义:即使使用这样的随机投影,重建的数据也反映了原始数据的一些属性:我们仍然可以看到 XY 正相关,甚至重建数据的斜率也非常接近原始数据。

我们使用了一些随机的无动机的投射,所以问题是“我们能做得更好吗”?我们当然可以,这就引出了 PCA 算法。但首先,在一般情况下推导它之前,让我们对它建立一个更好的理解。

主成分直觉

在我们开始之前,让我们进行一个小的数据准备,让我们把我们的数据转移到中心(从每个维度减去平均值)。正如我们所见,这种转变似乎很有帮助。它不改变变量之间的关系,很容易回到原始数据,我们只需要添加相应变量的平均值。

图 3。数据居中:X_c= X -E[X],Y_c = Y -E[Y]。对于逆变换,我们保持 E[X]和 E[Y]。

所以现在我们要进行降维,保存尽可能多的信息。我们可以用重建误差来衡量信息损失,重建误差定义为原始数据和重建点之间的平均平方距离:

这里我们需要首先提到的是,我们应该使用正交投影,以便最小化原始点和投影点之间的距离。考虑图 4 的左图:正交投影(灰色)的距离总是小于非正交投影(橙色)的距离。这可以用三角形不等式来证明,稍后我们将从另一个角度再次看到它。

图 4。正交投影可以最小化距离。现在我们需要找到最佳拟合向量 b。

所以我们已经介绍了投影类型,现在我们需要找到这样一个向量【b】,它最小化 L 。从图 4 中可以明显看出 b2b1 好,但是如何在所有的中找到最好的一个呢?

现在让我们稍微作弊一下,跳过大部分数学运算,通过旋转基向量并为每个向量【b】计算相应的重建误差,在数值上优化 L 。我还在右图中添加了投影点的方差,因此您可以注意到我们稍后将讨论的一个关键特性。

图 5 .我们旋转基向量 b 并将数据投影到相应的子空间上,然后计算重建误差 L 和投影方差 Var(λ)。

可以看出,通过这种方式,我们可以找到一个使重建误差最小化,同时使投影方差最大化的向量。这是一个重要的特性,所以让我们稍微关注一下:

  • 跟随左边的动画:当我们向最优解移动时,投影(灰色)点试图尽可能远离原点。
  • 这是因为正交距离的最小化与投影和原点之间距离的最大化是相同的。从任何蓝点到原点的距离保持固定,我们最小化正交距离(虚线),因为根据勾股定理,这与最大化从原点到投影点的距离是相同的。
  • 投影的期望值为零(因为我们在开始时将数据居中),从原点到投影的距离最大化也会导致方差最大化。这是一个至关重要的特征,我们将在后面的一般推导中面对它。

还有一件事要讨论。让我们考虑子空间()U2)正交,因为我们刚刚在下图上找到():**

图 6。我们找到的基本向量是我们数据的主要组成部分。主成分描述了大多数数据差异。

这些子空间上的投影是我们数据的主要组成部分。考虑图 6 中的投影方差:相应的子空间具有一个重要的性质,即第一投影包含大部分方差,而第二投影包含最小方差。它们一起描述了所有的数据差异。将总数据方差视为它们的总和,我们可以计算出子空间描述了 97.1%的数据方差,子空间 U2 描述了 2.9%。因此,如果我们降低维数,只保留在 U1 上的投影,信息损失将仅为 2.9%。**

让我们从一个稍微不同的角度来看看我们到目前为止所做的事情:我们用一种新的基来执行数据的正交分解,其方式是基向量之一跨越一个具有最大方差的子空间。根据正交分解定理我们总是可以通过某个子空间及其正交补来分解任何向量,并且在 PCA 中,我们正在搜索包含大部分数据方差的子空间。

比如在 3d 中我们可以找到 2d (一个平面)或者 1d (一条线)数据方差最大的子空间。考虑跨越具有最大方差的平面的两个基向量和与其正交的第三个基向量:

图 7。红线代表数据主成分。大多数数据方差可以用基于前两个主成分的平面来描述。

对于更高维度,很难绘制或想象所有这些,但想法是相同的:找到一个子空间(线、平面、超平面),其中包含大部分数据方差及其正交补。跨越这些子空间的向量是主分量。

一旦我们得到这个,我们就可以进入一个普通的案例。

一般情况 PCA 推导

假设我们有数据×数据** 由 N 个数据点组成,每个点都是一个 D 维向量:**

就像前面提到的例子一样,我们希望在另一个标准正交基中分解每个数据点,以使第一组分量包含大部分数据方差。我们可以这样写:

这里 b 是我们新的基向量,β是对应的坐标。我们希望找到这样的 b 和β,使得第一个 M 基向量所跨越的子空间上的投影包含大部分数据方差。让我们回顾一下例子:

  • 2d M=1 中,我们找到了一个方差最大的子空间(一条直线),一个正交子空间包含最小方差。
  • 在图 7 上的3dM = 2中,我们找到了一个方差最大的子空间(一个平面),它的正交补又包含最小的数据方差。

为了降维,我们去掉了具有最小方差的第二项,并用第一个 M 基向量来表示我们的数据:

:【x 仍然是一个 D 维度向量,但现在它生活在一个更小的子空间中,可以用 M 坐标描述在新的*基础上

我们的目标保持不变:最小化我们定义为原始点和重建点之间的均方距离的重建误差:

但是现在我们要推导出解析解。为此,让我们重写损失函数。首先,让我们用数据在相应基向量上的点积来表示投影坐标:

注: 一个人可以证明正交投影是最优解,考虑 L 关于β的偏导数并设为零,得到同样的结果。

使用β的表示,我们可以将 x 重写如下:*

我们可以基于同样的推理重写 x :

所以数据和重建的区别在于:

损失可以写成:

已知 b 是正交基的组成部分,得到如下:

然后我们可以重新排列总数,得到这个:

这里有一件好事:假设我们的数据居中,括号中的一项是协方差矩阵 S=Cov(X) ,所以我们可以这样写:

我们将最小化在 {M+1,D} 基础上预测的方差(这与在 {1,M} 基础上最大化的方差相同,在图 6 中概括结果)。

还有最后一步要走:我们要最小化 L 关于bb假设bb是一个标准正交基,它应该满足下面的等式:**

因此,我们手头有一个约束优化问题。这可以用拉格朗日法解决(如果你不熟悉,这里有一个伟大的来源):

为了找到 b 我们需要将拉格朗日的梯度设置为零,因此对于 b 的导数,我们得到:

最后,我们得到:

这就是协方差矩阵的特征向量问题!这意味着协方差矩阵的特征向量是主分量(注意,由于协方差矩阵的对称性,它们是正交的)**

如果我们将它们代入损失函数的最终方程,我们也可以清楚地看到相应特征值的含义:

这意味着为了使 L 最小,我们应该取一个由具有最小特征值的 D-M 特征向量构成的子空间。换句话说,对应于第一个 M 个 最大特征值的特征向量跨越具有最大数据方差的子空间。因此,如果我们按特征值降序排列特征向量,第一个特征向量将代表最大数据方差的方向,第二个特征向量将代表来自其余正交方向的最大数据方差的方向,依此类推。

设置 M=0, 我们可以计算最大损耗。它等于所有特征值的总和(这是非常明显的,因为我们去掉了所有的主成分,即我们去掉了所有的信息)。这意味着我们可以计算每个主成分中包含的方差(信息)的比率,如下所示:**

这就很容易决定我们应该为降维留下多少组件。为了方便起见,我们可以绘制解释方差的比率(scree plot)及其累积比率。考虑我们已经在图 7 中看到的数据示例:

图 8 . 3D 数据示例的碎石图,第一主成分解释了约 65%的数据差异,第二主成分解释了约 25%,第三主成分解释了约 10%。

因此,对于这个例子,如果我们减少 1 维,去掉第三个主成分,我们将丢失大约 10%的信息(数据方差)。

现在我们准备定义一个用主成分分析进行降维的最终算法。

主成分分析的降维算法

最后是 PCA 算法,用于由数据点组成的数据 X ,其中每个点是一个 D 维向量:**

  1. 首先,我们需要集中我们的数据:

2.然后我们计算中心数据的协方差矩阵 S 并找到它的特征向量 b 和特征值λ。

3.按对应的特征值降序排列特征向量。

4.然后我们从第一个(M<【D】特征向量形成矩阵 B 并进行降维:**

5.对于逆变换,我们需要回到原始空间(寻找投影)并将数据从中心移回,加上平均值:

龟背竹压缩示例

我们已经做了很多工作,所以让我们玩得开心点。还记得特色图片中的龟背竹吗?让我们用 PCA 压缩它。它是[225,255,3] RGB 图像,因此我们可以分别对每个通道应用 PCA,并探索不同数量的主分量的解释方差。

图 9 解释了不同颜色通道的差异。

请注意,完全分解由 255 个分量组成,同时第一个分量解释了 40%以上的方差,前 40 个分量解释了大约 99%的方差。因此,我们可以使用大约 6 倍的较小表示来压缩图像,并且能够以接近零的损失来重建图像。让我们看看它是什么样子的:

图 10。使用 PCA 压缩的图像重建示例。

编后记

到目前为止,我们已经想出了一个基本的 PCA 算法,并用其中一种可能的方法推导出了它。除了基本 PCA,还有其他更具体的实现,如稀疏 PCA、鲁棒 PCA、内核 PCA 等。其中一个扩展是从称为概率 PCA 的潜在变量模型的角度来看的 PCA,我很想写一篇关于它的帖子。敬请关注更新。感谢阅读!

Power BI 中的直接查询—什么、何时以及为什么?

原文:https://towardsdatascience.com/direct-query-in-power-bi-what-when-why-8180825812d2?source=collection_archive---------5-----------------------

关于 DirectQuery 您想知道但不敢问的一切!导入模式的 Power BI 替代方案的最终指南

皮查拜在 Pexels.com 拍摄的照片

使用 Power BI 时,您需要首先做出以下决定:

一旦您计划获取一些数据,Power BI 会要求您选择数据连接模式。如果您已经阅读了这篇文章,或者更好,从这篇文章开始,您可能会熟悉导入选项,以及表格模型如何在 Power BI 的背景下支持您的查询并生成闪电般快速的报告。

在本文中,我想更深入地研究 DirectQuery 选项,因为我感觉这个选项仍然没有得到充分利用(不管是好是坏,我们将在本文中进行探讨)。

什么是 DirectQuery?

顾名思义,DirectQuery 是一种检索数据的方法,它在查询时间 直接从数据源 中提取数据!这句话的最后一部分掌握着关键—虽然导入模式将数据的快照存储在内存中,但 DirectQuery (DQ)不存储任何数据。对于每一个请求,它都直接进入数据源(在 99%的情况下是 SQL 数据库),并从那里提取数据。

因此,在查询执行之前、期间和之后,数据都驻留在其原始源中!

在与报表交互时,您的用户会生成一个查询(或者大多数情况下是一组查询),需要执行该查询才能满足请求。您可能还记得本文中的,表格模型由公式引擎(FE)和存储引擎(SE)组成。Formula Engine 接受请求,创建查询计划,然后根据您在 Import 和 DirectQuery 模式之间的选择,生成针对相应数据源的查询。

Pexels.com 上 Lum3n 的照片

您可能会注意到,当您选择 DQ 选项时,公式引擎会将 DAX“翻译”为 SQL,并将查询直接发送到数据源。

一旦选择了 DirectQuery 选项,Power BI 将不会从底层表中导入数据。它将只保存它们的元数据。

好消息是:您可以在 Power BI 中使用复合模型。简而言之,这意味着您可以在数据模型中结合 DQ 和导入模式,为每个表设置首选选项!

正如您可能看到的,一旦我使用 DQ 选项创建了 Power BI 数据模型,当我在 model 视图中打开我的数据时,在 Advanced 下,我可以选择对所选表应用哪种存储模式。重要提示:您可以从直接查询模式切换到导入模式,但不能从导入模式切换到直接查询模式!

双重模式就像一个混合体——导入模式和 DirectQuery 的结合。表中的数据被加载到内存中,但是在查询时,也可以直接从源中检索。

什么时候使用 DirectQuery?

老实说,这是一个“百万美元”的问题:)…而且,和大多数情况一样,唯一正确的答案是: 【看情况】 。但是,让我们看看这取决于什么!

乔治·贝克尔在 Pexels 上的照片

  • 您需要“实时”或接近实时的数据吗?如果您对这些问题的回答是:是的,您应该考虑使用 DirectQuery 模式。为什么?因为,导入模式会保存数据的快照,并且需要定期刷新以获取最新数据。例如,如果您需要最大延迟为 1 分钟的数据,使用导入模式实际上是不可能的
  • 您的数据模型规模大吗?我说的大,就是大!大到你无法在最大限度内容纳它。pbix 文件大小(专业版许可 1 GB,高级版/嵌入式版 10 GB)。假设您的用户需要在高粒度级别上分析十亿行表中的数据。您不能简单地将十亿行的表数据导入到表格模型中,相反,数据会保留在源中,并且在细化的结果返回到报表之前,您的聚合/计算会在源中执行

为什么(不)使用 DirectQuery?

如果您的工作负载需要上述场景之一(“实时”分析和/或过大的数据模型),DirectQuery 将是一个显而易见的选择。

但是,让我们研究一下在更频繁的情况下使用 DirectQuery 的一些一般优缺点:

使用 DirectQuery 时最重要的考虑是,整体用户体验几乎完全取决于底层数据源的性能。这意味着,如果您的源数据库没有针对分析工作负载进行优化(缺少索引;不适当的索引;数据建模不充分,以至于查询需要针对多个表),您的报表性能会很差!

此外,与报告并行交互的用户数量也会产生影响。想象一下这样一个场景,10 个人浏览带有 20 个视觉效果的报告页面——这将同时生成对底层数据源的 200 个查询!请记住,每个可视化将生成(至少)一个对数据源的查询!

此外,如果您有机会通过应用不同的技术来改善这两个方面,您还应该记住,有些事情是您无法控制的,例如:

  • 源服务器的性能 —例如,如果在同一台服务器上运行数十种不同的工作负载,您将无能为力
  • 网络延迟

优化数据源的技术

正如我上面提到的,有不同的技术可以提高数据源的性能(假设您可以访问底层数据源,并且可以应用结构性更改)。

皮查拜在 Pexels.com 拍摄的照片

  • 添加合适的索引——支持你最详尽的查询。您应该考虑为大型分析工作负载创建列存储索引,但是设计良好的 B 树索引也应该提高性能
  • 数据完整性到位 —确保维度表包含正确的键,并且这些键与事实表相关(每个事实表键值在维度表中都有相应的值)
  • 在源数据库 中创建持久对象——这意味着,尝试物化所有的聚合、转换和计算,无论是在特殊的表中还是在索引视图中。这样,Power BI 可以从一个地方检索所有数据,而不是每次执行查询时都执行复杂的操作(比如多个表之间的连接)
  • 使用源系统 中的数据表——每一个(合适的)数据模型都应该依赖一个单独的日期维度。确保您已经在数据库中建立了日期表

Power BI 和 DirectQuery 最佳实践

一旦您使用了 Power BI,在使用 DirectQuery 模式时,您应该坚持以下最佳实践:

  • 避免复杂的 Power Query 转换 —每次将转换应用到数据模型时,Power Query 都会生成一个查询并将其发送到源数据库。假设我想用 783 替换我所有的 ProductKey 值 782。如果我在超级查询编辑器中这样做,会发生以下情况:

您可以看到 Power Query 为满足我们的请求而生成的 M 语句,但是如果您右键单击该步骤并选择 View Native Query 选项,您还可以看到将被发送到 SQL Server 数据库的 SQL 查询:

现在,对于这些简单的转换,Formula Engine 足够智能,可以在一次迭代中完成所有工作。但是,如果您执行一些更复杂的东西,打开 SQL Server Profiler 或 DAX Studio,看看有多少请求被发送到您的源数据库…

  • 如果您需要使用 计算列 ,请尝试将它们的创建推送到源数据库并保持它们的持久性
  • 避免复杂的 DAX 度量——由于您的 DAX 语句需要“翻译”成 SQL,请记住,此过程可能会产生昂贵的 SQL 查询。同样,只要有可能,在源端执行所有的计算
  • 避免 GUID 列(唯一标识符)上的关系— Power BI 不支持这种数据类型,需要在查询执行期间应用一些数据转换,这会影响性能。解决方案是在 Power BI 生成自己的查询之前,在源数据库中转换这种数据类型
  • 尽可能限制并行度—您可以定义 DQ 可以同时打开的最大连接数:

如果转到“选项”,在“当前文件”下,可以为每个数据源的最大连接数指定一个值(默认为 10)。

  • 在 Power BI 报告中,几乎没有额外的优化选项:

这些选项中的大多数都是不言自明的。基本上,您可以限制切片器和过滤器,因为默认情况下,当您更改一个切片器值时,所有切片器都会生成对数据源的查询(即使是那些没有更改的数据)!

因此,您可以添加“应用”按钮,以便用户可以具体选择需要刷新的数据部分。当然,这将影响您的报表设计,因为您需要为这些按钮提供额外的空间:

你还应该仔细想想报表页面上的 视觉效果的数量 —视觉效果越多,数据检索所需的时间就越长。

最后,你应该 关闭视觉效果之间的交叉高亮和交叉过滤 ,因为这将减少生成的查询数量。您可以在整个报告级别上关闭这些功能(在选项- >查询缩减- >中,选中选项:默认情况下禁用交叉突出显示/过滤),或者仅针对特定视觉效果关闭这些功能。

还有一个非常重要的建议— 与您的用户 交谈,尝试向他们解释导入模式和 DirectQuery 之间的区别,他们使用这两种模式中的每一种可以获得什么好处,以及预计会有哪些缺点(尤其是在选择 DQ 模式时)。

结论

正如你自己可能得出的结论: 如果你正在考虑使用 DirectQuery,为工作选择正确的工具 是最好的建议。

在做出最终决定之前,请仔细评估您的潜在工作负载,并尝试确定导入和 DirectQuery 方法的所有优缺点。

感谢阅读!

成为会员,阅读媒体上的每一个故事!

订阅这里获取更多有见地的数据文章!

https://youtu.be/7dssvnKEmCA

直接到熊猫数据框

原文:https://towardsdatascience.com/direct-to-pandas-dataframe-ab2e97ae7574?source=collection_archive---------43-----------------------

使用 Chrome DevTools 和 Python 直接从远程 URL 下载和读取数据到熊猫数据框架的 3 分钟指南

照片由帕特里克·福尔Unsplash 拍摄

挑战

对于新的数据科学家和软件开发人员来说,从远程 URL 下载数据通常是自动化的首要任务之一。例如,在下图中,我想下载并解析来自 Data.gov每小时降水量 CSV 样本数据,并将其直接解析到熊猫数据帧中。

问题是不清楚如何找到下载按钮重定向到的 URL 路径。

查找远程 URL 路径

使用 Chrome DevTools(或类似工具)并遵循以下步骤,很容易找到远程 URL 路径。

  1. 导航到要下载数据的页面。
  2. 右键打开 Chrome DevTools 检查或导航到 Chrome 菜单→更多工具→开发者工具
  3. 在 Chrome 开发工具中导航到控制台。您可能会在控制台中看到文本,可以使用右键→清除控制台来清除。
  4. 打开控制台,点击下载按钮或类似按钮。
  5. 下载按钮重定向到的 URL 路径现在显示在控制台中。

使用 Chrome DevTools 控制台查找 URL 路径

直接下载数据到熊猫数据框

一旦找到远程 URL 路径,就很容易将数据读入 Pandas 数据帧。下面的代码演示了如何解析 CSV 文件,但是对于 JSON、Excel 和其他文件类型来说,这样做也很容易。

从远程 URL 直接读取数据到熊猫数据框

上述代码的输出

直接批量下载到熊猫数据框

通常你不会自动下载单个文件,而是从一个远程 URL 下载一批文件。例如,下图显示了温哥华国际机场每小时天气数据的下载门户。问题是下载时间为 1 个月,所以如果我想下载 1 年的数据,我需要下载 12 次。

让我们检查一下下载数据按钮使用 Chrome DevTools 控制台重定向的远程 URL。

" https://climate . weather . GC . ca/climate _ data/bulk _ data _ e . html?format = CSV&station id = 51442&Year = 2020&Month = 6&Day = 7&time frame = 1&submit =下载+数据

注意,除了其他唯一标识符之外,还有下载年、月和日。因此,让我们编写一个简短的脚本来下载 1 年的数据,并将其合并到一个单一的熊猫数据帧中。

从远程 URL 读取多个文件的数据并合并

上述代码的输出

结论

Pandas 是任何数据科学家的必备工具,Chrome DevTools 是工具箱中的绝佳补充。按照上面的步骤,很容易将数据直接下载到 Pandas 数据框架中,并且很容易将其扩展到其他文件类型。

编码快乐!

脏数据—质量评估和清理措施

原文:https://towardsdatascience.com/dirty-data-quality-assessment-cleaning-measures-39efb90ad734?source=collection_archive---------23-----------------------

在《坏数据手册》一书中,伊森·麦卡勒姆说得对,“我们都说我们喜欢数据,但我们在乎的不是数据,而是我们从中获得的见解。”然而,数据分析师只能将 20%的时间投入到从数据中产生洞见的艺术和科学中。他们剩下的时间花在构建和清理数据上😫

为了最大限度地减少数据清理的时间投入,需要适用于不同行业、职能部门和领域的不同数据和业务用例的标准化框架和工具。

这个博客旨在让你具备构建和执行这种标准化数据质量框架所需的知识,这些框架适用于你的数据和用例。我们将重点关注:

  1. 数据质量和数据清理的基本概念,以及如何根据您的数据周期需求构建一个框架。
  2. 使用适合您的数据质量和清理流程的工具和技术快速跟踪执行。

I/什么是数据质量和数据清理?

数据质量 是通过检查不一致、不准确、不完整或不合理的数据值来评估数据质量的方法。

数据清洗 是根据数据的质量来修正数据的方法。

数据质量和清理方法循环运行,直到我们达到所需的数据质量状态。用例以及数据属性定义了这个过程的所有组成部分:比如要测量的数据度量或者检查的数量、需要的自动化程度、错误处理的复杂度等等。

例如,一个组织的销售数据每天为销售、营销和增长团队的多个仪表盘提供动力。这些团队中的每一个都依赖于底层数据的准确性。在这种情况下,流程应该包含所有可能的检查,维护可共享的数据质量报告,并运行日志来追溯错误。然而,如果我们使用除我们之外的任何人都不会使用的广告数据进行特定的分析,那么数据检查只需要应用于对该分析至关重要的表的相关列。

数据质量(DQ)指标

数据质量和清理都在一个共同的线程上运行,我们喜欢称之为数据质量 (DQ) 指标。它们奠定了评估质量和定义清洁措施的基础。每个指标就像一个 Q & A,每个响应都有一个相关的动作。

例如,X 列中缺失观测值的计数是一个 DQ 度量。此指标的值有助于评估数据质量,并相应地构建清理操作,例如用 X 的中值替换 X 中所有缺失的值。

II/如何建立数据质量和清理流程?

要构建您的数据质量流程,理解以下数据属性和相关用例至关重要,这些属性和用例决定了您的整个数据质量和清理过程。

  1. 模式:数据的模式包括 R*C 矩阵形式的数据大小和列的数据类型(字符串、整数、日期等)。关于图式的有趣讨论在这里
  2. 数据来源:导入导出来源。例如:数据库、数据仓库、数据湖、API、设备文件、商业智能工具等。和文件扩展名,如。xlsx,。csv,。shp,。兽人,。拼花地板。pdf,。tiff 等。
  3. 数据上下文:数据所有者、原始数据源(数据收集或进入系统的方式)、数据谱系、所有必需的列及其元单元、预期值或范围、相互关系等方面的数据上下文。越多越开心。
  4. 用例:干净数据支持的用例的上下文。这是非常重要的,但困难和费时,所以要聪明。从业务涉众那里了解数据的主要用例,并相应地构建您的流程。
  5. 规模:数据的规模包括过去、当前和未来数据表的规模,尤其是当数据更新且过程可重复时。
  6. 技术堆栈:用户对工具的偏好,包括代码工具与非代码工具、开源软件与付费软件、所需的自动化水平、现有的数据工具&软件以及资源投资。
  7. 输出:理想的输出结构。例如:干净数据表的结构,数据质量报告,错误表,日志等。
  8. 可重用性:对数据质量可重用性水平的需求&清理代码,确定所需的自动化水平。人们可以使用 excel 清理静态数据表,编写快速代码来清理多个相似的文件,用 R/python 构建多用途黑盒库或函数,以便团队使用。
  9. 可重复性:整个数据质量的可重复性&清洗过程取决于相同数据表的更新频率,例如,一家快速消费品公司的销售数据每天都会刷新。这包括选择工具来构建协调的数据清理工作流、数据质量分析或报告、错误处理、运行日志维护等。

III/确定数据质量(DQ)指标列表

它以一个简单的问题开始:这个数据集会出什么问题?模式、数据格式、数据上下文、用例以及输出:这些是数据&用例的属性,将有助于回答这个问题,并建立您的 DQ 度量的详尽列表。

根据我们的经验,我们将 DQ 指标分为三个级别:

级别 1 :不管数据集类型或用例的类型如何,都应该检查通用的 DQ 度量。几个例子:

  • 数据类型/模式
  • 缺少值
  • 唯一主键
  • 极端值
  • 负值等。

点击 阅读更详细的一级 DQ 指标

级别 2 :不同数据集和用例的上下文检查。例如:

  • 列关系-列应遵循其预期的关系,如人口普查数据文件中的人口列值不能小于家庭列值。
  • 上下文数据范围-基于数据的上下文,测试期望值或值的范围,例如,如果数据是从 2014 年开始的,则日期不能在 2013 年之前。

级别 3 :比较检查,将数据表与理想或主数据表进行比较。

  • 主匹配/实体解析-检查地理名称、产品名称、电子邮件域等实体。对照标准主文件剔除不良名称并用正确的名称替换它们。

IV/数据质量(DQ)指标的执行

除了一系列 DQ 度量标准,关于属性的知识,如可重用性、可重复性、规模和技术堆栈有助于执行 DQ 度量标准来评估质量,并相应地选择正确的工具集&技术来清理数据

为了更好地说明这一点,我们采用了三个场景,类似于我们过去遇到的场景,它们具有这些特征的不同组合,来演示不同的执行方法和手段。

[案例 1] —创建一次性仪表板来显示国家电力计划的进度

国家电力计划的数据是印度所有地区的月度时间序列数据。xlsx 格式(2 个小于 25k 行的文件& 15 列)。由于它是为一次性用例创建的(用于会议中的演示),所涉及的任务没有可重复性。然而,无论如何,该过程最好是可重复使用/可再现的,以便在达到最终期望输出之前考虑数据处理的多次迭代。

解决方案:

完成这项任务的最合适的工具可以是一个无代码开源工具,也可以是 R 中的一个脚本(由于我们对这种语言的熟悉程度),它将生成一个 CSV ,进而驱动一个 R 闪亮的仪表板。

我们使用 R 来评估数据质量,并为我们的 R shiny 仪表板生成干净的结构化文件。

链接到与其相应的 web 链接一起使用的 R/python 函数列表。

或者,您也可以使用:

  1. MS Excel:Excel 的宏功能可以自动执行可重复的功能,如找出缺失值、删除重复数据等。并安装额外的插件,如模糊的地理主匹配。
  2. Tableau : Tableau 既有可视化&数据清理工具——假设你愿意用 Tableau 代替 R shiny。你还是得做一场地理大师赛。
  3. 如果你喜欢无代码的方法,那么这是一个很好的选择,因为它将 UI 命令转换成 R 代码片段,可以存储并在以后使用。

[案例 2] —创建一个显示进度指标的控制面板,并使用通过卫星图像捕获的外部代理指标进行分析和验证。

在这种情况下,方案数据在村庄级别(60 万以上的印度村庄)持续 5 年,每月卫星图像的粒度是印度村庄的 10 倍。因此,数据量很大,输入数据格式多种多样—CSV 和地理空间栅格(图像),输出仪表板需要一个带有数据表的 shapefile,以便能够绘制地图,并且我们要求清理代码可重复使用

解决方案:

在这种情况下,我们使用 R,因为它可以处理 CSV 以及空间矢量和栅格格式。我们也更喜欢使用 QGIS,因为它非常适合视觉验证。以下是除案例研究 1 中提到的检查之外,我们对卫星图像和地理标记村庄数据文件进行的一些额外检查。

链接到与其对应的 web 链接一起使用的 R/python 函数列表。

或者,您也可以使用:

  1. Python:在这种情况下,使用 R 或 python 没有区别。它只是基于分析师的偏好和技能。
  2. ArcGIS:对于所有的空间/卫星操作和可视化来说,它是一个伟大的无代码选项,但它是一个付费许可软件。

加成:在 R-【https://atlan.com/courses/introduction-to-gis-r/】学习 GIS

[案例 3] —创建一个实时仪表盘,每月使用方案和卫星图像数据进行更新,无需任何人工干预。

在这种情况下,亮点是自动化的 ETL 工作流,它将为实时仪表板提供动力。除了我们在案例 I 和案例 II 中涉及的检查,我们还需要做出正确的决策,并对以下数据和用例属性执行:

  • 全自动可重复可重用流程,用于提取/导入数据、运行转换(数据结构化、DQ 指标、清理操作)以及在仪表板中加载/导出数据(ETL 工作流)。所有这些将以不同的频率运行(每周、每月和每年)。
  • 通过 API(JSON 和 XML 格式)处理输入,过去的数据存储在 AWS cloud 和 mysql DB 中。
  • 管理日益增长的 大规模数据(5 年内每月约 400 万行 x 400 列)和 DQ 指标。这是因为存在更多数量的方案,因此需要检查更多这样的 DQ 度量。
  • 所需的输出不仅仅是每月的干净数据文件,还有错误日志、运行日志、数据质量报告和错误表。
  • 改变技术堆栈以实现上述所有要点。

为实现这些特性而采取的附加措施列表-

链接到 R/python 函数列表,这些函数与相应的 web 链接一起使用。

Docker 是一个开源工具,可以帮助开发一个与机器无关的应用程序或一组脚本。Docker 引入了容器概念,这是一个标准化的软件单元,允许开发人员将他们的应用程序与其环境隔离开来,解决了“但它可以在我的机器上工作”的头痛问题。由于我们的 ETL 过程是在我们的系统上构建的,但是应该在客户的系统上运行,所以需要对它们进行分类。点击了解更多关于 docker 的信息。

Apache Airflow 是支持该数据管道自动运行的系统,并向用户提供 UI 来查看日志、监控管道等。这是一个开源的工作流程管理工具,由 Airbnb 于 2008 年启动。在我们的例子中,每个方案都是一个独立的工作流或 DAG(直接无环图),每个都由不同的节点组成。每个节点执行一个步骤:从 API 获取数据、存储数据、检查数据、清理和转换数据,最后将最终输出写入指定的目的地。可以有多个输入和输出到达和去往不同的源。对于每个节点,我们可以跟踪它运行了多长时间,或者运行失败了多少次,等等。评估仪表板上的数据更新所需的净时间。

这种设置也有助于将项目轻松移交给其他团队,他们将来会负责监控和更新 scheme 的数据。要了解更多关于气流的信息,请查看这个博客:如何在 Apache Airflow 中创建工作流来跟踪印度的疾病爆发。此外,日志和警报在此设置中至关重要,因为它们有助于故障排除和准确识别问题所在的步骤。我们可以在 R 和 Python 中使用简单的、描述性的打印语句和其他日志库,如 log4r日志tryCatch

或者,现在市场上有一些有趣的工具可以帮助你用更少的资源更快地构建一个类似的过程

  1. Python:这是一种开源语言,可用于构建数据质量指标,并通过设置端到端 ETL 工作流来自动化其运行过程。虽然它适用于数据和用例规模不是如此之大的情况,但是没有资源编排或在外部机器上部署脚本,并且您或您的项目同事非常了解 python——那么 python 可以在某种程度上解决可重复性问题。查看这些关于这个主题的好文章,给 mac 或 linux 用户和 windows 用户。
  2. Alteryx :在 ETL 工具类别中,Alteryx 是市场上的一个既定名称。您可以使用大量可用于表格和空间数据的数据转换节点和质量度量来设置工作流,同时提供易于使用的错误处理和其他工作流管理流程。它有一些限制,如:与 Mac 或 Linux 不兼容(只有一个 Windows 兼容的桌面应用程序)或数据大小的挑战,这取决于您的 PC 或笔记本电脑在测试&构建工作流时的配置。根据您的预算和需求,您还可以查看类似的产品,如 InformaticaTalenddatataiku等。
  3. Atlan : Atlan 是一个云优先的现代数据质量管理工具&。就构建和维护数据工作流而言,您几乎可以做上面讨论的所有事情——完全不需要了解 docker 或 apache airflow,也不需要工程师的帮助。您可以根据您的数据使用其数据质量&清理建议来测试数据质量,也可以使用其度量库定义您自己的自定义数据检查,或者使用 SQL、regex 等构建您自己的度量。虽然在图像数据上构建数据质量检查在该产品上还不可用。由于 Atlan 是作为 web 应用程序部署的,因此不存在操作系统兼容性问题。免责声明:安基塔和我目前都在 Atlan 工作,所以不要质疑我们的偏见。😛

五.结论

在这篇博客中,我们试图揭开数据清理和质量实际需要什么的神秘面纱,这样你就可以最大限度地利用你的时间。在开始分析或建模部分之前,列出数据质量指标及其相关的数据清理活动有助于预先发现数据问题并优化清理工作。对可用工具和产品的了解有助于您学习和选择适合您的技能和团队技术的工具和产品。

无论数据有多大,只有质量好,才能产生有用的见解。否则,就等于垃圾进,垃圾出。

来源:讲解 xkcd;许可证: CC BY-NC 2.5

接下来的步骤: 岗位数据清洗和质量是最期待的部分,数据分析,统计&机器学习。为了下一步,我们创建了一个关于统计和机器学习的 python 课程。

我们的 Github repo'简介 _ to _ Statistical _ Learning _ summary _ python'总结本书《 统计学习简介》由 Daniela Witten、Trevor Hastie、Gareth M. James、Robert Tibshirani 编写,并将其变成一门 python 课程这本书的每一章都被翻译成一本 jupyter 笔记本 用概念总结、数据& python 代码来练习。

欲知该系列更多脉络,请通读其 简介 为好。

有趣的资源可以查看-

数据清洗概念-

  1. http://www . unofficialgogledatascience . com/2016/10/practical-advice-for-analysis-of-large . html
  2. http://2.droppdf.com/files/GU2nI/bad-data-handbook.pdf
  3. https://www . Reddit . com/r/data science/comments/fzweaf/my _ giant _ data _ quality _ check list/?UTM _ source = share&UTM _ medium = IOs _ app&UTM _ name = iossmf

数据清理工具列表-

  1. https://www.kdnuggets.com/software/data-cleaning.html
  2. https://www . analyticsvidhya . com/blog/2016/09/18-free-explorative-data-analysis-tools-for-people-dont-code-so-well/

数据清洗在 R-

  1. https://cran . r-project . org/doc/contrib/de _ Jonge+van _ der _ Loo-Introduction _ to _ data _ cleaning _ with _ r . pdf
  2. https://towards data science . com/data-cleaning-with-r-and-the-tidy verse-detecting-missing-values-ea23 c 519 BC 62
  3. R 中 dlookr 包的数据质量
  4. 汇总数据最喜欢的 R 包&R-汇总数据包-第 2 部分

R-中的错误处理

  1. http://mazamascience.com/WorkingWithData/?p=912
  2. http://adv-r.had.co.nz/Exceptions-Debugging.html

关于我们 :

我和我的合著者 Ankita mathur 花了 6 年多的时间共同致力于数据项目,从社会计划和跟踪仪表板到位置智能,并为国际慈善机构、非政府组织、国家中央政府机构和各种企业建立替代数据 API。

这些年来,我们发现了数据质量和清理过程的模式,这些模式帮助我们建立了框架和工具,以最大限度地减少我们的数据处理和清理时间,并在 SocialCops 运行这些大型项目。

在 2018 年初,我们意识到这些工具和框架可以被世界各地的数据团队使用。那时我们开始了与 Atlan 的旅程——构建工具来帮助数据团队减少他们在数据质量、清理、设置 ETL 流程方面的时间投资,并花费更多时间从他们的数据中找到有意义的见解。

作为我们社区努力的一部分,我们已经通过博客,课程,研讨会和实践研讨会帮助世界各地的数据团队建立他们的数据质量和清理流程。

特别感谢我们这个系列的杰出编辑——艾西瓦娅·比德卡尔🎉(并承诺再也不用“和”开头的句子🙈)

肮脏的科学:实践中的纪律

原文:https://towardsdatascience.com/dirty-science-discipline-in-practice-9b2dfc165686?source=collection_archive---------25-----------------------

在数据科学中保持“科学”的挑战。

在没有某种方向的情况下探索原始数据就像在垃圾场翻箱倒柜。当然,垃圾场里可能有一件有价值的古董,你甚至可能会偶然发现它。话又说回来,你可能只是花一下午的时间来分类垃圾。另一方面,如果你有一个具体的问题,可以用各种部件解决,并且你知道要找什么,那么从垃圾中筛选是值得的。区别在于有一个明确的目标,所以很容易接受/拒绝许多项目,而不是评估每个项目的价值。

如果你在一个收集所有可能数据的组织中,你不会像一个穿着干净的白大褂做实验的科学家一样。你将分类垃圾,寻找零件,并(希望)将它们组装成有用的东西。虽然你不是一个纯粹的科学家,但科学方法将帮助你完成这个过程。如果你不愿意弄脏你的手,把你的期望工资减半,申请一些研究基金。

科学是一个过程

卡尔·萨根写道:“对我来说,把握宇宙的本来面目要比坚持妄想好得多,无论这种妄想多么令人满意和安心。”太多时候,我看到数据科学家和分析师无意中充当宣传机器,用数据编织故事,无意识地扩散证实和事后诸葛亮的偏见。在一个充满数据的世界里,声称“数据驱动”很容易,但现实是,如果没有纪律,它往往会让人们相信一堆热气腾腾的垃圾是一顿热腾腾的营养餐。

科学旨在解释和理解自然界。该过程的主要部分是获得准确的测量值,并对其进行分析以得出结论。当数据以这种方式被收集和分析时,它是非常强大的。想要成为数据驱动的组织应该努力使用更多基于证据的实践。这里强调的是努力,因为在实验室和真正的随机对照试验之外,科学标准通常是无法达到的。

扫地

没有背景和方法,数据不是证据,它只是原始信息,可能有用,也可能没用。作为数据科学家,我们面临的挑战是,在不产生不必要的摩擦的情况下,通过建立上下文来大致应用科学方法。这是一个很难把握的平衡,你不可能让每个人都开心,但这不是我的工作。工作就是用数据来解释和理解你周围的世界。

这已经够难的了,所以不要通过个人失败来重新发现科学方法。遵守纪律,相信 500 年的老流程。我试着记住这是一门肮脏的科学,永远不会是完全干净的。因此,与其拿着一个可能会让我因为拖慢一切而被解雇的标准,我不如把它当成扫地,这样其他的工作也能完成。推扫帚是遵循科学方法的步骤。

1.问题

永远从好奇开始。找出一个未知的因素来激励你的组织,比如为什么某些客户会重复购买。这也可以更具体,比如我们如何设计更好的注册体验或开发一个应用程序来激励健康的习惯。这一步的关键是清楚地识别未知并记录下来。这将为剩下的过程提供方向和动力。我们都有好奇的天性,所以要了解它。

2.假设

接下来,制定一个明确的假设。这相当于为上一步中确定的未知因素声明一个解释。假设应该写成类似“简化注册过程将提高转化率”的陈述形式。在写假设的时候,留意下一步,帮助收紧。例如,“注册过程太复杂”不会导致明显的预测,而“简化注册过程将提高转化率”会。

一个好的假设是可证伪的,这意味着证据可以反驳或证伪它。“一艘外星飞船坠毁在罗斯威尔”这一说法是不可证伪的,因为如果这不是真的,那就只是缺乏证据。然而,“从来没有飞船降落在罗斯威尔”是可证伪的。找到一艘宇宙飞船,这个陈述就被证明是错误的。太多的组织持有不可证伪的信念,如“人们忠于我们的品牌。”推行这些信念就像用糖果来安抚一个有不良行为的孩子。不要走捷径。做一名科学家。

3.预测

有了明确的假设,是时候把筹码放在桌子上做个预测了。接受假设的逻辑结果,并致力于有证据支持的结果。例如,“如果我们简化注册体验,转化率将至少提高 1%”是一个可以实际测试的预测。预测通常会映射到多个假设或解释,因此重要的是要具体,并将其与主要调查紧密联系起来。

在实践中,我们想要做出的预测通常无法正确测试,或者它们可能会构成政治挑战。这就是纪律至关重要的地方。写下预测,好像一切皆有可能,然后评估测试的可行性。不采取这一步是一个滑坡,因为它很快导致完全避免预测。由于缺乏时间、资源或我无法改变物理定律,我做出的绝大多数预测都不可能得到验证。尽管如此,我不会限制自己的想象力,把事情写下来。

4.实验

现在是时候投入一些时间和精力来进行一项实验了。这是获取知识的黄金标准,正确地使用它并不简单。例如,测试更简单的注册体验的影响需要设计新的体验,将其与当前体验并行部署,将新用户随机分配到不同的组,并跟踪每个组中的指标。这伴随着硬开发成本以及你传递的其他东西的机会成本。

有时,这些测试可以像改变按钮颜色或字体一样简单,但在我看来,在那里没有多少知识可以获得。像这样的快速 A/B 测试对微调很有帮助,但在解释或理解我们的世界方面,它们并没有给我们太多帮助。有意义的发现有巨大的成本和风险。我们不得不花费资源来获取知识,并冒着假设错误的风险。科学是艰难的,这通常是外界压力毒害过程的地方。

另一方面,对我们来说,并不是一切都是科学。让每个想法都经过严格的实验过程是疯狂的。有时候,简化注册体验并自信地交付会更好,因为这是合理的做法。竞争瞬息万变,能够辨别新知识相对于获取成本的价值是一项非常高级的技能。

5.分析

最后,数据!如果你做了定义一个明确的假设,做出一个可证伪的预测,并运行一个干净的实验,那么这应该像比较两个组的统计数据并分享结果一样简单。在这种情况下,挑战通常是解决模糊性,并向非技术人员传达统计概念。对于年轻的数据科学家和分析师来说,这是一个在商界崭露头角的好地方。然而,通常情况下,它并没有这么干净。

当你丢失了随机对照试验的重要组成部分时,事情就变得肮脏了。也许你不能随机分配小组,或者根本无法创建实验小组。你可能会被迫分析时间序列数据或寻找一个自然实验来检测预期的效果。无论如何,分析应该总是在预测之后进行。如果你不能进行实验,在你寻找证据之前,对证据会是什么样子做出硬性承诺,并对你做出的让步极其清楚。

统计学家常常以开发出越来越复杂的方法来处理像这样的混乱局面而自豪。这是我见过的最底层的竞争。有时候,数据就是不存在,不准确的结论比根本没有结论更糟糕。至少“我们不知道”是诚实的,并把问题留待进一步调查。你从一个干净的、适当的随机实验中得到的越多,你就应该越不舒服。也就是说,不要害怕弄脏你的手,试一试。只要诚实,通过记录必要的让步来保护你的正直。

结论

当不可能严格遵循科学方法时,我经常听到非常聪明的、有良好意愿的人说“有总比没有好”之类的话。我倾向于同意这种观点,但会使用更谨慎的语言:“更准确是首选,但并不总是可以达到的。”你愿意没有尺子还是一把随机不准确的尺子?没有尺子,我有一个适定的问题:找一把好的尺子。有了一个有缺陷的统治者,我可能会在没有意识到的情况下制造更大的问题。

科学的目标是解释和理解世界。科学的方法提供了明确的步骤来识别和获取必要的知识。在各种环境中应用它需要纪律和视角。对我们来说,科学是一个大部分但不总是有用的过程,能够区分这两者是一个独特的挑战。数据科学是一门肮脏的科学。现在拿起扫帚回去工作。

基于脑电时间序列分析的残疾诊断

原文:https://towardsdatascience.com/disability-diagnostic-using-eeg-time-series-analysis-8035300837ac?source=collection_archive---------21-----------------------

一种关于如何通过使用卷积神经网络(CNN)分析脑电图(EEG)记录来识别儿童是否受到自闭症影响的研究方法。

(来源:http://epscicon . vidyacademy . AC . in/WP-content/uploads/2017/12/EEG . jpg)

介绍

儿童出生时可能会有残疾。在早期发现他们的问题可以在以后的生活中带来更持续的改善。这主要取决于神经可塑性(大脑在一生中自我重组的能力),这在我们生命的最初几年要高得多。

自闭症谱系障碍(ASD)是一种神经发育障碍。这些类型的疾病主要被认为是正常脑功能的破坏。在过去的几年里,有报道称越来越多的儿童出现这种症状。美国 2014 年的一项调查数据显示,与 2012 年进行的同一调查相比,增长了 16%。根据这项研究,美国约有 1/59 的儿童患有自闭症谱系障碍(“CDC 的自闭症和发育障碍监测(ADDM)网络”,[1])。

最近的研究表明,强化的早期开始干预方法可以导致这种情况的重大改善。因此,使用机器学习的早期检测可以在这一领域发挥至关重要的作用。

在这篇文章中,我将概述我采用的不同方法来分析标记的 EEG 数据(已经预处理),这些数据包含受 ASD 影响和不受 ASD 影响的儿童的脑电波记录。利用诸如、支持向量机(SVM) 、决策树、CNN 和 LSTM 的算法,已经有可能实现 96%以上的分类准确度。

机器学习

我一直在处理的数据集由 129 列组成。前 128 列代表信号采集期间使用的 128 个 EEG 通道,最后一列是数据标签(受自闭症影响或不受自闭症影响)。数据集的每 250 行代表一个时间序列重复。每个孩子的时间序列重复在 20 和 80 之间变化,取决于主题。数据集被证明是平衡的,包含 49.03%的无自闭症儿童的数据和 50.97%的自闭症儿童的数据。

数据预处理包括首先加载数据,将其标准化,然后针对输入和输出值将其分为训练集(70%)和测试集(30%)。为此,我使用了 python 库,如 pandas、numpy、matplotlib.pyplot、sklearn 和 itertools。所使用的 ML 模型试图仅使用单一的刺激重复来识别儿童是否受到 ASD 的影响。

获得的结果总结在表 1 中。

表 1: ML 分类精度。

决策树获得了最准确的结果。决策树是一种监督学习算法,可用于分类或回归任务。决策树与其他 ML 算法的区别在于它们能够使用倒置的树状表示来显示决策过程。在树中,每个节点代表数据集中的一个特征,每个分支代表一个决策规则,每个叶子代表一个决策结果。在这个实现中,我决定使用 CART(分类和回归树)算法实现,它利用基尼指数作为度量。

卷积神经网络(CNN)

为了提高分类性能,我决定使用 Keras Python 库设计一个 CNN 模型。CNN 是一类神经网络,通常用于图像识别/分类,但也可用于时间序列分析。这可以通过将时间序列转换成类似灰度图像的格式来实现。

图 1: CNN 的脑电图分析[2]

模型架构包括:

  1. 64 个滤波器的一个 2D 卷积层、5×5 的核大小、ReLU(校正线性单元,等式 1)函数和相同的填充。
  2. 另一个 2D 卷积层具有 32 个滤波器、5×5 的核大小、ReLU(校正线性单元)函数、相同的填充和 0.01 的 L2 正则化系数(以防止过拟合)。
  3. 2x2 大小的 2D 最大池层。
  4. 强度为 0.2 的丢弃层(为了避免过拟合数据)。
  5. 一个层首先将数据从三维展平为一维,然后另一个层压缩输入以提供给分类器 128 特征(总是使用 ReLU 函数和 L2 正则化)。
  6. 强度为 0.5 的第二脱落层。
  7. 最后,密集层(两个神经元)使用 Softmax 激活函数产生分类结果。在这种情况下,Softmax 函数(等式 2)将从神经元获取两个输入,并将它们转换为两个概率,这两个概率的总和为 1。较大概率被向上/向下舍入到 1 或 0,以表示 CNN 输出选择。

等式 1: ReLU 激活函数

等式 2: Softmax 函数

为了优化训练,采用 Adam(自适应矩估计)梯度下降算法,并用交叉熵函数计算模型损失。在二元分类的情况下,交叉熵函数可以通过使用等式 3 来计算。

等式 3:交叉熵函数

该模型在仅仅三十个训练周期中实现了 94.58%的总体验证准确率。

图 2: CNN 的准确性和损失

结论

本文证明了在预处理的 EEG 数据上使用 ML 和深度学习模型总体上可以获得良好的分类结果。作为这个项目的进一步发展,我还设法开发了一个 LSTM 模型,能够达到 96%以上的分类准确率。其他指标,如混淆矩阵、灵敏度、特异性和 AUC-ROC 曲线已被用于交叉验证这些模型的准确性。

此外,在预处理阶段应用自动编码器网络[3]来检查是否可以使分类更容易并加快模型训练时间也是有意义的。

本实验中需要考虑的一个问题可能是所用数据的可靠性(例如,数据是否收集正确,测量过程中记录的所有噪声是否已被正确过滤……)以及使用不同类型的 EEG Cap 或在不同实验室条件下收集的其他形式的数据,这些精度结果的可重复性如何。

当试图在医疗应用中使用人工智能时,必须获得高精度和可靠性。可解释的人工智能(创建能够解释自己分类决策过程的模型)可以帮助医生理解在决策时如何/是否使用人工智能。

此研究项目的完整在线图书版本可通过此链接获得。

我要感谢kou shik Maharatna教授给我这个机会开展这个项目。

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]疾病控制和预防中心,国家出生缺陷和发育障碍中心,出生缺陷处。访问时间:【https://www.cdc.gov/ncbddd/autism/data.html】T22018 年 12 月。

[2]使用卷积神经网络的单次试验 EEG RSVP 分类,Jared Shamwell,Hyungtae Lee et al. 访问:https://www . spiedicallibrary . org/conference-proceedings-of-spie/9836/983622/Single-trial-EEG-RSVP-classification-using-convolutionary-neural-networks/10.11117/12.224172 . short?SSO = 12019 年 6 月。

[3]杰瑞米·乔登自动编码器简介。访问日期:https://www.jeremyjordan.me/autoencoders/2019 年 3 月。

令人失望的人工智能:使用 AWS 进行 OCR 和名人识别

原文:https://towardsdatascience.com/disappointing-ai-using-aws-for-ocr-celebrity-recognition-818b8ba2a95c?source=collection_archive---------27-----------------------

或者当 AWS 把巫师误认为塞尔维亚足球运动员时。

摇滚猴子Unsplash 上的照片

Forbes 报道,到 2020 年,83%的企业工作负载将在云中,其中 41%在 AWS、谷歌云平台或微软 Azure 等公共平台上。在我最近作为数据科学家的一个项目中,我不得不开始习惯云计算、存储和部署。在这个方向上的另一个好的步骤可能是开始试验云服务,如图像识别、字符识别和语音识别,如果不只是为了了解它们提供的性能。

在黑客马拉松中测试 OCR 服务

测试提供光学字符识别的云服务是我几周前参加的一次黑客马拉松的主题,那是在我目前工作的公司。这让我有机会测试 AWS 的人工智能服务,主要集中在 OCR ( Textract,Rekognition ),但也有一些有趣的服务,如名人识别** ( Rekognition )。事实上,AWS Rekognition 应该是一个相当完整的服务,可以检测物体,识别人脸和检测文本。现实与此略有不同。**

关于 OCR 的一个小提示

OCR 代表系统将键入的、手写的或印刷的文本检测为机器编码的文本的能力,无论该文本是来自扫描的文档、文档的照片、标志还是照片中显示的其他类型的文本。它目前是一个热门话题,因为许多“老”机构(如政府机构、银行或保险)都致力于将所有文档(如可能包含印刷和手写字符的印刷合同)数字化。通常,OCR 可以分为两组:

  • HCR :手写字符识别
  • PCR :印刷字符识别

HCR 往往比 PCR 更具挑战性,原因很明显:大多数人的笔迹令人困惑。这种差异在我们体验的结果中也是显而易见的。

第一个失望:使用 AWS Textract 的 OCR

黑客马拉松旨在比较 AWS、谷歌视觉和微软 Azure 的人工智能服务在光学字符识别方面的表现。在 AWS 的一个数据工程项目中有了成功的经验,我选择了加入 AWS AI 服务团队。我很兴奋,期待亚马逊再次展示其世界领先的性能。以下是亚马逊对这项服务的承诺:

Amazon Textract 是一项从扫描的文档中自动提取文本和数据的服务。Amazon Textract 超越了简单的光学字符识别(OCR ),还可以识别表单中的字段内容和存储在表格中的信息。

示例发票上的 AWS Textract 结果(印刷字符识别)

由于 PCR 似乎是 Textract ( 扫描文件)的重点,我用我能在网上找到的一份发票样本进行了尝试。事实上,结果相当准确,除了一些拼写错误的单词。

Mnist 数据集的 AWS 文本摘要结果

由于 Textract 应该“超越 OCR ”,我希望它也能处理手写文本,比如众所周知的 MNIST 数据集。不幸的是,我错了。Textract 在手写字符识别方面做得很糟糕。

然而,Textract 似乎更像是一个 PCR 服务,而不是我们所期望的完整的 OCR 服务。然而,这就像三天前你在网站上订购了一台咖啡机,亚马逊却推荐给你另一台一样令人失望。

为了弄清楚我是错过了什么还是在 AWS 中选择了错误的服务,我进一步研究了云提供商的 OCR 选项。这时我发现 Rekognition 也应该检测文本。如果 Rekognition 是为了处理手写作业而创建的呢?

第二个失望:使用 AWS Rekognition 的 OCR

事实上,AWS Rekognition 也被认为擅长检测图片上的文本。亚马逊声称:

文本检测是 Amazon Rekognition 的一项功能,允许您检测和识别图像或视频中的文本,如街道名称、标题、产品名称、叠加图形、视频字幕和车辆牌照。文本检测专门用于处理真实世界的图像和视频,而不是文档图像。

同样,没有明确提到手写文本,但我不得不尝试一下。

同一 MNIST 数据集上自动气象站识别中的文本检测结果

结果比 Textract 提供的要好得多,尽管我们可以期待更好的结果。许多数字被误认为是字母,没有一行被完全准确地检测到(第 5 行几乎被检测到,但不知何故数字 2 根本没有被检测到)。令人惊讶的是,虽然我们预计亚马逊会提供出色的性能,但我的同事们发现,在 MNIST 数据集等手写内容上,谷歌视觉和微软 Azure 的表现优于 AWS(尽管我没有他们的结果来证明这一点)。

那么目标检测和名人识别呢?

为了克服 AWS 中的 OCR 所带来的失望,同时为了确保我们不会(再次)错过能够更好地执行此任务的服务,我们研究了 AWS 提供的其他服务。正如我在上面简要提到的,AWS Rekognition 服务的主要目的是检测目标,但它也可以识别成千上万的名人,在体育,政治等。由于我们在 OCR 上找不到其他服务,为了给自己留点娱乐,我们在 Rekognition 上尝试了名人识别选项。

第三个失望:亨利·卡维尔,《女巫》(The Witcher)和一个金发足球运动员。

AWS Rekognition 承认亨利·卡维尔。最初的照片来自托马斯·桑森/法新社 Getty Images

就像世界上其他成千上万的人一样,我最近在疯狂地看了《The Witcher》之后,成了亨利·卡维尔的超级粉丝。好的故事,好看的演员,魔术,龙,最重要的是,马(主要是罗奇,但至关重要的故事)。我完全被说服了。无论如何,当我们发现了名人识别软件,我的第一个想法是看看它是否会承认亨利·卡维尔是…嗯,亨利·卡维尔。没什么好惊讶的,确实如此!(耶,一个准确的探测!).

在第二个例子中,我想挑战这个系统,看看它是否能把巫师 T4 认出来,也就是亨利·卡维尔。老实说,我没想到它会起作用。他的头发颜色完全不同,长度也完全不同,眼睛颜色也不一样,他的脸甚至还做了额外的修饰,使它看起来更瘦更白。这对于一款人工智能车型来说是不公平的竞争,即便它的动力来自于巨头亚马逊。然而,我绝对没有准备好这个结果…

AWS Rekognition 把 Witcher 误认为塞尔维亚足球运动员。首张照片来自卡塔林凡尔纳/网飞

杜三·巴斯塔(见下文)是塞尔维亚的一名国际足球运动员,他和亨利·卡维尔和 The Witcher 没有任何关系(除了他是金发和长头发)。公平地说,我想被娱乐,我得到了一个很好的笑了这个。

杜三·巴斯塔,足球运动员,(据 AWS 报道)新任命的 Witcher

我把这项服务用在了《The Witcher》中亨利·卡维尔的其他几张照片上,脸上的照明效果更好,相机的角度也更好。在这些案例中,既没有名人面孔被认出,也没有发现塞尔维亚足球运动员。

另一张巫师的照片上写着“不认识任何名人”。最初的照片来自卡塔琳·凡尔纳/网飞

总之,我不认为 AWS 人工智能服务不可用,但与其他云提供商相比,它们在 OCR 方面肯定落后。这些结果对于手写字符识别来说是不够准确的,并且当检测扫描文档(PCR)中的文本时仍然可以改进。然而,我查询了几次 API,它检测文本的速度是惊人的。API 的可用性也是一个很大的优势。免费层允许每月分析 5000 张图像,之后,每张图像的价格仍然非常实惠,为每张图像 0.001 美元。

最重要的是,我必须承认名人识别服务是一个有趣的工具。同样有趣的是,当一个男人留着长长的金发时,人们很容易被弄糊涂。因为我相信我们只是触及了可能性的表面,所以我会很高兴地收到关于 AWS 服务的建议,无论是对其性能、结果还是任务的娱乐性而言,这些服务都是有趣的。

如果你喜欢这篇文章,请关注我的 Medium Twitter,以便在新文章发布时获得通知。

[## 如何成功转向数据科学职业

许多人希望有不同的职业道路,并希望过渡到数据科学家的职位,但这只是一个…

towardsdatascience.com](/how-to-make-a-successful-switch-to-a-data-science-career-537e55469af8) [## 向任何人解释版本控制的简单故事

让我们一起盖房子吧…用 git。

towardsdatascience.com](/a-simple-story-to-explain-version-control-to-anyone-5ab4197cebbc)

DiscoBERT:缩短你阅读时间的 BERT

原文:https://towardsdatascience.com/discobert-a-bert-that-shortens-your-reading-time-be49d03e1ff?source=collection_archive---------33-----------------------

使用 BERT 来总结那些你不想阅读的长文档

布雷迪·贝里尼在 Unsplash 上的照片

现在,世界联系如此紧密,我们不断受到来自各种不同来源的信息的轰炸,这可能会令人不知所措。社交媒体也改变了信息呈现给我们的方式,Instagram 和 Pinterest 等应用程序主要关注视觉内容,而不是文本。这使得当我们不得不阅读大段文字时,它就不那么吸引人了。

但是,如果这些大篇幅的文本可以转换成仅仅是关键点的摘要,那会怎么样呢?机器学习再次拯救了我们。

多年来,摘要一直是自然语言处理社区感兴趣的任务,有两种广泛使用的方法:抽象提取。抽象方法侧重于理解整个文本并一次生成一个单词的摘要。另一方面,抽取式摘要侧重于从文本中选择重要信息(通常是句子),并将它们拼接在一起形成摘要。

抽象的摘要通常比提取的模型更简洁,但可能在语法上不正确,因为基于概率生成单词不是编写摘要的最直观的方式。然而,这两种方法都有一个共同的问题。他们无法跟踪文档中的长期上下文。例如,如果一篇新闻文章在第一段谈到了利奥·梅西,在第四段又提到了他,这两种方法都很难将这些提及的上下文联系起来。对于抽象模型(主要是 Seq2Seq 模型),这是因为 Seq2Seq 模型比其他模型更重视最近的单词,而提取模型通常意味着几个句子或一个段落,而不是整个文档。

微软 Dynamics 365 AI Research 的研究人员旨在通过提出他们自己的模型来改进这些摘要技术,用于文本提取的话语感知 BERT(disco BERT)。这个名字透露了一个事实,即它是一个提取模型,但有一个扭曲。与其他提取模型不同,DiscoBERT 提取基本话语单元 ( EDUs ,它们是句子的一部分),而不是句子用于其摘要。这有助于生成的摘要简洁,因为整个句子可能包含一些 EDUs 中不存在的无关信息。

它还通过创建文档的两个不同的图来解决长期上下文问题。第一张图,****RST 图,是基于修辞结构理论。这一理论声称,文本的结构可以归结为一棵树的独立单元。以类似的方式,RST 图试图分析这些单位之间的依赖关系,并确定它们在句子中的作用。第二张图,共指图,着重于建立对贯穿全文讨论的关键实体(人、组织等)、事件和主题的理解。

通过这两个图表,DiscoBERT 了解了文本的重要区域是什么,并在此基础上对 edu 进行了排序。为了生成摘要,DiscoBERT 选择排名前 N 的 edu(也考虑了依赖关系)并按时间顺序将它们缝合在一起。

研究人员通过在两个新闻数据集上运行 DiscoBERT 来验证它,一个由来自纽约时报的文章组成,另一个来自 CNN 和 DailyMail,它们需要一个模型来总结每篇文章。DiscoBERT 在这两个数据集上实现了新的最先进的性能。

"大多数行业新闻应用在新闻推荐方面做得很好,但我们希望以一种更高效、更容易理解的方式发布新闻。微软动态 365 人工智能研究院高级首席研究经理刘晶晶说,他负责监督这个项目。所以,下次当你坐公交车想快速浏览每日新闻时,让 DiscoBERT 来帮你吧!

如果你想了解更多关于 DiscoBERT 的信息,这里有一个链接到论文,如果你想尝试训练它总结你的故事、报告等等,这里有一个链接到代码,点击这里查看更多我们的出版物和其他工作。

参考文献

1.威廉·C·曼和桑德拉·A·汤普森,1988,修辞结构理论:走向语篇组织的功能理论,《语篇研究的语篇跨学科期刊》,8(3):243–281。

2.徐,贾成,柘淦,于成,,话语感知的神经抽取文本摘要计算语言学协会第 58 届年会论文集,第 5021–5031 页。2020.

使用 CloudWatch 警报、SNS 和 AWS Lambda 的不一致通知

原文:https://towardsdatascience.com/discord-notification-using-cloudwatch-alarms-sns-and-aws-lambda-71393861699f?source=collection_archive---------17-----------------------

Khadeeja YasserUnsplash 上拍摄的照片

警报的存在是为了在我们的系统以一种意外的方式运行时通知我们,这保证了手动干预来纠正。当我们在一个生产环境中有多个系统而一个错误没有被注意到时,后果可能是灾难性的。

当系统无法自动恢复,需要人工干预时,应发出警报。如果警报发生得过于频繁,可能会导致更长的响应时间,甚至被遗漏。

在本文中,我们将为 AWS Lambda 函数构建一个警报通知管道。为此,将使用 3 个 AWS 服务:AWS Lambda、简单通知服务(SNS)和 CloudWatch。目标是在触发 CloudWatch 警报时向不和谐频道发送通知

使用 Lucidchart 设计:https://www.lucidchart.com

步骤 1 —云监控警报

选择指标

首先,您需要选择一个 CloudWatch 指标来观察警报。对于 Lambda 函数,有 3 种类型的指标:

  • 调用度量:调用结果的二进制指示器。例子:调用错误死信错误目的交付失败节流
  • 性能指标:单次调用的性能细节。如:时长迭代器
  • 并发度量:跨函数、版本、别名或 AWS 区域处理事件的实例数量的总数。示例:并发执行ProvisionedConcurrentExecutionsProvisionedConcurrencyUtilizationUnreservedConcurrentExecutions。

你可以在 AWS 开发者指南中阅读更多关于 AWS Lambda 指标的细节。

在这个例子中,我们将监控错误度量,即导致函数错误的调用次数。

图表度量

接下来,您需要为指定时间段内的数据聚合选择统计,即应用上面定义的度量的统计:样本计数平均值总和最小值最大值。我们将使用总和

变量 period 表示应用上述统计数据的时间,单位为秒。我们将它设置为 60s。数据与指定阈值进行比较的周期数将被设置为 1,因为我们希望我们的错误指标仅在 1 分钟的间隔内进行评估。

情况

最后,您需要设置比较运算符,这是在比较指定的统计数据和阈值时使用的算术运算。支持的条件:

  • GreaterThanOrEqualToThreshold
  • 更大的阈值
  • 小于阈值
  • LessThanOrEqualToThreshold

我们将使用条件: GreaterThanThreshold ,阈值为 0

通知

正如本文开头提到的,CloudWatch 警报将触发一个 SNS 主题来通知警报何时处于ALARM状态。

地形代码

下面的代码包含了上面讨论的所有设置。它还包含一些这里不会讨论的附加设置。您可以看到,我在变量alarms_actions中添加了我们将在下一节中创建的 SNS 的 ARN,这意味着当 SNS 转换到ALARM状态时,该警报将触发 SNS。

上面的代码允许您创建任意数量的 CloudWatch 警报。您只需编辑local.alarms_dimensions:

步骤 2 —简单通知服务(SNS)

来自 AWS 文档:

Amazon Simple Notification Service(Amazon SNS)是一种 web 服务,用于协调和管理向订阅端点或客户端发送消息。它使您能够通过发布/订阅(发布/订阅)模式在系统之间进行通信

我们的 SNS 需要向 Lambda-Alarm 发布消息,然后 Lambda-Alarm 会向 Discord 通道发送一条自定义消息。

地形代码

在下面的代码中,我们为 SNS 主题订阅了 Lambda-Alarm,并在endpoint参数中传递了 lambda ARN。

第 3 步—λ警报

当被 SNS 主题触发时,该 Lambda 将使用以下消息格式向不和谐频道发送警报:

不和谐频道截图

不和谐的网钩

首先,您需要在所需的频道中创建一个 Webhook。在编辑频道页面,你将进入整合,以及创建 Webhook。

不和谐频道设置截图

然后,您将复制 Webhook URL 并将其粘贴到您的 Lambda 代码中。

λ代码

下面的代码非常直观,不需要过多解释。你可以在 Discord 的开发者门户上获得关于通道对象的更多信息。

结论

在本文中,我们使用 CloudWatch Alarms、SNS 和 AWS Lambda 构建了一个警报通知系统。当我们的 lambda 中出现错误时,我们将在 Discord 通道中收到一条消息。

当然,您可以将 Lambda 警报与除 Discord 之外的其他服务集成在一起,例如:

  • 任何电子邮件服务:Gmail,Outlook,雅虎等
  • 松弛的
  • 微软团队

你可以在这里查看完整代码!

参考

[## 基于静态阈值创建云观察警报

您为要观察的警报选择一个 CloudWatch 指标,以及该指标的阈值。警报进入警报状态…

docs.aws.amazon.com](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/ConsoleAlarms.html) [## Discord 开发者门户-面向机器人和开发者的 API 文档

将你的服务与不和谐融为一体——无论是机器人、游戏还是你能想到的任何东西…

discord.com](https://discord.com/developers/docs/resources/channel#embed-limits) [## 使用 AWS Lambda 函数度量

当您的函数处理完一个事件时,Lambda 会将关于调用的指标发送到 Amazon CloudWatch。你…

docs.aws.amazon.com](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html) [## 地形注册表

编辑描述

registry.terraform.io](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm)

使用 Python 探索 3D 点云处理

原文:https://towardsdatascience.com/discover-3d-point-cloud-processing-with-python-6112d9ee38e7?source=collection_archive---------4-----------------------

3D Python

教程简单地设置您的 python 环境,开始处理和可视化 3D 点云数据。重点介绍 Anaconda、NumPy、Matplotlib 和 Google Colab。

设置 python 环境的 5 步指南

  • 我们需要建立我们的环境。我推荐下载 Anaconda Navigator ,它有一个简单的 GUI。
  • 下载并安装后,创建一个环境(左边的第二个选项卡> create 按钮),它允许您指定 python 版本(最新版本即可)。当绿色箭头出现在它旁边时,您就知道它被选中了。

anaconda GUI——通过具有不同库/ Python 版本的环境管理独立项目

  • 创建后,您可以链接所需的库,而不会发生冲突。非常得心应手!为此,只需在已安装的软件包中搜索软件包(如 NumPy,Matplotlib),如果没有弹出,则选择未安装,选中它们并单击应用安装它们。Numpy 和 Matplotlib 是标准库,将对这个项目和未来的项目有用。

在 conda 库包管理器中搜索库

  • 您已经差不多设置好了,现在回到 Anaconda Home 选项卡,确保您处于正确的环境中(XXX 上的应用程序),然后您可以安装 Spyder 作为 IDE(集成开发环境)来启动您的代码项目。

🤓 : Spyder 对于任何初学 python 编码的业余爱好者来说都是最好的工具之一。另一个很棒的工具是 Jupyter,它可以很好地将交互式代码呈现给高层管理人员,以实现更好的可视化。我们将在以后的文章中探讨这一点以及 Google Colab。

  • 一旦安装进度条完成,你就准备好了!您现在可以启动 Spyder 了。虽然 GUI 可能允许几种可能性,但要直接获得结果,首先要编写您的脚本(1),执行您的脚本(2)并在控制台中探索并与结果交互(3)。

Spyder Python 界面的 3 个基本元素

下载您的点云数据集

对于未来的实验,我们将使用一个采样点云,您可以从这个库免费下载。如果想在不安装任何东西的情况下提前可视化,可以查看 webGL 版本

哥本哈根 Glyptotek 博物馆雕像的 3D 点云,用摄影测量法重建。数据集的获取扩展了本文的范围,将在另一篇文章中讨论。如果你想学习这些技术,你可以参观地理数据学院三维重建器

[## 点云处理在线课程- 3D 地理数据学院

编队学习先进的点云处理和三维自动化。开发新的 python 地理数据技能和开源…

learngeodata.eu](https://learngeodata.eu/point-cloud-processor-formation/)

你的第一行代码

在 Sypder 中,让我们从使用一个非常强大的库开始:NumPy。您已经可以在脚本区域(左侧窗口)编写您的第一个短代码了:

1 import numpy as np
2 file_data_path=”E:\sample.xyz”
3 point_cloud= np.loadtxt(file_data_path, skiprows=1, max_rows=1000000)

此短代码(1)导入库 NumPy 以进一步用作短名称“NP”;(2)创建保存指向包含点的文件的字符串的变量;(3)将点云作为名为 point_cloud 的变量导入,跳过第一行(例如,保存点数),并设置运行测试的最大行数,而不会出现内存短缺。

现在,您可以运行您的脚本(绿色箭头),并在弹出窗口出现时将其保存为硬盘上的. py 文件。现在,您可以通过直接在控制台中写入以下内容来访问保存数据的实体的第一个点(point_cloud):

In: point_cloud[0]

然后,您将获得一个包含第一个点的内容的数组,在本例中是 X、Y 和 Z 坐标。

Out: array([0.480, 1.636, 1.085])

这是你使用 python 和点云的第一步。现在您已经知道如何加载点数据,让我们来看看一些有趣的过程。

提取所需的属性

我们有一个具有 6 个属性的点云:X,Y,Z,R,G,b。需要注意的是,当使用 NumPy 数组时,索引总是从 0 开始。因此,如果加载时没有字段名,那么获取第二个点就完成了:

In: point_cloud[1]
Out: array([0.480, 1.636, 1.085, 25, 44, 68])

如果我们想从那里获得红色(R)属性(NumPy“列”的索引是 3),我们可以这样做:

In: point_cloud[1][3]
Out: 25

如果我们想要提取点云中所有点的 Z 属性:

In: point_cloud[:,1]
Out: array([2.703, 2.716, 2.712, …, 2.759, 2.741, 2.767])

如果我们只想提取所有点的 X,Y,Z 属性:

In: point_cloud[:,:3]
Out: array([[4.933, 2.703, 2.194],
[4.908, 2.716, 2.178],
[4.92 , 2.712, 2.175],
…,
[5.203, 2.759, 0.335],
[5.211, 2.741, 0.399],
[5.191, 2.767, 0.279]])

恭喜你,你刚刚玩了多维索引👏。注意,在上面的例子:3]中,第三列(R)被排除在选择之外。如果需要多次使用,每个结果都可以存储在变量中:

xyz=point_cloud[:,:3]
rgb=point_cloud[:,3:]

基于属性的数据分析

现在让我们来看一些有用的分析。如果你想知道你的点云的平均高度,你可以很容易地做到:

In: np.mean(point_cloud,axis=0)[2]
Out: 2.6785763

💡 提示: 这里将轴设置为 0 是要求独立查看每个“列”。如果忽略,则对所有值求平均值,如果设置为 1,将对每行求平均值。

如果现在您想要提取距离平均高度 1 米以内的点(我们假设该值存储在 mean_Z 中):

In: point_cloud[abs( point_cloud[:,2]-mean_Z)<1]
Out: array([…])

💡 提示: 在 python 中,以及一般的编程中,解决一个问题的方法不止一种。提供的是一种非常简短有效的方法,可能不是最直观的。尝试使用 for 循环来解决它是一个很好的练习。这样做的目的是在清晰度和效率之间取得良好的平衡,参见 PEP-8 指南

现在您知道如何设置您的环境,使用 Python、Spyder GUI 和 NumPy 进行编码工作。你可以加载点云并玩属性,但你可以尝试其他场景,如颜色过滤,点邻近…

基本 3D 可视化

对于这第一次 3D 点云绘图体验,我们将得到一个基本的库:Matplotlib。首先,我们将在初始脚本的导入部分添加包,以允许我们使用它们:

1 import numpy as np
2 import matplotlib.pyplot as plt
3 from mpl_toolkits import mplot3d

导入的依赖项的作用如下:

  • 你已经知道这个了😉
  • matplotlib.pyplot处理绘图
  • mpl_toolkits.mplot3d允许创建 3d 投影

从这里,我们可以添加之前探索过的代码:

4 file_data_path=”E:\sample.xyz”
5 point_cloud= np.loadtxt(file_data_path,skiprows=1)
6 mean_Z=np.mean(point_cloud,axis=0)[2]
7 spatial_query=point_cloud[abs( point_cloud[:,2]-mean_Z)<1]
8 xyz=spatial_query[:,:3]
9 rgb=spatial_query[:,3:]

🤓 注意 :我们通常会在编码时尝试增强可读性,如果你想要更好的指导方针,那么PEP-8是一个很好的资源。理想情况下,变量名应该小写,单词之间用下划线隔开。

你得到的是 4 个变量(xyz 和 rgb),基于空间查询过滤初始点云。现在让我们深入了解如何简单地绘制结果。首先,让我们创建一个 3d 轴对象。为此,我们将projection='3d'传递给plt.axes,后者返回一个Axes3DSubplot对象。

10 ax = plt.axes(projection='3d')

这是我们将要绘画的空白画布。然后我们将绘制一个散点图,我们想给每个点分配一种颜色。这可能是一个非常繁琐的过程,下面的代码行简化了这一过程

11 ax.scatter(xyz[:,0], xyz[:,1], xyz[:,2], c = rgb/255, s=0.01)

如你所见,我们通过xyz[:,0]xyz[:,1]xyz[:,2]将坐标 x、y、z 传递给函数;通过标准化到[0:1]间隔除以最大值,每个点的颜色通过c = rgb/255;通过s = 0.01显示屏幕上各点的尺寸。

最后,我们可以使用下面的命令绘制图表,并欣赏可视化效果:

12 plt.show()

💡 提示 :在 Spyder 4.1.2 及以上版本中,您可以在变量资源管理器窗口的 graph 选项卡中访问您的绘图,默认情况下会创建图像。如果你想创建一个交互式可视化,在启动脚本之前,键入 %matplotlib auto 切换到“自动”(即交互式)绘图。

完整的脚本可以在这里获得,并且可以在 Google Colab 上远程执行。

结论

您刚刚学习了如何导入、处理和可视化由数百万个点组成的点云,只需 12 行代码!干得好的👏。但这条路并没有到此为止,未来的帖子将深入点云空间分析、文件格式、数据结构、可视化、动画和网格划分。我们将特别关注如何管理大点云数据,如下面的文章中所定义的。

[## 三维点云的未来:一个新的视角

被称为点云的离散空间数据集通常为决策应用奠定基础。但是他们能不能…

towardsdatascience.com](/the-future-of-3d-point-clouds-a-new-perspective-125b35b558b9)

我的贡献旨在浓缩可操作的信息,以便您可以从零开始为您的项目构建 3D 自动化系统。你可以从今天开始在地理数据学院参加一个编队

[## 点云处理在线课程- 3D 地理数据学院

编队学习先进的点云处理和三维自动化。开发新的 python 地理数据技能和开源…

learngeodata.eu](https://learngeodata.eu/point-cloud-processor-formation/)

3 向数据科学文章推荐

如果你有时间,我认为这些额外的资源值得一读,如果你曾经分别被 NumPy、Matplotlib 或者如果你在 Mac 上编码所困扰的话。

[## NumPy 初学者终极指南

开始使用 NumPy 需要知道的一切

towardsdatascience.com](/the-ultimate-beginners-guide-to-numpy-f5a2f99aef54) [## 为赶时间的人准备的 Matplotlib 指南

Matplotlib 是一个常用的绘图库,但有时很难绘制自定义的绘图。这篇文章是一篇…

towardsdatascience.com](/all-your-matplotlib-questions-answered-420dd95cb4ff) [## 如何在 Mac 上成功安装 Anaconda(并让它实际工作)

正确安装 Anaconda 和修复可怕的“找不到 conda 命令”错误的快速而简单的指南

towardsdatascience.com](/how-to-successfully-install-anaconda-on-a-mac-and-actually-get-it-to-work-53ce18025f97)

利用 RoBERTa 模型发现 Reddit 子群的情感

原文:https://towardsdatascience.com/discover-the-sentiment-of-reddit-subgroup-using-roberta-model-10ab9a8271b8?source=collection_archive---------27-----------------------

我们将使用预训练的 RoBERTa 模型来建立情感分析模型,以发现 Reddit 子群的情感。

被困在付费墙后面?点击这里阅读这篇文章和我的朋友链接。

资料来源:jeffrey grospe(联合国统计司)

当你登录社交媒体账号,阅读开篇帖子时,你有什么感受?它是让你微笑还是让你悲伤或生气?我有一个复杂的经历。大多数时候,社交媒体上的帖子让我很开心。怎么会?好吧,我们不能控制其他人发布什么,但我们可以控制我们想在自己的社交媒体账户上看到什么。

如果你加入了一个负面评价很高的群体,那么你会更频繁地阅读那些评论。那会让你生气和难过。在它对你精神健康造成损害之前,离开那些有毒的群体。

所以如果我让你找出你社交媒体账户的有毒群体,你能做到吗?

这篇文章将帮助你建立一个模型,帮助你总结所有帖子或评论的观点。所以你可以在他们让你觉得想退出社交媒体之前离开那些群。

我们将在本文中使用 Reddit 社交媒体参考。我将分析我的 Reddit 子群。并且检查这些子群是否具有高数量的负面评论。

为什么是 Reddit?

像 Reddit 和 twitter 这样的社交媒体将允许你通过 API 访问用户的帖子和评论。您可以在 Reddit 数据上测试和实现情感分析模型。

本文分为两部分。在第一部分中,我将建立一个 RoBERTa 模型。在第二部分,我们分析了 Reddit 子群的情感。

罗伯塔模型的建立

我们将使用 twitter 数据集对预训练模型 RoBERTa 进行训练和微调。你可以在这里找到数据。该数据集包含积极和消极情绪的推文。我选择了二元情感数据来提高准确性。二元预测很容易解读。此外,它使决策过程变得容易。

Huggingface 团队变形金刚库将帮助我们访问预训练的罗伯塔模型。RoBERTa 模型在 NLP 基准,通用语言理解评估(GLUE)上表现非常好。RoBERTa 模型的性能与人类水平的性能相匹配。点击了解更多关于罗伯塔的信息。点击了解更多关于变形金刚库的信息。

现在,让我们依次检查代码的不同部分。

第一部分。配置和令牌化

预训练模型具有包含信息片段的配置文件,例如层数和注意力头的数量。RoBERTa 模型配置文件的细节如下所述。

{
    “architectures”: [
    “RobertaForMaskedLM”
    ],
    “attention_probs_dropout_prob”: 0.1,
    “bos_token_id”: 0,
    “eos_token_id”: 2,
    “hidden_act”: “gelu”,
    “hidden_dropout_prob”: 0.1,
    “hidden_size”: 768,
    “initializer_range”: 0.02,
    “intermediate_size”: 3072,
    “layer_norm_eps”: 1e-05,
    “max_position_embeddings”: 514,
    “model_type”: “roberta”,
    “num_attention_heads”: 12,
    “num_hidden_layers”: 12,
    “pad_token_id”: 1,
    “type_vocab_size”: 1,
    “vocab_size”: 50265
}

标记化意味着将 python 字符串或句子转换成整数的数组或张量,这是模型词汇表中的索引。每个模型都有自己的标记器。此外,它还有助于为模型准备数据。

from transformers import RobertaTokenizer
roberta_tokenizer = RobertaTokenizer.from_pretrained(“roberta-base”)

注意:代码的最终版本可以在本文末尾找到。

第二部分。数据预处理

在本节中,我们使用分词器对句子或输入数据进行分词。这种模式需要在序列的开头和结尾添加标记,如[SEP]、[CLS]或或

def convert_example_to_feature(review):
  return roberta_tokenizer.encode_plus(review,
                                       add_special_tokens=True,
                                       max_length=max_length,
                                       pad_to_max_length=True,
                                       return_attention_mask=True,
  )def encode_examples(ds, limit=-1):
     # prepare list, so that we can build up final TensorFlow dataset from slices.
  input_ids_list = []
  attention_mask_list = []
  label_list = []
  if (limit > 0):
    ds = ds.take(limit)
  for review, label in tfds.as_numpy(ds):
    bert_input = convert_example_to_feature(review.decode())
    input_ids_list.append(bert_input[‘input_ids’])
    attention_mask_list.append(bert_input[‘attention_mask’])
    label_list.append([label])
  return tf.data.Dataset.from_tensor_slices((input_ids_list,
                                             attention_mask_list,
                              label_list)).map(map_example_to_dict)

max_length:这个变量表示句子允许的最大长度。此变量的最大值不应超过 512。

pad_to_max_length:如果为真,标记器在句尾添加[PAD]。

罗伯塔模型需要 3 个输入。

1.input_ids:数据点的序列或索引。

2.attention_mask:它将原词与特殊记号或填充词区分开来。

3.标签:带标签的数据

第三部分。模型训练和微调

Transformers 库在一行代码中加载预训练的 RoBERTa 模型。权重被下载并缓存在本地机器上。我们根据 NLP 任务微调这些模型。

from transformers import TFRobertaForSequenceClassificationmodel = TFRobertaForSequenceClassification.from_pretrained(“roberta-base”)
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate, epsilon=1e-08)
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
metric = tf.keras.metrics.SparseCategoricalAccuracy(‘accuracy’)
model.compile(optimizer=optimizer, loss=loss, metrics=[metric])
model.fit(ds_train_encoded, 
          epochs=number_of_epochs, 
          validation_data=ds_test_encoded, 
          callbacks=[metrics])

使用以下指针微调模型。

1.1e-05 到 1e-06 之间的 learning_rate 变量的值给出了良好的准确度分数。

2.批量的增加提高了准确性,也增加了训练时间。

3.预训练模型不需要在更多的时期上训练。3 到 10 之间的纪元就可以了。

第四部分。准确性、F1 得分并保存模型

准确性分数有助于您检测模型中的偏差和差异。模型的改进主要取决于准确性得分。在平衡数据中使用准确度分数,在不平衡数据中使用 F1 分数。F1 分数告诉我们模型是否平等地学习了所有数据。我们将使用 Keras 回调函数来计算模型的 F1 分数。

class ModelMetrics(tf.keras.callbacks.Callback): def on_train_begin(self, logs={}):
    self.count_n = 1 def on_epoch_end(self, batch, logs={}):
    os.mkdir(‘/create/folder/’ + str(self.count_n))
    self.model.save_pretrained(‘/folder/to/save/model/’ + str(self.count_n))
    y_val_pred = tf.nn.softmax(self.model.predict(ds_test_encoded))
    y_pred_argmax = tf.math.argmax(y_val_pred, axis=1)
    testing_copy = testing_sentences.copy()
    testing_copy[‘predicted’] = y_pred_argmax
    f1_s = f1_score(testing_sentences[‘label’],
                    testing_copy[‘predicted’])
    print(‘\n f1 score is :’, f1_s)
    self.count_n += 1metrics = ModelMetrics()

我们将使用 save_pretrained 方法来保存模型。您可以保存每个历元的模型。我们将保留具有高准确度的模型,并删除其余的。

分析 Reddit 子群的情绪

一旦你完成了 RoBERTa 模型的构建,我们将检测 Reddit 子群的情绪。这些是你完成任务的步骤。

1\. Fetch the comment of the Reddit subgroup. Learn more about how to fetch comments from Reddit here.2\. Check the sentiment of each comment using your RoBERTa model.3\. Count the positive and negative comments of the Reddit subgroup.4\. Repeat the process for different Reddit subgroup.

你可以在这里找到第 1 步和第 2 步的详细解释。我选择了我最喜欢的五个子网格进行分析。我们分析排名前 10 的每周帖子的评论。由于 Reddit API 请求的限制,我限制了评论。

正面和负面评论的数量会给你 Reddit 子群的整体情绪。我已经在代码中实现了这些步骤。你可以在本文末尾找到这段代码。

我最喜欢的 5 个 Reddit 子群的情感分析图。

Reddit 子群被他们的版主高度监管。如果你的评论违反了任何子编辑规则,那么 reddit 机器人将删除你的评论。Reddit 机器人不会根据他们的情绪删除评论。但你可以说,大多数负面评论都打破了 subreddit 规则。

结论

在这篇文章中,你可以学习如何发现社交媒体平台 Reddit 的情绪。本文还介绍了情感分析任务的 RoBERTa 模型的构建。在预先训练好的模型的帮助下,我们可以解决很多 NLP 问题。

NLP 领域中的模型正在变得成熟和强大。Huggingface 变形金刚库使得访问这些模型变得非常容易。用不同的配置和任务尝试这些模型。

作者其他文章

  1. EDA 的第一步:描述性统计分析
  2. 路透社文章的文本数据分析和可视化
  3. 为 Reddit Post: TextBlob 和 VADER 自动化情感分析流程

构建 RoBERTa 分类模型的代码

分析 Reddit 子组的代码

发现你下一个最喜欢的餐馆 Yelp 数据集上的探索和可视化

原文:https://towardsdatascience.com/discover-your-next-favorite-restaurant-exploration-and-visualization-on-yelps-dataset-157d9799123c?source=collection_archive---------30-----------------------

我为餐馆 X 生成的单词云(你会在这篇文章的结尾找到 X)

你这辈子第一次来到这个城市。

午餐时间到了。你想找一家好的当地餐馆,但不幸的是,你不知道该地区有谁可以推荐你。你现在做什么?

如果你选择继续 Yelp 寻找答案,你不是唯一的一个。

乔纳斯·洛伊普Unsplash 上拍摄的照片

作为一个发布关于企业的众包评论的平台,Yelp 可以帮助用餐者进行决策,提供有价值的功能,如餐厅评级、提示和其他用餐者提供的评论。截至 2020 年 3 月,Yelp [1]上有2.11 亿条累积评论。

有了这些海量数据,Yelp 还发布了他们的业务、评论和用户数据的子集,用于教育和学术目的[2]。它还举办了“Yelp 数据集挑战”,为学生提供了一个通过挖掘这些数据进行研究和分析的机会(查看过去几轮获奖者及其论文此处)。

现在我确信你对这个数据集中有什么和我们能发现什么很好奇。事不宜迟,让我们开始探索之旅吧!

古玩目录

  1. 业务数据中有什么?
  2. 餐厅位于哪里?
  3. 哪些餐厅出现率高?他们的评分有可比性吗?
  4. 我们能在餐厅属性中找到什么?
  5. 如何根据我们的需求找到顶级餐厅?
  6. 我们下一步能做什么?

数据集

在这篇文章中,我将通过 Kaggle 探索 Yelp 数据集(此处链接到数据集)。请注意,数据集是在 2020 年 3 月用更新的数据更新的,我的分析是在更新的数据集(截至 2020 年 7 月)上进行的。

该数据集包含 6 个 JSON 文件:

business.json —位置、评级(星级)和属性等业务数据。
review.json —用户评论包括用户 id、商家 id 等。
user.json —用户数据,比如写的评论数、朋友和一些元数据。
checkin.json —签入数据,包括签入 id 和业务 id。
tip.json —用户写的提示,是比较短的评论(一开始我以为这是客户服务后留下的“提示”。对我们的另一个提醒是确保在分析前充分理解数据。
photo.json —包含标题和标签的照片数据。

有关这些文件和数据格式的完整文档,请查看此链接

值得注意的是,这个数据集是 Yelp 拥有的所有数据的一个子集,所以我们在做出任何大胆的结论之前需要注意。这篇文章使用了 business.jsontip.json 文件,我将在以后的文章中使用其他一些文件( review.json 等)。).

要查看我的完整 Python 代码,请查看我的 Kaggle 内核或我的 Github 页面。这篇文章旨在展示分析结果和发现,而不是详细检查代码。

窥视业务数据

因为每个文件都是 JSON 格式的,所以我们需要先把它加载到 DataFrame 中。然后,让我们看看业务数据的维度:

(209393, 14)

有 209393 个业务,14 个属性。

让我们看看有哪些不同的属性:

Index(['business_id', 'name', 'address', 'city', 'state', 'postal_code', 'latitude', 'longitude', 'stars', 'review_count', 'is_open', 'attributes', 'categories', 'hours'], dtype='object')

由于我们最感兴趣的是餐馆,我们需要使用类别列对其进行子集划分。让我们检查一下这一栏中是否有任何缺失的数据:

0.002502471429321897

在该数据集中的所有企业中,只有大约 0.25%的企业缺少类别数据。我们可以在分析中安全地排除这些。之后,让我们再次检查它的形状:

(208869, 14)

Yelp 上有超过 1200 种不同的商业类别。哪个类别的企业数量最多?我们的直觉可能是餐馆。让我们通过条形图快速确认一下:

事实上,“餐馆”拥有最多的业务。请注意,每个企业最多可以分为三类[3]。

现在,我们可以将数据集仅划分为餐馆。一个很自然的问题是这些餐馆在哪里?

地理可视化

由于我们事先不知道这些选定餐馆的位置,我们先把它们标在世界地图上:

似乎 Yelp 数据集中的所有餐馆都在北美地区。让我们放大一下:

所有餐厅都位于美国或加拿大。

让我们找出在这个数据集中餐馆数量最多的城市:

多伦多和拉斯维加斯是餐馆数量最多的两个城市。让我们来看看这两个城市中这些餐厅的位置:

该地区越明亮,该地区的餐馆就越多。

出现率高的餐厅&评级对比

在该数据集中的所有餐馆中,哪些餐馆比其他餐馆出现的频率高?我们的直觉可能是快餐连锁店。

我们的直觉是正确的。注意“地铁餐厅”是“地铁”被命名为两个不同的餐厅,但它们应该是相同的。让我们来解决它:

赛百味和麦当劳在前 2 名。注意,总部位于多伦多的 Tim Hortons 也名列前茅。

由于每个单独的餐厅分店都有一个“星级”栏(评分在 1.0 到 5.0 之间,增量为 0.5),我们可以通过并排的方框图来比较这些受欢迎的连锁餐厅分店的评分:

麦当劳 vs 汉堡王

有趣的是,他们的评分似乎一点区别都没有。他们的评级中值是 2.0,利差大致相同。中间的 50%(第 25 百分位—第 75 百分位)分支有 1.5 星— 2.5 星。

必胜客 vs 达美乐比萨

两相比较,达美乐比萨的评分略高于必胜客。中间 50%的分店的必胜客星级为 1.5-2.5,达美乐比萨星级为 2.0-3.0。

赛百味 vs 吉米·约翰

与之前的对比相似,吉米约翰的评分比赛百味略高。位于中间 50%的分店,赛百味为 2.0-3.0 星,吉米约翰为 2.5-3.5 星。

塔可钟 vs 墨西哥烤肉店

最后一对比较显示了有趣的结果。与 Chipotle 相比,Taco Bell 的价差更大,IQR 更高(Q3-Q1)。这可能部分是因为 Taco Bell 在这个数据集中比 Chipotle 多了 100 多家餐厅。此外,也可能是 Chipotle 的食品质量和服务更加均衡。请注意,我们看不到 Chipotle 的中线,因为它与其 Q1 或 Q3 重叠。让我们检查它的中值来确认:

3.0

好吧,和 Q3 重叠了。这种重叠的原因是因为 Chipotle 有大量的“3 星”。

通过这些比较,我们可以看到,对于同一家餐厅,不同分店的评分可能会有很大差异(Chipotle 可能是个例外),而且在我们观察的每个比较对中,评分都没有明显的优势。

评论数量和评级之间的关系

除了“stars”属性,还有一个“review_count”属性,表示 Yelp 用户提交的评论数量。让我们创建一个散点图来直观显示评论数和评分之间的关系:

我们可以看到,随着评分从 1.0 增加到 4.0,评论的数量也趋于增加。然而,随着评分的进一步增加,尤其是从 4.5 增加到 5.0,评论的数量会减少。

深入了解餐厅属性

该业务数据集中的一列是“属性”,表示商业设施。让我们来看看它们:

Index(['RestaurantsAttire', 'RestaurantsTakeOut', 'BusinessAcceptsCreditCards', 'NoiseLevel', 'GoodForKids', 'RestaurantsReservations', 'RestaurantsGoodForGroups', 'BusinessParking', 'RestaurantsPriceRange2', 'HasTV', 'Alcohol', 'BikeParking', 'RestaurantsDelivery', 'ByAppointmentOnly', 'OutdoorSeating', 'RestaurantsTableService', 'DogsAllowed', 'WiFi', 'Caters', 'Ambience', 'GoodForMeal', 'HappyHour', 'WheelchairAccessible', 'BYOB', 'Corkage', 'DietaryRestrictions', 'DriveThru', 'BusinessAcceptsBitcoin', 'Music', 'BestNights', 'GoodForDancing', 'Smoking', 'BYOBCorkage', 'CoatCheck', 'AgesAllowed', 'RestaurantsCounterService', 'Open24Hours', 'AcceptsInsurance', 'HairSpecializesIn'], dtype='object')

比特币餐馆

这些属性中的大多数都非常简单。我发现“BusinessAcceptsBitcoin”属性很有趣,所以让我们看看这个数据集中有多少家餐馆接受比特币:

False    4245
True       74
Name: BusinessAcceptsBitcoin, dtype: int64

在包含这一属性的 4319 家餐厅中,有 74 家接受比特币。

大衣格子与价格区间的关系

我的直觉是,随着价格区间的扩大,提供衣帽寄存服务的餐厅比例也会增加。

价格范围和是否有大衣格子之间的交叉列表(列出的数字是百分比)

请注意,对于价格范围,“1”表示“\(”,“2”表示“\)$”等。查看这里Yelp 美元符号和实际价格范围之间的映射。

我们可以看到,当价格范围在 100 美元到 100 美元之间时,几乎没有一家餐馆提供衣帽寄存服务.然而,当价格范围为$$\(-\)$$,近一半的餐厅提供 coatcheck。

餐厅外卖与评分的关系

我的直觉是,随着餐馆评分的增加(1.0-3.0),提供外卖的餐馆的百分比也应该增加,随着餐馆评分从 3.0 增加到 5.0,该百分比可能会略有下降。

评分和餐厅是否提供送货服务之间的交叉列表

提供外卖服务的餐馆的比例实际上保持不变,与其评级无关。

请注意,在给定的属性和评级/评论数量之间,我们可以研究更多的二元或多元关系!

根据你的需求发现餐馆

假设您想在多伦多找到一家符合以下条件的餐厅:

  1. 评分> 3.5 星
  2. 100 多篇评论
  3. 打开(重要!)
  4. 价格范围为 11-30 美元
  5. 接受外卖服务(#社交距离)
  6. 接受信用卡

假设我们希望看到按评分(第一)和评论数(第二)排序的前 15 家餐馆:

28918                      Ramen Isshin
46423             Fresco's Fish & Chips
49260                           BarChef
37688    Descendant Detroit Style Pizza
4472                         Hodo Kwaja
49245                      Saigon Lotus
27989                           deKEFIR
60497                      Banh Mi Boys
30969           Manpuku Japanese Eatery
16129                       Le Gourmand
2827                White Brick Kitchen
30799                        La Palette
43941           Aoyama Sushi Restaurant
4794                      Mangia & Bevi
4136                          La Cubana

现在你可能在想拉面伊申,以及人们对这家餐厅有什么建议。为此,我们需要查看提示数据。以下是用户在拉面伊申上给出的五个随机提示:

1\. 'Very small place fits about 30 people. They don't quite have a big sign.'
2\. 'Try the soft tofu appetizer! It's quite light but I find it really tasty!'
3\. 'They have a really cool chart in the front of their menu that breaks down each dish!'
4\. 'Order the white sesame ramen! Quite tasty!'
5\. 'By far, the most spacious ramenya in Toronto.'

单词云是一种有趣的方式来可视化文本组中的流行单词/短语。要创建一个,我们需要首先处理原始文本(删除非字母字符/标点符号/停用词等。).点击阅读关于 NLP 文本处理的更多信息。

不太喜欢拉面,你更喜欢“弗雷斯科的鱼和薯条”?当然…这里有一些人们给“弗雷斯科炸鱼薯条”的随机提示:

1\. 'Check in for a free drink!!'
2\. 'Add extra fish for only a few dollars, great for sharing!'
3\. 'Ask to add extra piece of fish to order if you don't want too many chips but want two orders; more fish less chips less money. ;)'
4\. 'Use the free drink checking offer!'
5\. 'Sunday! 3 items for just $5! I like the shrimp!'

我们可以看到“入住免费饮料”和“加额外鱼”在这里都被提到了两次。我知道你现在可能在想什么…你是对的。这篇博文的特色图片确实是“Fresco 的鱼&薯条”。

接下来

Yelp 数据集有如此多的文本信息,我们在这篇文章中没有涉及。 review.json 文件包含的用户评论比一般的提示长。这些数据可用于建立情感分析模型。具体来说,问题如下:有可能预测用户基于他们的文本评论给出的餐馆评级(或总体正面/负面情绪)吗?

点击这里查看我如何使用逻辑回归创建情感分类模型。

总结

我们研究了 Yelp 数据集中的业务数据,并检查了一些快餐连锁店的餐厅评级。然后我们看了看不同的餐馆属性和它们之间的关系。最后,我们通过一个例子来说明如何找到符合我们需求的顶级餐厅,并使用小费数据来创建可视化效果,以帮助我们理解餐厅小费。

我希望你喜欢这篇文章,并请分享你的想法:)

DS/ML 初学者?查看我的另一篇关于如何使用 Iris 数据集构建第一个 Python 分类器的帖子:

** [## 使用 Python Scikit-learn-Iris 数据集探索分类器

如何用 Python 构建第一个分类器的分步指南。

towardsdatascience.com](/exploring-classifiers-with-python-scikit-learn-iris-dataset-2bcb490d2e1b)**

参考

[1]https://www.yelp-ir.com/overview/default.aspx
【2】https://www.yelp.com/dataset
【3】https://blog.yelp.com/2018/01/yelp_category_list

发现 Python3 字符串格式函数的所有魅力

原文:https://towardsdatascience.com/discovering-all-the-charms-in-python3-string-format-function-af07d609c760?source=collection_archive---------32-----------------------

照片由克里斯里德Unsplash 拍摄

一个神奇的函数格式化 Python 字符串,给你更多呈现字符串和数字的想法。

由于 Python3 涉及到了[str.format()](https://docs.python.org/3.8/library/stdtypes.html#str.format)函数,我们有了一种更优雅的方式来定义字符串模板,并将变量值作为参数插入到这个函数中。

本文列出了使用该函数的所有不同方式,并展示了我们可以用它做的事情的潜力。

0x01 按位置传递参数

我们需要使用花括号{}作为占位符,然后将格式函数中的参数传递给字符串模板。有许多使用这种技术的方法,如下所列。

如果我们不指定位置,参数将按默认顺序传递。

>>> '{} {} {}'.format('I', 'love', 'Python')
'I love Python'

我们也可以定义位置,实现同样的事情。

>>> '{0} {1} {2}'.format('I', 'love', 'Python')
'I love Python'

在字符串模板中,我们可以自定义位置。

>>> '{1} {0} {2}'.format('love', 'I', 'Python')
'I love Python'

如果format函数中的参数比字符串模板中定义的多,那么多余的参数将被忽略。

>>> '{0} {1} {2}'.format('I', 'love', 'Python', 'yes')
'I love Python'

我们还可以在字符串模板中的特定位置多次使用该参数。当然,这样使用format功能需要明确指定位置。

>>> '{0} {1} {2}. {3} {1}s {2}, too'.format('I', 'love', 'Python', 'She')
'I love Python. She loves Python, too'

0x02 通过键传递参数

与位置号相比,使用文本定义的键会使代码更具可读性。所以,有时你可能想通过键传递参数。

>>> '{subject} {verb} {object}'.format(subject='I', verb='love', object='Python')
'I love Python'

0x03 传递字典

我们可以直接传入一个字典,而不是在format函数中使用“键=值”对,因为它也是“键:值”格式。

>>> dict = {'subject': 'I', 'verb': 'love', 'object': 'Python'}
>>> '{subject} {verb} {object}'.format(**dict)
'I love Python'

0x04 传递列表

在使用format函数将值传递给字符串模板之前,不必从列表中提取值。

>>> l = ['I', 'love', 'Python']
>>> '{0} {1} {2}'.format(*l)
'I love Python'

还可以通过下标访问列表中的元素。

>>> l = ['I', 'love', 'Python']
>>> '{0[0]} {0[1]} {0[2]}'.format(l)
'I love Python'

高级用法:格式化符号

在花括号{}中,我们不仅可以插入位置指示器,还可以插入格式化符号。下表显示了可用的格式化符号。

上表完全是用format函数打印出来的,源代码如下。

def table():
 print('|| {:=^15} || {:=^50} ||'.format(' symbols ', ' meanings '))
 print('|| {:<15} || {:<50} ||'.format(':', 'format indicator'))
 print('|| {:<15} || {:<50} ||'.format('-/=\\#', 'filling characters'))
 print('|| {:<15} || {:<50} ||'.format('<', 'left aligned'))
 print('|| {:<15} || {:<50} ||'.format('>', 'right aligned'))
 print('|| {:<15} || {:<50} ||'.format('^', 'centre aligned'))
 print('|| {:<15} || {:<50} ||'.format('10 (number)', 'width'))
 print('|| {:<15} || {:<50} ||'.format(',', 'formating number'))
 print('|| {:<15} || {:<50} ||'.format('.', 'decimal precision'))
 print('|| {:<15} || {:<50} ||'.format('f', 'floating number indicator'))
 print('|| {:<15} || {:<50} ||'.format('b', 'binary number'))
 print('|| {:<15} || {:<50} ||'.format('d', 'decimal number'))
 print('|| {:<15} || {:<50} ||'.format('o', 'octal number'))
 print('|| {:<15} || {:<50} ||'.format('x', 'hex number'))
 print('|| {0:=^15} || {0:=^50} ||'.format(''))

这些格式化符号将在下一节解释。

0x05 填充、对齐和宽度

对准宽度必须一起使用。前者必须是<左对齐、>右对齐或^居中对齐,后者必须是整数。

>>> print('|{:<15}|'.format('left'))
|left           |
>>> print('|{:^15}|'.format('centre'))
|    centre     |
>>> print('|{:>15}|'.format('right'))
|          right|

如果提供了填充字符,上面字符串中的所有空格都将被给定的字符替换。

>>> print('|{:=<15}|'.format('left'))
|left===========|
>>> print('|{:*^15}|'.format('centre'))
|****centre*****|
>>> print('|{:$>15}|'.format('right'))
|$$$$$$$$$$right|

请注意,填充字符可以是任何字符,但只能是一个字符。

0x06 十进制精度

format功能还允许我们指定十进制数的精度。

>>> print('{:.3f}'.format(3.1415926))
3.142

我们甚至可以通过计算直接显示百分比

>>> print('{:.2%}'.format(7/8))
87.50%

0x07 数字格式

我发现format 功能的数字格式化功能还是挺有用的。它可以很容易地在正确的位置添加逗号。

>>> print('{:,}'.format(123456789))
123,456,789

此外,我们可以控制正数和负数的输出格式,以便有更好的表示。

始终显示标志:

>>> print('{:+f}\n{:+f}'.format(100, -100))
+100.000000
-100.000000

在正数前加一个空格,使数字从负数的相同位置开始:

>>> print('{: f}\n{: f}'.format(100, -100))
 100.000000
-100.000000

0x08 基数转换

我们还可以使用format函数快速转换数字的基数。

十进制到二进制:

>>> print('{:b}'.format(100))
1100100

二进制到十进制

>>> print('{:d}'.format(0b1100100))
100

十进制到八进制

>>> print('{:o}'.format(100))
144

十进制到十六进制

>>> print('{:x}'.format(100))
64

0x09 直接将日期时间转换为字符串

通常我们需要使用strftime函数将一个datetime对象转换成特定格式的string类型。但是,如果你只想打印datetime,你可以不使用该功能。

>>> from datetime import datetime
>>> print('{:%d-%B,%Y %A %H:%M:%S}'.format(datetime.now()))
11-March,2020 Wednesday 22:17:44

摘要

尼尔斯·斯塔尔在 Unsplash 上拍摄的照片

下面是 Python 中str.format()函数的全部魔力。我希望这能让你在打印或格式化 Python 字符串和数字时有更多的想法。

[## 通过我的推荐链接加入 Medium 克里斯托弗·陶

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@qiuyujx/membership)

如果你觉得我的文章有帮助,请考虑加入 Medium 会员来支持我和成千上万的其他作者!(点击上面的链接)

发现数学思维

原文:https://towardsdatascience.com/discovering-mathematical-mindset-d856ec63c21a?source=collection_archive---------40-----------------------

数据科学是如何帮助我养成数学态度的!

照片由乔恩·泰森Unsplash 拍摄

或者许多人认为,数学灌输了恐惧、仇恨、厌倦或者至少是某种程度的冷漠。这是一个很难理解的话题。人们很容易联想到诗歌、文学、音乐、法律或历史。但是数学在很大程度上被认为是神秘的,没有灵魂的。上面这张照片抓住了这种感觉:“美术比数学重要”。但是我倾向于不同意因为我相信数学也是一门艺术

这篇文章试图从新的角度来看待数学。

让我们从最基本的开始。

什么是数学?

照片由 Antoine DautryUnsplash 上拍摄

有人会说数学是对数字的研究。其他人可能会将其描述为一套用于计数和测量的技术/函数/方程。数学是一门你必须找到解决问题的方法的学科。你被要求解代数方程并计算出 x 的值。你学习处理微分、积分、极限、概率、三角函数、向量微积分等等。我们可以学习如何做到这一点,我们中的许多人甚至在考试中取得好成绩。然而,有很多人对这整个练习感到超脱。做这件事有什么意义?这到底意味着什么?数学方程式或数学公式代表什么?

这些都是重要的问题。要回答这个问题,我们必须深入一点。让我们闭上眼睛, 思考 讲述 学习 ,总的来说。

照片由安特·哈默斯特Unsplash 上拍摄

当你观察到一种现象并认识到一种模式时,学习就发生了。你试图通过发现现象中涉及的实体之间是否有任何关系来理解这种模式。

花点时间。你会意识到,对任何事物的任何一种理解或知识,都不过是对模式的识别和解释。想想农业、季节变化、行星运动、生命进化、化学反应、股票市场分析、心理学研究、临床试验、药物开发…几乎每一个对知识的追求都包括对某种模式的识别。(查看此)

如果你同意……..请继续阅读。

一旦模式被识别出来,它就必须被表达出来。人类使用“语言”、“符号”和“图画”来表达、描述和交流思想和认识。

乔尔·那仁Unsplash 上拍摄的照片

例如,如果我们必须描述“民主”的概念,我们可以用英语来解释。在维基百科上你可以找到如下描述:

D emocracy ( 希腊语 : δημοκρατία, dēmokratiā ,来自dēmos‘人民’和‘统治’)是政府的一种形式,其中人民拥有权力来选择他们的统治立法

如果非要描述如何从一个城市到达另一个城市,可以画一张 地图 来表达。仅使用文本不太适合表达这个想法。

来源:谷歌地图

现在,自然界中有许多模式或想法是抽象的。比方说,你有两个橘子放在一个篮子里,三个香蕉放在另一个篮子里。你总共有多少水果?你可以把 2 加 3 得到 5,也可以把 3 加 2 得到 5。顺序不重要。实际上这是 加法交换律 它可以用英语表达为:

当两个数相加时,数字的顺序不起任何作用。

然而,表达上述想法的更有效的方式是:

P + Q = Q + P

关于数学的伟大思想来了

数学是表达“模式”的语言之一。

对于什么样的模式,应该使用数学?

更正式或更抽象的模式,可以用数学语言高效地表达出来。

例如,如果一个人以某个恒定的速度驾驶一辆汽车,在行驶距离、他的速度和驾驶的总时间之间是否存在关系或模式。

是的,有一种模式可以用英语表达如下:

"司机行驶的距离总是等于他的速度乘以他行驶的时间"。

这种解释是正确的,但如果我们用数学符号和运算来表达它,会更有用,如下所示:

距离=速度*时间

T

数学上,我们可以说,如果速度不变,距离和时间之间存在线性关系。和学校里教的直线方程比较一下。

在我们的例子中,直线穿过原点,因此 b=0。在时间 0,没有距离被覆盖。速度是“斜率”,即我们线的米数)

现在,为什么数学表达式更有用?这是因为它抓住了现象背后的“广义”概念。自然界中任何存在线性关系的现象都可以用直线的形式来表达,我们可以通过遍历这条线来预测未来的值。

[这就是线性回归在很高的水平上]

新的视角

S 顶级思维数学作为一门解决问题的学科。开始把它当成一种语言——一种表达自然界模式的语言。

“数学是上帝书写宇宙的语言”——伽利略·伽利雷

不同类型的模式导致不同的数学分支:

  1. 形状的模式:几何
  2. 运动和变化的模式:微积分
  3. 位置模式:拓扑
  4. 机会模式:概率
  5. 推理模式:逻辑

一旦你开始认为数学是一种模式语言,你就可以开始从不同的思维模式来看待方程。可以把一个等式看作是该模式的表达式。试着想象这个等式想要表达的模式。就当是故事情节,人物之间有关系。

先说一个基本形状:

圆心在原点(0,0)的圆的数学公式为:

现在想想这个等式表达的模式。

“圆上的每一点离圆心的距离都是一样的”。

实际上,这种模式使它成为一个圆。一个形状的每一点到一个固定点的距离都相等,这个形状就是圆。它不能是矩形或三角形或任何其他形状。只有圆圈遵循这种模式。这是知识或学习,上面的等式是想说!有没有其他的方式,可以表达这种模式?

用世界上任意一个圆的直径除以圆周!你将总是得到一个常数' 𝜋'(大约 3.14159)

这是描述圆背后图案的另一种方式。任何数学公式中只要有一个圆圈,就会有 𝜋.

圆的面积,球体的体积,圆柱体的体积:

当然,你可以推导出这些公式的证明,但我试图传达的基本思想是,它们背后有一个共同的模式——“圆形”——因此有了 𝜋

让我们更进一步。以前,我把圆形(或球形)称为基本形状。如果你认为,这是自然界中最丰富的形状(可能不是完美的圆形或球形,但仍然足够接近),像行星和其他天体的形状,气泡,雨滴,原子,行星的旋转和公转,星系的形状等

自然界中存在一种基本模式吗?我认为有。而这个基本模式就是“重复”。有许多重复的自然现象。例如地球(行星)绕轴旋转、行星绕恒星公转、整个星系公转、电子绕原子核运动、季节变化、波浪起伏、光合作用等生物化学循环等等。(人说历史也是重演的。😎)

你看出其中的联系了吗?

这里的想法是,任何遵循重复模式的现象,都可以被认为是一个圆或圆的一部分。因此在其数学形式的某个地方, 𝜋将找到一个位置。

所以现在,每当你看到一个带有 𝜋的数学公式或方程,就想到一个圆,然后思考这个方程试图表达的现象中是否有任何重复。您将看到一个连接。

一些例子:

单摆的时间周期:

来源:维基百科

有“反复”的议案!

开普勒行星运动第三定律:

它捕捉了行星离太阳的距离和它们的轨道周期之间的关系。(轨道是“松散的”圆形。行星“反复”围绕太阳转)

库仑电力定律:

电荷周围的电场遵循圆形轨迹。

来源:维基百科

…….你可以找到更多。当然,所有这些过程背后的实际数学和物理学比这里定义的要复杂得多。但我所建议的是从一个不同的心态来看待这些方程式或公式。哪里有 𝜋,哪里就会有圆圈(重复)的存在

正如 𝜋 对于循环模式,自然界中还有其他模式可以被概括。例如,任何涉及指数增长或下降的现象几乎总是可以用“e”来表示。想想放射性衰变或人口增长,或新冠肺炎感染人数的增加,或你银行的复利。(亲爱的读者,我将把它留在这里让你思考这些模式)

这与机器学习和数据科学有什么关系?

照片由亨特·哈里特Unsplash 拍摄

数据科学和机器学习是用来模仿人类学习的领域。目标是自动发现数据中“模式”并建立输出变量和输入变量之间的关系。表达这种关系的最好方式是什么?如前所述,数学是表达这种形式关系的最佳语言。

像线性/逻辑回归或神经网络这样的机器学习算法使用线性代数和概率定理。ML 模型的成本优化采用微积分。单词和句子嵌入使用向量演算。所以,下次你实现任何机器学习算法的时候,想想为什么要使用特定的数学方法或函数,你想找出什么模式?

和所有自然循环一样,我将 重复 (双关语):

数学是表达模式的语言。数学方程式或公式只是该模式的模型或解释。

发现新数据

原文:https://towardsdatascience.com/discovering-new-data-9be5ae5787ca?source=collection_archive---------28-----------------------

使用 Explorium 和 Python 通过几个简单的步骤发现数据。

Héizel Vázquez 插图

当你在数据科学领域工作时,最困难的部分之一是发现在试图解决业务问题时使用哪些数据。

请记住,在试图获得数据来解决问题之前,您需要了解业务和项目的背景。我所说的背景是指所有关于公司如何运作其项目、公司如何建立、其竞争对手、有多少部门、他们有不同的目标和目的,以及他们如何衡量成功或失败的细节。

当你拥有了所有这些,你就可以开始考虑获取解决业务问题所需的数据了。在本文中,我不会过多地谈论数据收集,相反,我想讨论并向您展示用新数据丰富您已有数据的过程。

请记住,获取新数据必须以系统的方式进行,而不仅仅是凭空获取数据,我们必须始终如一地做这件事,规划它,创建一个流程来做这件事,这取决于工程、架构师、数据运营以及我将在其他文章中讨论的更多内容。

设置环境

在本文中,我们将使用三种工具:Python、GitHub 和 Explorium。如果你想了解更多关于探索博物馆的信息,请点击这里:

[## 数据在哪里?

或者如何丰富数据集并自动创建新要素。

towardsdatascience.com](/where-is-the-data-798faccb4e29)

让我们从创建一个新的 git repo 开始。我们将在这里存储我们的数据、代码和文档。转到您的终端,创建一个新文件夹并移到那里:

mkdir data_discovery
cd data_discovery

然后初始化 git repo:

git init

现在让我们在 GitHub 上创建一个远程回购:

现在转到您的终端并键入(将 URL 改为您的):

git remote add origin [https://github.com/FavioVazquez/data_discovery.git](https://github.com/FavioVazquez/data_discovery.git)

现在让我们检查一下:

git remote -v

您应该看到(当然是您自己的 URL):

origin [https://github.com/FavioVazquez/data_discovery.git](https://github.com/FavioVazquez/data_discovery.git) (fetch)
origin [https://github.com/FavioVazquez/data_discovery.git](https://github.com/FavioVazquez/data_discovery.git) (push)

现在让我们开始一个自述文件(我正在使用 Vim ):

vim Readme.md

你想在里面写什么都行。

现在让我们将文件添加到 git:

git add .

并创建我们的第一个提交:

git commit -m "Add readme file"

最后,让我们把这个推给我们的 GitHub repo:

git push --set-upstream origin master

此时,您的回购应该如下所示:

寻找数据

我们来找一些数据。我不打算在这里做理解数据、探索数据、建模或类似事情的整个数据科学过程。我只是去找一些有趣的数据给你做个演示。

对于这个例子,我们将从一些企业的 YELP 评论中探索数据。数据最初在 Kaggle 上:

https://www.yelp.com/dataset/challenge

但是我在这里使用的是来自的 CSV 数据集。我使用的 CSV 文件叫做“yelp_reviews_RV_categories.csv”

让我们将它添加到 git 中:

git add .
git commit -m "Add data"
git push

我们将从加载 Pandas 上的数据并执行基本 EDA 开始:

from pandas_profiling import ProfileReport
profile = ProfileReport(df, title="Pandas Profiling Report")
profile

这将为您提供一份全面的数据报告,如下所示:

使用 Explorium 获取更多数据

太好了,现在该去探索博物馆了。要了解更多关于 Explorium 的信息,请点击此处:

[## 增强数据科学平台|探索

自动连接到数以千计的源,并发现 Explorium 正在驱动的精确模型的功能…

hubs.ly](https://hubs.ly/H0r3HMF0)

让我们创建一个新项目:

命名项目后,您应该从本地机器添加数据。我们将添加我们的主数据集。你会看到这样的东西:

您可以在下面的浏览栏中获得关于 Explorium 上的专栏的更多基本信息:

我们拥有的数据包含关于特定企业的信息,比如它的位置(城市、州、邮政编码)、它的名称、类别,以及关于它的评论者的数据。但是我们想知道更多。关于生意的更多信息。我们将首先将所有我们不关心的信息列入黑名单:

  • 用户标识
  • 文本

现在,让我们试着获得更多关于这个数据集的信息。Explorium 现在会要求您预测一些能够运行的东西,我们实际上不想预测任何东西,但让我们把一些东西放进去,这样它就能运行(我们将使用“星星”作为目标):

以星星为目标

当我们单击“播放”时,系统将开始收集有关数据集的信息。我们必须在这里等。在这一点上,系统不仅从外部来源引入新数据,还基于我们现有的列创建新功能。我们现在不使用它,但是在下一篇关于特性工程的文章中,它将是重要的。

几分钟后,我看到了这个屏幕:

这意味着 Explorium 找到了 102 个可以补充我的原始数据的数据集,最终,它从我的数据和外部来源创建/获取了 3791 列。请记住,我们有兴趣找到有关业务的更多信息,所以我将从外部数据集中挑选一些列,并将它们添加到我的原始数据中。

这是丰富数据的实际过程。正如你所看到的,系统可以告诉你前 50 个特征是什么,但是关于什么呢?如果你记得我们试图从其他列中“预测”星星,那么它告诉你的是这 50 或 100 个特征对我们选择的目标有一些预测能力。

您实际上可以获得关于感兴趣的特定列的更多信息。

让我们从最基本的开始。获取企业网站。为此,我将使用数据集:搜索引擎结果:

如果你点击箭头,你将下载它。之后,你可以把它装在熊猫身上:

# search engine
search = pd.read_csv("Search  Engine  Results.csv")

正如您将看到的,我们有许多缺失的数据,但这在您进行数据充实时是正常的。太好了,让我们选择公司的网站:

search[["name", "Company Website"]]

你看到的是给定名称的最有可能是某个特定企业的网页。很酷吧?

各商家周边的暴力犯罪数量呢?为此,我将使用“美国犯罪统计”数据集:

我们将使用不同的方法下载这些数据。我希望直接显示暴力犯罪,因此在我的要素部分,在按犯罪数据集过滤后,我将只选择暴力犯罪:

然后单击下载。让我们在 Python 上看看:

crimes[["name","violent_crime"]].dropna().drop_duplicates()

就像这样,你知道你有多少具体的商店暴力犯罪。创建这个变量的实际过程相当复杂。要了解更多信息,请访问 Explorium 并在选择变量时单击了解更多:

在“要素来源”部分,您可以看到它是如何创建的以及来自哪些数据源:

如你所见,这个过程并不那么简单,但是系统已经在为你做了,这太棒了:)

您可以对系统为您收集或创建的每一个变量都这样做,这样,您就可以完全控制整个过程。

更多数据

如果你记得我们把“星星”列为预测。尽管我们对此不感兴趣,但 Explorium 尽了最大努力来获取预测该列的数据。在以后的文章中,我将使用该工具创建整个项目,这样您就可以看到全貌了。

现在,我们可以选择最好的 50 个特征来预测我们选择的“测试”变量。为此,我们转到引擎选项卡并选择功能:

我们将仅从系统收集和创建的 3791 个变量中获得最佳的 50 个变量。然后我们会像以前一样下载它们。默认情况下,我们下载的数据集名为“all_features.csv”。让我们将它载入我们的 EDA 笔记本:

data = pd.read_csv("all_features.csv")

这些是我们的栏目:

['name',
 'address',
 'latitude',
 'longitude',
 'categories',
 'review_stars',
 'KNeighbors(latitude, longitude)',
 'review_stars.1',
 '"rv" in categories',
 'KNeighbors(Latitude, Longitude)',
 '"world" in Results Snippets',
 '"camping" in Results Snippets',
 '"camping" in Title of the First Result',
 '"camping" in Results Titles',
 '"world rv" in Results Titles',
 '"motorhomes" in Results Snippets',
 '"campers" in Results Snippets',
 '"accessories" in Results Titles',
 '"rated based" in Results Snippets',
 '"parts accessories" in Results Snippets',
 '"5th wheels" in Results Snippets',
 '"sale" in Results Titles',
 '"based" in Results Snippets',
 '"service center" in Title of the First Result',
 '"rvs" in Results Titles',
 '"buy" in Results Snippets',
 '"dealer" in Results Titles',
 '"inventory" in Results Snippets',
 '"travel" in Results Titles',
 'KNeighbors(LAT, LONG)',
 'Number of Related Links',
 'day(Website Creation Date)',
 'month(Website Creation Date)',
 '"service" in Website Description',
 'year(Website Creation Date)',
 'Percentile',
 'Number of Website Subdomains',
 '"rv" in Website Description',
 'MedianLoadTime',
 '"camping" in Website Description',
 '"buy" in Website Description',
 '"rv dealer" in Title',
 '"dealer" in Title',
 'Number of Connections to Youtube',
 '"trailers" in Website Description',
 'month(Domain Update Date)',
 'Domain Creation Date - Domain Update Date',
 'Domain Creation Date - Domain Expiry Date',
 'Stopword Count(Associated Keywords)',
 '"pinterest instagram" in Social Networks',
 'Number of Social Networks',
 '"facebook" in Social Networks',
 'Year of Establishment',
 'AdultContent->False',
 'AdultContent->empty',
 'Results From Facebook->False',
 'Results From Facebook->True',
 'Url Split(Company Website).company_website_10->empty',
 'Url Split(Company Website).company_website_10->utm_campaign=Yext%20Directory%20Listing',
 'Url Split(Company Website).company_website_10->utm_medium=organic',
 'Url Split(Company Website).company_website_10->utm_source=moz',
 '_TARGET_']

如你所见,我们有非常不同的数据,但当然很有趣。我现在不打算做更多的事情,因为我的想法是向您展示如何为您已经拥有的数据获取更多的数据,但同样,我将创建完整的项目,在其中我将遵循整个数据科学过程。

结论

我们可以看到,这个过程非常简单和直观,如果您不需要建模,您可以保留新数据并自己工作。如果您将这种类型的软件与对业务的良好理解和有效的数据科学方法相结合,您将更快地震撼数据科学世界。

请务必查看他们的网页了解更多信息:

[## 增强数据科学平台|探索

自动连接到数以千计的源,并发现 Explorium 正在驱动的精确模型的功能…

hubs.ly](https://hubs.ly/H0r3HMF0)

感谢阅读:)

通过探索性数据分析发现数据中的模式

原文:https://towardsdatascience.com/discovering-patterns-in-data-with-exploratory-data-analysis-fd07ce1c5efa?source=collection_archive---------34-----------------------

使用 Pandas、Seaborn 和 Plotly 对 SAT 和 ACT 数据的可视化探索

探索性数据分析(EDA)由 John W. Tukey 于 1977 年首次描述,指的是探索数据以了解变量之间的关系、检测异常并了解变量是否满足统计推断的假设的过程。EDA 是一个非常强大的工具,可以发现数据中的模式,并促进新的研究问题的发展。在这篇文章中,我将描述我如何使用 EDA 来揭示数据集中的趋势,以及这些趋势如何帮助我制定和回答一个研究问题。

背景

该项目是作为大会数据科学沉浸式训练营的一部分完成的。这个项目的目标是使用 EDA 来揭示 SAT 和 ACT 数据的趋势,以便为数据科学问题提出建议。对于这个项目,我收到了六个数据集,提供了 2017 年、2018 年和 2019 年各州的 SAT 和 ACT 数据。数据集中的变量包括各州的平均分数和平均参与率。为了准备 EDA,我清理了数据,并将所有六个数据集合并到一个 51 行 23 列的 Pandas 数据框架中。数据帧的部分报头如下所示。

显示 2017 年 SAT 和 ACT 数据的标题

探索性数据分析

作为我的 EDA 的一部分,我创建了几个数字来调查 SAT 和 ACT 的参与率趋势。我最感兴趣的是 SAT 和 ACT 参与率如何随时间和地理位置而变化。

为了探索参与率随时间的变化趋势,我使用 Seaborn 和 Matplotlib 为每个测试和年份创建了箱线图。比较参与率的分布让我不仅了解了平均参与率是如何随着时间变化的,还了解了分布的方差是如何变化的。

使用 Matplotlib 和 Seaborn 创建的箱线图。从这个图中,我们可以看到,SAT 参与率中位数从 2017 年的 38%上升到 2019 年的 54%。此外,第三个四分位数(第 75 个百分位数)的参与率从 2017 年的 66%上升到 2019 年的 82%。

从上面的图中,我注意到从 2017 年到 2019 年,SAT 参与人数中位数有增加的趋势。

使用 Matplotlib 和 Seaborn 创建的箱线图。从这个图中,我们可以看到中位数 ACT 参与率从 2017 年的 69%下降到 2019 年的 54%。此外,第一个四分位数(第 25 个百分位数)的参与率从 2017 年的 31%下降到 2019 年的 25%。

上面的方框图开始说明 2017 年至 2019 年 ACT 和 SAT 参与率的全国趋势。越来越多的州开始增加 SAT 的参与率,而 ACT 的参与率则有所下降。

当我研究这些数据时,我还使用 Plotly 创建了各州 SAT 和 ACT 参与率的 choropleth 图。如下所示,这些图表明,2017 年,除密歇根州和印第安纳州外,沿海各州的 SAT 参与率最高,而 ACT 参与率在中部各州占主导地位。

使用 Plotly 创建的 Choropleth 地图。这张地图显示,东部和西部海岸的 SAT 参与率最高。

使用 Plotly 创建的 Choropleth 地图。这张地图显示,在美国中部,青蒿素综合疗法的参与率较高。

考虑到我用箱线图揭示的参与率的时间趋势和用 choropleth 图说明的参与的地理差异,形成了一个研究问题。因为大多数中西部州在基线(2017 年)时表现出较低的 s at 参与率,所以我决定探索中西部在 2017 年至 2019 年期间是否也经历了 SAT 参与率中位数的整体增长。

首先,我创建了一个只包含中西部各州(伊利诺伊州、印第安纳州、爱荷华州、堪萨斯州、密歇根州、明尼苏达州、密苏里州、内布拉斯加州、北达科他州、俄亥俄州、南达科他州和威斯康星州)数据的 Pandas 数据框。从那里,我能够使用 Seaborn 和 Matplotlib 创建一个方框图,以查看 2017 年至 2019 年 SAT 参与的分布情况。如下图所示,虽然参与率的差异从 2017 年到 2019 年有所增加,但 SAT 参与率的中位数在三年期间保持相似。对数据的进一步研究显示,2017 年至 2018 年差异的增加主要是由于伊利诺伊州和俄亥俄州 SAT 参与率的增加。

使用 Plotly 创建的 Choropleth 地图。从这张地图中,我们可以看到,尽管伊利诺伊州和俄亥俄州的 SAT 参与率在 2017 年至 2019 年期间有所增长,但大多数中西部州的 SAT 参与率并没有增长。

结论

这个项目展示了 EDA 在揭示数据趋势方面的能力,这些数据可以在以后用于产生一个研究问题。通过使用 Matplotlib、Seaborn 和 Plotly 创建的简单图,我能够发现从 2017 年到 2019 年 SAT 参与率的增长趋势以及中西部各州在基线(2017 年)对 ACT 的地理偏好。综合来看,这些见解激励我调查 SAT 参与率的上升趋势是否也在中西部各州出现。仅用六个简单的图表(只有两种类型),我就能确定一个研究问题并寻求答案。想象一下用 EDA 你能发现什么样的洞察力!

使用双深度 Q 学习构建改进的末日智能体

原文:https://towardsdatascience.com/discovering-unconventional-strategies-for-doom-using-double-deep-q-learning-609b365781c4?source=collection_archive---------37-----------------------

Pytorch 中强化学习的实现。

简介

O 在过去的几篇文章中,我们已经讨论了 VizDoom 游戏环境中实现了 深度 Q 学习 (DQN)并测试了它的性能。深度 Q-learning 是一种高度灵活且响应迅速的在线学习方法,它利用场景内的快速更新来估计环境中的状态-动作(Q)值,以便最大化回报。Q-learning 可以被认为是一种策略外的 TD 方法,其中该算法旨在选择独立于当前遵循的策略的最高值的状态-动作对,并且已经与 OpenAI Atari 健身房环境的许多原始突破相关联。

我们的香草 DQN 代理的游戏性,训练超过 500 集。

然而,DQN 倾向于乐观地高估 Q 值,特别是在训练的初始阶段,导致次优动作选择的风险,因此收敛较慢。为了理解这个问题,回想一下 Q 学习更新方程,该方程利用当前状态奖励和最高值的状态-值对来估计当前状态的 Q 值,该 Q 值用于训练 DQN。

q-学习更新。

请注意,误差项中存在一个 TD-target,它由当前奖励和最高值的状态-动作对的 Q 值之和组成,与代理的当前策略无关,因此,Q-学习通常被称为非策略 TD 学习。

因此,Q-learning 依赖于为下一个状态选择具有最高值的动作的“远见”。但是我们怎么能确定下一个状态的最佳行动是具有最高 Q 值的行动呢?根据定义,Q 值的准确性取决于我们之前探索过的状态动作。因此,在训练开始时计算的 Q 值的准确性往往是不准确的,因为我们还没有完全探索状态-动作空间。在这种状态下取最大 Q 值可能会选择一个次优的行动,阻碍收敛。由于 TD 目标计算和行动选择是在同一个网络中进行的,这一事实加剧了这一问题,这可能会导致对次优行动选择的强化偏向。

为了解决这个问题,双深度 Q 学习(DDQN)首先由 Van Hasselt 等人引入。a1 通过使用两个网络将动作选择与 Q 值目标计算步骤分离。我们可以通过将 TD 目标的 Q 值部分重写为两个部分来实现这一点,如下所示:

在特定的时间步长和状态,我们使用我们的 DQN 网络为我们的下一个状态选择具有最高 Q 值的动作。然后使用目标网络来计算在下一个状态采取该行动的 Q 值,然后将其与当前状态行动的奖励相结合,以形成 TD 目标。网络定期对称更新。

因此,DDQN 可以减少 Q 值高估,从而确保稳定的学习和更快的收敛。

在我们的上一篇文章中,我们探索了如何通过使用开源的 OpenAI gym 包装库 Vizdoomgym ,将深度 Q-learning 应用于训练代理在经典 FPS 游戏 Doom 中扮演一个基本场景。我们将在那篇文章的基础上修改我们的方法,在 Pytorch 中加入 DDQN 架构。

实施

我们将在与上一篇文章相同的 VizDoomgym 场景中实现我们的方法,保卫防线,使用相同的多目标条件。环境的一些特征包括:

  • 一个 3 的动作空间:开火,左转,右转。不允许扫射。
  • 向玩家发射火球的棕色怪物,命中率为 100%。
  • 试图以之字形靠近来咬玩家的粉红色怪物。
  • 重生的怪物可以承受更多伤害。
  • 杀死一个怪物+1 点。
  • -死了得 1 分。

“防线方案”的初始状态

回想一下,在我们最初的 DQN 实现中,我们已经利用了两个并发网络——一个用于行动选择的评估网络,以及一个定期更新的目标网络,以确保生成的 TD 目标是固定的。我们可以利用这个现有的设置来构建我们的 DDQN 架构,而无需初始化更多的网络。

请注意,由于两个网络定期更新彼此的权重,因此这两个模型仍然是部分耦合的,但重要的是,动作选择和 Q 值评估是由在特定时间步长不共享同一组 a 权重的独立网络完成的。

我们的 Google 协作实现是利用 Pytorch 用 Python 编写的,可以在 GradientCrescent Github 上找到。我们的方法基于泰伯优秀强化学习课程中详述的方法。由于我们的 DDQN 实现类似于我们之前的普通 DQN 实现,所以整个高级工作流是共享的,这里不再重复。

让我们从导入所有必需的包开始,包括 OpenAI 和 Vizdoomgym 环境。我们还将安装火炬视觉所需的 AV 包,我们将使用它进行可视化。请注意,安装完成后必须重新启动运行时。

#Visualization cobe for running within Colab
!sudo apt-get update
!sudo apt-get install build-essential zlib1g-dev libsdl2-dev libjpeg-dev nasm tar libbz2-dev libgtk2.0-dev cmake git libfluidsynth-dev libgme-dev libopenal-dev timidity libwildmidi-dev unzip# Boost libraries
!sudo apt-get install libboost-all-dev# Lua binding dependencies
!apt-get install liblua5.1-dev
!sudo apt-get install cmake libboost-all-dev libgtk2.0-dev libsdl2-dev python-numpy git
!git clone [https://github.com/shakenes/vizdoomgym.git](https://github.com/shakenes/vizdoomgym.git)
!python3 -m pip install -e vizdoomgym/!pip install av

接下来,我们初始化我们的环境场景,检查观察空间和动作空间,并可视化我们的环境..

#Check the environment. You'll need to restart the runtime for it to work
import gym
import vizdoomgym

env = gym.make('VizdoomCorridor-v0')# use like a normal Gym environment
state = env.reset()
state, reward, done, info = env.step(env.action_space.sample())
print(state.shape)
# env.render()
env.close()

接下来,我们将定义预处理包装器。这些类继承自 OpenAI gym 基类,覆盖了它们的方法和变量,以便隐式地提供所有必要的预处理。我们将开始定义一个包装器来重复许多帧的每个动作,并执行元素方式的最大值以增加任何动作的强度。您会注意到一些三级参数,如 fire_firstno _ ops——这些是特定于环境的,在 Vizdoomgym 中对我们没有影响。

class RepeatActionAndMaxFrame(gym.Wrapper):
  #input: environment, repeat
  #init frame buffer as an array of zeros in shape 2 x the obs space
    def __init__(self, env=None, repeat=4, clip_reward=False, no_ops=0,
                 fire_first=False):
        super(RepeatActionAndMaxFrame, self).__init__(env)
        self.repeat = repeat
        self.shape = env.observation_space.low.shape
        self.frame_buffer = np.zeros_like((2, self.shape))
        self.clip_reward = clip_reward
        self.no_ops = no_ops
        self.fire_first = fire_first def step(self, action):
        t_reward = 0.0
        done = False
        for i in range(self.repeat):
            obs, reward, done, info = self.env.step(action)
            if self.clip_reward:
                reward = np.clip(np.array([reward]), -1, 1)[0]
            t_reward += reward
            idx = i % 2
            self.frame_buffer[idx] = obs
            if done:
                break max_frame = np.maximum(self.frame_buffer[0], self.frame_buffer[1])
        return max_frame, t_reward, done, info def reset(self):
        obs = self.env.reset()
        no_ops = np.random.randint(self.no_ops)+1 if self.no_ops > 0    else 0
        for _ in range(no_ops):
            _, _, done, _ = self.env.step(0)
            if done:
                self.env.reset()

        if self.fire_first:
            assert self.env.unwrapped.get_action_meanings()[1] == 'FIRE'
            obs, _, _, _ = self.env.step(1) self.frame_buffer = np.zeros_like((2,self.shape))
        self.frame_buffer[0] = obs return obs

接下来,我们为我们的观察定义预处理函数。我们将使我们的环境对称,将它转换到盒子空间,将通道整数交换到张量的前面,并将其从原始(320,480)分辨率调整到(84,84)区域。我们也将我们的环境灰度化,并通过除以一个常数来归一化整个图像。

class PreprocessFrame(gym.ObservationWrapper):
  #set shape by swapping channels axis
 #set observation space to new shape using gym.spaces.Box (0 to 1.0)
    def __init__(self, shape, env=None):
        super(PreprocessFrame, self).__init__(env)
        self.shape = (shape[2], shape[0], shape[1])
        self.observation_space = gym.spaces.Box(low=0.0, high=1.0,
                                    shape=self.shape, dtype=np.float32) def observation(self, obs):
        new_frame = cv2.cvtColor(obs, cv2.COLOR_RGB2GRAY)
        resized_screen = cv2.resize(new_frame, self.shape[1:],
                                    interpolation=cv2.INTER_AREA)
        new_obs = np.array(resized_screen, dtype=np.uint8).reshape(self.shape)
        new_obs = new_obs / 255.0 return new_obs

接下来,我们创建一个包装器来处理帧堆叠。这里的目标是通过将几个帧堆叠在一起作为单个批次,帮助从堆叠帧中捕捉运动和方向。这样,我们可以捕捉环境中元素的位置、平移、速度和加速度。通过堆叠,我们的输入采用(4,84,84,1)的形状。

class StackFrames(gym.ObservationWrapper):
  #init the new obs space (gym.spaces.Box) low & high bounds as repeat of n_steps. These should have been defined for vizdooom

  #Create a return a stack of observations
    def __init__(self, env, repeat):
        super(StackFrames, self).__init__(env)
        self.observation_space = gym.spaces.Box( env.observation_space.low.repeat(repeat, axis=0),
                              env.observation_space.high.repeat(repeat, axis=0),
                            dtype=np.float32)
        self.stack = collections.deque(maxlen=repeat) def reset(self):
        self.stack.clear()
        observation = self.env.reset()
        for _ in range(self.stack.maxlen):
            self.stack.append(observation) return  np.array(self.stack).reshape(self.observation_space.low.shape) def observation(self, observation):
        self.stack.append(observation) return np.array(self.stack).reshape(self.observation_space.low.shape)

最后,在返回最终环境供使用之前,我们将所有的包装器绑定到一个单独的 make_env() 方法中。

def make_env(env_name, shape=(84,84,1), repeat=4, clip_rewards=False,
             no_ops=0, fire_first=False):
    env = gym.make(env_name)
    env = PreprocessFrame(shape, env)
    env = RepeatActionAndMaxFrame(env, repeat, clip_rewards, no_ops, fire_first)

    env = StackFrames(env, repeat) return env

接下来,让我们定义我们的模型,一个深度 Q 网络。这基本上是一个三层卷积网络,它采用预处理的输入观察值,将生成的展平输出馈送到一个全连接层,生成将游戏空间中的每个动作作为输出的概率。请注意,这里没有激活层,因为激活层的存在会导致二进制输出分布。我们的损失是我们当前状态-动作的估计 Q 值和我们预测的状态-动作值的平方差。我们将使用 RMSProp 优化器来最小化我们在训练期间的损失。

import os
import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as npclass DeepQNetwork(nn.Module):
    def __init__(self, lr, n_actions, name, input_dims, chkpt_dir):
        super(DeepQNetwork, self).__init__()
        self.checkpoint_dir = chkpt_dir
        self.checkpoint_file = os.path.join(self.checkpoint_dir, name) self.conv1 = nn.Conv2d(input_dims[0], 32, 8, stride=4)
        self.conv2 = nn.Conv2d(32, 64, 4, stride=2)
        self.conv3 = nn.Conv2d(64, 64, 3, stride=1) fc_input_dims = self.calculate_conv_output_dims(input_dims) self.fc1 = nn.Linear(fc_input_dims, 512)
        self.fc2 = nn.Linear(512, n_actions) self.optimizer = optim.RMSprop(self.parameters(), lr=lr) self.loss = nn.MSELoss()
        self.device = T.device('cuda:0' if T.cuda.is_available() else 'cpu')
        self.to(self.device) def calculate_conv_output_dims(self, input_dims):
        state = T.zeros(1, *input_dims)
        dims = self.conv1(state)
        dims = self.conv2(dims)
        dims = self.conv3(dims)
        return int(np.prod(dims.size())) def forward(self, state):
        conv1 = F.relu(self.conv1(state))
        conv2 = F.relu(self.conv2(conv1))
        conv3 = F.relu(self.conv3(conv2))
        # conv3 shape is BS x n_filters x H x W
        conv_state = conv3.view(conv3.size()[0], -1)
        # conv_state shape is BS x (n_filters * H * W)
        flat1 = F.relu(self.fc1(conv_state))
        actions = self.fc2(flat1) return actions def save_checkpoint(self):
        print('... saving checkpoint ...')
        T.save(self.state_dict(), self.checkpoint_file) def load_checkpoint(self):
        print('... loading checkpoint ...')
        self.load_state_dict(T.load(self.checkpoint_file))

回想一下,Q-learning 的更新功能需要:

  • 当前状态 s
  • 当前动作
  • 当前动作后的奖励 r
  • 下一个状态s’
  • 下一个动作a’

为了以有意义的数量提供这些参数,我们需要按照一组参数评估我们当前的策略,并将所有变量存储在一个缓冲区中,我们将在训练期间从该缓冲区中提取迷你批次中的数据。因此,我们需要一个重放内存缓冲区来存储和提取观察值。

import numpy as npclass ReplayBuffer(object):
    def __init__(self, max_size, input_shape, n_actions):
        self.mem_size = max_size
        self.mem_cntr = 0
        self.state_memory = np.zeros((self.mem_size, *input_shape),
                                     dtype=np.float32)
        self.new_state_memory = np.zeros((self.mem_size, *input_shape),
                                         dtype=np.float32) self.action_memory = np.zeros(self.mem_size, dtype=np.int64)
        self.reward_memory = np.zeros(self.mem_size, dtype=np.float32)
        self.terminal_memory = np.zeros(self.mem_size, dtype=np.bool)#Identify index and store  the the current SARSA into batch memory
    def store_transition(self, state, action, reward, state_, done):
        index = self.mem_cntr % self.mem_size
        self.state_memory[index] = state
        self.new_state_memory[index] = state_
        self.action_memory[index] = action
        self.reward_memory[index] = reward
        self.terminal_memory[index] = done
        self.mem_cntr += 1def sample_buffer(self, batch_size):
        max_mem = min(self.mem_cntr, self.mem_size)
        batch = np.random.choice(max_mem, batch_size, replace=False)
        #I believe batch here is creating a list limit that is acquired through max_mem, whihch we use to subselect memory
        states = self.state_memory[batch]
        actions = self.action_memory[batch]
        rewards = self.reward_memory[batch]
        states_ = self.new_state_memory[batch]
        terminal = self.terminal_memory[batch] return states, actions, rewards, states_, terminal

接下来,我们将定义我们的代理,它不同于我们普通的 DQN 实现。我们的代理正在使用一个勘探率递减的ε贪婪策略,以便随着时间的推移最大化开发。为了学会预测使我们的累积奖励最大化的状态-行动-值,我们的代理人将使用通过抽样存储的记忆获得的贴现的未来奖励。

您会注意到,作为代理的一部分,我们初始化了 DQN 的两个副本,并使用方法将原始网络的权重参数复制到目标网络中。虽然我们的常规方法利用这种设置来生成固定的 TD 目标,但我们的 DDQN 方法将扩展到这一范围之外:

  • 从重放存储器中检索状态、动作、奖励和下一状态(sar)。
  • 评估网络用于生成当前状态的所有动作的 Q 值。
  • 评估网络用于创建下一状态的 Q 值,最高 Q 值保存为 max_actions。
  • 目标网络还用于创建下一个状态的 Q 值。
  • 当前状态的 TD-target 通过将当前状态中的奖励与通过评估网络识别的 max_actions 从下一个状态的目标网络导出的 Q 值相结合来计算。
  • 通过将 TD 目标与当前状态 Q 值进行比较来计算损失函数,然后将其用于训练网络。
import numpy as np
import torch as T
#from deep_q_network import DeepQNetwork
#from replay_memory import ReplayBufferclass DDQNAgent(object):
    def __init__(self, gamma, epsilon, lr, n_actions, input_dims,
                 mem_size, batch_size, eps_min=0.01, eps_dec=5e-7,
                 replace=1000, algo=None, env_name=None, chkpt_dir='tmp/dqn'):
        self.gamma = gamma
        self.epsilon = epsilon
        self.lr = lr
        self.n_actions = n_actions
        self.input_dims = input_dims
        self.batch_size = batch_size
        self.eps_min = eps_min
        self.eps_dec = eps_dec
        self.replace_target_cnt = replace
        self.algo = algo
        self.env_name = env_name
        self.chkpt_dir = chkpt_dir
        self.action_space = [i for i in range(n_actions)]
        self.learn_step_counter = 0 self.memory = ReplayBuffer(mem_size, input_dims, n_actions) self.q_eval = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_eval',
                                    chkpt_dir=self.chkpt_dir) self.q_next = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_next',
                                    chkpt_dir=self.chkpt_dir) #Epsilon greedy action selection
    def choose_action(self, observation):
        if np.random.random() > self.epsilon:
          # Add dimension to observation to match input_dims x batch_size by placing in list, then converting to tensor
            state = T.tensor([observation],dtype=T.float).to(self.q_eval.device)
            actions = self.q_eval.forward(state)
            #Return tensor, but use item() to reutnr integer
            action = T.argmax(actions).item()
        else:
            action = np.random.choice(self.action_space) return actiondef store_transition(self, state, action, reward, state_, done):
        self.memory.store_transition(state, action, reward, state_, done) def sample_memory(self):
        state, action, reward, new_state, done = \
                                      self.memory.sample_buffer(self.batch_size) states = T.tensor(state).to(self.q_eval.device)
        rewards = T.tensor(reward).to(self.q_eval.device)
        dones = T.tensor(done).to(self.q_eval.device)
        actions = T.tensor(action).to(self.q_eval.device)
        states_ = T.tensor(new_state).to(self.q_eval.device) return states, actions, rewards, states_, dones def replace_target_network(self):
        if self.learn_step_counter % self.replace_target_cnt == 0:
            self.q_next.load_state_dict(self.q_eval.state_dict()) def decrement_epsilon(self):
        self.epsilon = self.epsilon - self.eps_dec \
                           if self.epsilon > self.eps_min else  self.eps_min def save_models(self):
        self.q_eval.save_checkpoint()
        self.q_next.save_checkpoint() def load_models(self):
        self.q_eval.load_checkpoint()
        self.q_next.load_checkpoint()
    #Main DDQN difference here
    def learn(self): #First check if memory is even big enough
        if self.memory.mem_cntr < self.batch_size:
            return self.q_eval.optimizer.zero_grad() #Replace target network if appropriate
        self.replace_target_network() states, actions, rewards, states_, dones = self.sample_memory()
        #Fetch indices for  matrix multiplication for q_pred
        indices = np.arange(self.batch_size) #Calculate the value of the states taken using the eval network
        # We use the indices here to make sure our output q_pred is of shape batch_size instead of batch_size x action_size 
        q_pred = self.q_eval.forward(states)[indices, actions]
        # calculate the state action value of the next state according to target network
        q_next = self.q_next.forward(states_)
        # calculate the state action value of the next state according to eval network
        q_eval = self.q_eval.forward(states_) #Calculate the maximum action value for the new states according to the eval network
        max_actions = T.argmax(q_eval, dim=1)

        #Set q_next to 0 for terminal states
        q_next[dones] = 0.0
        q_target = rewards + self.gamma*q_next[indices, max_actions] loss = self.q_eval.loss(q_target, q_pred).to(self.q_eval.device)
        loss.backward()
        self.q_eval.optimizer.step()
        self.learn_step_counter += 1 self.decrement_epsilon()

定义了所有支持代码后,让我们运行主训练循环。我们已经在最初的总结中定义了大部分,但是让我们为后代回忆一下。

  • 对于训练集的每一步,在使用ε-贪婪策略选择下一个动作之前,我们将输入图像堆栈输入到我们的网络中,以生成可用动作的概率分布
  • 然后,我们将它输入到网络中,获取下一个状态和相应奖励的信息,并将其存储到我们的缓冲区中。我们更新我们的堆栈,并通过一些预定义的步骤重复这一过程。
  • 在一集的结尾,我们将下一个状态输入到我们的网络中,以便获得下一个动作。我们还通过对当前奖励进行贴现来计算下一个奖励。
  • 我们通过 Q 学习更新函数生成我们的目标 y 值,并训练我们的网络。
  • 通过最小化训练损失,我们更新网络权重参数,以便为下一个策略输出改进的状态-动作值。
  • 我们通过跟踪模型的平均得分(在 100 个训练步骤中测量)来评估模型。
env = make_env('VizdoomCorridor-v0')
best_score = -np.inf
load_checkpoint = False
n_games = 5000
agent = DDQNAgent(gamma=0.99, epsilon=1.0, lr=0.0001,input_dims=(env.observation_space.shape),n_actions=env.action_space.n, mem_size=5000, eps_min=0.1,batch_size=32, replace=1000, eps_dec=1e-5,chkpt_dir='/content/', algo='DDQNAgent',env_name='vizdoogym')if load_checkpoint:
  agent.load_models()fname = agent.algo + '_' + agent.env_name + '_lr' + str(agent.lr) +'_'+ str(n_games) + 'games'
figure_file = 'plots/' + fname + '.png'n_steps = 0
scores, eps_history, steps_array = [], [], []for i in range(n_games):
  done = False
  observation = env.reset()score = 0
  while not done:
    action = agent.choose_action(observation)
    observation_, reward, done, info = env.step(action)
    score += rewardif not load_checkpoint:
      agent.store_transition(observation, action,reward, observation_, int(done))
      agent.learn()
    observation = observation_
    n_steps += 1scores.append(score)
  steps_array.append(n_steps)avg_score = np.mean(scores[-100:])if avg_score > best_score:
    best_score = avg_score

    print('Checkpoint saved at episode ', i)
    agent.save_models()print('Episode: ', i,'Score: ', score,' Average score: %.2f' % avg_score, 'Best average: %.2f' % best_score,'Epsilon: %.2f' % agent.epsilon, 'Steps:', n_steps)eps_history.append(agent.epsilon)
if load_checkpoint and n_steps >= 18000:
    break

我们绘制了 500、1000 和 2000 集的代理商平均得分和 epsilon 比率。

500 集后我们经纪人的奖励分配。

1000 集后我们经纪人的奖励分配。

2000 集后我们经纪人的报酬分配。

查看结果并将其与我们的普通 DQN 实现进行比较,您会立即注意到 500、1000 和 2000 集的评分分布有显著改善。此外,您会注意到振荡是如何被显著抑制的,这表明与普通实现相比,收敛性得到了改善。

我们可以想象我们的代理人在 500 集以下的表现。你可以将它与文章顶部的游戏视频进行比较,视频来自我们在相同的剧集持续时间内训练的香草 DQN 实现。

但是在 1000 集以上,我们开始看到有趣的行为——代理人停止与怪物接触,只是不停地转圈。这一点之前在 2017 Spark AI 峰会的 Vizdoom 环境中已经观察到了。

虽然在峰会演示中,他们通过奖励功能工程解决了这个问题,但理解为什么会出现这种情况很重要。因为每一次重生都被证明可以显著增加怪物的生命值,所以有可能代理人发现棕色怪物的火球的高伤害在每一次重生中都更加可靠。然而,考虑到粉色怪物的移动模式有些随机,在多集游戏中依赖它们并不是一个可靠的策略。

解决这个问题要么需要改变环境(用扫射代替转弯可能是一种选择),要么通过奖励工程——例如,根据生存的持续时间增加奖励。

这就结束了双深度 Q 学习的实现。在我们的下一篇文章中,我们将继续用更高级的 Q-learning 方法来检查我们的代理在这些环境中的性能。

我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库。

来源

萨顿等人。al,“强化学习”

塔博尔,“运动中的强化学习”

西蒙尼尼,“深度 Q 学习的改进*

数据分析的发现

原文:https://towardsdatascience.com/discovery-of-data-analysis-66471b5cab91?source=collection_archive---------42-----------------------

数据不仅对企业或机构很重要,对生活的每个领域也很重要,甚至对一个简单的个人也是如此。甚至我们的记忆也是数据。有了数据,我们对过去感到愤怒,对未来充满希望。

穆罕默德·哈桑·pixabay.com

简介

数据和信息是一个组织最具战略性的工具之一。如果一个企业正确理解和处理它的数据,它可以使用它来改进它的流程。数据直接转化为盈利。数据可以用于许多不同的目的;

  • 股票跟踪,
  • 欺诈和诈骗检测,
  • 价格和折扣决策,
  • 购买和品牌忠诚度,
  • 识别和分组用户行为,
  • LFL 金融分析,
  • 趋势分析和预测,

除了将数据转化为有意义的结果之外,数据处理速度提高得越快,企业在决策阶段就越有优势。

Stephen Dawson 在 Unsplash 上拍摄的照片

快速准确地获得数据并将其转化为有意义的结果当然很重要,但仅此还不够。数据应该成为企业内部的一种文化。将数据转化为文化;

  • 要知道如何用数据告诉正确的故事
  • 故事将在哪里或在什么样的环境中讲述,这一点应该很清楚。
  • 故事应该传达给正确的人,并采取行动。
  • 能够采取行动并需要数据的人应该能够容易地访问数据或数据衍生分析,并且总是知道如何访问它。
  • 能够从数据中采取行动的人应该很容易理解这个故事,并利用他们从这个故事中获得的东西来引发行动
  • 创建的动作必须不断测试并且过程必须返回到顶部并注入新的故事。

数据分析概述

数据分析过程基于数据清理、建模和可视化

JESHOOTS.COMUnsplash 上的照片

  • 数据清理:我们在商业生活中很少遇到干净的数据,通常我们需要移除扰乱分析的数据或者以不扰乱完整性的方式填充
  • 数据建模:按统计区间对数据进行公式化、分组或分组通过对研究的原始数据建立数学函数,将数据带到下一个层次。我们利用它让数据更有价值。
  • 数据可视化:干净且有意义的数据仍然可以处于不必要的细分或细节中。分组和汇总这些数据可以让我们更专注于数据的故事。我们需要用图形和颜色来刺激汇总数据,使人脑更容易感知。

有一些基本组件根据它们的级别来分析数据。分析的类型将改变从数据中获得的内容。例如,看到客户希望从你那里得到的过去情况的后果,正在等待你的解释性分析。

描述性分析

以史料为依据,回答了“发生了什么”的问题。KPI 和绩效指标就属于这一类。此外,监控销售、库存和价格的 LFL 趋势分析也是解释性分析的一部分。

M. B. M.Unsplash 上拍摄的照片

诊断分析

再次,基于过去的数据,它是一个回答“为什么”一个事件发生的问题的分析。当我们想要解释在基于过去几个月的数据的描述性分析中看到的急剧的销售下降时,我们使用它。要解释这种下降的原因,我们必须首先确定异常现象。一旦检测到恶意代码,可能还需要从不同来源收集数据。结合数据后,我们需要找到关系或趋势来解释这种异常。在这个阶段,它主要受益于统计数据。

预测分析

对于一个企业来说,知道“未来会发生什么”这个问题的答案至关重要。在即将上映的电影中,马蒂·邓泽旭的行动救了他的命,他知道未来会发生什么。这种未来的信息可以挽救一个企业的生命,或者帮助其竞争对手扩大差异。

詹·西奥多在 Unsplash 上的照片

但是预测通常是困难的。很难将有意义的数据和关系数据结合在一起,并且需要非常专业的知识。数据必须由合格人员解释和批准。相反,统计操纵的预测可能会产生不良后果。

为了进行预测分析,你可能需要输入机器学习方法,并使用高级算法。最常用的有回归、决策树和神经网络

规定性分析

指导性分析与预测性分析交织在一起。或者预测分析的继承者。在相信我们已经找到了问题"会发生什么"的答案后,如果我们能找到问题"我们将如何做"和"我们应该做什么"的答案,它就能告诉我们对未来的一个事件应该提前采取什么行动。

认知分析

认知分析依赖于机器学习、深度学习和人工智能算法。如果情况发生变化,这有助于探索可能出现的情况。事实上,认知分析是一个循环的表达。它通过在旧数据上添加新数据来创建一个持续的学习循环。

数据处理角色

随着数据的增长,它变得更加复杂;获取和呈现数据所需的主要步骤产生了各自不同的专业。根据业务的规模和业务中的数据,有些职位可能是向内的。

https://docs . Microsoft . com/tr-tr/learn/modules/data-analytics-Microsoft/3-roles

商业中最广泛使用的能力分布是业务分析师、数据分析师、数据工程师、数据科学家和数据库经理

  • 业务分析师:数据分析师和业务分析师在很多业务中是同一个人。将业务分析师与数据分析师区分开来的最大特征是其与流程的接近性,并且擅长解释数据。
  • 数据分析师:他经常使用可视化、报告和数据处理工具。他的主要目的是处理、转换,然后汇总和可视化原始数据。在这样做的时候,他根据业务的需要采取行动。他擅长创造故事,是一个目的性很强的大师。大多数时候,他主导业务流程并提供建议。数据分析师的成功取决于他所能创造的洞察力的质量。数据分析师与数据工程师密切合作。他设计收集数据的过程,工程师和分析数据,并不断生产资源养活自己。
  • 数据工程师:他负责从多个不同来源导入、提取、处理、转换和存储数据。为此,他使用内部环境或云技术。与数据库经理的区别在于,他对操作数据流不感兴趣。因为他可能需要不同的专业知识。数据工程师还需要数据的安全性、数据流的管理以及设计警告机制。他们与数据科学家和数据分析师密切合作,在数据采集阶段设计和实施解决方案。
  • 数据科学家:为了获得比数据更高层次的数据,数据科学家还使用统计方法进行高级分析研究。用描述性分析评估数据,而揭示趋势、模式和异常。确定敏感点。他通过以上这些观点产生洞见。他为这些预测的持续生存建立了灵活的模型。在这样做的过程中,他实施了机器学习、深度学习、人工智能等方法,并使用了先进的工具。他用不变的假设来测试它们。数据分析师和数据科学家的任务在许多方面是相同的。在某些业务中,他们可以是同一个人。在一些业务中,根据数据大小和目的,它们需要密切合作,并且是相同的。
  • 数据库经理:实施和管理业务上的数据库服务、云数据平台和混合解决方案。数据库管理员负责保存数据的环境的性能、速度和连续性。数据工程师负责管理用于分析和可预测性的数据和分析环境,这主要取决于数据科学,而数据库经理更负责运营数据和原始数据的流动。当然,就像数据科学家和数据分析师在某些业务中交织在一起一样,数据库经理和数据工程师的任务和职责也可以交织在一起,并且可以是唯一的。

数据分析师任务的详细概述

如果数据分析师正确管理其数据并保持其连续性,对业务数据和数据分析师工作的信心将会增加。如果数据管理不当,建模不当,或者流很快就容易出错,那么除了他对数据的不信任之外,他还会导致企业做出错误的决策,从而导致比预期更多的问题。

穆罕默德·哈桑·pixabay.com

一个数据分析师基本上执行 5 个步骤;

  • 准备:准备数据的过程需要与数据工程师密切合作。增值的分析数据应该从分析数据仓库环境中提供。该数据必须以性能方式进行清理、提取和获取。没有哪个部门会希望在业务中使用整天都不更新的数据。
  • 建模:决定将哪些数据与结果数据组合。通过增加数据的附加值来增加新的项目、类别和计算
  • 可视化:高级数据现在需要进行汇总、着色、分组和交互。可视化不仅仅是用图形来写,还可以用大点来写,用指定的范围来着色和高亮,高亮的数字和文字也是可视化的一部分。
  • 推论:被形象化和概括的数据必须被解释。根据数据中的模式,可能需要对数据进行多样化处理,或者根据不同的故障进行下降。
  • 共享:组织好的数据应该在企业内部容易访问。此外,并非所有数据在企业中都可见。访问组的管理,相似报告的分组,应用程序接口也包含在共享中。

正确处理数据并使其可食用并不像看起来那么简单,这是一顿美味的大餐。如果一个企业想要获得正确的数据,它必须首先获得数据文化,并使其有价值,根据数据行动。如果这种思维模式一开始就不存在,那么无论输出有多好,建立起来的数据步骤和组织都将保持不活动。此外,处于接触数据位置的人应该密切关注不断发展的技术,跟上创新。今天,许多数据处理、数据存储、数据可视化和机器学习环境要么在两三年前不存在,要么不那么先进和用户友好。

资源;

1-https://docs . Microsoft . com/tr-tr/learn/paths/data-analytics-Microsoft/

2-https://eksisozluk . com/data-analyst-1282941

3-https://towardsdatascience.com/tagged/data-analysis

4-https://en.wikipedia.org/wiki/Data_analysis

5-https://data science . Berkeley . edu/about/what-is-data-science/

我的 Linkedin 个人资料;

https://www.linkedin.com/in/onur-okyol-ba253b72/

价值发现

原文:https://towardsdatascience.com/discovery-of-value-8444ffff3c0c?source=collection_archive---------84-----------------------

机遇、挑战和经验教训

Chan2545 在Shutterstock.com拍摄的照片

序言。第四次工业革命已经来临。世界经济论坛将其描述为信息物理系统的出现,涉及人和机器的全新能力,以及技术嵌入我们社会的新方式。数据科学是一个很好的例子,其中全新的社区正在形成,具有非常不同但高效的学习和再学习、创新、信息交流流程,并拥有为有效利用现有和新知识以及创业繁荣提供激励的环境。然而,世界经济论坛抓住了一个重大挑战:

“因此,我们应该记住,所有工业革命最终都是由人们的个人和集体选择推动的。重要的不仅仅是开发底层技术的研究人员、发明家和设计师的选择,更重要的是在日常生活中采用和使用这些技术的投资者、消费者、监管者和公民的选择。”

因此,随着数据科学社区的蓬勃发展,研究人员、发明家和设计师必须意识到共同的挑战,以及将他们有价值的新发现嵌入现有商业领域的机会。更一般地说,这种情况会延伸到其他技术领域,在这些领域,问题往往不在于组织未能理解新技术或新能力,从而导致缺乏资金或支持,而是与文化和创新的一般方法有关。理解这一点是关键。

考虑这个简短的例子。柯达已经在市场上存在了 130 多年,在 2012 年申请破产之前,它被认为是当时最具创新性的公司之一。所有人都知道,他们未能应对从胶片到数码相机的需求转变。但也许不是每个人都知道柯达是第一家在 70 年代末制造出可用数码相机的公司,甚至当苹果在 90 年代发布其第一款数码相机时,它实际上也是由柯达设计的。前副总裁唐·斯特里克兰(Don Strickland)在 90 年代初离开了公司,因为他无法说服公司追求数码相机市场,他说:“我们开发了世界上第一台消费数码相机,但由于担心对电影市场的影响,我们无法获得批准推出或销售它。”正如唐所说,当市场条件被新技术或新能力动摇,企业在捍卫现有地位时,会有一种恐惧。然而,这不是恐惧,而是一系列完全理性的决定,这些决定根植于组织文化和创新的一般方法中。当你读完这篇文章时,回到这一段,并思考一下——柯达就像今天许多其他公司一样,在红海中游泳。数据科学的发现被许多组织接受,就像柯达接受数码相机一样。在本文中,您将了解为什么会发生这种情况,以及如何解决它。

介绍

在的文献中,几乎很难找到关于如何寻找和发现新价值的实用而清晰的指南。在这篇文章的最后,看看我关注的人的名单,他们是世界上在他们的学科中最有影响力的思想家。我已经深入研究了他们的工作,并注意到他们的方法主要是服务于高管或高层管理人员的需求,在这种情况下,学习直接融入到战略中,并清楚地传达了对公司增长的影响。所有的案例研究、理论和观察都是围绕着这种高管文化展开的。这是完全合理的,因为高管设定方向并执行公司增长战略,然后分配资源并批准新项目。然而,一旦项目被批准,或者一个总的方向被设定,更多的决策和优先次序在一个层级中被降低,并且当团队开发技术时几乎每天都是如此。随着我们过渡到知识社会,创新的欲望空前增长,这些技术交付的动力变得更加活跃。为了应对不断增长的创新需求,成熟的组织正在从行动缓慢的层级结构向灵活、更具协作性和敏捷的结构转变。麦肯锡&公司将这种转变定义为从机器般的转变为有机体般的结构,赋予公司在快速变化和模糊的市场环境中更新自我、适应、改变和成功的更大能力。因此,传统上在团队和部门之间划分界限的盒子和线条现在变得不那么重要了,允许跨职能部门进行更少层级的协作,据报道,这将大大提高生产力和创新。因此,在这个动态的环境中,自主性在不断增加,这也是公司实现创新目标的有效性的主要定义。换句话说,更好的效率允许执行更高要求的目标,并允许组织追求更高要求的增长战略。因此,有两个因素有助于组织释放新价值的能力——高管创造并交付的成功战略,以及员工对该战略的交付。虽然我看到的所有实用指南都是针对前者的,但后者往往没有得到应有的重视。想一想亨利·福特(Henry Ford)对他的行业的名言:“如果我问顾客想要什么,他们会说是一匹更快的马”。尽管他的问题是在高管层面发掘新的价值,但同样的推理模式也可以应用于组织层级中其他层面出现的其他问题。虽然当前用于发现价值最大化和风险最小化的解决方案以解决我们在创新中的日常问题的通用方法很麻烦,并且通常缺乏清晰的定义和系统的程序,

我的问题是:如何将世界上最成功的高管发现和执行新市场的战略制定成一个系统框架,使其也适用于层级较低的其他每个人,这些人肩负着实现高管设定的创新目标的重要责任?

在这种情况下,这是一个更广泛的理念——不仅是发现新市场和创造能够带来长期盈利增长的战略,而且更广泛地说,我称之为发现价值,能够更有效地实现创新目标,推动组织对可能性的理解。

我的观点是,不仅是高管,任何级别、任何角色的其他创新人员都应该意识到发现价值和执行战略背后的机遇、挑战和斗争。有趣的是,在创建最成功的公司的过程中,我们可以从世界顶级思想家和企业家身上学到很多东西。无论你是一名渴望开始一个有意义的副业项目的大学生,一名将你的企业带上天空的企业家,一名准备推销的员工,一名追求新研究议程的教授,还是一名寻找新增长机会的知名领导者,这里都有适合每个人的伟大课程。

尽情享受吧!

机遇、挑战和经验教训

许多世界顶级思想家为我们提供了清晰理解创新类型之间的根本差异以及所做选择如何影响长期业务增长的方法。这些创新战略以不同的名字被推广,如硅谷的彼得·泰尔的竞争垄断,哈佛的克雷·克里斯滕森的持续颠覆性创新,INSEAD 的 Chan Kim 等人的红海蓝海,或者全球本地化。通用电气公司的杰夫·伊梅尔特等人的逆向创新。然而,它们都传递相同的信息。公司有时会陷入竞争的红海中,这种竞争会侵蚀他们的利润,阻止他们创造有意义的差异化,并阻止他们投资可以创造新市场和新价值的颠覆性产品。世界上最成功的公司通过投资于专注的、差异化的、创造新市场的技术,设法逃避竞争,使竞争变得无关紧要。他们设法创造了蓝海和 T21,并利用创新技术来扰乱他们的市场,这导致了强劲和有利可图的增长的新轨迹。

在他的畅销书《零比一》中,彼得·泰尔写道,“它的竞争[……]就像战争:据称是必要的,据称是勇敢的,但最终是毁灭性的”。发现新市场和创造垄断的公司可以创造压倒性的价值。“我们所说的垄断是指这样一种公司,它在所做的事情上非常出色,没有任何其他公司能够提供与之相当的替代品。[……]垄断拥有自己的市场,所以可以自己定价。由于没有竞争对手,它以最大化利润的数量和价格组合进行生产。”然而,根据 Clay Christensen 的说法,运营良好的公司的经理通常会有意或无意地追求颠覆性创新,Thiel 称之为垄断。这是因为它从根本上挑战了他们的供应链、成本结构、流程、价值观和文化,而这些都是他们在了解和熟知的市场中最强大的客户的需求时发展起来的。需要一种根本不同的战略来应对颠覆性创新,这些创新创造了新的市场、新的竞争、新的需求、新的客户群体,或者以新产品属性和商业模式瞄准现有的既定客户群体。Chan Kim 等人调查了世界上最成功、最持久和最有远见的公司为持续超越市场和反复创造蓝海所遵循的战略的共性。他们发现,发现蓝海的成功战略的基石是价值创新,而不是专注于击败竞争对手,重点应该是通过为客户创造价值的飞跃,让竞争变得无关紧要,从而开辟新的无竞争市场空间。“基于竞争的红海战略假设一个行业的结构条件是给定的,企业被迫在其中竞争。[……]相比之下,价值创新基于这样一种观点,即市场边界和行业结构不是给定的,可以通过行业参与者的行动和信念来重构。”然而,Chan Kim 等人认为,“目前的竞争环境严重失衡,有利于工具和分析框架在红海中取得成功。只要这一点保持不变,红海将继续主导公司的战略议程,即使创造蓝海的业务需求变得更加紧迫。也许这解释了为什么尽管之前呼吁公司超越现有的行业空间,但公司还没有认真对待这些建议。"

通用电气(General Electric)前首席执行官杰夫伊梅尔特(Jeff Immelt)等人已经很好地记录了追求蓝海的影响以及在现有组织内调整其战略的实际挑战。杰夫等人在《哈佛商业评论》上写道,“通用电气迫切需要创新……不仅要扩展到高端市场……还要抢先于其他公司创造类似的产品,然后用它们来瓦解通用电气。坦率地说,如果通用电气的业务要在未来十年生存和繁荣,它们必须变得像在红海中竞争一样擅长(追逐蓝海)。”然而,蓝海战略的发现和执行远非一帆风顺。Jeff 举了一个很好的例子,他描述了通用电气公司在交付的各个阶段带领蓝海所面临的巨大挑战。他写道,作为阿格的领导者,你通常严格负责实现增长计划,满足现有市场中现有客户的需求——换句话说,重点是维持或逐步改善红海。你将很少有时间参加像发现蓝海和创造新策略这样的课外活动。但是,与在内部销售蓝海方案相比,将你的战略写在纸上的这些挑战算不了什么。这样做需要引起公司层级中多个其他领导的注意,例如制造主管、营销主管、财务主管、全球研发主管&,他们都有自己的日程安排,几乎没有时间来说明情况。而且,即使这个提议成功了,你也需要年复一年地与更多短期回报的项目争夺资金。与此同时,你仍然需要担心日常工作的季度数据。杰夫认为,开发全新产品的成功案例少之又少,这不足为奇。

在 Jeff Immelt 等人描述的例子中,组织挑战背后的机制可以用 Christensen 的逻辑很好地解释。问题的核心是普遍接受和实践的衡量增长的方法。当增长是用相对值而不是绝对值来衡量时,比较机会就更容易了。因此,比率被用来衡量和比较提案、项目、投资组合或公司之间的增长。最常用的是资产回报率(ROA)和毛利润。第三个是内部收益率(IRR),它通过评估所有现金流的净现值等于零的贴现率来估计潜在投资的盈利能力,可以理解为一段时间内的投资。优秀的经理会做有意义的事情,而有意义的事情就是关注并投资那些能最大化 ROA、毛利润和 IRR 的项目。这可以通过增加净收入、收入或投资,或者减少资产数量和制造成本,或者投资于为投资者提供更高净现金流的短期项目来实现。前者通常更难实现,因此后者是增长背后最大的意图和焦点。因此,根据这一逻辑,红海文化是一种倾听最强大客户意见的文化,积极投资于近期技术,以满足这些客户的需求,寻求更高的利润,并瞄准足够大的市场以维持增长预期。如果你仔细观察,这里的悖论是,这些是完全理性的行为,但它们与追求蓝海——提供最大价值——背道而驰。这正是杰夫伊梅尔特等人的故事背后的挑战,或许还有更多其他人试图向他们的经理推销想法。作为一名经理,看着蓝海提案,你会得出这样的结论:这些提案通常会被你最强大的客户拒绝,利润较低,表现不如现有技术,投资回报较长,初始市场规模可能微不足道。因此,蓝海的价值不应该用比率来表示和比较,而应该用绝对值来表示和比较。鉴于蓝海在创造无与伦比的价值方面取得的显著成功,我们应该给予它应有的关注和耐心。

作者图片

克里斯滕森和泰尔已经很好地研究了聚焦于蓝海的发现和执行的困难背后的原因。它触及了个人和组织行为的核心。组织开发符合其主要业务需求的流程和价值观。这些流程和价值观是构成组织能力的要素,并决定公司成功建立市场领先地位的能力。员工学习什么可以被定义为最佳解决问题的方法和决策标准,当他们成功地使用这些方法并做出决策来解决重复出现的任务时,流程就被定义了。流程可以包括产品开发、采购、市场研究、预算、计划、员工发展和薪酬以及资源分配。做出决策的标准既可以是符合道德的,例如与公司愿景和使命一致,也可以是资源-流程-价值观,这是员工做出优先决策并判断某些行动是否更有吸引力、某些客户是否更重要、或者某些想法是否更有吸引力的标准。组织变得越大越复杂,就越有必要建立适当的流程,并培训员工独立决定优先事项。一旦员工开始通过假设而不是有意识的决定来使用流程和价值观,那么组织文化就建立起来了,这使得员工能够自主一致地行动。通常是文化决定了公司的发展方向,并最终决定了公司的长期发展轨迹。克里斯滕森认为,文化是我们看到经营良好的大公司最终失去市场地位,无法应对竞争基础变化的核心原因。公司没有文化,公司就是文化。文化使员工自主一致地行动,定义员工将做出什么决定,决定资源分配的路径,并决定成本结构。成长文化是由肩负使命的个人建立的,并由他们自己对价值所在的理解所驱动。一些文化重视竞争和满足最强大客户的需求,而另一些文化则通过差异化和创造新市场来推动增长。换句话说,文化将最终决定一个组织脱离竞争并创造新价值的能力。当组织能力已经根植于文化之中时,就像杰夫·伊梅尔特(Jeff Immelt)等人详述的通用电气(GE)的案例那样,变革可能会异常困难。

克服这一困难的一种方法是建立一个独立的增长团队,该团队类似于一个初创环境,拥有决策自主权,并有潜力创造自己的文化,旨在创造新的市场。杰夫·伊梅尔特(Jeff Immelt)等人写道,通用电气通过创建独立的本地增长团队(LGT)来应对他们追求蓝海的组织挑战,他们自己的流程、价值观和文化与业务的其他部分分离,使发现过程比以前更有效,而主流组织的重点是红海。在 2008 年严重的全球经济衰退中,通用电气的一些主要业务增长了 25%,这主要是因为其 LGT 子公司开发的新技术创新为客户创造了价值飞跃,并开辟了新的市场空间。其他人也有类似的战略努力。1998 年至 2007 年间,美国宇航局高级概念研究所(NIAC)每年的预算约为 400 万美元,一个由企业家、研究人员和发明家组成的社区承担了发现蓝海的任务。他们有效地脱离了主流组织强加的障碍。该研究所在其 9 年的生命周期中评估了 10 到 40 年规划范围内的约 1309 项建议,5-10%的选定概念后来被纳入 NASA 的主流长期计划。与此同时,欧洲航天局建立了自己的先进概念小组(ACT ),每年预算 100 万€,任务是监测、执行和促进创新概念和工作方法的进展。NIAC 和 ACT 的成功被评估为采用选定的高风险、高回报的概念、技术、想法和方法,并加以证明,然后转移到主流组织,成功率在 10-30%之间。如果没有他们先进的概念单元,就不可能以如此大的规模和如此高的成功率评估蓝海提案。同样,近年来,在整个航空航天行业,我们可以看到专业业务单位/团体/团队的创建,它们拥有自主权和一套探索先进概念和新市场的策略。例如空客创建了 A3 实验室,R-R 创建了 R2 实验室,GKN 创建了全球技术中心。这是人工智能、区块链、量子计算、物联网、新型推进技术、增材制造等先进概念的地方。被评估为他们的客户创造价值的飞跃,并为新的利润增长设定轨迹。

最后,重申 Chan Kim 等人的观察结果——虽然我们看到一些公司正在采取行动来占领蓝海,但仍有更多的公司尚未采取行动。那些不采取行动的公司,迟早会看到新的陌生竞争的出现,失去市场份额,或者无法留住他们的客户。

底线

努力做到真实和原创,不要将自己的行为与他人进行比较,创造有针对性、差异化的解决方案,为你的目标客户创造价值飞跃。注意你周围文化的特征——这将最终决定你传达和接受对你各种愿景的支持的能力。如果你在挣扎,和那些绝对看到价值的人在一起。然后,当你向在红色海洋中游泳的人推销想法时,要注意你如何接近他们以及信息是如何呈现的。最后,培养你的情感勇气,接受潜在的不适、不确定、拒绝、失败或不安全感。这些是承诺一些更大的和超越常规的事情的风险,如果你想完成任何有价值的事情,这些是你可能不得不感受到的感觉。

无论你如何决定,追求蓝海绝对是值得的!祝你好运,度过一波又一波的挑战,我希望我已经帮助你预见到这些挑战会是什么。

但是如何真正发现价值呢?我有一个好消息,我正在开发一个价值发现的系统框架——敬请关注最新消息!

在我的研究中选择的关键人物

彼得·泰尔是硅谷精英,他共同创立了 PayPal,并成为脸书的风险投资人和早期投资者。他还共同创立了中情局支持的初创企业 Palantir,最近估值约为 260 亿美元。

克莱·克里斯滕森(1952-2020)是哈佛商学院的教授。他被认为是世界上创新和增长方面的顶级专家之一。2011 年,他被评为世界上最具影响力的商业思想家。《经济学人》将他的书创新者的困境命名为有史以来最重要的六本商业书籍之一。

W. Chan Kim 是 INSEAD 蓝海战略研究所的联合主任,也是 INSEAD 大学战略和国际管理的讲座教授。他在世界顶级管理大师中排名前三,并因商业和经济思维领导力获得诺贝尔学术讨论会奖。他与 renée maoborgne 合著的《蓝海战略》一书被认为是有史以来最具标志性和影响力的战略书籍之一。

彼得·布雷格曼是畅销书作家,伯格曼合伙人公司的首席执行官,该公司帮助首席执行官和高级领导人完成困难的事情。

彼得·德鲁克(1909-2005)被认为是管理学的创始人,2002 年他被老布什总统授予总统自由勋章。

丹尼尔·贝尔(1919-2011)可能是我们这个时代最著名的社会学家。他在他的书《后工业社会的到来》(T2,1973)中提出了后工业社会或信息时代的概念。后来,他将这一概念重新命名为信息社会,一般认为他是这一术语的创造者(1979)。

桑尼·贝恩斯是伦敦 UCL 的科学家和记者,也是《解释未来:如何研究、分析和报道新兴技术的作者。

James Surowiecki 是一名报道金融和人类行为的记者。他是研究群体及其集体知识和智力的主要贡献者之一。詹姆斯写了一本畅销书《群体的智慧》。

Richard Peterson 是行为经济学家、精神病学家、作家和 MarketPsych 的创始人,这是汤森路透 MarketPsych 指数(TMRI)背后的一股创新力量。在他的《基于情绪的交易》一书中,理查德展示了管理情绪是如何帮助顶级投资者跑赢大盘的。理查德正在开发突破性的基于预测情绪的模型,分析新闻和社交媒体中的情绪,帮助顶级投资者大幅跑赢大盘。他的团队开发了一个市场中立的基于社交媒体的对冲基金,在 2008 年金融危机期间,该基金的表现超过标准普尔 500 指数 24%。

感谢阅读!如果你觉得这篇文章有用或者有任何问题,请给我留言。同样让我们连线上 LinkedinMedium

Plotly Python 中的离散色阶

原文:https://towardsdatascience.com/discrete-colour-scale-in-plotly-python-26f2d6e21c77?source=collection_archive---------33-----------------------

一个我决定要解决的恼人问题

右侧一组离散的颜色条(原始内容)

我喜欢使用 python 库进行 Plotly,但是我注意到,当我试图使用 Graph 对象来做一些事情,比如制作 Choropleth 地图时,我很难为我的数据获取离散的颜色范围。

我喜欢根据类别将我的数据分成不同的区域。例如,如果我试图显示三类数据,我将为第一组使用 0-33 的范围,为第二组使用 34-66 的范围,最后为 67-100。在每一个类别中,我都想展示它自己的一套独立的颜色(见上图)。为了做到这一点,我写了下面的函数,我希望它能帮助那些想做同样事情的人!

该功能

这个函数所做的就是接受一个颜色元素列表。然后,它会想出如何为各种输入制作离散的色块。读一读,看看下面实际怎么叫。

def generateDiscreteColourScale(colour_set):
    **#colour set is a list of lists**
    colour_output = []
    num_colours = len(colour_set)
    divisions = 1./num_colours
    c_index = 0. **# Loop over the colour set**
    for cset in colour_set:
        num_subs = len(cset)
        sub_divisions = divisions/num_subs **# Loop over the sub colours in this set**
        for subcset in cset:
            colour_output.append((c_index,subcset))
            colour_output.append((c_index + sub_divisions-
                .001,subcset))
            c_index = c_index + sub_divisions
    colour_output[-1]=(1,colour_output[-1][1])
    return colour_output

上面是一段很短的代码,但是它为我们做了很多工作。

使用

为了绘制主标题图像,我实际上有 4 个类别(哈利·波特房屋),我不仅想用纯色显示这 4 个类别,还想根据出现的次数(从亮到暗)对它们进行细分。我已经准备好了所有的数据,但需要得到色阶。在下面我们来定义这个方案。每个列表元素对应一个类别(即哈利波特的房子),这个列表元素实际上包含了我想要使用的颜色范围的列表(从最亮的颜色到最暗的颜色)。

**# Harry Potter Houses**
color_schemes = [
    ['#890000','#890000','#5c0000'],
    ['#2a6b28','#0b4c07','#003206'],
    ['#4f5a90','#374798','#30375a'],
    ['#fff4b1','#ffed86','#ffdb00']
]

现在,我可以调用上面的函数来获取适当的格式,并将其传递给 go。Choropleth plotly 对象。

colorscale = generateDiscreteColourScale(color_schemes)

最终增加了我们的情节。

fig.add_trace(go.Choropleth(
    geojson=counties,
    locations=fips,
    z=final['split'].astype(float),
    ***colorscale=colorscale,   <--- THE MONEY***
    marker_line_width=0.0, # line markers between states
    marker_line_color='rgba(0,0,0,0.1)',
    text=final['label'], # hover text
))

就这么简单!你可以用它来制作一堆好看的情节。下面是我使用它的一些例子。

使用映射功能的一些示例(原始内容)

当然,这并不局限于地图,在 plotly 中使用 colourscale 输入的任何情况下都可以使用。

希望这有所帮助!如果有任何问题,请随时联系我!

参考

[## 谷歌趋势 DMA

www.kaggle.com](https://www.kaggle.com/kapastor/google-trends-dma) [## Choropleth 地图

如何用 Plotly 在 Python 中制作 choropleth 地图?

plotly.com](https://plotly.com/python/choropleth-maps/)

旧金山警察局交通站的歧视

原文:https://towardsdatascience.com/discrimination-in-san-francisco-police-department-traffic-stops-2800bf06895d?source=collection_archive---------50-----------------------

使用假设检验探索旧金山警察局交通检查站种族歧视的证据

安库什·巴拉德瓦伊

伊莎贝拉·史密斯Unsplash 上拍摄的照片

介绍

在过去几个月里,针对乔治·弗洛伊德(George Floyd)和冷血杀手布里奥纳·泰勒(Breonna Taylor)被公开处决的和平抗议已经发展成为一场由一代美国年轻人领导的运动,他们已经看够了警察使用不合理的武力摆放黑人尸体。现在,我并不是要让你相信我们的警察系统中存在系统性的种族主义:这里有一篇文章可以帮助你开始。相反,我的目标不是关注整个治安,而是通过数据科学,主要是通过假设检验,调查警察部门进行的交通拦截中的种族歧视。虽然已经有研究充分的证据表明警察交通拦截中存在种族偏见,但我想探究旧金山警察局实施的交通拦截中的种族歧视。我决定研究 SFPD,因为三藩市被认为是一个自由的城市,我想了解“自由”对于一个城市的警察部门的种族主义到底意味着什么。因此,当我开始着手这个项目时,我提出的问题如下:

在旧金山警察局进行的交通拦截的结果方面,是否有歧视黑人的证据?

访问交通站点数据

为了获取交通停车数据,我使用了由斯坦福大学的研究人员和记者团队斯坦福开放警务项目汇编的数据,他们从全国各地的执法部门收集并标准化了车辆和行人停车数据。更多关于他们数据结构的信息可以在这里找到。

将旧金山交通站点的数据导入 Python 后,我开始为下一步的分析清理和准备数据。我首先使用 Seaborn Python 包通过热图(如下所示)可视化了数据集中的 NA 值。

可视化 NA 值

在该热图中,红色表示安娜值的存在。由于“违禁品 _ 发现”和“搜索 _ 基础”列有太多的 NA 值需要通过插补来解决,所以我简单地从数据集中删除了这些列。为了处理剩余的 NA 值,任何包含安娜值的行也被从数据集中删除。

最后,准备数据的最后一步是确保每一列都有适当的数据类型。对于这个数据集,我只需要将“date”列更改为 datetime 对象。

探索性数据分析

现在数据已经准备好了,是时候进入探索性的数据分析了。该过程的第一步是使用 Pandas value_counts 函数显示数据集分类列中每个值的计数。在这一步中,我注意到在“reason_for_stop”列中有一个问题,因为一些行被标上了新的原因,尽管已经为该原因建立了一个类别。例如,虽然已经有许多因“酒后驾车检查”而发生的停车,但有几个停车将“违章行驶|酒后驾车检查”列为其“停车理由”。后退一步,我返回到数据清理和准备步骤来修复所有出现的错误。

这种情况说明了数据科学过程的一个关键方面;虽然将数据科学过程视为从数据预处理到 EDA 再到数据分析/建模的一系列线性步骤更容易,但事实是,大多数时候,您必须后退一步来处理数据中以前无法预见的问题。

接下来,我在熊猫的交叉表函数的基础上定义了一个函数,来构建两个因素的比例交叉表。使用这个函数,我生成了一个表,该表比较了由于交通拦截的每个独特原因而被逮捕的人的种族比例。然后将该表可视化为条形图,以便针对每个停车原因,可以比较每个种族群体因交通停车而被捕的比例。

黑人被逮捕的比率似乎已经高于其他人群,特别是当逮捕的理由是“违反 MPC”和“BOLO/APB/逮捕证”时。

假设检验

要回答我的问题,我需要一些具体的证据,而不仅仅是条形图。在这种程度上,我将利用假设检验,即当分析师使用样本数据来检验假设,以对样本应该代表的总体做出陈述。在我可以使用的许多具体测试中,我使用了一个双比例 Z 测试,它可以用来确定两个样本比例之间的差异是否显著。

进行假设检验时,陈述你的零假设和你的替代假设是至关重要的,零假设是关于你的总体参数的陈述,替代假设直接与零假设相矛盾。例如,我的无效假设表明,因 SFPD 交通拦截而被捕的黑人比例小于或等于因 SFPD 交通拦截而被捕的所有其他种族的人的比例。另一方面,我的另一个假设认为,由于 SFPD 的交通堵塞而被捕的黑人的比例大于所有其他种族的人由于 SFPD 的交通堵塞而被捕的比例。

下一步是决定重要性水平。进行假设检验允许您使用检验统计量计算 p 值,如果 p 值小于显著性水平,则拒绝零假设,反之亦然。对于我的 Z 检验,我将显著性水平设置为 0.01。

现在,是 Z 测试的时候了。使用stats models proportions _ ztest 函数,我计算了 Z 值和 p 值,比较了因 SFPD 交通拦截而被捕的黑人比例和因 SFPD 交通拦截而被捕的所有其他种族的人的比例。当考虑整个数据集时,通过前面提到的零假设、替代假设和显著性水平,我的 Z 测试的结果使我拒绝了零假设,并得出结论,由于 SFPD 的交通堵塞,黑人比其他种族的人被捕的比率更高。然后,我重复了这个过程,这一次,在进行 Z 测试之前,对每个“停止原因”的数据集进行子集化。这一步的结果如下所示。

每个停止原因的 Z 测试结果

因此,尽管在整个数据集中,黑人的被捕率高于其他种族的人,但这种被捕率的差异仅在考虑因“机械或非移动违规(V.C .)”、“BOLO/APB/Warrant”、“移动违规”和“MPC 违规”而导致的交通堵塞时才一致。

中产阶级化和交通堵塞

在摆弄数据集时,我对旧金山县正在进行的中产阶级化和交通站数量之间的关系产生了好奇。因此,我决定将我的项目向前推进一步,看看中产阶级化和交通堵塞之间是否存在关联。

首先,我必须确定我将使用什么指标来定义中产阶级化。受这篇文章的启发,我决定用收入和教育程度来定义中产阶级化。

接下来,我必须弄清楚如何专门为旧金山获取这些数据。幸运的是, CensusData Python 包提供了对美国社区调查数据的访问。随着美国人口普查局的官方文件,我发现了 2009 年至 2015 年旧金山县居民的家庭工资或薪金总收入和最高教育水平。

为了将这些指标与 SFPD 在 2009-2015 年间每年进行的交通拦截次数进行比较,我必须首先将数据重新调整到 0 到 1 之间,以便更好地直观比较这些指标的变化情况:

假设由于工资或薪金而增加的家庭总收入和拥有研究生或专业学位的居民比例的增加表明中产阶级化的增加,那么 SFPD 每年进行的交通检查次数似乎会随着中产阶级化而减少(除非从 2014 年到 2015 年交通检查大量增加)。为了找到这种说法的具体证据,我找到了最高教育程度、工资或薪水导致的家庭总收入以及一年中停车次数之间的相关性:

教育程度、总收入和交通站点之间的相关性

从上面的热图中,我们可以明确地得出结论,我们的两个中产阶级化指标和每年的交通停车数量之间存在适度的负相关。当然,这个结论应该有所保留,因为中产阶级化比我使用的两个指标要复杂得多。

结论

通过这个项目,我在旧金山警察局进行的交通拦截中发现了种族歧视。具体来说,在旧金山,黑人因交通堵塞而被捕的比率高于其他所有种族。

此外,我发现旧金山的中产阶级化与 SFPD 在 2009 年至 2015 年期间进行的交通拦截数量之间存在适度的负相关。这意味着,随着旧金山日益中产阶级化,交通拦截的数量下降。

用于这个项目的代码可以在这里找到。

作为免责声明,受雇于 SFPD 并不能使个人成为种族主义者 。SFPD 作为一个组织,表现出种族主义的模式,这可能源于各种来源,如警察培训、部门文化等。但这并不能证明 SFPD 官员是种族主义者。

疾病传播预测模型

原文:https://towardsdatascience.com/disease-spread-forecast-modeling-9bbb6ea552ed?source=collection_archive---------42-----------------------

预测器、结果、建模方法

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

斯科特·韦伯在 Unsplash 拍摄的照片

我们现在正在新冠肺炎看这出戏。预测疾病传播(尽我们所能)对于如何应对至关重要,例如制定短期策略和长期战略。是否(以及何时)封锁地区或整个国家。是否采取限制较少的措施。预测所需的资源,如急救人员、医生、护士、个人防护设备、医院病床、ICU 病床、呼吸机等,可以确定缺口,并对决定下一步的工作重点至关重要。

先进的预测模型还允许最坏情况和最好情况的预测,以及对各种假设情景的预测,例如当 50%的公众与 90%的公众保持社交距离时的预测。

当疾病发生在像新冠肺炎这样的全球疫情时,传播预测变得尤其重要。在建模方面,它也非常复杂。所以这是一个很好的案例研究。

让我们开始吧。

疾病状态

最简单的区别可能是二元的:个体是否被感染。假设有一个测试可以可靠地揭示一个人的状态。假设群体中的个体每天都被测试。一天内新感染的人正在被统计。

这为我们提供了新感染计数的每日时间序列。我们可以想象这一点,并发现趋势。感染人数呈指数增长吗?变平了?我们甚至可以预测未来几天的价值。

我们可以用各种方式对人口进行分割,重复这一过程:全球的、特定国家的、美国国内的国家。我们甚至可以跨区域比较时间序列,以算法或视觉方式预测下一个热点将在哪里以及何时到来。

好的,所以我们可以根据该人群的历史感染人数,也可能根据其他人群的历史感染人数来预测近期的感染人数。但是有多好呢?这取决于许多因素。

一个潜在的巨大因素是测试的能力或意愿。有些国家(或州)可能会积极地进行测试,有些则不那么积极。这将严重影响他们的感染人数。此外,如果人群中的检测率发生显著变化(这种情况会经常发生),每天的新感染人数也会受到影响。新的感染人数可能会增加,因为我们正在测试更多的人。

到目前为止,我们讨论的二元疾病状态模型没有区分轻度和重度病例。显然这样做是有益的。在我们预测的新感染病例中,有多少可能严重到需要住院治疗?有多少人严重到需要重症监护?我们做出这种精细预测的能力可能是预测我们将需要多少病床的关键;我们需要多少特殊设备,如通风机;等等。

好的,那么让我们把感染分为轻度中度重度。我们需要另一种疾病状态:死亡。显然很重要。我们想知道预计会有多少人死亡。其实还有一个:恢复了

让我们假设我们可以(合理地)准确确定谁在什么时间点处于什么状态,从住院计数,从 ICU 计数,太平间数据等。我们可以用这些数据来估计状态概率和状态转移概率。

轻度对中度对重度感染者的百分比是多少?严重者最终死亡的百分比是多少?恢复的百分比是多少?从轻度中度或者从中度重度有(有时)进展吗?

我们还可以估计发生各种转变所需的时间?最终康复的重症患者的平均康复时间是多少?几天?几周?

所有这些都可以表示为一个马尔可夫模型。节点是疾病状态。弧线携带从一个状态到另一个状态的转移概率。这在下面描述。没有显示所有转换。

healthy → mild, mild → recovered, mild → moderate, severe → recovered, severe → died

https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5871642/中的图 1 描绘了一个疾病进展模型,其主要状态为易感暴露感染痊愈。从结构上看,模型中的主要路径是

susceptible → exposed → infected → recovered

建模延迟

考虑过渡exposed →infected。到目前为止,我们的建模揭示了这种转变的一个可学习参数, P ( 已感染 | 已暴露),它表示暴露个体被感染的概率。如果我们怀疑这个概率随着时间的推移而变化,我们可以尽可能频繁地重新估计这个概率,以保持它是最新的。

到目前为止,该模型没有考虑潜伏期,即暴露者感染所需的时间。潜伏期很大程度上取决于疾病。扩展该模型以包含这一点将让我们预测,除了暴露者中有多少比例会被感染,还有他们什么时候会被感染。这反过来可以对疾病传播做出更准确的预测。

我们如何增强我们的模型以适应潜伏期?一个想法是用特定时间的版本exposed(t)代替exposed。后者代表在时间 t 暴露的个人。虽然可能不知道任何一个人的确切情况,但是可以获得某一天接触到的人数的一个很好的估计。同样,我们可以用infected(t)代替infected。所以现在我们可以获得对任何特定滞后 dP ( 感染(t+d)|暴露 ( t )的估计。这可以利用关于潜伏期的知识。

基本再现号

这是对一个感染者平均感染多少人的估计。准确的估计对预测疾病的传播非常有用。大于 1 的值表示指数增长,而小于 1 的值表示指数收缩。显然,知道我们在哪里是很重要的。

基本再生数对于进行定性预测特别有帮助。新冠肺炎的基本繁殖数大于 1,明显高于季节性流感。所以新冠肺炎以指数速度传播。即使这种预测只是方向性的,它仍然是有用的。尤其是在早期阶段。

出行模式

旅行模式也有助于预测。假设疫情在 A 国爆发。假设从 A 国到 B 国的旅行很多,从 A 国到 c 国的旅行更少。预测 B 国将比 c 国更快开始“感受到疫情”似乎是合理的。

另一种旅行模式是季节性的。旅游季节(比如假期)的疫情可能会比一年中的其他时间传播得更快。

干预

在疫情期间,某些干预措施可能会成为疾病传播的抑制剂。在新冠肺炎,主要原因是社交距离。哪些地理位置在何时以及在何种程度上进行了干预。(我们所说的“程度”是指严重程度,如封锁、旅行建议等。)捕捉这样的数据在建模中非常有用。

干预数据在多大程度上有助于提高预测质量?不好说。在特定的环境中,可能存在多种相互竞争的力量,如疾病传播和干预。如果我们不进行干预,我们可能无法轻易判断情况会有多糟糕。

也就是说,获取和使用干预数据总比没有好。我们总可以在以后整理出它有多有用。如果我们不抓住它,我们甚至没有追索权。

考虑几个月后的全球疫情。假设我们知道哪些地理实体(国家、州、县或城市)在何时以及在何种程度上引入了哪些干预措施。我们还知道哪些还没有。将这些数据与感染前和感染后的计数相关联,在超过 100 个地理实体中进行,可能会开始产生一个明确的图像。

把所有的放在一起

假设我们想要预测某个特定的地理区域(国家、州、县)。或城市)新感染人数在接下来的一周(7 天)内,可能会进一步细分为轻度中度重度痊愈,以及死亡。下面我们盘点了这篇文章中所有可能有助于预测的因素。本质上,这一部分是我们在这篇文章中看到的内容的总结,在这个特定的预测问题的背景下呈现。

预测因素的一个关键组成部分是该地区近期感染计数的时间序列。我们可以将此进一步细分为每日新增的轻度中度重度康复,以及死亡

这种预测因子的组合可以通过我们前面描述的疾病进展模型的类型来增加。这里有一个场景,描述了这样做的意义。假设昨天我们有一些新的温和的计数。根据我们的疾病状态进展模型,我们可以预测这些计数中有多少将在各种其他状态下结束以及何时结束。显然,这在我们的预测问题中是有用的。此外,请注意,我们的疾病进展模型利用了领域知识,如潜伏期。因此,我们的预测者也可以从这些领域知识中受益。如果该位置处于疾病爆发的早期阶段,这可能特别有用。

我们的预测模型还应该尽可能利用已知的基本再生产数量。这在疫情爆发的早期阶段可能特别有帮助,因为我们可能没有足够的实际新感染天数来进行预测。

如果旅行模式被认为是相关的,我们的预测模型也可能考虑到它们。如果疾病已经在某个地区广泛传播,并且从该地区到我们所在地的旅行很重要,就可能出现这种情况。或者我们的位置是否处于“旅游季节”状态。

我们的预测模型也可能使用我们所在位置的当前干预状态作为预测器。干预是什么时候进行的?它的严重性是什么:锁定?旅游咨询?….在有很多人来往的地方使用干预状态也可能有所帮助。

假情报:如何通过可视化理解假情报网络

原文:https://towardsdatascience.com/disinfovis-how-to-understand-networks-of-disinformation-through-visualization-b4cb0afa0a71?source=collection_archive---------22-----------------------

数据可视化在清晰地表达复杂性方面有着巨大的潜力,但这需要迭代、移情和针对特定数据和领域的定制。

图片作者。

假情报行动的网络可视化具有启发的力量。当有效设计时,它们可以表达那些散布假信息的人的微妙策略。这些密集形式的数据可视化受益于贯穿其开发的明智的设计选择,以及改进未来迭代的评估。

去年,我从伦敦大学城市学院的数据科学硕士学位毕业。这个为期一年的特别强化课程教会了我机器学习、神经计算、计算机视觉和视觉分析的基础知识。在我为期三个月的论文项目中,我选择了网络分析作为我研究的领域。特别是,我专注于网络可视化的设计和评估,随着时间的推移,应用于虚假信息领域。

毕业后,我继续研究时间网络及其在虚假信息研究中的应用。2020 年初,我发表了一篇名为“观察六个长达十年的假情报行动在六分钟内展开”的媒体文章,它有点像病毒一样传播开来。我目前在蒙田研究所研究针对法国的虚假信息,并在威尔逊中心通过网络分析研究性别化和性化的虚假信息。我所有的研究都有一个共同的线索,那就是使用网络可视化来理解那些想要在网上影响他人的人的策略是如何随着时间的推移而演变的。

今天我想后退一步,告诉你我的时态网络可视化的设计选择是如何在我的理学硕士论文完成过程中发展的。这项工作得到了伦敦大学城市数据科学研究所的慷慨支持,并得到了我在城市的学术顾问,世界领先的可视化研究小组之一 giCentre 的 Jason Dykes 教授的指导。我们在 EuroVis 2020 上以反病毒的适当标题展示了这篇论文研究的海报。

如果您对数据可视化设计感兴趣,希望您会对我的方法感兴趣。如果你对虚假信息网络感兴趣,希望你会发现可视化信息丰富,可视化方法有用(在各种其他研究报告中有记录)。如果你正在考虑在理学硕士水平上学习数据科学,你可以用这篇文章作为一篇理学硕士论文的例子。

继续我的论文,题目是…

评估信息操作的时间网络可视化表示

这里关键的活动部分是,在这项研究中,我评估了三种时间网络可视化的两种不同表示(静态幻灯片和动态视频)。我使用的数据集是 Twitter 自 2018 年以来一直在识别和发布的中国、俄罗斯和委内瑞拉的信息操作

这篇论文的目标是了解哪种表示类型的假情报实践者认为最有用,以便未来时态网络可视化的设计可以根据他们的需要进行定制。我通过回答两个相关的研究问题实现了这个目标,第一个是数据科学,第二个是社会科学研究问题:

研究问题 1: 时态网络可视化的静态和动态表示有什么好处和挑战?

研究问题 2 :俄罗斯、中国和委内瑞拉国家支持的推特信息运作有哪些独特和共有的方面?

这两个问题是数月思考、编辑、阅读和思考的结果。总的来说,人们好奇的是时间网络可视化是否有助于确定俄罗斯、中国和委内瑞拉等国家在多大程度上拥有其竞选活动的独特在线“指纹”。创建动态和静态可视化的选择随着时间的推移而发展,因为我在思考如何通过时间网络分析的方法最好地贡献在线虚假信息的领域知识。

委内瑞拉信息运作随时间演变的例子。图片作者。

这些方法

我的项目包括两个阶段的方法。首先,我在 Twitter 上创建了俄罗斯、中国和委内瑞拉信息运作的时间网络可视化。这是基于我对可视化设计中一些已确立的最佳实践的理解。然后,我通过在结构化采访中向造谣者展示这些网络来评估它们。在采访中,参与者描述了他们如何与网络互动,以及他们在其中看到了什么。

网络可视化本身的数据来自不真实账户(有时被称为机器人、电子人或巨魔)发布的推文,这些账户“可能是 Twitter 上国家支持的信息操作的一部分。自 2018 年以来,Twitter 发布了用户资料和推文的数据集,他们认为这些数据是国家支持的行为者在他们的平台上从事“不真实行为”的更广泛网络的一部分。发布这些数据是为了“研究人员可以调查、学习和建立未来的媒体素养能力”。

Twitter 已经发布了 16 个国家(还在增加)的数据,但在这项研究中,我选择只分析来自中国、委内瑞拉和俄罗斯信息运营部门的英语推文,这些推文都包含大量英语内容。这个决定是为了在三个月的项目时间框架内捕捉英语在线对话中的外国干扰,是一个主观选择,框定了我的整个研究及其结果。在随后的研究中,我扩展到可视化六个国家的所有语言。

为了进一步研究数据,我选择只可视化不真实账户使用的标签。换句话说:我的可视化所需要的只是不真实账户的名称(源)和他们使用的所有标签(目标)。这些源和目标(总是按国家分开)在网络可视化中被连接起来。这些连接线(边缘)是按照虚假账户创建的年份进行颜色编码的,以保留更多的可视化内容。

显示连接到目标(标签)的源(非真实帐户)的简化图形。图片作者。

为了让我的参与者(虚假信息从业者)更容易理解时间网络可视化,我制作了这个“网络可视化介绍”视频(以及相同信息的幻灯片)供我的参与者观看:

网络可视化介绍。作者视频。

在将英语委内瑞拉语、俄语和中文的不真实账户分解为标签关系后,它们被分别上传到 Gephi 。Gephi 是一个开源的网络可视化软件,它可以通过时间元素来分解可视化。对于每个国家,由于 Gephi 的内存限制,30 万条推文的随机子集被可视化。

在网络可视化中有许多不同的方式来排列节点(称为布局算法)。我选择使用 ForceAtlas2,因为它吸引密切相关的节点,排斥不相关的节点,并在网络中紧凑地布置“突发”。

在我将一个国家数据集上传到 Gephi 后,我运行了 ForceAtlas2 布局算法,按帐户创建年份进行颜色编码,最后将网络转换为时态网络可视化,以便查看网络不同区域的活动时间。这样做的时候,在软件窗口的底部出现了一个切换栏,允许我挑出在特定时间段发生的非真实用户和标签之间的联系。Gephi 的这种可提供性是这个项目中使用该软件的一个重要原因。下面你可以看到整个中国网络的可视化(左),以及一年的网络切片(右):

全中文网络可视化(左),以及 Gephi 软件中显示的 2017 年同一网络可视化中使用的标签(右)。右边的图像是网络的“时间片”,它构成了静态幻灯片中的一张幻灯片。图片作者。

创建静态表示

幻灯片首先显示了整个网络,然后是同一网络的 5-8 个时间片段。这是中国数据集的最终幻灯片:

中文信息操作的静态表示。作者幻灯片。

创建动态表示

为了让这项研究有两个可比较的表示,我还在 Gephi 中记录了网络的发展,并创建了与幻灯片中的文本完全相同的画外音解释。结果是 YouTube 上的这个中国可视化视频:

中文信息操作的动态表示。作者视频。

评估时间网络可视化表示

鉴于对领域专业知识的需求,在这种情况下,根据 Sheelagh Carpendale 关于信息可视化评估的指南进行了一项狭窄而丰富的定性研究:

“使用完整的数据集、领域特定的任务和领域专家作为参与者来运行评估,将有助于为给定信息可视化的有效性开发更加具体和现实的证据。”

一小部分致力于了解、打击或告知公众网上虚假信息的领域专家是通过滚雪球抽样方法发送的电子邮件邀请招募的。这导致了 5 次会议,参与者进行培训,探索可视化,然后通过结构化访谈提供定性反馈。

通过转录响应和进行主题分析(如 Fereday 和 Muir-Cochrane,2006 所述)来分析数据,这些分析总结了参与者对他们在与可视化交互时所面临的好处和挑战的反应,他们对未来可视化的建议,以及他们对网络本身的分析。

通过向领域专家询问他们对可视化的印象和体验,我能够收集到由虚假信息网络知识提供的发现,并围绕可视化的可能最终用户。这似乎很适合数据科学论文,因为任何数据分析过程的最后阶段(与需要分析的利益相关者的沟通)都依赖于数据可视化的有效使用。

结果呢

这项研究的结果可以根据他们回答的研究问题进行细分。对于第一个研究问题,我想知道我的参与者所经历的好处和挑战的答案,以便在虚假信息研究的背景下为未来的网络可视化设计选择提供信息。对于第二个研究问题,我希望进入我的参与者的头脑,了解他们在网络中实际看到的

研究问题 1:时态网络可视化的静态和动态表示有什么好处和挑战?

这个研究问题是在采访中通过询问参与者哪个表征更容易观察或给他们足够的信息,以及每个表征的哪个部分在试图理解他们正在看的东西时更有帮助来解决的。每个问题的不同回答根据被提及的次数进行统计,并整理成表格。

研究结果:我发现参与者更喜欢信息操作的复杂时间网络可视化的动态表示,或视频。这在很大程度上是因为每个参与者都认为视频让他们更好地了解了网络随着时间的演变,并评论说视频使他们更容易理解“不同活动时期之间的巨大差异”。虽然参与者喜欢按照自己的节奏浏览静态幻灯片,但五分之四的参与者更喜欢动态或动态和静态相结合的方式来分析网络或向他人解释网络。

下一步:这些发现让我们能够为未来的设计提出建议。我们建议使用在关键时刻和更大的上下文信息之间有停顿的交互式动态表示。我们已经制作了这样一个工具的线框——一个通过可视化展示我们建议的概念设计。

提议的具有静态和动态元素的时间网络可视化线框(在 EuroVis 2020 上展示)。图片作者。

我从这项研究中学到的关于视觉化设计的一个关键要点也是我在学习专业沟通时学到的:根据你的媒介和受众定制你的信息。使用可视化作为媒介的美妙之处在于,它可以是交互式的、信息丰富的,并且具有无限的设计选择。在我为 Mozilla 做的研究中(我在完成这项研究后进行了这项研究),我决定不实施上面的一些更详细的建议,而是采用一种我认为更适合中型文章的更简单的格式,因为我的目标是解释网络并告知公众,而不是给他们可探索或可调查的可视化。在可视化的目的是允许探索和研究的未来迭代中,更复杂的可视化设计可能是合适的。

研究问题 2:俄罗斯、中国和委内瑞拉国家支持的推特信息运作有什么独特和共同之处?

这个研究问题是在采访中通过询问参与者网络的哪些方面对他们来说是独特的,或者在多个网络之间是共享的来解决的。根据是否需要理解网络结构、hashtag 内容和时态成分,对独特和共享的方面进行了汇总和分析。下图显示了调查结果。

研究结果:通过分析时间网络可视化,参与者注意到信息操作是循环的、动态的,并且随着时间的推移而演变。他们还发现,一些行动似乎比其他行动更有组织性和政治性,俄罗斯的活动在其网络结构中独特地表现出两极分化。俄罗斯和中国都被发现使用#followme 等越来越受欢迎的标签,并在 2016 年和 2019 年恢复了早在 2009 年的旧账户。这些行为表明行动是长期战略的一部分。另一方面,委内瑞拉没有表现出这两种行为,并且可能在使用账户角色上有一个不太完善的策略。

下图概述了与会者提到的信息运作的主要方面。有些是个别国家独有的,有些是两个或所有三个国家共有的。

俄罗斯、委内瑞拉和中国在推特上的信息运作的异同。结果按照提出要求的参与者人数(“p”)和被提及的总次数(“x”)排序。图片作者。

每个方面右侧的红色字母表示识别它们所需的信息属性。它们是:标签的内容(“C”),交互的结构(“S”),以及操作的时间(“T”)演变。这三个信息属性是在主题分析中导出方面之后开发的。根据识别每个方面所需的数据类型来识别它们。使用两个或三个信息属性的方面可以认为是更健壮的,比如发现所有的信息作战战术都是随时间演化的。hashtag 内容和时间演变等方面可以使用不同的方法来可视化,比如词频表或条形图。内容和时间发现的稳健性以及网络结构发现可以在进一步的研究中探索。

那么,下一步是什么?

在这篇理学硕士论文上的工作经历教会了我通过评估和迭代为设计选择提供信息的价值。它还巩固了为特定领域(如在线虚假信息)定制数据可视化设计的好处。

当我继续在牛津大学攻读社会数据科学的哲学博士学位,研究虚假信息网络时,我将带着我通过这个项目以及通过完成 EuroVis 海报所吸收的设计价值观。通过谨慎而系统地推进这类研究,我们可以对虚假信息、网络可视化以及两者之间的有益关系有更深入的了解。

用 Math 和 Pytorch 分解神经网络以理解其内部运作

原文:https://towardsdatascience.com/dismantling-neural-networks-to-understand-the-inner-workings-with-math-and-pytorch-beac8760b595?source=collection_archive---------21-----------------------

简化的数学与例子和代码,以揭示黑盒内的光

弗洛里安·克劳尔在 Unsplash 上拍摄的照片

动机

作为一个孩子,你可能会在狂热的好奇心驱使下拆开一个玩具。你可能被它发出的声音的来源吸引了。或者可能是二极管发出的诱人的彩色光召唤你,让你的手去打开塑料。

有时候,你可能会觉得被欺骗了,因为它的内部与外表的光鲜亮丽让你想象的相去甚远。我希望你足够幸运,打开了正确的玩具。那些充满了足够的错综复杂,值得打开它们。也许你发现了一辆未来外观的 DC 汽车。或者可能是一个奇怪的扬声器,背面有一个强磁铁,你在冰箱上试过。我敢肯定,当你发现是什么让你的控制器振动的时候,感觉就很好。

我们也要做同样的事情。我们正在用数学和 Pytorch 拆除一个神经网络。这将是值得的,我们的玩具甚至不会打破。也许你会感到气馁。这可以理解。在神经网络中有如此多不同而复杂的部分。这是压倒性的。这是通往更明智状态的仪式。

所以为了帮助我们自己,我们需要一个参照物,某种北极星来确保我们在正确的方向上。Pytorch 的预建功能将是我们的北极星。他们会告诉我们必须得到的产量。我们有责任找到将我们引向正确输出的逻辑。如果差异听起来像你曾经熟悉的被遗忘的陌生人,不要烦恼!我们会再做一次介绍,这将是非常愉快的。
我希望你会喜欢。

线性

神经元的价值取决于其输入、权重和偏差。为了计算一层中所有神经元的这个值,我们计算输入矩阵与权重矩阵的点积,并添加偏置向量。当我们写道:

一层中所有神经元的值。

然而,数学方程的简洁是通过对内部工作的抽象来实现的。我们为简洁所付出的代价是让理解和在头脑中想象所涉及的步骤变得更加困难。为了能够编码和调试像神经网络这样复杂的结构,我们既需要深刻的理解,也需要清晰的思维可视化。为此,我们倾向于冗长:

具有三个输入、三个权重和一个偏差的一个神经元的值。

现在,这个等式的基础是一个特定案例所施加的约束:一个神经元、三个输入、三个权重和一个偏差。我们已经从抽象转向了更具体、更容易实现的东西:

为了计算 z ,我们已经从一层输入前进到下一层神经元。当神经网络一路向前通过其层并获得知识时,它需要知道如何向后调整其先前的层。我们可以通过衍生品实现这种知识的反向传播。简单地说,如果我们对 z 的每个参数(权重和偏差)进行微分,我们可以得到输入层 x 的值。

如果你已经忘记了如何求导,请放心:你不会被告知去温习微积分的整个分支。我们将在需要时回忆差异化规则。 z 对一个参数的偏导数告诉你把那个参数看成一个变量,其他所有参数都看成常数。变量的导数等于它的系数。常数的导数等于零:

w0 是变量,其他都是常数。w0 的系数是 x0。

同样,你可以相对于w2b来区分 z (其中 b 的不可见系数为 1)。你会发现 z 的每一个偏导数都等于它所微分的参数的系数。考虑到这一点,我们可以用 Pytorch 亲笔签名来评估我们数学的正确性。**

非线性

我们引入激活函数的非线性。这使得神经网络成为通用函数逼近器。有各种类型的激活,每一种实现不同的目的,产生不同的效果。我们将讨论 ReLU、Sigmoid 和 Softmax 的公式和区别。

热卢

整流线性单位函数将神经元的值与零进行比较,并输出最大值。我们可以认为 ReLU 将所有非阳性神经元标记为同样不活跃。

所有非负值保持不变,而负值被零取代。

为了实现我们自己的 ReLU,我们可以将 z 与 0 进行比较,并输出较大的值。但是 Torch 包中提供的 夹钳 方法已经可以为我们做到这一点了。在 Numpy 中,等效的函数叫做 clip 。在使用 Pytorch 的 relu 评估其输出之前,以下代码实现了一个基于 clamp 的 ReLU。

ReLU 的区别很简单:

“ReLU”为 1 或 0,取决于 z

  • 对于所有正的 z ,ReLU 的输出为 z 。因此微分就是 z 的系数,等于 1。
  • 对于所有非正的 z ,ReLU 的输出等于零。因此,微分也等于零。

让我们把我们的理解翻译成 Python 代码。我们先实现自己的 ReLU'( z )再和 Pytorch 的 ReLU 自动微分进行对比。

为什么我们要给 backward() 一个 1 的张量?
backward() 默认在单个标量上被调用的情况,并使用默认参数 torch.tensor(1。)这是以前我们调用 z.backward()时的情况。由于 torch_relu 不是单个标量,我们需要明确提供一个与 torch_relu 形状相同的张量。

乙状结肠的

sigmoid 激活函数产生从ℝ到范围[0,1]的映射 z 的效果。当执行二进制分类时,我们通常用值 1 标记属于目标类的实例,用值 0 标记所有其他实例。我们将 sigmoid 的输出解释为一个实例属于目标类的概率。

sigmoid 激活函数产生从ℝ到范围[0,1]的映射 z 的效果。

****小测验:神经网络的任务是进行二元分类。该网络的输出层由 sigmoid 激活等于 0.1 的单个神经元组成。在下列解释中,哪一个是正确的?

  1. 该实例属于类 1(目标类)的概率为 0.1。
  2. 该实例属于类 0 的概率为 0.1。
  3. 该实例属于类 0 的概率为 0.9。

****解法:只有 1 和 3 是正确的。重要的是要理解,具有一些输出 p 的乙状结肠激活的神经元,对于非目标类隐含地给出了输出 1-p 。同样需要记住的是 p 是与目标类关联的概率(通常标记为 1),而 1-p 是与非目标类关联的概率(通常标记为 0)。

****观察:认为 p(1-p) 之和等于 1。这在现阶段似乎太明显而无法指出,但当我们讨论 Softmax 时,记住这一点将会很有用。

我们再次用 Python 翻译数学,然后用 sigmoid 的 Pytorch 实现检查我们的结果:

乙状结肠分化。

乙状结肠的分化是优雅的。然而,它确实需要一条曲折的道路来达到它的优雅。一旦我们回忆起一些微分规则,我们就有了在蜿蜒的道路上漫步所需的一切。

乙状结肠的详细分化。

了解了如何区分 sigmoid,我们现在可以实现数学,并用 Pytorch 的亲笔签名对其进行评估。

注: sigmoid_prime() 依赖于前面实现的同一个 sigmoid()

如今,ReLU 已被广泛用作乙状结肠的替代物。但是 sigmoid 仍然在附近徘徊,隐藏在它的更一般化的形式的名字之下:Softmax。

Softmax

我们想到 sigmoid 用于二分类,softmax 用于多类分类。这种联系虽然是正确的,但却误导我们许多人认为 sigmoid 和 softmax 是两种不同的函数。当我们查看 sigmoid 和 softmax 的方程时,这一点得到了强调,它们之间似乎没有明显的联系。

softmax 激活的神经元是其值的指数除以共享同一层的所有其他神经元的指数之和。

再一次,公式的抽象使它乍一看一点也不直观。举个例子会更具体。我们以两个输出神经元为例,第一个 (z0) 输出实例属于标记为 0 的类别的概率,第二个 (z1) 输出实例属于标记为 1 的类别的概率。换句话说,对于 z0 目标类标记为 0,对于 z1 目标类标记为 1。为了用 softmax 激活 z0z1 ,我们计算:

Softmax 应用于输出层中的每个神经元。除了将来自ℝ的所有神经元映射到范围[0,1]之外,它还使它们的值相加为 1。

现在,我们可以纠正乙状结肠和 softmax 之间似乎缺乏明显的联系。我们将通过简单地重写 sigmoid 来做到这一点:

sigmoid 的另一种写法,显示它实际上是 softmax,有两个类。

第一种形式的乙状结肠比第二种形式的更常见。这是因为后一个版本在计算上更昂贵。然而,它的优势仍然在于帮助我们理解 softmax。

在输出层只有两个神经元,并且给定 softmax 使所有输出神经元总和为 1 的事实:我们总是知道soft max(z0)将等于【1-Softmax(z1)*** 。因此,对于二进制分类,将视为等于 0 是有意义的,并且使用 sigmoid 仅计算 z1 的激活。***

下面的代码实现了 softmax,并以三个输出神经元为例进行了测试。然后将我们的结果与 Pytorch 的 softmax 的结果进行比较。

我们根据每个神经元区分 softmax 激活。保持具有两个神经元的输出层的相同示例,我们得到四个 softmax 微分:

softmax 的雅可比矩阵。

不考虑输出神经元的数量,softmax 微分只有两个公式。当我们对神经元相对于其自身的 softmax 进行微分(雅可比矩阵中左上和右下的微分)时,应用第一个公式。当我们将一个神经元的 softmax 相对于某个其他神经元进行微分(雅可比矩阵中右上和左下的微分)时,应用第二个公式。

为了理解 softmax 的差异化所涉及的步骤,我们需要回忆另一条差异化规则:

除法法则。

以下差异包含详细的步骤。虽然它们看起来很密集,看起来令人生畏,但我向你保证,它们比看起来容易得多,我鼓励你在纸上重做它们。

softmax 的详细偏微分。

softmax 微分的实现要求我们遍历神经元列表,并对每个神经元进行微分。因此涉及两个循环。请记住,这些实现的目的不是为了提高性能,而是为了显式地翻译数学,并达到 Pytorch 的内置方法所达到的相同结果。

交叉熵损失

在神经网络所涉及的操作序列中,softmax 之后通常是交叉熵损失。事实上,这两个函数联系如此紧密,以至于 Pytorch 中的方法 cross_entropy 将两个函数合二为一。

我记得当我看到交叉熵损失的公式时的第一印象。这是接近欣赏象形文字。解读之后,我希望你能和我一样,对简单的想法有时会有最复杂的表现感到敬畏。

交叉熵损失函数。

参与计算交叉熵损失的变量有 pymK 。都以 ik 为计数器,分别从 1 迭代到 mK

  • Z : 是一个数组,其中每一行代表一个实例的输出神经元。 m : 是实例数。
  • K : 是班级人数。
  • p : 是实例 i 属于类 k. 的神经网络的概率,这与从 softmax 计算的概率相同。
  • y : 是实例 i 的标签。根据 y 是否属于类 k 为 1 或 0。
  • log : 为自然对数。

假设我们正在执行一个多类分类任务,其中可能的类的数量是三个( K =3)。每个实例只能属于一个类。因此,每个实例被分配给一个带有两个 0 和一个 1 的标签向量。例如y=【0,0,1】表示 y 的实例属于类 2。同样,y=【1,0,0】表示 y 的实例属于 0 类。1 的索引是指实例所属的类。我们说标签是一键编码****

现在我们来举两个实例( m =2)。我们计算它们的 z 值,我们发现: Z = [[0.1,0.4,0.2],[0.3,0.9,0.6]]。然后我们计算他们的 softmax 概率,发现: 激活 = [[0.29,0.39,0.32],[0.24,0.44,0.32]]。我们知道第一个实例属于类 2,第二个实例属于类 0,因为: y = [[0,0,1],[1,0,0]]。

要计算交叉熵:

  1. 我们取 softmax 激活的日志: log(激活 ) = [[-1.24,-0.94,-1.14],[-1.43,-0.83,-1.13]]。
  2. 我们乘以-1 得到负对数:-log(激活)= [[1.24,0.94,1.14],[1.43,0.83,1.13]]。
  3. 将 log(activations)乘以 y 得出:[[0。, 0., 1.14], [1.43, 0., 0.]].
  4. 所有类别的总和给出:[[0。+0.+1.14], [1.43+0.+0.]] = [[1.14], [1.43]]
  5. 所有实例的总和为:[1.14+1.43] = [2.57]
  6. 除以实例数得到:[2.57 / 2] = [1.285]

观察:

  • 步骤 3 和 4 相当于简单地检索目标类的负日志。
  • 第 5 步和第 6 步相当于计算平均值。
  • 当神经网络预测实例以 0.32 的概率属于目标类时,损失等于 1.14。
  • 当神经网络预测实例以 0.24 的概率属于目标类时,损失等于 1.43。
  • 我们可以看到,在这两种情况下,网络未能给正确的类最高的概率。但是与第一个实例相比,网络更确信第二个实例不属于正确的类。因此,它被处以 1.43 英镑的更高损失。

在交叉熵的实现中,我们结合了上述步骤和观察结果。像往常一样,在比较两个输出之前,我们还将经历 Pytorch 等效方法。

注意:我们不是存储标签的独热编码,而是简单地存储 1 的索引。比如之前的 y 变成了【2,0】。注意,在索引 0 处, y 的值是 2,而在索引 1 处, y 的值是 0。使用索引 y 及其值,我们可以直接检索目标类的负日志。这是通过访问第 0 行第 2 列和第 1 行第 0 列的-log(activations)来完成的。这使得我们可以避免第 3 步和第 4 步中浪费的乘法和零加法。这个技巧叫做整数数组索引,杰瑞米·霍华德在他的《基础深度学习》第 9 讲 34:57 中解释了这个技巧**

如果向前穿过神经网络的层可以被看作是获取某种知识的旅程,那么这里就是可以找到知识的地方。使用损失函数的微分可以通知神经网络它在每个实例上有多少误差。把这个误差倒过来看,神经网络可以自我调整。

交叉熵微分。

在回忆了几个微分规则后,我们将经历交叉熵的微分步骤:

回想一下这两条微分法则。还记得 lnlogbasede**是一样的。 e 贯穿全文。****

交叉熵微分步骤。

我们还不能用 Pytorch 的输出来评估下面的实现。原因要追溯到 Pytorch 的 cross_entropy 结合 softmax 和 cross-entropy。因此,使用向后也会涉及链规则中 softmax 的微分。我们将在下一节“反向传播”中讨论和实现这一点。现在,这是我们对交叉熵的实现:

反向传播

对于我们讨论的每一个函数,我们都在神经网络的层中前进了一步,并且我们还利用函数的微分进行了等效的后退。由于神经网络在往回走之前一直向前移动,我们需要讨论如何连接我们的功能。

向前

一路向前,具有一个隐藏层的神经网络从将输入馈送到线性函数开始,然后将其输出馈送到非线性函数,然后将其输出馈送到损失函数。以下是具有实例 x 的示例,其对应的标签 y ,三个线性神经元 z ,每个神经元使用其三个权重 w 和一个偏置b***计算,后跟一个 softmax 激活层和一个交叉熵损失。*****

向后的

一路回溯,同一个神经网络从给予损失函数的相同输入开始,并将其馈送给该损失函数的导数。损失函数的导数的输出就是误差,我们称之为获得的知识。为了调整其参数,神经网络必须将该误差再向后一步带到非线性层,并从那里再向后一步到线性层。

下一步不是简单地将误差反馈给非线性函数的导数。我们需要使用链式法则(我们之前在 sigmoid 的微分中回忆过),我们还需要注意我们应该给每个导数的输入。

前馈和反向传播的关键规则:

  • 函数及其导数接受相同的输入。
  • 函数向前发送它们的输出,作为下一个函数的输入。
  • 导数将其输出向后发送,以乘以前一个导数的输出。

愿你的机器的输出总是与你的数学相一致。

结论

我的印象是,很多来自不同学科背景的人都对机器学习充满好奇和热情。不幸的是,有一种合理的趋势,即在试图远离令人生畏的数学的同时获取知识。我认为这很不幸,因为我相信许多人实际上渴望加深他们的理解;要是他们能找到更多的资源就好了,因为他们来自不同的背景,可能到处都需要一点提醒和一点鼓励。

这篇文章包含了我写读者友好的数学的尝试。我指的是提醒读者需要遵循的规则的数学。我的意思也是数学和方程,避免跳过这么多步骤,让我们思考一行和下一行之间发生了什么。因为有时候我们真的需要有人牵着我们的手,和我们一起走过陌生概念的领域。我真诚地希望我能牵到你的手。

参考

米(meter 的缩写))胺,神经网络的内部运作,我的 Colab 笔记本 (2020)。
M. Amine,神经网络的内部工作原理,我的 Gists ,(2020)。
A. Géron,用 Scikit-Learn、Keras 和 TensorFlow 进行动手机器学习,(2019)。
S. Gugger,numpy中的一个简单神经网络,(2018)。
J. Howard, Fast.ai:从基础开始深度学习第 9 课,(2019)。
py torch 文档

旧金山低收入少数民族的迁移

原文:https://towardsdatascience.com/displacement-of-low-income-minorities-in-san-francisco-af161182e347?source=collection_archive---------40-----------------------

旧金山的住房危机延续了该市低收入少数族裔面临的贫困陷阱。

旧金山是世界上房租最高的城市之一已经不是什么新闻了。高薪工作将人才吸引到一个城市,而这个城市的发展速度已经超过了可用空间。这种高租金不仅是新移民的压力来源,而且对低收入少数民族也有严重影响。一份新的报告显示,湾区房租的上涨已经将少数族裔家庭推向了该地区的边缘。这一点很重要,因为这些少数民族移居的地区缺少高质量的学校、杂货店和其他资源。与人口相比,美国的少数民族面临着更低的生活质量,因此旧金山住房危机导致的流离失所进一步加剧了不平等。本文将研究结果延伸到 2017 年,并假设随着时间的推移,流离失所和由此产生的不平等只会加深。

数据

这项研究使用了美国人口普查局 2015 年和 2017 年旧金山县的美国社区调查(ACS) 数据。所用的变量是房租中位数、家庭收入、18 岁至 65 岁年龄在贫困线以下的人口百分比(仅非裔美国人、仅亚裔美国人)和总人口。使用的变量有租金中位数、家庭收入、18 岁至 65 岁贫困线以下的人口百分比(仅非裔美国人、亚裔美国人)和总人口。数据从人口普查网站下载后,会被清理并以表格形式上传到 ArcMap。数据表然后被连接到旧金山县域。来自最后一层的数据用于创建计算和地图。计算的统计数据是每个地图多边形中花费在房租上的收入比例和生活在贫困线以下的人口百分比。

旧金山县

2018 年,旧金山县人口 883305 人(比 2017 年下降 0.12%),家庭收入中位数为 112376 美元(比 2017 年增长 1.41%),房产价值中位数为 120 万美元(增长 8.3%)。

此外,如图 1 所示,旧金山生活在贫困线以下的人口比例自 2015 年以来有所下降。这并不一定意味着人们变得更富有,但它确实暗示了生活在贫困线以下的人们的流离失所。

结果

图 2

上一节的统计数据表明,房产价值的增长速度快于收入的增长速度——旧金山县居民越来越多地将收入的更大一部分用于支付房租。图 2 和图 3 支持这一结论。

图 2 显示,2015 年至 2017 年期间,全县 65 岁以下成年人口的总百分比一直在下降,而花在房租上的收入比例一直在上升。这支持了一个普遍的说法,即房租价格的上涨速度比人们的消费倾向要快。

为了了解这是如何影响贫困的少数民族人口的,图 3 关注的是历史上亚裔人口最多的唐人街及其周边地区。然而,这个地区的亚裔贫困人口已经被转移。这一点在图 3 中很明显,该图显示该地区的房租已经上涨,贫困人口的比例也明显下降。

图 3

此外,房租上涨的街区的贫困水平也在上升,这可能是未来流离失所的先兆。还有一些街区,即使租金与收入比率群体保持不变,贫困人口的比例也有所下降。这可能意味着,即使房租没有大幅上涨,流离失所现象仍在继续。

政策,政策,政策?

旧金山郡的住房政策是寻求解决住房危机的最大挑战。目前的政策限制了可以建造的新住房的数量,这对增加住房供应是一个巨大的障碍。13 号提案也鼓励房主尽可能长时间呆在自己的房子里,以反对新的住房建设。相反,应该制定政策来允许和加速新的发展。

在 Power BI 中显示选定的切片器

原文:https://towardsdatascience.com/display-selected-slicers-in-power-bi-a99d81500e76?source=collection_archive---------11-----------------------

在报告中显示切片器选择可能比看起来容易。获得更好用户体验的额外提示!

Alex 在 Unsplash 上拍摄的照片

就在最近,在介绍我的会议时:“宏伟的 7-促进您的 Power BI 开发的简单技巧”在数据之星会议上,我收到的一个问题是:

有没有办法在页面上显示作为切片器选择结果的实际活动过滤器?

我已经在这篇文章中回答了这个问题,但后来我想:也许更多的人在寻找这个问题的解决方案,所以我决定写一个简短的帖子来详细解释你如何才能实现这一点。

像往常一样,我将使用一个示例 Contoso 数据库进行演示:

达克斯是你的朋友!

这是起点。您可以注意到报告画布上有三个切片器,假设我想向用户显示在 Brands slicer 中选择了哪些品牌。

第一步是创建一个 DAX 度量,它将捕获切片器中所有选定的值。可以使用两个不同的 DAX 函数来获取值: VALUES()DISTINCT()VALUES() 函数更复杂,因为您可以同时使用表名和列名作为参数,但是这里让我们集中讨论列名作为参数。

基本上, VALUES() 将从我们传递的列中返回所有不同的值,包括空格(如果存在)!另一方面, DISTINCT() 将返回所有不同的值,但忽略空白值。您想使用哪一个,主要取决于业务请求(如果您的用户想看到数字是否为空白)。就我个人而言,我更喜欢使用 VALUES() ,因为它给了我全貌。

因此,我将创建以下度量:

Selected Brands = VALUES('Product'[BrandName])

现在,当我将这个度量放入卡片视觉效果中时,让我们看看会发生什么:

哎呀,这给了我一个错误!错误消息说明计算需要单个值。问题是,值()截然不同()返回表!然而,它不是一个“普通”的表,它是由 DAX 引擎动态创建的虚拟表,所以我们应该应用一些额外的计算,以便从中提取单个值。

迭代器函数来拯救!

迭代器函数顾名思义——它们遍历表并逐行应用计算!迭代器函数最后有 X:SUMX,AVERAGEX,COUNTX…在我们的场景中,我们需要迭代我们的不同值,并将它们连接到我们的字符串中,稍后将在报告中显示。为了实现这一点,我们将使用concatenax()函数。

这个函数接受三个参数:第一个是我们想要迭代的表(在我们的例子中,是使用 VALUES() 函数创建的虚拟表),然后是我们在这个表上逐行应用的表达式,最后是我们想要用来分隔提取值的分隔符。

Selected Brands = CONCATENATEX(
                            VALUES('Product'[BrandName]),
                            'Product'[BrandName],
                            ",")

在我的例子中,我使用逗号作为分隔符,但你也可以使用其他的,如分号等。现在,当我再次查看我的报告时,我可以看到我得到了想要的结果:

而且,如果我在切片机中只选择了几个品牌,我的卡将进行调整以反映这些变化:

结合多种措施

您还可以为其他切片器创建相同的度量,然后将所有结果放入可视化表格中。因此,我将为 Year slicer 创建度量值:

Selected Year = CONCATENATEX(
                            VALUES(Dates[Year])
                            ,Dates[Year]
                            ,",")

现在,当我将我的度量拖到表字段中时,我在一个地方看到了我选择的两个切片器的值:

额外想法!

如果您的报告中有许多切片器,并且您想要显示所有选择,但是您不想浪费报告画布上的空间,您可以创建一个书签,其中包含具有所有选定值的表,然后只在用户请求时显示书签。

大概是这样的:

多酷啊!因此,您不需要担心使用这个表会增加您的报告空间。您可以使用书签和操作来显示/隐藏它。

成为会员,阅读媒体上的每一个故事!

订阅这里获取更多有见地的数据文章!

剖析欧洲制药发展的 28 年

原文:https://towardsdatascience.com/dissecting-28-years-of-european-pharmaceutical-development-3affd8f87dc0?source=collection_archive---------56-----------------------

数据新闻

对从欧洲药品管理局(EMA)收集的数据的探究

micha Parzuchowski 在 Unsplash 上的照片

自从抗生素被发现以来,新药的出现大大改善了我们的生活。药物开发需要高额投资,同时也伴随着高度的不确定性,因为我们需要从逻辑上证明候选新药的安全性和有效性,然后才能在人体内自由使用。因此,制药公司需要通过社会健康需求、国际法律框架和商业动机找到一条成功之路。

T4 欧洲药品管理局(EMA)是指导药物开发并决定是否批准一种新药在欧洲上市的官方监管机构。在他们的网页上,他们存储了每一种批准的药物及其相应的上市许可持有人(MAH)公司的信息。

为了更好地理解成功的欧洲生物技术公司和欧洲药品管理局为继续开发新的有用药物而实施的战略,我使用我之前从欧洲药品管理局关于他们的 1222 种药物的网页上删除的信息进行了探索性分析。

EMA 对孤儿药开发的有效倡导

图片由 作者

根据 EMA,当一种药物“旨在用于治疗罕见疾病”时,该药物将获得“孤儿名称”状态。不幸的是,由于少数人患有这些疾病,他们很可能被忽视,因为他们代表了一个小市场。2000 年,欧洲药品管理局制定了一系列激励措施(见法规),鼓励那些致力于开发孤儿药的公司为那些有被忽视高风险的治疗领域带来经济利益。基本上,它们提供了一种特殊的处理方式,可以加速官僚程序,降低整体成本。事实上,我们立即看到了这一政策的有效性,因为批准的孤儿药数量自那时以来稳步增加。显然,我们看不到有多少等同于孤儿的药物在其定义之前被批准,但趋势似乎很明显。

复杂和流行的疾病仍然是制药公司的兴趣所在

在欧洲药品管理局授予上市许可的 414 个不同治疗领域中,近 20%的获批药物仅试图治疗下图中列出的 9 种疾病。

图片由 作者

所有这些疾病有什么共同点?它们的生物复杂性;它们是由多种因素引起的,这些因素在人群中可能非常不同。因此,缺乏对它们如何工作的完整理解使得发现和开发适合许多患者的药物变得极其困难。另一方面,制药公司是否根据疾病的复杂性选择只应对哪种疾病?可能,如上所述,他们还考虑了患有这些疾病的人数,这代表了疾病本身的社会影响和药物的市场规模。例如,2018 年,3420 万美国公民患有糖尿病,其中至少 90%的病例对应于二型糖尿病这一事实可以解释为什么大多数批准的药物都适用于治疗糖尿病。特别是对于复杂的疾病,我们希望看到这些不断尝试开发新的更好的药物,以进入一个大市场,并能够增加市场份额。看起来是双赢。

出于好奇,我进一步调查了 2005 年至 2010 年间心肌梗死药物出现峰值的原因。它是由基于氯吡格雷的 28 种不同仿制药的批准引起的。

成功公司的药物开发策略

制药公司持有的批准上市授权的数量可以被视为成功的衡量标准,因为它表明了每个公司在药物开发过程中的表现。

图片由 作者

著名的诺华公司以每年几乎 3 种的速度批准了 59 种药物,其中没有一种是仿制药。相反,雅阁,第二个批准率最高的制药公司,仅在 7 年内就批准了 16 种药物。但是,它们中的大多数(16 个中的 15 个)是仿制药,这意味着比开发一种全新的药物需要更少的研究和要求。这是两种截然不同的战略的两个例子:诺华完全选择创新,而雅阁选择生产许多仿制药,同时开发较少的新药。

多样化或者死亡,真的吗?

风险资本家的一个常见做法是分散投资以分散风险。同样,制药公司必须选择他们想要解决的疾病。

图片由 作者

为不同治疗领域开发药物确保了在许多市场占有份额。我们看到,拥有高批准率和大量总批准药物的公司也拥有涉及许多治疗领域的药物,这些药物可能大多与药物开发的业务方面有关。

图片由 作者

事实上,大多数制药公司选择开发与治疗领域相同比例的药物或选择具有多种适应症的候选药物。然后,在首次适应症的药物被批准后,他们不需要再次经历整个过程并增加利润。

不足为奇的是,很少有公司专门开发同样适应症的药物,因为这是一种依赖于竞争对手数量和质量的高风险“商业选择”。

然而,治疗领域的多样化可能成为一把双刃剑。一方面,为批准的药物寻找新的适应症,也称为“药物再利用”,最大限度地利用和循环利用从工作台到床边花费的资源,并允许增加许多疾病的可用治疗方案,这有助于应对疾病并发症和副作用。另一方面,如果一种药物不能改变用途,大多数生物技术公司可能不得不重新开发治疗不同疾病的新药,以保持业务。因此,与其利用他们的专业知识,他们可能会“重新利用”他们训练有素的团队,专注于一个新的、不同的目标,并失去在以前的治疗领域获得的部分经验。

多样化可能会减缓药物开发的整体进展,以满足商业需求。然而,现有模式的替代方案是什么?我们能改变它吗?

用 R 进行探索性数据分析

对于那些希望复制或进一步分析这些数据的人。

0.加载所需的库:

**require**(tibble)
**require**(readr)
**require**(stringr)
**require**(ggpubr)

1.准备和争论数据:

*# download data*
urlEMA = 'https://raw.githubusercontent.com/MiqG/EMA-Data-Scratching-with-RSelenium/master/drug_df_EMA.csv'
ema = read_csv(urlEMA)# subset features of interest and change their names
featuresOI = c('Name',
               'Active substance',
               'International non-proprietary name (INN) or common name',
               'Generic',
               'Orphan',
               'Exceptional circumstances',
               'Countries',
               'Marketing-authorisation holder',
               'Therapeutic area (MeSH)',
               'Date of issue of marketing authorisation valid throughout the European Union')newColNames = c('name',
                'substance_act',
                'substance_inn',
                'generic',
                'orphan',
                'exceptional_circ',
                'country',
                'mah',
                'mesh',
                'date')ema = ema[featuresOI]
colnames(ema) = newColNames
ema$date = strptime(ema$date, '%d/%m/%Y')
ema$year = as.numeric(format(ema$date, '%Y'))
ema$generic = !is.na(ema$generic)
ema$orphan = !is.na(ema$orphan)
ema = ema[!is.na(ema$country),] *# unsuccessful applications will have an empty Country*

2.汇总数据:

# study therapeutic areas
## list drugs and their therapeutic area (MeSH)
mesh = lapply(ema$mesh, function(meshTerms) unlist(strsplit(meshTerms, split = '\n')) )
names(mesh) = ema$name
## get their frequency percentages
mesh = table(unlist(mesh))
mesh = mesh / sum(mesh) * 100
mesh = sort(mesh, decreasing = TRUE)
mesh = tibble(therArea = names(mesh),
              perc = mesh)# study approved drugs per therapeutic area per year
years = min(ema$year, na.rm = TRUE):max(ema$year, na.rm = TRUE)
therArea_perc = paste0(mesh$therArea,' (',round(mesh$perc,2),'%)')
meshApproved = expand.grid(therArea_perc = factor(therArea_perc, levels = therArea_perc), 
                           year = years)
meshApproved$therArea = gsub(' \\(.*','',as.character(meshApproved$therArea_perc))
meshApproved$nApprovedDrugs = sapply(1:nrow(meshApproved), function(idxRow){ 
    sum( str_count(ema$mesh[ema$year == meshApproved$year[idxRow]], pattern = as.character(meshApproved$therArea[idxRow])), na.rm = TRUE) 
    })# study pharmaceutical companies
mahCount = sort(table(ema$mah), decreasing = TRUE)
mahCount = tibble(nApprovedDrugs = as.numeric(mahCount), mah = factor(names(mahCount), levels = names(mahCount)))
mahCount$deltaTime = sapply(mahCount$mah, function(mah) max(ema$year) - min(ema$year[ema$mah == mah]) )
mahCount$approvalRate = (mahCount$nApprovedDrugs) / (mahCount$deltaTime)
mahCount$approvalRate[mahCount$deltaTime <= 5] = 0
mahCount$weightedApprovalRate = mahCount$approvalRate * mahCount$nApprovedDrugs
mahCount$nTherAreas = sapply(mahCount$mah, function(mah) length(unique(unlist(strsplit(ema$mesh[ema$mah == mah], split = '\n')))) )
mahCount$module = sqrt((mahCount$approvalRate/sum(mahCount$approvalRate))^2 + (mahCount$nTherAreas/sum(mahCount$nTherAreas))^2)
mahCount$nTherAreaPerDrug = mahCount$nTherAreas / mahCount$nApprovedDrugs
mahCount$strategy = NA
mahCount$strategy[mahCount$nTherAreaPerDrug > 1] = '>1'
mahCount$strategy[mahCount$nTherAreaPerDrug == 1] = '=1'
mahCount$strategy[mahCount$nTherAreaPerDrug < 1] = '<1'

3.制作图表:

# variables
baseSize = 9# orphan drugs
perc = round(sum(ema$orphan)/nrow(ema) * 100,2)
figTitle = paste0('Approved orphan drugs (',perc,'%)')
plt1 = ggplot(ema, aes(x = orphan, fill = orphan)) + 
       geom_bar() +
       scale_fill_viridis_d() +
       ggtitle(figTitle) +
       theme_pubr(legend = 'none', base_size = baseSize)
figTitle = 'Evolution of orphan drugs'
plt2 = ggplot(ema, aes(x = year, fill = orphan)) + 
       geom_bar() +
       scale_fill_viridis_d() +
       ggtitle(figTitle) +
       theme_pubr(legend = 'bottom', base_size = baseSize)
ggarrange(plt1, plt2, common.legend = TRUE)# therapeutic areas
n = 9
figTitle = paste0('Evolution of drug approvals in top ',n,' therapeutic areas')
idxTherAreas = meshApproved$therArea %in%  mesh$therArea[1:n]
ggline(meshApproved[idxTherAreas,], x = 'year', y = 'nApprovedDrugs', color = 'therArea', 
       facet.by = 'therArea_perc', point.size = 0.1,
       title = figTitle, linetype = 'dashed', palette = 'jco') +
       theme_pubr(base_size = baseSize, x.text.angle = 45, legend = 'none')# pharmas
## n mahs and approval rates
n = 10
mahCount = mahCount[order(mahCount$nApprovedDrugs, decreasing = TRUE),]
levels(mahCount$mah) = mahCount$mah
figTitle = paste('Top 10 Marketing Authorization Holders')
plt1 = ggplot(mahCount[1:n,], aes(x = mah, y = nApprovedDrugs)) + 
       geom_bar(stat = 'identity',fill='orange') +
       ggtitle(figTitle) +
       theme_pubr(legend = 'bottom', x.text.angle = 67, base_size = baseSize)dat = mahCount[!is.infinite(mahCount$approvalRate),]
dat = dat[order(dat$approvalRate, decreasing = TRUE),]
dat$mah = factor(dat$mah, levels = dat$mah) 
figTitle = paste('Top 10 Approval Rate')
plt2 = ggplot(dat[1:n,], aes(x = mah, y = approvalRate)) + 
       geom_bar(stat = 'identity',fill='orange') +
       ggtitle(figTitle) +
       theme_pubr(legend = 'bottom', x.text.angle = 67, base_size = baseSize)figTitle = paste('Marketing Authorization Holders out of', nrow(mahCount), 'companies')
annotate_figure(ggarrange(plt1, plt2), top = figTitle)
## diversification
### scatter
topNmah = mahCount$mah[order(mahCount$module, decreasing = TRUE)][1:4]
plt1 = ggscatter(mahCount, x = 'approvalRate', y = 'nApprovedDrugs',
          add = 'reg.line', conf.int = TRUE, add.params = list(linetype = 'dashed', fill = 'lightgray'),
          label = 'mah', label.select = topNmah, font.label = c(8, 'darkorange'), 
          repel = TRUE, label.rectangle = FALSE, size = 0.5, alpha = 0.75,
          theme = theme_pubr(base_size = baseSize)) +
          stat_regline_equation(label.x = 2, label.y = 5, size = 3)plt2 = ggscatter(mahCount, x = 'approvalRate', y = 'nTherAreas',
          add = 'reg.line', conf.int = TRUE, add.params = list(linetype = 'dashed', fill = 'lightgray'),
          label = 'mah', label.select = topNmah, font.label = c(8, 'darkorange'), 
          repel = TRUE, label.rectangle = FALSE, size = 0.5, alpha = 0.75,
          theme = theme_pubr(base_size = baseSize)) +
          stat_regline_equation(label.x = 2, label.y = 5, size = 3) 
figTitle = paste('Diversification in therapeutic areas of\nsuccessful pharmaceutical companies')
annotate_figure(ggarrange(plt1, plt2), top = figTitle)
### bar plot
figTitle = 'Therapeutic areas per approved drug\nacross European pharmas'
ggplot(mahCount, aes(strategy, fill = strategy)) +
  geom_bar() +
  ggtitle(figTitle) +
  theme_pubr(x.text.angle = 0, legend = 'none', base_size = baseSize)

用 Python 和 Quandl 剖析失业数据

原文:https://towardsdatascience.com/dissecting-unemployment-data-with-python-and-quandl-4d6d5d0fcdcd?source=collection_archive---------55-----------------------

使用 Quandl 下载美联储经济数据,并按种族、性别、年龄和教育程度绘制失业率图

4 月份的失业率上升到 14.7%,这是自 1948 年以来的最高数字。美国劳工部报告说,所有主要行业的就业人数都大幅下降,休闲和酒店业的失业情况尤为严重。这促使我分析与其他人口特征相关的失业数据,以了解冠状病毒关闭如何影响美国工人。

通过 Quandl 访问 FRED

佛瑞德(【https://fred.stlouisfed.org】)由圣路易斯美联储银行的研究部门创建和维护,是一个在线数据库,由来自数十个国家、国际、公共和私人来源的数十万个经济数据时间序列组成。Python 中的 Quandl 包使得从包括 FRED 在内的多个数据源获取金融和经济数据变得很容易。

要使用 Quandl 访问数据,用户需要创建一个免费的 Quandl 帐户并请求一个 API 密钥。可以在帐户设置页面上找到 API 密钥。

一旦有了 API 密钥,就可以设置 API 密钥:

quandl.ApiConfig.api_key = “insert_your_api”

现在,我们准备好下载弗雷德数据和可视化。

按种族分列的失业率

下面的代码片段显示了如何检索多列 FRED 数据。

所有种族的失业率都急剧上升。然而,西班牙裔和黑人比白人和亚洲人受到的打击更大。

按性别分列的失业率

女性失业率几乎比男性高 3 个百分点。

按种族和性别分列的失业率

西班牙裔女性受到的打击最大,其次是黑人女性和西班牙裔男性。

按年龄分列的失业率

年轻年龄组的失业率高于年长年龄组。

按教育分列的失业率

失业率和受教育程度高度相关。受过教育的工人(大学以上)相对于教育程度较低的工人失业率较低。

摘要

Quandl Python 模块可以轻松下载失业等经济数据并分析趋势。尽管所有人群都感受到了历史最高的失业率,但一些人受到的冲击比其他人更大。受打击较大的人群包括受教育程度较低的人、女性、西班牙裔/黑人和较年轻的人群。

神经网络中知识的提炼

原文:https://towardsdatascience.com/distillation-of-knowledge-in-neural-networks-cc02f79698b6?source=collection_archive---------6-----------------------

在更小更快的型号上实现最先进的性能

知识的提炼(在机器学习中)是一种架构不可知的方法,用于在一个神经网络内概括知识(巩固知识)以训练另一个神经网络。

重要

目前,特别是在 NLP 中,正在训练非常大规模的模型。其中很大一部分甚至无法安装在普通人的硬件上。此外,由于收益递减规律,模型规模的巨大增加几乎不能映射到精确度的微小增加。

这些模型几乎不能在商业服务器上运行,更不用说在智能手机上了。

使用蒸馏,人们可以将像 BERT 这样的模型的大小减少 87%,而仍然保持其 96%的性能。

基本上,蒸馏使人能够:

  • 获得一流的精度
  • 只有很小一部分
  • 在很短的响应时间内
  • 在微调时间的一小部分内
  • 在单个 GPU 上安装合奏
  • 可以在 CPU 上运行模型
  • 支持联盟学习
  • 等等

普通神经网络的问题

每个学习者的目标是优化其在训练数据上的性能。这并不能完全解释为数据集中知识的概括。

以 MNIST 数据集为例。我们来选一张 3 号的样图。

在训练数据中,数字 3 转化为相应的一个热点向量:

0 0 1 0 0 0 0 0 0 0 0

这个向量简单地告诉我们这个图像中的数字是 3,但是

没有明确提到任何关于数字 3 的形状。像 3 的形状类似于 8。

因此:

从来没有明确要求神经网络学习对训练数据的一般理解。泛化程度就是神经网络的隐含能力。

因此,在正常训练的神经网络中,每个神经元内的信息(检测到的特征)相对于期望的输出并不同等重要。

简而言之,正常训练的神经网络以神经元的形式承载了大量的负载,这些神经元从未学会概括数据,因此导致测试数据的准确性降低。

蒸馏

蒸馏使我们能够使用预训练的网络来训练另一个神经网络,而没有原始神经网络的自重。

使我们能够压缩网络的规模而不损失太多的准确性。

因此,经过提炼的模型比正常训练的模型具有更高的精确度。

注意:知识的提炼可以从任何形式的学习者(逻辑回归,SVM,神经网络等)到任何其他形式的学习者。

虽然为了博客的简单,我将只引用神经网络。

信息的概括

让我们后退一步,修改神经网络的目标:

通过归纳训练数据中的知识,预测网络在训练期间从未见过的样本的输出。

以鉴别神经网络为例,其目标是识别给定输入的相关类别。

现在,神经网络返回所有类别的概率分布,甚至是错误的类别。

这告诉我们很多关于网络在训练数据中概括概念的能力。

一般化的度量

对于 MNIST 上训练有素的神经网络来说,以下观察将是正确的:

  • 即使数字 3 的概率明显大于数字 8 和数字 0 的概率
  • 8 和 0 的概率是相当的
  • 8 和 0 的概率比其他数字更高

因此,神经网络能够识别图像中数字 3 的形状,但是神经网络还表明 3 的形状与数字 8 和 0 的形状非常相似(都非常弯曲)

信息概括过程

不,你实际上不需要所有这些设备。这只是有趣和相关的。

让我们从显而易见的开始,以正常的方式训练一个巨大的神经网络(你的硬件可以支持)。我们将把这个网络称为繁琐网络

注意:这个笨重的模型很可能是多个正常训练的模型的集合。

参考资料:

  • 软目标:网络在所有类别中的概率/权重分布
  • 硬目标:原始训练数据内的一键向量表示

当繁琐模型不是单个模型而是多个模型的集合时,其输出的算术/几何平均值作为软目标。

新模型可以在与原始模型相同的数据集上训练,也可以在称为“转移集”的不同数据集上训练。

Transfer-Set:通过繁琐的模型传递数据,并使用其输出(概率分布)作为各自的真值。它可以由用于训练原始模型的数据集、新数据集或两者组成。

温度和熵

通过调整软目标的温度,可以调整该转移集的大小。

当软目标具有高熵时,它们在每个训练样本上比硬目标给出更多的信息(让我们马上回到这个话题)。

这导致:

  • 损失更小,因此校正梯度更小(反向传播)
  • 不同训练示例的梯度之间的差异较小

因此:

  • 可以使用更大的学习速率来训练模型
  • 可以使用较小的数据集来训练模型

更多关于温度

我们在物理学中学到的东西在这里适用'随着温度'增加。

让我们打个比方:

想象一个盒子,里面的球一个接一个堆叠在一起。如果我们通过摇动盒子来增加它的熵,球不会从盒子里掉出来,而是会散开一点。

得到的分布具有与原始分布相似的形状,但是其峰值的大小发生了变化。

然而,在更高的温度下,一定量的热量添加到系统中会导致比相同量的热量在更低的温度下产生更小的变化。

下面是一个概率分布的例子,比较一个系统的低温和高温。

现在,我们用温度公式将知识从一个神经网络转移到另一个神经网络:

  • ᵢ:结果概率
  • ᵢ:一个阶级的逻辑
  • ⱼ:其他逻辑
  • t:温度

Logits :分类模型生成的原始(非标准化)预测向量,通常会传递给标准化函数。如果模型正在解决多类分类问题,logits 通常会成为 softmax 函数的输入。

好吧,想象一下下面的场景:

想象一个 MNIST 数据点的数字 3(是的,我喜欢数字 3),但是让我们只关注 3 个(是的是的)类:

  • 0
  • 3
  • 8

数据集值 : 0,1,0

逻辑值(神经网络输出) : 0.1,0.7,0.2

温度 0.5: 0.1831659252,0.5939990325

温度 1: 0.254628528,0.4639634285

温度 2: 0.294019937,0.3968854016

温度 5: 0.3176924658,0.358197259,0.35825357

更高的温度导致更柔和的类概率分布。

现在,让我们想象一下随着温度的升高,概率分布的平滑度:

训练蒸馏模型

最简单的蒸馏形式是使用由具有高温的笨重模型产生的软目标来训练模型,并将其蒸馏到具有相同温度的另一个模型中。

在训练之后,提取的模型的温度被设置为 1。

到目前为止,我们已经确定,提取的网络可以在由软目标组成的转移集上训练。

我们还可以利用所有或部分数据已知的真实值或硬目标。

最有效的方法之一是使用两个目标函数:

  • 使用高温笨重模型的软目标的交叉熵
  • 交叉熵与硬目标使用相同的繁琐模型,但温度设置为 1

注意:软目标的幅度被缩放到 i/T 倍,而硬目标没有这样的缩放。因此,我们将软目标乘以 T,以标准化软目标和硬目标的影响。

密码

HuggingFace 实际上提供了脚本来训练你自己的distil Bert&distil Roberta,它的体积小了 40%,速度快了 60%,同时保留了原始模型 99%的准确性。

要获取存储库,请使用:

git clone [https://github.com/huggingface/transformers.gi](https://github.com/huggingface/transformers.git)t

然后

cd transformers/examples/distillation

首先,我们将对数据进行二进制化,也就是说,对数据进行标记化,并在模型词汇表的索引中转换每个标记。

python scripts/binarized_data.py \
    --file_path data/dump.txt \
    --tokenizer_type bert \
    --tokenizer_name bert-base-uncased \
    --dump_file data/binarized_text

《拥抱脸》沿袭了《T4》和《XLM》的风格,通过一个更加强调生僻字的因素,平滑了屏蔽的可能性。因此,计算数据中每个标记的出现次数:

python scripts/token_counts.py \
    --data_file data/binarized_text.bert-base-uncased.pickle \
    --token_counts_dump data/token_counts.bert-base-uncased.pickle \
    --vocab_size 30522

一旦对数据进行预处理,蒸馏训练就变得非常简单:

python train.py \
    --student_type distilbert \
    --student_config training_configs/distilbert-base-uncased.json \
    --teacher_type bert \
    --teacher_name bert-base-uncased \
    --alpha_ce 5.0 --alpha_mlm 2.0 --alpha_cos 1.0 --alpha_clm 0.0 --mlm \
    --freeze_pos_embs \
    --dump_path serialization_dir/my_first_training \
    --data_file data/binarized_text.bert-base-uncased.pickle \
    --token_counts data/token_counts.bert-base-uncased.pickle \
    --force # overwrites the `dump_path` if it already exists.

结论

通过提取神经网络,我们获得了一个更小的模型,它与原始模型有很多相似之处,同时更轻、更小、运行速度更快。

因此,提取模型是将大规模神经网络投入生产的一个有趣的选择。

使用无标签问答数据集提取 BERT

原文:https://towardsdatascience.com/distilling-bert-using-unlabeled-qa-dataset-4670085cc18?source=collection_archive---------29-----------------------

如何利用知识提炼将未标记数据用于问答任务

照片由阿尔方斯·莫拉莱斯Unsplash 拍摄

数据标注过程相当复杂,尤其是机器阅读理解(问答)等任务。在这篇文章中,我想描述一种技术,我们使用有限的标记数据来使问答模型适应特定的领域——T4 知识蒸馏 T5。事实证明,我们不仅可以使用它来“压缩”模型,还可以利用域内未标记的数据。

问答和小队📖

最简单的问答系统形式之一是机器阅读理解(MRC)。这里的任务是在提供的文档中找到一个问题的简短答案。MRC 最流行的基准是斯坦福问答数据集(SQuAD) [1]。它包含 100,000 个问答对和 53,775 个无法回答的问题,这些问题来自维基百科的热门文章的 23,215 个段落。每个问题的答案都是来自相应阅读文章的一段文字(一个跨度)。对于无法回答的问题,系统应确定该段落何时不支持任何答案,并放弃回答。

2.0 班中可回答和不可回答问题的示例

还有其他可用的问答数据集,如 Natural Questions 和 MS MARCO,但 SQuAD 2.0 是使用最多的数据集之一,也是我们项目的起点。

评估预训练模型👩🏻‍🔬

最近,我们为一个照片&摄像机在线商店开发了一个问题回答系统,在那里我们训练了一个机器阅读理解模型。在我们的项目中,我们测试了各种预先训练的问答模型(感谢🤗拥抱脸),并发现在 SQuAD 2.0 上训练的 ALBERT-xxlarge【2】在我们的域上显示了有希望的结果:****

使用在 SQuAD 2.0 (图片由作者提供)上预先训练的模型的测试数据集的结果

但是这个模型太慢了,我们不能在生产中使用它。在这种情况下,常用的方法之一是使用知识蒸馏**

知识升华⚗️

当我们想要训练更快或更小的模型时,知识蒸馏[3]通常被用作模型压缩技术。在这个过程中,我们使用我们主要的较大模型(老师)的输出概率来训练一个较小的模型(学生),因此一个学生开始“模仿”老师的行为。基于比较产出分布的损失比仅基于硬目标的损失要丰富得多。这个想法是,通过使用这样的软标签,我们可以从教师模型中转移一些“黑暗知识”。此外,从软标签中学习可以防止模型对其预测过于确定,这类似于标签平滑[4]技术。****

知识蒸馏(图片作者)

知识提炼是一项非常漂亮的技术,效果出奇地好,对变形金刚尤其有用——更大的模型往往显示出更好的结果,但很难将如此大的模型投入生产。

使用未标记数据的蒸馏🔮

在 SQuAD 上提取或训练的模型在我们的数据集上没有显示出竞争结果。小队数据集与我们的领域没有重叠,所以这样的提取效果不好。似乎在 SQuAD 上训练的较大模型在域外数据上工作得更好。为了使提炼过程有效,我们需要使用来自我们领域的数据集。除了我们的小标签数据集,我们还有大约 30,000 个未标签的(没有突出显示的答案)问题-文档对,我们考虑如何使用它们。

我们蒸馏需要什么?一个老师和一个带标签的数据集。ALBERT-xxlarge 可以当老师模特。对于我们的 30K 示例,我们没有标签,但是我们可以删除使用标签的部分损失吗?当然,在没有接地标签的知识提炼过程中,我们会继承更多老师的错误。但是目前我们没有比 ALBERT-xxlarge 更好的模型,所以即使用更小的模型得到类似的结果对我们也是有用的。因此,我们试图仅使用未标记的 30K 个示例,从 ALBERT-xxlarge 到 ALBERT-base 提取知识。

知识蒸馏透地标签(图片由作者提供)

使用在 SQuAD 2.0 (图片由作者提供)上预先训练的模型的测试数据集的结果

正如你所看到的,我们用 F1/EM 接近它的老师得到了 ALBERT-base,并且我们没有使用任何标记的数据进行训练。当然,这并不意味着我们不再需要标记的数据,分数仍然远不理想,我们也继承了老师的错误,所以添加标记的数据可能会改善这个训练程序。

蒸馏作为预培训🏋🏻‍♂️

我们还可以考虑将蒸馏作为额外的预训练步骤,以便在使用标记数据时实现更好的样品效率。在下面,您可以看到,在我们的例子中,蒸馏有助于进一步微调带标签的数据。

使用在 SQuAD 2.0 上预训练并在域内标记数据上微调的模型的测试数据集上的结果(~1000 个标记示例)(图片由作者提供)**

自蒸馏👯‍♀️

你的老师越好,你就能培养出越好的学生。因此,一个主要的方向可以是改进教师模式。除了使用我们的标记数据集,当我们对学生和教师使用相同的模型架构时,还有另一种令人兴奋的技术— 自我升华。自我升华让我们训练出一个比老师表现更好的学生模型。这听起来可能很奇怪,但是因为学生从老师没有看到的数据中更新其权重,这可以导致学生(可比大小)在来自该分布的数据中表现稍好。当我们对 ALBERT-xxlarge 应用自蒸馏,然后使用它作为我们的教师之一来进一步蒸馏到一个更小的模型时,我们的实验也再现了这种行为。

合奏👯‍♀️️️👯‍♂️

当然,提炼训练程序允许我们有效地使用教师模型的集合,并将它们提炼为一个更小的模型。结合所有这些方法(见下文)和领域自适应语言预训练,我们能够使用有限数量的标记示例获得良好的结果。

我们培训管道的所有步骤 (图片由作者提供)

等等,我们的数据真的没有标签吗?🤔

值得一提的是,即使我们没有给任何数据贴上蒸馏的标签,我们仍然有疑问,但情况并非总是如此。与文本分类和 NER 等 NLP 任务相比,问答模型的输入由一个问题和一个文档组成。尽管问题是一个输入,但它可以被视为数据标注过程的一部分(你必须写一个问题,而不只是突出显示一个答案)。这样,真正无标签的 QA 数据集是当我们只有没有问题的文档时。

但是,即使我们有问题,我们也没有手动将每个问题准备到一个特定的文档中。我们使用预先训练的 USE-QA 模型收集了这 30,000 个与一组独立文档的问题相匹配的 QA 对。通过这种方式,我们可以从预先训练的模型开始,并在生产中进一步改进模型。在我们开始收集与我们的系统交互的用户提出的真实问题后,我们可以以同样的方式找到这些问题的候选文档,并使用这个数据集进行知识提炼,而无需标记许多示例。

使用从已部署系统收集的问题(图片由作者提供)**

🛠实验装置

我们使用了来自的示例🤗拥抱面部变形金刚进行知识提炼,1x NVIDIA 2080TI 用于我们所有的实验。梯度累积允许我们使用更大的模型,如 ALBERT-xxlarge 和 RoBERTa-large,以及混合精度(fp16)来更快地训练模型。为了在一个 2080TI 上对 ALBERT-xxlarge 进行自我蒸馏,我们首先预计算了老师的预测(软标签)。

结论

知识提炼是一种方便的技术,它允许我们“压缩”巨大的变压器模型,以便在生产中使用它们。我们还可以以对未标记数据进行预训练的形式使用它,这有助于在对标记样本进行微调时提高样本效率。这对于机器阅读理解等任务尤其有用,在这些任务中,标记数据的过程相当复杂。使用未标记的数据进行提炼并不是一个新颖的想法,它已经在计算机视觉( SimCLRv2嘈杂的学生)和 NLP ( 阅读能力强的学生学得更好)中显示出良好的结果。

如果您想了解更多关于我们构建问答系统的旅程,请查看我们更详细的帖子,其中我们描述了我们如何收集和标记数据,微调模型,以及应用各种技术,如知识提取和修剪:

**** [## 我们如何使用 BERT 为网上商店构建问答系统

在这篇博文中,我们描述了我们基于 Transformer 模型构建问答系统的经验,比如…

blog.griddynamics.com](https://blog.griddynamics.com/question-answering-system-using-bert/)****

要了解更多关于知识提炼和其他压缩方法的信息,我推荐以下资源:

以下资源将帮助您深入机器阅读理解任务:

[1] Pranav Rajpurkar,Robin Jia, Percy Liang,知道你不知道的:对小队的无法回答的问题 (2018),ACL 2018

[2]·兰,陈明达,塞巴斯蒂安·古德曼,凯文·金佩尔,皮尤什·夏尔马,拉杜·索里库特,阿尔伯特:一个用于语言表征自我监督学习的 Lite BERT(2019)

[3] Geoffrey Hinton,Oriol Vinyals,Jeff Dean,在神经网络中提取知识 (2015),NIPS 2014 深度学习研讨会

[4] Rafael Müller,Simon Kornblith,Geoffrey Hinton,标签平滑在什么情况下有帮助? (2019),NeurIPS 2019

提取神经网络中的知识

原文:https://towardsdatascience.com/distilling-knowledge-in-neural-network-d8991faa2cdc?source=collection_archive---------7-----------------------

学习用较小的模型学得更好

在传统的机器学习中,为了达到最先进的(SOTA)性能,我们经常训练一系列集成模型来克服单个模型的弱点。然而,实现 SOTA 性能通常伴随着使用具有数百万个参数的大模型的大计算。像 VGG16/19、ResNet50 这样的 SOTA 模型分别具有 1.38 亿和 2,300 万以上的参数。在边缘部署这些模型是不可行的。

智能手机和物联网传感器等边缘设备是资源受限的设备,不可能在不影响设备性能的情况下执行训练或实时推理。因此,研究的重点是将大型模型压缩成小型紧凑的模型,在边缘部署时性能损失最小甚至为零。

以下是一些可用的模型压缩技术,但不限于此

  • 剪枝和量化
  • 低秩因子分解
  • 神经结构搜索(NAS)
  • 知识的升华

在这篇文章中,重点将放在[1]提出的知识提炼上,参考文献链接[2]提供了上面列出的模型压缩技术的一个很好的概述。

知识的升华

知识提取是使用从大型模型模型集合中推断出的提取知识来训练一个紧凑的神经网络。使用提取的知识,我们能够有效地训练小而紧凑的模型,而不会严重损害紧凑模型的性能。

大型和小型模型

我们称大型模特模特组合笨重模特教师网络,而小型模特和紧凑型模特学生网络。

一个简单的类比是,一个大脑小而紧凑的学生在准备考试时,试图从老师那里吸收尽可能多的信息。然而老师只是教所有的东西,不知道考试中会出现哪些问题的学生尽力吸收所有的东西。

这就是压缩发生的地方,从老师那里提取知识给学生。

图一。教师模型将知识提炼为学生模型

在提取知识之前,笨重的模型或教师网络应该达到 SOTA 性能,这种模型通常由于其记忆数据的能力而过度拟合。尽管过度拟合,这个笨重的模型也应该能很好地推广到新的数据。繁琐模型的目标是最大化正确类别的平均对数概率。更有可能正确的类将被分配高概率分数,而不正确的类将被分配低概率分数。

下面的例子显示了当对给定的“鹿”图像执行推理时,softmax 图 2 后的结果。为了得到预测,我们取最大类概率分数的 argmax,这将给我们 60%的正确机会。

图二。“马”看起来像“鹿”(Softmax 预测)

然而,给定上面的图 2。(为了便于说明),我们知道与“船”相比,“马”与“鹿”非常相似。因此,在推理过程中,我们有 60%的正确率和 39%的错误率。由于“鹿”和“马”之间有一些空间相似性,我们不能责怪网络预测“马”。如果网络被提供了诸如“我认为这个图像有 60%的几率是鹿,39%的几率是马”的信息,例如【鹿:0.6,马:0.39,船:0.01】,那么网络被提供了更多的信息(高熵)。使用类别概率作为目标类别提供了比仅仅使用原始目标更多的信息。

蒸馏

教师向学生提炼预测类概率的知识,作为“S 经常的目标”。这些数据集也被称为“转移集”,其目标是教师提供的类别概率,如图 2 所示。蒸馏过程通过将超参数 T (温度)引入 softmax 函数来进行,使得教师模型可以为学生模型产生转移集的合适的软目标集。

软目标 善于帮助模型泛化,可以作为防止模型过于自信的正则化子。

Eq 1。Softmax 随温度变化( T=1 只是 softmax,随着 T 的增加,概率的等级分布将变得更柔和)。

培训教师和学生模型

首先训练繁琐的/教师模型,因为我们需要繁琐的模型很好地概括新数据。在提取过程中,学生目标函数是两个不同目标函数 Loss1Loss2 的加权平均值。

Loss1(软目标的交叉熵损失)

Loss1 是教师 q 和学生 p 与温度 T > 1 的两个温度 softmax 的交叉熵(CE)损失乘以权重参数α

Eq 2。损失函数 1

Loss2(硬目标交叉熵损失)

Loss2 是正确标签和学生硬目标在 T = 1 时的交叉熵(CE)损失。Loss2 对学生模型做出的硬目标( student_pred )的【T2(1-alpha)】关注不多,以匹配教师模型的软目标 q

Eq 3。损失函数 2

学生模型的目标是蒸馏损失,即损失 1损失 2 之和。

然后,学生模型在传输装置上接受训练,以使其蒸馏损失最小化。

结果

MNIST 实验

如下表 1。是来自论文[1]的结果,该论文显示了使用在具有 60,000 个训练案例的 MNIST 数据集上训练的教师、学生和精选模型的性能。所有模型是两层神经网络,分别具有 1200、800 和 800 个神经元,用于教师、学生和提取模型。当使用与学生模型相比的提取模型时,在温度设置为 20 的情况下,教师和提取模型之间的测试误差是可比较的。然而,当只使用学生模型和硬目标时,它的泛化能力很差。

表 1。MNIST 的初步实验[1]

语音识别实验

如下表 2。是文[1]的另一个结果。繁琐模型是由 85M 参数组成的语音模型,该模型是在 2000 小时的英语口语数据上用大约 700M 训练样本训练的。表 2 中的第一行。是在 100%的训练示例上训练的基线(繁琐)模型,其产生 58.9%的准确度。第二行仅用 3%的训练样本进行训练,这导致了严重的过拟合,最后,第三行是用同样 3%的训练样本训练的相同语音模型,其具有仅使用 3%的训练数据就达到 57%准确度的软目标。

表 2:软目标允许一个新模型仅从 3%的训练集中进行很好的概括。软目标是通过在全训练集上训练获得的。[1]

结论

知识提炼是一种将计算带到边缘设备的模型压缩技术。目标是有一个小而紧凑的模型来模仿笨重模型的性能。这是通过使用软目标来实现的,软目标充当正则化器,以允许小而紧凑的学生模型进行归纳,并从教师模型中恢复几乎所有的信息。

根据 Statista [3]的数据,到 2025 年,已安装的物联网(IoT)连接设备总数预计将达到215 亿。随着边缘设备数量的增加,为边缘设备带来计算能力对于让边缘设备变得更加智能来说是一个不断增长的挑战。知识提炼允许我们在不影响边缘设备性能的情况下执行模型压缩。

参考文献

【1】辛顿、杰弗里、奥里奥尔·维尼亚尔斯和杰夫·迪恩。"从神经网络中提取知识."arXiv 预印本 arXiv:1503.02531 (2015)。

[2] 空间深度学习的模型压缩技术综述

[3] 全球联网设备数量

社交网络中的独特性中心性:Python 教程

原文:https://towardsdatascience.com/distinctiveness-centrality-56c1e6762328?source=collection_archive---------37-----------------------

独特性中心性是一组新的网络度量,它高度重视独特的、非冗余的连接。它们对于社会和复杂网络的分析是有用的。

独特性中心性提供了既定指标的另一种观点,即基于其经纪能力、其连接的数量和权重以及其快速到达所有其他节点的能力来识别中心节点。

正如我们在图中看到的,即使节点#2 比节点#1 具有更高的度,后者也被认为更重要(根据独特性),因为节点#6、#7 和#8 没有其他连接。另一方面,节点#2 的邻居有更多的连接,因此与#2 的链接是许多链接中的一个。

一些传统的中心性度量不太重视节点与网络外围的连接。另一方面,独特性中心性赋予那些与松散连接的节点更多的重要性。这在我们想要惩罚到网络集线器的连接的情况下是有帮助的。

独特性中心性可能有助于识别犯罪组织中的突出节点,当这些组织是由半独立的细胞组成的团体时,大型网络中心代表一种风险。在其他场景中,可能存在到达某些对等体的唯一节点,并且可以用作传播促进人群健康的实践的种子。或者,独特中心性可以用来测试新的网络分割策略,意在遏制流行病。

当分析单词共现网络以评估品牌重要性时(参见我的另一篇关于 语义品牌得分 ),与独特单词的品牌链接可能更重要,因为它们传达了品牌形象的独特特征。

在有向网络中,如果传入弧源自具有低出度的节点,则区别中心性用于更重视传入弧。事实上,从一个节点向所有其他节点发送弧的连接被认为没有什么价值。让我们以奥罗拉收到布鲁诺的情书为例,布鲁诺正在给附近的所有人发送情书。寄给奥罗拉的信对奥罗拉来说远不如布鲁诺只寄了一封信(给奥罗拉)的情况重要。类似地,如果传出弧到达具有低入度的对等体,则它们的价值更高。这意味着,如果奥罗拉只收到布鲁诺的情书,她会比收到邻居所有人的情书更加关注它。

本文【1】提供了度量标准、它们的公式和性质的完整描述。

用 Python 计算独特性中心性

我最近开发了一个 Python 包[2],用于简单计算独特性中心性。代码在 GitHub 上也有 这里有一个简短的教程。

首先,我们需要安装并导入显著性包,可以通过 pip 完成。还需要 Networkx 和 Numpy。

**#To install the package**
pip install -U distinctiveness

我们将额外使用 Pandas(可选)来处理结果。

**# Import distinctiveness**
from distinctiveness.dc import distinctiveness

**#Import Networkx and Pandas**
import networkx as nx
import pandas as pd

让我们创建一个玩具网络(无向带弧权重),以展示独特性中心性的计算示例。

**#Create and draw an Undirected Toy Network**
G= nx.Graph()
G.add_edge("A","E", weight = 5)
G.add_edge("A","B", weight = 2)
G.add_edge("B","F", weight = 5)
G.add_edge("B","C", weight = 2)
G.add_edge("B","D", weight = 2)
G.add_edge("C","D", weight = 5)
pos = nx.spring_layout(G)
labels = nx.get_edge_attributes(G,'weight')
nx.draw(G,pos=pos,with_labels = True,node_color='#00c0ff')
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels)

无向玩具网络

独特性中心性有五种变化(替代指标),而distinctiveness 函数将计算所有变化。现在,我们设置 alpha = 1。可以增加该系数,以更强烈地惩罚具有高度连接的节点的链路。

**#Calculate the 5 metrics of Distinctiveness Centrality**
DC = distinctiveness(G, alpha = 1, normalize = False, measures = ["D1","D2","D3","D4","D5"])
DC = pd.DataFrame(DC).sort_index()
DC

当 alpha > 1 时,负值是可能的,因为到高度连接的对等体的链接会对节点的重要性产生负面影响。也可以为每个指标指定不同的α值。

DC = distinctiveness(G, normalize = False, alpha = **[1,2,3,4,5]**)
DC = pd.DataFrame(DC).sort_index()
DC

度量可以标准化,以便在不同网络之间进行比较。这可以通过设置normalize=True.来实现。现在让我们看一个有向图的例子。

**#Create a Direceted Toy Network**
G= nx.DiGraph()
G.add_edge("A","E", weight = 5)
G.add_edge("A","B", weight = 6)
G.add_edge("B","A", weight = 2)
G.add_edge("B","F", weight = 5)
G.add_edge("B","C", weight = 2)
G.add_edge("B","D", weight = 2)
G.add_edge("C","D", weight = 3)
G.add_edge("D","C", weight = 5)
pos = nx.spiral_layout(G)
labels = nx.get_edge_attributes(G,'weight')
nx.draw(G,pos=pos,with_labels = True,node_color='#00c0ff')
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels, label_pos=0.4)

定向玩具网络

当计算有向图的区别中心性时,每个度量都有一个“_in”和“_out”后缀。这些逻辑和公式都在本文的引言中提出,并在【1】中详述。

DC = distinctiveness(G, normalize = False, alpha = 2)
DC = pd.DataFrame(DC).sort_index()
DC

如需了解更多信息,您可以观看此视频或联系我

参考文献

[1] Fronzetti Colladon,a .,& Naldi,M. (2020 年)。社会网络中的独特性中心性。 PLoS ONE15 (5),e0233276。https://doi.org/10.1371/journal.pone.0233276

[2]显著性 GitHub 库:https://github.com/iandreafc/distinctiveness

分布式观鸟

原文:https://towardsdatascience.com/distributed-birding-2b2565ce9660?source=collection_archive---------48-----------------------

分开观鸟,一起观鸟

或者秃鹰的翼展…

圣克拉拉县是硅谷的中心,有一些令人惊叹的观鸟点。随着各种各样的鸟类马拉松赛的到来,我们已经进入了原地避难指令的第 n 周。我的观鸟朋友夏普一家正在考虑如何最好地覆盖我们的领地。我们的户外活动被限制在我们房子的五英里范围内,但是 5MR 观鸟(半径 5 英里)已经是一件事了,所以让我们跟着它跑吧。

这个项目的 Jupyter 笔记本是 5MR 观鸟或 计划重要日子和圣诞节鸟类计数的绝佳工具,无论你们是一起观鸟还是分开观鸟

我是团队中最差的观鸟者,但我拍了一些不错的照片,事实证明我的数据科学技能派上了用场,所以他们容忍了我。我决定用这些技能给我们尽可能多的信息,告诉我们鸟儿在哪里,我们应该去哪里。

该项目生成的文件列出了每个人的热点,只有某些团队成员才能看到的独特鸟类,帮助可视化最佳观鸟地点的地图等等。

康奈尔鸟类学实验室运行着世界上最大的与生物多样性相关的公民科学项目。因为它们也支持编程接口,所以我们可以使用像 Python 这样的工具来挖掘和探索这些数据。

当一个团队正在运行一个重要的一天的活动(分布式或非分布式),目标是在 24 小时内找到尽可能多的鸟类物种。使用该工具生成的文件,您可以看到在哪里集中您的努力以获得最大效果,并且优化您的大日子体验

这方面的代码可以在 GitHub 库中找到。

[## jhurley 13/分布式绑定

使用 eBird 数据规划分布式观鸟活动的工具将 data/external 中的 Contacts.csv 文件替换为您的…

github.com](https://github.com/jhurley13/DistributedBirding.git)

开胃食品

在深入研究技术细节之前,我将从一个小问题开始。该工具生成的文件之一是每个参与者 5MR 的交互式个人地图。这是一个 HTML 文件,显示了一个人 5MR 中的热点。

标记的颜色表示物种的数量:灰色表示底部的三分之一,青色表示中间,蓝色表示最多。点击一个标记显示实际计数和纬度/经度。将鼠标悬停在标记上可显示热点名称。鉴于旧金山湾附近有大量的蓝色标记,其中一个热点可能是你应该首先去的地方

一个 5MR 显示热点

利用叶子,一个生成地图的包,生成地图的代码只有 85 行 Python 代码;整个项目的代码不到 1000 行。

谁应该读这个?

这主要是针对观鸟者的,但也有相当数量的数据科学、地理定位和地图代码,所以这应该会引起数据科学家的兴趣。这个项目利用了一些强大的 Python 库,如 Folium、GeoPandas、Shapely 和 Geog,来产生用于规划的有用数据。数据科学的一个关键部分是理解你的数据,理解你的用户。由于我的观鸟小组要求增加一些功能,这段代码用了几个月的时间才完成。

就了解你的用户而言,许多观鸟者不是技术人员,但他们对使用电子表格很熟悉,所以输入和输出都是电子表格。视觉化同样重要。生成的 HTML 地图可以让您快速做出一些决定。在上面的 5MR 地图示例中,沿旧金山湾密集分布的蓝色标记会立即告诉您,这是开始聚集最多物种的地方。

如果你是一个没有编码经验的观鸟团队,可以寻求 Python 程序员的帮助来运行程序;生成的报告将大大简化你的分布式观鸟规划。

先决条件

开始之前,您需要做和/或了解一些事情。

  • 您将需要一个 eBird 的 API 键,以便能够查找最近的观察和热点。
  • 这不是一个独立的应用或服务,所以你必须知道如何在 Jupyter 笔记本上运行代码。
  • 如果您想定制任何东西,对 Python 的基本熟悉将是有用的。
  • 它只需要两个输入文件:一个联系人列表(又名观鸟朋友)和一个可选的参数文件,该文件允许您指定联系人的子集作为特定事件的参与者列表。
  • 您应该已经使用 Python 3.7 或更高版本设置了虚拟环境。
  • Github 库下载或克隆代码。

TL;灾难恢复设置

  • 用自己的联系人文件替换数据/外部中的 Contacts.csv 文件。
  • 编辑并将您的ebirdcredcredentials . yml文件放在您的主目录中
  • 运行 Jupyter 笔记本中的所有单元格,distributed birding . ipynb
  • 输出在报告目录中

使用

使用 my5mr-uniques.csvmy5mr-limited.csv 文件,每个团队成员可以看到三个或更少的团队成员有可能找到的鸟类。根据 eBird 数据,这是基于过去 14 天中其他人在这些地点看到的物种。

使用 my5mr-map_NAME.html 中的交互式地图,每个人都可以看到他们的 5mr 以及每个热点的标记。这些标记用颜色编码,因此浅灰色是物种数量的底部三分之一,浅青色是中间的,矢车菊蓝色是顶部三分之一。将鼠标悬停在一个标记上可以显示热点的名称,点击它可以显示该位置的物种数量。

为了在合并的 5mr 上获得尽可能好的覆盖,您可以使用my5mr-unique-hotspots . CSV文件来查看您独有的热点。这可能意味着你会看到别人看不到的物种。

一个高级特性是,您可以将联系人列表的子集放入参数文件中,以限制特定事件的参与者。例如,如果您的 contacts 文件有 20 个条目,但是只有 5 个人要参加某个特定的活动,请将他们的名字添加到参数文件中。这将正确地计算交叉点,以便您知道谁需要去哪里。

输入

唯一需要的输入是观鸟联系人的电子表格。对于每个人,需要地址或纬度/经度来确定他们的 5MR。该工具可以读取 CSV 或 Excel 文件。

用您自己的联系人文件替换数据/外部中的 Contacts.csv 文件。在样本输入目录中可以找到几个例子。

输出

该工具的大部分输出位于 reports 目录中。有 HTML 格式的交互式地图,也有 CSV 格式的各种数据的电子表格。

个性化文件

  • my5mr-DATE_NAME.csv :包含最近 14 天在 5MR for NAME 中的观察结果
  • my5mr-map_NAME.html :每个 5mr 圈的互动图,有热点和计数。使用 Chrome、Safari 或其他浏览器查看。

公共文件

  • my5mr-team _ circles . html:一张 5mr 圈子的互动地图,面向所有参与者。使用 Chrome、Safari 或其他浏览器查看。
  • my5mr-hotspots.csv :每个人 5mr 中的热点列表
  • my5mr-limited.csv :只出现在 3 个或更少个体 5mr 中的所有物种的列表
  • my5mr-unique-hotspots.csv :为每个人列出一个你独有的热点列表(即不属于其他人的 5mr)
  • my5mr-uniques.csv :包含每个人只在 5mr 中见过的物种列表

my5mr-uniques.csv 示例

从这篇报道中,我们依靠约翰·古尔德为红十字嘴鹪鹩找到岩鹪鹩和哈里特·赫曼威。米娜和托马斯有他们的工作要做,但是约翰·奥杜邦对任何特定的物种都没有责任。

my5mr-limited.csv 示例

在这里,明娜·霍尔和雷切尔·卡森最有可能看到黑色的蛎鹬。

示例my5mr-team _ circles . html

团队圈子图提供了所有 5MR 圈子的快速概览。

区域代码

eBird 使用的区域代码类似于“US-CA-085”(美国加利福尼亚州圣克拉拉县)。这些可以由代码自动处理,至少在美国是这样。

如果您需要手动查找您的地区代码,请按照下列步骤操作:

  • 前往https://ebird.org/explore
  • 在“探索地区”的搜索栏中输入您所在的县
  • 当页面打开时,查看 URL。您所在的地区在“region/”之后,在“?”之前,例如,对于 US-CA-085,url 将是:
[https://ebird.org/region/US-CA-085?yr=all](https://ebird.org/region/US-CA-085?yr=all)

该代码将查看所有参与者的所有 5mr,并与美国的县相交。对于每个相交的县,将找到 eBird 区域代码并将其添加到热点、观测值等的列表中。

如果您想要限制县列表,您可以在数据/外部中的参数文件中手动输入区域代码。在样本输入/参数-区域. csv 中可以找到一个例子。

资源使用

运行这段代码时,需要注意两个服务。第一个是地理定位服务命名。每次查找都会延迟一秒钟,以避免对服务器造成太大冲击。此外,我们在data/interim/geolocation cache . CSV中维护了一个本地缓存,以避免重新查询地址。更多信息请参见他们的使用条款

另一项服务是 eBird,我们试图限制我们拨打的电话,尽管我们目前没有缓存这些信息。

编码细节

您已经做到了这一步,但是还没有看到任何代码。我不想吓跑那些不编码的鸟人,因为我认为这个工具可能是有用的。

该代码读取 CSV 或 Excel 文件,使用 nomist 从一个地址查找地理位置,演示结果的缓存,并进行一些几何操作。我们使用请求库从 eBird 获取数据。

没有真正短小精悍的代码片段可以放入一篇文章中,所以我将重点介绍一些主要的函数以及您可能从中学到的东西。

笔记本的整体结构,包括像 create_project_paths 这样的功能,都来源于我的这篇文章:

[## 为您的 Jupyter 笔记本创建一个模板

快速启动您的数据科学调查

medium.com](https://medium.com/@jhurley_97842/create-a-template-for-your-jupyter-notebooks-80352d265cd4)

我的一些代码是去年的,那时还不支持 API 的 2.0 版本。将来,我会考虑使用下面显示的 ebird-api 包,而不是自己编写通过 api 访问 ebird 的包。

[## ebird-api

eBird API 提供了一组包装函数,用于访问 eBird API 2.0 中的端点。pip 安装 ebird-api…

pypi.org](https://pypi.org/project/ebird-api/)

为所有参与者创建带有 5MR 圆圈的地图的代码足够短,可以在这里显示:

假设 df5mr 是具有纬度、经度列的数据帧

下面列出了一些功能:

  • eBird API 调用: get_hotspots_for_region,get _ recent _ nearby _ observations,get_observations,get_sub_region_list
  • Excel/CSV: r ead_parameters,load_contacts
  • 地理位置的查找/缓存:地址 _ 到 _ 地理位置,查找 _ 地理位置
  • 主要功能:生成 _5mr_reports
  • 寻找集体圈地图中心点的有趣代码在 main 的主体中
  • 树叶/地图制作:创建 _ 热点 _ 标记,创建 _ 圈子 _ 地图,我的 _ 热点 _ 地图,个人 _ 热点 _ 地图
  • 下载/解压形状文件:下载 _ 美国 _ 县 _ 形状文件

请注意在individual _ hotspot _ maps中使用 pd.qcut 将热点的物种数量分为低、中、高,以便标记可以适当地着色。

编写region _ codes _ from _ contacts中的代码很有趣,因为它查看所有 5mr 及其与美国所有县的交集,以找到重叠部分。从匹配的县中,它调用 eBird 来获取地区列表并返回代码,例如 US-CA-085。

安全说明

鉴于我的背景,我必须指出几个与安全相关的问题。首先,注意凭证文件存储在项目目录之外,在您的主目录中。因为这是一个秘密,所以您不希望意外地将其签入存储库。我实际上把我的保存在一个加密的磁盘映像中。如果这是一个更敏感的秘密,我会把它存在 macOS 的钥匙圈里。

其次,如果保护人们的信息或位置很重要,你可以用附近公共场所的地址或地理位置来代替实际的家庭地址。这将意味着 5MR 偏离了一点。

结论

我希望这篇文章和代码对鸟类旅行计划和学习数据科学和制图技术都有用。

参考

[## 下载 eBird 数据产品- eBird

eBird 为登录用户提供多种格式的开放数据访问,从原始数据到经过处理的数据集…

ebird.org](https://ebird.org/science/download-ebird-data-products) [## 命名

Nominatim 使用 OpenStreetMap 数据通过名称和地址查找地球上的位置(地理编码)。它还可以做…

nominatim.org](https://nominatim.org) [## 0.11.0 文件

使用用于绘制地图的 Python 和 fleed . js 类制作漂亮的交互式地图。基地…

python-visualization.github.io](https://python-visualization.github.io/folium/modules.html)

我的一些照片

AWS EKS 和提督使分布式数据管道变得简单

原文:https://towardsdatascience.com/distributed-data-pipelines-made-easy-with-aws-eks-and-prefect-106984923b30?source=collection_archive---------12-----------------------

如何在几分钟内建立一个分布式云工作流程编排系统,并专注于提供价值而不是管理集群

卢克·切瑟在 Unsplash 上的照片

B 为 ETL & ML 数据管道构建分布式系统是困难的。如果您尝试自己实现一个,您可能会体验到,将工作流编排解决方案与 Spark 或 Dask 等分布式多节点计算集群捆绑在一起可能很难正确设置和管理。在本文中,您将了解如何获得一个高度可用、可伸缩的分布式系统,这将使您的 ETL & ML 数据管道的编排更加愉快,并将释放您的时间 来处理数据并从中产生价值,而不是花费时间来维护集群。

整个设置在很大程度上是由 AWS 和 Prefect 自动完成的。另外,一开始你几乎不用花什么钱。为什么?因为我们将使用一个带有无服务器 Kubernetes 集群的工作流编排系统作为您的分布式、容错、高度可用、自我修复&自动可伸缩执行层。

你需要做的就是:

  • 创建 AWS 帐户 & IAM 用户具有创建 AWS ECR、ECS & EKS 资源的编程权限
  • 通过在 Fargate 上使用 AWS EKS 运行几个 shell 命令来建立一个 Kubernetes 集群(控制面板每小时只需要 0.10 美元!之前是 0.20 美元但是AWS 2020 年初价格减半
  • 注册一个免费的完美云账户 ( 免费开发者账户可以让你使用所有功能,但仅限于你自己——如果你想在你的团队中使用它,你需要升级到团队或企业计划
  • 在提督云用户界面中生成一个认证令牌,将您的提督帐户连接到 AWS 上的无服务器 Kubernetes 集群
  • 运行分布式数据管道。

我们开始吧!

注意:本文使用了提督 1 的代码,不推荐使用。检查最新版本的文档

无服务器 Kubernetes 集群作为执行层

2019 年 12 月,AWS 推出了一个新的 Fargate 功能,对许多人来说,这被认为是一个改变游戏规则的功能——他们引入了一个在 ECS Fargate 上使用 AWS EKS 的选项,这是一种说法: AWS 使 Fargate 服务不仅成为 ECS 的指挥者,还成为以及【EKS的指挥者。到目前为止,AWS Fargate 是一种只在 AWS ECS 上运行容器的无服务器方式。

EKS 和法盖特让在 AWS 上运行基于 Kubernetes 的应用变得简单明了,因为他们不再需要为 pods 提供和管理基础设施。

这对你来说意味着什么?你现在可以在 EKS 上拥有一个无服务器* Kubernetes 集群,它只对正在运行的 pods 收费,而不对它们的底层 EC2 实例收费。除其他优势外,它还意味着:*

  • 不再需要维护工作节点,
  • 不再有猜测的能力,
  • 不再有 EC2 自动缩放组来缩放工作节点。

你所需要做的就是为你的部署编写 YAML 文件,并通过 kubectl 与 EKS 交互。简而言之:你现在唯一的任务就是编写你的 ETL & ML 代码来为你的业务增值,AWS 负责运营,即操作、维护和扩展你的 Kubernetes 集群。

考虑到我们只为实际的 vCPU 和运行 pod 的内存收费,这为现代数据平台提供了巨大的基础。

这听起来好得令人难以置信:有哪些不利之处?

使用几乎任何无服务器选项的一个可能的缺点是当容器编排系统需要首先分配和准备计算资源 ( a.o .将最新版本的映像从 docker 注册表拉至分配的工作节点并构建映像)时的冷启动问题,这可能会在容器(或您的 K8s pod* )进入运行状态之前增加一些额外的延迟。*

如果您的数据工作负载要求非常低的延迟水平,您可以选择具有传统数据平面的 AWS EKS 集群,并按照这篇博客文章中的说明在 AWS EKS 上设置一个非无服务器集群,并将其连接到您的完美云环境。

但是,**你兼得**!AWS 允许您混合这两者:

  • 您可以在默认名称空间中以无服务器方式运行相同的 AWS EKS 集群(这是通过生成 Fargate 概要文件来设置的)
  • 您可以拥有一个 EC2 实例( ex。,使用 GPU 为您的数据科学模型连接到 EKS 上的同一个 Kubernetes 集群,但在不同的名称空间内或使用不同的 Kubernetes 标签。当您为与 Fargate 概要文件中定义的名称空间和标签不匹配的 pod 创建部署时,它将被调度到您维护的 EC2-worker 节点,并且该节点是无延迟可用的。

如你所见,AWS 很有远见地设计了 Fargate 上的 EKS,允许你混合无服务器和非无服务器选项以节省你的时间、金钱和维护工作。你可以在 AWS 介绍这项服务的视频中找到更多信息。

AWS 设置

您需要拥有一个具有管理员访问权限的 AWS 帐户,或者至少拥有 IAM 权限的用户,以便创建 ECR、EKS 和 ECS 资源。然后,您必须为该帐户配置 AWS CLI 并安装 eksctl ,如本 AWS 文档中所述。

AWS 上的 Kubernetes 与 AWS ECR 配合得很好,后者是 Docker 图像的注册表。要使用您的 ECR 帐户对您的终端进行身份验证,请运行:****

  • 如果您使用新的 AWS CLI v2:
*aws ecr get-login-password --region <YOUR_AWS_REGION> | docker login --username AWS --password-stdin <YOUR_ECR_REGISTRY_ID>.dkr.ecr.<YOUR_AWS_REGION>.amazonaws.com*
  • 如果您使用旧版本的 AWS CLI:
*$(aws ecr get-login --no-include-email --region <YOUR_AWS_REGION>)*

注: <YOUR_AWS_REGION>可能是 ex。美东-1* ,欧盟-中部-1,更多。*

如果您得到登录成功消息,您可以为您的数据管道创建您的 ECR 存储库。我们将创建两条数据管道:dask-k8basic-etl-prefect-flow——使用相同的名称来遵循本演练,但是一般来说,最简单的方法是将您的 ECR 存储库与您的完美流命名为以避免混淆。**

*aws ecr create-repository --repository-name dask-k8aws ecr create-repository --repository-name basic-etl-prefect-flow*

然后,您需要做的就是运行以下命令,这将在您的 VPC 中部署 Kubernetes 控制平面和 Fargate 配置文件:

*eksctl create cluster --name fargate-eks --region <YOUR_AWS_REGION> --fargate*

我为集群选择了名称 fargate-eks ,但是可以随意更改。
--fargate标志确保我们创建一个新的 Fargate 配置文件用于该集群。如果需要,EKS 允许您创建自定义的 Fargate 配置文件。

提供所有资源可能需要几分钟时间。完成后,您应该会看到类似如下的输出:

然后,如果你检查你的上下文:

*kubectl config current-context*

您应该会得到与下面类似的输出:

*<YOUR_AWS_USER_NAME>@fargate-eks.<YOUR_AWS_REGION>.eksctl.io*

这样,您可以看到您连接到了一个运行在 AWS Fargate 上的无服务器 Kubernetes 集群!为了进一步证明这一点,运行:

*➜  ~ kubectl get nodes
NAME                                                       STATUS   ROLES    AGE   VERSION
fargate-ip-192-168-163-163.eu-central-1.compute.internal   Ready    <none>   15m   v1.17.9-eks-a84824
fargate-ip-192-168-180-51.eu-central-1.compute.internal    Ready    <none>   15m   v1.17.9-eks-a84824*

在输出中,您应该看到至少有一个 Fargate 节点等待您的 pod 部署。

注意:这些节点在您的 VPC 内运行,但是它们在您的 EC2 仪表板内不可见。您也不能通过 SSH 访问这些节点,因为它们完全由 Fargate 以无服务器的方式管理和部署。**

将这个 AWS EKS 集群与提督相结合的好处是,整个 Kubernetes pod 部署和调度由提督从您这里抽象出来 。这意味着你甚至不需要太了解 Kubernetes 就能从中获取价值。在下一节中,我们将把这个集群连接到我们的 Prefect Cloud 帐户,并开始构建分布式 ETL & ML 数据管道。

完美设置

让我们先在 https://cloud.prefect.io/注册一个免费的开发者账户。

完美云:注册页面——作者图片

首先,你将受到一个干净的用户界面的欢迎,显示你的流程、代理、最近流程运行的概述下一个计划的任务。流程本身可以被组织成几个项目。当您开始构建数据管道时,这个主仪表板可以让您快速识别所有数据管道的当前状态。

Prefect Cloud UI 的主仪表板—作者图片

您可以看到,我现在有一个 Fargate 代理准备在 AWS ECS 上部署流。现在,我们将只关注AWSEKS。

完美的云用户界面显示了一个健康的 Fargate 代理——图片由作者提供

安装提督

让我们继续设置并在您的机器上安装 prefect。以下命令将安装带有 AWS 和 Kubernetes extras 的提督(而不是* [kubernetes,aws] 如果您想安装外部系统的所有提督扩展,您可以使用 [all_extras] ):*

*pip install "prefect[kubernetes,aws]"*

现在,为了确保您使用的是 Prefect Cloud,而不是开源版本的 Prefect Core,请将上下文切换到 Cloud:

*prefect backend cloud*

创建个人访问令牌,将您的流注册到 Prefect Cloud

注册免费账户后,你需要创建一个个人访问令牌来认证你的本地终端。这将允许直接从您的计算机向 Prefect Cloud 注册您的流(即您的 ETL & ML 数据管道)。进入侧边栏:用户→个人访问令牌→ + CREATE TOKEN按钮。

完美的云 UI:创建令牌来注册流——图片由作者提供

选择一些有意义的名字。MyTokenToRegisterFlows

然后复制令牌,并在您的终端中运行以下命令:

*prefect auth login -t <MyTokenToRegisterFlows>*

现在,您可以注册您的流程,以便从 Prefect Cloud 进行编排!

创建 API 令牌来授权您的 AWS EKS 代理运行您的流

最后一部分是为您的 Kubernetes 代理创建一个 RunnerToken 并注册代理。进入侧边栏:团队→ API 令牌→ + CREATE TOKEN按钮。

完美云用户界面:创建 API 令牌来注册你的 AWS EKS 代理——图片由作者提供

或者,您可以从您的终端进行同样的操作:

*prefect auth create-token -n MyEKS_on_Fargate_K8s_Token -r RUNNER*

选择RUNNER范围非常重要,否则您的代理将无法代表您执行流程。

点击CREATE并复制生成的 API 令牌。

使用生成的 API 令牌将 EKS 集群设置为您的完美代理

现在我们到了最激动人心的部分:使用下面的命令,您将能够将您的无服务器 AWS Kubernetes 集群设置为您的完美数据管道的执行层 ( 即代理):

现在,您应该能够在您的 Prefect Cloud 帐户中看到一个新的 Kubernetes 代理:

完美云用户界面:AWS EKS 集群现已注册并准备好运行流——图片由作者提供

我们还可以看到一个对应于提督代理的新 pod:

*➜  ~ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
prefect-agent-68785f47d4-pv9kt   1/1     Running   0          5m59s*

基本完美流程结构

现在一切都设置好了,我们可以开始创建我们的流了。Prefect 文档包括各种有用的教程,将快速向您展示如何修改您的 Python ETL & ML 代码,使其在 Prefect 上运行。简而言之,你只需要用 **@task**装饰器装饰你的 Python 函数,添加任务和流导入并创建一个对象:

注意:添加log_stdout=True确保打印输出会出现在提督云流日志中。

创建项目来组织您的流程

现在,我们可以创建一个项目来组织我们的流,无论是从 UI 还是通过使用终端:

*➜ prefect create project Medium_AWS_Prefect
Medium_AWS_Prefect created*

将您的流量注册到完美云

如果我们现在运行这个脚本,我们应该会得到一个到 Prefect Cloud UI 的链接,从这里我们可以触发或安排流程:

*➜ python3 basic-prefect-etl-flow.py 
Result check: OK
Flow: [https://cloud.prefect.io/anna/flow/888046e6-f366-466a-b9b5-4113cd437e4d](https://cloud.prefect.io/anna/flow/888046e6-f366-466a-b9b5-4113cd437e4d)*

从 UI 运行您的流程

当我们触发该流时,我们将看到它停留在预定状态,并且不运行。

完美云用户界面:从用户界面触发流运行—作者图片

这是因为要在 AWS EKS 集群上运行您的流,您的流必须包含关于代码存储位置的信息

添加存储信息&将流程推送到 ECR

关于如何存储你的代码,有几个选择:EKS 可以从 ECR、S3 或 Github 下载。

最简单的选择是将您的流程分类,并将图像推送到 ECR。幸运的是,提督团队让事情变得非常简单——我们只需要:

  • 为我们的流程创建一个 ECR 存储库(这个步骤可以在您的生产环境中使用 CI/CD 管道自动完成)
  • 在代码中添加 Docker 存储。

如果您还记得 AWS 设置部分,我们已经创建了两个 ECR 存储库:dask-k8basic-etl-prefect-flow。因此,我们只需要将storage=Docker()参数添加到我们的流代码中,这样它就可以由我们的无服务器 Kubernetes 代理执行:

上面代码的一些重要注释:

  • 在引擎盖下,Prefect 检查您的 Prefect 版本并扩展相应的 Prefect Docker 映像以包含您的流及其依赖项,
  • 确保设置您的 ECR 注册 ID ,这样您的流将被归档并被推送到我们之前创建的 ECR 存储库dask-k8
  • image_tag='latest'用于禁用 ECR 图像的版本控制。将其设置为最新或任何其他特定标记将确保每次注册流程时,您都覆盖标记为最新的先前 ECR 图像。这对我很有用,因为我已经使用 Git 对我的代码进行版本控制,并且我不需要 ECR 版本控制。此外,它可以在 AWS 上为您节省一些资金,因为 ECR 上的每个新版本的图像,您都需要为这些 ECR 图像的存储支付更多的费用。但是可以跳过这个争论。这样,提督将使用当前日期和时间标记图像,并使用不同的标记存储每个流版本。

最后注意:额外的参数*python_dependencies=["pandas==1.1.0"]*允许定义需要安装在容器中的 Python 包的列表。如果您需要对您的图像进行更细粒度的控制,您可以提供一个自定义 over 文件的路径,例如:*dockerfile='/Users/anna/my_flows/aws/Dockerfile'*

部署新的流版本

我们现在可以将之前的流程运行标记为完成或失败:

完美云用户界面:将流程运行标记为已完成或失败——作者图片

最后,我们可以通过简单地重新运行修改后的脚本来注册流的新版本,如第二个要点所示:

在 UI 中检查流程进度

如果我们访问输出中的链接,我们可以看到版本 2 已经创建,如果我们运行它,我们可以看到流程将转移到状态提交执行(黄色),然后运行(蓝色),最后所有引用任务成功(绿色):

完美云用户界面:示意性任务视图—作者图片

从日志和 CLI 查看进度

日志显示更多信息:

完美云用户界面:流量日志—作者图片

我们也可以使用 kubectl 检查吊舱:

总结一下我们到目前为止所做的工作:

  • 我们创建了一个完美的云帐户
  • 我们使用 AWS EKS 部署了一个无服务器的 Kubernetes 集群
  • 我们创建了一个 ECR 存储库,并将包含我们的流代码和所有 Python 包依赖项的 Docker 映像推送到这个 ECR 存储库中
  • 然后,我们注册我们的流,从 Prefect Cloud UI 运行它,并使用 kubectl 检查它在 UI 和 CLI 中的状态。

如果你走了这么远,恭喜你!👏🏻

在下一节中,我们将创建第二个流,它将利用分布式 Dask 集群。

按需分布式 dask 集群可并行处理您的数据管道

提督云与 Dask 分布式很好的工作。为了以分布式方式在 Dask 上运行 Python 代码,通常需要部署一个带有几个工作节点的 Dask 集群,并对其进行维护。提督提供了一个很好的抽象概念DaskKubernetesEnvironment,它:

  • 加速 按需 Dask 集群跨越多个 pod,也可能跨越多个节点(您可以指定最小和最大工人数量)
  • 将您的流提交到这个按需集群
  • 清理资源(即作业完成后终止集群)。

这里有一个基于完美文档的示例流程,您可以用它来测试您的 Dask 设置。我将这个流保存为dask-k8.py,并提供了与流名称和 ECR 存储库名称相同的名称 dask-k8 :

我们现在注册该流,并再次从 UI 中触发它:

*➜  python3 dask-k8.py*

完美云用户界面:快速运行流程的选项

现在,我们可以观察正在创建的在 Dask 工人之间分配工作的 pod:

我们现在可以看到几个与 Dask 相关的吊舱。UI 显示并行运行的所有任务的当前进度,以及最终运行时间:

完美的云用户界面:在 AWS EKS 的按需 Dask 集群中成功执行数据管道——图片由作者提供

如果我们分析的日志,我们可以看到:

  • 该流在 12:43 被提交执行
  • 流水作业开始于 12:47
  • 流程在 12 点 51 分结束。

这意味着,从 Kubernetes 代理获得流,到在我们的无服务器 AWS EKS 集群上配置 Dask 集群,以及提取所有必要的映像,大约需要 3 分钟。之后,该流程需要 4 分钟才能成功运行。

**

完美的云 UI:运行在 AWS EKS 分布式 Dask 集群上的数据流。左图:初始日志。右图:最终日志—作者图片

如果您的工作负载无法接受与按需创建的 Dask 集群相关的延迟,您可以创建和维护自己的 Dask 集群,并按如下方式修改代码:

这样,您将使用 Executor 而不是环境抽象。此外,如果需要的话,您必须使 255.255.255.255 适应您的 Dask 调度程序地址,并相应地更改端口 8786。

清理资源

在我们结束之前,确保删除 AWS EKS 集群和 ECR 存储库,以避免任何费用:

结论

在本文中,我们使用 Fargate 上的 AWS EKS 在 AWS 上创建了一个无服务器的 Kubernetes 集群。我们以安全的方式将它作为我们的执行层与完美云连接起来。然后,我们通过使用DockerStorage抽象,将我们的 Prefect 流分类,并将图像推送到 ECR。最后,我们部署了在单个 pod 中运行的简单数据管道,以及分布式 Dask 流,允许您的 ETL & ML 代码的高级并行。

在这个过程中,我们确定了差异,以及在我们需要维护的现有资源上运行数据管道与以无服务器方式运行容器化流的优缺点。

希望这个设置能让您更容易开始编排 ETL 和数据科学工作流。无论您是初创企业、大型企业还是为论文编写代码的学生,Fargate 上的 Prefect & AWS EKS 组合都可以让您轻松地将数据项目转移到生产中。

感谢您的阅读!

在 Kubernetes 上使用 Horovod 进行分布式深度学习训练

原文:https://towardsdatascience.com/distributed-deep-learning-training-with-horovod-on-kubernetes-6b28ac1d6b5d?source=collection_archive---------16-----------------------

你可能已经注意到,即使是像英伟达 DGX 这样强大的机器也不足以足够快地训练深度学习模型。更不用说将数据复制到 DGX 的漫长等待时间了。数据集变得越来越大,GPU 从存储中分离出来,使用 GPU 的工作人员需要协调模型检查点和日志保存。您的系统可能会超出单个服务器,团队希望轻松共享 GPU 硬件和数据。

通过 Kubernetes 上的 Horovod 进入分布式培训。在这篇博客中,我将通过 Kubernetes 上的 Horovod 在多工人分布式环境中训练一个深度学习模型的设置。

Kubernetes 上有张量流的 Horovod

Horovod 是一个面向 TensorFlow、Keras、PyTorch 和 Apache MXNet 的分布式深度学习训练框架。由优步开源的 Horovod 已经证明,只需很少的代码更改,它就可以将单个 GPU 训练扩展到并行运行在多个 GPU 上。

Horovod 缩放效率(图片来自 Horovod 网站

作为一个例子,我将使用 Horovod 和 TensorFlow 和 Keras 训练一个电影评论情感模型。尽管 Keras 本身支持分布式训练,但我发现它比 Horovod 更复杂,也更不稳定。

很多时候,客户问我如何在这样的环境下分配和管理团队成员的 GPU 时间表。这在多服务器环境中变得更加重要。我听说过像 Excel 中的时间表(很糟糕,但仍然很常见)、Python 脚本、Kubernetes 和商业软件这样的解决方案。我将使用 Kubernetes,因为它支持在集群上运行许多应用程序容器(包括深度学习)的良好界面。

快速共享存储/文件系统对于简化分布式培训至关重要。它是将机器学习工作流程的不同阶段结合在一起的粘合剂,它使团队能够共享 GPU 硬件和数据。我将使用 FlashBlade S3 来托管数据集,使用 FlashBlade NFS 来设置检查点和存储 TensorBoard 日志。

以下是该设置的架构:

针对 Kubernetes 和 FlashBlade 上 TensorFlow 的 Horovod 分布式培训

在库伯内特斯部署霍洛佛德

在多工作者 Horovod 设置中,单个主要和多个工作者节点协调以并行训练模型。它使用 MPI 和 SSH 来交换和更新模型参数。在 Kubernetes 上运行 Horovid 的一种方法是使用 kubeflow 和它的 mpi-job 库,我发现仅仅为了这个目的而引入 kubeflow 有点过了。Kubeflow 本身就是一个大项目。现在,让我们保持简单。

我们需要先安装 MTP 和 SSH。Horovod 为此提供了一个官方 Docker 文件。我已经定制了它来适应我的需求。虽然可以将 MPI 和 SSH 设置放入 Docker 映像中,但是我们确实需要为 Horovod pods 配置无密码 SSH 身份验证。这不是一个硬性要求,但是为了使示例更加简洁,我使用 Kubernetes 持久卷(PV)来存储我的 SSH 配置,并在/root/.ssh将它挂载到所有容器上。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: horovod-ssh-shared
spec:
  accessModes:
    - **ReadWriteMany**
  resources:
    requests:
      storage: 1Gi
  storageClassName: **pure-file**

请注意,PV 是一个具有ReadWriteMany访问模式的pure-file级(由 NFS 闪存支持)。同样,我还为检查点和张量板日志创建了另一个名为tf-shared的 PV。我将这些 PV 安装到所有容器上:

volumeMounts:
  - name: horovod-ssh-vol
    mountPath: /root/.ssh
  - name: tf-shared-vol
    mountPath: /tf/models

在 Horovod 主容器启动之前,我使用 KubernetesInit Container运行一个init-ssh.sh脚本来生成 SSH 无密码认证配置。

initContainers:
- name: init-ssh
image: uprush/horovod-cpu:latest
volumeMounts:
  - name: horovod-ssh-vol
    mountPath: /root/.ssh
command: ['/bin/bash', '/root/init-ssh.sh']

init-ssh.sh的内容是这样的:

if [ -f /root/.ssh/authorized_keys ]
then
    echo "SSH already configured."
else
    ssh-keygen -t rsa -b 2048 -N '' -f /root/.ssh/id_rsa
    cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
    chmod 700 /root/.ssh
    chmod 600 /root/.ssh/authorized_keys
fi

然后,我声明两个 Kubernetes 部署:一个用于主节点,另一个用于工作节点。当主服务器什么都不做时,工作人员在 pod 中启动一个 SSH 服务器。

- name: horovod-cpu
    image: "uprush/horovod-cpu:latest"
    command: [ "sh", "-c", "/usr/sbin/sshd -p 2222; sleep infinity" ]

有了这些,root主 pod 上的用户无需密码即可通过 SSH 连接到工作人员。Horovod 设置就绪。

在 TensorFlow 中访问 S3 数据集

我的数据集作为 TensorFlow 记录文件存储在 S3 的 FlashBlade 中。我希望我的 TensorFlow 脚本直接访问它,而不是下载到本地目录。所以我使用 Kubernetes Secret 在部署中添加了几个环境变量:

env:
- name: AWS_ACCESS_KEY_ID
  valueFrom:
    secretKeyRef:
      name: tf-s3
      key: access-key
- name: AWS_SECRET_ACCESS_KEY
  valueFrom:
    secretKeyRef:
      name: tf-s3
      key: secret-key
- name: S3_ENDPOINT
  value: 192.168.170.11
- name: S3_USE_HTTPS
  value: "0"

在我的 TensorFlow 脚本的后面,我将使用这些变量进行 S3 认证:

endpoint_url = f"[http://{os.environ['S3_ENDPOINT'](/{os.environ['S3_ENDPOINT')]}"
kwargs = {'endpoint_url':endpoint_url}
s3 = s3fs.S3FileSystem(anon=False, client_kwargs=kwargs)# List all training tfrecord files
training_files_list = s3.ls("s3://datasets/aclImdb/train/")
training_files = [f"s3://{f}" for f in training_files_list]# Now let's create tf datasets
training_ds = tf.data.TFRecordDataset(training_files, num_parallel_reads=AUTO)

FlashBlade S3 速度非常快,最低部署可以达到 7GB/s 的读取吞吐量,持续约 3 毫秒的延迟。对于许多 DL 培训工作来说,这已经足够好了。

Kubernetes 上的 GPU 调度

为了让 Kubernetes 基于 GPU 资源请求调度 pod,我们需要安装 Nvidia k8s 设备插件。需要使用nvidia-docker2包而不是常规 docker 作为默认运行时。遵循自述文件,了解如何准备 GPU 节点。设备插件的安装很简单使用舵。在我的实验室里,我只在装有 Tesla GPUs 的节点上安装插件。所以我给我的 GPU 节点添加了节点标签。

kubectl label nodes fb-ubuntu01 nvidia.com/gpu.family=teslahelm install \
    --version=0.6.0 \
    --generate-name \
    --set compatWithCPUManager=true \
    --set nodeSelector."nvidia\.com/gpu\.family"=tesla \
    nvdp/nvidia-device-plugin

该插件将作为 DaemonSet 安装在kube-system名称空间中。如果一切顺利,GPU 节点现在应该具有 GPU 容量:

kubectl describe node fb-ubuntu01

Capacity:
  cpu:                32
  ephemeral-storage:  292889880Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             264092356Ki
  **nvidia.com/gpu:     1**
  pods:               110

然后,我们可以为 Horovod pods 请求 GPU 资源:

resources:
  limits:
    nvidia.com/gpu: 2 # requesting 2 GPUs

准备训练

接下来,我使用一个预训练脚本来准备训练环境。该脚本使用 Kubernetes CLI 选择 Horovod pods,然后执行以下操作:

  • 生成一个pip-install.sh脚本,在所有 pod 上安装 Python 依赖项。
  • 生成一个horovod-run.sh脚本来启动 Horovod 作业。
  • 将源代码和生成的脚本从我的工作站复制到 Horovod 主 pod 的共享 PV 中。

运行pre-train.sh脚本后,我的主 pod 将在共享 PV 中包含这些文件:

root@horovod-primary-84fcd7bdfd-2j8tc:/tf/models/examples# ls
horovod-run.sh  imdb-sentiment.py  pip-install.sh  pre-train.sh  requirements.txt

下面是一个生成horovod-run.sh的例子:

mkdir -p /tf/models/aclImdb/checkpointsAWS_LOG_LEVEL=3 horovodrun -np 3 \
 -H localhost:1,10-244-1-129.default.pod:1,10-244-0-145.default.pod:1 \
 -p 2222 \
 python imdb-sentiment.py

该脚本在三个并行单元上运行培训作业,每个单元使用一个 CPU。这里我们不用 GPU,因为模型很小。

因为一切都是自动化的,所以每次我在我的 VSCode 中更改培训代码时(我使用远程扩展通过 SSH 在服务器上编写代码),我都会运行以下代码来启动培训作业:

  1. 运行pre-train.sh脚本重新生成并复制源代码。
  2. 进入 Horovod 主吊舱。
  3. 运行pip-install.sh在所有 pod 上安装依赖项。
  4. 运行horovod-run.sh开始 Horovod 培训工作。

到目前为止,这个工作流程对我来说很好。

具有张量流的 Horovod

在 TensorFlow 中使用 Horovod 所需的培训脚本的修改在这里有详细说明。

我的示例代码是一个端到端可运行的脚本,用来训练一个影评情感模型。它类似于单节点训练,除了:

  • 代码在所有的 Horovod pods 上并行运行。
  • 每个 pod 只处理训练和验证批次总数的一部分,因此需要切分数据集(使用tf.data.Dataset.shard())并在调用model.fit时正确设置steps_per_epochvalidation_steps
  • 一些任务,例如保存检查点、张量板日志和模型,应该注意只在主 pod ( hvd.rank() = 0)上运行,以防止其他工作人员破坏它们。
  • 因为 pods 可以在 Kubernetes 集群中的任何服务器上运行(只有在请求 GPU 资源时才运行 GPU 节点),所以我们应该将检查点、TensorBoard 日志和模型保存在持久卷(在我的示例中是 FlashBlade NFS)或对象存储(例如,FlashBlade S3)中。

我将在这里跳过训练代码的细节。请参考我的示例代码

下面是一个运行输出:

运行输出示例

如果我查看我的 Kubernetes 监控 UI,我可以看到所有 Horovod pods 的 CPU 使用率都在上升。这表明培训作业正在所有 pod 上并行运行。

Horovod 训练期间的 Pod 资源使用

摘要

分布式训练是深度学习的未来。使用 Horovod 和 Kubernetes,我们演示了快速旋转动态分布式深度学习训练环境的步骤。这使得深度学习工程师和研究人员能够轻松共享、调度和充分利用昂贵的 GPU 和数据。

像 FlashBlade 这样的共享存储在这种设置中起着重要的作用。FlashBlade 使资源和数据共享成为可能。它让我不用保存/聚集检查点、张量板日志和模型。有了 Kubernetes 和 FlashBlade 的 Horovod 让我的深度学习生活变得容易多了。

Excel 中不再有时间表!

利用 Ansible、AWS 和 Pytorch Lightning 进行分布式深度学习。第一部分

原文:https://towardsdatascience.com/distributed-deep-learning-with-ansible-aws-and-pytorch-lightning-part-1-91d500e1039f?source=collection_archive---------40-----------------------

维多利亚博物馆在 Unsplash 拍摄的照片

如何使用 Ansible、AWS 云基础设施和 Pytorch Lightning 库来自动化和扩展您的深度学习实验。

假设你是一名深度学习实践者,但你没有内部的 GPU 集群或机器学习平台可供你使用。近十年来,没有人在 CPU 上训练他们的模型。更糟糕的是,随着模型和数据集变得越来越大,你必须处理分布式深度学习,并在模型并行和/或数据并行机制中扩展你的训练。我们能做些什么呢?

我们可以遵循现代云范式,利用一些 GPU 即服务。它将允许您按需动态分配必要的基础设施,并在完成后释放它。它工作得很好,但这是主要的复杂性所在。像 PyTorch Lightning 或 Horovod 这样的现代深度学习框架使数据并行分布式学习变得很容易。最烦人和耗时的事情是创建一个合适的环境,因为我们经常必须手动完成。即使对于那些对你隐藏了大量基础设施复杂性的服务——比如谷歌协作paper scape——一些手工工作仍然需要完成。

我坚信重复性的日常工作是你的敌人。为什么?以下是我个人关注的问题:

  1. 结果的再现性。你听说过所谓的人为因素吗?我们是非常容易出错的生物,我们不擅长记忆非常详细的东西。一些过程涉及的人力工作越多,将来就越难复制。
  2. 精神涣散。深度学习是一种经验主义的努力,你在这方面的进步深深依赖于你快速迭代和尽可能多地测试假设的能力。由于这个事实,任何分散你注意力的事情,比如训练和评估你的模型或者分析数据,都会对整个过程的成功产生负面影响。
  3. 效力。计算机做许多事情都比我们人类快得多。当你不得不一遍又一遍地重复同样缓慢的过程时,这一切就累积起来了。

常规是你的敌人

在这篇文章中,我将描述如何自动化进行深度学习实验的方式。

自动化您的深度学习实验

以下是这篇文章的三个主要观点:

  1. 利用基于云的基础设施为您的培训目的动态分配资源;
  2. 使用 DevOps 自动化工具集管理实验环境设置的所有手动工作;
  3. 在现代深度学习框架中编写您的训练程序,使其能够毫不费力地进行数据并行分布式学习。

AWS EC2、Ansible 和 Pytorch Lightning。作者图片

为了实际实现这些想法,我们将利用 AWS 云基础设施、Ansible 自动化工具和 PyTorch Lightning 深度学习库。

我们的工作将分为两部分。在本文中,我们将提供一个最小的工作示例,它:

  • 为我们的深度学习集群自动创建和销毁 EC2 实例;
  • 建立 Pytorch 和 Pytorch Lightning 分布式培训所需的连接;
  • 创建本地 ssh 配置文件以启用到群集的连接;
  • 创建一个 Python 虚拟环境,并为实验安装所有库依赖项;
  • 提供提交脚本,以便在创建的集群上运行分布式数据并行工作负载。

在下一篇文章中,我们将添加额外的特性,并为分布式学习实验构建一个完全自动化的环境。

现在,让我们简要概述一下所选的技术体系。

什么是 AWS EC2?

来源:https://github.com/gilbarbara/logos/tree/master/logos

AWS 弹性计算云(EC2) 是一项核心的 AWS 服务,允许您管理亚马逊数据中心的虚拟机。使用此服务,您可以通过 AWS 控制台或 AWS SDK 提供的 API 手动动态创建和销毁您的机器。

截至今天,AWS 为我们的目的提供了一系列支持 GPU 的实例,每个实例有一个或多个 GPU,以及不同的 NVIDIA GPUs 选择:Tesla GRID K520、M60、K80、T4、V100。完整名单见官方网站

什么是 Ansible?

来源:https://github.com/gilbarbara/logos/tree/master/logos

Ansible 是一款软件和基础设施供应和配置管理工具。使用 Ansible,您可以远程供应整个远程服务器集群,在其上供应软件,并监控它们。

这是一个用 Python 编写的开源项目。它使用声明式方法:您定义一个期望的系统状态,Ansible 执行必要的动作。要做到这一点,你可以使用普通的 YAML 文件。Ansible 的声明性也意味着你为它定义的大多数指令都是幂等的:如果你不止一次运行它,它不会引起任何不良的副作用。

Ansible 的一个显著特征是它是无代理的,即它不需要在可管理的节点上安装任何代理软件。它仅通过 SSH 协议运行。因此,您唯一需要确保的是运行 Ansible 命令的控制主机和您想要管理的清单主机之间的 SSH 连接。

可行的核心概念

让我们深入探讨一下 Ansible 的核心概念。这些工具并不多,所以您可以很快了解它们,并开始使用这个出色的工具。

  • 库存

清单只是您希望使用 Ansible 管理的主机列表。它们被组织成命名的组。如果您有一个静态的预定义基础结构,您可以在 INI 格式的文件中定义清单。另一种方法——使用清单插件,如果您的基础设施事先不知道或者可能动态变化(就像我们这里的情况),它会告诉 Ansible 在哪些主机上运行。

  • 模块

模块是可以在 Ansible 中执行的工作单元。Ansible 中有大量的模块可供您使用。并且它构成了一个非常可扩展的架构。参见模块索引

  • 变量

这里没什么特别的。您可以像在任何编程语言中一样定义变量,以便将逻辑与数据分离,或者在系统的不同部分之间传递信息。Ansible 收集了大量的系统信息,并将它们存储在预定义的变量中——事实。你可以在官方文档中阅读更多关于变量的内容。

  • 任务

任务是带有一些参数的模块调用。您还可以为任务定义名称、存储结果的变量、条件表达式和循环表达式。下面是一个任务示例,当定义了some_variable变量时,该任务将一些本地文件复制到远程计算机的文件系统中:

复制任务示例

  • 播放

Ansible 中的行动是一种将任务列表应用于清单中的一组主机的方式。在 YAML,你把戏剧定义为字典。hosts参数指定一个库存组,tasks参数包含一个任务列表。

  • 剧本

行动手册只是一个 YAML 文件,其中包含要运行的行动列表。运行行动手册的方法是将它传递给 ansible 安装附带的 ansible-playbook CLI。

下面的图表说明了这些概念是如何相互影响的:

可行的核心概念

Ansible 中还有更高级的概念,允许您为复杂的场景编写更模块化的代码。我们将在文章的第 2 部分使用其中的一些。

Pytorch 闪电是什么?

来源:维基百科

Pytorch Lightning 是 Pytorch 之上的一个高级库。你可以把它想成 PyTorch 的 Keras。有几个特性使它从其他基于 PyTorch 的深度学习库中脱颖而出:

  • 是透明的。正如作者在文档中所写的,编写 Pytorch 代码更像是一种约定,而不是一个单独的框架。您不需要学习另一个库,也不需要付出巨大的努力来转换您的普通 PyTorch 代码,以便与 Pytorch-Lightning 一起使用。你的 PyTorch 闪电代码实际上就是你的 PyTorch 代码。
  • 它隐藏了很多样板工程代码。Pytorch 是一个出色的框架,但是当使用它进行全功能实验时,您很快就会得到许多与您正在进行的实际研究没有特别关系的代码。而且每次都要重复这个工作。Pytorch 照明为您提供了这一功能。具体来说,它将分布式数据并行学习能力添加到您的模型中,而不需要您对代码进行任何修改!
  • 很简单。所有 PyTorch Lightning 代码库都围绕着一些抽象概念:
  1. LightningModule是一个组织 PyTorch 代码的类。使用 PyTorch Lightning 的方法是创建一个从LightningModule继承的自定义类,并实现它的虚拟方法。LightningModule本身是从 PyTorch 模块继承来的。
  2. Trainer自动化您的训练程序。一旦您将 PyTorch 代码组织到 LightningModule 中,您就将它的实例传递给一个Trainer,它会完成实际的繁重训练。
  3. CallbacksLoggersHooks是定制Trainer行为的手段。

PyTorch 闪电建筑

欲了解更多信息,请阅读官方文档

好了,说够了,让我们开始建设。

构建实验工具集

在本文的其余部分,我将带您一步步地构建我们的实验环境。如果你对最终结果感兴趣,这里有一个 GitHub 回购的链接。另外,请看第 2 部分,我们将向我们的工具集添加额外的特性。

设置 AWS 帐户和 Ansible

让我们安装 Ansible 并将其配置为与 AWS 一起工作。

设置 AWS 账户并配置 AWS SDK

如果你没有 AWS 账户,你首先要做的就是建立一个。为此,请访问官方文档中的创建新账户链接,并按照说明进行操作。

接下来,让我们安装 AWS SDK 来获得 Ansible 所需的对 AWS 的 API 访问:

pip install boto

我们需要 AWS API 的凭据。要获取它们,请登录您的 AWS 控制台,并按照中的说明进行操作。选择一个编程访问选项,并应用 AdministratorAccess 策略为您的 API 提供管理访问权限。是的,一般来说这不是一个好的做法,所以您应该在将来将它改为更具限制性的特权。

将您新创建的用户凭证放到您的.bashrc文件中:

echo "export AWS_ACCESS_KEY_ID=<your key id>" >> ~/.bashrc
echo "export AWS_SECRET_ACCESS_KEY=<your secret key>" >> ~/.bashrc
source ~/.bashrc

设置 SSH 密钥并获取默认 VPC ID

我们将使用默认的 SSH 密钥对(~/.ssh/id_rsa~/.ssh/id_rsa.pub)来连接 EC2 实例。如果你的系统上还没有,你应该用ssh-keygen工具生成它。一旦生成,就在 AWS EC2 服务中注册它。您可以在 AWS 控制台的 EC2 服务中的键对菜单选项下进行操作。请注意,这些键是特定于地区的,所以您需要在您计划创建 EC2 实例的同一地区下注册它们。

接下来,我们需要复制默认 VPC 的 ID。VPC(虚拟专用网)是 AWS 云中的一个虚拟网络,在这里您可以连接 EC2 实例和其他 AWS 服务。我们将在实验中使用默认的 VPC。转到 AWS 控制台中的 VPC 服务,打开一个区域中所有 VPC 的列表。找到默认 VPC 列值设置为的那个。

最后,创建一个config.yaml文件,并将注册的 SSH 对名称和 VPC ID 写入其中:

aws_ssh_key: <your registered ssh key pair name here>
vpc_id: <ID of your default VPC>

我们将在以后的行动手册中导入该文件。

设置责任

Ansible 是用 Python 编写的,因此可以用一个命令轻松安装:

pip install ansible==2.9.9

你可以安装最新的版本或者我写这篇文章时使用的版本。

禁用易受攻击的主机密钥检查

因为我们没有预定义的静态基础设施,所以通过 Ansible 禁用主机键检查更方便。您可以在/etc/ansible/ansible.cfg中为所有用户进行全局设置,在~/.ansible.cfg中为特定用户进行全局设置,或者在./ansible.cfg中为给定项目进行本地设置。无论使用哪种方法,都可以创建包含以下内容的文件:

配置 Ansible AWS EC2 动态清单插件

还记得 Ansible 库存插件吗?因为我们动态地创建 EC2 实例,并且没有给它们分配任何预定义的 DNS 名称,所以我们事先不知道它们的地址。AWS EC2 库存插件将在这里帮助我们,并为我们的行动手册提供它们。我们需要为它创建以下配置文件:

aws_ec2.yaml

您可以在这里定义插件在哪里以及如何寻找实例。regions字段包含 AWS 地区列表。filters字段定义元数据属性,通过该属性过滤感兴趣的实例。我们使用一个managed_by标签来识别它们。稍后,我们将把这个标签分配给我们用工具集创建的实例。

为提交脚本安装附加依赖项

我们的提交脚本需要在本地工作站上安装几个额外的 Python 包:

pip install Click==7.0 fabric==2.5.0 patchwork==1.0.1

我们解决方案的总体描述

我们将使用以下 Ansible 模块和插件来完成这项工作:

  • [ec2](https://docs.ansible.com/ansible/latest/modules/ec2_module.html)创建 EC2 实例的模块;
  • [ec2_group](https://docs.ansible.com/ansible/latest/modules/ec2_group_module.html)为我们的 EC2 实例创建安全组的模块;
  • 用于为 EC2 实例创建集群放置组的模块;
  • [aws_ec2](https://docs.ansible.com/ansible/latest/plugins/inventory/aws_ec2.html)库存插件,用于发现创建的 EC2 实例,并将它们添加到库存中;
  • [ec2_instance](https://docs.ansible.com/ansible/latest/modules/ec2_instance_module.html)模块终止我们的实例。

为了向集群提交我们的训练脚本,我们将使用 fabric python 包。

代码将被分成以下文件:

  • setup-play.yml:为实验创建 EC2 基础设施的剧本。此外,它还引入了环境行动手册;
  • environment-play.yml:剧本规定了在 EC2 实例上进行实验所需的环境;
  • cleanup-play.yml:破坏 EC2 实例释放资源的剧本;
  • config.yml:包含实验环境所有配置的变量文件;
  • aws_ec2.yml:AWS EC2 动态清单插件的配置文件;
  • submit.py : CLI 提交要在集群上运行的培训脚本。

设置基础设施行动手册

setup-play.yml

本剧本包含两个剧本。第一个在控制节点上执行(即在我们的本地工作站上),它的工作是为我们的集群创建 EC2 基础设施。让我们看一下它的任务:

  • 创建群集放置组。我们稍后将把它提供给我们的 EC2 实例,以建议 AWS 将它们放置得彼此靠近,并减少网络通信的延迟;
  • 创建安全组来配置实例的防火墙规则。我们允许实例之间的所有流量和来自互联网的 SSH 流量。注意,我们传递 config.yml 中定义的变量;
  • ec2模块创建 EC2 实例。我们标记它们,以便aws_ec2动态清单插件能够在以后找到它们。我们将这个任务的结果注册到ec2变量中;
  • 存储第一个创建的实例的本地 IP 地址,使其成为我们深度学习集群中的主节点;
  • 创建一个 SSH 配置文件,以便能够连接到 Ansible 外部的实例;
  • 将创建的实例添加到主机清单中的新主机组,以便第二个 play 能够对它们进行操作;
  • 等到创建的实例准备好接受 SSH 流量

第二个剧本包含一个任务,其目标是定义 PyTorch Lightning 所需的环境变量。

最后,我们导入了环境行动手册。

提供环境剧本

环境-游戏. yml

通过这个行动手册,我们将环境(以及对它的更改)部署到实例中。它相对简单,只包含 3 个步骤:

  • 为实验创建 Python virtualenv
  • 复制包含所有第三方依赖项的 requirements.txt 文件
  • 安装它们

清理剧本

cleanup-play.yml

本行动手册允许您终止 EC2 实例并删除 SSH 配置文件。

最后,让我们看看您应该为每个实验的需求定制的配置文件。

配置文件

我们已经在准备部分创建了这个文件。这是添加了一些附加参数的最终版本:

好吧,那我们现在有什么?

我们可以在配置文件中提供我们想要创建的实例的种类和数量,并在 requirements.txt 文件中指定我们想要在其上安装的库,然后运行一个命令:

ansible-playbook setup-play.yml

几分钟后,一个随时可用的集群将由我们支配。我们可以通过以下方式 SSH 到它的实例:

ssh -i ssh_config worker[x]

当我们完成后,我们可以用以下方法销毁它:

ansible-playbook -i aws_ec2.yaml cleanup-play.yml

现在,让我们在新的闪亮深度学习集群上简化代码部署和培训过程的实际运行。

将培训程序部署到集群

首先,让我们创建一个示例模型和一个培训过程。

培训程序示例

为了使事情变得简单并专注于文章的主题,我选择了深度学习“你好,世界!”举例。让我们采用一个简单的 3 层全连接网络,并在 MNIST 数据集上对其进行训练。代码非常简单明了,由一个简单的 lightning 模型和用一个Trainer适合这个模型的main过程组成。

ddp _ 火车 _ 示例. py

它使用 awesome hydra 库进行参数化。下面是一个带有参数的 YAML 文件:

ddp_train_example.yaml

文件中的默认参数允许我们在本地笔记本电脑上以单节点模式运行脚本,而无需任何 GPU。让我们在本地运行该脚本,并确保它正常工作:

python ddp_train_example.py max_epochs=5

现在,正如我前面所说的,PyTorch Lightning 的伟大特性是,您实际上不需要修改任何代码就可以在集群上以数据并行分布式模式运行它。我们唯一需要改变的是传递给我们在 hydra 配置中定义的Trainer实例的几个参数。要在各有一个 GPU 的两个节点上运行我们的脚本,我们应该以如下方式调用它:

python ddp_train_example.py gpus=1 num_nodes=2 \ distributed_backend=ddp

剩下唯一要做的事情就是实现一个可重用的 CLI 来将我们的训练脚本提交给集群。

提交脚本

提交 CLI 将任何 Python 脚本及其参数作为参数。它将同步当前工作目录中的所有文件,并在所有集群节点上运行给定的脚本。有了 fabric Python 库,我们只用几行代码就能做到:

提交. py

实际的提交逻辑驻留在run函数中。main函数在所有集群节点上调用它:在工作节点上异步调用,在主节点上同步调用。主节点上运行的脚本的所有标准输出都会自动打印到工作站上的 stdout。

最后,让我们向集群提交我们的培训脚本:

./submit.py -- ddp_train_example.py \
   gpus=<number of GPUs per instance> \
   num_nodes=<number of nodes in our cluster> \
   distributed_backend=ddp

仅此而已。我们的模型就像在本地机器上一样被训练,但是使用 Pytorch 和 PyTorch Lighting 分布式学习功能。

结论

那么我们最后得到了什么?只需三个命令,您就可以在 AWS 中动态创建深度学习集群,在其上提交训练作业,并在完成实验后删除它:

# create our deep learning cluster
ansible-playbook setup-play.yml# submit training job to it
./submit.py -- ddp_train_example.py \
   gpus=<number of GPUs per instance> \
   num_nodes=<number of nodes in our cluster> \
   distributed_backend=ddp# terminate the cluster
ansible-playbook -i aws_ec2.yaml cleanup-play.yml

您可以使这个功能可重用,或者只是将它复制到您的所有实验目录中。

现在,您可以将您的实验提升到一个新的水平,更加敏捷,并且不害怕分布式深度学习的可怕野兽。

但这并不是这个故事的结尾。在第 2 部分中,我们将为我们的工具集添加额外的功能,使我们能够使用 Jupyter Notebook 在集群上进行交互式工作,使用 Tensorboard 监控训练过程,并将实验结果存储到永久存储器中。

敬请期待!

TensorFlow 中豆类图像分类的分布式学习

原文:https://towardsdatascience.com/distributed-learning-on-image-classification-of-beans-in-tensorflow-5a85e6c3eb71?source=collection_archive---------32-----------------------

使用 TensorFlow 2.0 的内置函数执行分布式学习,并使用它来训练一个简单的图像分类模型。

深度学习已经导致了机器学习领域的各种技术进步,但它仍然受到在大数据集上训练所需的大量计算时间的困扰。用于基准测试的 ImageNet 等训练数据集可能需要一个 GPU 系统长达一周的时间。另一方面,在多台机器或 GPU 之间划分训练的分布式训练已经被认为极大地减少了这种训练时间。

TensorFlow 提供了使用不同算法执行分布式学习的内置功能。今天,我们将了解“镜像策略”,并在“bean”数据集上实施该策略。

导入依赖项并下载数据集

首先,我们需要下载所需的库和我们将要处理的数据集。

既然我们已经下载了数据集及其依赖项,我们就可以开始编写代码了。

代码入门

现在,在我们开始训练之前,需要一些必要的步骤。需要定义超参数,更重要的是,我们需要初始化分布式学习算法。

我们通过从数据集中检索来初始化训练和测试图像的数量。我们还为我们的训练定义了一个缓冲区大小和批量大小,这将取决于 GPU 的数量。你拥有的 GPU 越多,你就可以保持更大的批量,这意味着更快的训练。

创建模型和补充功能

我们将使用一个简单的 CNN 模型在这个数据集上进行训练,并演示分布式学习功能。我们的 CNN 也将需要一些功能,我们将在此过程中定义。

首先,我们需要做一些轻微的数据预处理和归一化我们的图像的像素值。我们还对训练数据进行批处理,并在内存中保存训练数据的缓存,以提高性能。

现在我们可以开始建设我们的 CNN 了。

最后,我们需要定义一些补充功能,包括检查点、学习率衰减和回调。

检查点将在每个时期后保存模型的进度,随着时期数量的增加,学习率衰减将帮助我们获得动态学习率,并且模型在每个时期结束时使用回调来运行所有这些功能。

一旦模型构建完成,补充功能定义完成,我们就可以进入激动人心的部分,训练模型。

训练模型

这是代码中最简单的部分,但也是代码运行中最重要的部分。我们现在可以在数据集上训练模型,这将通过分布式学习来实现!

分布式学习策略

有各种策略或算法可用于分布式学习。首先,让我们来看看我们在代码中使用的镜像策略。

镜像策略 支持一台机器上多个 GPU 的同步训练。它会为每个 GPU 设备创建一个副本,并且模型中的每个变量都会跨所有副本进行镜像。通过应用相同和同时的更新,变量的所有副本彼此保持同步。

多工人镜像策略 是镜像策略的一个版本,但用于多工人培训。这就是我们可以在多台机器而不仅仅是一台机器上使用分布式学习的地方。这种策略对多个工作者使用同步训练,每个工作者可以有多个 GPU。它创建每个工人的每个设备的模型中的所有变量的副本。

参数服务器策略 是一种灵活的策略,因为它允许在多个 GPU 上进行同步本地训练,也允许跨多台机器进行异步训练。其本地训练与镜像策略的不同之处在于,它不是创建变量的副本并同时更新所有变量,而是将变量存储在 CPU 上,并在所有本地 GPU 上复制操作。

以上所有策略只是分发算法的一些例子。它们都可以使用 tf.distribute 库在 TensorFlow 中实现。

# *To use Mirrored Strategy, we have used this in our code* 
$ tf.distribute.MirroredStrategy()# *To use Multi Worker Mirrored Strategy*
$ tf.distribute.MultiWorkerMirroredStrategy()# *To use Parameter Server Strategy*
$ tf.distribute.ParameterServerStrategy()

但是一切都变好了吗?

由于各种策略各不相同,各有利弊。

我们的目标是最大限度地利用 GPU 的能力,同时尽可能少地浪费数据获取的时间。在镜像策略中,当我们在具有多个 GPU 的单个本地系统上使用分布式学习时,我们看到 GPU 的使用远非完美。理想的情况是,平均来说,你的每个 GPU 都得到了同等的优化,但这并没有发生。但是,我们确实看到,随着检索指令所花费的时间减少,内存访问时间明显缩短,因为任务被划分到多个 GPU 上。最重要的是,随着数据集变得越来越大,我们可以预计分布式学习过程将更多地减少训练时间。为了进一步改善这一点,TensorFlow 具有预取功能,可在当前时段进行的同时获取下一时段的数据。但是,该功能仍处于试验阶段,可能会有错误。

多工作器镜像策略允许您获得更高的性能,因为多个工作器拥有自己的 GPU 集,但同时,该策略容易出现系统错误。如果其中一名员工出现系统错误,由于培训过程需要在所有员工之间同步,TensorFlow 会暂停培训过程,直到该员工重新上线。这确实导致训练时间的增加,并且训练过程越长,网络或系统故障的机会就越多,训练时间就越长。此外,检查点评估不能由任何工作线程完成,因为当一个工作线程正在评估检查点时,其他工作线程将不得不等待,这可能会导致通信超时。因此,检查点的评估由与所有工作人员通信的主系统完成,并且该过程完全由一台机器的能力决定。

代码的完整笔记本可以在 colab 这里找到。

Unsplash 上由蒂贾娜·德恩达尔斯基拍摄的照片

用 Python 和 Dask 实现分布式机器学习。

原文:https://towardsdatascience.com/distributed-machine-learning-with-python-and-dask-2d6bae91a726?source=collection_archive---------28-----------------------

使用 Python 和 Dask 的分布式机器学习

使用 Dask 的多节点分布式训练

照片由奥列格马尼 发自Pexels

资料科学是一门实验科学。我们可以知道哪种算法在验证或测试集上表现得“好”,但是在我们尝试之前,我们怎么知道呢?有多少次你听到一个同事说:“哪种算法效果最好取决于数据?我们需要实验。”然后,我们继续同意训练和评估通过优化参数权重将监督学习算法转化为模型,我们找到最符合数据基本事实的一组值。

考虑到这一点,我将演示 Dask 如何让您减少算法训练时间。事实上,Dask 让您可以选择减少那些痛苦的长时间运行,并允许您尝试更多的算法或参数组合。我们将回答这些问题—我为什么要利用分布式计算?为什么是达斯克?

之前我们已经了解了 Dask 是如何工作的,使用 Dask 组装一个独立的集群预处理文本。

如果你正在寻找运行 Dask 的快捷方式,你可以试试 土星云 ,它提供了一个免费的计划。

我们试图预测什么?

我想根据文本将亚马逊产品的评论分为正面或负面。显然,在将数据输入到 ML 训练管道之前,我需要对数据进行一些转换。

数据呢?

我使用的是亚马逊提供的公开数据集,有 300 万条评论和评级。这是一个 1.5 GB 的中型数据集,但是本文的目的是展示如何以及为什么使用 Dask 来处理更大的数据集。

加载集群

在进入我们的笔记本之前,我们需要几个快速命令来准备好我们的独立集群。如果您遇到问题,本文将指导您创建一个好的独立集群。

首先,与您的每台机器建立一个 SSH 连接。让我们从您想要选择作为调度程序的机器开始。您只需要输入以下命令:

dask-scheduler

我的独立 Dask 集群有五个工人(机器)。如果要将这些节点与调度程序连接,需要在“Worker node”终端运行以下命令:

调度程序节点

现在,您需要连接其他机器作为工作节点。为此,您需要在调度程序终端输入以下命令。在下面的示例中,调度程序节点的 IP 是 10.1.1.93,Dask 应该连接的端口是 8786。

# You should specify the IP and port of the scheduler node as below. dask-worker tcp://10.1.1.93:8786

将第一个工作线程连接到调度程序节点

我将再连接四台机器作为工作节点,这使得我的最终集群有一个调度器节点和五个工作节点。连接每个节点后,您应该会在调度程序终端中看到类似这样的内容:

连接五个工作节点后调度虚拟机

恭喜你。现在,您可以开始使用 Jupyter 笔记本了。

Dask 提供了一个漂亮的仪表板,您可以在其中看到正在运行的进程的所有指标。您可以使用以下命令让它运行:

# load the cluster
import dask 
from sklearn.externals import joblib
from dask.distributed import Client# IP of the scheduler node.c = dask.distributed.Client('tcp://10.1.1.93:8786')# call the cluster c

单击 dashboard 链接,它应该会在您的浏览器中打开一个新选项卡。Dask 的仪表板可以让你实时看到每个工人正在做的所有计算。太棒了,对吧?

Dask 仪表板

加载数据

接下来,我将向你展示一种将数据加载到 dask 数据帧中的方法,它不同于我们在之前使用的。我将把数据加载到 pandas 数据帧中,然后转换成 Dask 数据帧。

此外,您可以使用参数npartitions = 5指定分区的数量。事实上,Dask 工作负载是由任务组成的,我建议您构建更小的图(DAG)。你可以通过增加你的块大小来做到这一点。

为了使用更易于管理的数据集来演示这个问题,我从之前的文章中描述的清理过的数据集中选择了 10,000,000 条评论来填充我们的模型。我认为这一数量的案例很好地允许你在你的本地环境中复制这个实验。您可以在以后对更大的数据负载使用相同的方法。在这一点上,您应该检查您的集群是否有足够的容量,因为一旦您对您的文本进行矢量化,您将为您的模型提供一个巨大但稀疏的矩阵。

4.3 拆分数据

使用 dask_ml 包拆分数据

4.4 数据矢量化

这篇文章的主要目的不是传达 NLP 的概念,但是我们确实需要传达一些核心的 NLP 概念,以引起这个例子的共鸣。首先要考虑的是,我们使用的是字符串。因此,需要将单词编码为整数或浮点值,以用作机器学习算法的输入。这被称为特征提取或矢量化。

Scikit-learn 库提供了易于使用的工具来执行文本数据的标记化和特征提取。我们将使用 tfidf 矢量器和哈希矢量器。

  • tfidf 矢量器

变形金刚出现在 NLP 场景之前,我的最佳表现模型是按照使用 TF-IDF 对单词的相对重要性评分的策略建立的。

Dask 不支持本地分布式 TF-IDF 矢量化。事实上,这是一个复杂的问题,因为对于大多数计算环境,TF IDF 需要内存中的整个术语文档矩阵。这就是为什么我们将在后面回顾 HashingVectorizer 的原因,它本来就包含在 dask_ml 库中。我还想提到这个方法,因为如果整个数据集都在内存中,可能会获得更好的性能。

TF-DIF

  • 哈希矢量器

如前所述, HashingVectorizer 是另一个文本矢量器实现,它使用散列技巧来查找标记字符串名称,以实现整数索引映射。它将一组文本文档转换成一个 scipy.sparse 矩阵,保持令牌出现计数(或二进制出现信息)较低。最重要的是,它受到 dask_ml 的支持。

[dask_ml.feature_extraction.text](https://ml.dask.org/modules/generated/dask_ml.feature_extraction.text.HashingVectorizer.html)T1。哈希矢量器

这种策略有几个优点:

  • 它的内存非常低,可扩展到大型数据集,因为不需要在内存中存储词汇词典
  • pickle 和 un pickle 的速度很快,因为除了构造函数参数之外,它不保存任何状态
  • 它可以用于流(部分 fit)或并行管道,因为在 fit 期间没有计算状态。

哈希矢量器

提示:如果您有一个巨大的数据集(100GB 或更多的数据),请按照本文 应用这个方法。

可扩展多节点训练

现在我将向你展示 Dask 如何加速我的算法。我们将使用 Xgboost 模型并从 Dask 库中随机搜索超参数优化。如果你想深入研究训练算法和管道,请参考我的 github 中的。我将重点介绍这一部分,向您展示结果和输出。

我们已经创建了一个自定义函数来帮助我们理解使用 sklearn 库训练单节点模型与使用 dask 在集群中训练多节点模型之间的区别。

我已经决定应用哈希矢量器作为这个例子的文本矢量器实现,但是如果你想在产品中实现你的模型,我也会尝试 TfidfVectorizer 和其他方法。记住这一点为您的模型评估选择正确的指标至关重要。在我们的例子中,我为您打印准确性,因为它已经成为“全球”评估指标。然而,我使用 ROC 作为我培训管道中的主要评估者。事实上,我的经验告诉我,精度对于二元分类来说不是一个好的模型评估器,因为您可能会有不平衡的数据。

提醒一下,这篇文章的主要目的是演示 Dask 如何让你减少训练算法的时间。事实上,它给了你减少那些痛苦的长时间运行的选择,这反过来允许你尝试更多的算法或参数组合。

那么,让我们看看我用 Scikit-Learn 库用单个节点训练模型花了多少时间:

没有 DASK 的训练

模型的训练时间是 12 分 52 秒,我们得到了 0.7415 的准确度。我只使用一个 8 核节点来训练模型。作为一个友好的提醒,Scikit-Learn 允许您使用参数njobs =-1在这 8 个内核上训练算法。

但是,让我们看看使用多节点集群和 Dask 的模型训练的结果。

与 DASK 一起训练

哇!!训练时间是 7 分 37 秒,我们得到了精度0.7615 。因此,使用 DASK 的模型训练比仅使用 Scikit-Learn 库的单个节点快 1.69 倍

结论

我们已经证明了 dask 可以让你加快训练进度。我知道您可能没有访问集群的权限,或者您在自己构建集群时面临一些困难。然而,云计算正变得越来越好,它提供了您可能需要的灵活性,而无需复杂的学习曲线。总之,您可以在 AWS 或 Azure 中轻松实现 Dask。请继续关注,因为我们会告诉你怎么做。

非常感谢你的阅读。

[https://github.com/dask/dask-ml/issues/5](https://github.com/dask/dask-ml/issues/5)[https://ml.dask.org/modules/generated/dask_ml.feature_extraction.text.HashingVectorizer.html](https://ml.dask.org/modules/generated/dask_ml.feature_extraction.text.HashingVectorizer.html)[https://examples.dask.org/machine-learning/text-vectorization.html](https://examples.dask.org/machine-learning/text-vectorization.html)[https://dask.org/](https://dask.org/)

使用 Dask 的分布式机器学习介绍

原文:https://towardsdatascience.com/distributed-machine-learning-with-python-and-dask-introduction-328a86571049?source=collection_archive---------34-----------------------

使用 Python 和 Dask 的分布式机器学习

Dask 是 Python 的一个包,它将【扩展】你的数据科学技能。

Pablo Salvador LopezAadarsh Vadakattu 合作撰写

照片来自 Pexels

Dask 将帮助您使用 Python 扩展您的数据科学技能。您将能够处理大数据并扩展您的代码,从而提高您的工作效率。

Dask 简介

欢迎数据科学爱好者!您之所以对本文感兴趣,是因为要么您一直在痛苦地挣扎着长时间运行,要么您的数据集适合您的 RAM。如果我没猜错的话,你是一个 Python 用户,能够自如地使用最常见的数据科学库,如 PandasScikit-LearnNumpy。读完这组文章后,你将学会如何使用 Dask,它能提高你的工作效率,让你在没有痛苦的学习曲线的情况下处理大数据。

我们将使用 Dask Python 包来扩展 NLP 文本分类问题。我们还将回顾支持可伸缩 ML 训练管道的数据转换。

如果您已经了解了 Dask 和分布式计算的基础知识,请继续阅读第二篇文章,回顾如何使用 SSH 构建 Dask 集群。

Dask 概念

如果您曾经使用过 Apache Spark,那么这一部分对您来说会非常熟悉。下面这个外行的例子演示了 Dask 如何使用两个关键概念扩展任务:有向无环图(‘DAG’)和惰性计算。

想象一下这个场景。这是一个星期天,一个家庭决定做一个蛋糕。他们没有任何配料,所以妈妈开车带着四个孩子去超市。她讨厌购物,商店的停车条件也很差,所以今天她决定让孩子们去工作:

  • 她把孩子们放在门口,绕着街区转了一圈。每个孩子都去商店寻找不同的原料:黄油、鸡蛋、面粉、糖等。
  • 在每个孩子拿了单子上的所有食材后,他们在收银台和他们的兄弟姐妹见面。他们会在那里等到四个兄弟姐妹都到了。
  • 他们通过群聊与妈妈交流,妈妈指导他们在一次交易中为产品付款。
  • 然后妈妈绕回商店门口。孩子们回到车里,开车回家。

妈妈相信这个新计划将使家庭购买所有产品的速度比一起完成所有步骤快四倍。下图是妈妈的策略:

家庭之犬

现在,让我们用计算机科学术语来编写这个例子,以展示 DASK 如何允许数据科学家创建可扩展的工作负载。将家庭中的每个成员想象成一台虚拟机(VM),或者更一般地说,一个节点。母亲是调度器节点,她正在与她的孩子通信,在这种情况下,孩子是其他计算节点,就像工作人员一样扩展任务集。此外,工作人员相互沟通,以确保他们根据计划完成并行任务(在计算机科学术语中称为 DAG ),并且不超过他们的最大内存或 CPU / GPU 使用率。

请记住,每个孩子可能有不同的特点,孩子 1 可能比孩子 2 更年轻、更快。类似地,每个工作节点可能具有不同数量的CPU,并且可能具有更大或更小的磁盘和 ram。这就是调度器在 Dask 架构中的作用——它协调工作负载,并以平衡的方式将工作分配给每个工作节点。

用外行人的话来说,这就是 Dask 的工作方式。我们现在清楚地知道 Dask 的调度程序如何使用 DAG(有向无环图)的概念在所有可用的节点上分配计算。

那么另一个计算机科学概念“懒惰计算”呢?

Dask 允许您为想要执行的计算构造一个处方(由数据科学家生成的 DAG)。Dask 是一个图形执行引擎,你写的所有不同的任务都被延迟。这意味着在您执行。compute()函数。

该函数将阻止执行,直到任务(或)构造的 DAG 完成。在 DAG 完成之后,整个计算直接从惰性 Dask 集合到本地内存中的具体值。这看起来可能很复杂,但是由于所有这些都是抽象的,您将在下面的文章中通过一个使用 NLP 的实际例子看到实现所有这些是多么容易。

希望你对接下来的部分感到兴奋!我保证不再有理论,只有可扩展的数据科学。

链接:

https://distributed.dask.org/en/latest/memory.html

https://stack overflow . com/questions/41806850/dask-difference-between-client-persist-and-client-compute

https://distributed . dask . org/en/latest/manage-computation . html

https://tutorial.dask.org/01x_lazy.html

[## PabloSalvadorLopez10 —概述

在 GitHub 上注册你自己的个人资料,这是托管代码、管理项目和构建软件的最佳地方…

github.com](https://github.com/PabloSalvadorLopez10)

PySpark 3.0 中基于 PyArrow 的新熊猫 UDF 的分布式处理

原文:https://towardsdatascience.com/distributed-processing-with-pyarrow-powered-new-pandas-udfs-in-pyspark-3-0-8f1fe4c15208?source=collection_archive---------13-----------------------

如何使用 Spark 3.0.0 支持的 PySpark 实现高性能的类似熊猫的用户定义函数(UDF)

来源

数据处理时间非常宝贵,因为用户花费的每一分钟都要付出经济代价。本文主要面向希望使用 Apache Spark 最新增强功能的数据科学家和数据工程师,因为在很短的时间内,Apache Spark 已经成为下一代大数据处理引擎,并以前所未有的速度在整个行业得到广泛应用。

Spark 的整合结构支持兼容和可构建的 API s,这些 API 通过优化程序中构建的各种库和函数来实现高性能,使用户能够构建超越现有库的应用程序。它也为用户提供了在上面编写自己的分析库的机会。

数据迁移成本很高,因此 Spark 专注于对数据进行计算,而不管数据位于何处。在用户交互 API 中,Spark 努力管理这些看似广泛相关的存储系统,以防应用程序不需要关心它们的数据在哪里。

当数据太大而无法在一台机器上运行时,在一台机器上执行计算需要很长时间,这就促使 it 将数据放在多台服务器或计算机上。这种逻辑要求以分布式方式处理数据。 Spark DataFrame 是一个终极的结构化 API,提供一个包含行和列的数据表。通过其列和列类型的模式,它可以跨越大量的数据源。

本文的目的是介绍 Spark 3.0 当前发布的一个特性的好处,该特性与 pandas 和 Apache Arrow 使用 PySpark 相关,以便能够以并行方式执行类似 Pandas 的 UDF。在下面的标题中,PyArrow 对 PySpark 会话配置的重要使用,PySpark 启用的 Pandas UDFs 将通过提供相应主题的代码片段来详细解释。在文章的结尾,为进一步的研究添加了参考文献和附加资源。

1.PyArrow 和 PySpark

在以前版本的 Spark 中,在 PySpark 中将 DataFrame 转换为 Pandas 的步骤效率很低,需要将所有行收集到 Spark 驱动程序,将每一行序列化为 Python 的 pickle 格式(逐行)、,然后将它们发送到 Python 工作进程。在这个转换过程的最后,它将每一行拆成一个庞大的元组列表。为了能够克服这些无效操作,可以使用与 Apache Spark 集成的 Apache Arrow 来实现更快的列数据传输和转换。

1.1.为什么使用 PyArrow 和 PySpark

Apache Arrow 有助于加速从传统列内存到 pandas 对象的转换,提供高性能的内存列数据结构。

以前,Spark 揭示了一个基于行的接口,用于解释和运行用户定义函数(UDF)。这在序列化和反序列化中引入了很高的开销,并且使得很难使用 Python 库,例如 NumPyPandas ,这些库是用本地 Python 编码的,这使得它们能够更快地编译成机器代码。

在新提出的 UDF 中,它主张引入新的 API 来支持 Python 中的矢量化 UDF,其中通过逐块序列化而不是逐行序列化,将数据块以某种列格式转移到 Python 中执行。

Pandas 包得到了机器学习和数据科学专家的认可,因为它与大量 Python 库和包进行了一致的集成,包括scikit-learnmatplotlibNumPy**

此外,Pandas UDFs 支持用户在 Apache Spark 中分发数据负载和使用 Pandas APIs。

用户自定义功能可参考官方网站执行:

  • Apache Arrow 通过利用 Arrow 列式内存布局加快字符串数据的处理,实现了数据在 Java 虚拟机和 Python 执行器之间的精确传输,且序列化成本为零。
  • Pandas 库使用它的实例和 API。

1.2.火花会话配置

为了获得更好的性能,在执行作业时,应设置如下配置。

为了能够从 PyArrow 优化中获益,可以通过将此配置设置为默认禁用的**true** 来启用以下配置:spark.sql.execution.arrow.pyspark.enabled

在出现错误的情况下,上面启用的优化可以退回到非箭头优化实现情况。为了应对 Spark 中实际计算出现的这个问题,fallback.enabled将被设置为**true** : spark.sql.execution.arrow.pyspark.fallback.enabled

由于以下原因,parquet-summary-metadata无法有效地启用以下配置:

  1. merge schema=false:假设所有拼花局部文件的模式都是相同的,因此,可以从任何局部文件中读取页脚。**
  2. merge schema=true:所有文件都需要读取页脚来实现合并过程。****
**spark.sql.parquet.mergeSchema **false**
spark.hadoop.parquet.enable.summary-metadata **false****

综上所述,Arrow 优化配置的最终推荐列表如下:

**"spark.sql.execution.arrow.pyspark.enabled", "**true**""spark.sql.execution.arrow.pyspark.fallback.enabled", "**true**"
"spark.sql.parquet.mergeSchema", "**false**"
"spark.hadoop.parquet.enable.summary-metadata", "**false**"**

1.3.要升级的包

正确使用 PyArrowPandasUDF 需要在 PySpark 开发平台中升级一些包。

需要更新以下软件包列表,以便能够正确使用 Spark 3.0 的 PandasUDF 最新版本。

****# Install with Conda**
conda install -c conda-forge pyarrow**# Install PyArrow with Python**
pip install pyarrow==0.15.0**# Install Py4j with Python**
pip install py4j==0.10.9**# Install pyspark with Python**
pip install pyspark==3.0.0**

此外,您可能需要分配一个新的环境变量,以便在运行 Pandas UDFs 时不会面临 py arrow 0 . 15 . 1 升级的任何问题。

****# Environment Variable Setting for PyArrow Version Upgrade**
import os
os.environ["ARROW_PRE_0_15_IPC_FORMAT"] = "1"**

2.Python 的 PyArrow

2.1.更快地处理拼花格式的文件

py arrow在读取拼花文件而非其他文件格式时,性能差距较大。在这个博客中,你可以找到关于不同文件格式读取的基准研究。

它可用于 Python 中处理时间不同的不同种类的包:

  • 拼花箭头: pyarrow.parquet
****# Importing PyArrow** 
import pyarrow.parquet as pqpath = "dataset/[**dimension**](https://www.kaggle.com/yassinealouini/m5-sales-hierarchy-dataset)"data_frame **=** pq**.**read_table(path)**.**to_pandas()**
  • 拼花到带熊猫的箭头数据框: pyarrow.parquet然后转换成pandas.DataFrame
**import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pqpandas_df = pd.DataFrame(data={'column_1': [1, 2], 'column_2': [3, 4], 'column_3': [5, 6]})table = pa.Table.from_pandas(pandas_df, preserve_index=True)pq.write_table(table, 'pandas_dataframe.parquet')**

2.2.计算脚本处理时间

只要我们关心编写的脚本的性能和处理速度,了解如何度量它们的处理时间是有益的。

执行 Python 脚本时,存在两种类型的时间流逝处理计算。

处理器时间:测量特定进程在 CPU 上活动执行的时间。睡眠、等待 web 请求或时间不包括在内。*time.process_time()*

挂钟时间:它计算“挂在墙上的钟”已经过了多少时间,即外面的实时时间。*time.perf_counter()*

还有其他方法可以计算运行脚本所花费的时间。

*time.time()*功能也是 quantifes time-passed 作为挂钟时间;然而,它可以被校准。为此,需要回到过去重置它。

*time.monotonic()*函数单调,简单向前;然而,与*time.perf_counter()*相比,其精度性能有所下降

3.PySpark 与熊猫 UDF

Pandas 用户定义的函数 可以被识别为由 Apache Arrow 提供支持的矢量化 UDF,与一次一行Python UDF相比,它允许提供更高性能的矢量化操作。通过对定制功能的分布式处理,它们可以被视为 Apache Spark 中最具影响力的改进。它们带来了无数的好处,包括让用户能够使用熊猫 API 和提高性能。

在 Python 中摄取 Spark 定制函数结构向 SQL 用户展示了它的高级功能,允许他们调用函数,而无需生成额外的脚本来连接他们的功能。

功能可以通过 **窗口 来执行,数据格式对于列可以使用,对于表结构可以使用data frame**。********

3.1.标量熊猫 UDF

标量型熊猫 UDF 可以描述为将一个或多个熊猫系列转化为一个熊猫系列*。最终返回的数据序列大小应该与输入数据序列的大小相同。*******

****import pandas as pdfrom pyspark.sql.functions import pandas_udf
from pyspark.sql import Windowdataframe = spark.createDataFrame(
 [(1, 5), (2, 7), (2, 8), (2, 10), (3, 18), (3, 22), (4, 36)],
 (“index”, “weight”))**# The function definition and the UDF creation**
@pandas_udf(“int”)
def weight_avg_udf(weight: pd.Series) -> float:
 return weight.mean()dataframe.select(weight_avg_udf(dataframe[‘weight’])).show()****

(图片由作者提供)

3.2.熊猫 UDF

熊猫 UDF 的分组 Agg 可以定义为一个或多个熊猫系列转化为一个标量。最终返回的数据值类型要求是原语 (boolean、byte、char、short、int、long、float、double) 数据类型。

******# Aggregation Process on Pandas UDF**dataframe.groupby("index").agg(weight_avg_udf(dataframe['weight'])).show()w = Window \
    .partitionBy('index') \
    .rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)****

(图片由作者提供)

****[**# Print the windowed results**](https://github.com/pinarersoy/PyArrow-Powered-New-Pandas-UDFs-in-Spark-3/blob/master/PySpark_PandasUDFs_in_one_file.ipynb?short_path=808df3f)dataframe.withColumn('avg_weight', weight_avg_udf(dataframe['weight']).over(w)).show()****

(图片由作者提供)

3.2.熊猫 UDF 组图

熊猫 UDF 分组图可以被识别为一个或多个熊猫数据帧到一个熊猫数据帧的转换。最终返回的数据大小可以是任意的。

****import numpy as np**# Pandas DataFrame generation**
pandas_dataframe = pd.DataFrame(np.random.rand(200, 4))def weight_map_udf(pandas_dataframe):
    weight = pandas_dataframe.weight
    return pandas_dataframe.assign(weight=weight - weight.mean())dataframe.groupby("index").applyInPandas(weight_map_udf, schema="index int, weight int").show()****

(图片由作者提供)

根据输入和输出数据的规格,您可以通过向这些矢量化 UDF 添加更复杂的函数来在它们之间进行切换。

完整的实现代码和 Jupyter 笔记本都在我的 GitHub 上。

非常感谢您的提问和评论!

4.参考

  1. Python 用户定义函数
  2. 熊猫 API
  3. 阿帕奇火花
  4. 阿帕奇箭头

5.额外资源

  1. 矢量化 UDF:使用 Python 和 PySpark 进行可扩展分析
  2. Apache Arrow Tokyo Meetup 2018 演示
  3. 星火:权威指南

基于 Python 和 Dask 的分布式文本预处理

原文:https://towardsdatascience.com/distributed-text-preprocessing-with-python-and-dask-5312a4d26ae?source=collection_archive---------30-----------------------

用 Python 和 Dask 进行分布式机器学习

一种扩展您的数据争论任务的方法

照片由 Jelleke VanooteghemUnsplash 上拍摄

如果你已经组装了一个独立的集群,并且对Dask 如何在后台工作有了足够的了解,那么是时候学习一些实用的数据科学了。对了,你可以在我的 GitHub 账号里找到我所有的源代码和数据。

我们试图预测什么?

我想根据文本将亚马逊产品的评论分为正面或负面。显然,在将数据输入到 ML 训练管道之前,我需要对数据进行一些转换。

数据呢?

我使用的是亚马逊提供的公开数据集,有 300 万条评论和评级。这是一个 1.5 GB 的中型数据集,但是本文的目的是展示如何以及为什么使用 Dask 来处理更大的数据集。

加载集群

在进入我们的笔记本之前,我们需要几个快速命令来准备好我们的独立集群。如果您遇到问题,这篇文章将指导您创建一个好的独立集群。

首先,与您的每台机器建立一个 SSH 连接。让我们从您想要选择作为调度程序的机器开始。您只需要输入以下命令:

dask-scheduler

我的独立 Dask 集群有两个工人(机器)。如果要将这些节点与调度程序连接,需要在“Worker node”终端运行以下命令:

调度程序节点

现在,您必须将其他机器作为工作节点连接起来。为此,您需要在调度程序终端输入以下命令。在下面的例子中,调度器节点的 IP 是10.1.1.93,Dask 应该连接的端口是8786

# You should specify the IP and port of the scheduler node as below. dask-worker tcp://10.1.1.93:8786

将第一个工作线程连接到调度程序节点

我将再连接四台机器作为工作节点,这使得我的最终集群有一个调度器节点和五个工作节点。连接每个节点后,您应该会在调度程序终端中看到类似这样的内容:

连接五个工作节点后调度虚拟机

恭喜你。现在,您可以开始使用 Jupyter 笔记本了。

Dask 提供了一个漂亮的仪表板,您可以在其中看到正在运行的进程的所有指标。您可以使用以下命令让它运行:

# load the cluster
import dask 
from sklearn.externals import joblib
from dask.distributed import Client# IP of the scheduler node.c = dask.distributed.Client('tcp://10.1.1.93:8786')# call the cluster c 

单击 dashboard 链接,它应该会在您的浏览器中打开一个新选项卡。Dask 的仪表板可以让你实时看到每个工人正在做的所有计算。太棒了,对吧?

Dask 仪表板

Dask 数据帧

按照以下示例将数据加载到 Dask 数据帧中。

让我们看看 Dask 生成的数据帧:

Dask 数据帧

哇!它看起来和熊猫的数据帧很不一样,对吗?这是因为两个主要原因:

  • Dask 的read_csv函数将数据分成小熊猫分区,让我们可以处理大数据集。在我们的例子中,Dask 将 1.5 GB 的数据集分成 22 个分区,使我们有 68.18 MB 的分区。作为一般建议,当您处理较大的数据集时,您的数据块应该足够小,以便它们中的大部分可以一次放入一个工作者的可用内存中。当您选择 Dask 数据帧中的分区大小或 Dask 数组中的块大小时,您可以控制这一点。
  • Dask 使用惰性计算比如 Spark。Dask 是一个图形执行引擎,所以所有不同的任务都被延迟,这意味着在您点击函数.compute()之前,没有任何函数被实际执行。在上面的例子中,我们有 66 个延迟的任务。

让我们点击.compute(),看看会发生什么。

.compute函数将 22 个小分区组装成一个包含 300 万条评论的 pandas DataFrame。如果您的数据集很大,不要这样做,因为这样做会使您将数据集放入单台机器的本地内存中,这是 Dask 试图避免的。

缺少值

检查缺失值类似于在熊猫数据帧上执行.isnull() 功能。但是记住,如果你想看到结果,你必须点击.compute()

缺少值

好消息是,在我们的示例数据集中没有需要处理的缺失值。

描述统计学

让我们简要地看一下 table 中的前五行数据,这样我们就可以看到我们正在处理哪种类型的值。Dask dataframes 允许您使用 pandas 功能,如.head().groupby().loc().merge()等。

数据帧

我们有三个栏目:标题、评论和评分。我想知道我们是否正在处理数据中的不平衡,因此我将计算我的目标变量(评级)的值。

这看起来是一个非常好的“训练”数据集,因为这些类被平等地表示。我们不需要担心应用技术来解决不平衡的数据。

您肯定可以使用更复杂的 EDA 进行文本分析,如查找最常用的术语、LDA 主题检测、基于产品的聚类评论等,从而为您的训练数据集添加新功能。然而,这些主题超出了本文的范围。

到目前为止,我认为您已经明白了这一点——Dask 是 Python 用户的友好库,允许您使用大数据

数据争论

例如,如果我们要建立一个二元分类问题,我们需要将评论分为“正面”或“负面”。亚马逊用户将这些评论评为 1-5 星。为了转换这一点,我假设将从 1 到 3 颗星的评级分类为负面,并用值 0 标记它们,反之亦然。

请记住——Dask 以这样一种方式转换您的场景,即使您的数据很大,您也可以处理小块的 pandas 数据帧,因此这是迭代所有分区的最优雅和有效的方式。

让我们来看看数据帧“df2”:

df2

我们创建了一个包含 177 个任务的 DAG。记住,我们不会做任何计算,直到我们点击.compute()

让我们看看转换的输出。

新目标变量

我们已经成功地将这个问题转化为二进制文本分类。

在清除文本中的干扰之前,我还将“标题”和“评论”功能结合起来。根据这种情况,很明显,最重要的数据点存在于评论中,但有时,重要的数据点也存在于标题中。我想确保我两者都考虑到了。

管理内存

在继续之前,了解如何使用 Dask 管理 RAM 的使用是很重要的。

dask.distributed将任务的结果存储在工作节点的分布式内存中。中央调度程序节点跟踪集群上的所有数据,并确定何时应该释放数据。完成的结果通常会尽快从内存中清除,以便为更多的计算腾出空间。但是,如果您想将数据帧存储在 RAM 中,您应该使用函数persist

**compute**和** **persist** 有什么区别?**

compute返回每个输入的单个输出,而persist返回每个块(或分区)的集合的副本,由单个输出代替。简而言之,使用 persist 在集群上保存一个完整的集合,当您想要一个小的结果作为离散输出时,使用 compute。

但是你可能会奇怪为什么我要用 **persist**

从 RAM 访问数据通常比从磁盘访问数据快得多。一旦您的数据集处于干净的状态,并且:

  1. 适合内存,而且
  2. 足够干净,你会想尝试许多不同的分析,

那么这就是把你的数据保存在内存中的好时机。

让我们看一个例子:

前一部分生成的 df3

让我们坚持 df3...

持续 df

所以基本上persist()为每个 Dask 任务返回一个副本,包括它们以前的懒惰计算,现在提交到集群上运行。否则,您可以选择将数据帧保存到本地(不推荐)或以拼花格式保存到集群。

将 df3 保存到拼花地板:

用于机器学习的干净文本

当我们对文本进行矢量化时,我们会将我们的语料库(评论)转换为所谓的“稀疏矩阵”。稀疏矩阵是主要由零值组成的矩阵。矩阵的稀疏性可以用分数来量化,分数是矩阵中零值的个数除以矩阵中元素的总数。这可能导致空间和时间复杂性方面的问题。因此,清除评论中的噪声对于提高训练和结果模型的性能变得至关重要。我附上了我的清理算法的一部分,但你会在我的 GitHub 上找到更多的部分。****

清理数据

但是我们还需要一个步骤——我们需要对所有的分区应用 clean 函数。为此,使用 Dask 提供的map_partitons()函数,而不是 pandas 的map()

当你叫map_partitions(就像你在熊猫上叫.apply()一样。DataFrame),您尝试映射(或应用)的函数会将 dataframe 作为第一个参数。

dask.dataframe.map_partitions的情况下,第一个参数将是一个分区,在pandas.DataFrame.apply的情况下,它将是一个完整的数据帧。这意味着您的函数必须接受 dataframe 分区作为第一个参数,您的代码可能如下所示:

地图 _ 分区

让我们检查结果!

清洁 df

不错!!我们已经用 Dask 清理了我们的文本!下一部分将展示如何用 Dask 加速你的算法。

感谢大量阅读。

** [## PabloSalvadorLopez10 -概述

在 GitHub 上注册你自己的个人资料,这是托管代码、管理项目和构建软件的最佳地方…

github.com](https://github.com/PabloSalvadorLopez10)

链接

https://docs.dask.org/en/latest/best-practices.html

https://docs.dask.org/en/latest/dataframe.html**

tf.keras 中带权重和偏差的分布式培训

原文:https://towardsdatascience.com/distributed-training-in-tf-keras-with-w-b-ccf021f9322e?source=collection_archive---------44-----------------------

生产中的机器学习

探索以最少的代码更改来分配您的培训工作量的方法,并使用权重和偏差(W&B)来分析系统指标。

查看关于体重和偏见的交互式仪表盘

介绍

在这份报告中,我将向您展示如何无缝集成[tf.distribute.MirroredStrategy](https://www.tensorflow.org/api_docs/python/tf/distribute/MirroredStrategy),以便针对tf.keras型号在多个 GPU 之间分配您的培训工作负载。当您拥有非常大的数据集,并且需要调整培训成本时,分布式培训会非常有用。仅在单个硬件加速器(在这种情况下是 GPU)上执行训练变得不现实,因此需要执行分布式训练。

查看 GitHub 库上的代码。

查看关于重量和偏差的交互式仪表盘

在报告的结尾,我们将看到两种方法可以使分布式训练非常有效——a .预取数据,以便模型在完成一个时期后就可以使用这些数据,b .调整批量大小。

非常感谢 Google 的 Martin Gorner(ka ggle 团队的 ML 产品经理)为我准备这份报告提供了指导。

TensorFlow 的分布式策略使我们能够极其轻松地在多个硬件加速器之间无缝扩展我们繁重的训练工作量,无论是 GPU 还是 TPU。也就是说,分布式训练长期以来一直是一个挑战,尤其是在神经网络训练方面。分布式培训程序带来的主要挑战如下:

  • 我们如何在不同的设备上分配模型参数?
  • 我们如何在反向传播过程中累积梯度?
  • 模型参数将如何更新?

如果您从头到尾考虑培训过程,所有这些听起来可能非常令人生畏。幸运的是,像 TensorFlow 这样的库给了我们非常容易地合并分布式训练的自由——无论是经典的fitcompile范式的tf.keras模型,还是定制的训练循环。然而,本报告只涉及前者。如果您有兴趣了解更多关于定制培训循环的分布式培训,请务必查看本教程。

在本报告的前半部分,当我在 GCP 虚拟机上进行实验时,我们将讨论在谷歌云平台(用于分布式培训)上选择虚拟机时需要记住的一些要点。但是这些指针也应该适用于您选择的任何平台。然后,我们将看到在单个机器中的多个 GPU 之间分配tf.keras型号的培训工作负载所需的步骤。最后,我们将通过分析这个[wandb](https://app.wandb.ai/sayakpaul/tensorflow-multi-gpu-dist/) 运行概要中的系统指标来得出结论。

系统设置、成本等

我们主要有两个选择来执行 GCP 的分布式训练-

  • 计算引擎
  • 人工智能平台笔记本

计算引擎允许您创建具有许多不同软件和硬件配置的虚拟机,这些虚拟机可能适用于各种任务,而不仅仅是训练深度学习模型。另一方面, AI 平台笔记本为我们提供了预配置的 Jupyter Lab 笔记本实例,具有定制的灵活性。

根据我的经验,我发现建立一个计算引擎实例的过程比构建一个人工智能平台笔记本实例更复杂。让我们从成本的角度来看看它们有什么不同。

以下是我的系统配置:

  • n1-标准-4 个 vcpu-15 GB
  • 4 辆特斯拉 K80s
  • 100 GB 标准永久磁盘
  • 预配置映像: TensorFlow 2.1 (采用英特尔 MKL-DNN/MKL 和 CUDA 10.1)

计算引擎,为了以上,将花费我-

在 GCE 上计算我们虚拟机的成本

而且,人工智能平台笔记本电脑会让我付出以下代价-

人工智能平台笔记本电脑上虚拟机的成本计算

正如你所看到的,两者的成本是不同的,但后者(人工智能平台笔记本)只是一个点击就走的东西。作为一名从业者,我希望我的时间花在与我的专业知识相关的事情上,我不想在不需要的时候重新发明轮子。因此,我选择了人工智能平台笔记本电脑。关于设置和使用人工智能平台笔记本的更全面的报道,请参考本指南

为了能够在一个 AI 平台笔记本实例中使用多个 GPU,您首先需要申请配额增加。你可以看看这个帖子来了解更多。

给我看看代码

默认情况下,使用 all-reduce 算法在同步模式下进行参数更新。但是,TensorFlow 2.x 也支持异步模式下的参数更新。解释它们的细节超出了本报告的范围。如果您有兴趣了解更多,这里有一些非常好的资源:

好了,回到代码!

作为起点,我们先在单个 K80 GPU 上训练一个区分猫和狗的图像分类器。我们将使用 MobileNetV2 网络(在 ImageNet 上进行了预培训)作为我们的基础架构,在它的顶部,我们将添加分类头。所以,在代码中,它看起来像这样-

训练这家伙 10 个纪元给了我们一个好结果-

训练进度图 I(在此处与图互动)

请注意,我们正在对这个网络进行微调,而不仅仅是预先计算瓶颈,然后将其提供给分类顶部。因此,EXTRACTOR.trainable = True就是这样设定的。当trainable参数设置为False时,它只是一个带有不可训练特征提取器的浅层网络。在这种情况下,我们不太可能看到分布式培训的任何优势,因为它会变成一个非常肤浅的网络。

我们已经在验证集上获得了大约 94%的准确率。但这不是我们关心的问题。我们希望能够通过使用分布式训练来加速模型训练。

训练大约需要2090 秒 。关于这个特定实验的所有重要统计数据都可以在一个格式良好的表格中找到(你可以在这里看到)

不同 W&B 运行的集合一起称为运行集

当微调网络时,使用一个带有加速的学习速率表是一个很好的实践。这里的想法是从一个低的学习率开始,这样基础网络的预训练权重就不会被破坏。然后我们提高学习率,再降低学习率。时间表应该是这样的-

带有斜坡的学习率(LR)时间表

使用 LR 调度进行微调(单个 GPU)

现在让我们看看学习计划对培训是否有任何影响。

训练进度图 II(在此处与图互动)

虽然这次网络中有一点过度拟合的行为,但你也可以看到性能的改善( ~98%的验证准确率)。模型训练时间也不受此影响( ~ 2080 秒)。通过在开始时使用较低的学习速率,同时使用学习速率时间表,可以进一步减轻过拟合。

将模型移植到多个 GPU

现在,为了在四个 GPU 之间分配这种训练,我们首先需要定义MirroredStrategy范围,并在范围上下文中编译我们的模型

get_training_model包括如上所示的模型定义以及编译步骤。所以,我们正在做的是在MirroredStrategy的范围内创建和编译模型。这之后绝对一样——你叫model.fit。我们的学习率计划也将有所改变,因为我们现在将在四个 GPU 上分配模型参数(注意 Y 值)。

多 GPU 的 LR 调度

从下图中可以看出,性能变化不大(至少在准确性和损失方面)

训练进度图三(在此处与图互动)

正如你所看到的精度(在这种情况下,我们有 ~98% )和损耗仍然与上面的图有些相同。如果我们从较低的学习率开始,左边图中的尖峰可以变平。

不过,模型训练时间减少了——1046 秒。哇哦。惊人的 2 倍加速。这还可以进一步改进,我们稍后会看到如何改进。但首先,让我们对 GPU 指标做一些分析。

分析 GPU 指标

相对而言,GPU 不如其他商用硬件便宜。因此,确保 GPU 利用率尽可能高非常重要。让我们快速看看我们在那里做得怎么样。下面是 GPU 利用率和 GPU 访问内存以获取数据所用时间的图表(单个 GPU)。正如我们所见,大多数时候 GPU 利用率都很高,这很好。

单 GPU 指标

作为一名深度学习实践者,你的目标应该是最大化GPU 利用率,同时减少GPU 访问内存以获取数据所花费的时间。因此,减少 GPU 获取数据所花费时间的一个显而易见的解决方案是在一个时期完成时预取数据

当我们使用多个 GPU 时,我们会得到-

多 GPU 指标 I

(您会看到多行,因为这里有四个计算指标的 GPU)

正如我们所看到的,四个 GPU 的平均 GPU 利用率远远低于预期,但内存访问时间已经大大减少,因为它现在分布在多个 GPU 上。至于利用率,这是因为数据集的容量相对较低。有了更大的数据集,我们可以期待看到 GPU 性能的更多提高。在下一节中,我们将讨论两种常用技术来进一步提高利用率。

我们还可以看到上面两个图的线条有些平滑,这表明所有的 GPU 都在相同的负载下运行。可以肯定地说,在多 GPU 环境下运行时,这种平滑度是意料之中的。如果您看到奇怪的峰值,这可能是一个迹象,表明 GPU 没有在相同的负载下运行,您可能希望使之均匀。

进一步提高性能的两种方法

方法#1

TensorFlow 的数据 API 提供了许多东西来进一步改善输入数据流成为瓶颈的模型训练。例如,理想情况下,当模型正在训练时,下一个时期的数据应该已经准备好,因此模型不需要等待。如果它需要等待,那么它会在总训练时间方面引入一些瓶颈。

[prefetch](https://www.tensorflow.org/api_docs/python/tf/data/Dataset)允许我们指示 TensorFlow 在模型完成当前时期后立即准备好下一批数据。它甚至允许我们预先指定系统应该获取的样本数量。但是,如果我们希望系统根据系统进程和硬件的带宽为我们做出决定,那该怎么办呢?我们可以用tf.data.experimental.AUTOTUNE来指定

方法二

我们可以做的第二件事是调整批量大小。由于我们使用同步参数更新方案的多个 GPU,每个 GPU 将接收一部分数据,并在此基础上进行训练。因此,如果我们使用过高的批处理大小,GPU 可能很难在彼此之间正确分配。另一方面,如果我们使用的批量太小,GPU 可能会利用不足。所以,我们需要找到最佳点。这里有一些一般性的建议(这些来自马丁·戈纳的笔记本)

  • 从 16 开始,作为每个 GPU 的本地批量大小。
  • 然后,全局批量大小变为— local_batch_size * number_of_GPUs

请注意,我们现在使用的批量更大,在之前的实验中,我们使用的批量为 16。

Martin 的上述笔记本包含许多使用分布式培训时优化模型性能的提示和技巧,其中包括:

本指南中也提供了许多建议。

好了,说够了!现在是时候把上面讨论的方法结合起来看看结果了

训练进度图四(在这里与图互动)

批量越大,性能受到的影响越大,但另一方面,训练时间进一步减少到了 887 秒。

在同样的批量 32 的情况下,我们得到-

多 GPU 指标 II

虽然 GPU 利用率和内存访问时间的提高非常微小,但它仍然存在。

LR 调度+批量 16 +预取

训练进度剧情五(在此与剧情互动)

正如你所看到的,我们能够保持同样的性能,并且还稍微减少了模型训练时间( ~ 6 秒)。

就 GPU 指标而言-

多 GPU 指标 III

如上图所示,GPU 指标几乎没有变化。当使用多个 GPU 时,通常数据越多,利用率就越高。同样重要的是要记住,在这些情况下使用较大的批量可能会损害模型性能,正如我们在上面看到的那样。

我们鼓励您查看下面为两个不同的 Kaggle 竞赛提供的两个惊人的基准:

它还提供了许多不同硬件配置之间的成本权衡。这将有助于您根据您的成本预算做出最佳决策。

结论和下一步措施

重要的是要记住,在使用分布式培训时,数据集越大,加速器的利用率就越高。无论是 TPU 还是多个 GPU,这一逻辑都是正确的。

当使用多个 GPU 时(无论是在单个还是多个机器/集群中),注意多个加速器之间协调所需的同步时间的相关成本非常重要。本视频讲解了与此相关的一些取舍。

我希望您已经意识到为您的tf.keras模型分配培训工作量是多么容易。作为下一步,您可能想尝试本报告和我提到的参考资料中分享的不同技巧和诀窍。如果你更喜欢定制训练循环,你也可以尝试混合精度训练和分布式训练。

如果你有任何反馈要与我分享,你可以通过推文这里这样做。我真的很感激。

建立一个分类算法来预测国会选区翻转并确定因果关系

原文:https://towardsdatascience.com/district-flip-forecasts-in-congressional-elections-47324c71e7ab?source=collection_archive---------39-----------------------

预测国会选区的政党变动。

METIS 数据科学沉浸式解决分类算法项目 5 之 3。你可以在我的 Github 上看看这个项目的文件。

根据维基媒体知识共享许可授权的美国国旗

我一直想当然地认为,地区派对的转变是特定地区人口或文化变化(或不公正选区划分)的结果,这种现象通常被称为空间排序——由比尔·毕晓普在《大排序》中推广开来。我用这个项目来评估这个假设。

目标

  • 预测国会选区的政党变动。
  • 确定派对狂欢的最大促成因素

在过去的 45 年里,平均不到 9%的地区改变了党派。图片由 Anupama Garla 提供

当您预测的类别(翻转/非翻转)相等时,分类算法往往会做得更好。这意味着这个项目可能是一个分类问题的挑战,也许可以更好地用异常检测算法进行评估。然而,我想先尝试更简单的分类工具。为了解决班级不平衡的问题,我计划在训练算法时,对我的少数民族班级——翻转的地区——进行上采样。

方法学

区级数据:

  • 从麻省理工学院选举实验室提取的 1973–2016 年累积翻转历史并转化为目标(翻转与否),以及量化先前翻转的 4 个特征。
  • 人口统计和相对城市化来源于 Daily Kos 对美国人口普查和美国社区调查的分析,涵盖教育、种族和大都市人口的 20 个特征
  • 财务来源于 SEC,涵盖候选人资金和支出的 10 个特征

型号选择:

支持向量机是在测试了其他传统分类模型之后选择的:高斯朴素贝叶斯、K-最近邻、CART 模型和逻辑回归。

指标:

回想一下之所以被选中,是因为该指标优先考虑捕捉尽可能多的翻转案例。精确度和 F1 分数也被参考,以确保模型不会指示太多不会翻转的地区。还参考了 AUC 然而,由于类别不平衡和翻转的相对可预测性,在这方面模型之间没有重大差异。

结果:

76%召回

63%的 F1 分数,48%的精确度,94%的 AUC

Anupama Garla 的模型限制

测试集的表现不如训练集准确的一个可能原因是包含 2012-2016 年地区变化的训练集和由 2018 年地区变化组成的测试集之间的数据划分。在翻转的次数和方向方面,训练测试组有所不同。一些额外的差异可以通过包括更多的功能来捕捉,如库克的党派投票指数和民意调查结果等。然而,考虑到从训练集中的上采样数据到测试集中的类不平衡的转变,该模型做得相当好。

Anupama Garla 的交互式 Tableau 仪表板,国会选区 Hex 地图来自 Daily Kos 的 Daniel Donner,可在此处获得。

上面的视频显示了根据分类模型的地区翻转可能性,以及映射到 Daily Kos 制作的美国修改地图上的 2018 年实际地区翻转。该地图也是可点击的,因此您可以看到每个地区的相关特征。

该地图将每个国会选区的大小绘制为具有相同的面积,表示根据美国人口普查,每个选区由大约相同数量的人口组成。这使我们能够看到更准确的结果分布,尽管在美国西部人口较少的地区产生了差距。

洞察力

还实施了一个决策树模型,以获得最有影响力的功能的排名,前 7 名如下:

预测特征排名

  1. \[$ —财务 \]

3.中等收入

4.第二大城市:人口的%

5.状态

6.最大的地铁站的人口比例

7.人口统计超参数—捕捉城市+多样化与农村+同质的比率

如果我们深入研究财务特征,我们可以看到它们的主要预测特征:

金融特征排名

  1. 业务开支
  2. 党委贡献
  3. 个人单位化贡献
  4. 总支出
  5. 总作用

结论

竞选活动的运营支出是决定一个选区是否会翻转的最大因素。人口统计和地理因素也有影响,尽管没有专家让我们想的那么大。

仔细观察财务特征,他们的排名显示个人和党委的贡献确实有影响——但是党的贡献排名更高。在这项研究之后,我不得不同意伯尼·桑德斯“让公司资金远离政治”的运动。

翻转的第二大指标是历史上有争议的地区。这是有道理的,因为如果一个特定的地区是高度分裂的,这样或那样的一些投票可能导致翻转。如果一个地区的历史周转率很高,某一年没有发生变化,第二年很可能会发生变化——特别是如果更多的竞选资金投入到这个特定的地区。

翻转的跟踪指标都与人口统计和地理有关,中等收入和居住在第二大城市的人口比例超过了这些预测特征。后者很有趣,因为这意味着第二大城市的选民具有政治多样性,可以以某种方式被说服——第二大城市人口组成的选区越大——就越容易改变立场。然而,由该州人口最多的城市或人口第三多的城市组成的地区,其政治联系更加紧密。

该模型的预测特征排名为进一步的竞选策略提供了信息,而该模型本身在预测国会选区翻转的概率方面表现良好。一些超参数和附加功能的微调可能会带来更多的见解。

附录:探索性数据分析

这一部分深入到我开始这个项目时使用的数据集——翻转、人口统计和地理数据的累积历史。在这个项目的早期阶段,我研究了这些数据集的细微差别,以改进模型并更好地理解数据和我的结果。

下面是一张地图,显示了每个地区的整个累积翻转历史。一个地区最大的翻转次数是 7 次。

第二强预测因子:Anupama Garla 的累积翻转史

将上面的历史翻转图与下面的 2018 翻转图进行比较,我们可以直观地看到翻转与翻转历史之间的相关性。

阿努帕马·加拉的 2018 年国会选区翻转

上面的地图——2018 翻转——组成了我的坚持测试集。下面的地图(2010 年至 2016 年)组成了我的训练集。我们可以观察到,地区变化的数量以及变化的方向每年都有很大的差异。2018 年与 2016 年截然不同,之前的选举要追溯到 2010 年。我认为,如果我们再往后看,我们会看到成交量和方向的周期性。

2016 年,2014 年国会选区翻转

2012 年,2010 年国会选区翻转

对于这些数据集——翻转历史和人口/地理——我的模型表现极差,召回率约为 30%。检查我的配对图和我的模型特征重要性,我可以看到人口统计/地理特征根本没有通知我的模型。

基于我最初的假设,即地区翻转主要是由于该地区的人口统计和地理状况,我认为如果我尝试一种多类分类模型,将类分为 3 类(无翻转|向右翻转|向左翻转),而不是 2 类(无翻转|翻转),我的模型会表现得更好。在多类分类模型中,分数必须以不同的方式计算——我创建了一个自定义函数来计算召回率和精确度——并且 AUC-roc 在这里不能被引用。当我将数据集分成三类时,我的配对图显示了这三类之间的更多差异,所以我认为这是一个有希望的方向。我深入我的人口统计和地理特征来设计一些超参数。我在 tableau 中可视化了一些结对图发现,如下所示:

由 Anupama Garla 绘制的配对图

上面的图标识了向左翻转和向右翻转的两个集群。蓝色的左翻转群集通常有较高比例的亚洲人和较高比例的有单身汉的白人。右边红色区域的亚裔比例较低,有单身汉的白人比例也较低。这强化了受过教育的多样化人口向左倾斜和受教育程度较低的同质人口向右倾斜之间的二分法。

Anupama Garla 的配对图

在左边的图中,你可以看到一个主要由向右转的人组成的集群,他们通常比向左转的地区受教育程度低,收入也少。在右边的图中,我们可以看到一个主要由向右转的地区组成的集群,与向左转的地区相比,它的多样性和城市化程度较低。

这些图加强了我在项目开始时所做的假设。由于这个 EDA,我设计了超参数,这些超参数基本上捕捉了多样化的、受过教育的城市人口与同质的、受教育较少的农村人口的比率。然而,当我运行基于这种多类划分的模型时,我的结果并没有显著提高。我只是需要一个更有影响力的特征来捕捉数据集中的差异。我那精通政治的兄弟——他建议增加竞选财务数据——将模型提高了 33 个百分点。永远不要低估领域知识!

这个多类切线,虽然很有趣,也很有知识性,最后还是走进了死胡同,但是给我提供了宝贵的一课。对项目方法和设计的深入和反复思考对于获得有意义的结果是非常重要的。做到这一点的最好方法是走出你的头脑,与有知识的利益相关者交谈,或者甚至看看那些前人的工作——比如我的英雄,FiveThirtyEight 的 Nate Silver。

丢弃数据库

原文:https://towardsdatascience.com/ditch-the-database-20a5a0a1fb72?source=collection_archive---------13-----------------------

如何使用自动气象站 S3 选择查询更智能,也许更便宜

马库斯·斯皮斯克在 Unsplash 上的照片

亚马逊 S3 精选是亚马逊 S3 的一项功能,于 2018 年 4 月正式上市。通过使用亚马逊 S3 精选,可以在没有“经典”数据库的情况下运行应用程序,从而简化应用程序架构。

任何处理数据的人都可能在其数据管道的某个点上使用过基于云的存储解决方案。就个人而言,亚马逊 S3 是我的首选,因为它使用相对简单,易于扩展,与其他 AWS 服务相结合,并且存储几乎任何文件类型的对象。

虽然亚马逊 S3 在存储数据集方面很棒,但像其他传统的基于云的对象存储解决方案一样,它不允许检索数据集的子集(即必须检索整个数据集)。例如,如果您需要 100 万行数据集中的前 1000 行,您必须检索整个数据集并进行本地过滤。这使得使用亚马逊 S3 作为应用程序的唯一数据库变得不切实际,特别是那些经常需要检索更大数据集的子集的应用程序(例如检索客户记录的 web 应用程序)。

输入亚马逊 S3 精选🙌

概述—亚马逊 S3 精选

亚马逊 S3 选择允许您使用简单的 SQL 表达式从亚马逊 S3 对象中提取您需要的数据。这是巨大的!!!

使用 S3 选择,您不再需要为了保留几行而检索整个数据集。相反,您可以使用经典的 SQL 表达式就地查询数据,并只检索一个子集,例如两个日期之间或高于某个价格的数据。目前,亚马逊 S3 精选支持 CSV、JSON 和 Apache Parquet 格式的对象。

通过减少您的应用程序必须加载和处理的数据量,S3 精选可以将大多数频繁从 S3 访问数据的应用程序的性能提高多达 400% 。—亚马逊网络服务

使用亚马逊 S3 精选进行简化

亚马逊 S3 精选最好的部分是它如何简化应用程序结构。在亚马逊 S3 精选之前,web 应用程序的典型结构可能如下所示。

  • 将数据存储在经典 SQL(即 MySQL 或 PostgreSQL)或 NoSQL(即 DynamoDB)数据库中,以便进行查询。
  • 在持久对象存储器(即亚马逊 S3)中保存数据的备份副本
  • 将新数据转移到亚马逊 S3,然后添加到数据库。

使用亚马逊 S3 选择 web 应用程序的结构如下所示。

  • 将数据存储在亚马逊 S3,以便查询。

好多了!现在你可以只使用亚马逊 S3 来构建一个端到端的应用程序。它速度快,价格便宜,界面简单!👍

示例—亚马逊 S3 精选

最近我构建并部署了一个 web 应用程序,让下载和可视化加拿大历史天气数据变得更加容易和快捷。这是我构建的第一个 web 应用程序,其中我没有使用经典数据库,而是直接从亚马逊 S3 存储和查询静态和动态数据。使用亚马逊 S3 选择,我能够快速方便地从我的动态仪表板中获取用户输入,将它们传递到选择表达式中,并只查询和检索请求的数据。

下面是一些示例数据和一个在亚马逊 S3 查询 CSV 并返回可以读入熊猫数据帧的 CSV 格式的函数。CSV 的亚马逊 S3 SQL 语法可能有点复杂。您可以使用列名或列索引进行查询,根据您选择的列名或列索引,SQL 表达式会有所不同。

示例“test.csv”

用于在亚马逊 S3 查询 CSV 并将数据解包为 CSV 格式的函数

如何按列名查询?

让我们使用列名在两个日期之间查询上面的数据集,并只返回日期、风速和风向的相应列。

使用列名在亚马逊 S3 查询 CSV

查询结果

如何按列索引查询?

让我们使用列索引查询上面的数据集,当温度高于 20 摄氏度时,只返回日期和温度的对应列。

请注意,在 S3 选择中,索引从 1 开始(即 s._1 ),而 Python 的索引是 0。通过使用“cast”命令将一种数据类型转换为另一种数据类型,确保您正在比较 SQL 表达式中的常见数据类型。如果我的温度数据是字符串格式,我将需要" cast(s . 4 as float)>20 "

使用列索引在亚马逊 S3 查询 CSV

查询结果

如何查询表头名称?

让我们只查询上面数据集中的标题名称,这样我们就可以使用它们来追加到其他查询结果中,或者作为动态仪表板下拉列表的输入。在这种情况下,我将 use_header 设置为 False ,以便在第一行返回列名,并且使用 S3 选择限制特性将返回的行数限制为 1。

在亚马逊 S3 查询列标题 CSV

#  query result
['Date/Time', 'Longitude', 'Latitude', 'Temperature', 'Wind Speed', 'Wind Direction']

亚马逊 S3 精选的成本

亚马逊 S3 精选很便宜!以下是我的应用程序的启动成本明细(截至 2020 年 6 月)。

  • 亚马逊 S3 对象存储—每 GB 0.023 美元(前 50tb/月)
  • 亚马逊 S3 选择——每 1000 条选择命令 0.0004 美元
  • 亚马逊 S3 数据传输—每 GB 0.01 美元(S3 →美国东部地区)

我在 S3 存储了大约 37 GB 的数据,每月执行近 100,000 条 SELECT 命令,每月从 S3 传输大约 50 GB 的数据。这使我每月的估计费用达到…1.39 美元!

结论

虽然亚马逊 S3 精选已经推出两年多了(在代码年,这是一个千年😆)这是一个简化应用程序的好工具,尤其是微服务框架。

查询愉快!

抛弃 AWS GUI 控制台

原文:https://towardsdatascience.com/ditching-the-aws-gui-console-ac77f46a05fa?source=collection_archive---------60-----------------------

通过 AWS CloudFormation 将无服务器基础设施作为代码

GitHub 回购/源代码在此!

2020–09–10

背景故事:

当我在时代广场的一家数据分析公司工作时,我经常在美丽的布莱恩特公园草坪上享用午餐。我可以查看 https://bryantpark.org/的草坪目前是否开放,但是 org 页面加载有点慢;里面全是我不在乎的图像。

因为这个原因(以及学习 AWS 的愿望),我从一个 ec2 盒子上创建了一个愚蠢的单一服务网站,它只显示草坪是否开放。你会去 是布莱恩特公园的草坪开点 com 然后看这个:

http://hasthelargehadroncolliderdestroyedtheworldyet.com/的启发

在科维德·疫情的萎靡不振中,我的简陋的网站已经失修,这提供了进行一些翻新的机会。上一次我做这个的时候,我非常依赖于在 AWS GUI 控制台方法中的点击——这是我想要摆脱的行为。

目标:

今天,我将着手重新设计这种(异想天开,可以说是“愚蠢”或“没人要求的”)服务,将其作为基于 AWS 的无服务器基础设施——利用 SAM CLI,使用 CloudFormation 进行配置,并依靠各种其他代码和基础工具。我想在不接触 GUI AWS 控制台的情况下做到这一点: CLI 和脚本全程!

架构流程图:

这里有一个我认为我会需要的服务的小地图:它都围绕着一个小数据库,一个 s3 bucket 网站,和一些 AWS Lambda 函数。

在我看来。以普莱狄卡为蓝本

如何使用每项服务:

云的形成:

  • YAML 模板中一次性提供以下所有服务
  • 在版本控制中被跟踪;be 基础设施符合规范
  • 学习良好的 DevOps 实践(这个男孩在找新工作)

RDS 数据库:

  • 仓库我们的开放/封闭二进制草坪数据,加上最少的元数据
  • 位于私有子网中,只有 Lambda 函数可以访问,从而不受公众影响
  • 拥有大约 35,040 个数据点后,丢弃旧行。(任意…2 年)

S3 水桶:

桶是对象存储容器。他们可以存储文件,也可以服务静态网站。我需要水桶来做以下事情:

路线 53:

  • 至少给我们愚蠢的网站一个自定义域名

EventBridge(前身为 CloudWatch):

λ函数(I)至(V):

为了完成一些计算,无服务器功能即服务绕过了调试云服务器的成本开销。你可以让 AWS 管理服务器基础设施,并且只在你的代码实际运行时计费,由一些事件时间表触发。以下是我需要的函数:

I)刮刀:

  • 每半个小时,旋转一个无头浏览器,从 Bryant park . org 网站上获取一个原始的草坪状态字符串(和 F 数据,为什么不呢)
  • 将收集的原始数据传递给后续的解析器函数

II)解析器:

  • 接受来自刮刀功能的原始数据
  • 将原始草坪状态字符串解析为二进制变量
  • 生成其他有趣的元数据,如星期几和小时
  • 将解析后的数据传递给 RDS 编写器

III) RDS 编写器:

  • 从解析器函数接受干净的解析数据。
  • 将新行写入 RDS 数据库

IV)基本状态更新器:

  • 每半小时,读取 RDS 数据库的最后几行
  • 如果草坪状态已经改变(这种情况将避免不必要的频繁 S3 推杆),然后更新 S3 水桶网站的索引页面

V) Viz 生成器:

  • 每天两次,将整个表加载到内存中,并运行一些聚合
  • 用聚合数据生成 Plotly HTML 可视化
  • 将这些图表推送到简单的 S3 网站的“统计”页面

通过云信息配置 VPC/数据库

是时候开始构建我们的云形成堆栈了。yml!这是我们通过 CloudFormation CLI 提交的文档,以完美协调的方式一次提供我们需要的所有资源。我一直在研究 AWS 上的云信息文档,想弄清楚这应该是什么样子:

节选自我的第一个云形成模板

# Excerpts from the .yml, provisioning more resources...lawnDBsubnetGroup: # RDS DB needs a subnet group. This is it
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: The two private subnets
      DBSubnetGroupName: lawn-DB-subnet-Group
      SubnetIds: 
        - !Ref lawnSubnetPrivateA
        - !Ref lawnSubnetPrivateB

我今天还没有机会测试和调试,但我已经开始为这个应用程序声明我的云基础设施(网络配置,安全规则,数据库实例)。建立我的网络的一个至关重要的资源是这个关于保持你的 RDS 实例私有,同时仍然给 Lambda RDS 访问,不牺牲 Lambda 的网络访问。

这其中的难点(私有子网 RDS +带 web 访问的 Lambdas)是建立一个廉价的 NAT 实例(通过 ec2 AMI 的硬手动方式)而不是简单地在 CloudFormation 中提供一个昂贵的预烤 NAT 网关。过去,走捷径让我付出了代价,以美元计。

2020 年 9 月 11 日:部署试验和错误

我在反复构建这个云层结构。yml 模板(目前)只有一个新的 VPC、一些子网、安全组/子网组和一个 RDS DB——还没有 Lambda 函数或 S3 桶站点。

在自动气象站云形成设计器中绘制

部署此堆栈的初始尝试。yml 会导致错误,因为我不可能第一次就做对:

通过查看控制台以 GUI 方式检查错误,这有点作弊

好了,我们得到了一些合理的错误,例如,对于“lawnDB”资源(我的 RDS 数据库),它没有启动,因为我得到了Value of property VPCSecurityGroups must be of type **List of** String。最初,我只给了它一个字符串,而不是字符串列表,这是我的错误。我将递归地纠正这些错误,并继续尝试验证/部署。我现在也很好奇 CloudFormation 中的!ref功能…

原来,[**!ref**](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html)函数 是云生成中必不可少的例如,如果您提供一个 VPC 和一个子网进入其中,子网可能需要!RefVPC 的逻辑名称(您在 YAML 中为其指定的名称)才能属于该 VPC。我的意思是:

# Inside the CF stack .yml:
# See bolded text for relevant bitsResources:
  **lawnVPC**: # The whole big sandbox. Contains all infra for this app
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: !Join ['', [!Ref "AWS::StackName", "-VPC" ]] lawnSubnetPublicA: # 1 of 2 public subnets
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: us-east-1b
      CidrBlock: 10.0.0.0/24
 **VpcId: !Ref lawnVPC**
      Tags:
        - Key: Name
          Value: lawn-Subnet-Public-A

在掌握了 CloudFormation 模板之后,看起来我已经有了一个基础设施的雏形,只是一些网络组织垃圾和一个数据库:

绿色的刻度让我的心在歌唱

这个。yml 已经变得很长了!有没有办法让我把它分开?显然,是的。我们可以在带有输出的栈之间嵌套 CF 栈或输入/输出值。我会的:

检查堆栈输出。其他堆栈现在可以引用这些值。

模块化 CloudFormation 模板感觉很好,就像我们将不祥的大 Python 模块分成几个一样。

接下来,我将尝试组装上面提到的 Lambda 函数。这是我第一次尝试通过 SAM CLI(和 docker)在本地调试 Lambda 函数的地方!

2020 年 9 月 16 日:λ和云的形成

Lambda 到底如何适应这种云计算基础设施即代码模型?我将把它们放入它们自己的“堆栈”中,并给它们分配 IAM 角色,允许它们做像写 RDS 和相互调用这样的事情。回想起来,嵌套堆栈可能是一种更好的方法,因为现在我在这个 CloudFormation 区域中混淆了导出的变量名称空间。

功能一:刮刀

我发现了一个方便的 repo,它生成一个 lambda 函数,可以用一个无头的 selenium 浏览器抓取网页。谢谢克里斯托弗。现在将它应用到我自己的 scraper 函数中,收集关于公园草坪的信息:

使用 AWS 的 SAM(无服务器应用程序模型)CLI,我能够快速重用和调试这段代码,从。org 网站。是我的新果酱。

功能二:解析器

这是将从。org 网站转换成二进制变量。我有一种感觉,这是我的“应用程序”中最不耐用的部分,因为我不知道这个粗糙的文本字段可能存在的值的范围。目前我只看到“草坪开放”和“草坪关闭”当新的可能值出现时,我会不断更新这个函数。好的方面是它有自己的 lambda 函数,有自己的日志和故障率。也许我以后会用 AWS SNS 设置短信故障通知。

一个单独的 lambda 函数来解析原始字符串

顺便说一下,所有的原始代码都在 GitHub 上公开,如果你之前没有注意到的话!

功能三:RDS 写入器

最终,我决定让 RDS writer 函数按计划运行,并通过 boto3 同步调用子函数 I (scraper)和 II (parser ):

在 lambda 函数中调用 lambda 函数

这个 lambda 函数有权限来调用其他 Lambda 函数,并修改我们在 CloudFormation 模板中提供的 RDS 数据库。它最终负责获取新数据并将其写入数据库。我在某处读到过,确保父 Lambda 函数在其子函数完成工作之前不会超时是个好主意。

功能四:站点更新器

这个函数从数据库中读取数据,从两个可能的 HTML 文档中选择一个(基于二进制的“打开”或“关闭”状态),并将该文档推送到为我们的站点提供服务的 s3 存储桶:

2020 年 9 月 21 日:网站备份

现在,我们正在抓取数据,将数据存储到数据库中,并引用该数据库向静态的 S3 网站编写 HTML 我们拥有了一个活生生的 web 服务!

一个朋友让我注意到了 TerraForm,因为我一直陷在 AWS 粉丝的泡泡里,所以我不知道它的存在。对于任何对“基础设施即代码”感兴趣的人来说,TerraForm 似乎是下一个自然要学的东西(例如,在 AWS CloudFormation 或 Google Cloud Deployment Manager 之后)。

功能五:图形统计生成器

这个想法是建立一个小 Plotly 仪表板页面,显示按星期几和一天中的小时数汇总的草坪开放历史数据。

在我可以构建一个汇总统计页面之前,我会让我的 lambda 函数将大约一周的数据刮到 RDS 数据库中,同时我会申请工作、烹饪千层面和各种丰盛的汤,并玩《野性的呼吸》。

我将在 2020 年 9 月 28 日左右回到这个话题。

2020 年 9 月 28 日:统计页面

现在起来了!

简单的统计页面正在工作!目前,这只是一张根据我的 2x/小时采样显示布莱恩特公园草坪在一天和一周不同时间的历史平均开放度的热图。

为了添加这个页面,首先我编写了一个 lambda 函数,将我的 RDS SQL DB/table 完整地导出到 S3 桶中的一个. csv 文件中,这样我就可以下载它,并在 Jupyter 笔记本中对它进行试验。有时尽可能多地在本地调试真的很有帮助,因此在这个小项目中尝试 SAM。

然后,我修改了 Jupyter 生成的工作 viz 生成器代码,使其返回到 Lambda 函数(V)中,该函数每 12 小时更新一次新的“stats”页面

让我困扰的是再加一个。我的 s3 存储桶站点的 HTML 页面没有自动使该页面在浏览器中“可加载”。当我在调试时,我的浏览器一直试图下载。HTML 文档到我的本地驱动器,而不只是呈现它。烦人!

起初,我认为这是因为 S3 网站不支持服务器端脚本,也许我不小心包含了一些服务器负责的代码..?但是没有。网络搜索让我意识到,我只需要确保在上传(put)时包含 s3 对象的“内容类型”,以便它能被正确地服务:

包括 boto3 AWS Python API 中的 ContentType 标记,用于网页渲染

结论

有了这些,我觉得我已经准备好去做一个不那么俗气的项目了!在未来,我想研究一下:嵌套的 CloudFormation 堆栈,远离 HTML/CSS/ES6 (TypeScript?节点?反应?),并在我未来的更多项目中利用一些机器学习(回归/分类),这是我已经非常熟悉的东西。我也很好奇卡夫卡/SQS、DynamoDB,以及管理用户凭证和会话;在写这本书的时候,我对这些东西知之甚少。

我最兴奋的是有机会解决一些现实世界的问题!如果你认为我可以开发一些软件,让你的生活(重复的、烦人的部分)变得更容易,或者如果你自己正在构建一些东西(开发团队?)你认为我能帮上忙。

从数据角度看炉石

原文:https://towardsdatascience.com/dive-in-hearthstone-from-a-data-perspective-e3c0b2851686?source=collection_archive---------39-----------------------

从牌组和卡片的棱镜看炉石

你好,在这篇文章中,我将详细介绍几周前我在炉石游戏上建立的一个数据集。

炉石科扎科!?

炉石是由暴雪在 2013 年制作的一款在线卡牌游戏,也就是说使用了暴雪在 1994 年开始的专营权魔兽的传说。我就不详细挖掘这个游戏的起源了,但是我会邀请你来看这个电竞一言以蔽之的视频。

这个游戏的原理相当简单;你从 9 个可用英雄中挑选一个开始;每个英雄都有一个特殊的能力,并且可以使用一些特殊的卡片。

如果你想了解更多,你可以在这里找到更多关于英雄们的细节。

在选择英雄之后,玩家必须遵守一些规则来建立他的牌组:

  • 从三个类别(仆从、能力和武器)中选择 30 张牌
  • 它不能包含两次以上相同的卡
  • 它不能包括专属于另一个英雄的卡片

在那之后,当一场炉石比赛开始时,每个对手都有 30 个生命指示物,要使用你在每回合结束时或使用某些特定牌时获得的法力来出牌。

我不想输入太多比赛进程的细节,但我会邀请你观看这个解释炉石原理的视频。

我喜欢这个游戏,我在 2013 年它发布时开始玩它,我觉得它很有趣。我认为我在炉石游戏中更像是一个休闲玩家,因为我只是在几周内的暴雪会后定期重启游戏,重启总是很痛苦,因为游戏的元数据改变了,等等。

几年前,我发现了这个关于炉石的 Kaggle 数据集,一些卡牌是在一个叫 HearthPwn 的网站上刮出来的。

这是一个围绕游戏(卡牌、卡片、比赛)分享的社区,我强烈推荐你去看一下。

所以就数据而言,Kaggle 上的数据集包含大约 360000 副牌。尽管如此,它还是老了,所以我决定建立一个系统来收集 Hearthpwn 的数据(与我的 CrossFit 文章中的过程相同,提醒一下,当你在刮的时候,不要做傻事,不要让服务器过载等等)。).

我收集了大约 800000 副牌和牌上的所有信息(我不分享数据集,因为我不是所有者)。

卡片和副牌概述

这个数据集中有趣的一点是在网站上制作了一些套牌的用户(贡献者)的数量以及他们正在制作多少套牌。在这个箱线图中,有贡献者建造的甲板数的四分位数(不包括传单)。

大多数用户只生产一个甲板(0-50%),其中 25%的用户生产 1-3 个甲板,在最后一个四分位数,他们生产 3-6 个甲板。我根据每个玩家建造的甲板数量做了一些分类。

现在让我们来看看每周发行的套牌。

大部分重要的内容发布之后是一批 Hearthpwn 上的新甲板(感谢明显船长)。

从英雄的角度来看,一些偏好正在浮现。下图是英雄内置函数的套数。

前三名的英雄似乎是牧师、法师和圣骑士,而且没有一个非常有统治力的。

从副牌中的牌的角度来看,我对副牌中的牌的类型做了一个简单的评估。

该图可能不清楚,但其目的是说明一副牌中的牌的重新分配,该图显示,例如,50%的牌具有:

  • 0 件武器
  • 0 到 10 个咒语
  • 0 到 17 个小黄人

正如我们所看到的,大部分的卡牌都没有使用武器,而且拥有更多的小喽啰似乎是比法术更好的计划。

现在让我们来看一下更高级的数据分析。

英雄 VS 套牌 VS 卡片

从卡片的角度来看,英雄们在相关卡片的重新分配上有一点不同。

大多数武器都与战士/盗贼/猎人/萨满和圣骑士有关,每个英雄都有大约 150 张卡片。现在让我们从一个奴才的角度来看一下对套牌内容的评价。

术士似乎是那种需要比普通玩家拥有更多的爪牙的英雄。让我们从法术的角度来看。

术士是从群众中崛起的(不是跟着跟班潮流)。最后,让我们看看武器方面。

在最后两个图表中,我们展示了在卡中拥有武器的特定英雄身上武器的用法。

我将花一些时间来分析与特定英雄相关的卡片的更多详细信息。先来分析一下卡的成本。

我们可以看到,费用上的第一个四分位数对于所有的英雄都是一样的,费用值是 2(快速发挥)。除了德鲁伊,第二个四分位数的牌的成本在 2 到 3 之间。在第三个四分位数中,英雄身上发生了一些分裂:

  • 成本 5(术士/牧师/战士/猎人/圣骑士/法师)
  • 费用 4(盗贼/萨满)
  • 成本 6(德鲁伊)

从一般的角度来看,这个分析说明了基于英雄能力的游戏性的不同。现在让我们来看看卡牌的宠物一面的攻击/生命值。

在小黄人(生命值/攻击值)方面,英雄之间的 boxplot 没有显著差异。

现在让我们看看取决于英雄的总费用/攻击/生命值。

从成本角度来看,我们可以看到,术士的价值范围很广,从 20 到 220 左右。大约在第三个四分位数,所有的英雄都有接近 100 马纳斯的中间值(也许对于建造一副牌来说是个不错的选择)。现在让我们看看牌组的攻击点。

从攻击的角度来看,德鲁伊拥有最广泛的价值范围(0-122 左右),但是一副牌的总攻击中值似乎在 50 点左右。让我们看看甲板的健康状况。

在这种情况下,对于所有的英雄来说,生命值的范围是相当大的,但是我要说的是中间值大约是 70 点。

这种分析是非常高层次的数据集,并且似乎一般在 Hearthpwn deck 上构建:

  • 大多数包含少于 17 个爪牙/ 10 个法术/ 0 件武器
  • 这副牌的平均成本大约是 100 法力值
  • 副牌的攻击中值约为 50 点
  • 甲板健康的中位数大约是 70 分。

除了这些发现,所有这些数据给了我一些想法。

为什么不建立一个卡片推荐系统?

如何建立一个(简单!)炉石卡牌推荐

老实说,这不会是我要做的最疯狂的推荐系统,但是让我们从小处着手。对我来说,最直接的推荐系统是“最受欢迎的商品”推荐系统。

每个英雄(以及所有套牌中)的卡片使用是这个推荐器的核心。在下表中,我为每个英雄制作了前 25 张卡片(但是你可以在这个谷歌电子表格上找到所有卡片的排名)。

我计划使用这个数据集作为推荐系统的文章来源。就过程/算法而言,机器学习领域令人兴奋,关于这个主题的一本好书是 P 金·福尔克的实用推荐系统。

老实说,这是一本关于这个主题的极好的书,我想我会把它作为我未来文章的框架。

敬请关注,不要犹豫,给一些反馈。

最初发布于 2020 年 1 月 15 日 http://the-odd-dataguy.com

深入研究 Python 中的队列模块——它不仅仅是 FIFO

原文:https://towardsdatascience.com/dive-into-queue-module-in-python-its-more-than-fifo-ce86c40944ef?source=collection_archive---------14-----------------------

多线程的良好候选

micha Parzuchowski 在 Unsplash 上拍摄的照片

队列是一种非常实用的数据结构。通常与数组链表、线性数据结构相比较。线性结构与其他结构的不同之处在于添加和删除发生的位置。

队列数据结构有三种类型: FIFO、LIFO优先级队列。Python 中的队列模块实现了所有这些类型。此外,它在线程编程中也非常有用,这使得这个模块更加闪亮。

在本文中,我将首先帮助您更新关于数据结构的知识,并学习如何使用模块queue。然后我将向您展示如何使用queue来解决 2 个不同的多线程问题。最后我会说说这个模块的利弊。

数据结构

在讨论一些高级特性之前,让我们先来讨论一下队列数据结构,并学习如何使用该模块来创建一个“简单”的队列。

先进先出——先进先出

这是队列的基本类型。首先插入的元素将首先被删除,这类似于现实世界中的等待列表。越早加入名单,你就能越早得到音乐会的门票。

先进先出()

Python 模块提供了实现 FIFO 队列的queue.Queue()queue.SimpleQueue()queue.SimpleQueue()是 Python 3.7 中的新特性。它们之间有两个不同之处:

  1. SimpleQueue()线程编程中不做任务跟踪。线程编程将在后面讨论。
  2. SimpleQueue()是一个无界 FIFO 队列,而Queue()可以有一个上限。在这两个类中,如果队列为空,get()操作将被阻塞,直到新元素被插入。在Queue()中,如果队列已满,put()操作也将被阻塞,直到元素被移除。这种情况绝不会发生在SimpleQueue()身上。根据 Python 文档,可以在get()put()中使用block=False来禁用该块,然后你会立即收到一个queue.Fullqueue.Empty异常。

queue-fifo.py

后进先出法

LIFO 队列还有一个名字:Stack。最后插入的元素将首先被移除。这就像超市的货架一样。人们通常会拿第一排最近添加的项目。

后进先出()

这在queue.LifoQueue()类中实现。除了移除元素的顺序之外,界面与queue.Queue()相同。如果您将结果与之前的结果进行比较,您应该会看到打印结果的差异。

queue-lifo.py

优先级队列

顾名思义,优先级队列赋予队列中的每个元素一个优先级。它首先弹出优先级最高的元素。在下图中,元素 5 具有最高优先级,因此它将是情况 2 和 3 中弹出的第一个元素。

优先队列()

现在的问题是 Python 如何决定优先级?在队列模块中, 最低值的条目首先被检索 。该值越低,其优先级越高。那么在前面的例子中,2 实际上比 5 具有更高的优先级。这是因为 Python 优先级队列内部使用了 最小堆队列算法 。最小堆的一个特性是最小的元素是第一个元素。

堆实际上是一棵二叉树,其中节点的值小于其子节点的值。当一个新节点被推送到树中时,树将被再次排序。根节点总是下一个要检索的元素。push()pop()操作的时间复杂度分别为 O(log n)和 O(1)。

闵堆()

queue-priority-simple . py

优先级队列不仅适用于数字,也适用于复杂的数据类型,如元组或定制类,只要对象是可比较的。为了使一个类对象具有可比性,你需要实现几个丰富的比较方法。更简单的方法是使用[@dataclass](/understand-how-to-use-namedtuple-and-dataclass-in-python-e82e535c3691),dataclass 可以用 config [order=True](https://docs.python.org/3/library/dataclasses.html#module-level-decorators-classes-and-functions)为你实现这些方法。

在本例中,对象首先按排序,然后按排序。

queue-priority-advanced . py

线程编程

队列数据结构有很多应用。一个众所周知的用例必须是消息队列,其中我们有一个生产者和一个消费者。生产者和消费者通常位于不同的线程中。幸运的是,Queue()类有一个线程安全的实现,带有所有需要的锁定机制。因此,来自不同线程的生产者和消费者可以安全方便地使用同一个队列实例。

本质上,队列存储了许多要完成的“任务”,线程可以将任务插入队列,并在任务可用时从队列中取出任务,然后完成任务并返回进行下一个任务。

如果你是 Python 线程的新手,我强烈推荐你阅读 Real Python 的Python 线程介绍。这是一篇非常好的文章。

[## Python 线程介绍——真正的 Python

立即观看本教程有一个由真正的 Python 团队创建的相关视频课程。和书面的一起看…

realpython.com](https://realpython.com/intro-to-python-threading/)

好的,我假设你已经阅读了这篇文章,并且很好地理解了threading模块在 Python 中是如何工作的。让我们把它与Queue()课结合起来。我在这里举了两个例子。它们是略有不同的用例。

例 1 :创建一个图片 URL 队列,用多线程“并行”下载那些图片。(如果你不明白我为什么在这里加引号,请阅读上面的文章)

我将在这个例子中使用save_image函数。

def save_image(id, url):
    with open(f'pic{id}.jpg','wb') as image:
        response = requests.get(url, stream=True)
        for block in response.iter_content(1024):
            if not block:
                break
            image.write(block)

每个Thread对象都有启动线程的start()方法,还有通知主线程等待直到线程 x 完成运行的join()方法。我们可以这样解决问题:

download-images-1.py

由于Queue()是为多线程设计的,它还提供了 2 个方法,支持任务跟踪:Queue.task_done()Queue.join()Queue.task_done()表示队列中的一个任务已经被处理,通常在get()之后调用。Queue.join()类似于Thread.join(),它将阻塞主线程,直到队列中的所有任务都被处理完。但是你不必创建一个工人列表workers并在最后做一个循环来加入每个线程,只需做一个单独的queue.join()。让我们检查一下代码。

下载-图片-2.py

例 2 :创建一个消息队列,一个生产者,一个消费者。来自不同线程的生产者和消费者能够与同一个队列实例进行交互。生产者将发送一个警告消息告诉消费者停止这个过程。

在示例 1 中,我们让 10 个不同的线程执行相同的功能。但是在这个例子中,两个线程正在做完全不同的工作。由于 Python 线程中没有真正的“并行处理”,所以一个线程将首先完成这项工作。在这种情况下,首先触发生产者线程,然后触发消费者线程。您也可以在结果中看到这种行为。

如果我取消第 9 行的注释,结果会是什么?想想吧!😄

生产者-消费者-队列. py

如果我在每个put()操作之前暂停生产者线程,那么我们将更清楚地看到生产者和消费者之间的交互。

Insert element 0
Retrieve element 0
Insert element 1
Retrieve element 1
Insert element 2
Retrieve element 2
Insert element 3
Retrieve element 3
Insert element 4
Insert sentinel
Retrieve element 4
Receive SENTINEL, the consumer will be closed.

结论

**queue**的一个明显优势就是它的线程安全特性。但我们不能把这种便利视为理所当然。如果我们没有线程安全queue会发生什么?如果我想在多个线程之间共享一个字典或者一个定制的类对象怎么办?

对于初学者来说,他们可能会遇到比赛条件的问题。同样,同一篇文章有一个关于竞态条件的非常直观的例子。基本上,相同的值不会在线程间同步。一个解决方法是使用threading.Lock()。但是它需要一些额外的代码和额外的注意力来控制线程。

****另一个优势是避免潜在的内存爆炸。如果生产者的工作速度比消费者快得多,那么任务将被存储在“管道”中,直到到达内存边界。在这种情况下,maxsize属性可以避免这个问题。

**queue** 也有助于程序减少繁忙的等待。程序不需要一直试图从“管道”中拉东西,但是线程会被阻塞,直到数据可用。

同时,我们也应该理解queue的局限性。queue使用多线程,所以程序不会因此运行得更快。另外,maxsize指的是队列中的项目数,而不是内存大小。所以,还是有可能达到极限的。

我希望你喜欢这篇文章!如果你有任何想法,请在下面留下你的评论。

参考资料:

**[## 队列——一个同步的队列类——Python 3 . 8 . 3 文档

源代码:Lib/queue.py 该模块实现了多生产者、多消费者队列。它在…方面特别有用

docs.python.org](https://docs.python.org/3/library/queue.html)** **[## heapq -堆队列算法- Python 3.8.3 文档

源代码:Lib/heapq.py 这个模块提供了堆队列算法的实现,也称为…

docs.python.org](https://docs.python.org/3/library/heapq.html)**

背离棒线,为什么&如何

原文:https://towardsdatascience.com/diverging-bars-why-how-3e3ecc066dce?source=collection_archive---------18-----------------------

用分歧讲故事

图片来自 Unsplash

术语 divergent 来自拉丁语,表示分离或不同。它是意见的不一致、不一致、差异、交叉和争论的同义词。它意味着对某一特定问题的不同意见或分歧。它还用于表示这些问题之间的差异正在增加。自然,必须有一些视觉表现来显示这一方面是人类的特征。分叉棒线是最适合此目的的图表。

图 1:发散堆积条形图,SD:强烈不同意;d:不同意;n:没意见;答:同意;山:非常同意。图片作者。

为什么:发散条形图是为了方便多个类别的比较。它的特殊设计有助于对比特定类别中的数值。它的主要应用是正确显示问卷或调查的结果,尤其是那些使用 Likert 量表的结果。它能让我们快速想象出那些不利的、不想要的或消极的积极的、想要的或正面的反应。

如何操作:传统的变体由两个水平条组合而成,其中一条从右到左,另一条从左到右,两者都从一个公共的垂直基线开始,通常位于图表的中心。与所有条形图不同的是,每个条形的长度与其要显示的数值成比例。每个条形代表一个类别,它们之间通常留有一些空间。

发散条的最佳编码是当只有两个备选项需要比较时。L. Verde Arregoitia 使用该图“代表峡谷不同斜坡上的两个平行植被横断面,其中在五个采样点记录了本地植物和外来植物”(#1)。这张图表清楚地显示了引进植物和本地植物在物种数量上的对比。这些类型的图表也被称为双边,对立,滑动,或双向条形图。

图 L. Verde Arregoitia 使用的分叉棒线,来源:(#1)。

通常的做法是使用不同的颜色和不同的颜色强度来区分向左的线条和指向另一侧的线条。基线左侧的值不一定表示负值,但通常是不需要的或不令人满意的响应。还可以方便地将条形按升序或降序排列,以便于讲故事。

最普遍的变体是发散堆积条形图(图 1)。它们是水平堆叠条形图的延伸,增加了一条垂直基线。每个条形被分成一个接一个堆叠的水平矩形段。由线段指示的数值可以是绝对值或百分比(相当于单个堆积条形图与 100%堆积条形图,#2)。

发散堆积条形图广泛用于显示通过李克特量表分析的调查结果。当被不同的人口统计类别分开时,他们可以比较受访者的回答。显示有利或有利结果的线段通常显示在基线的右侧,而代表不利或不利结果的线段则放在中心线的左侧。在某种类型的中性反应的情况下,它位于中心基线之上。

李克特量表是社会心理学家伦西斯·利克特在 1932 年的博士论文中提出的。当调查的参与者被咨询以对他们对咨询项目的同意程度进行分类时,使用它。最常用的量表包括以下五种回答备选:强烈不同意、不同意、中立、同意、强烈同意。虽然 5 点李克特量表是最常见的,但其他作者包括了具有不同数量响应的其他量表(图 4),甚至还有仅包含两个选项(如有趣和不有趣)的语义差异量表。视觉标度和表情符号也被用作指示备选方案的非文本方式(图 3)。

图 3:李克特量表的表情符号,来源:(#3)。

下图显示了 2016 年总统大选前在美国进行的一项调查结果(#4)。该调查涉及不同人口类别对选举进程之外的政治的态度:年龄、性别、政党、教育水平和种族。有四种可能的回答:两种赞成永久参与政治,两种不愿意。可以看出,与婴儿潮一代和老年人相比,年轻人对政治持不情愿的态度。受教育程度也是如此:51%拥有大学或更高学位的人大部分时间都对政治感兴趣,而最不感兴趣的是那些没有完成高中学业的人。发散堆积条形图无疑是一种非常有效的可视化策略,可以在相对较小的空间内显示大量信息,便于比较、对比和讲述故事。观察到颜色和颜色强度的适当使用来区分四个备选方案。

图 4:由分叉条表示的调查响应。来源:#4。

理查德·黑贝格(Richard Heibegger)和内奥米·罗宾斯(Naomi Robbins)在他们 2011 年的论文《绘制李克特和其他评级量表》(Plotting Likert and Other Rating Scales)中热情地为背离的堆叠条形图辩护。作者举例说明,回顾和评论了一些在研究中使用的图表技术,如李克特量表。他们将背离的棒线与标准的、分组的、堆叠的和多重的棒线图进行比较。他们还将它们与表格、雷达图、带状图和华夫饼图表进行比较。他们总是得出这样的结论:发散堆积条形图最适合用于比较人口统计、职业和其他类别的调查结果。

发散堆叠棒线遇到了与标准堆叠棒线相同的困难:随着分段数量的增加,观众可能会感到困惑,讲故事变得更加困难。基于这个原因,的分段数不应该超过五个。有利于发散棒线的一个区别是,通过有一个中心基线,这些线段中的两个与该基线接触,这使得比较它们更容易。

Python 没有绘制分叉棒线的特定功能。最常见的策略之一是使用堆叠的水平条*(barh(stacked = True))*后跟一条*axvline*指令,在图的中心画一条垂直线。你也可以使用*hlines*,一个绘制水平线的函数,用某个值*linewidth*来模拟那些线,实际上,是水平的线条。这两种策略都需要一些预先编码来确定将要水平堆叠的段的长度和顺序。Tableau、Power Bi、Excel 和其他复杂的可视化工具也需要一些前期工作才能获得专业水平的结果。

蝶形图非常类似于发散条形图,每个类别或变量有两个水平条。在蝴蝶图中,只有两组数据可以使用两个带有中心基线的水平条进行比较,类似于蝴蝶的翅膀。蝶形图通常在条形之间留有空间,用来放置被比较变量的名称(图 5)。一些可视化工具将蝴蝶图与龙卷风图相匹配。然而,后者通常仅用于根据选定输入变量的变化对输出变量进行不确定性和敏感性分析(#7)。

图 5:蝴蝶图。资料来源:#8。

不要将堆积条形图与人口金字塔混淆,这一点非常重要。人口金字塔是两个水平条形图的组合,代表给定人口中男性和女性人口的年龄结构。按照惯例,男性表示在左边,女性表示在右边。

这种图表代表一个国家、地区或洲的居民分布,按年龄和性别划分。纵轴显示年龄(以年度或五年为一组,称为组群),横轴显示每个组群中男性和女性的总数或百分比。通常显示一对人口金字塔,每一个都代表某个时间或地点,以检测所研究人口的变化或趋势。

图 6:人口金字塔。图片作者。

结论:

发散条形图允许不同项目或类别之间的比较。它主要应用于使用李克特量表开发的调查或问卷的可视化分析。然而,人们应该始终考虑实际实现所需的额外工作是否在视觉上合理。

如果你对这篇文章感兴趣,请阅读我以前的(https://medium.com/@dar.wtz):

Mekko 图表,为什么和如何

[## Mekko 图表

为什么和如何

towardsdatascience.com](/mekko-charts-f38311c576e2)

仪表图和项目符号图,为什么和如何,用仪表讲故事

[## 仪表图和项目符号图

为什么&如何,用量表讲故事

towardsdatascience.com](/gauge-bullet-charts-cfe171ca3094)

参考文献:

【1:【https://luisdva.github.io/rstats/Diverging-bar-plots/】

#2:韦茨、达里奥,《堆积条形图,为什么&如何,讲故事&警告》,https://towardsdatascience . com/Stacked-Bar-Graphs-Why-How-f1 b 68 a 7454 b 7

3:https://www . vision metrics . net/en/blog/research/Likert-scale-overview/

**# 4:http://thebarchartguy . blogspot . com/2015/01/divergent-stacked-bars-survey-data . html

**# 5:http://www . asa SRMs . org/Proceedings/y 2011/Files/300784 _ 64164 . pdf

**# 6:https://www . Montana . edu/msse/Data _ analysis/Likert % 20 survey % 20g raphs . pdf

**# 7:http://wiki.analytica.com/Tornado_charts】

# 8:https://www . slide team . net/butterfly-chart-tornado-chart-for-price-comparison-PowerPoint-slide . html

多样性是我们所需要的。

原文:https://towardsdatascience.com/diversity-is-what-we-need-98b5f09287c1?source=collection_archive---------36-----------------------

现在思考人工智能的未来。

照片:V.A. Merzmensch

你有没有想过所有的狗?随着 Google Deep Dream 的发布,AI 识别并放大了所有照片中的狗脸。到处,每个角落:狗,除了狗什么都没有。

这个谜团的答案很简单——但令人担忧:

在 Deep Dream 的案例中,数据集来自 ImageNet,这是一个由斯坦福大学和普林斯顿大学的研究人员创建的数据库,他们建立了一个包含 1400 万张人类标记图像的数据库。但是谷歌没有使用整个数据库。相反,他们使用了 2012 年发布的 ImageNet 数据库的一个较小的子集,用于一场比赛…这个子集包含“120 个狗子类的精细分类” (来源:fast company)

人工智能的 Pareidolia只能检测它所训练的模式和物体。数据集才是最重要的。它们越大,质量越好。

但是还有一个方面:贴标签。

你还记得比根那个令人兴奋的时代吗,你可以在历史上第一次把一只狗和一架航天飞机混在一起。

我发现了一个奇怪的现象。如果你想用 BigGAN ( 笔记本)生成一个乐器,你就得到一个音乐家。

法国号:

比根,566)圆号,法国号

萨克斯:

比根:776)萨克斯,萨克斯管

口琴

比根:593)口琴,口风琴,竖琴,口琴

唯一的解释是:给图片贴标签的人没有做好他的工作。它不是仅仅给一个乐器贴上标签,而是用一个乐器,包括一个演奏者,给整个形象贴上标签。正确的做法应该是给一张带有萨克斯管的图片贴上“萨克斯管”的标签,而不添加任何其他元素或特征。

如果你让一个在这个数据集上训练的人工智能驱动的机器人给你带来一个萨克斯管,它会给你带来一个萨克斯管演奏家,你不应该责怪机器。它使它的工作变得完美。这是关于我们,谁是错误的。

正如我们将看到的,不准确的贴错标签会导致可怕的后果。

另一个实验,我报道的关于的实验,已经显示了围绕标签不足而产生的问题:

凯特·克劳福德特雷弗·帕格伦用他们的 ImageNet-Roulette ( 现在离线)演示了贴错标签的危险。

你可以上传你的照片——砰!——你被系统描述了。该描述来自 ImageNet 中的标签,而这些标签是由人类书写的。不准确。带来毁灭性的后果。

对我来说,这或多或少是无害的(我上传了一张自己为萨沃米尔·mrożek):的舞台剧《卡罗尔》表演的照片

照片:v .佩雷尔曼

我被检测为不吸烟者(“不吸烟的人”)。我手中的巨大来复枪被艾瞄上了。

不过,也有其他案例,比如给某人贴上“强奸嫌疑犯”的标签:

一次性标签被曝光在 ImageNet 中贴错标签。这个项目和由两位艺术家举办的相关展览“训练人类”对 ImageNet 产生了影响:

ImageNet 背后的研究人员宣布,他们将删除数据集“人物”类别中 120 万张图片中的一半以上。(来源)

推特事件

你可能在最近几天偶然发现了这样的推文:

https://twitter.com/NotAFile/status/1307337294249103361

或者这个:

https://twitter.com/bascule/status/1307440596668182528

或者:

此外:

我在想,那里发生了什么事。如果你上传一张更大的图片到 Twitter,它会将你时间轴中的预览裁剪到图片的相关部分。如果您打开在此处裁剪的整个图像,您将看到以下内容:

人们对面部图像进行了实验,发现了一种可以被描述为“推特的种族主义”的现象,因为白人占了上风,被选为“相关部分”,与有色人种相反。

尽管如此,在奥巴马与猫的对比中,奥巴马是赢家:

推特上出现了一波可以理解的警告。

但是正如我们所看到的——问题仍然在标签上。我怀疑推特被训练成隐藏非裔美国人的种族主义意图,并被白人面孔所控制。或者被男人。

最有可能的问题是:数据集。他们缺少足够的多样性。他们都是白人。他们有偏见。人为因素。

我们需要更多的多样性。而不是作为“好心”搬东西的托辞。而不是“好吧,如果你幸运的话,这里有一些多样性”。

而是多样性作为一种基本的、不可或缺的、必要的、强制性的思维方式。

所以,亲爱的人工智能社区,亲爱的编码者,亲爱的贴标签者,亲爱的人工智能研究者,大家。

这不是演习。我们才刚刚开始培育和训练我们的人工智能系统。如果我们不站在一个角度思考,如果我们忽略了未来的思考,我们的 AI 就会有偏差。

万一 AI 会伤害人类,那是我们人类的错。

用动态填充和统一长度批处理将拥抱面部变形器训练时间除以 2 或更多

原文:https://towardsdatascience.com/divide-hugging-face-transformers-training-time-by-2-or-more-21bf7129db9q-21bf7129db9e?source=collection_archive---------10-----------------------

机器学习工程

减少训练时间有助于在固定的预算时间内进行更多的迭代,从而达到更好的效果。

上面的照片是由这个(免费用于非商业用途)和那个 (Pexel 许可,免费用于任何用途)制作的

2020 年 6 月 4 日更新:根据一项建议,在报告中增加了对口味卡门贝干酪进行的实验。TL;DR:训练时间从 4 小时减少到 1 小时 30 分。

我在 Lefebvre Sarrut 出版社工作,这是一家出版公司,是欧洲法律行业的主要参与者。正如前面的文章中所解释的,我们的需求要求我们在非常大的文本数据集上应用深度学习(法律案件匿名化、文档分类等)。),这就是为什么我们对让我们所有的(机器学习)工具变得更快如此感兴趣。

本着一篇先例文章的精神,这篇文章的目的是探索两个非常简单的优化,它们可以显著减少变形金刚库的训练时间,而不会对准确性产生负面影响。我们在一个大型的知名 NLP 数据集(X-NLI 的法国部分)上运行了 21 个实验+ 12 个再现性实验,我们表明,对于模型的基础风格,通过简单地使用现成的法国 BERT 模型( CamemBERT )、默认参数、单个消费级 GPU 和这些优化,我们可以达到最大令牌长度为 128 的、最大令牌长度为 16 分钟的 比没有任何优化的 56 分钟训练获得的分数高出 0.5 分,比 CamemBERT 模型作者为此任务报告的分数高出 0.3 分。 在相同的模型上,对于 493 最大令牌长度,增益甚至更令人印象深刻,其中训练时间从没有任何优化的 4h38 减少到有所有优化的 1h01,仍然达到相同的分数。 大型 型号达到了类似的训练时间缩减(从 4h 到 1h30 为 128 令牌长度)。

重现实验的源代码可从那里获得。

这些优化不是特定于任务/模型/语言的,但前提是下面的代码是为 Pytorch 编写的。

如果你对这个话题感兴趣,在推特上关注我:https://twitter.com/pommedeterre33

查看报告,看看权重&偏差是一个你不知道自己需要的强大工具

此外,我们记录了所有关于重量&偏差的实验,这是一种范围更大的在线张量板,所以你可以自己分析我们的实验结果,这里是的报道

(注意“等长配料”在报告上被命名为“智能配料”)

“动态/可变长度填充”是 NLP 在 2017-2018 年的事情,但现在单个神经网络架构使得任何 GPU 所有者都能够在几乎任何任务上达到 SOTA 分数,似乎 NLP 社区已经将重点从寻找最佳批量大小转移到在神奇的架构上构建东西。“统一长度批处理”是一个天真的想法,进一步推动我们想要检查的动态填充增益。

在撰写本文时,对于大多数常见任务来说,这两个工具在 Transformers 库上都不是现成的。

您只能在文本生成/语言建模任务中找到动态填充。有希望的是,我们将看到这两种技术都很容易在普通任务(分类等)的用户端实现。)并且我们认为大多数 NLP 从业者应该测试/使用它们。

在过去的一年里,来自拥抱脸的变形金刚库成为使用大型预训练语言 NLP 模型的标准方式。它附带了大量的功能,涵盖了大多数 NLP 用例,并且有一个完美的 API,直到你开始期望它是完美的。自从版本 2.9 带给我们训练器类以来,这种感觉更加强烈,这是一个精心设计的 William Falcon 的 Pytorch Lightning 训练 API 适应特定的变压器要求,它将用户从大多数工程方面的训练中解放出来(张量板测井、混合精度、梯度累积、多 GPU 设置等)。)因此是微调模型的新的默认方式。

在一切都如此完美的情况下,你倾向于相信一切都被优化到了极致。不要忘记,这个库还很年轻,团队同时在几条战线上工作(最近的例子是与来自 Azure DevOps 的吴合作,利用 ONNX 和在一些设置中减少推理时间。当你深入图书馆时,你可能仍然会发现一些有趣的低挂水果。

我们进行了实验,总结了我们在下面和那里的发现。

如果你还不是“填充/注意力面具”专家,你可能想再读一遍这篇为《老式 RNN》写的优秀的文章(摘自威廉·法尔肯 …!).简而言之,在一批序列上训练神经网络要求它们具有完全相同的长度,以构建批矩阵表示。由于现实生活中的 NLP 数据集总是由可变长度的文本组成,我们经常需要通过截断它们来缩短一些序列,并通过在末尾添加一个重复的假标记(称为“填充”标记)来延长一些序列。因为填充令牌不代表真实的单词/子单词/信号,所以当大多数计算完成时,在计算损失之前,我们通过注意力屏蔽矩阵将填充令牌信号乘以 0 来擦除它。

这两种优化背后的主要思想是尽可能避免不使用的计算:

  • 动态填充:我们限制添加填充令牌的数量,以达到每个小批量的最长序列的长度,而不是为整个列车组设置一个固定值。因为添加的令牌的数量在小批之间变化,所以我们称之为"动态"填充;
  • 统一长度批处理:我们通过生成由相似长度序列组成的批处理来进一步推动逻辑,因此我们避免了极端情况,即迷你批处理中的大多数序列都很短,我们需要为每个序列添加大量填充标记,因为同一迷你批处理的 1 个序列非常长。

此外,我们检查了第三种选择的好处,混合精度,它单独使用或与上述两种技术结合使用的价值。

设置

为了检查这些优化是否与基于 transformer 的语言模型配合良好,我们在 X-NLI 的法语部分运行了 14 个不同设置的实验。

如果你不知道 X-NLI,它基本上是一个句子对分类任务,其中模型需要猜测第二个句子与第一个句子相比是否需要/矛盾/中性(3 类)。X-NLI 训练集是大型英语数据集的机器翻译,测试集由来自同一英语数据集的 15 种语言(包括法语)的 5K 对人工翻译组成。

用于实验的模型是卡门贝塔建筑,由脸书费尔和 Inria 实验室用法语文本训练而成。存在几种口味,下面的数字与已经发布的第一种相关,在变形金刚中称为卡门贝干酪基(在 130 Gb 数据上训练的 110 万个参数)。camembert-large实验报告中均有。

Camembert 论文作者在 10 epoch s 中达到了 81.2%的准确率,提前停止,1e-5 学习率,512 个令牌的序列长度以及其他一些东西

对于每个实验,我们将训练限制在 1 个时期,因为我们只能使用单个 GPU 来运行所有实验。所有运行都使用相同的种子(不同种子的“再现性”实验除外,如下所述)。除非另有说明,超参数保持默认值,因为我们没有资源对每个实验进行网格搜索。正如我们将看到的,在大多数实验中,我们在 X-NLI 上击败了作者报告的分数,所以默认设置可能已经足够好了。

X-NLI 主要由短句对组成:当短句对的两个部分连接成一个序列时,超过 97%的短句对长度小于 128 个标记。这种长度分布对减少培训时间的机会有很大影响。简而言之,在这个数据集上,即使在消费级 GPU 上,也可以在 128 个令牌处截断序列来构建“大型”批处理。在一个更平衡的数据集上,比如 T2 的马可女士,你需要 512 个单词的限制才能接近 SOTA 的平均倒数排名。在这两种情况下,这里介绍的两种技术带来了大量的训练时间减少(2 倍或更多),但出于不同的原因,我们将在下面进行分析。在我们对私有数据集使用这些优化时,无论数据集的特征如何,我们总是对训练时间产生显著影响。

时间和精度是在单个 Nvidia 2080 TI GPU 上测量的。3 个优化选项的每个组合已经运行了 2 次,一次是将一批 64 个序列截短为 128 个记号,第二次是将一批 16 个序列(2 步 8 个序列和单个梯度累积)截短为 493 个记号。493 是字符数最长序列中的 Camembert 子词标记数,可能是训练集中标记数最长的标记序列之一。步长已被设置为该 GPU 在内存中可以占用的最大值。

计时包括中间评估(在我们的例子中是每 500 个小批量),因为在现实生活中,大多数 NLP 从业者都是这样进行培训的。

结果

每个组合随时间变化的精确度,报告中的交互式图表此处

TL;博士:

定时

  • 单独使用动态填充可以显著减少训练时间,这可以通过使用统一大小批处理和混合精度来增强;
  • 在一些设置中(小批量+短序列),混合精度可以产生更长的训练时间,在其他情况下,它是游戏改变者(大批量和/或长序列)。

精度

  • 在 14 次运行中,有 11 次在单个时期内获得了 81.18%以上的分数(Camembert 论文中报道的 10 个时期的分数,并提前停止);
  • 当我们比较成对的运行(相同的设置,在 128 处截断与在 493 处截断)时,不出所料,截断平均具有(小的)准确性成本,即使只有 3%的数据集与 128 令牌截断有关。

通过使用优化和混合精度,我们在 16mn 训练中击败了 4h38 训练的分数!

基础模型的发现与大型模型相同,额外 12 个实验的测量结果在报告中。

优化机会

当你要抛出结果时,避免计算

如上所述,pad 令牌信号通过应用注意屏蔽来消除。序列末尾的填充标记越多,执行的未使用的计算就越多。

在训练器类中,您定义了一个(固定的)序列长度,训练集的所有序列都被填充/截断以达到该长度,没有任何例外。在 X-NLI 上,最短的序列是 10 个记号长,如果你提供 128 个记号长,你将把 118 个填充记号加到这 10 个记号序列上,然后在这 118 个有噪声的记号上执行计算。

最差,如原伯特回购自述“…注意是序列长度的平方。换句话说,一批 64 个长度为 512 的序列比一批 256 个长度为 128 的序列要贵得多。”

小批量由从数据集中取样的少量序列组成。即使在 X-NLI 中随机选择,小批量中的最长序列也有可能比整个训练集的最大序列长度短。因为学习/梯度下降是在小批量水平执行的,所以我们有机会限制填充效应,更准确地说,我们可以首先在小批量中搜索最长的序列长度,然后相应地填充其他序列。

这些操作可以在 collate_fn 功能中执行。该函数的用途在 Pytorch 文档中有所描述,基本上它采用数据集返回的单个示例,并将它们合并以构建张量矩阵,并将其发送至模型。

动态填充

如上所述,这个想法是在小批量水平而不是数据集水平调整序列长度。这样我们可以限制未使用的计算。该工作在 Pytorch 数据加载器内进行。让我们提醒一下它是如何工作的:

Pytorch 数据加载器内部

组件:

  • Dataset() 是访问原始文本数据的砖块,是一个简单的字符串列表或类似数据库连接器的东西;
  • Sampler() 生成索引,指向数据集中的一个数据点。它遵循一种策略,例如顺序生成(对于测试集)或随机生成(对于训练集)。
  • collate_fn() :对于每个小批量,它接收由采样器选择的数据点(来自数据集)并将它们分组到一个张量中(理论上它可以是其他东西,但通常这是您期望的数据加载器输出/模型输入)。

collate_fn 是执行动态填充的最佳位置。幸运的是,Pytorch Dataloader 在其构造函数中有一个参数来提供我们自己的实现,不需要覆盖任何东西。变形金刚库中的训练师类在其构造函数中有一个类似的参数,我们将使用它。它不是一个函数,而是等待一个“整理器”(一个特定于 Transformers 的类)的实例,它只有一个目的,包装 collate 方法。

在下面找到一个可能的排序器类的实现。

*# ...*def **pad_seq**(seq: List[int], max_batch_len: int, pad_value: int) -> List[int]:
 *# IRL, use pad_sequence
    #* [*https://pytorch.org/docs/master/generated/torch.nn.utils.rnn.pad_sequence.html*](https://pytorch.org/docs/master/generated/torch.nn.utils.rnn.pad_sequence.html)
    return seq + (max_batch_len - len(seq)) * [pad_value][@dataclass](http://twitter.com/dataclass)
class **SmartCollator**(DataCollator):
    pad_token_id: int def **collate_batch**(self, batch: List[Features]) -> Dict[str, torch.Tensor]:
        batch_inputs = list()
        batch_attention_masks = list()
        labels = list()
        *# find the max length of the mini batch*
        max_size = max([len(ex.input_ids) for ex in batch])
        for item in batch:
            *# apply padding at the mini batch level*
            batch_inputs += [pad_seq(item.input_ids, max_size, self.pad_token_id)]
            batch_attention_masks += [pad_seq(item.attention_mask, max_size, 0)]
            labels.append(item.label)
        *# expected Transformers input format (dict of Tensors)*
        return {"input_ids": torch.tensor(batch_inputs, dtype=torch.long),
                "attention_mask": torch.tensor(batch_attention_masks, dtype=torch.long),
                "labels": torch.tensor(labels, dtype=torch.long)
                }

动态填充有助于减少训练时间吗?

我们运行了 4 个实验,按每批大小进行分组,对于每组,我们比较了使用和不使用动态填充的情况。当为以下情况启用时:

  • 16 批未截短的序列,时间从 4h39 减少到 0h 59(-79%);
  • 64 个序列的批次被截短为 128 个标记,计时从 0h56 减少到 0h48 (-15%)。

在这两种情况下,计时减少都是显著的,并且对于长序列是 4 倍强。这是有意义的,在训练集中,97%的例子短于 128 个令牌,所以对于大多数例子,我们为拥有 493 个最大序列大小而缴税。通过使用优化,我们只为有用的计算付费。

对于 128 个记号的截断,仍然有增益,因为大多数序列仍然比 128 个记号小得多,并且 BERT 复杂度关于其输入长度是二次的,避免的计算成本低得多,并且训练时间减少“仅仅”15%。

会影响准确性吗?

我们运行了 4 个实验,按每批大小进行分组,对于每组,我们比较了使用和不使用动态填充的情况。当为以下情况启用时:

  • 16 批未截短序列,准确率从 81.42%提高到 82.0%;
  • 一批 64 个序列被截短为 128 个标记,准确率从 81.0%提高到 82.0%。

在这两种情况下,动态填充都可以提高精度。

均匀尺寸配料

统一批量生产由简单地构建由相似长度序列制成的批次组成。目的是在与动态填充结合使用时尽可能减少填充。

有许多方法可以实现它,我们遵循的方法是:

  • 在一个简单的 Python 列表中按长度对示例进行排序,
  • 随机选择一个索引,
  • 提取示例和后面的 n 个示例( n 为批次/步长),
  • 从列表中删除提取的示例,
  • 重复此操作,直到列表中不再有示例。

这样,每一批都是由相似长度的序列组成的,但是后面的几批是不同长度的。

天真(简单易懂/不干净)的实现可能看起来像这样:

统一大小批量真的能减少训练时间吗?

为了减少时间,我们之前已经展示了动态填充带来了大量的训练时间减少,让我们比较使用动态填充和不使用统一大小批处理以及启用两种优化的训练时间。用于:

  • 批 16 个未截短的序列,训练时间从 1h01 减少到 0h 52(-15%);
  • 一批 64 个序列被截短为 128 个标记,训练时间从 0h48 减少到 0h30 (-38%)。

所以在这两种情况下,我们天真的想法似乎带来了另一个显著的训练时间减少。

统一尺寸配料是否会以任何方式影响精确度?

通常,神经网络是在随机排序的数据点上训练的。均匀尺寸配料限制了这种随机性,因此引入了一种偏差,这种偏差在理论上可能会影响精度。

我们将只比较有和没有统一尺寸配料的设置:

  • 对于一批 16 个样品,当激活统一长度配料时,准确度从 81.4%增加到 81.6%;
  • 对于一批 64 个样品,当激活统一尺寸配料时,准确度从 81.0%提高到 81.7%。

在这两种情况下,都有改进,我们可以得出结论,对准确性没有负面影响。

然而,我们结合多种选择进行了许多实验,根据权重&偏差仪表盘,使用统一尺寸配料与准确性呈负相关。手动检查实验对后(有/没有选项),这种效果不明显。

(如果您想深入了解,请随时查看报告)

混合精度

通过 Nvidia apex 库,Pytorch 上的混合精度是可能的。简而言之,在其最常见的模式中,混合精度包括以半精度执行大多数操作,并以全精度累加结果(更多信息请参见 apex 文档)。

Apex 以在某些场景中带来改进而闻名,有时它也会带来一些不稳定性(例如,训练期间的损失幅度比没有混合精度时更大),并且很少避免模型收敛。换句话说,它不是一个银弹,而是一个有趣的工具来测试你的情况。

好消息是 Trainer class 实现了开箱即用,要利用它,您只需要在命令行中添加正确的标志(“— fp16”)。

关于 16 个长序列的小批量的训练时间,情况是不寻常的。用于:

  • 单独的混合精度通过将训练时间从 4h38 减少到 2h50 而使事情变得更好;
  • 混合精度结合动态填充和统一大小批处理,使得训练变慢,从 0h52 到 1h01!

原因可能是在第二种情况下,它增加了开销,并且没有多大帮助,因为大多数批处理只由短序列组成。混合精度对大型矩阵运算帮助最大。

当应用于 64 个短序列的小批量时,事情如预期的那样:

  • 单独使用时,时间训练从 0h56 减少到 0h26
  • 结合其他两个选项,时间从 0 时 30 分减少到 0 时 17 分

这一次,即使步长由短序列组成,每个包含 64 个序列,使得矩阵足够大,可以从混合精度中受益。

关于准确性,没有明确的模式。你可以通过查看权重&偏差 报告来自己拿主意。

重复的结果

所有的实验都是使用相同的种子进行的。可能我们很幸运,我们的方法达到了精度,但不是用这个种子和这个数据集。

我们在启用所有优化设置的情况下用不同的种子重新运行 16 分钟的训练 5 次,并且再现了准确度/计时。

对大模型也进行了同样的复制实验。结果是一样的。

还是那句话,想要互动图?-> 报告<—

一个结论?

我们已经证明,这两种技术都能在不降低准确性的情况下持续大幅缩短时间。此外,我们了解到,在具有小批量的数据集上,应该小心混合精度,因为如果没有足够的计算来执行,它可能会导致意外的训练变慢。

我们确信这两种技术都是唾手可得的,应该被变形金刚用户广泛使用。

总结一个更一般的想法,我们对这些简单的想法所获得的结果感到惊喜。只是为了这个故事,在另一个不相关的实验中,我们注意到 X-NLI 的法语训练集(它是英语数据集的机器翻译)质量很低(许多例子在法语中是绝对无意义的),我们想知道翻译质量更好的它是否会提高测试集(这是人工翻译)的准确性。这对我们来说是一个重要的机会,因为如果它成功了,就意味着有大量的法语数据集可以使用。我们在 DeepL 上花了几块钱,翻译好了很多……而且准确度也没变(我们甚至觉得我们的度量有 bug)。并非所有简单的想法都是平等的!

将数据科学视为预言

原文:https://towardsdatascience.com/divination-and-data-science-1ddda913b812?source=collection_archive---------49-----------------------

为什么占卜实际上与数据科学密切相关

Shreyas shah 在 Unsplash 上拍摄的照片

我从小就对文化和神话感兴趣。这项研究中让我感兴趣的一部分是占卜的艺术。出于这个原因,在我被监禁的这段时间里,我决定参加哈佛大学提供的关于占卜和他们的系统的在线课程。

据我所知,每种文化都有自己的方式来预测未来会发生什么,以及如何通过占卜师收集的数据来预测。没错,占卜其实和数据息息相关,和数据科学本身。在这篇文章中,我将尝试以简洁直观的方式解释占卜如何与数据科学相关联。

这篇文章只是纯粹的自以为是和知识的目的。没有任何其他方式贬低任何长期存在的做法已经存在。

占卜预测系统

根据维基百科的说法,占卜试图通过神秘的、标准化的过程或仪式来洞察一个问题或情况。简单来说,这意味着通过仪式过程了解未来。

根据他们的输入,占卜本身可以分为四个更广泛的类别,包括:

  • 随机;其中输入来自随机过程,有时也被称为“自发的”例如,罗马鸟预兆我们需要等待鸟儿自发行动后再做预测。
  • 随机化;其中输入是人类发起的以产生随机结果。比如掷骰子。
  • 人类;直接来自占卜者的输入或者除了占卜者之外任何人都不能解释的输入;例如,着魔的人。
  • 非随机;其中输入来自对任何一致的、可重复的、可预测的和可知的过程的观察;例如,测量行星的位置。

在某种程度上,上面使用的术语在数据科学领域非常熟悉。有一个输入,取决于我们有什么输入;产生预测的仪式类型也会不同。

这个仪式可以被想象成一个预测系统或者是一个用于预测的“算法”。在这个“算法”中,我们可以说,如果我们把“A”输入到“算法”,预测“B”;例如,如果木星在 273.87 度的位置,预测未来的浪漫生活。就像数据科学中的任何预测模型一样;“算法”没那么简单。

占卜系统中有意思的是,系统本身有时候有点不为外界所知;因为其中涉及到神秘主义,或者系统本身过于复杂,甚至因为它在历史上消失了。在数据科学预测模型中,就把这个想象成一个深度学习模型。它涉及到很多统计方法,系统真的很复杂,而且经过训练,由于它们的随机性,我们不知道里面发生了什么。我们只要有深度学习模型,我们输入观察到的数据,预测就出来了。太棒了。

以上只是一个有点勉强的例子,说明占卜类似于预测模型。如果我们一步一步地使用系统预测的例子,会感觉更自然。

占卜框架

1.观察

任何预言或数据科学领域的第一步总是观察数据。这是我们输入到“算法”中产生预测的输入。就像我们把数据输入机器学习模型一样。理论上,我们的观察越多,预测就越强。

2。预测

观察数据后,我们有了预测。预测的清晰程度因系统而异。虽然很少有预测是严格的,比如行星的位置;许多预测系统包含歧义,有意或无意的,不同的解释。

在数据科学中,以无监督学习中的聚类分析为例;我们将最终得到系统预测它们应该聚集在一起的聚类结果。作为数据科学家,我们如何解释这个结果将取决于我们。

3.评估准确性并做出更改

这个预测正确吗?就像在任何数据科学过程中一样,这是一个简单的问题,但实际上评估它是一个复杂得多的问题。看起来很奇怪,占卜需要评估他们的预测准确性,但这正在发生,尽管并不总是如此。事实上,许多占卜系统本身可以进化得更准确,但大多数时候我们不知道,因为历史已经湮没在时间中。

以古代美索不达米亚时期的春辣为例。haruhiscid 是一种仪式,他们牺牲一只羊并提取肝脏来获得预言。由于预测不可能产生 100%肯定或否定的结果,占卜者可能会进行另一种仪式来减少不确定性。在这种情况下,占卜者就像现代科学家一样,通过进行额外的实验来获取数据。

基于该方法的准确性,可以对系统进行一些改变。就像我们数据科学家玩超参数一样,系统中的一些变化肯定会发生。虽然,正如我之前提到的;大多数时候我们不知道预测系统中发生了什么变化,因为它在时间中丢失了。

数据科学中占卜的重要性

我已经在上面的文章中向你解释了占卜和数据科学在某些方面是相似的。事实上,很多占卜都是以数据为基础的。塔罗牌、占星术、罗马鸟占卜术等。它们都是基于发生的数据和模式。

占卜看似不科学,但却是人类的需要。占卜告诉我们未来会发生什么,或者解释过去和现在发生了什么。今天发生的事情与昨天不同,明天也会不同,因此,占卜师在帮助你理解社区的性质,社会的性质,正在发生的事件,当然,如何处理这些事件方面的重要性。

在现代,我们当然会消除由这种不科学的方法产生的任何意见。基于骰子或羊肝的预测会被认为是假的,不会被认真对待。出于这个原因,我们人类需要更多的理由来相信我们的预测是有根据的。在这里,我们发展了许多科学理由,只是为了确保将要发生的事情在某种意义上是可预测的。数据科学家只是碰巧做到这一点的角色之一。我们尝试分析并创建一个基于数据的模型,根据输入数据预测会发生什么。就像占卜一样。

不同于我们为个人日常生活而咨询的占卜师,数据科学家的需求来自于人类很久以前就想知道未来会发生什么的好奇心。因此,我们作为数据科学家所做的就离占卜者不远了。

结论

这篇文章纯粹是我的观点,以及我作为一名数据科学家如何看待占卜。作为人类,我们想知道模式和未来会发生什么的好奇心从未改变;只是我们获取知识的方式不同。

如果您喜欢我的内容,并希望获得更多关于数据或数据科学家日常生活的深入知识,请考虑在此订阅我的简讯。

如果您没有订阅为中等会员,请考虑通过我的介绍订阅。

深入探究 AWS Lambda

原文:https://towardsdatascience.com/diving-deeper-into-aws-lambda-a52b22866767?source=collection_archive---------13-----------------------

了解 Lambda 的基本基础设施,如何运行可执行文件和解析 CloudWatch 日志

杰里米·毕晓普在 Unsplash 上的照片

1.介绍

Amazon Lambda 已经成为云基础设施开发中必不可少的无服务器服务。to focus(功能即服务)背后的理念是让程序员专注于编码,提高他们的生产力和基于微服务的现代系统的灵活性。

在另一篇文章中,我介绍了 AWS Lambda 的基本概念,如何用 Python 设置你的第一个函数,使用外部库(Lambda 层)以及使用 boto3 调用函数。

在本文中,我们将探索其他特性,并扩展 AWS Lambda 函数的可用性。

  1. AWS Lambda 背后的基础设施
  2. 在 AWS Lambda 中运行可执行文件
  3. cloud watch 中的监控日志

本文中的大部分信息摘自 AWS 白皮书,以及参考资料会议中引用的文章。

2.AWS Lambda 背后的基础设施

根据 AWS:

Lambda 是一种基于功能的大规模、免配置的无服务器计算产品。它为您的应用程序提供云逻辑层。Lambda 函数可以由 AWS 或支持第三方服务上发生的各种事件触发。

这可能不是最好的定义,因为它过于依赖模糊、抽象的概念,并且没有定义 Lambda 中的元素。即使你以前运行过一些 AWS Lambda 函数,并且熟悉这个接口,当你调用你的函数时,后面发生的事情对你来说仍然是一个谜。

那么,AWS Lambda 的背后到底是什么?

从基础设施的角度来看,每个 AWS Lambda 都是运行 Amazon Linux 的容器的一部分(称为功能容器)。您为 AWS Lambda 创建的代码文件和资产被称为功能代码包,存储在由 AWS 管理的 S3 存储桶中。每当 Lambda 函数被触发时,函数代码包就从 S3 桶下载到函数容器,并安装在它的 Lambda 运行时环境中。这个过程很容易扩展,AWS 基础设施可以毫不费力地执行对特定 Lambda 函数的多次调用。

Lambda 函数代码包的大小限制是:压缩后 50MB,解压缩后 250MB,这可能与每次调用函数时需要从 S3 加载到函数容器有关。如果功能代码包大于当前限制,加载过程的效率可能会受到影响。

冷启动与暖容器

当您调用 Lambda 函数时,它会遵循上述步骤并执行代码。执行完成后,Lambda 容器在被终止前会保持可用几分钟。这叫做 冷启动

如果您调用同一个函数,并且 Lambda 容器仍然可用(还没有被终止),AWS 使用这个容器来执行您的新调用。这个使用活动函数容器的过程被称为热容器,它提高了 Lambda 的响应速度(图 2)。

图二。AWS Lambda 冷启动与热启动(“带 AWS Lambda 的无服务器架构”)

无状态与有状态

AWS Lambda 的另一个重要方面是它是一个无状态服务,这意味着“你的代码不能对状态做出假设”。即使使用暖容器,新调用也不能使用上一次调用生成的状态和数据,这意味着每次新调用都要重新启动容器。Lambda 处理这些限制的一种方法是使用来自有状态服务的数据,如 S3、DynamoDB、RDS 等。因此,在设计云基础设施时,请记住这一点。

3.运行可执行文件

AWS 提供了各种各样的运行时(语言),如 Ruby、Python、Node.js、Java、Go 和。您还可以运行您在部署包中上传的任何代码或包。这意味着您可以使用运行上述语言的 Lambda 函数,但有时您需要使用通过命令行运行的工具来执行您的函数,比如 bash 文件。由于 AWS Lambda 背后的基础设施是不透明的,实现它可能很棘手,因为您不能简单地调用存储在 Lambda 部署包中的可执行文件。

在 AWS Lambda 上运行可执行文件的关键是:

  1. 将其打包到您的部署包中;
  2. 将可执行文件复制到 Lambda/tmp/folder;
  3. 给它适当的权限;
  4. 最后,执行它。

为了说明这个过程,我们将使用一个名为maft Aligner的 bash 命令行工具,这是一个用于执行序列比对的生物信息工具。我们可以下载便携包来在我们机器的任何文件夹中运行它。我们将运行以下命令来执行它:

# 1) Unzip the package
tar -xvf mafft-7.450-linux.tar# 2) Execute the .bat file inside the extracted folder
./mafft-linux64/mafft.bat

要使用 AWS Lambda 复制此流程,我们将使用以下代码:

前两个函数,load_file_from_S3upload_file_to_S3,用于获取存储在 S3 上的文件,并将 Mafft 工具的输出保存回 S3,以便我们以后访问它(记住,Lambda 是无状态的,因此当函数终止时,所有内容都将从环境中删除)。一个重要的细节是,您只能使用 Lambda 的临时文件夹来保存文件,因此第 10 行和第 18 行明确了/tmp/的路径。

lambda_handler函数(第 22 行)包含了使用 Mafft 工具加载、执行和保存文件的所有必要命令。第 25 到 28 行指定了变量(输入、输出文件和 S3 桶的名称)。Mafft 的可执行文件从功能代码包复制到/tmp/文件夹(第 40 和 41 行),并授权使用chmod执行(第 43 和 44 行)。最后,使用os.system()(第 47 行)通过命令行执行该工具,并将输出文件上传到 S3 存储桶(第 50 行)。

通过使用这里介绍的相同逻辑,您可以通过打包用不同语言编码的可执行文件来扩展 Lambda 函数的功能。

4.通过 CloudWatch 监控日志

每次运行 AWS Lambda 时,函数日志都会存储在 CloudWatch 上,这样更容易监控执行情况。对于每个函数容器,CloudWatch 都会生成一个日志流。所以当你在跟踪冷启动调用时,你会有一个一个调用一个日志关系。当你有暖容器时,问题就开始了,活动容器的日志流会有关于多个 Lambda 调用的信息,这使得解析信息有点棘手。

Boto3 是一个不可思议的库,它使用 Python 集成了 AWS 上托管的管道和系统,使得一些流程更容易执行。为了处理暖容器日志的问题,我使用 boto3 开发了一个代码来处理来自 CloudWatch 的日志数据。要运行它,您需要提供与每个 Lambda 调用相关的 3 个重要变量的信息:

A)日志组:是 CloudWatch 为你的 Lambda 函数生成的名字。惯例是aws/lambda/your_lambda_name

B)流名称:这是为每个执行的 Lambda 函数容器创建的日志的名称。如果使用暖容器执行 Lambda 函数,则当前和过去执行的日志将具有相同的流名称,它们将属于同一个日志。

C)请求 ID: 这是为每个 Lambda 执行生成的唯一标识符。

您可以通过访问lamdbda_handler函数中的 Lambda 上下文对象来找到这些值。

# Accessing values from Lambda Context object
def lambda_handler(event, context):
    RequestId = context.aws_request_id
    GroupName = context.log_group_name
    StreamName = context.log_stream_name

接下来,您可以使用这些值运行函数parsing_lambda_logs,只提取与特定 RequestId 相关的日志行。

上面的代码还返回了lambda_status,它会告诉您在 Lambda 调用的执行过程中,对于特定的 RequestId 是否有错误。因此,您可以将 Lambda 调用与该函数集成,以监控您的系统是否正常工作。

5.结束语

正如简介中提到的,本文的目的是提供更多关于 AWS Lambda 如何工作以及如何执行一些重要过程的细节,以便在设计云基础设施时为您提供更多资源。我希望 AWS Lambda 现在不再是一个“黑箱”,你可以探索它更多的可能性和功能。

非常感谢你阅读我的文章!

  • 你可以在我的个人资料页面 找到我的其他文章🔬
  • 如果你喜欢并且想成为中级会员,你可以使用我的 推荐链接 来支持我👍

资源

[## 无服务器:比 FaaS 多得多

也就是数据、逻辑和基础设施——它们是如何结合在一起的?

medium.com](https://medium.com/@PaulDJohnston/serverless-its-much-much-more-than-faas-a342541b982e) [## 使用 Python3 介绍 Amazon Lambda、Layers 和 boto3

面向数据科学家的无服务器方法

towardsdatascience.com](/introduction-to-amazon-lambda-layers-and-boto3-using-python3-39bd390add17) [## AWS 白皮书和指南

通过 AWS 和 AWS 社区创作的 AWS 技术内容扩展您的云知识,包括…

aws.amazon.com](https://aws.amazon.com/whitepapers/?nc1=h_ls&whitepapers-main.sort-by=item.additionalFields.sortDate&whitepapers-main.sort-order=desc) [## 恐怕你对 AWS Lambda 冷启动的想法完全错了

当我在 API Gateway 的上下文中与人们讨论 AWS Lambda 冷启动时,我经常得到这样的回答…

medium.com](https://medium.com/hackernoon/im-afraid-you-re-thinking-about-aws-lambda-cold-starts-all-wrong-7d907f278a4f) [## 语言、内存、封装尺寸如何影响 AWS Lambda 的冷启动?

比较使用不同语言、内存分配和部署规模的 AWS Lambda 的冷启动时间…

read.acloud.guru](https://read.acloud.guru/does-coding-language-memory-or-package-size-affect-cold-starts-of-aws-lambda-a15e26d12c76) [## 以下是如何让您的云基础架构稳定、安全且可扩展。

启动 DevOps 很难

medium.com](https://medium.com/free-code-camp/heres-how-to-make-your-cloud-infrastructure-stable-secure-and-scalable-f9f4749697d6) [## 了解微服务:从想法到起跑线

在过去的两个月里,我花了大部分的空闲时间来学习什么是…

medium.com](https://medium.com/free-code-camp/microservices-from-idea-to-starting-line-ae5317a6ff02)

深入线性回归

原文:https://towardsdatascience.com/diving-deeper-into-linear-regression-81adaa7b79e5?source=collection_archive---------18-----------------------

OLS,山脊,拉索和超越…

Artem VerboUnsplash 上拍摄的照片

W 当我提到“线性回归”时,大多数人都会想到传统的普通最小二乘法 (OLS)回归。如果你不熟悉这个术语,这些等式可能会有帮助…

β_1、β_2:权重;β_0:偏倚;J(β):成本函数

你也想到 OLS 了吗?如果是,那么你就在正确的轨道上。但是线性回归不仅仅是 OLS!首先,让我们更仔细地看看 OLS。

内源性阿片样物质

这种技术的名字来自于成本函数。在这里,我们取误差平方和(基础事实和预测之间的差异),并试图将其最小化。通过最小化成本函数,我们获得了向量β(包含偏差和权重)的最优值。在下图中,显示了成本函数的等高线(同心椭圆)。最小化后,我们得到β作为中心点。

内源性阿片样物质

起初,OLS 似乎足以解决任何回归问题。但是随着我们增加特征的数量和数据的复杂性,OLS 倾向于过度拟合训练数据。过度拟合的概念非常广泛,值得单独写一篇文章(你可以找到很多这样的例子),所以我会给你一个简要的介绍。过度拟合意味着模型已经很好地学习了训练数据,以至于无法进行概括。换句话说,该模型已经学习了训练数据中即使是小规模(不显著)的变化,因此它不能对看不见的(验证和测试)数据产生良好的预测。为了解决过度拟合的问题,我们可以使用许多技术。在我们的成本函数中增加一个正则化项就是这样一种技术。但是我们应该用什么术语呢?我们一般使用以下两种方法之一。

山脉

在这种情况下,我们将权重的平方之和添加到我们的最小平方成本函数中。所以现在看起来像这样…

m:β的 1+维;λ:正则化参数

但是这个术语如何防止过度拟合呢?增加这一项相当于对β的可能值增加了一个额外的约束。因为要达到最小成本,β _j 的总和一定不能超过某个值(比如 r)。这种技术可以防止模型对某些特征赋予比其他特征更大的权重,从而解决过度拟合问题。数学上,

换句话说,β应该位于以原点为圆心,半径为√r 的圆内(或上)。这是可视化效果…

山脉

请注意,由于存在约束(红色圆圈),β的最终值比在 OLS 中更接近原点。

套索

山脊和套索之间的唯一区别是正则化项。这里,我们将权重的绝对值的和添加到我们的最小二乘成本函数中。所以成本函数变成了…

在这种情况下,约束可以写成…

现在我们可以把约束想象成正方形而不是圆形。

套索

值得注意的是,如果轮廓碰到正方形的一个角,则一个特征被完全忽略(权重变为 0)。对于高维特征空间,我们可以用这个技巧减少特征的数量

注意:在正则化项中,我们不使用偏差(β_0 ),因为只有对应于特征的非常大的权重(β_i,对于 i>0)对过拟合有贡献。偏差项只是一个截距,因此与过拟合没有太大关系。

唷…这是很多关于正规化。上述方法的共同点是:它们的成本函数中都有残差/误差(地面真实预测)。这些误差平行于 y 轴。我们也可以考虑沿 x 轴的误差,并以类似方式进行。见下图。

y 误差和 x 误差

如果我们使用一种不同的误差呢?

主轴(正交)回归

在这种情况下,我们考虑两个方向 (x 轴和 y 轴)的误差。观察数据点和预测线之间的垂直距离的平方和将被最小化。让我们通过仅取一个特征来形象化这一点。

(X_i,Y_i):从最佳拟合线上的(x_i,y_i)绘制的垂线的脚

让我们的模型

然后通过最小化可以获得回归系数

在约束下

简化长轴回归

这与上面的方法非常相似,只是略有变化。这里,我们最小化由(X_i,Y_i)和(x_i,y_i)形成的矩形的面积之和。

缩减长轴

由 n 个数据点扩展的总面积是,

这里的约束条件与正交回归相同。

什么时候应该使用正交回归?

当研究变量(y)和解释变量(x)都存在不确定性时,应该选择正交和简化的主轴回归。

正交回归中有趣的一点是,它会产生对称的 y 误差和 x 误差。但是在 OLS,我们没有得到对称,因为我们要么最小化 y 误差,要么最小化 x 误差,而不是两者都最小化。

还好奇?看一个我最近做的视频…

我希望你喜欢阅读。下次见…学习愉快!

深入探究 Unity-ML 代理

原文:https://towardsdatascience.com/diving-deeper-into-unity-ml-agents-e1667f869dc3?source=collection_archive---------11-----------------------

Unity-ML 代理课程

训练一个好奇的特工去摧毁金字塔。

我们推出了新的免费、更新、 深度强化学习课程从初学者到专家,用拥抱面对🤗

👉新版教程:https://huggingface.co/deep-rl-course/unit0/introduction

下面的章节是以前的版本,新版本在这里👉https://huggingface.co/deep-rl-course/unit5/introduction?fw=pt

unity ML-代理

我们推出了新的免费、更新、 从初学者到专家的深度强化学习课程,用拥抱面对🤗

👉新版教程:https://huggingface.co/deep-rl-course/unit0/introduction

下面的章节是以前的版本,新版本在这里👉https://huggingface.co/deep-rl-course/unit5/introduction?fw=pt

上一次,我们学习了 Unity ML-Agents 的工作原理,并训练了一个学会FQ的代理。

这是一个很好的体验,但是我们想创造能够解决更复杂任务的代理。所以今天我们将训练一个更聪明的机器人,它需要按下一个按钮来生成一个金字塔,然后导航到金字塔,撞倒它,并移动到顶部的金砖。

为了训练这个寻找按钮然后摧毁金字塔新代理人,我们将使用两种奖励的组合,一种是环境给予的外在奖励。也是一种内在的好奇心。这一秒会促使我们的代理好奇,或者换句话说,更好地探索它的环境。

因此,今天我们将了解深度强化学习中好奇心这一强大想法背后的理论,并且我们将训练这一好奇代理。

我们开始吧!

深度 RL 中的好奇心是什么?

我已经在另外两篇文章和*中详细介绍了好奇心,如果你想深入研究数学和实现细节的话。***

现代逆向物流中的两大问题

为了理解什么是好奇心,我们需要首先理解 RL 的两个主要问题:

首先是稀疏奖励问题:也就是说,大多数奖励不包含信息,因此被设置为零。

请记住,RL 是基于奖励假设,即每个目标都可以被描述为奖励的最大化。因此,奖励是对 RL 代理的反馈,如果他们没有收到任何反馈,他们对哪种行为是适当的(或不适当的)的知识不会改变。****

由于奖励,我们的代理人知道在那个状态下的这个行动是好的

例如,在 Vizdoom“末日之家”中,你的代理只有在找到防弹衣时才会得到奖励。但是,马甲离你的起点很远,所以你的大部分奖励会是零。因此,如果我们的代理没有收到有用的反馈(密集的奖励),它将需要更长的时间来学习一个最优策略,并且可能会花时间转来转去而找不到目标。

非常感谢 Felix Steger 的这个插图

第二个大问题是外在的奖励函数是手工的,也就是说,在每个环境中,一个人必须实现一个奖励函数。但是我们如何在大而复杂的环境中扩展它呢?

那么什么是好奇心呢?

因此,解决这些问题的一个方法是开发一个奖励函数,该函数是代理固有的,即由代理自身生成。代理将充当自学者,因为它既是学生,也是自己的反馈主人。

这种内在的奖励机制被称为好奇心,因为这种奖励推动探索新奇/不熟悉的状态。为了实现这一目标,我们的代理人在探索新的轨迹时将获得高额报酬。

这种奖励实际上是根据人类的行为设计的,我们天生就有探索环境和发现新事物的本能欲望。

有不同的方法来计算这种内在奖励,Unity ML-Agents 通过下一状态预测方法使用好奇心。****

基于预测的惊奇(或下一状态预测)引发的好奇心

我已经介绍过这个方法 这里 如果你想深入到数学的细节。

所以我们刚才说,在不熟悉/新奇的状态下,好奇心很高。但是我们如何计算这种“不熟悉”?****

给定当前状态和采取的行动,我们可以将好奇心计算为我们的代理预测下一个状态的误差。更正式地说,我们可以将此定义为:

为什么?因为好奇心的想法是鼓励我们的代理人执行减少代理人预测其自身行为后果能力的不确定性的行为(在代理人花费较少时间的区域,或者在具有复杂动态的区域,不确定性会更高)。

如果代理在这些状态上花费很多时间,那么预测下一个状态(低好奇心)将是好的,另一方面,如果是一个未探索的新状态,那么预测下一个状态(高好奇心)将是不好的。

让我们进一步分解一下。假设你玩超级马里奥兄弟:

  • 如果你在游戏开始的时候花了很多时间(这并不新鲜),那么代理就能准确预测到下一个状态会是什么,那么奖励就会低。****
  • 另一方面,如果你发现一个新房间,我们的代理将非常不擅长预测下一个状态,所以代理将被推动去探索这个房间。****

使用好奇号将促使我们的智能体倾向于预测误差较高的转换(在智能体花费时间较少的区域或具有复杂动态的区域,预测误差较高)从而更好地探索我们的环境。****

但是因为我们不能通过预测下一帧来预测下一个状态(太复杂了,无法直接预测像素),我们使用了更好的特征表示,它将只保留可以由我们的代理控制或影响我们的代理的元素。****

而为了计算好奇心,我们将使用文中介绍的一个模块,好奇心驱动的自我监督预测探索** 称为内在好奇心模块。**

如果你想知道它的工作原理, 查看我们的详细文章

训练一名特工摧毁金字塔

现在,我们通过下一个状态预测了解了什么是好奇心,以及它是如何工作的,让我们来训练这个新的代理。

我们在 github 上发布了我们训练过的模型,你可以在这里下载

金字塔环境

这个环境的目标是训练我们的代理人获得金字塔顶端的金砖。为了做到这一点,他需要按一个按钮来产生一个金字塔,然后导航到金字塔,撞倒它,并移动到顶部的金砖。****

奖励制度是:

在观察方面,我们使用的是 raycast 版本。有 148 个光线投射,但是探测开关、砖块、金砖和墙壁。

我们还使用一个布尔变量来表示开关状态。

动作空间是离散的,有 4 个可能的动作:

我们的目标是达到平均回报为 1.75 的基准。

让我们摧毁一些金字塔!

首先我们打开 UnitySDK 项目。

在示例中搜索金字塔并打开场景。

就像 WallJump,你可以在场景中看到很多代理,他们每个人都来自同一个预置,他们都共享同一个大脑(策略)。****

同一个代理预置的多个副本。

事实上,正如我们在经典的深度强化学习中所做的那样,当我们启动一个游戏的多个实例(例如 128 个并行环境)时,我们在此复制并粘贴代理,以便有更多不同的状态。

所以,首先,因为我们想从头开始训练我们的代理,我们需要从代理预置中移除大脑。我们需要去预置文件夹并打开预置。****

现在在预设层级中,选择代理并进入检查器。

行为参数中,我们需要移除模型。如果你有一些图形处理器,你可以改变推理设备从 CPU 到 GPU。

对于第一次训练,我们将只修改总训练步骤,因为它太高了,我们只能在 500k 的训练步骤中达到基准。为此,我们转到config/trainer _ config . YAML,您将这些修改为 max_steps 到 5.0e5 以用于金字塔情况:

为了训练这个代理,我们将使用 PPO(近似策略优化),如果你不知道或者你需要更新你的知识,查看我的文章

我们看到,为了训练这个代理,我们需要使用 Python API 调用我们的外部通信器。这个外部通讯器会要求学院启动代理。****

所以,你需要打开你的终端,到 ml-agents-master 所在的地方,输入这个。

ml agents-learn config/trainer _ config . YAML-run-id = " Pyramids _ first train "-train

它会要求你运行 Unity 场景,

按下编辑器顶部的▶️按钮。

您可以使用以下命令启动 Tensorboard 来监控您的训练:

tensor board—logdir = summaries

看着你的特工FQ

你可以在训练的时候通过查看游戏窗口来观察你的经纪人。

培训结束后,您需要将保存在 ml-agents-master/models 中的模型文件移动到 unity SDK/Assets/ML-Agents/Examples/Pyramids/TF models。

再次打开 Unity 编辑器,选择金字塔场景。

选择金字塔预设对象并打开它。

选择代理

代理行为参数中,拖动金字塔. nn 文件到模型占位符。

然后,按下编辑器顶部的▶️按钮。

是时候做些实验了

我们刚刚训练我们的特工学会了FQ。既然我们有了好的结果,我们可以尝试一些实验。

记住,最好的学习方法是通过实验保持活跃。所以你应该试着做一些假设并验证它们。

顺便说一下,有一个关于如何通过沉浸式限制超参数调谐金字塔环境的惊人视频,你一定要看。

将时间跨度增加到 256

文档中所解释的,时间范围是每个代理在将经验放入经验缓冲区之前要收集的经验步数。这在长期范围(偏差较小,但方差估计较高)和短期范围(偏差较大,但方差估计较小)之间进行权衡。****

在这次体验中,我们将时间跨度从 128 增加到了 256** 。增加它允许我们的代理**在他的行动序列中捕获比以前更多的重要行为。****

然而,这并没有影响我们新代理的培训。事实上,他们分享了完全相同的结果。

我们在 github 上发布了我们训练过的模型,你可以在这里下载

今天就到这里吧!

你刚刚训练了一个比上次更聪明的特工。你也学到了深度强化学习中的好奇心。太棒了。

现在我们已经完成了,你可能想更深入地了解 Unity ML-Agents** 。不要担心,下一次我们将创建我们自己的环境,下一篇文章我们将创建我们自己的强化学习实现。**

因此在下一篇文章中,我们将从头开始创建我们的第一个环境。这个环境会是怎样的?我不想破坏现在的一切,但我给你一个提示:

向培根先生问好🐽

下次见!

如果你有任何想法,评论,问题,欢迎在下面评论或者发邮件给我:hello@simoninithomas.com,或者发推特给我 @ThomasSimonini

不断学习,保持牛逼!

第三章:玛雅奇遇

深入自动化 API 测试

原文:https://towardsdatascience.com/diving-into-automated-api-tests-e08510c72e7?source=collection_archive---------21-----------------------

来源

想到自动化,你会想到什么?机器人制造机器人,大型工厂生产产品,车辆自动驾驶,或者其他一些遥远的未来?嗯,自动化已经远远不止这些了。

自动化意味着我们让某种机器在最少的人工协助下完成工作。大多数人可能想到的最常见的自动化的例子是工厂。其中多台机器以同步方式工作,大量传送带环绕整个设施。

来源

这个形象让很多人相信,很快所有的工厂都会这样运行。然而,自动化远比这种狭隘的视野更广阔。最重要的自动化每天都在我们的生活中发生,而我们甚至没有太注意它。

什么是 API,我们为什么需要它?

让我们举一个社交媒体应用程序的例子。你可能在社交媒体帖子里看到过地名。当我们点击它们时,它们会把我们带到一张地图上,给我们指出一个特定的地方。现在,这家社交媒体公司可能有了自己的地图服务。然而,更多的时候,它会使用第三方地图服务。

该第三方地图服务可能正在向许多其他应用程序提供服务。如果它允许所有应用程序使用它的原始数据库,至少会导致数据损坏。最坏的情况是数据完全毁坏(伴随着一些严重的诉讼)。因此,允许任何应用程序不受任何限制地使用其数据库是一种非常糟糕和不切实际的方法。

该服务提供商也不可能向所有应用程序发送数据副本,因为每个应用程序可能只需要非常小的数据子集。此外,数据必须尽快更新。此外,定期发送如此多的数据需要天文带宽的问题。

我们如何克服这些问题,以便地图服务可以将数据发送到社交媒体应用程序?

怎么样-地图服务提供了一组功能、工具和规则,用于通过适当的文档与社交媒体应用程序进行通信。使用这些服务,社交媒体应用程序可以获得所需的数据,而不会危及地图服务提供商的安全(当然,必须需要一些认证系统)。

这个由功能、工具和规则组成的包被称为 API 或应用程序编程接口 ,因为它提供了与应用程序交互的接口。

来源

我喜欢把 API 想象成一台复杂机器上的一组按钮(比如一台带键盘的电脑:)。你可能希望这台机器为你做一些事情。你要做的就是用那些按钮来指挥它做你的工作。

例如,让机器包含一个地图数据库。现在,我们可以要求机器向我们提供关于某个特定坐标的信息,它会把这些信息悄悄告诉我们,而不需要我们关心它的内部工作情况或它有什么其他数据。这是与机器通信的相对安全的方式。

除了提供上述优势和服务之外,API 通常是跨平台的,可以与许多编程语言一起使用。这一点很重要,因为不止一个应用程序可能从一个服务请求数据。

例如,让我们再次考虑我们的地图服务。假设另一个应用程序想要使用它的数据。然而,这个应用程序使用的语言和平台与我们的社交媒体应用程序不同。它如何使用来自地图服务提供商的数据?

这里又出现了一个 API。API 应该能够与多种语言和平台一起工作,因此它应该有助于克服这个问题。

为什么要测试 API?

API 在我们的日常活动中非常有用。如果您正在使用任何类型的计算机设备,您可能会在不知不觉中使用 API。大多数应用程序和软件通过 API 进行通信。甚至软件的不同部分也通过 API 进行通信。

现在,如果您如此频繁地使用 API,一个自然的问题可能会出现。我如何知道哪个 API 最适合我?或者我如何确保我正在使用的 API 是安全的,并且按照预期工作。或者也许因为大多数软件都在使用 API,它们甚至是安全的吗?他们重视我的隐私吗?是否有任何漏洞可以被利用来访问我的数据。

API 为我们提供了一条通向公司数据库的通道。如您所料,如果 API 构建不当,它们可能会被用来窃取或破坏数据。因此,彻底测试我们的 API 是极其重要的。

然而,安全性并不是测试我们的 API 的唯一问题。我们还需要确保所有提供的功能都按预期工作。并且 API 可以处理它所声明的任意多的用户。我们还应该测试它在被拉伸到极限时是否有效。我们可能还需要考虑一些极限情况。

提供 API 的公司可能会使用人类来完成这项任务。但是可用的 API 的数量和人类执行这些测试所需的技能的数量会使它变得不可行。再加上应用程序与人工测试人员相比节省的时间,你会同意应用程序测试 API 比人工测试更好。

有许多可用的 API 测试工具。其中最受欢迎的是 JMeter,邮递员,放心休息。我发现了一个很棒的视频,它用例子解释了 REST API。我推荐你观看这个视频来获得更多关于 API 的信息。观看这个视频会让你感受到应用程序是如何被用来测试 API 的。我也强烈推荐这篇博文

到目前为止,我们已经确定了自动化的有用性。以及它在节约成本和效率方面的优越性。我们应该更深入地了解测试,以理解如何实现自动化,以及为什么自动化是一个合理的选择。

为什么我们应该自动化测试?

浏览网页时,您可能会看到类似“404 网站未找到”或“200 OK”的消息。这些消息对于理解我们的请求状态非常重要。例如,假设我们向服务器发送一个网页请求。它可能没有该网页,并可能返回特定代码。或者它可能拥有该网站或文件,并可能返回特定代码。

我们可以检查代码并获得关于我们请求的信息。因为我们知道发送什么请求和我们期望什么。我们可以将这些信息放在一个自动化工具中,让它在所有服务器上查询网站或文件。这将使测试自动化并帮助我们。

让我们考虑另一个例子。假设我们有大量代码的软件。如果你以前写过代码,你可能会遇到这个问题——你改变了一些小的东西,它破坏了整个代码。因此,如果开发人员改变代码中的一些小东西,就有可能破坏整个软件。

为了确保这种情况不会发生,我们进行了一系列测试。我们将这些上传到测试工具,每当我们对软件进行任何更改时,我们都会运行这些测试。如果发现一些问题,我们会在将代码提交到软件的源代码之前重新检查代码。这有可能为我们节省几个小时的头痛时间。

有许多 API 测试工具可供使用。我一直在用的是 Loadmill ,一个我觉得很有帮助的免费工具。让我们仔细看看这个工具,了解它在做什么,以及它如何帮助您的测试过程。

首先,我们必须选择我们想要运行的测试(或者进行我们自己的测试)。然后我们进行测试。最终结果可能是这样的:

来源:loadmill 的自动测试

你可以看到它运行了许多测试,这些测试可以被输入其中。这些测试可以在多个网站上运行。在这里,它运行 9 个测试,并报告所用的时间以及测试是失败还是通过。这里有 8 个测试已经通过,只有一个测试失败。

单击这些测试中的任何一个都会显示关于测试的更多信息,包括为什么测试通过或失败,或者哪里出错了。

最后的话

我希望我已经让你相信自动化确实非常有帮助,我们正在日常生活中使用它,甚至没有注意到它的效率或称赞它在使我们的生活更简单和更有成效方面的作用。

自动化并不是什么新鲜事。人类一直在寻找聪明的方法来自动化事情,以便他们可以专注于其他工作。我会考虑一个轮子作为自动化来解决走太多的问题。或者中世纪时用风车碾小麦。或者使用引擎来驱动汽车。

然而,最近越来越多的人认为自动化的兴起是一件消极的事情,会使人类变得无用。这种想法与事实相去甚远。自动化从一开始就已经存在,并且应该一直存在到后来。我们总能让事情变得更有效率。

对自动化持否定态度的人没有意识到,当发动机出现时,它们也取代了许多工作,但随着时间的推移,新的工作也创造出来了,总的来说,人类只是在进步。

即使现代技术已经走了很远,它仍然有很长的路要走,在每一步,都有许多人维护所有的工具,并努力使它们越来越好。此外,即使我们开发了一个程序来自动完成一项工作,我们也需要一些开发人员来维护这个程序。

我认为随着我们获取越来越多的数据,技术变得越来越好,将会有越来越多的任务被自动化。更明智的方法是找到可以自动化的任务,并尝试自动化它们,然后继续下一个任务。一旦一切完成,让我们集中精力去火星!

用深度 Q 学习潜入深度强化学习

原文:https://towardsdatascience.com/diving-into-deep-reinforcement-learning-with-deep-q-learning-376e588bb803?source=collection_archive---------55-----------------------

从表格方法到函数逼近的过渡

在本文中,我们将了解如何开始深度强化学习。经常可以看到,列表 RL 方法比函数逼近方法更容易理解。很多人发现很难从 q 学习过渡到深度 q 学习。所以在这篇文章中,我们将仔细研究深度 Q 学习背后的思想。

什么是深度强化学习

我们先来了解一下什么是深度强化学习。深度强化学习由基于函数逼近的算法组成,即有一些函数逼近动作的 Q 值。这里,我们不是维护一个 Q 值表,而是使用像神经网络这样的函数来预测特定状态下的动作的 Q 值。

为什么要深度强化学习

你可能想知道,当我们可以简单地使用 Q 值的表格时,为什么我们需要神经网络。答案很简单,将每个 Q 值存储在一个表中会增加内存的使用,这对于具有大的状态空间和动作空间的环境是不可行的,我们的代理可能不会遇到一个状态甚至两次。使用神经网络可以让我们学习网络的权重,而不是学习单个的 Q 值。

表格法与函数逼近。图片由 阿灵古塔https://www.geeksforgeeks.org/deep-q-learning/

深度 Q 学习理念

让我们了解一下 Q 学习的概念和优化技巧。

  1. 重放记忆:当我们的智能体在环境中行动并探索世界时,我们不会在旅途中训练神经网络。相反,我们将代理的所有经验存储在一个缓冲空间中,并通过随机抽取一批经验或优先化的经验来训练我们的模型。这背后的想法是,神经网络可以找到创建的 b/w 状态,并且可能倾向于过度拟合,并且在新状态下可能无法正常工作。
  2. 目标网络:深度 Q 学习使用的不是一个而是两个神经网络。这不是强制性的,但是具有两个网络可以优化性能,并且还可以解决移动目标的问题。当智能体使用相同的神经网络估计 Q 值和目标值时,出现移动目标。因此,为了避免移动目标,我们使用另一个具有固定参数的神经网络(目标网络),并且目标网络定期更新。
  3. 堆叠的帧:如果你使用一个图像作为一个状态,那么一堆多个图像可以用来代表一个状态。这种堆叠的图像给代理一种运动感,从而有助于采取正确的行动。
  4. 行动选择:许多行动选择技术可以用于探索,如 epsilon greedy,softmax。在这些技术中,Softmax 动作选择要好得多,因为它会在一段时间后自动停止探索。在本文中,我们将使用 greedy。

算法:

深度 Q 学习的步骤如下:

  1. 代理观察环境的当前状态。如果随机数小于或等于ε,则代理将采取随机行动,否则 DQN 预测 Q 值,并采取最大 Q 值的行动。
  2. 下一个状态、奖励和一个终端变量存储在重放内存中。
  3. 经过一段时间,当记忆中有足够的例子时,代理通过抽样一批经验来训练 DQN。当前状态集被视为特征,标签是目标值,计算为[Traget = set _ of _ reward+gamma * numpy . max(target _ net . predict(set of next _ state))* set _ of _ done]。这里的 done 是这里使用的终端变量,因此终端状态的值为零。
  4. 目标网络定期更新主网络。

图片 via dnddnjs.gitbooks.io

你可以在这里找到代码。我将在下一篇文章中解释代码

感谢你阅读我的文章,希望你喜欢并且能够理解我想要解释的东西。希望你阅读我即将发表的文章。哈里奥姆…🙏

参考资料:

[## 强化学习

由阿尔伯塔大学提供。强化学习专业化包括 4 门课程探索权力…

www.coursera.org](https://www.coursera.org/specializations/reinforcement-learning)

https://MIT press . MIT . edu/books/reinforcement-learning-second-edition

深入 Azure Cosmos DB 中的 Gremlin 查询

原文:https://towardsdatascience.com/diving-into-gremlin-queries-in-azure-cosmos-db-83eb1d7c6350?source=collection_archive---------15-----------------------

使用 Azure Cosmos DB Gremlin API 执行图形查询的简单介绍

感谢 Azure Cosmos DB 提供的 Gremlin API,我们可以构建全球分布式数据库来存储和操作图形数据。我们可以使用 Gremlin 查询语言在 Cosmos DB 中执行图形查询。

在本文中,我将向您展示如何设置一个支持 Gremlin API 的 Cosmos DB 帐户,以及如何在该帐户中执行一些简单的图形查询。

小妖精吉祥物挺可爱的 IMO:)

如果你想跟进,你需要设置 Azure 订阅。看看这个链接如何做到这一点。要不,我们开始吧!

设置我们的图形数据库

让我们从创建图形数据库开始。进入你的 Azure 门户,点击“创建新资源”。搜索 Azure Cosmos DB 并点击“‘创建新的”。

为了创建我们的图形数据库,我们需要提供一些基本信息:

  • 订阅 —本质上是将向您的 Cosmos DB 帐户收费的订阅。
  • 资源组 —资源组对于管理 Azure 资源集合非常有用。在本教程结束时,我们可以删除一个组中的所有资源,而不是逐个删除资源。您可以将您的 Cosmos DB 帐户分配给预先存在的资源组,或者为其创建一个新的资源组。我决定在本教程中创建一个新的。
  • API —我们可以创建不同种类的 Cosmos DB 数据存储库,具体取决于我们分配给它的 API。在本例中,我们正在创建一个图形数据库,因此我们将选择 Gremlin (Graph) API。
  • 笔记本 —爱 Jupyter 笔记本?Cosmos DB 支持这一点!这超出了本教程的范围,所以我不打算启用它。
  • 位置 — Cosmos DB 是 Azure 中的一项基础服务,这意味着它在所有 Azure 地区都可用。选择一个离你最近的地区。在我的例子中,我位于新西兰的奥克兰,所以我将我的数据库部署在澳大利亚东部。
  • 账户类型 —这是新的?!我以前从未经历过这种事。悬停在工具提示上,看起来它与 UI 体验的工作方式有关。我将不得不对此做更多的调查,但现在,我只是把它投入生产。你可以改变它,它不会影响引擎。
  • 地理冗余和多区域写入 —我现在已经禁用了这个。

请看下面的截图示例:

点击审查+创建,你将被重定向到一个验证页面。我们暂时不需要担心网络或标签。如果您的配置有效,您将看到一条成功消息,您可以单击“ Create ”来提供您的图形数据库!

在本教程中,我们将使用 Cosmos DB 为所有新的 graph 帐户提供的示例图。为此,点击快速启动。您将被要求选择一个平台来创建一个示例应用程序,但是我们在这里所做的只是用一些示例数据创建一个容器。

Cosmos DB 团队有一个我们可以使用的 Persons 容器,所以单击 Create ' Persons '容器就足够了。

点击“数据浏览器”导航至您的图表。

这是新的吗?他们改变了数据浏览器的工作方式!看起来我们必须将当前的 IP 地址添加到防火墙规则中才能看到我们的数据?(非常 Azure SQL,如果你之前用过 Azure SQL 的话)。

单击通知,您将被重定向到防火墙设置选项卡。您的 IP 地址应该已预先填入下面的防火墙部分,因此只需单击“保存”即可将您的 IP 地址添加到允许访问列表中。几分钟后你应该可以走了。

一旦这些都完成了,导航回数据浏览器,我们应该在我们的 Cosmos DB 帐户中看到我们的新人员图表。

好了,这就是我们需要做的所有管理设置,让我们开始深入一些令人敬畏的图形查询!

在我们的图形数据库中执行查询

让我们从在 Persons 图表中插入几个项目开始。打开您的数据浏览器,您应该会看到一个允许我们执行 Gremlin 查询的文本框。

让我们从在图表中添加 5 个人开始。键入以下 Gremlin 查询:

# Add Will Velida
g.addV('person').property('firstName', 'Will').property('lastName', 'Velida').property('age', 28).property('hairColor', 'blonde').property('userId', 1).property('pk', 'pk')g.addV('person').property('firstName', 'Alex').property('lastName', 'Smith').property('age', 22).property('hairColor', 'brown').property('userId', 2).property('pk', 'pk')g.addV('person').property('firstName', 'Mike').property('lastName', 'Jones').property('hairColor', 'black').property('userId', 2).property('pk', 'pk')g.addV('person').property('firstName', 'Sarah').property('lastName', 'Smith').property('hairColor', 'blonde').property('userId', 4).property('pk', 'pk')g.addV('person').property('firstName', 'Debbie').property('lastName', 'Stevens').property('hairColor', 'black').property('age', 57).property('userId', 5).property('pk', 'pk')

我们在这里做的是添加 5 个人的顶点。在我们的图中,顶点是离散的实体。在这个例子中,我们使用人作为顶点,但是他们也可以是地点或事件。

让我们更深入地了解一下每个 Gremlin 命令在做什么:

  • addV() —这为我们的图形添加了一个顶点(我们的离散实体)。
  • 属性() —这为我们的垂直市场增加了一个属性

现在让我们添加一些顶点之间的关系。这些在图形数据库中被称为边。让我们编写以下查询:

g.V().hasLabel('person').has('firstName', 'Will').addE('knows').to(g.V().hasLabel('person').has('firstName', 'Alex'))g.V().hasLabel('person').has('firstName', 'Alex').addE('knows').to(g.V().hasLabel('person').has('firstName', 'Mike'))

让我们检查一下新的 Gremlin 查询:

  • add()—两个顶点之间有一条边(关系)。
  • has()hasLabel() —用于过滤属性、顶点和边。在本例中,我们根据'first name'属性进行筛选。

我们可以用这样的命令更新我们的顶点:

g.V().hasLabel('person').has('firstName', 'Mike').property('userId', 3)

让我们尝试过滤我们的人员集合。让我们检索年龄小于 30 的所有顶点:

g.V().hasLabel('person').has('age', lt(30))

在这里,我们得到两个结果:

我们可以进一步细化这个查询,只返回名称属性:

g.V().hasLabel('person').has('age', lt(30)).values('firstName')

这将返回以下结果:

最后,让我们在图中执行一个简单的遍历,找出 Alex 认识的所有人:

g.V().hasLabel('person').has('firstName', 'Alex').outE('knows').inV().hasLabel('person')

我们可以看到,他认识一个人,迈克·琼斯:

[
  {
    "id": "c2260feb-207b-403b-82cb-30cd47006912",
    "label": "person",
    "type": "vertex"
    "properties": 
    "firstName": [
    {
      "id": "1a383c60-82ce-414b-a610-8c2b9ab08759",
      "value": "Mike"
    }
    ]
    "lastName": [
    {
      "id": "1a1a762a-dace-4912-aa16-e87f4327d1a7",
      "value": "Jones"
    }
    ],
    "hairColor": [
    {
      "id": "5f71ca42-1cf0-4da4-93a9-c24693975e56",
      "value": "black"
    }
    ],
    "userId": [
    {
      "id": "9e06cf3a-e58a-494f-aaf2-e0f1e2940cce",
      "value": 3
    }
    ],
    "pk": [
    {
      "id": "4f27fd02-1861-480a-8887-4b40c8c7d6c6",
      "value": "pk"
    }
    ]
    }
  }
]

评估图形查询的性能

这是非常酷的东西,但是我已经听到你们中的一些人说‘我如何测量这些查询的性能?’。

Gremlin 查询语言有一个名为 executionProfile() 的方法,它允许我们查看查询执行情况的指标。让我们将这个方法附加到前面的查询中,看看它是如何执行的:

g.V().hasLabel('person').has('firstName', 'Alex').outE('knows').inV().hasLabel('person').executionProfile()

这是我们得到的回应:

让我们来看看以下属性:

  • gremlin —被执行的语句。
  • 总时间 —操作所用的时间,以毫秒为单位
  • 指标 —在我们的 gremlin 查询中执行的每个步骤的指标。这些分为 GetVertices (获取我们的属性) GetEdges (查看我们的顶点之间的关系是什么)和get neighborhood vertices(查看哪些顶点有什么关系)。这些指标包括执行查询所用的时间(以毫秒计)、总查询执行时间的百分比时间、返回的结果数量、计数和大小。

结论

在本文中,我们建立了一个新的 Cosmos DB 图形数据库,并在其中执行了一些基本查询。我们还研究了如何评估这些查询的性能。如果您已经完成了这个图形数据库,请随意删除它。否则,为什么不添加更多的属性,看看 Cosmos DB Graph 文档,看看还能用 Gremlin 查询做些什么。

在以后的文章中,我将讨论如何为图形数据库建模数据,并查看一些最佳实践。我还将讨论我们如何在为 Azure Cosmos DB 开发应用程序的上下文中应用这些概念。

如果你有任何问题,请在下面的评论区提问。

下次见!

NumPy 入门

原文:https://towardsdatascience.com/diving-into-numpy-b92abfd7a00f?source=collection_archive---------22-----------------------

照片由 Myriam JessierUnsplash 上拍摄

NumPy 是数据科学和机器学习中使用的最重要和最基本的库之一,它包含多维数组的功能,高级数学函数,

  • 线性代数运算
  • 傅里叶变换
  • 随机生成器

NumPy 数组也构成了 scikit-learn 的基本数据结构。Numpy 的核心是经过很好优化的 C 代码,所以在使用 NumPy 的同时在 Python 中提高了执行速度。

用 Python 实现科学计算的基础包— NumPy

本文包括 NumPy 中的基本操作和最常用的操作。这篇文章对初学者来说是友好的,对中级和高级读者来说也是复习的。

让我们从导入 NumPy 开始,

import numpy as np

as关键字使np成为 NumPy 的别名,所以我们可以用 np 代替 NumPy。这是一种常见的做法,可以节省时间,使工作更容易。

NumPy 数组

为了创建一个 NumPy 数组,我们可以使用np.array函数来创建它,并使用dtype作为一个可选参数,将数组更改为所需的类型。下面是数组数据类型的列表。当数组中的元素属于不同的数组数据类型时,那么这些元素将被提升到最高级别的类型。这意味着如果一个数组输入混合了intfloat元素,那么所有的整数都将被转换成它们的浮点等价物。如果一个数组混合了intfloatstring元素,那么所有的东西都会被转换成字符串

为了将数组转换成所需的类型,我们可以使用astype函数。该函数的必需参数是数组的新类型,要知道数组的类型,我们可以使用.dtype

要复制一个数组,我们可以使用固有的copy函数并执行它。NaN(非数字)值也可以通过使用np.nan来使用。 nan 将作为一个占位符,不接受整数值。如果在包含 nan 时使用整数类型,将导致错误

数字基础

NumPy 提供了一个使用[np.arange](https://docs.scipy.org/doc/numpy/reference/generated/numpy.arange.html)创建范围数据数组的选项。该函数的行为非常类似于 Python 中的range函数,并返回一个 1D 数组。如果一个数字n作为参数被传递,那么它将返回从 0n-1的数字。如果两个数字作为参数mn传递,它将返回从mn-1的数字。如果使用三个参数mns,它将使用步长s返回从mn-1的数字。

shape函数用于知道数组的形状。使用reshape函数时,它将输入数组和新形状作为参数。例如,如果数组中元素的数量是 10,那么新形状应该是(5,2)或(2,5),因为它们形成乘法结果。我们被允许在新形状的至多一个维度中使用特殊值-1 。带有-1 的维度将采用允许新形状包含数组的所有元素所必需的值。

函数flatten将任意大小的数组整形为 1D 数组。

数学运算

借助 NumPy 数组,我们可以对数组中的每个元素应用算术、逻辑和其他运算。这有助于通过少量操作修改大量数值数据。NumPy 数组对数组中的每个元素执行基本的算术运算。除了基本的算术函数,NumPy 还可以执行其他的三角函数、双曲线函数、指数函数、对数函数、以及更多的函数。这些功能已经在这里列出

函数[np.exp](https://docs.scipy.org/doc/numpy/reference/generated/numpy.exp.html)对数组执行以 e 为底的指数运算,而[np.log10](https://docs.scipy.org/doc/numpy/reference/generated/numpy.log10.html)使用以 10 为底的运算对输入数组执行对数运算。为了对任何基地进行常规的能量操作,我们使用[np.power](https://docs.scipy.org/doc/numpy/reference/generated/numpy.power.html)。该函数的第一个参数是基数和幂,第二个参数是幂和幂。为了在两个数组之间执行矩阵乘法,我们使用了函数np.matmulnp.matmul中两个输入矩阵的维数必须服从矩阵乘法的原理。第一个矩阵的第二个维度必须等于第二个矩阵的第一个维度,否则np.matmul将导致ValueError。**

随机发生器

NumPy 有一个名为np.random的模块,用于伪随机数生成,它执行从 1D 数组到多维数组的随机操作。[np.random.randint](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.random.randint.html#numpy.random.randint)函数将生成随机整数。该函数将接受一个必需的参数(high)。整数将在从low (含)到 high (不含)的范围内生成。size参数将返回指定大小的数组。[np.random.seed](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.random.seed.html#numpy.random.seed)函数用于设置随机种子,允许我们控制伪随机函数的输出。这个函数接受一个代表随机种子的参数。使用np.random.seed函数时,随机函数生成的输出在每次后续运行中都是相同的。在多维数组沿着第一轴洗牌时,我们使用[np.random.shuffle](https://numpy.org/doc/stable/reference/random/generated/numpy.random.shuffle.html)来洗牌。

np.random可以从不同的概率分布中抽取样本。最常用的分布是np.random.uniformnp.random.normal,其他的是这里是np.random.uniform函数为所提供的范围抽取随机样本,并根据提到的大小返回数组。np.random.normal按照正态分布抽取样本,其中locscale分别代表均值和标准差。这两个函数都没有必需的参数。

访问数组元素

访问 NumPy 中的数组元素类似于访问 Python 列表中的元素。对于一个多维数组,它将与访问 Python 的列表列表中的元素相同。借助: (冒号)操作符,切片可以像 Python 一样在 NumPy 中完成。会再次切分整个数组。在中,多维数组,(comma)用于在每个维度上分隔切片。负步进和切片将在向后方向上运行。

为了找出数组中最小和最大元素的索引,我们可以使用np.argminnp.argmax。注意使用此函数 时,将返回元素的索引 。这两个函数的必需参数是输入数组。在上面的代码中,第56行,该函数将返回每列中最小的行元素的索引。在行57中,该函数将返回每行中最大列元素的索引。

数据提炼

由于有大量的数据,我们只需要过滤出分析所需的数据。这可以借助基本的关系操作来完成,如 ==、>、<、!等。,NumPy 在数组上按元素执行这些操作。~操作代表一个布尔否定,即它翻转数组中的每个真值。

np.isnan函数用于确定数组的哪个位置包含nan值,如果有nan值,则返回,否则返回假。[np.where](https://numpy.org/doc/stable/reference/generated/numpy.where.html)函数采用一个必需的第一个参数,即输入布尔数组。该函数将返回满足条件的元素的位置,即布尔值输出为真。当它与唯一的第一个参数一起使用时,它返回一个一维数组的元组。与此同时,它将返回数组的数据类型。np.where功能必须与 1 或 3 个参数一起使用。在 3 个参数的情况下,第一个参数必须是输入布尔数组,第二个参数代表替换值,第三个参数代表替换值。

如果我们想根据数据的行或列进行过滤,我们可以使用[np.any](https://docs.scipy.org/doc/numpy/reference/generated/numpy.any.html)[np.all](https://docs.scipy.org/doc/numpy/reference/generated/numpy.all.html)函数。这两个函数接受相同的参数并返回一个布尔值。这两个函数的必需参数是一个布尔数组。np.any返回如果数组中至少有一个元素满足所提供的条件,否则返回假。 np.all返回如果数组中的所有元素都满足给定的条件,否则返回np.anynp.all分别相当于逻辑 OR ||和逻辑 AND &&运算符。

聚集技术

聚合将涉及一些技术,如求和、串联、累积求和等等。要对单个数组中的元素求和,我们可以使用np.sum函数。我们可以使用axis参数并跨行和列获取。要执行累积加法,我们可以使用np.cumsum功能。不设置axis将返回展平数组中所有值的累积和。设置axis=0返回每的累积和数组,而axis=1返回每的累积和数组。

为了执行相同大小的多个数组的连接,我们可以使用np.concat函数。这里axis默认值将是0,因此垂直发生串联。如果axis=1水平连接发生

统计操作

为了检查数组中的数据,我们可以使用 NumPy 执行一些统计操作。为了获得 NumPy 数组中的最小值和最大值,我们可以分别使用minmax函数。axis关键字参数与主题‘访问数组元素’np.argminnp.argmax中的用法相同。在这里,我们使用axis=0来查找arr1的每一列中的最小值的数组,使用axis=1来查找arr1的每一行中的最大值的数组。

我们还可以分别借助np.meannp.mediannp.varnp.std找到平均值、中值、方差和标准差。我们还可以使用axis关键字参数,并获得数组的行和列的度量。这里是 NumPy 提供的全部统计操作

结束备注!!

从这篇文章中,我们已经介绍了 NumPy 中基本的和常用的操作。从 NumPy 开始将是你的数据科学或机器学习生涯的一个开端。要成为 NumPy 的大师,我建议你阅读整个 NumPy 文档。我希望这篇文章对你有所帮助。

DIY: Apache Spark & Docker

原文:https://towardsdatascience.com/diy-apache-spark-docker-bb4f11c10d24?source=collection_archive---------3-----------------------

全民数据科学

被亲切地称为“黑暗”或“斯波克”

在 Docker 容器内运行的完全分布式 Spark 集群

介绍

在过去几年中流行起来的两种技术是 Apache Spark 和 Docker。

Apache Spark 为用户提供了一种以分布式方式执行 CPU 密集型任务的方法。与 Hadoop 等其他分布式技术相比,它的速度更快,因此在过去几年中,它的采用率一直在稳步上升。2014 年 Spark 赢得了 Gray Sort 基准测试,在该测试中,他们对 100TB 数据的排序速度比以前的 Hadoop 集群快 3 倍,使用的机器数量少 10 倍。

另一方面,Docker 在各种情况下被广泛采用。Docker 为用户提供了定义环境最小规格的能力,这意味着您可以轻松地开发、发布和扩展应用程序。此外,由于使用了 linux 容器,用户可以开发 Docker 容器,这些容器可以在一台服务器上同时运行,同时保持相互隔离。最后,Docker 提供了一个名为 Docker Engine 的抽象层,它保证了可以运行 Docker 的机器之间的兼容性,解决了“它在我的机器上可以工作,我不知道为什么它在你的机器上不能工作”这个老问题。

随着大数据的兴起,这两种技术是天作之合。Apache Spark 提供分析引擎来处理数据,Docker 提供快速、可扩展的部署以及一致的环境。

先决条件

我假定您了解 Docker 命令和术语以及 Apache Spark 概念。因此,如果您对这两种技术都不熟悉,我不推荐您阅读本文。出于简洁的考虑,本文将有意省略正在发生的事情的大部分细节。对于架构的完整描述和更连续的过程,我将读者引向我的 github repo

我还假设您至少有云提供商的基本经验,因此能够在您的首选平台上设置一个计算实例。

本文的其余部分将直接介绍不同层次的架构复杂性:

  1. Docker 容器网络—本地机器
  2. Docker 容器网络—多台机器
  3. Apache Spark 集群—本地机器
  4. Docker & Spark —本地机器
  5. Docker & Spark —多台机器
  6. 奖励:Docker Stack 和 Spark

我们开始吧!

来源

Docker —本地机器

首先,我们需要掌握一些基本的 Docker 网络。首先,我们将对运行在同一台机器上的容器执行此操作。

  1. 要做的第一件事是使用 my repo 中的docker 文件构建 docker 映像,或者更方便的是使用以下命令提取 docker 映像
docker pull sdesilva26/spark_master:0.0.2
docker pull sdesilva26/spark_worker:0.0.2

注意:为了这个部分的目的,任何图像都可以。

2.通过运行以下命令创建桥接网络

docker network create --driver bridge spark-net-bridge 

3.通过运行以下命令,在这个用户定义的桥接网络上运行两个容器

docker run -dit --name spark-master --network spark-net-bridge --entrypoint /bin/bash sdesilva26/spark_master:0.0.2
docker run -dit --name spark-worker1 --network spark-net-bridge --entrypoint /bin/bash sdesilva26/spark_worker:0.0.2 

4.检查网络并找到两个容器的 IP 地址

docker network inspect spark-net-bridge

上面的输出应该如下图所示

5.连接到 spark-master 容器,使用它的 IP 地址和容器名测试它与 spark-worker 容器的通信

ping -c 2 172.24.0.3
ping -c 2 spark-worker

因为容器已经被部署到相同的 Docker 桥网络中,所以它们能够使用容器的名称来解析其他容器的 IP 地址。这叫做自动服务发现,以后会给我们很大帮助。

体系结构

使用上述命令,我们创建了以下架构。

Docker —多台机器

  1. 继续在您最喜欢的云提供商上设置 2 个实例
  2. 在配置实例的网络时,确保将它们部署到同一个子网中
  3. 打开以下端口,以便容器相互通信,并用于覆盖网络流量(入站和出站);
Protocol  |  Port(s)  | Source
TCP       |  2377     | <your-security-group-or-subnet-name>
TCP       |  7946     | <your-security-group-or-subnet-name>
UDP       |  7946     | <your-security-group-or-subnet-name>
UDP       |  4789     | <your-security-group-or-subnet-name>

4.打开以下端口,以便实例与 docker hub 进行通信(入站和出站);

Protocol  |  Port(s)  | Source
HTTPS     |  443      | 0.0.0/0, ::/0

例如,在 AWS 上,我的两个实例部署在其中的安全组具有以下安全组设置

其中"SG-0140 fc 8 be 109 D6 ECF(docker-spark-tutorial)"是安全组本身的名称,因此只有来自网络内部的流量可以使用端口 2377、7946 和 4789 进行通信。

5.安装 docker。

sudo yum install docker -y
sudo service docker start
sudo usermod -a -G docker ec2-user # This avoids you having to use sudo everytime you use a docker command (log out and then in to your instance for this to take affect)

6.在实例 1 中,提取您选择的 docker 图像。

docker pull sdesilva26/spark_master:0.0.2

7.将另一个图像拖到实例 2 上。

docker pull sdesilva26/spark_worker:0.0.2

8.初始化 docker 群,并通过运行以下命令使实例 1 成为群管理器

docker swarm init

在实例 1 上。

9.从实例 1 复制上面命令的输出,并在实例 2 上运行它,以作为工作者节点加入群

10.在实例 1(群管理器)上创建一个覆盖网络

docker network create -d overlay --attachable spark-net

11.在实例 1 上,运行一个容器

docker run -it --name spark-master --network spark-net --entrypoint /bin/bash sdesilva26/spark_master:0.0.2

12.在实例 2 中,在 swarm manager 创建的覆盖网络中运行一个容器

docker run -it --name spark-worker --network spark-net --entrypoint /bin/bash sdesilva26/spark_worker:0.0.2

13.从实例 2 上的容器内部,通过 ping 实例 1 上运行的容器来检查容器通信

ping -c 2 spark-master

14.同样,检查从实例 1 中的容器到实例 2 中的容器的向后连接

ping -c 2 spark-worker

像以前一样,容器能够仅使用容器名称来解析彼此的 IP 地址,因为它们在相同的覆盖网络内。

体系结构

按照上面的说明,您已经创建了一个类似于下面的架构。

Apache Spark —本地机器

现在我们已经掌握了如何让两个不同的 docker 主机进行通信,我们将开始在本地机器上创建一个 Spark 集群。

  1. 从他们的网站安装 Spark
  2. 从命令行导航到 Spark 安装的 bin 目录
  3. 设置 Spark 主节点
./spark-class org.apache.spark.deploy.master.Master

4.通过导航到 http://localhost:8080,检查您主节点是否已经成功部署。您应该会看到以下内容

5.将一个工作节点连接到群集

./spark-class org.apache.spark.deploy.worker.Worker -c 1 -m 3G spark://localhost:7077

其中这两个标志定义了您希望该工作线程拥有的内核和内存数量。最后一个输入是前缀为“spark://”的主节点的地址和端口,因为我们使用的是 spark 的独立集群管理器

6.通过返回到 http://localhost:8080,检查 worker 是否已成功注册到主节点。现在,您应该看到 worker 节点是集群的一个资源。(您也可以通过访问 http://localhost:8081 来检查 worker 的 UI)

7.通过从 spark 安装的 bin 目录中打开一个 scala shell 来测试集群

./spark-shell --master spark://localhost:7077

和跑步

val NUM_SAMPLES=10000
var count = sc.parallelize(1 to NUM_SAMPLES).filter { _ =>
  val x = math.random
  val y = math.random
  x*x + y*y < 1
}.count() * 4/(NUM_SAMPLES.toFloat)

这将返回圆周率的估计值。

8.通过转到 http://localhost:4040 检查应用程序的 UI。您应该会看到类似于

你现在有一个功能齐全的火花簇!

火花&对接机—本地机

现在是时候将两者结合起来了。我们现在将通过在本地机器上的 Docker 容器中设置一个运行的 Spark 集群来学习先走后跑

  1. 创建一个用户定义的桥接网络(如果您还没有这样做)
docker create network -d bridge spark-net

2.在桥接网络内部创建一个 Spark 主节点

docker run -it --name spark-master --network spark-net -p 8080:8080 sdesilva26/spark_master:0.0.2 bash

3.通过导航到 http://localhost:8080 ,检查容器是否成功启动了 Spark 主节点。我已经将 sdesilva26/spark_master:0.0.2 映像设置为默认设置一个主节点。参见文档

4.在桥接网络内部创建一个 Spark 工作节点

docker run -dit --name spark-worker1 --network spark-net -p 8081:8081 -e MEMORY=2G -e CORES=1
  sdesilva26/spark_worker:0.0.2 bash

缺省情况下,sdesilva26/spark_worker:0.0.2 映像在运行时将尝试加入一个 spark 集群,其主节点位于 spark://spark-master:7077。

如果您更改运行 Spark 主节点的容器的名称(步骤 2),那么您需要将这个容器名称传递给上面的命令,例如-e <master_container_name>。见 dockerfile 此处。</master_container_name>

5.同样,通过导航到 http://localhost:8080http://localhost:8081,验证工作者已经成功注册到主节点。

6.将第二个 spark worker 连接到集群

docker run -dit --name spark-worker2 --network spark-net -p 8082:8081 -e MEMORY=2G -e CORES=1
  sdesilva26/spark_worker:0.0.2 bash

我们必须对步骤 4 中的命令进行的唯一更改是,我们必须给容器一个惟一的名称,而且我们必须将容器的端口 8081 映射到本地机器的端口 8082,因为 spark-worker1 容器已经在使用您的本地机器的端口 8081。

7.旋转 Spark 提交节点

docker run -it --name spark-submit --network spark-net -p 4040:4040 sdesilva26/spark_submit bash

您现在应该在 spark-submit 容器中了。

8.打开一个 scala shell 并连接到 Spark 集群

$SPARK_HOME/bin/spark-shell --conf spark.executor.memory=2G --conf spark.executor.cores=1 --master spark://spark-master:7077

和前面一样,如果运行 spark 主节点的容器的名称不同于 spark-master,那么可以用-master Spark://<your_master_container>:7077 来更改上面的命令。</your_master_container>

上面的命令还要求在您的集群上,您希望每个执行器包含 2G 内存和 1 个内核。Spark 主节点将分配这些执行器,前提是每个 worker 上有足够的资源来允许这样做。有关执行者和工作者的解释,请参见下面的文章

9.在交互式 scala shell 中运行一个示例作业

val myRange = spark.range(10000).toDF("number")val divisBy2 = myRange.where("number % 2 = 0")divisBy2.count()

10.通过导航到 http://localhost:4040 来检查应用程序 UI。您应该会看到以下内容

您刚刚在 Docker 容器中运行了一个 Spark 作业。斯波克出生了!

体系结构

我们在上面所做的是在 Docker 中创建一个网络,我们可以在其中部署容器,它们可以自由地相互通信。

下图中的白色箭头表示容器之间的开放通信。容器上的端口显示为绿色,本地机器上的端口显示为黄色。

您可以看到所有容器都部署在桥接网络中。如果我们现在在这个网络之外部署一个容器,它将不能仅仅通过使用它们的容器名来解析其他容器的 IP 地址。

Docker & Spark —多台机器

现在让我们把所有东西包装在一起,形成一个完全分布式的 Spark 集群,在 Docker 容器中运行。

注意:对于这一部分,你需要使用我已经创建的 3 个图像。

docker pull sdesilva26/spark_master:0.0.2
docker pull sdesilva26/spark_worker:0.0.2
docker pull sdesilva26/spark_submit:0.0.2

您也可以通过下载docker 文件自己构建它们

  1. 在您选择的云提供商上启动一个实例,并使其成为 docker swarm manager
docker swarm init

2.将上述命令的输出复制并粘贴到至少两个其他实例中。我已经在另外 4 个实例上这样做了——3 个将充当 Spark workers,1 个将作为我的 Spark 提交节点

3.在实例 1 上,像我们之前所做的那样创建一个覆盖网络

docker network create -d overlay --attachable spark-net

4.运行 spark_master 映像来创建一个将成为 spark 主节点的容器

docker run -it --name spark-master --network spark-net -p 8080:8080 sdesilva26/spark_master:0.0.2

5.通过将以下内容添加到安全组的入站规则中,打开端口 8080–8090 和 4040

Protocol    |  Port(s)   | Source
Custom TCP  |  8080-8090 | 0.0.0/0
Custom TCP  |    4040    | 0.0.0/0, ::/0

注意:在 AWS 中,安全组是有状态的,所以从实例到用户的返回流量是自动允许的,所以不需要修改安全组的出站规则。在其他云提供商上,您可能需要在出站规则中添加类似的规则。

我的入站安全组规则现在看起来像这样

6.在 http:// <public_ipv4_address_of_instance>:8080 查看 Spark 主节点 UI。您应该会看到与我们之前看到的相同的 UI。</public_ipv4_address_of_instance>

7.现在,在您的另一个实例上运行以下命令,将一个 Spark worker 节点连接到集群

docker run -it --name spark-worker1 --network spark-net -p 8081:8081 -e MEMORY=6G -e CORES=3 sdesilva26/spark_worker:0.0.2

注意:根据一般经验,启动 Spark worker 节点时,内存=实例内存- 1GB,核心=实例核心-1。这就为实例的操作系统留出了 1 个内核和 1GB 的空间来执行后台任务。

8.再次检查主节点的 web UI,以确保 worker 被成功添加。

9.冲洗并重复第 7 步,添加尽可能多的火花工作者。确保将容器的名称从 spark-worker1 增加到 spark-worker2,依此类推。

我已经连接了 3 个 workers,我的主节点的 web UI 如下所示

10.在另一个实例中,启动 Spark 提交节点

docker run -it --name spark-submit --network spark-net -p 4040:4040 sdesilva26/spark_submit:0.0.2 bash

11.启动 pyspark 交互式 shell 并连接到集群

$SPARK_HOME/bin/pyspark --conf spark.executor.memory=5G --conf spark.executor.cores=3 --master spark://spark-master:7077

注意:通过使用— conf 标志,您可以指定在将应用程序连接到集群时希望每个执行器拥有的资源。Spark 调优的主题本身就是一整篇文章,所以我不会在这里赘述。这两篇 Cloudera 博文我发现是理解资源分配的一个很好的资源: 第一部分 & 第二部分 。我还发现 C2FO.io 的 Anthony Shipman 的一篇 博客文章 非常有用,其中还包括一个方便的 excel 表格,可以计算内存、内核和并行化的设置。

12.通过检查 Spark 主节点的 UI 和 Spark 提交节点的 UI,检查提交节点是否成功连接到集群。它们应该看起来像下面的图片。

13.从 pyspark shell 运行一个示例作业

from random import random**def** inside(p):
    x, y = random(), random()
    **return** x*x + y*y < 1NUM_SAMPLES = 100000count = sc.parallelize(range(0, NUM_SAMPLES)).filter(inside).count()print("Pi is roughly {:0.4f}".format(4.0 * count / NUM_SAMPLES))

向 spark 集群提交作业更常见的方法是使用 Spark 安装中包含的 spark-submit 脚本。让我们也这样做。

14.退出 pyspark 并向集群上的执行器提交一个程序

$SPARK_HOME/bin/spark-submit --conf spark.executor.cores=3 --conf spark.executor.memory=5G --master spark://spark-master:7077 $SPARK_HOME/examples/src/main/python/pi.py 20

除了我们调用 spark-submit 脚本,并传递给它一个. py 文件和任何其他配置文件以供执行之外,它的语法与前面的几乎相同。

快乐的日子!我们现在已经创建了一个完全分布式的 Spark 集群,运行在 Docker 容器中,并向集群提交了一个应用程序。

体系结构

我们刚刚创建的架构如下所示

每个 Spark worker 节点和主节点都运行在 Docker 容器中,该容器位于它自己的计算实例上。spark 驱动程序节点(Spark 提交节点)也位于它自己的容器中,运行在一个单独的实例上。所有 Docker 守护进程都通过一个覆盖网络连接,在这种情况下,Spark 主节点是 Docker 群管理器。

在覆盖网络中,容器可以通过引用利用自动服务发现的容器名称来容易地解析彼此的地址。

如果需要,更多的 Spark worker 节点可以在其他实例上启动。

欢迎来到终点线!

Jonathan Chng 在 Unsplash 上拍摄的照片

结论

在本教程中,我们已经成功地逐步完成了在 Docker 容器中设置 Spark 集群的不同复杂程度。

我们首先从一些简单的 Docker 网络原则开始,在我们的本地机器上使用桥接网络,然后在分布式机器上使用覆盖网络和 docker swarm。

接下来,我们设置一个在本地机器上运行的 Spark 集群,以处理向集群添加工人的问题。

然后,我们将 Docker 重新引入到这个组合中,并在我们的本地机器上设置一个在 Docker 容器内部运行的 Spark 集群。

最后,我们将所有东西放在一起,构建了一个运行在 Docker 容器中的完全分布式的 Spark 集群。

希望您已经清楚地了解了这两种技术是如何联系在一起的,并且可以为您的特定问题或项目提供帮助。

现在是时候开始试验了,看看你能从这个架构中学到什么。

尽情享受吧!

[ 来源

奖金

手动创建集群的上述步骤信息量更大,因为它需要大量的手动输入和重复命令。

对于只需要 4 或 5 个计算实例的资源的小问题,这种工作量可能低于您的痛苦阈值。

然而,随着数据变得越来越大,所需的计算能力开始增加,遵循上述步骤将使您成为一名全职的集群创建者。

一种更加实用和优雅的建立集群的方式是利用 Docker compose

Docker 撰写

对于那些刚刚接触 Docker compose 的人来说,它允许你启动所谓的“服务”。

一个服务由一个 Docker 映像组成,但是您可能希望运行这个映像的多个容器。例如,从 docker 映像 sdesilva26/spark_worker:0.0.2 运行多个 Spark worker 容器将构成一个服务。

要启动一组服务,您需要创建一个 docker-compose.yml 文件,该文件指定了您想要运行的各种服务的所有信息。

然而,Docker compose 用于创建在单个主机上运行的服务。它不支持跨主机部署容器。

进入 Docker 栈。

Docker 栈是 Docker compose 思想的简单扩展。现在,您可以在连接成 Docker 群的多台主机上运行服务,而不是在一台主机上运行服务。

最重要的是,如果你有一个 Docker 合成文件,只需要做很少的修改就可以使用 Docker 堆栈命令。

让我们看看如何使用一个组合文件和 docker 堆栈来创建在 Docker 容器中运行的分布式 Spark 集群。

  1. 第一步是标记 Docker 群中的节点。从 Docker 群管理器中列出群中的节点。
docker node ls

您应该会得到类似下图的输出。

2.对于任何你希望成为 Spark 工作者的实例,给它们加上一个标签

docker node update --label-add role=worker x5kmfd8akvvtnsfvmxybcjb8w

3.现在,用 master 角色标记您希望运行 Spark 主节点的实例

docker node update --label-add role=master 

4.创建一个 docker-compose.yml 文件或者拉一个我已经创建的

在这个合成文件中,我定义了两个服务——spark-master 和 spark-worker。

第一个服务将单个容器部署到群中标签为“role=master”的任何节点上。

第二个服务将把 sdesilva26/spark_worker:0.0.2 映像的 3 个容器部署到标签为“role=worker”的节点上。如果群中没有找到 3 个合适的节点,它将尽可能多地部署。

最后,所有这些容器都将被部署到为我们创建的名为 spark-net 的覆盖网络中。

5.将 docker-compose.yml 复制到 swarm manager 实例中。

scp -i <YOUR_KEY>.pem /path/to/docker-compose.yml ec2-user@<PUBLIC_IP_ADDRESS_OF_INSTANCE>:/home/ec2-user/docker-compose.yml

[参见此处了解替代方法]

6.最后,从 swarm manager 运行 docker 栈,并给它一个名字。

docker stack deploy --compose-file docker-compose.yml sparkdemo

注意:你的栈的名字将被加在所有服务名的前面。于是服务“spark-master”就变成了“sparkdemo_spark-master”。您可以使用查看正在运行的服务

docker service ls

7.检查 spark_worker 映像是否在标记为“worker”的实例上运行,以及 spark_master 映像是否在标记为“master”的节点上运行。

恭喜你,我们已经将这篇文章中的所有工作简化为 Docker swarm manager 中的几个命令。

Docker 服务最大的好处是它非常容易扩展。例如,如果后来我们向 Docker swarm 添加了另一个实例,然后我们希望扩展“sparkdemo_spark-worker”服务,我们可以简单地运行

docker service scale sparkdemo_spark-worker=4

现在,您的集群中有 4 个 Spark workers!

码头工人。

照片由J E W E L M I T CH E L L LUnsplash

使用 Dash、Plotly 和 Heroku 的全球冠状病毒感染 DIY 仪表板

原文:https://towardsdatascience.com/diy-dashboard-for-coronavirus-infections-globally-dash-plotly-1ea1f732eb0a?source=collection_archive---------26-----------------------

当前全球冠状病毒感染状况的实时可视化

图片来源: Flickr

无论你是一个企业还是一个小企业,努力对市场产生影响,你需要了解销售,市场趋势,你的收入驱动因素等等。在 excel 电子表格上绘制图表是老一套。一个组织良好的仪表板是当今企业所需要的。唯一的缺点是,市场上所有可以快速实现这一目标的工具都是有成本的。当然,我们中的许多人希望有一种免费的方式来实现这一点,至少在探索性数据分析阶段是这样。我在这里解决了同样的需求,并提供了一种使用 plotly、dash 和 heroku 开发仪表板的免费而快速的方法。这个演示仪表板将反映当前全世界受冠状病毒影响的病例。我在这里使用的数据取自这个链接,并且每天更新,从而为我们提供了关于这种感染传播的最新信息。您可以在https://coronavirus-infections.herokuapp.com/访问这个最新的交互式仪表盘。

以下是创建该仪表板所需的步骤/编码。您可以修改此代码,用您自己的数据集创建任何自定义仪表板,以发现相关的见解。

第一步。为您的项目创建一个新文件夹:

$ mkdir dash_app_example
$ cd dash_app_example

第二步。用 **git** 和一个 **virtualenv**初始化文件夹

$ git init        # initializes an empty git repo
$ virtualenv venv # creates a virtualenv called "venv"
$ source venv/bin/activate # uses the virtualenv

virtualenv创建一个新的 Python 实例。你将需要使用此虚拟重新安装你的应用的依赖项新:

$ pip install dash
$ pip install plotly

您还需要一个新的依赖项gunicorn,用于部署应用程序:

$ pip install gunicorn

第三步。用一个样例 app ( **app.py** )、一个 **.gitignore** 文件、 **requirements.txt** 和一个 **Procfile** 初始化文件夹,进行部署

在项目文件夹中创建以下文件:

**app.py**

**.gitignore**

venv
*.pyc
.DS_Store
.env

**Procfile**

web: gunicorn app:server

(注意app指的是文件名app.pyserver是指那个文件里面的变量server

**requirements.txt**

requirements.txt描述你的 Python 依赖关系。您可以用以下内容自动填充该文件:

$ pip freeze > requirements.txt

第四步。初始化 Heroku,添加文件到 Git,部署

$ heroku create my-dash-app # change my-dash-app to a unique name
$ git add . # add all files to git
$ git commit -m 'Initial app boilerplate'
$ git push heroku master # deploy code to heroku
$ heroku ps:scale web=1  # run the app with a 1 heroku "dyno" ##not required

您应该可以在https://my-dash-app.herokuapp.com查看您的应用程序(将my-dash-app更改为您的应用程序的名称)。

第五步。更新代码并重新部署

当您用自己的代码修改app.py时,您需要将更改添加到 git,并将这些更改推送到 heroku。

$ git status # view the changes
$ git add .  # add all the changes
$ git commit -m 'a description of the changes'
$ git push heroku master 

结论

在等待仪表板软件许可获得批准的时候,我开始使用 dash/heroku。Dashboard 是在团队内部以及与其他利益相关方交流数据科学成果的一个非常巧妙的技巧。dash/heroku 的缺点是,你必须像编写传统的 web 应用程序一样编写每一个程序,很明显,这需要后台设计框架的灵活性。但是,这也增加了对定制要交付的内容的控制。我喜欢它…我还想在未来的时间里在这个网站上添加一些过滤功能,使网站的视图更有组织性和可读性。

冠状病毒在湖北的爆发令人震惊,并从那时起以不可阻挡的方式在全球蔓延。通过这篇文章,我想让大家了解世界上以及你所在地区的感染密度。小心——避开人群,保持双手清洁,不要时不时地摸脸。如果您发现了症状,请确保立即与正确的来源沟通。越早发现,存活的机会就越大。

奖金

以下是开发 dash/heroku 应用程序时您可能会发现有用的一些额外技巧。

  1. 列出与您的 heroku 应用程序关联的 url 的名称
heroku info -s | grep web_url | cut -d= -f2

2.列出您环境中安装的所有应用程序。用免费版 heroku 最多可以创建 5 个。

heroku apps

3.删除/销毁 Heroku 上的应用程序

heroku destroy appname

4.如果堆积/分组条形图中有超过 10 条迹线,如何给出颜色,例如:

colorway = ['#f3cec9', '#D2691E', '#FF6347', '#BC8F8F', '#9ACD32', '#006400','#182844','#8B0000','#FFD700','#00FF00','#808000','#4169E1','#BA55D3','#708090','#D2B48C','#4682B4','#F5DEB3','#FFE4E1','#DB7093','#DA70D6','#B0E0E6','#00FA9A','#FF7F50','#F08080','#BDB76B']layout = go.Layout(colorway=colorway)

包含 10 个以上类别的堆积条形图

DIY 机器学习培训管道

原文:https://towardsdatascience.com/diy-machine-learning-training-pipeline-1d408c972804?source=collection_archive---------39-----------------------

或者如何使用 Git 和 CI 服务器自动化您的机器学习培训管道。

田宽Unsplash 上拍照

机器学习管道不仅仅是一些粘在一起的工具。这是一个过程,通过消除任何平凡的任务或容易出错的步骤,可以使数据科学家和开发人员的生活更加轻松。培训过程的自动化需要许多不同团队成员的协作术语 MLOps 已经被用来描述软件工程、机器学习和系统操作的结合,以便将模型投入生产。管理机器学习管道的平台已经存在。在接下来的段落中,将介绍一个使用 Git 和 CI/CD 作为管道核心的解决方案。

写这篇文章的原因是:

  • 强调构建机器学习管道时遇到的基本部分和最常见的问题。
  • 提出一个培训管道的设计方案,供较小的团队使用,而不需要在部署方面付出很大的努力。

培训渠道

让我们从一个典型的机器学习训练管道的描述开始。这些管道由三个步骤组成:

  • 数据集准备
  • 模特培训
  • 部署模型

每一步都有自己的细节。

数据集准备

数据可能来自不同格式的不同来源。它们需要被合并、清理、转换等。输出是可用于训练模型的数据集。将这些数据集存储在一个便于团队中的每个人或观众检索的地方非常重要。为了重现过去的实验结果或测试解决问题的不同方法,也需要数据集。数据集也需要是不可变的。例如,数据集可以从数据库生成。数据库是其数据的快照,并且随着每次更新而快速变化。即使使用完全相同的参数运行相同的数据集生成脚本,也可能会产生不同的数据集。一种流行的解决方案是将生成的文件存储在一些对象存储中,如亚马逊 S3 或谷歌云存储,通常使用一些对象的键/路径的约定。

一个经常被忽视的方面是,生成数据集的代码是可以从数据集中发现的(反之亦然)。这有助于生成包含更多数据的更新版本的数据集,或者在修复错误后创建修订版本。

此外,了解数据集是如何生成的也很重要。使用了哪些数据库连接,作为输入传递的任何参数等等。

模特培训

一旦数据集准备好,就可以用于实验。训练过程可能产生多个工件(模型、报告、标度、词汇等)。此外,尝试不同的解决方案或迭代最佳性能模型是很常见的。有很多工具可以跟踪单个模型的性能。然而,他们中的大多数缺乏将结果/报告与生成的工件联系起来的方法。

像数据集准备一样,模型训练通常会产生许多不同的代码分支,每个分支用于不同的实验。保持模型源代码、用作输入的数据集和生成的模型/报告之间的联系非常有用。

最后,必须记录运行模型所需的任何参数或配置。如果您在笔记本电脑或 EC2 实例上通过手动键入培训命令来运行培训,您很可能会丢失或忘记这些信息。

部署

我们成功了。我们有一个看起来很有前景的模型,您希望将其部署到生产中。我不打算详细说明,但这可能有许多不同的工作方式,包括

  • 通过 RESTful 端点提供模型,
  • 处理来自流或队列的事件,或者
  • 批处理输入数据。

但是我们需要确保部署了模型的正确版本。一个非常糟糕的做法是手动复制训练好的模型。这可能会引入很多错误。模型可能是错误的版本,或者 REST 端点可能有更新的代码,并且它可能与模型的代码不兼容。

要求

记住以上所有内容,我们可以为构建机器学习培训管道创建一个需求列表。

  • 数据集必须“易于”访问。
  • 数据集必须进行版本化。
  • 必须能够确定用于创建数据集的代码。
  • 数据集生成必须自动化。
  • 必须能够生成数据集的不同版本。
  • 模型训练必须自动化。
  • 模型必须被版本化。
  • 结果必须是可重复的。
  • 必须能够训练不同的模型。
  • 必须能够确定用于训练模型的代码和数据集的版本。
  • 数据科学家/开发人员必须能够选择部署哪种模型。
  • 当部署一个模型时,必须能够回溯整个训练过程。

建立培训流程

这些要求可以分为三组:

  • 所有工件(代码、数据集和模型)都需要版本化。每个工件的版本需要以某种方式与所有相关工件的版本相联系。对于每个数据集,必须能够找到相关的代码,对于每个模型,必须能够找到相关的代码和数据集。
  • 数据集生成和模型训练等耗时的任务需要自动化。
  • 数据科学家/开发人员需要控制部署的内容和时间。

版本化事物

很明显,代码、数据集和模型的版本控制是最重要的部分之一。这是进行可重复实验和追踪用于生成数据集、特征提取等的代码所必需的。有许多版本控制系统(VCS)允许对代码进行版本控制。Git 是最流行的版本控制系统,可以肯定大多数团队都使用 Git(我希望没有人仍然通过电子邮件交换代码)。实际上,这就是我们将在本文中使用的方法。Git 允许团队在项目的不同部分工作,或者并行工作,在分支中测试不同的解决方案。说到这里,这里有几个假设:

  • 所有代码都存在于 Git 上。
  • 用于生成数据集、训练模型和服务模型的代码位于同一个存储库中。这使得从其他步骤引用代码变得更加容易。你当然可以有其他的库(*只要你注意版本控制)或者使用不同的 repo,但是将所有代码放在一个 repo 中会更容易。

像其他 VCS 一样,Git 在版本控制代码方面非常出色。此外,它允许您保留代码的分支,并选择将哪个分支合并到主分支。第一个想法可能是将其他工件(如数据集和模型)也添加到 Git 中。这是可能的,但有几个问题:

  • 向 Git 添加大文件或二进制文件可能会导致存储库的大小增加,当您想要推送更改或克隆存储库时,会导致非常大的下载量。
  • 数据集生成和模型训练是需要时间的过程。稍后我们将分享一些关于如何自动化它们的想法。这些长时间运行的流程需要将工件提交给 Git repo,这可能会导致合并冲突等问题。

我们将使用一些从 Git 到外部系统的“指针”,而不是将文件存储在 Git 中。Git 通过一个惟一的散列来标识不同版本的代码。每当我们运行一些代码并生成一些工件(数据集、模型、报告)时,我们可以使用散列来引用代码的版本。我们也可以做相反的事情。给定一些工件和散列代码,我们可以找到用来生成它们的代码版本。我们需要做的是遵循这些惯例:

  • 数据集必须存储在包含 git 哈希的路径下的对象存储中。
  • 模型必须存储在包含 git 散列的路径下的对象存储中。
  • 每当分发代码时,也就是部署到服务器时,git 散列必须与代码一起存储。但是,如果您使用某种版本控制方案,您也可以从版本中获得 git 散列。

但是等等,有几个问题。用于生成数据集或训练模型的配置参数呢?它们必须存储在 git repo 中。当您实际运行生成工件的代码时,您只需要阅读它们。我们可以将配置文件与代码打包在一起(即在 Docker 映像中)。或者我们可以有一个命令行界面来运行该过程的每一步。配置参数可以作为命令行参数传递,我们可以编写一个包含所有配置的小型 bash 脚本。

如果我们想测试多个版本呢?我们只是创建不同的分支。然后,我们可以合并,以掌握一个有最好的结果。

另一个问题是,如果一切都基于 git 散列,那么如何将数据集与训练代码联系起来?它们需要共享同一个 git hash 吗?一种方法是共享相同的代码。然而,这不允许我们在同一个数据集上有不同的模型。一个改进的解决方案是将数据集的 git 哈希作为训练脚本配置的一部分进行传递。这允许一系列选项:

  • 数据集可能来自任何分支。
  • 多个模型可能会重用同一个数据集(因为它只是一个链接)。

让我们来看一个 bash 脚本示例。它运行假想的train命令,负责训练模型。该脚本将存储在 git 存储库中。因此,对它的任何更改都会被版本化。目的是在运行自动化模型训练任务时用作入口点。

#!/bin/bash# Dataset version to use. Note that this is the commit hash of 
# the dataset generation's code, not the hash of current commit!DATASET_VERSION=da7a1#  Download dataset
aws s3 cp s3://bucket/project/datasets/${DATASET_VERSION}/dataset.csv data/dataset.csvecho "Training model..."# Imaginary command that trains a model using the given parameters
train data/dataset.csv --estimators 50 \
                       --max-depth 10 \
                        --output model.clf
...

让我们来看一个聚焦于流的例子。代码在分支上。创建一个名为数据集的新分支,用于编写数据集生成代码。几次提交之后,代码就准备好了(git hash da7a1 )。一些神奇的过程(我们将在后面讨论)从分支运行代码,并将数据集的两个文件上传到数据集对象存储。现在数据集生成代码和工件已经“连接”起来了。您可以向分支添加更多的提交,并再次执行相同的操作,或者您可以创建新的分支。您甚至可以并行执行此操作。现在,假设我们对结果很满意,并且 da7a1 被合并回分支。

下一步是用几个模型做实验。创建两个不同的分支,每个模型一个。与数据集生成类似,模型训练将导致在使用 git 散列的路径中上传模型对象存储中的工件。这里需要注意的一点是如何将数据集对象存储中的数据集与模型代码连接起来。如前所述,这可以使用 repo 中的配置文件来完成。该文件可能包含指向数据集或分支哈希的链接。

基于 Git 的流程示例

自动化

既然我们已经建立了关于每个工件将“生活”在哪里的约定,那么是时候继续自动化各个部分了。数据集生成和模型训练是需要在项目的某个时间点执行的任务。某些进程必须检测代码中的变化,触发任务的执行,并将结果上传到对象存储。由于代码在 VCS 中,持续集成(CI)服务器是一个完美的匹配。CI 服务器能够监视存储库的变化,并根据特定的触发器运行命令。一个真正常见的用例是在创建拉请求时运行单元测试。这样,在合并代码之前,成功的测试执行被强制执行。

对于我们的例子,我们可以有以下步骤:

  1. 对代码进行测试(确保我们的更改没有破坏任何东西)。
  2. 运行代码的触发任务。
  3. 将结果存储在目标对象存储的正确路径中。

第一步非常明显。步骤#2 取决于它是数据集生成还是模型训练。根据可用的基础架构、数据大小等,有许多不同的方法来执行此操作。以下是一些想法:

  • 在 CI 服务器内部运行—(尽管可能不可行,也不是最佳解决方案)。
  • 提交给一些始终在线的基础设施(服务器、kubernetes 集群、Kubernetes 作业、Spark 作业等)。
  • 提交给允许按需任务的基础设施(如 AWS Sagemaker )。

步骤#3 也可以用不同的方式完成。例如,Spark 可能向 S3 输出一个数据帧。Sagemaker 可以配置为在特定的 S3 路径上传结果。甚至你的代码可以直接上传到 S3 或者使用 AWS CLI 推送。但是,您需要确保 CI 服务器将传递包含 git 散列的路径。

这里有一个例子。您的代码公开了用于生成数据集的命令行接口。您可以编写一个像这样的小型 bash 脚本:

#!/bin/bash # Get current git hash (short version)
VERSION=$(git rev-parse --short HEAD | head -c7)echo "Creating dataset..."# Imaginary command that extracts data from a database and stores 
# them to a CSV
dataset generate --db-url redshift://user:pass@host:5439/warehouse \
                 --from 2019-01-01
                 --to 2019-12-31
                 --output data/dataset.csv#  Upload generated file to S3
aws s3 cp data/dataset.csv s3://bucket/project/datasets/${VERSION}/dataset.csv

运行时,它将生成数据集并将文件上传到 S3。使用这种方法的另一个好处是传递给数据集生成脚本的参数也存储在 git repo 中,因为它们是 bash 脚本的一部分。你绝对可以按照同样的逻辑进行模型训练。

如前所述,CI 服务器将负责触发任务的执行。但是我们希望什么时候触发任务的执行呢?最简单的方法是对主分支打开拉请求。这可能会触发对分支的每个后续提交的任务的执行(即使是对项目自述文件的一个小的提交)。这是耗时的、资源饥渴的,并且可能产生重复的工件。相反,我们可以依靠数据科学家或开发人员的判断来决定何时开始执行任务。当指示一个 CI 服务器需要准备一个新的项目发布时,类似的事情也会发生。常见的做法是创建一个标签。CI 服务器可以在检测到标签时触发任务。更好的是,我们可以根据标签建立一些约定,以便根据标签类型触发不同类型的任务。比如前缀为dataset/的标签可以用来触发数据集生成,而experiment/可以用来触发不同模型或实验的训练。下图正好说明了这一点。

用于触发任务的 Git 标签

开发人员为数据集生成创建了一个分支。一旦标签(dataset/1.1)被添加到带有散列cafe1的提交中,任务就从 CI 服务器中选取,导致数据存储在对象存储中。检查结果后,修复了一个 bug,并推送了一个新的提交。一旦创建了新的标记(dataset/1.2),CI 服务器就会运行新的任务,并将其存储到新的位置。

类似地,对于训练模型,两个分支可以并行存在。标签被应用,CI 服务器触发模型训练任务。注意,在分支model2中,标签是在第二次提交时添加的。第一次提交时没有运行任何任务。只有标签存在时,任务才会运行。

说得更专业一点,这里有一个关于如何使用 Travis 实现流程的例子:

language: python
python:
- '3.6'
git:
  depth: false

jobs:
  include:
  - stage: test
    script: python setup.py test
  - stage: extract dataset
    script: source scripts/generate-dataset.sh
    if: tag =~ /^(dataset\/)/
    before_script:
      - pip install -e .
  - stage: train model
    script: source scripts/train.sh
    if: tag =~ /^(experiment\/)/
    before_script:
    - pip install -e .

Travis 使用阶段对作业进行分组。每个阶段都是构建的一部分,可能被触发,也可能不被触发。在上述示例中,阶段test始终运行。只有前缀为dataset/的标签存在时,Stage extract dataset才会运行scripts/generate-dataset.sh bash 脚本。类似地,只有当标签以experiment/开始时,stage train model才会运行scripts/train.sh

部署

部署是管道的最后一步。正如前面所讨论的,一个模型可以部署在许多不同的环境中(流、REST 端点、批处理等)。重要的部分是拥有与代码的当前状态相匹配的模型版本。开发人员/数据科学家还应该控制什么模型可以投入生产。最简单的解决方案是,如果某个东西需要投入生产,那么它需要被合并到master分支。然后,CI 服务器可以下载模型的最新版本,将其与代码打包在一起,运行测试并部署。

但是 CI 服务器如何知道什么是最新版本呢?这可以通过简单地获取以experiment开始的master分支的最后一个标签来完成。下面是如何用 git 和一些 bash 魔法来实现的:

$ git rev-list --tags | xargs git describe | grep experiment | head -n 1experiment/model2

除了标记名,还可以获得 git 散列:

$ git rev-list --tags | xargs git describe | grep experiment | head -n 1 | xargs git rev-list -n 1 | head -c 7
caca000

最后的想法

在本文中,我描述了一个使用 Git 和 CI 服务器来驱动机器学习管道的流程。在结束之前,我想讨论几个话题。

在开发时,编写小型的自包含提交被认为是一个很好的实践,它可以被快速地审查和合并到主分支。这与机器学习项目设置形成对比,在机器学习项目设置中,不同版本的模型需要经过测试和微调,才能获得最终结果。许多分支(尤其是用于模型不同版本的分支)可能不会被合并到主分支中。这是模型标签的建议约定是experiment而不是model的原因之一。

此外,还有几个可能的问题:

  • 在任务完成之前合并分支。这可能会中断 CI/CD 流程。例如,如果带有实验标记的分支在训练完成之前被合并到主模型中,CI 服务器将无法找到已训练的模型,从而导致构建失败。这实际上是一件好事,因为在没有模型的情况下推送代码是没有意义的:)一个快速的解决方法是删除新合并的标签。
  • 挤压-合并分支(或历史重写)。这导致了单个提交的崩溃,创建了一个新的提交(带有一个新的 git hash ),从而打破了代码和工件之间的链接。
  • 重定基数也可能导致同样的情况。但是重定基础意味着在别人的代码之后添加你的代码。在大多数情况下,再次触发管道可能是一个好主意,只是为了验证模型的预期性能没有改变。

这里描述的分支模型也假设有一个主分支和特征分支。它与 Github 流非常接近,但肯定可以适用于其他分支模型,如Github 流。类似地,如果您的项目需要支持多个版本或者有一些其他需求,您可能需要适应。

还有几件事情没有讨论,但可以明确支持。第一个是循环训练。只是自动化整个过程(从数据集生成到部署),以便使用新数据。第二是如何记录每个实验的结果和超参数。同样,有多种解决方案可供选择,包括神圣mlflowAWS Sagemaker 的培训指标。在任何情况下,git 散列都可以用作日志参数。

Django、Pandas 和 Chart.js 用于快速仪表板

原文:https://towardsdatascience.com/django-pandas-and-chart-js-for-a-quick-dashboard-e261bce38bee?source=collection_archive---------4-----------------------

仪表盘

一个包含 Django、Pandas 和 Chart.js 的快速仪表盘可以在一个小时左右完成

作者截图

今天我们将讨论如何一起使用 Django、Pandas 和 Chart.js 来创建一个快速仪表板。这不是 Django 教程,我在这里走了一些捷径。目标是向您展示如何开始结合使用这些工具来快速显示一些数据。当您需要快速地将一个表可视化为几个不同的图表时,这是最好的应用。这也可能是一个更强大的 django 和 chart.js 站点的起点。

为什么是熊猫?我的经验是,当有人在网站上等待回应时,在网络服务器上做一群熊猫在幕后争吵通常不是一个好策略。当然有一种方法可以在 Django 中直接从数据库中完成所有这些工作。对于一个动态的生产网站来说,正确的数据库查询通常会更快,也是更好的长期答案。然而,有时候你已经在 Jupyter 笔记本上进行了一个很好的数据探索阶段,你只是想把一些东西扔在一起,以显示你到目前为止的发现。

为什么是姜戈?好吧,我们实际上是在讨论让 Django 启动、运行、认证并与您选择的数据库服务器对话的两分钟工作。当您可以将数据导出到单个表中时,Django 是这个项目的绝佳选择。如果您不需要身份验证,并且希望从现有数据库中访问数据,Flask 可能是更好的选择。

为什么选择 Chart.js?我发现,如果你想通过修改几个变量来部署一堆不同的交互式图表,Chart.js 非常棒。在我黑进第一张图表后,我发现其余的几乎只是稍微修改了一下。

这里的代码是这里的。让我们开始吃吧。

为了这个教程,我抓取了这个数据集。这不是动态图表的最佳数据集,但很容易。

Django 设置

我在 Debian 服务器上运行这个程序,我的目标只是记录下做了什么。同样,这不是 Django 或 python 教程。我将快速浏览这一部分。随意跳过它,但我认为这将是一个很好的参考,以便有人可以直接复制这一点。

pip install django pandas
django-admin startproject django_charts
cd django_charts
python manage.py migrate
python manage.py createsuperuser
python manage.py startapp data
cd data 
mkdir templates
cd ..
python manage.py runserver

可选调色板是调色板的绝佳选择。然而,没有它代码也能工作。

pip install palettable

基于数据集,我创建了以下模型,但这应该为您的数据修改。

从终端创建数据库后,请确保更新数据库:

python manage.py makemigrations
python manage.py migrate

我使用 pandas 和 Django 创建了一个文件,其中包含了将为 Kaggle 下载的 csv 文件加载到数据库中的方法:

运行它最简单的方法就是导入它并在您的视图中运行它。我们稍后会删除它,但这是确保网站设置的好方法。因此,让我们创建视图,但现在它只需要运行这个函数。

接下来,创建一个名为 base/dashboard.html 的空白文件,并创建一个 data/urls.py 文件

编辑 django_charts/urls.py 以添加数据 URL

更新 django_charts/settings.py 以在 INSTALLED_APPS 中包含“数据”:

让我们通过将它添加到 data/admin.py 来确保我们可以在管理站点中看到 purchase

现在,您应该能够在浏览器中转到仪表板页面。只做一次,然后在管理网站上查看是否看到记录。如果使用相同的数据集,应该有 1000 条记录。

好的。很抱歉速度跑,但现在我们可以从同一个地方开始。

HTML 设置

我有一个将被扩展的基本文件。

这可能看起来很多,但这是一个很好的基本模板。我倾向于在我的基本模板中放 4 个代码块。

{% block title %}{% endblock %}{% block custom_css %}{% endblock %}{% block page_content %}{% endblock %}{% block js_scripts %}{% endblock %}

名称可能是不言自明的,但这给了我一种从任何页面添加标题、css、body 元素或 javascript 的方法。诚然,我通常只使用 page_content 和 js_scripts 块。

你会注意到我在这里包含了 bootstrap CDN。这个项目真的不需要 popper.js。你可以不使用 bootstrap 来完成这个项目,但是你需要改变一些 dashboard.html。

我还包含了 chart.js cdn。你肯定需要这个。

我把我写的任何脚本都包装到一个$(文档)中。准备就绪,以便在页面准备就绪之前不会发生任何操作。

接下来是 dashboard.html,这是我们图表的页面:

基本上,我们正在创建一个引导容器,然后是一行,然后是一列。在柱子里,我有一副卡牌。卡片组将创建大小均匀的卡片。然后,我循环浏览每张图表,并将一张卡片放入卡片叠中。

卡牌组将试图把所有的东西都塞进同一行。forloop.counters 用于检测屏幕的大小,并相应地包装卡片组。这是我从这个例子修改的。然而,实际上你会发现 chart.js 忽略了你的画布大小设置。这是我希望 chart.js 重新考虑的一个方面。html 在这里的主要功能是布局,我不知道如何阻止 chart.js 忽略 HTML 布局。

你会看到我的两个主要循环。我将 html 元素一张一张地插入卡片中。然后,在 js 代码块中,我逐个图表地实现了 js 脚本。这是一个非常简单的方法来获得一个响应列(移动)或图表网格。在我最近的案例中,我只是希望在移动设备上显示一列图表。

views.py

我认为首先查看 views.py 文件来了解事情的流程可能是最简单的。您会注意到我删除了加载数据的一次性方法。

我们将使用模板视图。这是一个非常简单的添加内容的视图。我倾向于给我的 html 文件起一个我能猜到的名字,所以我用 template_name 来标识我使用的模板。在这种情况下,我们需要扩展的唯一方法是 get_context_data。这是 Django 在基于类的视图中使用的方法,用来获取要显示的数据。

我们拉出我们想要的对象并创建一个数据帧。因为 chart.js 通常不能很好地处理日期,所以在创建 dataframe 之后,我继续将日期转换为字符串。然后,我们只需将我们想要的每个图表添加到图表的上下文中。这允许我们在 html 代码中循环遍历图表。作为参考,每个图表都是一个字典,包含一个 html 和一个 js 条目。html 只是一个简单的带有 ID 的画布标签,js 完成了大部分工作。我在上一节 html 中讨论了循环。

Charts.py

剩下的部分在 data/charts.py 文件中。我这样做是为了便于移植到另一个项目中。你可以把它放在视图中。我真的应该清理并打包它,但这里的目标是快速,临时图表。我将在最后包含整个文件,但最好先浏览一些函数,然后进入图表类。以下所有函数都在 data/charts.py 文件中。

你可以通读评论,了解所有选项。但是这个函数接受一个 Django 模型并返回所有记录。您可以在所有名称参数后包含一些过滤器。比如说:

将结果限制在曼德勒市。Include 和 exclude 字段的工作方式与 Django 通常使用的方式相同。如果您不包含任何内容,它将从模型中提取所有字段。需要注意的是,排除是在包含之后处理的。因此,如果您包含和排除某个列,它将不会显示。我使用 pandas apply 根据输入转换日期。

在这个例子中,这实际上不做任何事情。然而,我为图表使用了一个数据类,这是为所有图表设置默认选项的一种方式。当我想为所有图表设置一个默认选项时,我重用代码并经常修改函数。

如果您不想标记每个图表,这个函数会设置一个随机 ID,js 使用它来修改相关的 html 元素。这个 ID 还用于命名 JS 函数和变量,以便于调试。因此,您需要避免图表 id 包含不允许的 js 字符(例如-).

get_colors 用于设置初始颜色。如果你传入一个调色板,它将使用它。这主要是为了利用任何可移植的导入。但是,如果你没有传入任何东西,也没有从 palletable 导入任何东西,它将随机生成颜色。

get_random_colors 用于在没有颜色传入、导入或颜色多于值的情况下生成颜色。它从你传入的颜色开始,然后随机添加它们,直到你达到想要的数量。

这些都是我们上课前需要的函数。

所以,下面是图表类。我将从这个类开始,然后讨论这个类中的每个函数。

我在这里使用了一个数据类。这些是 Python 中相对较新的内容,对于快速简单的类来说,它们通常是一个不错的选择。设置初始值可能看起来有点奇怪,但它有助于确保不使用可变对象作为实例。这真的不难,但确实会导致编写一些小函数来创建初始值。

from_lists 是我用数据、标签和颜色填充所有图表对象的方式。在检查以确保我们有足够的颜色并添加一些随机颜色后,它构建了 chart.js 所称的数据集。每个堆栈都需要在这里附加它唯一的小字典。如果只有一个值,我们希望追加整个调色板,这样每个条形都是不同的颜色。直接分配标签。from_lists 是如何绕过 pandas 并从数据库查询中创建图表的。

from_dataframe 帮助我们使用几行代码来操作几乎任何数据帧,以便能够轻松地直接输入到 chart.js 的 from_lists 中。数据透视表可以直接转储出图表所需的列表。如果你从一个单一的数据帧中做大量的图表,这是非常快的。

好吧…我知道。这是一个功能,几乎,也许应该阻止我发表这篇文章,直到我分手。然而,很容易看出这是怎么回事。我们需要形成包含标签、数据集和选项的初始元素字典。这些是基本的 charts.js 要求。一旦发生这种情况,代码就使用传入的类型来设置附加的图表选项。我想您会发现,对于大多数(如果不是全部)chart.js 图表来说,这可能是唯一需要添加的函数。例如,在这里我们需要告诉 chart.js 为堆积条形图堆积一个轴。对于其中的一些,您将看到“从零开始”,因为 chart.js 有时会默认从接近最低值的地方开始。我个人认为默认值应该是零。通常只需要定义一个图表类型就可以了。

我在这里编写函数来处理 html 和 javascript 代码。通常我会创建实际的 html 和 js 模板。这很容易做到,但这更便于携带。

没有比这更简单的了。只需创建供 js 更新的画布。这是 html 循环在每个图表的 html 模板中放置的内容。

get_js 只是生成 js 代码。可以看到大部分工作已经在上面的代码中完成了(get_elements)。这将获取 dom(画布)中的图表元素并添加图表。

最后,我定义了一个函数,将 html 和 js 作为字典返回。

总结

基本上,您可以将 chart.py 复制到您的项目中,并像我在 views.py 文件中所做的那样使用它。您需要确保您的基本 html 文件导入了 chart.js cdn。Bootstrap 显然是可选的,但确实很容易。快乐的仪表板!!!

下面是一个完整的类定义供参考:

Django 的 Web 应用程序

原文:https://towardsdatascience.com/django-web-applications-376a14167974?source=collection_archive---------15-----------------------

在 Django 中使用模板

来源

Django 是一个强大的基于 python 的 web 框架,支持开发数据库驱动的 web 应用程序。今天,许多技术都使用 Django 框架,包括 Instagram、Spotify 和 Youtube。在这篇文章中,我们将讨论如何使用 html 和 css 模板在 Django 中构建一个简单的视频游戏评论应用程序。

我们开始吧!

我们需要做的第一件事是打开一个终端并安装 Django:

pip install django

接下来,我们将创建一个 Django 项目。让我们使用以下命令创建一个名为“my_web_app”的项目:

django-admin startproject my_web_app

我们可以看看使用目录列表命令 tree 创建的文件的结构,我们看到:

tree

我们看到,在创建一个新项目时,我们已经生成了几个文件。我们有一个“my_web_app”目录,其中包含一个“manage.py”文件和一个附加的“my_web_app”文件夹。“manage.py”文件是一个命令行实用程序,允许我们创建应用程序、在服务器上运行应用程序等等。' init '“py”方法告诉 python 我们的应用程序是一个 python 包。“settings.py”允许我们修改设置和配置。“urls.py”文件包含我们将用来指引用户的路径。“wsgi.py”文件允许我们的应用程序与服务器通信。

现在让我们继续创建一个 reviews 应用程序

python manage.py startapp reviews

以及显示附加文件:

tree

现在,让我们进入我们的评论目录,打开“views.py”文件。我们应该看到以下内容:

from django.shortcuts import render# Create your views here.

让我们添加以下代码:

from django.shortcuts import renderposts = [{
'author': 'Sadrach Pierre',
'title': 'Call of Duty',
'review': 'This game is just ok.',
'date_posted': 'March 23, 2020',
}{
'author': 'Sadrach Pierre',
'title': 'Call of Duty',
'review': 'This video game is amazing!',
'date_posted': 'March 23, 2020',
}]def home(request):
    context = {
        'posts':posts
    }
    return render(request, 'reviews/home.html', context)

“views.py”现在包含了对视频游戏使命召唤的评论。它还包含一个“home”功能,允许我们呈现 html 代码,我们将使用该代码向用户显示文本。注意,在 home 函数中,我们在“reviews/home.html”路径中呈现 html 代码。

要继续,我们需要在 reviews 目录中创建一个模板目录,然后将 cd 放入 templates 目录:

mkdir templates
cd templates

现在,我们需要在 reviews 目录中创建一个附加的 reviews 文件夹和 cd:

mkdir reviews
cd reviews

接下来,我们将在“reviews/templates/reviews”目录中创建一个“base.html”文件和一个“home.html”文件。让我们创建我们的基本文件:

vi base.html

并在这里添加代码。接下来创建我们的主页 html 文件:

vi home.html

并将找到的代码添加到这里

接下来,我们需要在“my_web_app/reviews”目录中添加一个 css 文件,为我们的页面添加额外的视觉元素。让我们向上移动两个目录:

cd ../../

让我们创建一个名为“static”的目录,并将其放入我们的静态目录:

mkdir static
cd static

在我们的静态目录中创建一个额外的评论目录:

mkdir reviews
cd reviews

创建一个“main.css”文件:

vi main.css

并将找到的代码添加到这里

现在,让我们转到我们的根项目目录并打开“urls.py”,我们应该看到:

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path(**'admin/'**, admin.site.urls),
]

让我们将以下路径添加到我们的 url 列表中:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(**'admin/'**, admin.site.urls),
    path(**''**, include(**'reviews.urls'**)),
]

接下来,我们进入“my_web_app/reviews”目录,创建一个“urls.py”文件:

vi url.py 

让我们添加以下内容:

from django.urls import path
from . import views

urlpatterns = [
    path(**''**, views.home, name=**'reviews-home'**),
]

我们最终的树应该如下所示:

我们需要做的最后一件事是修改“my_web_app”目录中的“setting.py”文件:

在“setting.py”中,我们应该会看到已安装应用程序的列表:

INSTALLED_APPS = [
    **'django.contrib.admin'**,
    **'django.contrib.auth'**,
    **'django.contrib.contenttypes'**,
    **'django.contrib.sessions'**,
    **'django.contrib.messages'**,
    **'django.contrib.staticfiles'**,
]

让我们将“评论”应用添加到我们的列表中:

INSTALLED_APPS = [
    **'reviews.apps.ReviewsConfig'**,
    **'django.contrib.admin'**,
    **'django.contrib.auth'**,
    **'django.contrib.contenttypes'**,
    **'django.contrib.sessions'**,
    **'django.contrib.messages'**,
    **'django.contrib.staticfiles'**,
]

我们应该都准备好了。让我们导航到我们的根项目目录并运行我们的应用程序:

python manage.py runserver

我们应该看到:

让我们将“http://127.0.0.1:8000/”复制并粘贴到浏览器中,我们应该会看到:

目前为止这还不算多。我们可以修改“views.py”文件中的帖子,以包含更多有趣的评论。下面给使命召唤:现代战争加个评论。我来引用一下 卫报 的一篇评论。我还会添加另一款 2019 年游戏的评论,星球大战绝地:堕落秩序,来自 IGN :

from django.shortcuts import renderposts = [{
'author': 'The Gaurdian',
'title': 'Call of Duty: Modern Warfare',
'review': '[Call of Duty](https://www.theguardian.com/games/call-of-duty) is perhaps the most divisive mainstream gaming brand of all time; a gung-ho, partisan blockbuster combat romp selling us a vision of rough and ready spec-ops superstars travelling the globe with their guns and their competence, helping freedom fighters while killing rogue paramilitary groups, without pausing too long to consider the differences between them.',
'date_posted': 'March 23, 2019',
}{
'author': 'IGN',
'title': 'Star Wars Jedi: Fallen Order',
'review': 'Jedi: Fallen Order pushes all the right buttons for a Star Wars action-adventure. It’s a genre remix that samples the combat and exploration of a lightened-up Dark Souls and the action and energy of Uncharted, and that works out to be a great fit for the return of the playable Jedi.',
'date_posted': 'March 21, 2019',
}]def home(request):
    context = {
        'posts':posts
    }
    return render(request, 'reviews/home.html', context)

现在,我们的评论应用程序看起来像:

我就讲到这里,但是您可以自己随意摆弄代码。

结论

总之,在这篇文章中,我们讨论了如何使用 Django web 框架创建一个视频游戏评论 web 应用程序。您可以自由地将该应用程序进一步扩展为个人视频游戏博客或在线应用程序,供用户注册和评论视频游戏。如果你有兴趣了解 Django,我推荐科里·斯查费的 YouTube 教程,你可以在这里找到。这篇文章的代码可以在 GitHub 找到。感谢您的阅读!

使用 Amazon Macie 和 AppFlow 为 Slack 提供数据丢失防护(DLP)

原文:https://towardsdatascience.com/dlp-for-slack-using-amazon-macie-appflow-50990e2269e5?source=collection_archive---------57-----------------------

AWS 安全性

使用 Amazon Macie、AppFlow 的 Slack 集成、CloudWatch 事件来检测和通知 Slack 通道中的敏感数据泄漏

照片由 Fernando ArcosPexels 拍摄

AWS 最近宣布了一项名为亚马逊 AppFlow 的新服务。Amazon AppFlow 允许我们集成和自动化各种软件即服务(SaaS)应用程序和 AWS 服务之间的数据流。

这可以在许多方面改变游戏规则,并可以开辟许多新的可能性和有趣的用例。Amazon AppFlow 可以支持从 SaaS 应用程序到 AWS 以及从受支持的 AWS 服务到 SaaS 应用程序的数据流。在 SaaS 应用程序已经与 AWS PrivateLink 集成的情况下,数据流也可以以安全的方式发生。

让我们考虑一个有趣的数据泄漏场景

如今,许多企业使用 Slack 这样的交流平台进行更好的协作,甚至使用 ChatOps。我在这个场景中选择了 Slack,因为在我写这篇文章的时候,它是 Amazon AppFlow 中唯一支持的通信平台。

方案

DevOps 团队表示,Slack 用户可能会错误地在 Slack 频道上发布敏感信息(PII、社会安全号码、信用卡号、访问密钥等等)。

作为一项企业指导方针,您可能希望实施相关政策,以阻止或至少通知用户在其他渠道/团队成员可以看到的空闲渠道上共享此类信息。

给定场景,让我们看看如何使用 AWS 服务构建解决方案。

解决方案看起来怎么样?

体系结构

无服务器解决方案架构

高级实施步骤—

将 Amazon AppFlow 与一个 Slack 通道集成为源。

将对话数据上传到亚马逊 S3。

使用 Amazon Macie 来发现和检测 S3 内容中的敏感数据。

使用 Amazon Macie 生成的 Amazon CloudWatch 事件采取适当的措施进行补救或通知。

实施细节

将 Amazon AppFlow 与一个 Slack 通道集成为源

作为此步骤的先决条件,您需要以下详细信息来从 AppFlow 连接到 Slack。每当您尝试将 AppFlow 流连接配置为 Slack 时,都会看到此弹出窗口。如果需要建立新的连接,请参考下面的步骤 3。

图片:松弛连接细节

设置 Amazon AppFlow

步骤 1:为亚马逊 AppFlow 创建一个 S3 桶

创建一个 S3 桶,Amazon AppFlow 将从 Slack 上传数据。此时不需要额外的配置。如果需要,请检查此链接—

[## 我如何创建一个 S3 桶?

在将数据上传到亚马逊 S3 之前,您必须在其中一个 AWS 区域创建一个存储桶来存储您的数据。之后…

docs.aws.amazon.com](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/create-bucket.html)

第二步:指定流程细节

提供流名称、描述、加密和可选标记。

图片:流量详情

请注意,Amazon Macie 在加密数据发现方面有一定的限制。正如您将看到的,我们将为 Amazon Macie 使用AWSServiceRoleForAmazonMacie服务关联角色,我们将不得不使用自定义的 AWS KMS 客户管理密钥(CMK),如下面的截图“图片:数据加密”所示。如果您还没有可以使用的 AWS KMS 键,请创建一个。

注意:我必须获得这个配置部分以及 KMS 密钥权限(在下面的后面部分讨论),并且还要经过 AWS 的验证,因为这些信息并不容易获得。

图片:数据加密

有关亚马逊 Macie 的加密/解密相关细节,请参考此链接—

[## 用 Amazon Macie 分析加密的 S3 对象

如果一个对象存储在亚马逊 S3 存储桶中并且是加密的,亚马逊 Macie 可能无法分析其中的数据…

docs.aws.amazon.com](https://docs.aws.amazon.com/macie/latest/user/discovery-supported-encryption-types.html)

第三步:配置流量

这里需要创建一个新的松弛连接。遵循此链接中仔细提供的说明——

[## 亚马逊 AppFlow 的要求

在使用 Amazon AppFlow 创建流之前,请确认您拥有关于源和…的所需信息

docs.aws.amazon.com](https://docs.aws.amazon.com/appflow/latest/userguide/requirements.html#slack)

一旦创建了连接,就配置您想要监视的 Slack 通道。目前,不可能在同一个流中选择多个通道。因此,如果您想要监视多个通道,您必须配置多个流。

图片:配置流量

配置 Amazon AppFlow 将在其中上传松弛渠道对话数据的目标 S3 存储桶。

图片:目的地详情

可以根据需要安排流触发。松弛集成不支持“事件时运行流”触发器。

图片:流量触发器

步骤 4:创建源和目标数据字段之间的映射

在此步骤中,您可以选择需要映射到目标字段的源字段。在本例中,我已经映射了所有字段。您可以只选择几个您需要的,它允许您做一些数据修改,以及像屏蔽字段。

图片:数据映射

我没有添加任何验证或过滤器。继续做你喜欢的实验。

最后,创造流动。

创建完成后,在 Slack 通道中提交一些消息,看看 AppFlow 是否成功地将数据上传到 S3。测试数据应该是 Amazon Macie 能够正确检测的有效格式。

仅测试数据!😄

一旦流量开始触发,您可以看到如下运行历史。现在忽略 0 KB 的大小。当流处理非空数据时,它将显示正确的大小。

注意,“错误”记录不提供任何故障排除选项。没有可供浏览的链接,没有 CloudWatch 日志,只是一个黑洞。在 AWS 在未来的版本中提供一种方法之前,您只能靠自己找出问题所在!我现在已经提交了一个功能请求。如果您找到了进一步排除故障的方法,请与我们分享。

图片:跑步记录

将对话数据上传到亚马逊 S3

如果流运行成功,它应该将数据上传到配置的 S3 存储桶。上传的对象存储在使用流名称创建的路径下。

图片:S3

根据配置的数据映射,S3 对象内容可能如下所示

{“client_msg_id”:”51c6e23c-abb2–4dc2–82e6–12ff9216af58",”text”:”aws_access_key AKIAJVCPCOZKBNJFRAXQ”,”ts”:”1591923645.001300",”type”:”message”}

S3 铲斗配置

Amazon AppFlow 自动配置了一个 S3 存储桶策略以及一个如下所示的策略声明。

图片:AppFlow S3 桶政策

这种策略对于 Amazon AppFlow 来说很好,但是我们必须添加一个额外的策略,如下图所示,以允许 Amazon Macie 从 S3 桶中读取数据。这里的权限可能看起来过于宽松。继续尝试,使其更具限制性。

图片:亚马逊 Macie 的 S3 桶政策声明

使用 Amazon Macie 发现和检测 S3 内容中的敏感数据

随着数据上传到 S3,现在我们可以配置 Amazon Macie 来分类 S3 数据,并在检测到敏感数据时接收生成的警报。

亚马逊 Macie 使用机器学习来自动发现、分类和保护存储在 S3 桶中的敏感数据。

使用 Amazon Macie 生成的 Amazon CloudWatch 事件采取适当的补救或通知措施

可以捕获由 Amazon Macie 发现生成的 CloudWatch 事件。可以创建一个事件规则来向所需的目标发送通知。现在,我们将配置一个简单的 SNS 主题作为规则的通知目标。

让我们在配置 Macie 之前配置通知系统

第一步:创建或重用一个 SNS 主题,需要订阅者

请参考此链接创建 SNS 主题-

[## 设置 Amazon SNS 通知

或者,如果您计划使用 AWS 管理控制台创建 CloudWatch 警报,您可以跳过这一步…

docs.aws.amazon.com](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/US_SetupSNS.html)

第二步:创建一个云观察事件规则

使用下面的事件模式和 SNS 主题创建一个 CloudWatch 事件规则。

图片:Macie 的 CloudWatch 事件规则

现在,让我们配置 Amazon Macie

先决条件:需要启用亚马逊 Macie。如果你已经在使用 Macie 经典版,建议升级到新版本。新版本有很多改进。同样参考这篇文章

启用 Macie 后,您会看到一个类似下图的带有摘要视图的仪表板。Macie 可能需要一些时间来发现和显示数据,具体取决于数据量。

图片:亚马逊 Macie 仪表盘

第一步:配置 AWS KMS CMK 权限

添加如下所示的关键策略语句,以允许 Amazon Macie 的服务关联角色使用 CMK 密钥解密由 Amazon AppFlow 加密的 S3 对象。

图片:AWS KMS CMK 亚马逊 Macie 关键政策声明

步骤 2:创建自定义数据标识符(可选;取决于使用案例)

我正在为这个特定的用例演示配置自定义数据标识符。但是,它对你来说可能是可选的。我将在一个 Macie 数据分类作业配置中使用它们。

我已经配置了 2 个基于正则表达式的标识符,分别用正则表达式AKIA[0–9A-Z]{16}[0–9a-zA-Z/+]{40}来检测 AWS 访问密钥和 AWS 秘密密钥。

设置自定义数据标识符

步骤 3:创建数据分类作业

转至“S3 存储桶”页面,选择要发现和分类数据的存储桶,然后单击“创建作业”。单击下一步。

图片:S3 水桶精选

在“范围”屏幕中,配置作业执行频率和数据发现标准。我为此用例选择了“一次性工作”。而且,因为我想发现所有数据并进行分类,所以我没有配置任何包含和/或排除标准。

图片:发现和分类范围

如果需要,选择自定义数据标识符。

图片:添加自定义数据标识符

提供作业名称和描述,并创建作业。

图片:工作名称和描述

根据需要发现和分类的数据量,这项工作需要一些时间才能完成。

图片:工作列表

第四步:查看亚马逊 Macie 调查结果

一旦作业发现敏感数据,就可以在“调查结果”屏幕下过滤和搜索调查结果。Amazon Macie 显示了调查结果的严重程度、类型和其他细节,可以进一步深入研究。

图片:工作发现

从时差中发送更多数据

现在我们已经有了预期的解决方案,启动 Slack 并在通道中发送更多带有适当敏感数据的消息。

假设 Amazon AppFlow 每隔几分钟就处理一次数据,我们应该会在 Macie 中看到警告,也会收到 SNS 通知。

下面的通知是普通的 SNS 消息格式,所以可能不是很直观。根据 CloudWatch 事件目标,可以采取适当的措施,并根据需要处理数据。但是,无论如何你得到了这个想法!

图片:SNS 通知

结论

谢谢你读到这里!

因此,这篇文章展示了我们如何构建一个 AWS 解决方案来检测和通知基于聊天的通信平台中的敏感数据泄漏场景。该架构完全无服务器、安全且可扩展。

关于这个解决方案,有一点需要注意

上传到 Slack 频道的文档和图像不能被 Amazon AppFlow 处理,也不能以相同的格式存储在 S3。因此,亚马逊 Macie 将无法检测通过此类对象传递的敏感信息。

如果需要这样的能力,我们应该能够使用 Slack APIs 和 AWS Lambda 构建一个解决方案来检索数据,并将其存储回 S3 供 Amazon Macie 分析。

根据需求,可以使用亚马逊人工智能服务(如 Rekognition、Textract 等)构建各种这样的解决方案。也是。

参考

[## 亚马逊应用流-亚马逊网络服务

安全集成应用程序并自动处理任意规模的数据流,无需代码。Amazon AppFlow 是一个完全托管的…

aws.amazon.com](https://aws.amazon.com/appflow/) [## 新-宣布亚马逊应用流|亚马逊网络服务

软件即服务(SaaS)应用对我们的客户越来越重要,采用率也在不断提高…

aws.amazon.com](https://aws.amazon.com/blogs/aws/new-announcing-amazon-appflow/) [## 什么是亚马逊 Macie?

亚马逊 Macie 是一个完全托管的数据安全和数据隐私服务,使用机器学习和模式匹配…

docs.aws.amazon.com](https://docs.aws.amazon.com/macie/latest/user/what-is-macie.html) [## 全新增强的 Amazon Macie 现已推出,价格大幅降低| Amazon Web Services

Amazon Macie 是一个完全托管的服务,它使用机器学习来帮助您发现和保护您的敏感数据…

aws.amazon.com](https://aws.amazon.com/blogs/aws/new-enhanced-amazon-macie-now-available/)

关注我上LinkedInTwitter

上订阅更多此类帖子。

黑人的命也是命吗?警察暴力的统计分析

原文:https://towardsdatascience.com/do-black-lives-matter-do-white-lives-matter-e49948d0ffd3?source=collection_archive---------7-----------------------

谁的生活重要呢?

死亡是一系列严重问题的永久证据。除非复活,否则受害者无法从死亡中恢复,但这也减少了对个人、社会和少数群体造成伤害的灰色地带。为了理解谁的生活对一个社会至关重要,我们可以看看当局以及他们如何对待人民,以及人民如何对待当局。所以我把死亡作为评估警察暴力和针对警察的暴力的主要标准。我想回答这个问题,黑人的生命重要吗?在这个过程中,我偶然发现了另一个有趣的问题,白人的生命重要吗?

虽然数据清楚地表明黑人被杀的比率高于白人,但也表明很多人被警察枪杀。所以如果你相信“所有的生命都很重要”,那么你应该和黑人社区问同样的问题:为什么警察如此暴力?

我在这里不是要回答为什么,而是如何,在哪里,对谁的问题。暴力有多暴力?这哪里是暴力?这种暴力对谁的影响更大?

一般统计

首先,我从一个更高的层面看了被警察打死的人的统计数据。然后,我根据 4.91 的比率调整了人口,因为在美国,每 100 个黑人,就有 491 个白人。看着左上图中的数据,人们可能会认为没有问题。当这些数值根据人口进行调整时,特别是对于手无寸铁的人来说,显然存在一个问题。

然后我们可以看看黑人和白人谋杀的比例,同样,如果你是黑人,你比白人更有可能被警察杀死,不管情况如何。

拘留

第一个批评是,可能有更高比例的黑人卷入暴力犯罪。我们不得不考虑这种可能性,但应该注意的是,这种情况也可能是由系统性的种族主义造成的。撇开这个不谈,我们来看看 的抓捕人数

当按人口调整这些逮捕时,黑人被逮捕的次数是白人的两倍。现在我们可以看看警察因逮捕人数而导致的死亡人数。这个问题减轻了,但是它仍然存在,特别是对于没有武器或者在汽车里的人。

警告是,如果存在系统性的种族主义,我们如何确定黑人不会比白人更容易成为目标和被逮捕?我没有数据,所以不好说,但是大部分数据都指向一个问题。

年龄

按年龄分类显示,年轻黑人男性的趋势比总体统计趋势更糟糕。

按州

让我们按州来划分。任何一个白色的州都没有足够的数据。每个州的死亡人数根据该州的种族统计数据进行了调整。我本以为南部各州的情况会更糟,更不用说 4 比 1 的比例了。加州绝对令人惊讶。

一般警察对白人和黑人的暴行是相当高的,尤其是在南方。让我们通过种族来分析一下,看看你被警察杀死的概率。同样,如果你是黑人,在警察周围不是你想做的事情,但奇怪的是,南方似乎不那么可怕。

警察

没有对警察暴行的讨论,关于政策暴行的讨论是不完整的。还是那句话,让我们从过去几年的死亡人数开始了解风险。在 5 年零 5 个月的时间里,警察被谋杀的总数为 369 起,因此平均每年有 68 名警察因殴打而被暴力杀害。

https://datausa.io/profile/soc/police-officers报道,白人与黑人警察的比例为 5.84 比 1,相对接近 4.91 比 1 的全国平均水平。我们可以看看被谋杀的警察人数,按照警察和被侵犯者的种族来划分。在这种情况下,警察的种族似乎与他们是否被杀无关。然而,当按人口调整警察谋杀案时,杀害警察的人是不成比例的黑人。

谁是负责人?

维持治安的一个重要方面是谁来维持治安?谁管理警察部门?谁在忍受这样的暴力?于是我调出了一些数据,我发现了一个有趣的趋势。对于较大的城市,根据人口调整的权力地位是公平的。我用的是白人和黑人 4.91 的比例,但对于较小的城市,这个比例要高得多。这意味着小城镇的差距更大。

我们可以看看大城市 vs 小城镇,有很大的出入。

因此,我决定从城镇人口中提取我所掌握的关于警察暴力的数据。这些警察暴行发生在哪里?

按城市规模划分的残暴程度

我分别提取了城市规模信息,并将数据关联起来。这第一个分类显示了被警察杀害的人,然后我根据皮尤研究中心的假设白人和黑人的分布进行了调整,皮尤研究中心根据城市、郊区和农村地区进行了分类。我的假设是人口少于 9999 的城市。

现在,让我们来确定这些地区每 10 万人中的警察死亡率。趋势是住在小城镇对黑人来说更危险,但另一个有趣的趋势是,就警察暴行而言,他们对白人来说更危险。

为什么人们不在小镇上游行?

按每个城市的种族划分数据

我进一步分解了数据。我使用了一个人口普查工具尽可能多地查看主要城市,以确定它们的种族构成。这涵盖了人口超过 50,000 的大多数城市。至于其余的,我结合了每个州的种族分类和城市、郊区和农村地区的种族分类的一般数据。有了这些数据,我可以更深入地研究,我将警察造成的黑人死亡与他们所在的城市或州的人口进行标准化。

首先,在一些州,如纽约、加利福尼亚和许多北方州,由警察造成的黑人和白人的死亡比例要高得多。然而,令人震惊的是,如此多的国家是危险的。警察很危险。

我们按种族来分解一下,看看情况有多黑多白,如下图。在左边,我用不同的比例来衡量每 10 万人中白人和黑人的死亡率。在右边,你可以看到,一旦你使用同样的尺度,白人的死亡人数与黑人的死亡人数相差甚远,但有一点是清楚的,俄克拉荷马州不是你想去的地方。

我们可以把这个数据拆分成 5 万人以上和 5 万人以下的较大城市。这表明小城镇对黑人尤其危险,对白人也是如此。

总结一下这个数据,调整了城市和种族分类,我们可以看到小城镇的死亡人数要多得多,但是比率要低一些。

我已经尽我所能对这个分析保持客观和不带感情色彩,但是这个分析已经耗尽了我的精力。我知道警察暴力的问题很严重,我三年前看过这个数据。但是,这些数据很重要,而且还不完整。

一个更好的数据集应该具有以下内容:

  1. 记录每一站
  2. 记录每一次逮捕
  3. 记录每一次射击
  4. 记录警方的每一次暴力行为
  5. 记录涉案官员

这些数据还没有被收集甚至没有被尝试的原因是因为它会显示这个问题比这里呈现的更糟糕。这个数据是冰山一角。这些数据显示了最极端的种族主义形式,有时很难发现。

那么谁的生命重要呢?你告诉我。给我看看数据。

放下恐惧,实践爱

posted @ 2024-10-15 13:45  绝不原创的飞龙  阅读(173)  评论(0)    收藏  举报